/// <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);
        }
示例#2
0
        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);
            }
        }