/// <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; }
private void InitializeCaptureDevice(IAudioClient2 audioClientInterface) { this.audioClient = new AudioClient2(audioClientInterface); // Activation complete. Set the client properties AudioClientProperties props = new AudioClientProperties(); props.cbSize = 16; // (uint)System.Runtime.InteropServices.Marshal.SizeOf<AudioClientProperties>(); props.bIsOffload = false; // FALSE props.eCategory = AudioStreamCategory.Communications; // AUDIO_STREAM_CATEGORY::AudioCategory_Communications in C++ props.Options = AudioClientStreamOptions.None; // AUDCLNT_STREAMOPTIONS_NONE in C++ int hresult = audioClientInterface.SetClientProperties(props); if (hresult != 0) { Marshal.ThrowExceptionForHR(hresult); } this.waveFormat = this.audioClient.MixFormat; //if (this.isXMOS) //{ // // the mix format for the XMOS is likely a 6 channel interleaved audio stream that we don't need. // // in theory, we should be able to just request a single channel 48K stream and it will just work // // and it will be good!!! Of course, this doesn't actually work... Set the veil audio mic to be 1 channel // // before doing anything else... // this.waveFormat = WaveFormatExtensible.CreateIeeeFloatWaveFormat(48000, 1); //} long requestedDuration = REFTIMES_PER_MILLISEC * 100; this.frequency = 8 * waveFormat.AverageBytesPerSecond / (waveFormat.Channels * waveFormat.BitsPerSample); this.EventWriterDLL.BuildLine( "+2 start => WasapiCapture::InitializeCaptureDevice => Wave Format =\n" + " => average bytes per second = " + waveFormat.AverageBytesPerSecond + "\n" + " => bits per sample = " + waveFormat.BitsPerSample + "\n" + " => channels = " + waveFormat.Channels + "\n" + " => encoding = " + waveFormat.WaveFormatTag + "\n" + " => extra size = " + waveFormat.ExtraSize + "\n" + " => frequency = " + frequency); hresult = this.audioClient.Initialize(AudioClientShareMode.Shared, AudioClientStreamFlags.EventCallback, requestedDuration, 0, ref this.waveFormat, Guid.Empty); if (hresult == 0) { int bufferFrameCount = this.audioClient.BufferSize; this.bytesPerFrame = this.waveFormat.Channels * this.waveFormat.BitsPerSample / 8; this.recordBuffer = new byte[bufferFrameCount * bytesPerFrame]; this.EventWriterDLL.BuildLine( "+3 => WasapiCapture::InitializeCaptureDevice => " + string.Format("record buffer size = {0}", this.recordBuffer.Length)); // Get back the effective latency from AudioClient this.latencyMilliseconds = (int)(this.audioClient.StreamLatency / 10000); this.initialized = true; } else { this.EventWriterDLL.BuildLine("-3 => WasapiCapture::InitializeCaptureDevice => Error:" + string.Format("{0:X}", hresult)); } }