public bool GetFFTData(float[] fftDataBuffer) { if (!BassWasapi.BASS_WASAPI_IsStarted()) { return(false); } return(BassWasapi.BASS_WASAPI_GetData(fftDataBuffer, _maxFFT) > 0); }
public bool GetWaveData32(int length, out float[] waveData32) { waveData32 = null; if (!BassWasapi.BASS_WASAPI_IsStarted()) { return(false); } waveData32 = new float[length]; return(BassWasapi.BASS_WASAPI_GetData(waveData32, length) == (int)BASSError.BASS_OK); }
public bool GetChannelLevel(out double dbLevelL, out double dbLevelR) { dbLevelL = 0f; dbLevelR = 0f; if (!BassWasapi.BASS_WASAPI_IsStarted()) { return(false); } int level = BassWasapi.BASS_WASAPI_GetLevel(); dbLevelL = Un4seen.Bass.Utils.LevelToDB(Un4seen.Bass.Utils.LowWord32(level), 65535); // the left level dbLevelR = Un4seen.Bass.Utils.LevelToDB(Un4seen.Bass.Utils.HighWord32(level), 65535); // the right level return(true); }
/// <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); } }
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); } 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"); if (BassWasapi.BASS_WASAPI_IsStarted()) { 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); int frequency = stream.ChannelInfo.freq; int chans = outputChannels; bool wasApiExclusiveSupported = true; // 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, stream.ChannelInfo.chans, 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; } if (BassWasapi.BASS_WASAPI_Init(_bassPlayer.DeviceNumber, stream.ChannelInfo.freq, stream.ChannelInfo.chans, initFlags, 0f, 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); }
public void EnableBASS(bool setto) { if (setto) { if (BassWasapi.BASS_WASAPI_IsStarted()) { BassWasapi.BASS_WASAPI_Stop(true); } BassWasapi.BASS_WASAPI_Free(); Bass.BASS_Free(); if (VisualizerThread != null) { RunVisualizerThread = false; while (VisualizerThread.Status == TaskStatus.Running) { Thread.Sleep(5); Application.DoEvents(); } VisualizerThread.Dispose(); } String SerialOut = ""; MainFormClass.VisualizerFromSeriesIDNumericUpDown.Invoke((MethodInvoker) delegate { MainFormClass.VisualizerToSeriesIDNumericUpDown.Invoke((MethodInvoker) delegate { SerialOut = "6;" + MainFormClass.VisualizerFromSeriesIDNumericUpDown.Value + ";" + MainFormClass.VisualizerToSeriesIDNumericUpDown.Value; }); }); MainFormClass.SendDataBySerial(SerialOut); BassProcess = new WASAPIPROC(Process); var array = (MainFormClass.AudioSourceComboBox.Items[MainFormClass.AudioSourceComboBox.SelectedIndex] as string).Split(' '); int devindex = Convert.ToInt32(array[0]); Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATETHREADS, false); Bass.BASS_Init(0, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero); bool result = BassWasapi.BASS_WASAPI_Init(devindex, 0, 0, BASSWASAPIInit.BASS_WASAPI_BUFFER, 1f, 0.05f, BassProcess, IntPtr.Zero); if (!result) { var error = Bass.BASS_ErrorGetCode(); MessageBox.Show(error.ToString()); } BassWasapi.BASS_WASAPI_Start(); RunVisualizerThread = true; VisualizerThread = new Task(delegate { AudioDataThread(); }); VisualizerThread.Start(); } else { if (VisualizerThread != null) { RunVisualizerThread = false; while (VisualizerThread.Status == TaskStatus.Running) { Thread.Sleep(5); Application.DoEvents(); } VisualizerThread.Dispose(); } if (BassWasapi.BASS_WASAPI_IsStarted()) { BassWasapi.BASS_WASAPI_Stop(true); } BassWasapi.BASS_WASAPI_Free(); Bass.BASS_Free(); } }
async Task StartVisualizer(bool Start, string DeviceName) { if (Start) { if (RunVisualizer) { RunVisualizer = false; while (VisualizersRunning) { await Task.Delay(10); } } if (BassWasapi.BASS_WASAPI_IsStarted()) { BassWasapi.BASS_WASAPI_Stop(true); } BassWasapi.BASS_WASAPI_Free(); Bass.BASS_Free(); BassProcess = new WASAPIPROC(Process); var array = (AudioSourceCombobox.Items[AudioSourceCombobox.SelectedIndex] as string).Split(' '); int devindex = Convert.ToInt32(array[0]); Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATETHREADS, false); Bass.BASS_Init(0, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero); bool result = BassWasapi.BASS_WASAPI_Init(devindex, 0, 0, BASSWASAPIInit.BASS_WASAPI_BUFFER, 1f, 0.05f, BassProcess, IntPtr.Zero); if (!result) { var error = Bass.BASS_ErrorGetCode(); MessageBox.Show(error.ToString()); } BassWasapi.BASS_WASAPI_Start(); RunVisualizer = true; VisualizersRunning = true; Task VisualizerThreadStart = new Task(delegate { VisualizerThread(); }); VisualizerThreadStart.Start(); } else { if (RunVisualizer) { RunVisualizer = false; while (VisualizersRunning) { await Task.Delay(10); } } for (int i = 0; i < VisualizerStack.Count; i++) { VisualizerStack[i].VisualizerControlBeatZoneCanvas.Children.Clear(); } if (BassWasapi.BASS_WASAPI_IsStarted()) { BassWasapi.BASS_WASAPI_Stop(true); } BassWasapi.BASS_WASAPI_Free(); Bass.BASS_Free(); } }