예제 #1
0
        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));
        }
예제 #2
0
        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);
        }
예제 #3
0
        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();
            }
        }
예제 #4
0
 protected override void OnDisposing()
 {
     if (BassAsio.IsStarted)
     {
         BassAsioUtils.OK(this.StopASIO());
         BassAsioUtils.OK(this.ResetASIO());
     }
 }
예제 #5
0
        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();
        }
예제 #6
0
 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));
     }
 }
예제 #7
0
 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);
 }
예제 #8
0
 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);
     }
 }
예제 #9
0
 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.");
 }
예제 #10
0
 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);
 }
예제 #11
0
        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));
        }
예제 #12
0
 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;
     }
 }
예제 #13
0
 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;
     }
 }
예제 #14
0
 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;
     }
 }
예제 #15
0
 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.");
 }
예제 #17
0
 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));
 }
예제 #18
0
 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);
 }
예제 #19
0
        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;
            }
        }
예제 #20
0
 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);
 }