Esempio n. 1
0
        /// <summary>
        /// Starts capturing audio data.
        /// </summary>
        /// <param name="targetLatencyInMs">
        /// The target maximum number of milliseconds of acceptable lag between
        /// live sound being produced and capture operation.
        /// </param>
        /// <param name="gain">
        /// The gain to be applied to the captured audio.
        /// </param>
        /// <param name="outFormat">
        /// The desired output format of the captured audio.
        /// </param>
        /// <param name="speech">
        /// If true, optimizes the audio capture pipeline for speech recognition.
        /// </param>
        public void StartCapture(int targetLatencyInMs, float gain, WaveFormat outFormat, bool speech)
        {
            if (this.wasapiCapture != null)
            {
                this.StopCapture();
            }

            this.wasapiCapture = new WasapiCapture(this.audioDevice);

            // Create a callback delegate and marshal it to a function pointer. Keep a
            // reference to the delegate as a class field to prevent it from being GC'd.
            this.callbackDelegate = new AudioDataAvailableCallback(this.AudioDataAvailableCallback);

            // initialize the capture with the desired parameters
            this.wasapiCapture.Initialize(targetLatencyInMs, gain, outFormat, this.callbackDelegate, speech);

            // tell WASAPI to start capturing
            this.wasapiCapture.Start();
        }
Esempio n. 2
0
        /// <summary>
        /// Initialize the resampler.
        /// </summary>
        /// <param name="targetLatencyInMs">
        /// The target maximum number of milliseconds of acceptable lag between
        /// input and resampled output audio samples.
        /// </param>
        /// <param name="inFormat">
        /// The input format of the audio to be resampled.
        /// </param>
        /// <param name="outFormat">
        /// The output format of the resampled audio.
        /// </param>
        /// <param name="callback">
        /// Callback delegate which will receive the resampled data.
        /// </param>
        public void Initialize(int targetLatencyInMs, WaveFormat inFormat, WaveFormat outFormat, AudioDataAvailableCallback callback)
        {
            // Buffer sizes are calculated from the target latency.
            this.bufferLengthInMs    = targetLatencyInMs;
            this.inputBytesPerSecond = (int)inFormat.AvgBytesPerSec;
            this.inputBufferSize     = (int)(this.bufferLengthInMs * inFormat.AvgBytesPerSec / 1000);
            this.outputBufferSize    = (int)(this.bufferLengthInMs * outFormat.AvgBytesPerSec / 1000);

            // Activate native Media Foundation COM objects on a thread-pool thread to ensure that they are in an MTA
            Task.Run(() =>
            {
                DeviceUtil.CreateResamplerBuffer(this.inputBufferSize, out this.inputSample, out this.inputBuffer);
                DeviceUtil.CreateResamplerBuffer(this.outputBufferSize, out this.outputSample, out this.outputBuffer);

                // Create resampler object
                this.resampler = DeviceUtil.CreateResampler(inFormat, outFormat);
            }).Wait();

            // Set the callback function
            this.dataAvailableCallback = callback;
        }
Esempio n. 3
0
        /// <summary>
        /// Initialize the capturer.
        /// </summary>
        /// <param name="engineLatency">
        /// Number of milliseconds of acceptable lag between live sound being produced and recording operation.
        /// </param>
        /// <param name="gain">
        /// The gain to be applied to the audio after capture.
        /// </param>
        /// <param name="outFormat">
        /// The format of the audio to be captured. If this is NULL, the default audio format of the
        /// capture device will be used.
        /// </param>
        /// <param name="callback">
        /// Callback function delegate which will handle the captured data.
        /// </param>
        /// <param name="speech">
        /// If true, sets the audio category to speech to optimize audio pipeline for speech recognition.
        /// </param>
        public void Initialize(int engineLatency, float gain, WaveFormat outFormat, AudioDataAvailableCallback callback, bool speech)
        {
            // Create our shutdown event - we want a manual reset event that starts in the not-signaled state.
            this.shutdownEvent = new ManualResetEvent(false);

            // Now activate an IAudioClient object on our preferred endpoint and retrieve the mix format for that endpoint.
            object obj = this.endpoint.Activate(ref audioClientIID, ClsCtx.INPROC_SERVER, IntPtr.Zero);

            this.audioClient = (IAudioClient)obj;

            // The following block enables advanced mic array APO pipeline on Windows 10 RS2 builds >= 15004.
            // This must be called before the call to GetMixFormat() in LoadFormat().
            if (speech)
            {
                IAudioClient2 audioClient2 = (IAudioClient2)this.audioClient;
                if (audioClient2 != null)
                {
                    AudioClientProperties properties = new AudioClientProperties
                    {
                        Size     = Marshal.SizeOf <AudioClientProperties>(),
                        Category = AudioStreamCategory.Speech
                    };

                    int hr = audioClient2.SetClientProperties(ref properties);
                    if (hr != 0)
                    {
                        Console.WriteLine("Failed to set audio stream category to AudioCategory_Speech: {0}", hr);
                    }
                }
                else
                {
                    Console.WriteLine("Unable to get IAudioClient2 interface");
                }
            }

            // Load the MixFormat. This may differ depending on the shared mode used.
            this.LoadFormat();

            // Remember our configured latency
            this.engineLatencyInMs = engineLatency;

            // Set the gain
            this.gain = gain;

            // Determine whether or not we need a resampler
            this.resampler = null;

            if (outFormat != null)
            {
                // Check if the desired format is supported
                IntPtr closestMatchPtr;
                IntPtr outFormatPtr = WaveFormat.MarshalToPtr(outFormat);
                int    hr           = this.audioClient.IsFormatSupported(AudioClientShareMode.Shared, outFormatPtr, out closestMatchPtr);

                // Free outFormatPtr to prevent leaking memory
                Marshal.FreeHGlobal(outFormatPtr);

                if (hr == 0)
                {
                    // Replace _MixFormat with outFormat. Since it is supported, we will initialize
                    // the audio capture client with that format and capture without resampling.
                    this.mixFormat    = outFormat;
                    this.mixFrameSize = (this.mixFormat.BitsPerSample / 8) * this.mixFormat.Channels;
                }
                else
                {
                    // In all other cases, we need to resample to OutFormat
                    if ((hr == 1) && (closestMatchPtr != IntPtr.Zero))
                    {
                        // Use closest match suggested by IsFormatSupported() and resample
                        this.mixFormat    = WaveFormat.MarshalFromPtr(closestMatchPtr);
                        this.mixFrameSize = (this.mixFormat.BitsPerSample / 8) * this.mixFormat.Channels;

                        // Free closestMatchPtr to prevent leaking memory
                        Marshal.FreeCoTaskMem(closestMatchPtr);
                    }

                    this.inputBufferSize  = (int)(this.engineLatencyInMs * this.mixFormat.AvgBytesPerSec / 1000);
                    this.outputBufferSize = (int)(this.engineLatencyInMs * outFormat.AvgBytesPerSec / 1000);

                    DeviceUtil.CreateResamplerBuffer(this.inputBufferSize, out this.inputSample, out this.inputBuffer);
                    DeviceUtil.CreateResamplerBuffer(this.outputBufferSize, out this.outputSample, out this.outputBuffer);

                    // Create resampler object
                    this.resampler = DeviceUtil.CreateResampler(this.mixFormat, outFormat);
                }
            }

            this.InitializeAudioEngine();

            // Set the callback function
            this.dataAvailableCallback = callback;
        }