/// /// Adds the info for this local track-on-disk to the database /// public void Add( PlayableData newData ) { lock (_serializer) { _database.Add( newData ); } }
/// /// Add a track to the end of the playlist. Duh. :) /// void _PlaylistAppend( PlayableData trackData ) { _playQueueMutex.WaitOne(); try { _playQueue.Add( trackData ); } finally { _playQueueMutex.ReleaseMutex(); } }
/// /// This is sort of random. We sometimes play tracks even if /// they suck. /// /// \return true if this track doesn't suck too much /// bool _WantToPlayTrack( PlayableData info ) { int nUsers = _controllers.Count; uint trackKey = info.key; // Now, decide whether we are going to actually PLAY this // track. While we're at it, delete people who have abandoned // us or closed their browsers. double unSuckProb = 1.0; // (probability that it will play) double moodProb = 1.0; // Composite mood threshold DateTime now = DateTime.Now; // cached for speed? // make a copy of the hash table keys, cause otherwise we can't // delete things: object [] keys = new object[_controllers.Count]; _controllers.Keys.CopyTo( keys, 0 ); // This is a decimation playlist, so it has to pass everybody's // test before it will be played. If you have too many // contributors, you will end up with mediocre crap only. // The only way around this is to either listen to songs you don't // like or to limit the number of contributors. // Assuming this song has to pass everybody's tests, calculate // the overall probablility and just test once (conserves randomness) _Trace( "Merging contributor opinions..." ); for (int i = 0; i < keys.Length; i++) { uint key = (uint)keys[i]; Contributor contrib = (Contributor)_controllers[key]; // Check to see if this user has timed out. if (now - contrib.lastPing > CONTRIBUTOR_TIMEOUT) { _Trace( "User timed out, removing: " + contrib.user.name ); _controllers.Remove( key ); -- nUsers; Debug.Assert( nUsers >= 0, "removed more users than existed?" ); continue; // ** SKIP THIS LOGGED OUT DEADBEAT ** } _Trace( " - " + contrib.user.name ); double userSuck = (double)_database.GetSuck( contrib.user.id, trackKey ); userSuck /= 10000.0; // convert to probability unSuckProb *= 1.0 - userSuck; // combine max suck amounts double userMood = _database.GetAppropriate( contrib.user.id, contrib.mood.id, trackKey ); userMood /= 10000.0; moodProb *= userMood; } if (0 >= nUsers) // Nobody exists, just play it. return true; // Convert it back to an "int" int suckProb = (int)((1.0 - unSuckProb) * 10000.0); moodProb *= 10000.0; _Trace( "MOOD_PROB: " + moodProb + " SUCK_PROB: " + suckProb ); // If suckThresh is less than the suck amount, we don't play it. // Sort of like a saving throw. Save Vs. Suck. uint suckThresh = (uint)_cheapRandom.Next( 01000, 09000 ); if (suckProb > suckThresh) { _Trace( " Rejected. suckProb:" + suckProb + " suckThresh:" + suckThresh ); info.evaluation = TrackEvaluation.SUCK_TOO_MUCH; return false; // methinks it sucketh too much } uint moodThresh = (uint)_cheapRandom.Next( 01200, 08800 ); if (moodProb < moodThresh) { _Trace( " Rejected. moodProb:" + moodProb + " moodThresh:" + moodThresh ); info.evaluation = TrackEvaluation.WRONG_MOOD; return false; // not in the mood } _Trace( String.Format( " Accepted. suckProb:{0}, suckThresh:{1}, moodProb:{2}, moodThresh{3}", suckProb, suckThresh, moodProb, moodThresh )); info.evaluation = TrackEvaluation.ALL_GOOD; return true; // good enough }
/// /// Updates the database info for a file. Assumes the file is an /// MP3 file. /// /// \todo Create a pluggable interface to extract the file info /// from various file types, not just mp3 /// void _UpdateMP3FileInfo( string path, IBackend engine ) { // Get ID3 tags from the file ID3v2 tag = new ID3v2( path, false ); if (!tag.isValid) { _Trace( "Note: No id3v2 tag found in file: '" + path + "'" ); return; } if ((null == tag.tit2) || (tag.tit2.Length == 0)) { // Match anything after the last forward/backslash string basename = Path.GetFileName( path ); Debug.Assert( null != basename, "All files have names!" ); Debug.Assert( basename.Length > 0, "All files have names!" ); _Trace( "Song title is empty, using file name: '" + basename + "'" ); tag.tit2 = basename; } string genre = tag.DefaultGenre; if (null == genre) genre = "unknown"; if (engine.EntryExists( path )) { engine.FileIsNotMissing( path ); } else { // Oh god, the noise, the noise! Just comment when something interesting // heppens, OK? // _Trace( "adding '" + path + "'" ); PlayableData data = new PlayableData(); data.filePath = path; if (tag.tpe1 == null) data.artist = ""; else data.artist = tag.tpe1; if (tag.talb == null) data.album = ""; else data.album = tag.talb; if (tag.tit2 == null) // no title, maybe check tit1 and tit3? data.title = ""; else data.title = tag.tit2; data.track = tag.trackIndex; data.genre = genre; data.lengthInSeconds = 0; // unknown, ID3 tag doesn't know engine.Add( data ); } }