/// <summary>Loads the specified <see cref="MDBPlayListItem"/>.</summary> /// <param name="mdb">The <see cref="MusicDataBase"/> instance.</param> /// <param name="item">The <see cref="MDBPlayListItem"/>.</param> /// <returns></returns> public static MDBFileSelection Load(MusicDataBase mdb, MDBPlayListItem item) { var result = new MDBFileSelection(); result.AudioFile = mdb.AudioFiles.TryGetStruct(item.AudioFileID); result.File = mdb.Files.TryGetStruct(result.AudioFile.FileID); result.NowPlaying = MDBNowPlaying.Create(mdb, item.StreamID, item.OwnerID, item.SubsetID, DateTime.MinValue, result.AudioFile); result.PlayListItem = item; return(result); }
private void PlayFile(MDBFileSelection selection) { var silenceCompression = mdb.Config.ReadBool("Player", "SilenceCompression", false); IAudioDecoder decoder = new Mpg123(false); if (!decoder.IsAvailable) { decoder = new MP3AudioDecoder(); } this.LogInfo("Prepare playing {0} using audio decoder <cyan>{1}<default> silence compression {2}", selection.AudioFile, decoder, silenceCompression); string fileName = selection.File.GetFullPath(mdb); this.LogDebug("Open file {0}", fileName); var mp3FileStream = ResistantFileStream.OpenSequentialRead(fileName); decoder.BeginDecode(mp3FileStream); currentNowPlaying = MDBNowPlaying.Create(mdb, selection.PlayListItem.StreamID, selection.PlayListItem.OwnerID, selection.PlayListItem.SubsetID, DateTime.MinValue, selection.AudioFile); IAudioData audioData = decoder.Decode(); if (device == null) { device = SelectDevice() ?? throw new Exception("Could not find any available audio device implementation!"); } var audioOut = device.CreateAudioOut(audioData); this.LogInfo("Start buffering {0} <cyan>{1}", selection.AudioFile, audioData); TimeSpan inSilenceTime = TimeSpan.Zero; TimeSpan fileReadPosition = TimeSpan.Zero; skip = false; bool started = false; long underflow = 0; while (!exit && !skip) { //buffer until we got at least one second, or ten during playback var sleepTime = started ? audioOut.TimeBuffered - TenSeconds : audioOut.TimeBuffered - OneSecond; //buffer filled ? if (sleepTime > TimeSpan.Zero) { if (CurrentStreamSettings.StreamType != MDBStreamType.JukeBob) { skip = true; } if (audioOut.Volume != Math.Max(0, CurrentStreamSettings.Volume)) { audioOut.Volume = CurrentStreamSettings.Volume; } //already started ? if (started) { //yes, check for a gap/buffer underrun ? if (audioOut.BufferUnderflowCount != underflow) { //we got a gap, fix starttime underflow = audioOut.BufferUnderflowCount; this.LogWarning("Player GAP {0}, Buffer was empty!", underflow); currentNowPlaying.StartDateTime = DateTime.UtcNow - fileReadPosition + audioOut.TimeBuffered; ThreadPool.QueueUserWorkItem(delegate { mdb.NowPlaying.Replace(currentNowPlaying); }); } else { Thread.Sleep(Math.Min(1000, (int)sleepTime.TotalMilliseconds)); } } else { //do start if we are allowed to (check playing previous title) sleepTime = nextStart - DateTime.UtcNow; if (sleepTime > TimeSpan.Zero) { this.LogVerbose("Sleep {0}", sleepTime.FormatTime()); Thread.Sleep(sleepTime); } currentNowPlaying.StartDateTime = DateTime.UtcNow; audioOut.Start(); started = true; this.LogInfo("Start playing {0}", selection.AudioFile); //write to now playing ThreadPool.QueueUserWorkItem(delegate { mdb.NowPlaying.Replace(currentNowPlaying); mdb.PlayListItems.TryDelete(nameof(MDBPlayListItem.ID), selection.PlayListItem.ID); }); } } audioData = decoder.Decode(); //end of file ? if (audioData == null) { break; } //add packet duration to file position fileReadPosition += audioData.Duration; //skip silence if (silenceCompression) { if (audioData.Peak < 0.001f) { if (inSilenceTime > OneSecond) { continue; } inSilenceTime += audioData.Duration; } else { if (inSilenceTime > OneSecond) { //we skipped some silence, fix starttime this.LogDebug("Silence compression {0}", inSilenceTime.FormatTime()); currentNowPlaying.StartDateTime = DateTime.UtcNow - fileReadPosition + audioOut.TimeBuffered; ThreadPool.QueueUserWorkItem(delegate { mdb.NowPlaying.Replace(currentNowPlaying); }); } inSilenceTime = TimeSpan.Zero; } } if (!audioOut.Configuration.Equals(audioData)) { this.LogWarning("Frankenstein Stream in file <red>{0}", fileName); break; } audioOut.Write(audioData); } nextStart = DateTime.UtcNow; if (skip) { //skipped, start in 2s nextStart += TimeSpan.FromSeconds(1); } else { //start after current title nextStart += audioOut.TimeBuffered - TimeSpan.FromSeconds(1); } this.LogInfo("Finish playing {0}", selection.AudioFile); if (exit) { CloseAudioOut(audioOut); } else { CloseAudioOutAsync(audioOut); } }
public void GetPlayerState(WebData webData, int hash = 0, long streamID = 0) { //default is JukeBob if (streamID == 0) { streamID = (long)MDBStreamType.JukeBob; } MDBNowPlaying nowPlaying = mdb.NowPlaying.TryGetStruct(1); var myHash = nowPlaying.GetHashCode() ^ mdb.PlayListSequenceNumber ^ -1; DateTime timeout = DateTime.UtcNow.AddSeconds(30 + DefaultRNG.UInt8 % 30); while (hash == myHash && DateTime.UtcNow < timeout) { Thread.Sleep(200); nowPlaying = mdb.NowPlaying.TryGetStruct(1); myHash = nowPlaying.GetHashCode() ^ mdb.PlayListSequenceNumber ^ -1; } var playList = new List <MDBPlayListItem>(); playList.AddRange(mdb.PlayListItems.GetStructs( Search.FieldEquals(nameof(MDBPlayListItem.StreamID), streamID) & Search.FieldGreater(nameof(MDBPlayListItem.OwnerID), 0), ResultOption.SortAscending(nameof(MDBPlayListItem.Added)))); playList.AddRange(mdb.PlayListItems.GetStructs( Search.FieldEquals(nameof(MDBPlayListItem.StreamID), streamID) & Search.FieldEquals(nameof(MDBPlayListItem.OwnerID), 0), ResultOption.SortAscending(nameof(MDBPlayListItem.Added)))); var audioFileIDs = new Set <long>(nowPlaying.AudioFileID, playList.Select(i => i.AudioFileID)); var files = mdb.AudioFiles.GetStructs(audioFileIDs); var albums = mdb.Albums.GetStructs(files.Select(f => f.AlbumID)); var artistIDs = new Set <long>(files.Select(f => f.AlbumArtistID), files.Select(f => f.SongArtistID)); var artists = mdb.Artists.GetStructs(artistIDs); var users = authTables.Users.GetStructs(playList.Select(i => i.OwnerID).Where(i => i > 0)).Select(u => u.ClearPrivateFields()); var subsetIDs = playList.Where(i => i.SubsetID > 0).Select(i => i.SubsetID).ToSet(); if (nowPlaying.SubsetID > 0) { subsetIDs.Include(nowPlaying.SubsetID); } var subsets = mdb.Subsets.GetStructs(subsetIDs); long ownerID; if (webData.Session.IsAuthenticated()) { ownerID = webData.Session.GetUser().ID; } else { ownerID = -webData.Session.ID; } webData.Result.AddMessage(webData.Method, "Retrieved JukeBob NowPlaying"); webData.Result.AddStructs(playList); webData.Result.AddStructs(files); webData.Result.AddStructs(albums); webData.Result.AddStructs(artists); webData.Result.AddStructs(subsets); webData.Result.AddStructs(users); webData.Result.AddStruct(new MDBPlayerState() { ID = ownerID, Hash = myHash, StreamType = MDBStreamType.JukeBob }); nowPlaying.UpdateDateTime = DateTime.UtcNow; webData.Result.AddStruct(nowPlaying); }