/// <summary> /// Retrieves information on a device and adds it to the static deviceinfo dictionary do it can be reused later. /// </summary> /// <param name="deviceNo">Device number to retrieve information on.</param> protected void CollectDeviceInfo(int deviceNo) { // Device info is saved in a dictionary so it can be reused lateron. if (!_deviceInfos.ContainsKey(deviceNo)) { Log.Debug("Collecting device info"); BASS_WASAPI_DEVICEINFO bassDeviceInfo = BassWasapi.BASS_WASAPI_GetDeviceInfo(deviceNo); if (bassDeviceInfo == null) { throw new BassLibraryException("BASS_WASAPI_GetDeviceInfo"); } BASS_WASAPI_INFO bassInfo = BassWasapi.BASS_WASAPI_GetInfo(); if (bassInfo == null) { throw new BassLibraryException("BASS_WASAPI_GetInfo"); } DeviceInfo deviceInfo = new DeviceInfo { Name = bassDeviceInfo.name, Driver = "WASAPI", Channels = bassInfo.chans, MinRate = bassInfo.freq, MaxRate = bassInfo.freq, }; GetDeviceFormats(deviceNo, deviceInfo); lock (_deviceInfos) _deviceInfos.Add(deviceNo, deviceInfo); } Log.Debug("WASAPI device info: {0}", _deviceInfos[_deviceNo].ToString()); }
/// <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); }
public static extern bool BASS_WASAPI_GetInfo(BASS_WASAPI_INFO info);
public override void SetInputStream(BassStream stream, bool passThrough) { if (_deviceState != DeviceState.Stopped) { throw new BassPlayerException("Device state is not 'DeviceState.Stopped'"); } _inputStream = stream; _flags = BASSWASAPIInit.BASS_WASAPI_AUTOFORMAT | BASSWASAPIInit.BASS_WASAPI_BUFFER; // If Exclusive mode is used, check, if that would be supported, otherwise init in shared mode bool isExclusive = Controller.GetSettings().WASAPIExclusiveMode; if (isExclusive) { _flags |= BASSWASAPIInit.BASS_WASAPI_EXCLUSIVE; BASSWASAPIFormat wasapiFormat = BassWasapi.BASS_WASAPI_CheckFormat(_deviceNo, _inputStream.SampleRate, _inputStream.Channels, BASSWASAPIInit.BASS_WASAPI_EXCLUSIVE); if (wasapiFormat == BASSWASAPIFormat.BASS_WASAPI_FORMAT_UNKNOWN) { Log.Info("BASS: WASAPI exclusive mode not directly supported for samplerate of {0} and {1} channels", _inputStream.SampleRate, _inputStream.Channels); isExclusive = false; } } retry: if (!isExclusive) { Log.Debug("BASS: Init WASAPI shared mode with Event driven system enabled."); _flags &= ~BASSWASAPIInit.BASS_WASAPI_EXCLUSIVE; _flags |= BASSWASAPIInit.BASS_WASAPI_SHARED | BASSWASAPIInit.BASS_WASAPI_EVENT; } Log.Debug("BASS: Try to init WASAPI with a samplerate of {0} and {1} channels", _inputStream.SampleRate, _inputStream.Channels); bool result = BassWasapi.BASS_WASAPI_Init(_deviceNo, _inputStream.SampleRate, _inputStream.Channels, _flags, 0.5f, 0f, _streamWriteProcDelegate, IntPtr.Zero); BASSError?bassInitErrorCode = result ? null : new BASSError?(Bass.BASS_ErrorGetCode()); if (bassInitErrorCode.HasValue) { if (bassInitErrorCode.Value == BASSError.BASS_ERROR_ALREADY) { if (!BassWasapi.BASS_WASAPI_SetDevice(_deviceNo)) { throw new BassLibraryException("BASS_WASAPI_SetDevice"); } } else if (isExclusive) { // Allow one retry in shared mode Log.Warn("BASS: Failed to initialize WASAPI exclusive mode for samplerate of {0} and {1} channels. Trying fallback to shared mode.", _inputStream.SampleRate, _inputStream.Channels); isExclusive = false; goto retry; } else { throw new BassLibraryException("BASS_WASAPI_Init"); } } // If the GetDeviceNo() method returned BassConstants.BassDefaultDevice, we must request the actual device number // of the choosen default device _deviceNo = BassWasapi.BASS_WASAPI_GetDevice(); CollectDeviceInfo(_deviceNo); 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"); // For shared mode we require a mixer to change the sampling rates of input stream to device output stream. if (!wasapiInfo.IsExclusive) { // Recreate Mixer with new value Log.Debug("BASS: Creating new {0} channel mixer for frequency {1}", wasapiInfo.chans, wasapiInfo.freq); _mixerHandle = BassMix.BASS_Mixer_StreamCreate(wasapiInfo.freq, wasapiInfo.chans, MIXER_FLAGS); if (_mixerHandle == BassConstants.BassInvalidHandle) { throw new BassLibraryException("BASS_Mixer_StreamCreate"); } _mixer = BassStream.Create(_mixerHandle); AttachStream(); } int ms = Convert.ToInt32(Controller.GetSettings().DirectSoundBufferSize.TotalMilliseconds); if (!Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_BUFFER, ms)) { throw new BassLibraryException("BASS_SetConfig"); } // Enable update thread while the output device is active if (!Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATEPERIOD, ms / 4)) { throw new BassLibraryException("BASS_SetConfig"); } if (passThrough) { _fader = new BassStreamFader(_inputStream, Controller.GetSettings().FadeDuration); } ResetState(); }
public static void SetDevice(int inputDevice, int outputDevice) { thisControl = PlayerControl.MainFormControl; //Get some info about their selected input device var inputdeviceinfo = BassWasapi.BASS_WASAPI_GetDeviceInfo(inputDevice); InputDevice = inputDevice; InputDeviceInfo = inputdeviceinfo; //Get some info about their selected ouput device var outputdeviceinfo = BassWasapi.BASS_WASAPI_GetDeviceInfo(outputDevice); OutputDevice = outputDevice; OutputDeviceInfo = outputdeviceinfo; if (!m_isWasapiInit) { Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATEPERIOD, 0); Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_FLOATDSP, true); Bass.BASS_Init(0, 44100, BASSInit.BASS_DEVICE_DEFAULT, PlayerControl.MainFormControl.Handle); Player.Mixer = MixerStreamCreate(outputdeviceinfo.mixfreq); if (Player.Mixer == 0) { var error = Bass.BASS_ErrorGetCode(); MessageBox.Show(error.ToString(), "Could not create mixer!"); Bass.BASS_Free(); return; } _outstream = Player.Mixer; _instream = Bass.BASS_StreamCreatePush(inputdeviceinfo.mixfreq, inputdeviceinfo.mixchans, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE, IntPtr.Zero); // create a stream to receive the data // string fn = @"D:\tanie\karaokeNow Resources\LdVocal_01.wav"; // _instream = Bass.BASS_StreamCreateFile(fn, 0L, 0L, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_LOOP); //_stream[0] = BassMix.BASS_Split_StreamCreate(_instream, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE, null);//_instream; //_stream[1] = BassMix.BASS_Split_StreamCreate(_instream, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE, null);// _instream; //_stream[2] = BassMix.BASS_Split_StreamCreate(_instream, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE, null);//_instream; //_stream[3] = BassMix.BASS_Split_StreamCreate(_instream, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE, null);//_instream; // _outstream = BassMix.BASS_Mixer_StreamCreate(outputdeviceinfo.mixfreq, outputdeviceinfo.mixchans, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE); // Player.Mixer = _outstream; //Group 1 VST Effects ============================================================ Channel1Fx.SetEffects(_instream); //Group 2 VST Effects ============================================================ // Channel2Fx.SetEffects(_stream[1]); //Group 3 VST Effects ============================================================ //Channel3Fx.SetEffects(_stream[2]); //Group 4 VST Effects ============================================================ //Channel4Fx.SetEffects(_stream[3]); BassMix.BASS_Mixer_StreamAddChannel(_outstream, _instream, 0); // BassMix.BASS_Mixer_StreamAddChannel(_outstream, _stream[1], 0); //BassMix.BASS_Mixer_StreamAddChannel(_outstream, _stream[2], 0); // BassMix.BASS_Mixer_StreamAddChannel(_outstream, _stream[3], 0); m_isWasapiInit = true; } _wasapiInProc = new WASAPIPROC(InputProc); GC.KeepAlive(_wasapiInProc); _wasapiOutProc = new WASAPIPROC(OutputProc); GC.KeepAlive(_wasapiOutProc); //Set the input device so subsequent calls are on it BassWasapi.BASS_WASAPI_SetDevice(InputDevice); // Initialize BASS WASAPI input BASS_WASAPI_INFO input = new BASS_WASAPI_INFO(); BassWasapi.BASS_WASAPI_GetInfo(input); BassWasapi.BASS_WASAPI_Init(inputDevice, inputdeviceinfo.mixfreq, inputdeviceinfo.mixchans, BASSWASAPIInit.BASS_WASAPI_EVENT | BASSWASAPIInit.BASS_WASAPI_BUFFER | BASSWASAPIInit.BASS_WASAPI_SHARED, input.buflen, 0, _wasapiInProc, IntPtr.Zero); //Set the output device so subsequent calls are on it BassWasapi.BASS_WASAPI_SetDevice(OutputDevice); BASS_WASAPI_INFO output = new BASS_WASAPI_INFO(); BassWasapi.BASS_WASAPI_GetInfo(output); // Initialize BASS WASAPI output BassWasapi.BASS_WASAPI_Init(outputDevice, outputdeviceinfo.mixfreq, outputdeviceinfo.mixchans, BASSWASAPIInit.BASS_WASAPI_EVENT | BASSWASAPIInit.BASS_WASAPI_SHARED, output.buflen, 0, _wasapiOutProc, IntPtr.Zero); }