Exemplo n.º 1
0
        /// <summary>
        /// Attach a stream to the Mixer
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public bool AttachStream(MusicStream stream)
        {
            Bass.BASS_ChannelLock(_mixer, true);

            // Set SynyPos at end of stream
            SetSyncPos(stream, 0.0);

            bool result = BassMix.BASS_Mixer_StreamAddChannel(_mixer, stream.BassStream,
                                                              BASSFlag.BASS_MIXER_NORAMPIN | BASSFlag.BASS_MIXER_BUFFER |
                                                              BASSFlag.BASS_MIXER_MATRIX | BASSFlag.BASS_MIXER_DOWNMIX |
                                                              BASSFlag.BASS_STREAM_AUTOFREE);

            if (!result)
            {
                Log.Error("BASS: Error attaching stream to mixer. {0}", Bass.BASS_ErrorGetCode());
            }

            Bass.BASS_ChannelLock(_mixer, false);

            if (result && _mixingMatrix != null)
            {
                Log.Debug("BASS: Setting mixing matrix...");
                result = BassMix.BASS_Mixer_ChannelSetMatrix(stream.BassStream, _mixingMatrix);
                if (!result)
                {
                    Log.Error("BASS: Error attaching Mixing Matrix. {0}", Bass.BASS_ErrorGetCode());
                }
            }

            return(result);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Sets a SyncPos on the mixer stream
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="timePos"></param>
        public void SetSyncPos(MusicStream stream, double timePos)
        {
            double fadeOutSeconds = Config.CrossFadeIntervalMs / 1000.0;
            double totalStreamLen = Bass.BASS_ChannelBytes2Seconds(stream.BassStream, Bass.BASS_ChannelGetLength(stream.BassStream, BASSMode.BASS_POS_BYTES));
            long   mixerPos       = Bass.BASS_ChannelGetPosition(_mixer, BASSMode.BASS_POS_BYTES | BASSMode.BASS_POS_DECODE);
            long   syncPos        = mixerPos + Bass.BASS_ChannelSeconds2Bytes(_mixer, totalStreamLen - timePos - fadeOutSeconds);

            if (_syncProc != 0)
            {
                Bass.BASS_ChannelRemoveSync(_mixer, _syncProc);
            }

            // We might have stored the pinned object already, because we are skipping
            // Only store object it, when it doesn't exist
            if (!_pinnedObjects.ContainsKey(stream.BassStream))
            {
                Log.Debug("BASS: Updating Dictionary for GCHandle for stream {0}", stream.BassStream);
                // Add the pinned object to the global dictionary, so that we can free it later in the Sync End Proc
                _pinnedObjects.Add(stream.BassStream, GCHandle.Alloc(stream));
            }

            _syncProc = Bass.BASS_ChannelSetSync(_mixer,
                                                 BASSSync.BASS_SYNC_ONETIME | BASSSync.BASS_SYNC_POS | BASSSync.BASS_SYNC_MIXTIME,
                                                 syncPos, _playbackEndProcDelegate,
                                                 new IntPtr(stream.BassStream));
        }
Exemplo n.º 3
0
        /// <summary>
        /// Sets a SyncPos on the mixer stream
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="timePos"></param>
        public void SetSyncPos(MusicStream stream, double timePos)
        {
            double fadeOutSeconds = Config.CrossFadeIntervalMs / 1000.0;
            double totalStreamLen = Bass.BASS_ChannelBytes2Seconds(stream.BassStream, Bass.BASS_ChannelGetLength(stream.BassStream, BASSMode.BASS_POS_BYTES));
            long   mixerPos       = Bass.BASS_ChannelGetPosition(_mixer, BASSMode.BASS_POS_BYTES | BASSMode.BASS_POS_DECODE);
            long   syncPos        = mixerPos + Bass.BASS_ChannelSeconds2Bytes(_mixer, totalStreamLen - timePos - fadeOutSeconds);

            if (_syncProc != 0)
            {
                Bass.BASS_ChannelRemoveSync(_mixer, _syncProc);
            }

            GCHandle pFilePath = GCHandle.Alloc(stream);

            _syncProc = Bass.BASS_ChannelSetSync(_mixer,
                                                 BASSSync.BASS_SYNC_ONETIME | BASSSync.BASS_SYNC_POS | BASSSync.BASS_SYNC_MIXTIME,
                                                 syncPos, _playbackEndProcDelegate,
                                                 GCHandle.ToIntPtr(pFilePath));
        }
Exemplo n.º 4
0
    /// <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;
    }
Exemplo n.º 5
0
 /// <summary>
 /// Sets the End Position for the CUE Track
 /// </summary>
 /// <param name="stream"></param>
 private void SetCueTrackEndPosition(MusicStream stream, bool endOnly)
 {
   if (_currentCueSheet != null)
   {
     stream.SetCueTrackEndPos(_cueTrackStartPos, _cueTrackEndPos, endOnly);
   }
 }
Exemplo n.º 6
0
        /// <summary>
        /// Create a mixer using the stream attributes
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public bool CreateMixer(MusicStream stream)
        {
            Log.Debug("BASS: ---------------------------------------------");
            Log.Debug("BASS: Creating BASS mixer stream");

            bool result = false;

            BASSFlag mixerFlags = BASSFlag.BASS_MIXER_NONSTOP | BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_MIXER_NORAMPIN;

            if (Config.MusicPlayer == AudioPlayer.Asio || Config.MusicPlayer == AudioPlayer.WasApi)
            {
                mixerFlags |= BASSFlag.BASS_STREAM_DECODE;
            }

            int outputChannels = _bassPlayer.DeviceChannels;

            _mixingMatrix = null;

            // See, if we need Upmixing
            if (outputChannels > stream.ChannelInfo.chans)
            {
                Log.Debug("BASS: Found more output channels ({0}) than input channels ({1}). Check for upmixing.", outputChannels,
                          stream.ChannelInfo.chans);
                _mixingMatrix = CreateMixingMatrix(stream.ChannelInfo.chans);
                if (_mixingMatrix != null)
                {
                    outputChannels = Math.Min(_mixingMatrix.GetLength(0), outputChannels);
                    _upmixing      = true;
                }
                else
                {
                    outputChannels = stream.ChannelInfo.chans;
                }
            }
            else if (outputChannels < stream.ChannelInfo.chans)
            {
                // Downmix to Stereo
                Log.Debug("BASS: Found more input channels ({0}) than output channels ({1}). Downmix.", stream.ChannelInfo.chans,
                          outputChannels);
                outputChannels = Math.Min(outputChannels, 2);
            }

            Log.Debug("BASS: Creating {0} channel mixer with sample rate of {1}", outputChannels, stream.ChannelInfo.freq);
            _mixer = BassMix.BASS_Mixer_StreamCreate(stream.ChannelInfo.freq, outputChannels, mixerFlags);
            if (_mixer == 0)
            {
                Log.Error("BASS: Unable to create Mixer.  Reason: {0}.",
                          Enum.GetName(typeof(BASSError), Bass.BASS_ErrorGetCode()));
                return(false);
            }

            switch (Config.MusicPlayer)
            {
            case AudioPlayer.Bass:
            case AudioPlayer.DShow:

                if (!Bass.BASS_ChannelPlay(_mixer, false))
                {
                    Log.Error("BASS: Unable to start Mixer.  Reason: {0}.", Enum.GetName(typeof(BASSError), Bass.BASS_ErrorGetCode()));
                    return(false);
                }

                result = true;

                break;

            case AudioPlayer.Asio:

                Log.Info("BASS: Initialising ASIO device");

                if (BassAsio.BASS_ASIO_IsStarted() && !BassAsio.BASS_ASIO_Stop())
                {
                    Log.Error("BASS: Error stopping Asio Device: {0}", BassAsio.BASS_ASIO_ErrorGetCode());
                }

                // Disable and Unjoin all the 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 (!BassAsio.BASS_ASIO_ChannelReset(false, -1, BASSASIOReset.BASS_ASIO_RESET_JOIN))
                {
                    Log.Error("BASS: Error unjoining Asio Channels: {0}", BassAsio.BASS_ASIO_ErrorGetCode());
                }

                _asioProc = new ASIOPROC(AsioCallback);

                BassAsio.BASS_ASIO_ChannelSetVolume(false, -1, (float)Config.StreamVolume / 100f);

                // enable 1st output channel...(0=first)
                Log.Debug("BASS: Joining Asio Channel #{0}", "0");
                BassAsio.BASS_ASIO_ChannelEnable(false, 0, _asioProc, new IntPtr(_mixer));

                // and join the next channels to it
                int numChannels = Math.Max(stream.ChannelInfo.chans, outputChannels);
                for (int i = 1; i < numChannels; i++)
                {
                    Log.Debug("BASS: Joining Asio Channel #{0}", i);
                    BassAsio.BASS_ASIO_ChannelJoin(false, i, 0);
                }

                // since we joined the channels, the next commands will apply to all channles joined
                // so setting the values to the first channels changes them all automatically
                // set the source format (float, as the decoding channel is)
                if (!BassAsio.BASS_ASIO_ChannelSetFormat(false, 0, BASSASIOFormat.BASS_ASIO_FORMAT_FLOAT))
                {
                    Log.Error("BASS: Error setting Asio Sample Format: {0}", BassAsio.BASS_ASIO_ErrorGetCode());
                }

                // set the source rate
                Log.Debug("BASS: Set sample rate to {0}", stream.ChannelInfo.freq);
                if (!BassAsio.BASS_ASIO_ChannelSetRate(false, 0, (double)stream.ChannelInfo.freq))
                {
                    Log.Error("BASS: Error setting Asio Channel Samplerate: {0}", BassAsio.BASS_ASIO_ErrorGetCode());
                }

                // try to set the device rate too (saves resampling)
                if (!BassAsio.BASS_ASIO_SetRate((double)stream.ChannelInfo.freq))
                {
                    Log.Error("BASS: Error setting Asio Samplerate: {0}", BassAsio.BASS_ASIO_ErrorGetCode());
                }

                // and start playing it...start output using default buffer/latency
                if (!BassAsio.BASS_ASIO_Start(0))
                {
                    Log.Error("BASS: Error starting Asio playback: {0}", BassAsio.BASS_ASIO_ErrorGetCode());
                }
                Log.Info("BASS: Finished initialising ASIO device");
                result = true;

                break;

            case AudioPlayer.WasApi:

                Log.Info("BASS: Initialising WASAPI device");

                try
                {
                    BassWasapi.BASS_WASAPI_Free();
                    Log.Debug("BASS: Freed WASAPI device");
                }
                catch (Exception ex)
                {
                    Log.Error("BASS: Exception freeing WASAPI. {0} {1}", ex.Message, ex.StackTrace);
                }

                BASSWASAPIInit initFlags = BASSWASAPIInit.BASS_WASAPI_AUTOFORMAT;

                _wasapiProc = new WASAPIPROC(WasApiCallback);

                bool wasApiExclusiveSupported = true;

                // Check if we have an uneven number of channels
                var chkChannels = outputChannels % 2;
                if (chkChannels == 1)
                {
                    Log.Warn("BASS: Found uneven number of channels {0}. increase output channels.", outputChannels);
                    outputChannels++;                 // increase the number of output channels
                    wasApiExclusiveSupported = false; // And indicate that we need a new mixer
                }

                // Handle the special cases of 3.0, 4.0 and 5.0 files being played on a 5.1 or 6.1 device
                if (outputChannels == 3) // a 3.0 file
                {
                    Log.Info("BASS: Found a 3 channel file. Set upmixing with LFE, LR, RR set to silent");
                    _mixingMatrix            = CreateThreeDotZeroUpMixMatrix();
                    outputChannels           = _bassPlayer.DeviceChannels; // WASAPI device should be initialised with all channels active
                    wasApiExclusiveSupported = false;                      // And indicate that we need a new mixer
                }
                else if (outputChannels == 4)                              // a 4.0 file
                {
                    Log.Info("BASS: Found a 4 channel file. Set upmixing with Center and LFE set to silent");
                    _mixingMatrix            = CreateFourDotZeroUpMixMatrix();
                    outputChannels           = _bassPlayer.DeviceChannels; // WASAPI device should be initialised with all channels active
                    wasApiExclusiveSupported = false;                      // And indicate that we need a new mixer
                }
                else if (outputChannels == 5)                              // a 5.0 file
                {
                    Log.Info("BASS: Found a 5 channel file. Set upmixing with LFE set to silent");
                    _mixingMatrix            = CreateFiveDotZeroUpMixMatrix();
                    outputChannels           = _bassPlayer.DeviceChannels; // WASAPI device should be initialised with all channels active
                    wasApiExclusiveSupported = false;                      // And indicate that we need a new mixer
                }

                // If Exclusive mode is used, check, if that would be supported, otherwise init in shared mode
                if (Config.WasApiExclusiveMode)
                {
                    initFlags        |= BASSWASAPIInit.BASS_WASAPI_EXCLUSIVE;
                    _wasapiShared     = false;
                    _wasapiMixedChans = 0;
                    _wasapiMixedFreq  = 0;

                    BASSWASAPIFormat wasapiFormat = BassWasapi.BASS_WASAPI_CheckFormat(_bassPlayer.DeviceNumber,
                                                                                       stream.ChannelInfo.freq,
                                                                                       outputChannels,
                                                                                       BASSWASAPIInit.BASS_WASAPI_EXCLUSIVE);
                    if (wasapiFormat == BASSWASAPIFormat.BASS_WASAPI_FORMAT_UNKNOWN)
                    {
                        Log.Warn("BASS: WASAPI exclusive mode not directly supported. Let BASS WASAPI choose better mode.");
                        wasApiExclusiveSupported = false;
                    }
                }
                else
                {
                    Log.Debug("BASS: Init WASAPI shared mode with Event driven system enabled.");
                    initFlags |= BASSWASAPIInit.BASS_WASAPI_SHARED | BASSWASAPIInit.BASS_WASAPI_EVENT;

                    // In case of WASAPI Shared mode we need to setup the mixer to use the same sample rate as set in
                    // the Windows Mixer, otherwise we wioll have increased playback speed
                    BASS_WASAPI_DEVICEINFO devInfo = BassWasapi.BASS_WASAPI_GetDeviceInfo(_bassPlayer.DeviceNumber);
                    Log.Debug("BASS: Creating {0} channel mixer for frequency {1}", devInfo.mixchans, devInfo.mixfreq);
                    _mixer = BassMix.BASS_Mixer_StreamCreate(devInfo.mixfreq, devInfo.mixchans, mixerFlags);
                    if (_mixer == 0)
                    {
                        Log.Error("BASS: Unable to create Mixer.  Reason: {0}.",
                                  Enum.GetName(typeof(BASSError), Bass.BASS_ErrorGetCode()));
                        return(false);
                    }
                    _wasapiShared = true;
                }

                Log.Debug("BASS: Try to init WASAPI with a Frequency of {0} and {1} channels", stream.ChannelInfo.freq, outputChannels);

                if (BassWasapi.BASS_WASAPI_Init(_bassPlayer.DeviceNumber, stream.ChannelInfo.freq, outputChannels,
                                                initFlags | BASSWASAPIInit.BASS_WASAPI_BUFFER, Convert.ToSingle(Config.BufferingMs / 1000.0), 0f, _wasapiProc, IntPtr.Zero))
                {
                    BASS_WASAPI_INFO wasapiInfo = BassWasapi.BASS_WASAPI_GetInfo();

                    Log.Debug("BASS: ---------------------------------------------");
                    Log.Debug("BASS: Buffer Length: {0}", wasapiInfo.buflen);
                    Log.Debug("BASS: Channels: {0}", wasapiInfo.chans);
                    Log.Debug("BASS: Frequency: {0}", wasapiInfo.freq);
                    Log.Debug("BASS: Format: {0}", wasapiInfo.format.ToString());
                    Log.Debug("BASS: InitFlags: {0}", wasapiInfo.initflags.ToString());
                    Log.Debug("BASS: Exclusive: {0}", wasapiInfo.IsExclusive.ToString());
                    Log.Debug("BASS: ---------------------------------------------");
                    Log.Info("BASS: WASAPI Device successfully initialised");

                    // Now we need to check, if WASAPI decided to switch to a different mode
                    if (Config.WasApiExclusiveMode && !wasApiExclusiveSupported)
                    {
                        // Recreate Mixer with new value
                        Log.Debug("BASS: Creating new {0} channel mixer for frequency {1}", wasapiInfo.chans, wasapiInfo.freq);
                        _mixer = BassMix.BASS_Mixer_StreamCreate(wasapiInfo.freq, wasapiInfo.chans, mixerFlags);
                        if (_mixer == 0)
                        {
                            Log.Error("BASS: Unable to create Mixer.  Reason: {0}.",
                                      Enum.GetName(typeof(BASSError), Bass.BASS_ErrorGetCode()));
                            return(false);
                        }
                    }

                    BassWasapi.BASS_WASAPI_SetVolume(BASSWASAPIVolume.BASS_WASAPI_CURVE_DB, (float)Config.StreamVolume / 100f);
                    BassWasapi.BASS_WASAPI_Start();
                    result = true;
                }
                else
                {
                    Log.Error("BASS: Couldn't init WASAPI device. Error: {0}", Enum.GetName(typeof(BASSError), Bass.BASS_ErrorGetCode()));
                }
                break;
            }

            if (result)
            {
                Log.Debug("BASS: Successfully created BASS Mixer stream");
            }
            return(result);
        }
Exemplo n.º 7
0
        /// <summary>
        /// End of Playback for a stream has been signaled
        /// Send event to Bass player to start playback of next song
        /// </summary>
        /// <param name="handle"></param>
        /// <param name="stream"></param>
        /// <param name="data"></param>
        /// <param name="userData"></param>
        private void PlaybackEndProc(int handle, int stream, int data, IntPtr userData)
        {
            try
            {
                MusicStream musicstream;
                // Get the GC handle of the pinned object
                try
                {
                    musicstream = (MusicStream)_pinnedObjects[userData.ToInt32()].Target;
                }
                catch (KeyNotFoundException ex)
                {
                    Log.Error("BASS: GCHandle of Musicstream not found in Dictionary {0} {1}", userData.ToInt32(), ex.Message);
                    return;
                }

                Log.Debug("BASS: End of Song {0}", musicstream.FilePath);

                // We need to find out, if the nextsongs sample rate and / or number of channels are different to the one just ended
                // If this is the case we need a new mixer and the OnMusicStreamMessage needs to be invoked in a thread to avoid crashes.
                // In order to have gapless playback, it needs to be invoked in sync.
                MusicStream            nextStream = null;
                Playlists.PlayListItem nextSong   = Playlists.PlayListPlayer.SingletonPlayer.GetNextItem();
                MusicStream._fileType = Utils.GetFileType(musicstream.FilePath);
                if (nextSong != null && MusicStream._fileType.FileMainType != FileMainType.WebStream)
                {
                    nextStream = new MusicStream(nextSong.FileName, true);
                }
                else if (MusicStream._fileType.FileMainType == FileMainType.WebStream)
                {
                    if (MusicStreamMessage != null)
                    {
                        MusicStreamMessage(musicstream, MusicStream.StreamAction.InternetStreamChanged);
                        return;
                    }
                }

                bool newMixerNeeded = false;
                if (nextStream != null && nextStream.BassStream != 0)
                {
                    if (_bassPlayer.NewMixerNeeded(nextStream))
                    {
                        newMixerNeeded = true;
                    }
                    nextStream.Dispose();
                }

                if (newMixerNeeded)
                {
                    if (Config.MusicPlayer == AudioPlayer.WasApi && BassWasapi.BASS_WASAPI_IsStarted())
                    {
                        BassWasapi.BASS_WASAPI_Stop(true);
                    }

                    // Unplug the Source channel from the mixer
                    Log.Debug("BASS: Unplugging source channel from Mixer.");
                    BassMix.BASS_Mixer_ChannelRemove(musicstream.BassStream);

                    // invoke a thread because we need a new mixer
                    Log.Debug("BASS: Next song needs a new mixer.");

                    new Thread(() =>
                    {
                        if (MusicStreamMessage != null)
                        {
                            MusicStreamMessage(musicstream, MusicStream.StreamAction.Crossfading);
                        }
                    })
                    {
                        Name = "BASS"
                    }.Start();
                }
                else
                {
                    if (MusicStreamMessage != null)
                    {
                        MusicStreamMessage(musicstream, MusicStream.StreamAction.Crossfading);
                    }
                }
            }
            catch (AccessViolationException ex)
            {
                Log.Error("BASS: Caught AccessViolationException in Playback End Proc {0}", ex.Message);
            }
        }
Exemplo n.º 8
0
    /// <summary>
    /// This Message is sent from a MusicStream
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="action"></param>
    private void OnMusicStreamMessage(object sender, MusicStream.StreamAction action)
    {
      if (sender == null)
      {
        return;
      }

      MusicStream musicStream = (MusicStream)sender;

      // we want gapless playback for external controller (aka UPnP Controller)
      // therefore we use the same mechanism
      if (g_Player.ExternalController && action == MusicStream.StreamAction.InternetStreamChanged)
        action = MusicStream.StreamAction.Crossfading;

      switch (action)
      {
        case MusicStream.StreamAction.Ended:
          break;

        case MusicStream.StreamAction.Crossfading:

          string nextSong = Playlists.PlayListPlayer.SingletonPlayer.GetNextSong();

          if (nextSong != string.Empty)
          {
            g_Player.OnChanged(nextSong);
            PlayInternal(nextSong);
            g_Player.currentMedia = g_Player.MediaType.Music;
            g_Player.currentFilePlaying = nextSong;
            g_Player.OnStarted();
            NotifyPlaying = true;
          }
          else
          {
            Log.Debug("BASS: Reached end of playlist.");
            g_Player.OnStopped();
            Stop();
          }
          break;

        case MusicStream.StreamAction.InternetStreamChanged:

          _tagInfo = musicStream.StreamTags;
          if (InternetStreamSongChanged != null)
          {
            InternetStreamSongChanged(this);
          }
          break;

        case MusicStream.StreamAction.Freed:
          {
            _mixer.FreeGcHandle(musicStream.BassStream);
            musicStream.Dispose();
          }
          break;

        case MusicStream.StreamAction.Disposed:
          // Remove the stream from the active streams and free it
          lock (_streams)
          {
            if (_streams.Contains(musicStream))
            {
              _streams.Remove(musicStream);
            }
          }
          break;
      }
    }
Exemplo n.º 9
0
    /// <summary>
    /// End of Playback for a stream has been signaled
    /// Send event to Bass player to start playback of next song
    /// </summary>
    /// <param name="handle"></param>
    /// <param name="stream"></param>
    /// <param name="data"></param>
    /// <param name="userData"></param>
    private void PlaybackEndProc(int handle, int stream, int data, IntPtr userData)
    {
      try
      {
        GCHandle gch = GCHandle.FromIntPtr(userData);
        MusicStream musicstream = (MusicStream)gch.Target;

        Log.Debug("BASS: End of Song {0}", musicstream.FilePath);

        // We need to find out, if the nextsongs sample rate and / or number of channels are different to the one just ended
        // If this is the case we need a new mixer and the OnMusicStreamMessage needs to be invoked in a thread to avoid crashes.
        // In order to have gapless playback, it needs to be invoked in sync.
        MusicStream nextStream = null;
        Playlists.PlayListItem nextSong = Playlists.PlayListPlayer.SingletonPlayer.GetNextItem();
        MusicStream._fileType = Utils.GetFileType(musicstream.FilePath);
        if (nextSong != null && MusicStream._fileType.FileMainType != FileMainType.WebStream)
        {
          nextStream = new MusicStream(nextSong.FileName, true);
        }
        else if (MusicStream._fileType.FileMainType == FileMainType.WebStream)
        {
          if (MusicStreamMessage != null)
          {
            MusicStreamMessage(musicstream, MusicStream.StreamAction.InternetStreamChanged);
            return;
          }
        }

        bool newMixerNeeded = false;
        if (nextStream != null && nextStream.BassStream != 0)
        {
          if (_bassPlayer.NewMixerNeeded(nextStream))
          {
            newMixerNeeded = true;
          }
          nextStream.Dispose();
        }

        if (newMixerNeeded)
        {
          if (Config.MusicPlayer == AudioPlayer.WasApi && BassWasapi.BASS_WASAPI_IsStarted())
          {
            BassWasapi.BASS_WASAPI_Stop(true);
          }

          // Unplug the Source channel from the mixer
          Log.Debug("BASS: Unplugging source channel from Mixer.");
          BassMix.BASS_Mixer_ChannelRemove(musicstream.BassStream);

          // invoke a thread because we need a new mixer
          Log.Debug("BASS: Next song needs a new mixer.");
          
          new Thread(() =>
            {
              if (MusicStreamMessage != null)
              {
                MusicStreamMessage(musicstream, MusicStream.StreamAction.Crossfading);
              }
            }) { Name = "BASS" }.Start();
        }
        else
        {
          if (MusicStreamMessage != null)
          {
            MusicStreamMessage(musicstream, MusicStream.StreamAction.Crossfading);
          }
        }
      }
      catch (AccessViolationException)
      {
        Log.Error("BASS: Caught AccessViolationException in Playback End Proc");
      }
    }
Exemplo n.º 10
0
    /// <summary>
    /// Sets a SyncPos on the mixer stream
    /// </summary>
    /// <param name="stream"></param>
    /// <param name="timePos"></param>
    public void SetSyncPos(MusicStream stream, double timePos)
    {
      double fadeOutSeconds = Config.CrossFadeIntervalMs / 1000.0;
      double totalStreamLen = Bass.BASS_ChannelBytes2Seconds(stream.BassStream, Bass.BASS_ChannelGetLength(stream.BassStream, BASSMode.BASS_POS_BYTES));
      long mixerPos = Bass.BASS_ChannelGetPosition(_mixer, BASSMode.BASS_POS_BYTES | BASSMode.BASS_POS_DECODE);
      long syncPos = mixerPos + Bass.BASS_ChannelSeconds2Bytes(_mixer, totalStreamLen - timePos - fadeOutSeconds);

      if (_syncProc != 0)
      {
        Bass.BASS_ChannelRemoveSync(_mixer, _syncProc);
      }

      GCHandle pFilePath = GCHandle.Alloc(stream);

      _syncProc = Bass.BASS_ChannelSetSync(_mixer,
        BASSSync.BASS_SYNC_ONETIME | BASSSync.BASS_SYNC_POS | BASSSync.BASS_SYNC_MIXTIME,
        syncPos, _playbackEndProcDelegate,
        GCHandle.ToIntPtr(pFilePath));
    }
Exemplo n.º 11
0
    /// <summary>
    /// Attach a stream to the Mixer
    /// </summary>
    /// <param name="stream"></param>
    /// <returns></returns>
    public bool AttachStream(MusicStream stream)
    {
      Bass.BASS_ChannelLock(_mixer, true);

      // Set SynyPos at end of stream
      SetSyncPos(stream, 0.0);

      bool result = BassMix.BASS_Mixer_StreamAddChannel(_mixer, stream.BassStream,
                                        BASSFlag.BASS_MIXER_NORAMPIN | BASSFlag.BASS_MIXER_BUFFER |
                                        BASSFlag.BASS_MIXER_MATRIX | BASSFlag.BASS_MIXER_DOWNMIX |
                                        BASSFlag.BASS_STREAM_AUTOFREE);

      if (!result)
      {
        Log.Error("BASS: Error attaching stream to mixer. {0}", Bass.BASS_ErrorGetCode());
      }

      Bass.BASS_ChannelLock(_mixer, false);

      if (result && _mixingMatrix != null)
      {
        Log.Debug("BASS: Setting mixing matrix...");
        result = BassMix.BASS_Mixer_ChannelSetMatrix(stream.BassStream, _mixingMatrix);
        if (!result)
        {
          Log.Error("BASS: Error attaching Mixing Matrix. {0}", Bass.BASS_ErrorGetCode());
        }
      }

      return result;
    }
Exemplo n.º 12
0
    /// <summary>
    /// This Message is sent from a MusicStream
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="action"></param>
    void OnMusicStreamMessage(object sender, MusicStream.StreamAction action)
    {
      if (sender == null)
      {
        return;
      }
      MusicStream musicStream = (MusicStream)sender;

      switch (action)
      {
        case MusicStream.StreamAction.Ended:
          if (TrackPlaybackCompleted != null)
          {
            TrackPlaybackCompleted(this, musicStream.FilePath);
          }

          BassMix.BASS_Mixer_ChannelRemove(musicStream.BassStream);
          musicStream.Dispose();

          // Check, if PlaylistPlayer has to offer more files)
          if (Playlists.PlayListPlayer.SingletonPlayer.GetNext() == string.Empty)
          {
            MusicStream currentStream = GetCurrentStream();
            if (currentStream == null || !currentStream.IsPlaying)
            {
              Log.Debug("BASS: Reached end of playlist.");
              Stop();
            }
          }
          break;

        case MusicStream.StreamAction.InternetStreamChanged:
          if (InternetStreamSongChanged != null)
          {
            InternetStreamSongChanged(this);
          }
          break;

        case MusicStream.StreamAction.Disposed:
          // Remove the stream from the active streams and free it
          lock (_streams)
          {
            if (_streams.Contains(musicStream))
            {
              _streams.Remove(musicStream);
            }
          }
          break;
      }
    }
Exemplo n.º 13
0
    /// <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;
    }
Exemplo n.º 14
0
    /// <summary>
    /// Checks, if a new Mixer would be needed, because of changes in Sample Rate or number of channels
    /// </summary>
    /// <param name="stream"></param>
    /// <returns></returns>
    public bool NewMixerNeeded(MusicStream stream)
    {
      if (!_mixer.UpMixing)
      {
        BASS_CHANNELINFO chinfo = Bass.BASS_ChannelGetInfo(_mixer.BassStream);
        if (!_mixer.WasApiShared &&
            (chinfo.freq != stream.ChannelInfo.freq || (chinfo.chans != stream.ChannelInfo.chans && stream.ChannelInfo.chans != 1)))
        {
          if (stream.ChannelInfo.freq != _mixer.WasApiMixedFreq ||
              stream.ChannelInfo.chans != _mixer.WasApiMixedChans)
          {
            return true;
          }
        }
      }

      return false;
    }
Exemplo n.º 15
0
    /// <summary>
    /// Create a mixer using the stream attributes
    /// </summary>
    /// <param name="stream"></param>
    /// <returns></returns>
    public bool CreateMixer(MusicStream stream)
    {
      Log.Debug("BASS: ---------------------------------------------");
      Log.Debug("BASS: Creating BASS mixer stream");

      bool result = false;

      BASSFlag mixerFlags = BASSFlag.BASS_MIXER_NONSTOP | BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_MIXER_NORAMPIN;

      if (Config.MusicPlayer == AudioPlayer.Asio || Config.MusicPlayer == AudioPlayer.WasApi)
      {
        mixerFlags |= BASSFlag.BASS_STREAM_DECODE;
      }

      int outputChannels = _bassPlayer.DeviceChannels;
      _mixingMatrix = null;

      // See, if we need Upmixing
      if (outputChannels > stream.ChannelInfo.chans)
      {
        Log.Debug("BASS: Found more output channels ({0}) than input channels ({1}). Check for upmixing.", outputChannels,
                 stream.ChannelInfo.chans);
        _mixingMatrix = CreateMixingMatrix(stream.ChannelInfo.chans);
        if (_mixingMatrix != null)
        {
          outputChannels = Math.Min(_mixingMatrix.GetLength(0), outputChannels);
          _upmixing = true;
        }
        else
        {
          outputChannels = stream.ChannelInfo.chans;
        }
      }
      else if (outputChannels < stream.ChannelInfo.chans)
      {
        // Downmix to Stereo
        Log.Debug("BASS: Found more input channels ({0}) than output channels ({1}). Downmix.", stream.ChannelInfo.chans,
                 outputChannels);
        outputChannels = Math.Min(outputChannels, 2);
      }

      Log.Debug("BASS: Creating {0} channel mixer with sample rate of {1}", outputChannels, stream.ChannelInfo.freq);
      _mixer = BassMix.BASS_Mixer_StreamCreate(stream.ChannelInfo.freq, outputChannels, mixerFlags);
      if (_mixer == 0)
      {
        Log.Error("BASS: Unable to create Mixer.  Reason: {0}.",
                  Enum.GetName(typeof(BASSError), Bass.BASS_ErrorGetCode()));
        return false;
      }

      switch (Config.MusicPlayer)
      {
        case AudioPlayer.Bass:
        case AudioPlayer.DShow:

          if (!Bass.BASS_ChannelPlay(_mixer, false))
          {
            Log.Error("BASS: Unable to start Mixer.  Reason: {0}.", Enum.GetName(typeof(BASSError), Bass.BASS_ErrorGetCode()));
            return false;
          }

          result = true;

          break;

        case AudioPlayer.Asio:

          Log.Info("BASS: Initialising ASIO device");

          if (BassAsio.BASS_ASIO_IsStarted() && !BassAsio.BASS_ASIO_Stop())
          {
            Log.Error("BASS: Error stopping Asio Device: {0}", BassAsio.BASS_ASIO_ErrorGetCode());
          }

          // Disable and Unjoin all the 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 (!BassAsio.BASS_ASIO_ChannelReset(false, -1, BASSASIOReset.BASS_ASIO_RESET_JOIN))
          {
            Log.Error("BASS: Error unjoining Asio Channels: {0}", BassAsio.BASS_ASIO_ErrorGetCode());
          }

          _asioProc = new ASIOPROC(AsioCallback);

          BassAsio.BASS_ASIO_ChannelSetVolume(false, -1, (float)Config.StreamVolume / 100f);

          // enable 1st output channel...(0=first)
          Log.Debug("BASS: Joining Asio Channel #{0}", "0");
          BassAsio.BASS_ASIO_ChannelEnable(false, 0, _asioProc, new IntPtr(_mixer));

          // and join the next channels to it
          int numChannels = Math.Max(stream.ChannelInfo.chans, outputChannels);
          for (int i = 1; i < numChannels; i++)
          {
            Log.Debug("BASS: Joining Asio Channel #{0}", i);
            BassAsio.BASS_ASIO_ChannelJoin(false, i, 0);
          }

          // since we joined the channels, the next commands will apply to all channles joined
          // so setting the values to the first channels changes them all automatically
          // set the source format (float, as the decoding channel is)
          if (!BassAsio.BASS_ASIO_ChannelSetFormat(false, 0, BASSASIOFormat.BASS_ASIO_FORMAT_FLOAT))
          {
            Log.Error("BASS: Error setting Asio Sample Format: {0}", BassAsio.BASS_ASIO_ErrorGetCode());
          }

          // set the source rate
          Log.Debug("BASS: Set sample rate to {0}", stream.ChannelInfo.freq);
          if (!BassAsio.BASS_ASIO_ChannelSetRate(false, 0, (double)stream.ChannelInfo.freq))
          {
            Log.Error("BASS: Error setting Asio Channel Samplerate: {0}", BassAsio.BASS_ASIO_ErrorGetCode());
          }

          // try to set the device rate too (saves resampling)
          if (!BassAsio.BASS_ASIO_SetRate((double)stream.ChannelInfo.freq))
          {
            Log.Error("BASS: Error setting Asio Samplerate: {0}", BassAsio.BASS_ASIO_ErrorGetCode());
          }

          // and start playing it...start output using default buffer/latency
          if (!BassAsio.BASS_ASIO_Start(0))
          {
            Log.Error("BASS: Error starting Asio playback: {0}", BassAsio.BASS_ASIO_ErrorGetCode());
          }
          Log.Info("BASS: Finished initialising ASIO device");
          result = true;

          break;

        case AudioPlayer.WasApi:

          Log.Info("BASS: Initialising WASAPI device");

          try
          {
            BassWasapi.BASS_WASAPI_Free();
            Log.Debug("BASS: Freed WASAPI device");
          }
          catch (Exception ex)
          {
            Log.Error("BASS: Exception freeing WASAPI. {0} {1}", ex.Message, ex.StackTrace);
          }

          BASSWASAPIInit initFlags = BASSWASAPIInit.BASS_WASAPI_AUTOFORMAT;

          _wasapiProc = new WASAPIPROC(WasApiCallback);

          bool wasApiExclusiveSupported = true;

          // Check if we have an uneven number of channels
          var chkChannels = outputChannels % 2;
          if (chkChannels == 1)
          {
            Log.Warn("BASS: Found uneven number of channels {0}. increase output channels.", outputChannels);
            outputChannels++; // increase the number of output channels
            wasApiExclusiveSupported = false; // And indicate that we need a new mixer
          }

          // Handle the special cases of 3.0, 4.0 and 5.0 files being played on a 5.1 or 6.1 device
          if (outputChannels == 3)  // a 3.0 file
          {
            Log.Info("BASS: Found a 3 channel file. Set upmixing with LFE, LR, RR set to silent");
            _mixingMatrix = CreateThreeDotZeroUpMixMatrix();
            outputChannels = _bassPlayer.DeviceChannels;   // WASAPI device should be initialised with all channels active
            wasApiExclusiveSupported = false; // And indicate that we need a new mixer
          }
          else if (outputChannels == 4)  // a 4.0 file
          {
            Log.Info("BASS: Found a 4 channel file. Set upmixing with Center and LFE set to silent");
            _mixingMatrix = CreateFourDotZeroUpMixMatrix();
            outputChannels = _bassPlayer.DeviceChannels;   // WASAPI device should be initialised with all channels active
            wasApiExclusiveSupported = false; // And indicate that we need a new mixer
          }
          else if (outputChannels == 5)  // a 5.0 file
          {
            Log.Info("BASS: Found a 5 channel file. Set upmixing with LFE set to silent");
            _mixingMatrix = CreateFiveDotZeroUpMixMatrix();
            outputChannels = _bassPlayer.DeviceChannels;   // WASAPI device should be initialised with all channels active
            wasApiExclusiveSupported = false; // And indicate that we need a new mixer
          }

          // If Exclusive mode is used, check, if that would be supported, otherwise init in shared mode
          if (Config.WasApiExclusiveMode)
          {
            initFlags |= BASSWASAPIInit.BASS_WASAPI_EXCLUSIVE;
            _wasapiShared = false;
            _wasapiMixedChans = 0;
            _wasapiMixedFreq = 0;

            BASSWASAPIFormat wasapiFormat = BassWasapi.BASS_WASAPI_CheckFormat(_bassPlayer.DeviceNumber,
                                                                               stream.ChannelInfo.freq,
                                                                               outputChannels,
                                                                               BASSWASAPIInit.BASS_WASAPI_EXCLUSIVE);
            if (wasapiFormat == BASSWASAPIFormat.BASS_WASAPI_FORMAT_UNKNOWN)
            {
              Log.Warn("BASS: WASAPI exclusive mode not directly supported. Let BASS WASAPI choose better mode.");
              wasApiExclusiveSupported = false;
            }
          }
          else
          {
            Log.Debug("BASS: Init WASAPI shared mode with Event driven system enabled.");
            initFlags |= BASSWASAPIInit.BASS_WASAPI_SHARED | BASSWASAPIInit.BASS_WASAPI_EVENT;

            // In case of WASAPI Shared mode we need to setup the mixer to use the same sample rate as set in 
            // the Windows Mixer, otherwise we wioll have increased playback speed
            BASS_WASAPI_DEVICEINFO devInfo = BassWasapi.BASS_WASAPI_GetDeviceInfo(_bassPlayer.DeviceNumber);
            Log.Debug("BASS: Creating {0} channel mixer for frequency {1}", devInfo.mixchans, devInfo.mixfreq);
            _mixer = BassMix.BASS_Mixer_StreamCreate(devInfo.mixfreq, devInfo.mixchans, mixerFlags);
            if (_mixer == 0)
            {
              Log.Error("BASS: Unable to create Mixer.  Reason: {0}.",
                        Enum.GetName(typeof(BASSError), Bass.BASS_ErrorGetCode()));
              return false;
            }
            _wasapiShared = true;
          }

          Log.Debug("BASS: Try to init WASAPI with a Frequency of {0} and {1} channels", stream.ChannelInfo.freq, outputChannels);

          if (BassWasapi.BASS_WASAPI_Init(_bassPlayer.DeviceNumber, stream.ChannelInfo.freq, outputChannels,
                                      initFlags | BASSWASAPIInit.BASS_WASAPI_BUFFER, Convert.ToSingle(Config.BufferingMs / 1000.0), 0f, _wasapiProc, IntPtr.Zero))
          {
            BASS_WASAPI_INFO wasapiInfo = BassWasapi.BASS_WASAPI_GetInfo();

            Log.Debug("BASS: ---------------------------------------------");
            Log.Debug("BASS: Buffer Length: {0}", wasapiInfo.buflen);
            Log.Debug("BASS: Channels: {0}", wasapiInfo.chans);
            Log.Debug("BASS: Frequency: {0}", wasapiInfo.freq);
            Log.Debug("BASS: Format: {0}", wasapiInfo.format.ToString());
            Log.Debug("BASS: InitFlags: {0}", wasapiInfo.initflags.ToString());
            Log.Debug("BASS: Exclusive: {0}", wasapiInfo.IsExclusive.ToString());
            Log.Debug("BASS: ---------------------------------------------");
            Log.Info("BASS: WASAPI Device successfully initialised");

            // Now we need to check, if WASAPI decided to switch to a different mode
            if (Config.WasApiExclusiveMode && !wasApiExclusiveSupported)
            {
              // Recreate Mixer with new value
              Log.Debug("BASS: Creating new {0} channel mixer for frequency {1}", wasapiInfo.chans, wasapiInfo.freq);
              _mixer = BassMix.BASS_Mixer_StreamCreate(wasapiInfo.freq, wasapiInfo.chans, mixerFlags);
              if (_mixer == 0)
              {
                Log.Error("BASS: Unable to create Mixer.  Reason: {0}.",
                          Enum.GetName(typeof(BASSError), Bass.BASS_ErrorGetCode()));
                return false;
              }
            }

            BassWasapi.BASS_WASAPI_SetVolume(BASSWASAPIVolume.BASS_WASAPI_CURVE_DB, (float)Config.StreamVolume / 100f);
            BassWasapi.BASS_WASAPI_Start();
            result = true;
          }
          else
          {
            Log.Error("BASS: Couldn't init WASAPI device. Error: {0}", Enum.GetName(typeof(BASSError), Bass.BASS_ErrorGetCode()));
          }
          break;
      }

      if (result)
      {
        Log.Debug("BASS: Successfully created BASS Mixer stream");
      }
      return result;
    }
Exemplo n.º 16
0
    /// <summary>
    /// This Message is sent from a MusicStream
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="action"></param>
    private void OnMusicStreamMessage(object sender, MusicStream.StreamAction action)
    {
      if (sender == null)
      {
        return;
      }
      MusicStream musicStream = (MusicStream)sender;

      switch (action)
      {
        case MusicStream.StreamAction.Ended:
          break;

        case MusicStream.StreamAction.Crossfading:
          string nextSong = Playlists.PlayListPlayer.SingletonPlayer.GetNextSong();
          if (nextSong != string.Empty)
          {
            g_Player.OnChanged(nextSong);
            PlayInternal(nextSong);
            g_Player.currentMedia = g_Player.MediaType.Music;
            g_Player.currentFilePlaying = nextSong;
            g_Player.OnStarted();
            NotifyPlaying = true;
          }
          else
          {
            Log.Debug("BASS: Reached end of playlist.");
            g_Player.OnStopped();
            Stop();
          }
          break;

        case MusicStream.StreamAction.InternetStreamChanged:
          _tagInfo = musicStream.StreamTags;
          if (InternetStreamSongChanged != null)
          {
            InternetStreamSongChanged(this);
          }
          break;

        case MusicStream.StreamAction.Freed:
          {
            musicStream.Dispose();
          }
          break;

        case MusicStream.StreamAction.Disposed:
          // Remove the stream from the active streams and free it
          lock (_streams)
          {
            if (_streams.Contains(musicStream))
            {
              _streams.Remove(musicStream);
            }
          }
          break;
      }
    }
Exemplo n.º 17
0
    /// <summary>
    /// Sets a SyncPos on the mixer stream
    /// </summary>
    /// <param name="stream"></param>
    /// <param name="timePos"></param>
    public void SetSyncPos(MusicStream stream, double timePos)
    {
      double fadeOutSeconds = Config.CrossFadeIntervalMs / 1000.0;
      double totalStreamLen = Bass.BASS_ChannelBytes2Seconds(stream.BassStream, Bass.BASS_ChannelGetLength(stream.BassStream, BASSMode.BASS_POS_BYTES));
      long mixerPos = Bass.BASS_ChannelGetPosition(_mixer, BASSMode.BASS_POS_BYTES | BASSMode.BASS_POS_DECODE);
      long syncPos = mixerPos + Bass.BASS_ChannelSeconds2Bytes(_mixer, totalStreamLen - timePos - fadeOutSeconds);

      if (_syncProc != 0)
      {
        Bass.BASS_ChannelRemoveSync(_mixer, _syncProc);
      }

      // We might have stored the pinned object already, because we are skipping 
      // Only store object it, when it doesn't exist
      if (!_pinnedObjects.ContainsKey(stream.BassStream))
      {
        Log.Debug("BASS: Updating Dictionary for GCHandle for stream {0}", stream.BassStream);
        // Add the pinned object to the global dictionary, so that we can free it later in the Sync End Proc
        _pinnedObjects.Add(stream.BassStream, GCHandle.Alloc(stream));
      }

      _syncProc = Bass.BASS_ChannelSetSync(_mixer,
        BASSSync.BASS_SYNC_ONETIME | BASSSync.BASS_SYNC_POS | BASSSync.BASS_SYNC_MIXTIME,
        syncPos, _playbackEndProcDelegate,
        new IntPtr(stream.BassStream));
    }