public static void Init(int device) { if (device == BassAsioStreamOutputConfiguration.ASIO_NO_DEVICE) { throw new InvalidOperationException("A valid device must be provided."); } LogManager.Logger.Write(typeof(BassAsioDevice), LogLevel.Debug, "Initializing BASS ASIO."); BassAsioUtils.OK(BassAsio.Init(device, AsioInitFlags.Thread)); BassAsioUtils.OK(BassAsioHandler.Init()); IsInitialized = true; var info = default(AsioChannelInfo); BassAsioUtils.OK(BassAsio.ChannelGetInfo(false, PRIMARY_CHANNEL, out info)); Device = device; Devices[device] = new BassAsioDeviceInfo( info.Name, Convert.ToInt32(BassAsio.Rate), BassAsio.Info.Inputs, BassAsio.Info.Outputs, GetSupportedRates(), info.Format ); LogManager.Logger.Write(typeof(BassAsioDevice), LogLevel.Debug, "Detected ASIO device: {0} => Name => {1}, Inputs => {2}, Outputs = {3}, Rate = {4}, Format = {5}", Device, Info.Name, Info.Inputs, Info.Outputs, Info.Rate, Enum.GetName(typeof(AsioSampleFormat), Info.Format)); LogManager.Logger.Write(typeof(BassAsioDevice), LogLevel.Debug, "Detected ASIO device: {0} => Rates => {1}", Device, string.Join(", ", Info.SupportedRates)); }
protected virtual bool ConfigureASIO_PCM(IBassStreamComponent previous) { Logger.Write(this, LogLevel.Debug, "Configuring PCM."); BassAsioUtils.OK(BassAsio.SetDSD(false)); if (!this.CheckFormat(this.Rate, this.Channels)) { Logger.Write(this, LogLevel.Warn, "PCM format {0}:{1} is unsupported.", this.Rate, this.Channels); return(false); } else { BassAsio.Rate = this.Rate; } var format = default(AsioSampleFormat); if (previous.Flags.HasFlag(BassFlags.Float)) { format = AsioSampleFormat.Float; } else { format = AsioSampleFormat.Bit16; } Logger.Write(this, LogLevel.Debug, "PCM: Rate = {0}, Format = {1}", BassAsio.Rate, Enum.GetName(typeof(AsioSampleFormat), format)); BassAsioUtils.OK(BassAsio.ChannelSetRate(false, BassAsioDevice.PRIMARY_CHANNEL, previous.Rate)); BassAsioUtils.OK(BassAsio.ChannelSetFormat(false, BassAsioDevice.PRIMARY_CHANNEL, format)); return(true); }
public static void Detect(int device) { if (IsInitialized) { throw new InvalidOperationException("Device is already initialized."); } IsInitialized = true; Logger.Write(typeof(BassAsioDevice), LogLevel.Debug, "Detecting ASIO device."); try { BassAsioUtils.OK(BassAsio.Init(device, AsioInitFlags.Thread)); BassAsioUtils.OK(BassAsioHandler.Init()); var info = default(AsioChannelInfo); BassAsioUtils.OK(BassAsio.ChannelGetInfo(false, PRIMARY_CHANNEL, out info)); Info = new BassAsioDeviceInfo( BassAsio.CurrentDevice, Convert.ToInt32(BassAsio.Rate), BassAsio.Info.Inputs, BassAsio.Info.Outputs, GetSupportedRates(), info.Format ); Logger.Write(typeof(BassAsioDevice), LogLevel.Debug, "Detected ASIO device: {0} => Inputs => {1}, Outputs = {2}, Rate = {3}, Format = {4}", BassAsio.CurrentDevice, Info.Inputs, Info.Outputs, Info.Rate, Enum.GetName(typeof(AsioSampleFormat), Info.Format)); Logger.Write(typeof(BassAsioDevice), LogLevel.Debug, "Detected ASIO device: {0} => Rates => {1}", BassAsio.CurrentDevice, string.Join(", ", Info.SupportedRates)); } finally { Free(); } }
protected override void OnDisposing() { if (BassAsio.IsStarted) { BassAsioUtils.OK(this.StopASIO()); BassAsioUtils.OK(this.ResetASIO()); } }
public override void Connect(IBassStreamComponent previous) { if (previous.Channels > BassAsioDevice.Info.Outputs) { //TODO: We should down mix. Logger.Write(this, LogLevel.Error, "Cannot play stream with more channels than device outputs."); throw new NotImplementedException(string.Format("The stream contains {0} channels which is greater than {1} output channels provided by the device.", previous.Channels, BassAsioDevice.Info.Outputs)); } if (!this.CheckFormat(this.Rate, previous.Channels)) { Logger.Write(this, LogLevel.Error, "Cannot play stream with unsupported rate."); throw new NotImplementedException(string.Format("The stream has a rate of {0} which is not supported by the device.", this.Rate)); } var exception = default(Exception); for (var a = 1; a <= CONNECT_ATTEMPTS; a++) { Logger.Write(this, LogLevel.Debug, "Configuring ASIO, attempt: {0}", a); try { if (BassAsioUtils.OK(this.ConfigureASIO(previous))) { var success = default(bool); if (previous.Flags.HasFlag(BassFlags.DSDRaw) || BassUtils.GetChannelDsdRaw(previous.ChannelHandle)) { success = BassAsioUtils.OK(this.ConfigureASIO_DSD(previous)); } else { success = BassAsioUtils.OK(this.ConfigureASIO_PCM(previous)); } if (success) { Logger.Write(this, LogLevel.Debug, "Configured ASIO."); return; } } } catch (Exception e) { exception = e; Logger.Write(this, LogLevel.Warn, "Failed to configure ASIO: {0}", e.Message); if (BassAsioDevice.IsInitialized) { Logger.Write(this, LogLevel.Warn, "Re-initializing ASIO, have you just switched from DSD to PCM?"); BassAsioDevice.Free(); BassAsioDevice.Init(); } } Thread.Sleep(CONNECT_ATTEMPT_INTERVAL); } if (exception != null) { throw exception; } throw new NotImplementedException(); }
private static IEnumerable <SelectionConfigurationOption> GetASIODevices() { for (int a = 0, b = BassAsio.DeviceCount; a < b; a++) { var deviceInfo = default(AsioDeviceInfo); BassAsioUtils.OK(BassAsio.GetDeviceInfo(a, out deviceInfo)); LogManager.Logger.Write(typeof(BassAsioStreamOutputConfiguration), LogLevel.Debug, "ASIO Device: {0} => {1} => {2}", a, deviceInfo.Name, deviceInfo.Driver); yield return(new SelectionConfigurationOption(deviceInfo.Name, deviceInfo.Name, deviceInfo.Driver)); } }
protected virtual bool StopASIO() { if (!BassAsio.IsStarted) { Logger.Write(this, LogLevel.Debug, "ASIO has not been started."); return(false); } Logger.Write(this, LogLevel.Debug, "Stopping ASIO."); BassAsioUtils.OK(BassAsio.Stop()); return(true); }
protected virtual bool ConfigureASIO_DSD(IBassStreamComponent previous) { try { Logger.Write(this, LogLevel.Debug, "Configuring DSD RAW."); try { BassAsioUtils.OK(BassAsio.SetDSD(true)); } catch { //If we get here some drivers (at least Creative) will crash when BassAsio.Start is called. //I can't find a way to prevent it but it seems to be related to the allocated buffer size //not being what the driver *thinks* it is and over-flowing. // //It should be unlikely in real life as the device would have to report capability of some //very high PCM frequencies. Logger.Write(this, LogLevel.Error, "Failed to enable DSD RAW on the device. Creative ASIO driver becomes unstable and usually crashes soon..."); return(false); } if (!this.CheckFormat(this.Rate, previous.Channels)) { Logger.Write(this, LogLevel.Warn, "DSD format {0}:{1} is unsupported.", previous.Rate, previous.Channels); return(false); } else { BassAsio.Rate = this.Rate; } //It looks like BASS DSD always outputs 8 bit/MSB data so we don't need to determine the format. //var format = default(AsioSampleFormat); //switch (this.Depth) //{ // case BassAttribute.DSDFormat_LSB: // format = AsioSampleFormat.DSD_LSB; // break; // case BassAttribute.DSDFormat_None: // case BassAttribute.DSDFormat_MSB: // format = AsioSampleFormat.DSD_MSB; // break; // default: // throw new NotImplementedException(); //} Logger.Write(this, LogLevel.Debug, "DSD: Rate = {0}, Format = {1}", BassAsio.Rate, Enum.GetName(typeof(AsioSampleFormat), AsioSampleFormat.DSD_MSB)); BassAsioUtils.OK(BassAsio.ChannelSetFormat(false, BassAsioDevice.PRIMARY_CHANNEL, AsioSampleFormat.DSD_MSB)); return(true); } catch (Exception e) { Logger.Write(this, LogLevel.Warn, "Failed to configure DSD RAW: {0}", e.Message); return(false); } }
protected virtual void OnInit(object sender, EventArgs e) { if (!this.Enabled) { return; } BassAsioUtils.OK(Bass.Configure(global::ManagedBass.Configuration.UpdateThreads, 0)); BassAsioUtils.OK(Bass.Configure(global::ManagedBass.Configuration.PlaybackBufferLength, this.Output.BufferLength)); BassAsioUtils.OK(Bass.Init(Bass.NoSoundDevice)); this.IsInitialized = true; Logger.Write(this, LogLevel.Debug, "BASS (No Sound) Initialized."); }
public static int GetAsioDevice(SelectionConfigurationOption option) { for (int a = 0, b = BassAsio.DeviceCount; a < b; a++) { var deviceInfo = default(AsioDeviceInfo); BassAsioUtils.OK(BassAsio.GetDeviceInfo(a, out deviceInfo)); if (string.Equals(deviceInfo.Name, option.Id, StringComparison.OrdinalIgnoreCase)) { return(a); } } return(ASIO_NO_DEVICE); }
private static void InitPCM(int device, int rate, int channels, BassFlags flags) { BassAsioUtils.OK(BassAsio.SetDSD(false)); BassAsioUtils.OK(BassAsio.ChannelSetRate(false, BassAsioDevice.PRIMARY_CHANNEL, rate)); var format = default(AsioSampleFormat); if (flags.HasFlag(BassFlags.Float)) { format = AsioSampleFormat.Float; } else { format = AsioSampleFormat.Bit16; } BassAsioUtils.OK(BassAsio.ChannelSetFormat(false, BassAsioDevice.PRIMARY_CHANNEL, format)); }
public override void Play() { if (this.IsPlaying) { return; } Logger.Write(this, LogLevel.Debug, "Starting ASIO."); try { BassAsioUtils.OK(this.StartASIO()); } catch (Exception e) { this.OnError(e); throw; } }
public override void Stop() { if (this.IsStopped) { return; } Logger.Write(this, LogLevel.Debug, "Stopping ASIO."); try { BassAsioUtils.OK(this.StopASIO()); } catch (Exception e) { this.OnError(e); throw; } }
public override void Resume() { if (!this.IsPaused) { return; } Logger.Write(this, LogLevel.Debug, "Resuming ASIO."); try { BassAsioUtils.OK(BassAsio.ChannelReset(false, BassAsioDevice.PRIMARY_CHANNEL, AsioChannelResetFlags.Pause)); } catch (Exception e) { this.OnError(e); throw; } }
public override void Pause() { if (this.IsPaused) { return; } Logger.Write(this, LogLevel.Debug, "Pausing ASIO."); try { BassAsioUtils.OK(BassAsio.ChannelPause(false, BassAsioDevice.PRIMARY_CHANNEL)); } catch (Exception e) { this.OnError(e); throw; } }
protected virtual void OnInit(object sender, EventArgs e) { if (!this.Enabled) { return; } this.IsInitialized = true; BassAsioUtils.OK(Bass.Configure(global::ManagedBass.Configuration.UpdateThreads, 0)); BassAsioUtils.OK(Bass.Configure(global::ManagedBass.Configuration.PlaybackBufferLength, this.Output.BufferLength)); BassAsioUtils.OK(Bass.Configure(global::ManagedBass.Configuration.SRCQuality, this.Output.ResamplingQuality)); BassAsioUtils.OK(Bass.Init(Bass.NoSoundDevice)); //Always detect device for now. //if (BassAsioDevice.Info != null && BassAsioDevice.Info.Device != this.AsioDevice) { BassAsioDevice.Detect(this.AsioDevice); } Logger.Write(this, LogLevel.Debug, "BASS (No Sound) Initialized."); }
private static void InitDSD(int device, int rate, int channels, BassFlags flags) { BassAsioUtils.OK(BassAsio.SetDSD(true)); //It looks like BASS DSD always outputs 8 bit/MSB data so we don't need to determine the format. //var format = default(AsioSampleFormat); //switch (this.Depth) //{ // case BassAttribute.DSDFormat_LSB: // format = AsioSampleFormat.DSD_LSB; // break; // case BassAttribute.DSDFormat_None: // case BassAttribute.DSDFormat_MSB: // format = AsioSampleFormat.DSD_MSB; // break; // default: // throw new NotImplementedException(); //} BassAsioUtils.OK(BassAsio.ChannelSetFormat(false, BassAsioDevice.PRIMARY_CHANNEL, AsioSampleFormat.DSD_MSB)); }
protected virtual bool ConfigureASIO(IBassStreamComponent previous) { Logger.Write(this, LogLevel.Debug, "Configuring ASIO."); BassAsioUtils.OK(BassAsioHandler.StreamSet(previous.ChannelHandle)); BassAsioUtils.OK(BassAsioHandler.ChannelEnable(false, BassAsioDevice.PRIMARY_CHANNEL)); if (previous.Channels == 1) { Logger.Write(this, LogLevel.Debug, "Mirroring channel: {0} => {1}", BassAsioDevice.PRIMARY_CHANNEL, BassAsioDevice.SECONDARY_CHANNEL); BassAsioUtils.OK(BassAsio.ChannelEnableMirror(BassAsioDevice.SECONDARY_CHANNEL, false, BassAsioDevice.PRIMARY_CHANNEL)); } else { for (var channel = 1; channel < previous.Channels; channel++) { Logger.Write(this, LogLevel.Debug, "Joining channel: {0} => {1}", channel, BassAsioDevice.PRIMARY_CHANNEL); BassAsioUtils.OK(BassAsio.ChannelJoin(false, channel, BassAsioDevice.PRIMARY_CHANNEL)); } } return(true); }
public static void Init(int device, int rate, int channels, BassFlags flags) { if (IsInitialized) { throw new InvalidOperationException("Device is already initialized."); } IsInitialized = true; Logger.Write(typeof(BassAsioDevice), LogLevel.Debug, "Initializing BASS ASIO."); try { BassAsioUtils.OK(BassAsio.Init(device, AsioInitFlags.Thread)); BassAsioUtils.OK(BassAsioHandler.Init()); BassAsioUtils.OK(BassAsioHandler.ChannelEnable(false, PRIMARY_CHANNEL)); for (var channel = 1; channel < channels; channel++) { Logger.Write(typeof(BassAsioDevice), LogLevel.Debug, "Joining channel: {0} => {1}", channel, PRIMARY_CHANNEL); BassAsioUtils.OK(BassAsio.ChannelJoin(false, channel, PRIMARY_CHANNEL)); } if (flags.HasFlag(BassFlags.DSDRaw)) { InitDSD(device, rate, channels, flags); } else { InitPCM(device, rate, channels, flags); } BassAsio.Rate = rate; Logger.Write(typeof(BassAsioDevice), LogLevel.Debug, "Initialized BASS ASIO."); } catch { Free(); throw; } }
protected virtual bool ResetASIO() { try { var flags = AsioChannelResetFlags.Enable | AsioChannelResetFlags.Join | AsioChannelResetFlags.Format | AsioChannelResetFlags.Rate; Logger.Write(this, LogLevel.Debug, "Resetting channel attributes."); for (var channel = 0; channel < this.Channels; channel++) { BassAsioUtils.OK(BassAsio.ChannelReset(false, channel, flags)); } } catch (Exception e) { //Nothing can be done. Logger.Write(this, LogLevel.Warn, "Failed to reset channel attributes: {0}", e.Message); } return(true); }