示例#1
0
文件: Player.cs 项目: pascalfr/MPfm
        /// <summary>
        /// Plays the playlist from the current item index.
        /// </summary>
        public void Play(double initialPosition, bool startPaused)
        {
            try
            {
                if (_isPlaying)
                {
                    if (_currentLoop != null)
                    {
						//Tracing.Log("Player.Play -- Stopping current loop...");
                        StopLoop();
                    }

					//Tracing.Log("Player.Play -- Stopping playback...");
                    Stop();
                }

                RemoveSyncCallbacks();
                _currentLoop = null;
                _positionOffset = 0;
                _currentMixPlaylistIndex = Playlist.CurrentItemIndex;

                // How many channels are left?                
                int channelsToLoad = Playlist.Items.Count - Playlist.CurrentItemIndex;                

                // If there are more than 2, just limit to 2 for now. The other channels are loaded dynamically.
                if (channelsToLoad > 2)
                    channelsToLoad = 2;

                // Check for channels to load
                if (channelsToLoad == 0)
                    throw new Exception("Error in Player.Play: There aren't any channels to play!");

                // Load the current channel and the next channel if it exists
                for (int a = Playlist.CurrentItemIndex; a < Playlist.CurrentItemIndex + channelsToLoad; a++)
                    _playlist.Items[a].Load(_useFloatingPoint);

                // Start decoding first playlist item
                //_decodingService.StartDecodingFile(_playlist.Items[0].AudioFile.FilePath, _positionOffset);

                try
                {
                    // Create the streaming channel (set the frequency to the first file in the list)
					//Tracing.Log("Player.Play -- Creating streaming channel (SampleRate: " + _playlist.CurrentItem.AudioFile.SampleRate + " Hz, FloatingPoint: true)...");

#if IOS
                    _streamProc = new STREAMPROC(StreamCallbackIOS);
#else
                    _streamProc = new STREAMPROC(StreamCallback);
#endif

                    _streamChannel = Channel.CreateStream(_playlist.CurrentItem.AudioFile.SampleRate, 2, _useFloatingPoint, _streamProc);
					//Tracing.Log("Player.Play -- Creating time shifting channel...");
                    _fxChannel = Channel.CreateStreamForTimeShifting(_streamChannel.Handle, true, _useFloatingPoint);
                    //_fxChannel = Channel.CreateStreamForTimeShifting(_streamChannel.Handle, false, _useFloatingPoint);
                    //_fxChannel = _streamChannel;
                }
                catch(Exception ex)
                {
                    // Raise custom exception with information (so the client application can maybe deactivate floating point for example)
                    PlayerCreateStreamException newEx = new PlayerCreateStreamException("The player has failed to create the stream channel (" + ex.Message + ").", ex);
                    newEx.Decode = true;
                    newEx.UseFloatingPoint = true;
                    newEx.SampleRate = _playlist.CurrentItem.AudioFile.SampleRate;
                    throw newEx;
                }

                // Check driver type
                if (_device.DriverType == DriverType.DirectSound)
                {
                    try
                    {
                        // Create mixer stream
						Tracing.Log("Player.Play -- Creating mixer channel (DirectSound)...");
                        _mixerChannel = MixerChannel.CreateMixerStream(_playlist.CurrentItem.AudioFile.SampleRate, 2, _useFloatingPoint, false);
                        _mixerChannel.AddChannel(_fxChannel.Handle);
                        //_mixerChannel = _fxChannel;
                        AddBPMCallbacks();
                    }
                    catch (Exception ex)
                    {   
                        // Raise custom exception with information (so the client application can maybe deactivate floating point for example)
                        PlayerCreateStreamException newEx = new PlayerCreateStreamException("The player has failed to create the time shifting channel.", ex);
                        newEx.UseFloatingPoint = true;
                        newEx.UseTimeShifting = true;
                        newEx.SampleRate = _playlist.CurrentItem.AudioFile.SampleRate;
                        throw newEx;
                    }
                }
#if !IOS && !ANDROID
                else if (_device.DriverType == DriverType.ASIO)
                {
                    try
                    {
                        // Create mixer stream
                        Tracing.Log("Player.Play -- Creating mixer channel (ASIO)...");
                        _mixerChannel = MixerChannel.CreateMixerStream(_playlist.CurrentItem.AudioFile.SampleRate, 2, _useFloatingPoint, true);
                        _mixerChannel.AddChannel(_fxChannel.Handle);
                    }
                    catch (Exception ex)
                    {
                        // Raise custom exception with information (so the client application can maybe deactivate floating point for example)
                        PlayerCreateStreamException newEx = new PlayerCreateStreamException("The player has failed to create the time shifting channel.", ex);
                        newEx.DriverType = DriverType.ASIO;
                        newEx.UseFloatingPoint = true;
                        newEx.UseTimeShifting = true;
                        newEx.Decode = true;
                        newEx.SampleRate = _playlist.CurrentItem.AudioFile.SampleRate;
                        throw newEx;  
                    }
                    
                    // Set floating point
                    BassAsio.BASS_ASIO_ChannelSetFormat(false, 0, BASSASIOFormat.BASS_ASIO_FORMAT_FLOAT);

                    // Create callback
                    asioProc = new ASIOPROC(AsioCallback);

                    try
                    {
                        // Enable and join channels (for stereo output
                        Tracing.Log("Player.Play -- Enabling ASIO channels...");
                        BassAsio.BASS_ASIO_ChannelEnable(false, 0, asioProc, new IntPtr(_mixerChannel.Handle));
                        Tracing.Log("Player.Play -- Joining ASIO channels...");
                        BassAsio.BASS_ASIO_ChannelJoin(false, 1, 0);

                        // Set sample rate
                        Tracing.Log("Player.Play -- Set ASIO sample rates...");
                        BassAsio.BASS_ASIO_ChannelSetRate(false, 0, _playlist.CurrentItem.AudioFile.SampleRate);
                        BassAsio.BASS_ASIO_SetRate(_playlist.CurrentItem.AudioFile.SampleRate);
                    }
                    catch (Exception ex)
                    {
                        // Raise custom exception with information (so the client application can maybe deactivate floating point for example)
                        PlayerCreateStreamException newEx = new PlayerCreateStreamException("The player has failed to enable or join ASIO channels.", ex);
                        newEx.DriverType = DriverType.ASIO;
                        newEx.SampleRate = _playlist.CurrentItem.AudioFile.SampleRate;
                        throw newEx;
                    }

                    // Start playback
                    Tracing.Log("Player.Play -- Starting ASIO buffering...");
                    if (!BassAsio.BASS_ASIO_Start(0))
                    {
                        // Get BASS error
                        BASSError error = Bass.BASS_ErrorGetCode();

                        // Raise custom exception with information (so the client application can maybe deactivate floating point for example)
                        PlayerCreateStreamException newEx = new PlayerCreateStreamException("The player has failed to create the ASIO channel (" + error.ToString() + ").", null);
                        newEx.DriverType = DriverType.ASIO;
                        newEx.UseFloatingPoint = true;
                        newEx.UseTimeShifting = true;
                        newEx.Decode = true;
                        newEx.SampleRate = _playlist.CurrentItem.AudioFile.SampleRate;
                        throw newEx;                        
                    }
                }
                else if (_device.DriverType == DriverType.WASAPI)
                {
                    // Create mixer stream
                    Tracing.Log("Player.Play -- Creating mixer channel (WASAPI)...");
                    _mixerChannel = MixerChannel.CreateMixerStream(_playlist.CurrentItem.AudioFile.SampleRate, 2, true, true);
                    _mixerChannel.AddChannel(_fxChannel.Handle);

                    // Start playback
                    if (!BassWasapi.BASS_WASAPI_Start())
                    {
                        // Get error
                        BASSError error = Bass.BASS_ErrorGetCode();
                        throw new Exception("Player.Play error: Error playing files in WASAPI: " + error.ToString());
                    }
                }
#endif
                // Set initial volume
                _mixerChannel.Volume = Volume;

                // Load 18-band equalizer
				//Tracing.Log("Player.Play -- Creating equalizer (Preset: " + _currentEQPreset + ")...");
                AddEQ(_currentEQPreset);

                // Check if EQ is bypassed
                if (_isEQBypassed)
                {
                    // Reset EQ
					//Tracing.Log("Player.Play -- Equalizer is bypassed; resetting EQ...");
                    ResetEQ();
                }

                // Check if the song must be looped
                if (_repeatType == RepeatType.Song)
                    _playlist.CurrentItem.Channel.SetFlags(BASSFlag.BASS_SAMPLE_LOOP, BASSFlag.BASS_SAMPLE_LOOP);

                long length = _playlist.CurrentItem.Channel.GetLength();
                SetSyncCallback(length);
                _isPlaying = true;

                // Only the DirectSound mode needs to start the main channel since it's not in decode mode.
                if (_device.DriverType == DriverType.DirectSound)
                {
                    // For iOS: This is required to update the AirPlay/remote player status
                    Base.Start();

                    // Start playback
					//Tracing.Log("Player.Play -- Starting DirectSound playback...");
                    _mixerChannel.Play(false);

                    if (startPaused)
                    {
                        if(initialPosition > 0)
                            SetPosition(initialPosition);

                        Base.Pause();
                    }

                    _isPaused = startPaused;
                }

//                StartEncode(EncoderType.OGG);
//                StartCast(new CastServerParams()
//                {
//                    Bitrate = 128,
//                    Name = "Sessions Test Server",
//                    Url = "localhost:8000",
//                    Password = "******"
//                });

                // Raise audio file finished event (if an event is subscribed)
                if (OnPlaylistIndexChanged != null)
                {
                    PlayerPlaylistIndexChangedData data = new PlayerPlaylistIndexChangedData();
                    data.IsPlaybackStopped = false;
                    data.AudioFileStarted = _playlist.CurrentItem.AudioFile;
                    data.PlaylistName = "New playlist 1";
                    data.PlaylistCount = _playlist.Items.Count;
                    data.PlaylistIndex = _playlist.CurrentItemIndex; 
                    if (Playlist.CurrentItemIndex < Playlist.Items.Count - 2)
                        data.NextAudioFile = Playlist.Items[Playlist.CurrentItemIndex + 1].AudioFile;
                    OnPlaylistIndexChanged(data);
                }
            }
            catch (Exception ex)
            {
                Tracing.Log("Player.Play error: " + ex.Message + "\n" + ex.StackTrace);
                throw;
            }
        }
示例#2
0
文件: Player.cs 项目: pascalfr/MPfm
        /// <summary>
        /// Sync callback routine used for triggering the playlist index changed event and
        /// changing the output stream sample rate if necessary.
        /// </summary>
        /// <param name="handle">Handle to the sync</param>
        /// <param name="channel">Channel handle</param>
        /// <param name="data">Data</param>
        /// <param name="user">User data</param>
        internal void PlayerSyncProc(int handle, int channel, int data, IntPtr user)
        {
            bool playbackStopped = false;
            bool playlistBackToStart = false;
            int nextPlaylistIndex = 0;

            _mixerChannel.Lock(true);
            long position = _mixerChannel.GetPosition();
//            if (_useFloatingPoint)
//                position /= 2;

            // Get remanining data in buffer
            //int buffered = mainChannel.GetData(IntPtr.Zero, (int)BASSData.BASS_DATA_AVAILABLE);

            // Check if this the last song
            if (_playlist.CurrentItemIndex == _playlist.Items.Count - 1)
            {
                // This is the end of the playlist. Check the repeat type if the playlist needs to be repeated
                if (RepeatType == RepeatType.Playlist)
                {
                    // Set flags      
                    nextPlaylistIndex = 0;
                    playlistBackToStart = true;
                }
                else
                {
                    // Set flags
                    playbackStopped = true;
                }
            }
            else
            {
                // Set flags
                nextPlaylistIndex = _playlist.CurrentItemIndex + 1;
            }

            // Calculate position offset
            long offset = 0 - (position / 2);
            if (!playbackStopped)
            {
                // Multiply by 1.5 (I don't really know why, but this works for 48000Hz and 96000Hz. Maybe a bug in BASS with FLAC files?)
                if (Playlist.CurrentItem.AudioFile.FileType == AudioFileFormat.FLAC && Playlist.Items[nextPlaylistIndex].AudioFile.SampleRate > 44100)
                    offset = (long)((float)offset * 1.5f);

                // Check if the sample rate needs to be changed (i.e. main channel sample rate different than the decoding file)
                if (!playbackStopped && _mixerChannel.SampleRate != Playlist.Items[nextPlaylistIndex].AudioFile.SampleRate)
                    _mixerChannel.SetSampleRate(Playlist.Items[nextPlaylistIndex].AudioFile.SampleRate);

                // Set position offset
                _positionOffset = offset;
            }

            _mixerChannel.Lock(false);
            RemoveSyncCallback(handle);
            
            // Check if this is the last item to play
            if (_playlist.CurrentItemIndex == _playlist.Items.Count - 1)
            {
                // This is the end of the playlist. Check the repeat type if the playlist needs to be repeated
                if (RepeatType == RepeatType.Playlist)
                    Playlist.First();
            }
            else
            {
                // Increment playlist index
                Playlist.Next();
            }

            // Check if an event is subscribed
            if (OnPlaylistIndexChanged != null)
            {
                // Create data
                PlayerPlaylistIndexChangedData eventData = new PlayerPlaylistIndexChangedData();
                eventData.IsPlaybackStopped = playbackStopped;
                eventData.PlaylistName = "New playlist 1";
                eventData.PlaylistCount = _playlist.Items.Count;
                eventData.PlaylistIndex = _playlist.CurrentItemIndex;

                // If the playback hasn't stopped, fill more event data
                if (playbackStopped)
                {
                    // Set event data
                    eventData.AudioFileStarted = null;
                    eventData.AudioFileEnded = Playlist.CurrentItem.AudioFile;

                    // Check if EQ is enabled
                    if (_isEQEnabled)
                        RemoveEQ();

                    // Dispose channels
                    _playlist.DisposeChannels();
                    _isPlaying = false;
                }
                else
                {
                    // Set event data
                    eventData.AudioFileStarted = Playlist.CurrentItem.AudioFile;
                    if (Playlist.CurrentItemIndex < Playlist.Items.Count - 2)
                        eventData.NextAudioFile = Playlist.Items[Playlist.CurrentItemIndex + 1].AudioFile;

                    // Is this the first item, and did the last song of the playlist just play?
                    if (Playlist.CurrentItemIndex == 0 && playlistBackToStart)
                    {
                        // The audio file that just finished was the last of the playlist
                        eventData.AudioFileEnded = Playlist.Items[Playlist.Items.Count - 1].AudioFile;
                    }
                    // Make sure this is not the first item
                    else if (Playlist.CurrentItemIndex > 0)
                    {
                        // The audio file that just finished was the last one
                        eventData.AudioFileEnded = Playlist.Items[Playlist.CurrentItemIndex - 1].AudioFile;
                    }
                }

                // Raise event
                OnPlaylistIndexChanged(eventData);
            }
        }