/// <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); } }