/// <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);
            }
        }
Exemple #3
0
        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);
        }