private void StopCommand() { lock (_syncRoot) { _commandRegistered.Set(); MusicStream stream = GetCurrentStream(); try { if (stream != null && !stream.IsDisposed) { Log.Debug("BASS: Stop of stream {0}.", stream.FilePath); if (Config.SoftStop && !stream.IsDisposed && !stream.IsCrossFading) { if (Config.CrossFadeIntervalMs > 0) { Log.Debug("BASS: Performing Softstop of {0}", stream.FilePath); Bass.BASS_ChannelSlideAttribute(stream.BassStream, BASSAttribute.BASS_ATTRIB_VOL, 0, Config.CrossFadeIntervalMs); // Wait until the slide is done // Sometimes the slide is causing troubles, so we wait a maximum of CrossfadeIntervals + 100 ms // Enable only if it's music playing if (g_Player.IsMusic && g_Player._currentMediaForBassEngine != g_Player.MediaType.Video && g_Player._currentMediaForBassEngine != g_Player.MediaType.TV && g_Player._currentMediaForBassEngine != g_Player.MediaType.Recording) { DateTime start = DateTime.Now; while (Bass.BASS_ChannelIsSliding(stream.BassStream, BASSAttribute.BASS_ATTRIB_VOL)) { System.Threading.Thread.Sleep(20); if ((DateTime.Now - start).TotalMilliseconds > Config.CrossFadeIntervalMs + 100) { break; } } } } } BassMix.BASS_Mixer_ChannelRemove(stream.BassStream); stream.Dispose(); } if (Config.MusicPlayer == AudioPlayer.Asio) { Log.Debug("BASS: Stopping ASIO Device"); if (BassAsio.BASS_ASIO_IsStarted() && !BassAsio.BASS_ASIO_Stop()) { Log.Error("BASS: Error freeing ASIO: {0}", BassAsio.BASS_ASIO_ErrorGetCode()); } Log.Debug("BASS: unjoin ASIO CHannels"); if (!BassAsio.BASS_ASIO_ChannelReset(false, -1, BASSASIOReset.BASS_ASIO_RESET_JOIN)) { Log.Error("BASS: Error unjoining Asio Channels: {0}", BassAsio.BASS_ASIO_ErrorGetCode()); } Log.Debug("BASS: disabling ASIO CHannels"); if (!BassAsio.BASS_ASIO_ChannelReset(false, -1, BASSASIOReset.BASS_ASIO_RESET_ENABLE)) { Log.Error("BASS: Error disabling Asio Channels: {0}", BassAsio.BASS_ASIO_ErrorGetCode()); } } if (Config.MusicPlayer == AudioPlayer.WasApi) { try { if (BassWasapi.BASS_WASAPI_IsStarted()) { Log.Debug("BASS: Stopping WASAPI Device"); if (!BassWasapi.BASS_WASAPI_Stop(true)) { Log.Error("BASS: Error stopping WASAPI Device: {0}", Bass.BASS_ErrorGetCode()); } } } catch (Exception ex) { Log.Error("BASS: Exception stopping WASAPI: {0} {1}", ex.Message, ex.StackTrace); } // Even if stopping the WASAPI device fails we need to free it to make sure // the audio device is free to be used by others try { if (!BassWasapi.BASS_WASAPI_Free()) { Log.Error("BASS: Error freeing WASAPI: {0}", Bass.BASS_ErrorGetCode()); } } catch (Exception ex) { Log.Error("BASS: Exception freeing WASAPI: {0} {1}", ex.Message, ex.StackTrace); } } if (_mixer != null) { _mixer.Dispose(); _mixer = null; } // If we did a playback of a Audio CD, release the CD, as we might have problems with other CD related functions if (_isCDDAFile) { int driveCount = BassCd.BASS_CD_GetDriveCount(); for (int i = 0; i < driveCount; i++) { BassCd.BASS_CD_Release(i); } } if (PlaybackStop != null) { PlaybackStop(this); } HandleSongEnded(); // Switching back to normal playback mode SwitchToDefaultPlaybackMode(); } catch (Exception ex) { Log.Error("BASS: Stop command caused an exception - {0}. {1}", ex.Message, ex.StackTrace); } NotifyPlaying = false; } }
/// <summary> /// Stopping Playback /// </summary> public override void Stop() { // We might have performed the Stop already, because the end of the playback list was reached // g_Player is calling the Stop a second time. Don't execute the commands in this case if (_mixer == null) { Log.Debug("BASS: Already stopped. Don't execute Stop a second time"); return; } // Execute the Stop in a separate thread, so that it doesn't block the Main UI Render thread new Thread(() => { // First deactivate Viz RenderThread, in HandleSongEnded, it's too late VizWindow.Run = false; MusicStream stream = GetCurrentStream(); try { if (stream != null && !stream.IsDisposed) { Log.Debug("BASS: Stop of stream {0}.", stream.FilePath); if (Config.SoftStop && !stream.IsDisposed && !stream.IsCrossFading) { if (Config.CrossFadeIntervalMs > 0) { Log.Debug("BASS: Performing Softstop of {0}", stream.FilePath); Bass.BASS_ChannelSlideAttribute(stream.BassStream, BASSAttribute.BASS_ATTRIB_VOL, 0, Config.CrossFadeIntervalMs); // Wait until the slide is done // Sometimes the slide is causing troubles, so we wait a maximum of CrossfadeIntervals + 100 ms // Enable only if it's music playing if (g_Player.IsMusic && g_Player._currentMediaForBassEngine != g_Player.MediaType.Video && g_Player._currentMediaForBassEngine != g_Player.MediaType.TV && g_Player._currentMediaForBassEngine != g_Player.MediaType.Recording) { DateTime start = DateTime.Now; while (Bass.BASS_ChannelIsSliding(stream.BassStream, BASSAttribute.BASS_ATTRIB_VOL)) { System.Threading.Thread.Sleep(20); if ((DateTime.Now - start).TotalMilliseconds > Config.CrossFadeIntervalMs + 100) { break; } } } } } BassMix.BASS_Mixer_ChannelRemove(stream.BassStream); stream.Dispose(); } if (Config.MusicPlayer == AudioPlayer.Asio && BassAsio.BASS_ASIO_IsStarted()) { Log.Debug("BASS: Stopping ASIO Device"); if (!BassAsio.BASS_ASIO_Stop()) { Log.Error("BASS: Error freeing ASIO: {0}", BassAsio.BASS_ASIO_ErrorGetCode()); } Log.Debug("BASS: unjoin ASIO CHannels"); if (!BassAsio.BASS_ASIO_ChannelReset(false, -1, BASSASIOReset.BASS_ASIO_RESET_JOIN)) { Log.Error("BASS: Error unjoining Asio Channels: {0}", BassAsio.BASS_ASIO_ErrorGetCode()); } Log.Debug("BASS: disabling ASIO CHannels"); if (!BassAsio.BASS_ASIO_ChannelReset(false, -1, BASSASIOReset.BASS_ASIO_RESET_ENABLE)) { Log.Error("BASS: Error disabling Asio Channels: {0}", BassAsio.BASS_ASIO_ErrorGetCode()); } } if (Config.MusicPlayer == AudioPlayer.WasApi && BassWasapi.BASS_WASAPI_IsStarted()) { try { Log.Debug("BASS: Stopping WASAPI Device"); if (!BassWasapi.BASS_WASAPI_Stop(true)) { Log.Error("BASS: Error stopping WASAPI Device: {0}", Bass.BASS_ErrorGetCode()); } if (!BassWasapi.BASS_WASAPI_Free()) { Log.Error("BASS: Error freeing WASAPI: {0}", Bass.BASS_ErrorGetCode()); } } catch (Exception ex) { Log.Error("BASS: Exception freeing WASAPI. {0} {1}", ex.Message, ex.StackTrace); } } if (_mixer != null) { _mixer.Dispose(); _mixer = null; } // If we did a playback of a Audio CD, release the CD, as we might have problems with other CD related functions if (_isCDDAFile) { int driveCount = BassCd.BASS_CD_GetDriveCount(); for (int i = 0; i < driveCount; i++) { BassCd.BASS_CD_Release(i); } } if (PlaybackStop != null) { PlaybackStop(this); } HandleSongEnded(); // Remove the Viz Window from the Main Form as it causes troubles to other plugin overlay window try { RemoveVisualizationWindow(); } catch (Exception) { Log.Error("BASS: Stop RemoveVisualizationWindow command caused an exception"); } // Switching back to normal playback mode SwitchToDefaultPlaybackMode(); } catch (Exception ex) { Log.Error("BASS: Stop command caused an exception - {0}. {1}", ex.Message, ex.StackTrace); } NotifyPlaying = false; } ) { Name = "BASS Stop" }.Start(); }
/// <summary> /// Called to start playback of next files internally, without makinbg use of g_player. /// This gives us the capability to achieve gapless playback. /// </summary> /// <param name="filePath"></param> private bool PlayInternal(string filePath) { if (filePath == string.Empty) { return false; } // Cue support if (HandleCueFile(ref filePath, true)) { return true; } _filePath = filePath; MusicStream stream = new MusicStream(filePath); if (stream.BassStream == 0) { return false; } _streams.Add(stream); if (stream.Filetype.FileMainType == FileMainType.CDTrack) { _isCDDAFile = true; } else { _isCDDAFile = false; } if (_mixer == null) { _mixer = new MixerStream(this); _mixer.MusicStreamMessage += OnMusicStreamMessage; if (!_mixer.CreateMixer(stream)) { Log.Error("BASS: Could not create Mixer. Aborting playback."); return false; } } else { if (NewMixerNeeded(stream)) { Log.Debug("BASS: New stream has different number of channels or sample rate. Need a new mixer."); // Free Mixer _mixer.Dispose(); _mixer = null; _mixer = new MixerStream(this); _mixer.MusicStreamMessage += OnMusicStreamMessage; if (!_mixer.CreateMixer(stream)) { Log.Error("BASS: Could not create Mixer. Aborting playback."); return false; } } } // Enable events, for various Playback Actions to be handled stream.MusicStreamMessage += new MusicStream.MusicStreamMessageHandler(OnMusicStreamMessage); SetCueTrackEndPosition(stream, false); // Plug in the stream into the Mixer if (!_mixer.AttachStream(stream)) { return false; } return true; }
/// <summary> /// Starts Playback of the given file /// </summary> /// <param name="filePath"></param> /// <returns></returns> public override bool Play(string filePath) { if (!_initialized) { return false; } MusicStream currentStream = GetCurrentStream(); bool result = true; Speed = 1; // Set playback Speed to normal speed try { if (currentStream != null && filePath.ToLowerInvariant().CompareTo(currentStream.FilePath.ToLowerInvariant()) == 0) { // Selected file is equal to current stream if (_state == PlayState.Paused) { // Resume paused stream currentStream.ResumePlayback(); result = Bass.BASS_Start(); if (Config.MusicPlayer == AudioPlayer.Asio) { result = BassAsio.BASS_ASIO_ChannelReset(false, 0, BASSASIOReset.BASS_ASIO_RESET_PAUSE); // Continue playback of Paused stream } else if (Config.MusicPlayer == AudioPlayer.WasApi) { BassWasapi.BASS_WASAPI_Start(); } if (result) { _state = PlayState.Playing; if (PlaybackStateChanged != null) { PlaybackStateChanged(this, PlayState.Paused, _state); } } return result; } } else { // Cue support if (HandleCueFile(ref filePath)) { return true; } } // If we're not Crossfading, we want to stop the current stream at this time if (currentStream != null && currentStream.IsPlaying) { if (!currentStream.IsCrossFading) { currentStream.FadeOutStop(); } } _state = PlayState.Init; if (filePath == string.Empty) { return result; } _filePath = filePath; MusicStream stream = new MusicStream(filePath); if (stream.BassStream == 0) { return false; } _streams.Add(stream); if (stream.Filetype.FileMainType == FileMainType.CDTrack) { _isCDDAFile = true; } else { _isCDDAFile = false; } bool playbackStarted = false; if (_mixer == null) { _mixer = new MixerStream(this); if (!_mixer.CreateMixer(stream)) { Log.Error("BASS: Could not create Mixer. Aborting playback."); return false; } // Start a StreamCopy for Visualisation purposes if (HasViz) { _streamcopy.ChannelHandle = _mixer.BassStream; _streamcopy.Start(); } } else { if (!_mixer.UpMixing) { BASS_CHANNELINFO chinfo = Bass.BASS_ChannelGetInfo(_mixer.BassStream); if (!_mixer.WasApiShared && (chinfo.freq != stream.ChannelInfo.freq || chinfo.chans != stream.ChannelInfo.chans)) { if (stream.ChannelInfo.freq != _mixer.WasApiMixedFreq || stream.ChannelInfo.chans != _mixer.WasApiMixedChans) { Log.Debug("BASS: New stream has different number of channels or sample rate. Need a new mixer."); // The new stream has a different frequency or number of channels // We need a new mixer _mixer.Dispose(); _mixer = null; _mixer = new MixerStream(this); if (!_mixer.CreateMixer(stream)) { Log.Error("BASS: Could not create Mixer. Aborting playback."); return false; } if (HasViz) { _streamcopy.ChannelHandle = _mixer.BassStream; } } } } } // Enable events, for various Playback Actions to be handled stream.MusicStreamMessage += new MusicStream.MusicStreamMessageHandler(OnMusicStreamMessage); SetCueTrackEndPosition(stream); // Plugin the stream into the Mixer result = _mixer.AttachStream(stream); if (Config.MusicPlayer == AudioPlayer.Asio && !BassAsio.BASS_ASIO_IsStarted()) { BassAsio.BASS_ASIO_Stop(); playbackStarted = BassAsio.BASS_ASIO_Start(0); } else if (Config.MusicPlayer == AudioPlayer.WasApi && !BassWasapi.BASS_WASAPI_IsStarted()) { playbackStarted = BassWasapi.BASS_WASAPI_Start(); } else { if (Bass.BASS_ChannelIsActive(_mixer.BassStream) == BASSActive.BASS_ACTIVE_PLAYING) { playbackStarted = true; } else { playbackStarted = Bass.BASS_ChannelPlay(_mixer.BassStream, false); } } if (stream.BassStream != 0 && playbackStarted) { Log.Info("BASS: playback started"); // Slide in the Stream over the Cross fade Interval stream.SlideIn(); GUIMessage msg = new GUIMessage(GUIMessage.MessageType.GUI_MSG_PLAYBACK_STARTED, 0, 0, 0, 0, 0, null); msg.Label = _filePath; GUIWindowManager.SendThreadMessage(msg); NotifyPlaying = true; NeedUpdate = true; _IsFullScreen = GUIGraphicsContext.IsFullScreenVideo; _VideoPositionX = GUIGraphicsContext.VideoWindow.Left; _VideoPositionY = GUIGraphicsContext.VideoWindow.Top; _VideoWidth = GUIGraphicsContext.VideoWindow.Width; _VideoHeight = GUIGraphicsContext.VideoWindow.Height; // Re-Add the Viswindow to the Mainform Control (It got removed on a manual Stop) SetVisualizationWindow(); SetVideoWindow(); PlayState oldState = _state; _state = PlayState.Playing; if (oldState != _state && PlaybackStateChanged != null) { PlaybackStateChanged(this, oldState, _state); } if (PlaybackStart != null) { PlaybackStart(this, stream.TotalStreamSeconds); } } else { Log.Error("BASS: Unable to play {0}. Reason: {1}.", filePath, Enum.GetName(typeof(BASSError), Bass.BASS_ErrorGetCode())); stream.Dispose(); result = false; } } catch (Exception ex) { result = false; Log.Error("BASS: Play caused an exception: {0}.", ex); } return result; }