Quantcast
Channel: NAudio
Viewing all articles
Browse latest Browse all 5831

New Post: ASIO input

$
0
0

[quote]Did you perhaps grab the un-hacked file by accident?[/quote]

What d'you think I am, some sort of idi...

...oh yeah, I am an idiot. Sorry, folks, my bad.

Anyway, here it is. I did just test this out and it is working, at least in the context of my own code. The purpose of this change, by the way, was to write an SMPTE timecode decoder. This is also working, and if there's any interest in naudio being able to decode SMPTE timecode, I might be persuaded to donate it.

using System;

namespace NAudio.Wave.Asio
{

    /// 

    /// ASIODriverCapability holds all the information from the ASIODriver.
    /// Use ASIODriverExt to get the Capabilities
    /// 

    internal class ASIODriverCapability
    {
        public String DriverName;  
      
        public int NbInputChannels;
        public int NbOutputChannels;

        public int InputLatency;
        public int OutputLatency;

        public int BufferMinSize;
        public int BufferMaxSize;
        public int BufferPreferredSize;
        public int BufferGranularity;

        public double SampleRate;

        public ASIOChannelInfo[] InputChannelInfos;
        public ASIOChannelInfo[] OutputChannelInfos;
    }

    /// 

    /// Callback used by the ASIODriverExt to get wave data
    /// 

    internal delegate void ASIOFillBufferCallback(IntPtr[] InputBufferChannels, IntPtr[] OutputBufferChannels);

    /// 

    /// ASIODriverExt is a simplified version of the ASIODriver. It provides an easier
    /// way to access the capabilities of the Driver and implement the callbacks necessary 
    /// for feeding the driver.
    /// Implementation inspired from Rob Philpot's with a managed C++ ASIO wrapper BlueWave.Interop.Asio
    /// http://www.codeproject.com/KB/mcpp/Asio.Net.aspx
    /// 
    /// Contributor: Alexandre Mutel - email: alexandre_mutel at yahoo.fr
    /// 
    /// ASIO input hacked in by: Phil Rhodes
    /// This is currently extremely basic and breaks the previous API in several
    /// places. See http://naudio.codeplex.com/discussions/284258
    /// 
    /// Changes are particularly in CreateBuffers and BufferSwitchCallback.
    /// 
    /// The underlying driver in ASIODriver.cs does not seem to need changes to
    /// support input.
    /// 
    /// 

    internal class ASIODriverExt
    {
        private ASIODriver driver;
        private ASIOCallbacks callbacks;
        private ASIODriverCapability capability;
        public ASIOBufferInfo[] BufferInfos;
        private bool isOutputReadySupport;
        private IntPtr[] CurrentOutputBuffers;
        private IntPtr[] CurrentInputBuffers;
        private int nbOutputChannels;
        private ASIOFillBufferCallback BuffersReadyCallback;
        private int bufferSize;
        private int channelOffset;

        /// 

        /// Initializes a new instance of the  class based on an already
        /// instantiated ASIODriver instance.
        /// 

        /// A ASIODriver already instantiated.
        public ASIODriverExt(ASIODriver driver)
        {
            this.driver = driver;

            if (!driver.init(IntPtr.Zero))
            {
                throw new ApplicationException(driver.getErrorMessage());
            }

            callbacks = new ASIOCallbacks();
            callbacks.pasioMessage = AsioMessageCallBack;
            callbacks.pbufferSwitch = BufferSwitchCallBack;
            callbacks.pbufferSwitchTimeInfo = BufferSwitchTimeInfoCallBack;
            callbacks.psampleRateDidChange = SampleRateDidChangeCallBack;

            BuildCapabilities();
        }

        /// 

        /// Allows adjustment of which is the first output channel we write to
        /// 

        /// Channel offset
        public void SetChannelOffset(int channelOffset)
        {
            if (channelOffset + nbOutputChannels <= Capabilities.NbOutputChannels)
            {
                this.channelOffset = channelOffset;
            }
            else
            {
                throw new ArgumentException("Invalid channel offset");
            }
       }

        /// 

        /// Gets the driver used.
        /// 

        /// The ASIOdriver.
        public ASIODriver Driver
        {
            get { return driver; }
        }

        /// 

        /// Starts playing the buffers.
        /// 

        public void Start()
        {
            driver.start();
        }

        /// 

        /// Stops playing the buffers.
        /// 

        public void Stop()
        {
            driver.stop();
        }

        /// 

        /// Shows the control panel.
        /// 

        public void ShowControlPanel()
        {
            driver.controlPanel();
        }

        /// 

        /// Releases this instance.
        /// 

        public void ReleaseDriver()
        {
            try
            {
                driver.disposeBuffers();
            } catch (Exception ex)
            {
                Console.Out.WriteLine(ex.ToString());
            }
            driver.ReleaseComASIODriver();
        }

        /// 

        /// Determines whether the specified sample rate is supported.
        /// 

        /// The sample rate.
        /// 
        /// 	true if [is sample rate supported]; otherwise, false.
        /// 
        public bool IsSampleRateSupported(double sampleRate)
        {
            return driver.canSampleRate(sampleRate);
        }

        /// 

        /// Sets the sample rate.
        /// 

        /// The sample rate.
        public void SetSampleRate(double sampleRate)
        {
            driver.setSampleRate(sampleRate);
            // Update Capabilities
            BuildCapabilities();
        }

        /// 

        /// Gets or sets the fill buffer callback.
        /// 

        /// The fill buffer callback.
        public ASIOFillBufferCallback FillBufferCallback
        {
            get { return BuffersReadyCallback; }
            set { BuffersReadyCallback = value; }
        }

        /// 

        /// Gets the capabilities of the ASIODriver.
        /// 

        /// The capabilities.
        public ASIODriverCapability Capabilities
        {
            get { return capability; }
        }

        /// 

        /// Creates the buffers for playing.
        /// 

        /// The number of outputs channels.
        /// if set to true [use max buffer size] else use Prefered size
        public int CreateBuffers(int nbInputChannelsArg, int nbOutputChannelsArg, bool useMaxBufferSize)
        {
            if (nbOutputChannelsArg <= 0 || nbOutputChannelsArg > capability.NbOutputChannels)
            {
                throw new ArgumentException(String.Format(
                                                "Invalid number of channels {0}, must be in the range [1,{1}]",
                                                nbOutputChannelsArg, capability.NbOutputChannels));
            }

            // each channel needs a buffer info
            nbOutputChannels = nbOutputChannelsArg;
            // Ask for maximum of output channels even if we use only the nbOutputChannelsArg
            int nbTotalChannels = capability.NbInputChannels + capability.NbOutputChannels;
            BufferInfos = new ASIOBufferInfo[nbTotalChannels];
            CurrentOutputBuffers = new IntPtr[nbOutputChannelsArg];
            CurrentInputBuffers = new IntPtr[nbInputChannelsArg];
            // and do the same for output channels
            // ONLY work on output channels (just put isInput = true for InputChannel)
            int totalIndex = 0;
            for (int index = 0; index < capability.NbInputChannels; index++, totalIndex++)
            {
                BufferInfos[totalIndex].isInput = true;
                BufferInfos[totalIndex].channelNum = index;
                BufferInfos[totalIndex].pBuffer0 = IntPtr.Zero;
                BufferInfos[totalIndex].pBuffer1 = IntPtr.Zero;
            }

            for (int index = 0; index < capability.NbOutputChannels; index++, totalIndex++)
            {
                BufferInfos[totalIndex].isInput = false;
                BufferInfos[totalIndex].channelNum = index;
                BufferInfos[totalIndex].pBuffer0 = IntPtr.Zero;
                BufferInfos[totalIndex].pBuffer1 = IntPtr.Zero;
            }

            if (useMaxBufferSize)
            {
                // use the drivers maximum buffer size
                bufferSize = capability.BufferMaxSize;
            }
            else
            {
                // use the drivers preferred buffer size
                bufferSize = capability.BufferPreferredSize;
            }

            unsafe
            {
                fixed (ASIOBufferInfo* infos = &BufferInfos[0])
                {
                    IntPtr pOutputBufferInfos = new IntPtr(infos);

                    // Create the ASIO Buffers with the callbacks

                    driver.createBuffers(pOutputBufferInfos, nbTotalChannels, bufferSize, ref callbacks);
                }
            }

            // Check if outputReady is supported
            isOutputReadySupport = (driver.outputReady() == ASIOError.ASE_OK);
            
            return bufferSize;
        }

        /// 

        /// Builds the capabilities internally.
        /// 

        private void BuildCapabilities()
        {
            capability = new ASIODriverCapability();

            capability.DriverName = driver.getDriverName();

            // Get nb Input/Output channels
            driver.getChannels(out capability.NbInputChannels, out capability.NbOutputChannels);

            capability.InputChannelInfos = new ASIOChannelInfo[capability.NbInputChannels];
            capability.OutputChannelInfos = new ASIOChannelInfo[capability.NbOutputChannels];

            // Get ChannelInfo for Inputs
            for (int i = 0; i < capability.NbInputChannels; i++)
            {
                capability.InputChannelInfos[i] = driver.getChannelInfo(i, true);
            }

            // Get ChannelInfo for Output
            for (int i = 0; i < capability.NbOutputChannels; i++)
            {
                capability.OutputChannelInfos[i] = driver.getChannelInfo(i, false);
            }

            // Get the current SampleRate
            capability.SampleRate = driver.getSampleRate();


            // Get Latencies
            driver.getLatencies(out capability.InputLatency, out capability.OutputLatency);

            // Get BufferSize
            driver.getBufferSize(out capability.BufferMinSize, out capability.BufferMaxSize, out capability.BufferPreferredSize, out capability.BufferGranularity);
        }

        /// 

        /// Callback called by the ASIODriver on fill buffer demand. Redirect call to external callback.
        /// 

        /// Index of the double buffer.
        /// if set to true [direct process].
        private void BufferSwitchCallBack(int doubleBufferIndex, bool directProcess)
        {

            for (int i = 0; i < capability.NbInputChannels; i++)
            {
                CurrentInputBuffers[i] = BufferInfos[i + channelOffset].Buffer(doubleBufferIndex);
            }

            for (int i = 0; i < nbOutputChannels; i++)
            {
                CurrentOutputBuffers[i] = BufferInfos[i + channelOffset + capability.NbInputChannels].Buffer(doubleBufferIndex);
            }

            if (BuffersReadyCallback != null)

                BuffersReadyCallback(CurrentInputBuffers, CurrentOutputBuffers);

            if (isOutputReadySupport)
                driver.outputReady();            
        }

        /// 

        /// Callback called by the ASIODriver on event "Samples rate changed".
        /// 

        /// The sample rate.
        private void SampleRateDidChangeCallBack(double sRate)
        {
            // Check when this is called?
            capability.SampleRate = sRate;
        }

        /// 

        /// Asio message call back.
        /// 

        /// The selector.
        /// The value.
        /// The message.
        /// The opt.
        /// 
        private int AsioMessageCallBack(ASIOMessageSelector selector, int value, IntPtr message, IntPtr opt)
        {
            // Check when this is called?
            switch (selector)
            {
                case ASIOMessageSelector.kAsioSelectorSupported:
                    ASIOMessageSelector subValue = (ASIOMessageSelector)Enum.ToObject(typeof(ASIOMessageSelector), value);
                    switch (subValue)
                    {
                        case ASIOMessageSelector.kAsioEngineVersion:
                            return 1;
                        case ASIOMessageSelector.kAsioResetRequest:
                            return 0;
                        case ASIOMessageSelector.kAsioBufferSizeChange:
                            return 0;
                        case ASIOMessageSelector.kAsioResyncRequest:
                            return 0;
                        case ASIOMessageSelector.kAsioLatenciesChanged:
                            return 0;
                        case ASIOMessageSelector.kAsioSupportsTimeInfo:
//                            return 1; DON'T SUPPORT FOR NOW. NEED MORE TESTING.
                            return 0;
                        case ASIOMessageSelector.kAsioSupportsTimeCode:
//                            return 1; DON'T SUPPORT FOR NOW. NEED MORE TESTING.
                            return 0;
                    }
                    break;
                case ASIOMessageSelector.kAsioEngineVersion:
                    return 2;
                case ASIOMessageSelector.kAsioResetRequest:
                    return 1;
                case ASIOMessageSelector.kAsioBufferSizeChange:
                    return 0;
                case ASIOMessageSelector.kAsioResyncRequest:
                    return 0;
                case ASIOMessageSelector.kAsioLatenciesChanged:
                    return 0;
                case ASIOMessageSelector.kAsioSupportsTimeInfo:
                    return 0;
                case ASIOMessageSelector.kAsioSupportsTimeCode:
                    return 0;
            }
            return 0;            
        }

        /// 

        /// Buffers switch time info call back.
        /// 

        /// The asio time param.
        /// Index of the double buffer.
        /// if set to true [direct process].
        /// 
        private IntPtr BufferSwitchTimeInfoCallBack(IntPtr asioTimeParam, int doubleBufferIndex, bool directProcess)
        {
            // Check when this is called?
            return IntPtr.Zero;   
        }
    }
}

 

 


Viewing all articles
Browse latest Browse all 5831

Latest Images

Trending Articles



Latest Images