|
|
Comments and Discussions
|
|
 |

|
The only (original) article I can find on the web currently, to get started with WASAPI in managed .NET
|
|
|
|

|
Does anybody save the given buffer into wav file and play it?
|
|
|
|

|
This is another superb article. It was particularly useful for working out how to create the MMDevice. I eventually worked out how you can create the MFT resampler allowing you to play at any sample rate/bit depth. You have to enumerate through the installed MFTs and use the IMFActivate interface to create it. I'm surprised it let you open the audio device at all as my PC seems to always insist on 48kHz.
I have another idea for playing PCM audio, inspired in part by your other article. The idea is to do SetSource on a MediaElement, but with a content type of WAV and then pass it an implementation of IRandomAccessStream. Unfortunately it is currently crashing Windows, but if I can get it working it may be a simpler solution.
thanks again for this article, and I'll be referring to it again no doubt soon when I get round to adding recording support to NAudio.
|
|
|
|

|
Very useful article.
I start working with this code and face some issues:
1) troubles with capturing mono, IAudioClient.Initialize() fails with AUDCLNT_E_UNSUPPORTED_FORMAT
2) I'm not familiar with VB, what is the correct replacement of
Dim pAudioClient = Await icbh
in C#?
var pAudioClient = await icbh
doesn't work and I have no idea why.
Still looking forward for Win8 version of NAudio and C# wrapper for WASAPI.
|
|
|
|

|
(1) It seems that IAudioClient.Initialize() only succeeds if you give it high rates. On my soundcard it only succeeds with 44100Hz stereo or 48000Hz stereo. I'm still looking for an answer on how to get lower rates.
(2) Your C# translation is correct, "var pAudioClient = await icbh;"
You'll have to let me know the exact compiler-error message you're getting. I suspect you're getting an error because you haven't defined the ICBH's type fully including its GetAwaiter() method, or because it's not accessible.
It uses the new "await" feature in VB/C# introduced in VS2012. Normally you'd do "await t;" where t is an object of type Task. But I made it so I can also await icbh which is my own custom type. I wrote a blog about how to make things awaitable
http://blogs.msdn.com/b/lucian/archive/2012/11/28/how-to-await-a-storyboard-and-other-things.aspx[^] (it's still in VB, but the same rules apply to C#...)
|
|
|
|

|
(2) It was my mistake, I forgot to mark my method as async. Thank you for your article about async/await feature. Now I face another problem: Call of IAudioClient Initialize() method fails with UnathorizedAccessException (HRESULT: 0x80070005 - E_ACCESSDENIED). What I'm doing wrong now?
|
|
|
|

|
Solved. The problem was with permissions. I edit my Packege.appxmanifest, give program permissions to use webcam and microphone, and now I have new Exception, thrown by Initialize() method: Exception from HRESULT: 0x8890008.
|
|
|
|

|
WASAPI is very fussy about what format you use. In shared mode you have to provide audio at the rate the soundcard is running at (usually 44.1kHz or 48kHz). I get round this in NAudio by using the DMO resampler object. I was planning to use the Media Foundation Resampler for Windows Store apps, but it won't let me make one because its not on the "allowed" list of COM objects which is extremely annoying.
|
|
|
|

|
Hi,I had run succeed when I set microphone sample rate.1.
To run Mmsys.cpl, open a Command Prompt window and enter the following command:
control mmsys.cpl
Alternatively, you can run Mmsys.cpl by right-clicking the speaker icon in the notification area, which is located on the right side of the taskbar, and selecting either Playback Devices or Recording Devices.
2.After the Mmsys.cpl window opens, select a device from either the list of playback devices or the list of recording devices, and click Properties.
3.When the properties window opens, click Advanced, and select a format from the list of available formats in the box labeled Default Format.
So,I want to set sample rate in the sample,I don't known I should do what.
Thanks!
|
|
|
|

|
Hi,I don't known run if I set sample rate other value.Thanks!
|
|
|
|

|
I didn't run the sample,I use windows 8 rpo .prompt Exception that "HRESULT:88890008",thanks!
|
|
|
|

|
You'll have to tell us where exactly the exception was thrown. Is impossible to diagnose otherwise.
|
|
|
|

|
I test the sample just now,display System.Runtime.InteropServices.COMException. prompt “C:\windows\system32\WinMetadata\Windows.UI.Xaml.winmd” Can't find or open PDB file,
“C:\windows\system32\WinMetadata\Windows.ApplicationModel.winmd” Can't find or open PDB file,
“C:\windows\system32\WinMetadata\Windows.UI.winmd” .thanks!
|
|
|
|

|
prompt Exception HRESULT:0x88890008
|
|
|
|

|
Why do you use Short[] buffer instead of Byte[] buffer to keep recorded audio. Can you clarify recording audio part?
Thank you!
|
|
|
|

|
It would work with byte just fine.
In this sample I configured it to capture 16bit stereo audio -- in other words, each sample consists of 4 bytes: two bytes for a 16bit sample on the left channel, and two bytes for a 16bit sample on the right channel. Another way to say the same thing is that each sample consists of 2 shorts: one short for a 16bit sample on the left channel, and one short for a 16bit sample on the right channel.
By storing it as Short(), it was easier for me to manipulate it (e.g. for my to synthesize a waveform, or to scale it up to run FFT)
|
|
|
|

|
I see. So basically if I take every odd or even sample I'd get single channel wave audio?
|
|
|
|

|
I have noticed there is a synchronization issue (delay) when capture pass around one and half minute recording. Basically I am trying to process pcm for sound level peak but delay start after a minute and continue to increase delay. Why this behavior?
|
|
|
|

|
Hi!
In your sample code (good work btw.) you use 44kHz 16bit stereo to record audio.
After porting the sample to C# and playing around with the settings a bit, I found that (at least on my Win7 machine) IAudioClient.Initialize() always fails with AUDCLNT_E_UNSUPPORTED_FORMAT when I try to set anything less than your settings (22kHz stereo or 44kHz mono or 22kHz mono) for recording.
IAudioClient.IsFormatSupported() always suggests a hilariously overpowered format (44kHz 32bit stereo) in this case (or, depending on the audio device, even 48kHz) when all I want is to record 22kHz 16bit mono.
With WaveIO it was (and is) no problem to get less-than-top-notch recordings. Do I really have to use maximum quality and then downsample myself? I can hardly believe this.
Could you perhaps share a little light on the topic?
Thanks in advance,
mav
Regards,
mav
--
Black holes are the places where God divided by 0...
|
|
|
|

|
This articles seems very useful, but I need it's c# version.
|
|
|
|

|
Hi Farhan. I'm a VB coder myself. But if you or anyone wants email me a C# translation (under the same Public Domain license conditions), I'd be happy to put it up.
|
|
|
|

|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
namespace HuaXuApp
{
//IAudioClient sample, (c) 2012 Lucian Wischik
//------------------------------------------------
//In Windows 8 store apps, the waveOut family of functions are no longer available.
//The closest alternative is IAudioClient. In this sample we construct PCM buffers by
// writing to a memory array, and send them to the IAudioClient.
//IAudioClient has a simple playback sample here: http://msdn.microsoft.com/en-us/library/windows/desktop/dd316756(v=vs.85).aspx
//and a capture sample here: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370800(v=vs.85).aspx
//For Win8 app store you obtain IAudioClient slightly differently, as in this sample: http://code.msdn.microsoft.com/windowsapps/Windows-Audio-Session-22dcab6b
//Module Module1
//Sub Main()
// Dim p As New ConsoleProgress
// p.Report("---- Using .NET45 ----")
// TestFromConsoleAsync(p).GetAwaiter().GetResult()
// p.Report("---- WinRT ----")
// TestFromWin8Async(p).GetAwaiter().GetResult()
//End Sub
//Class ConsoleProgress
// Implements IProgress(Of String)
// Public Sub Report(value As String) Implements IProgress(Of String).Report
// Console.WriteLine(value)
// End Sub
//End Class
//Async Function TestFromConsoleAsync(progress As IProgress(Of String)) As Task
// Dim pEnum = CType(New MMDeviceEnumerator, IMMDeviceEnumerator)
// // Enumerating devices...
// Dim pDevices As IMMDeviceCollection = Nothing : pEnum.EnumAudioEndpoints(EDataFlow.eAll, DeviceStateFlags.DEVICE_STATE_ACTIVE, pDevices)
// Dim pcDevices = 0 : pDevices.GetCount(pcDevices)
// For i = 0 To pcDevices - 1
// Dim pDevice As IMMDevice = Nothing : pDevices.Item(i, pDevice)
// Dim pProps As IPropertyStore = Nothing : pDevice.OpenPropertyStore(StgmMode.STGM_READ, pProps)
// Dim varName As PROPVARIANT = Nothing : pProps.GetValue(PKEY_Device_FriendlyName, varName)
// progress.Report(CStr(varName.Value))
// PropVariantClear(varName)
// Runtime.InteropServices.Marshal.FinalReleaseComObject(pProps) : pProps = Nothing
// Runtime.InteropServices.Marshal.FinalReleaseComObject(pDevice)
// Next
// Runtime.InteropServices.Marshal.FinalReleaseComObject(pDevices) : pDevices = Nothing
// progress.Report("")
// // Generating audio programmatically
// Dim buf1 = Await GenerateAudioAsync()
// // Recording audio
// Dim pDeviceR As IMMDevice = Nothing : pEnum.GetDefaultAudioEndpoint(EDataFlow.eCapture, ERole.eConsole, pDeviceR)
// Dim pAudioClientR As IAudioClient2 = Nothing : pDeviceR.Activate(IID_IAudioClient2, CLSCTX.CLSCTX_ALL, Nothing, pAudioClientR)
// Runtime.InteropServices.Marshal.FinalReleaseComObject(pDeviceR) : pDeviceR = Nothing
// InitializeAudioClient(pAudioClientR)
// Dim reportInstructionsTask = ReportInstructionsAsync(progress)
// Dim buf2 = Await RecordSoundFromAudioClientAsync(pAudioClientR, progress)
// Await reportInstructionsTask
// Await Task.Delay(100)
// Runtime.InteropServices.Marshal.FinalReleaseComObject(pAudioClientR) : pAudioClientR = Nothing
// // Playing audio (two streams simultaneously - they share the audio device, but must be done on different IAudioClients)
// Dim pDeviceP1 As IMMDevice = Nothing : pEnum.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eConsole, pDeviceP1)
// Dim pAudioClientP1 As IAudioClient2 = Nothing : pDeviceP1.Activate(IID_IAudioClient2, CLSCTX.CLSCTX_ALL, Nothing, pAudioClientP1)
// Runtime.InteropServices.Marshal.FinalReleaseComObject(pDeviceP1) : pDeviceP1 = Nothing
// InitializeAudioClient(pAudioClientP1)
// //
// Dim pDeviceP2 As IMMDevice = Nothing : pEnum.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eConsole, pDeviceP2)
// Dim pAudioClientP2 As IAudioClient2 = Nothing : pDeviceP2.Activate(IID_IAudioClient2, CLSCTX.CLSCTX_ALL, Nothing, pAudioClientP2)
// Runtime.InteropServices.Marshal.FinalReleaseComObject(pDeviceP2) : pDeviceP2 = Nothing
// InitializeAudioClient(pAudioClientP2)
// //
// Dim playTask1 = PlaySoundOnAudioClientAsync(pAudioClientP1, buf1, progress)
// Dim playTask2 = PlaySoundOnAudioClientAsync(pAudioClientP2, buf2, progress)
// Await Task.WhenAll(playTask1, playTask2)
// //
// Runtime.InteropServices.Marshal.FinalReleaseComObject(pAudioClientP1) : pAudioClientP1 = Nothing
// Runtime.InteropServices.Marshal.FinalReleaseComObject(pAudioClientP2) : pAudioClientP2 = Nothing
// Runtime.InteropServices.Marshal.FinalReleaseComObject(pEnum) : pEnum = Nothing
//End Function
public class AudioClient
{
public async Task TestFromWin8Async( IProgress progress)
{
string defaultDeviceIdR = Windows.Media.Devices.MediaDevice.GetDefaultAudioCaptureId(Windows.Media.Devices.AudioDeviceRole.Default);
string defaultDeviceIdP = Windows.Media.Devices.MediaDevice.GetDefaultAudioRenderId(Windows.Media.Devices.AudioDeviceRole.Default);
// We can enumerate devices as follows:
string audioSelectorR = Windows.Media.Devices.MediaDevice.GetAudioCaptureSelector();
//var devicesR = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(audioSelectorR, {Interop.PKEY_AudioEndpoint_Supports_EventDriven_Mode.ToString()});
Windows.Devices.Enumeration.DeviceInformationCollection devicesR = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(audioSelectorR, new List() { Interop.PKEY_AudioEndpoint_Supports_EventDriven_Mode.ToString() });
string audioSelectorP = Windows.Media.Devices.MediaDevice.GetAudioRenderSelector();
//var devicesP = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(audioSelectorP, {PKEY_AudioEndpoint_Supports_EventDriven_Mode.ToString()});
Windows.Devices.Enumeration.DeviceInformationCollection devicesP = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(audioSelectorP, new List() { Interop.PKEY_AudioEndpoint_Supports_EventDriven_Mode.ToString() });
foreach(var device in devicesR.Concat(devicesP))
{
progress.Report((device.Id == defaultDeviceIdP ? "P ":(device.Id == defaultDeviceIdR ? "R ":" ")) + device.Name);
}
progress.Report("");
// Generating audio programmatically
short[] buf1 = await GenerateAudioAsync();
// Recording audio
// CAPABILITY: Microphone
// PERMISSION: Microphone - it pops up a blocking alert in InitializeAudioClient the first time after installation, and
// on subsequent attempts it either succeeds or fails depending on whether the user has granted permission
//Dim icbhR As New ActivateAudioInterfaceCompletionHandler(AddressOf InitializeAudioClient)
ActivateAudioInterfaceCompletionHandler icbhR = new ActivateAudioInterfaceCompletionHandler(
(IAudioClient2 pAudioClient)=>
{
try
{
WAVEFORMATEX wfx = new WAVEFORMATEX() { wFormatTag = 1, nChannels = 2, nSamplesPerSec = 44100, wBitsPerSample = 16, nBlockAlign = 4, nAvgBytesPerSec = 44100 * 4, cbSize = 0 };
pAudioClient.Initialize(AUDCLNT_SHAREMODE.AUDCLNT_SHAREMODE_SHARED, AUDCLNT_FLAGS.AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_FLAGS.AUDCLNT_STREAMFLAGS_NOPERSIST, 10000000, 0, ref wfx,IntPtr.Zero);
}
catch (Exception ex)
{
//System.Diagnostics.Debug.WriteLine((AUDCLNT_FLAGS.AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_FLAGS.AUDCLNT_STREAMFLAGS_NOPERSIST).ToString());
//new Windows.UI.Popups.MessageDialog().ShowAsync();
}
}
);
IActivateAudioInterfaceAsyncOperation activationOperationR = null;
Interop.ActivateAudioInterfaceAsync(defaultDeviceIdR, Interop.IID_IAudioClient2, IntPtr.Zero, icbhR, ref activationOperationR);
short[] buf2 = {0, 0};
try
{
IAudioClient2 pAudioClientR = await icbhR;
var reportInstructionsTask = ReportInstructionsAsync(progress);
buf2 = await RecordSoundFromAudioClientAsync(pAudioClientR, progress);
Marshal.FinalReleaseComObject(pAudioClientR);
pAudioClientR = null;
await reportInstructionsTask;
}
catch( UnauthorizedAccessException ex /*When ex.HResult = -2147024891*/)
{
progress.Report("OOPS! Can//t record. Please go to Charms > Settings > Permissions to grant microphone permission");
}
finally
{
Marshal.FinalReleaseComObject(activationOperationR);
activationOperationR = null;
}
// Playing audio (two streams simultaneously - they share the audio device, but must be done on different IAudioClients)
ActivateAudioInterfaceCompletionHandler icbhP1 = new ActivateAudioInterfaceCompletionHandler(InitializeAudioClient);
IActivateAudioInterfaceAsyncOperation activationOperationP1 = null;
Interop.ActivateAudioInterfaceAsync(defaultDeviceIdP, Interop.IID_IAudioClient2, IntPtr.Zero, icbhP1, ref activationOperationP1);
var pAudioClientP1 = await icbhP1;
Marshal.FinalReleaseComObject(activationOperationP1);
activationOperationP1 = null;
//
ActivateAudioInterfaceCompletionHandler icbhP2 = new ActivateAudioInterfaceCompletionHandler(InitializeAudioClient);
IActivateAudioInterfaceAsyncOperation activationOperationP2 = null;
Interop.ActivateAudioInterfaceAsync(defaultDeviceIdP, Interop.IID_IAudioClient2, IntPtr.Zero, icbhP2, ref activationOperationP2);
var pAudioClientP2 = await icbhP2;
Marshal.FinalReleaseComObject(activationOperationP2);
activationOperationP2 = null;
//
var playTask1 = PlaySoundOnAudioClientAsync(pAudioClientP1, buf1, progress);
var playTask2 = PlaySoundOnAudioClientAsync(pAudioClientP2, buf2, progress);
await Task.WhenAll(playTask1, playTask2);
//
Marshal.FinalReleaseComObject(pAudioClientP1);
pAudioClientP1 = null;
Marshal.FinalReleaseComObject(pAudioClientP2);
pAudioClientP2 = null;
}
public Task GenerateAudioAsync()
{
return Task.Run(
()=> {
int[] MaryHadALittleLamb = {247, 220, 196, 220, 247, 247, 247, 220, 220, 220, 247, 294, 294};
short[] buf1 = new short[44100 * 2 * MaryHadALittleLamb.Length * 8 / 10];
var phase = 0.0;
for(int i = 0 ;i<= buf1.Length / 2 - 1;i++)
{
int imusic = MaryHadALittleLamb.Length * 10 * i * 2 / buf1.Length;
double freq = (imusic % 10 == 0 ? 0: MaryHadALittleLamb[imusic / 10]);
phase += freq * 2.0 * 3.1415 / 44100.0;
short amp = Convert.ToInt16(Math.Sin(phase) * 0.5 * short.MaxValue - 1);
buf1[i * 2 + 0] = amp;
buf1[i * 2 + 1] = amp;
}
return buf1;
});
}
public async Task ReportInstructionsAsync(IProgress progress)
{
progress.Report(@"RECORDING HAS STARTED... please sing ""Mary had a little lamb""!" + Environment.NewLine + "...");
string[] str = {"Mary", "", "had", "a ", "little ", "", "lamb, ", "little ", "", "lamb, ", "little ", "", "lamb"};
foreach(string word in str)
{
if (!string.IsNullOrEmpty(word))
{
progress.Report(word);
}
await Task.Delay(800);
}
}
public void InitializeAudioClient(IAudioClient2 pAudioClient)
{
WAVEFORMATEX wfx = new WAVEFORMATEX() { wFormatTag = 1, nChannels = 2, nSamplesPerSec = 44100, wBitsPerSample = 16, nBlockAlign = 4, nAvgBytesPerSec = 44100 * 4, cbSize = 0 };
// Other alternative ways we could pick wave-format... (note that IsFormatSupported will return pwfx=Nothing if wfx was satisfactory)
IntPtr pwfx_default = IntPtr.Zero;
pAudioClient.GetMixFormat(ref pwfx_default); // we could ask it for its preferred format
WAVEFORMATEX wfx_default = (WAVEFORMATEX)(Marshal.PtrToStructure(pwfx_default,typeof(WAVEFORMATEX)));
if(pwfx_default != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(pwfx_default);
pwfx_default = IntPtr.Zero;
}
IntPtr pwfx_suggested = IntPtr.Zero;
pAudioClient.IsFormatSupported(AUDCLNT_SHAREMODE.AUDCLNT_SHAREMODE_SHARED, ref wfx, ref pwfx_suggested);
if(pwfx_suggested != IntPtr.Zero)
{
var wfx_suggested = (WAVEFORMATEX)(Marshal.PtrToStructure(pwfx_suggested, typeof(WAVEFORMATEX)));
Marshal.FreeCoTaskMem(pwfx_suggested);
}
// If pAudioClient was initialized with IID_ICaptureClient, then...
// CAPABILITY: Microphone
// PERMISSION: Microphone (granted on the charms bar). The first time this app is run after installation, the call to
// Initialize() will block while a UI prompt is shown asking for permission. On subsequent runs, it will remember the
// answer, and either succeed immediately or return E_ACCESS_DENIED (UnathorizedAccessException When ex.HResult = -2147024891)
pAudioClient.Initialize(AUDCLNT_SHAREMODE.AUDCLNT_SHAREMODE_SHARED, AUDCLNT_FLAGS.AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_FLAGS.AUDCLNT_STREAMFLAGS_NOPERSIST, 10000000, 0, ref wfx, IntPtr.Zero);
}
public async Task RecordSoundFromAudioClientAsync(IAudioClient2 pAudioClient,IProgress progress)
{
IntPtr hEvent = Interop.CreateEventEx(IntPtr.Zero, IntPtr.Zero, 0, EventAccess.EVENT_ALL_ACCESS);
pAudioClient.SetEventHandle(hEvent);
int bufferFrameCount = 0;
pAudioClient.GetBufferSize(ref bufferFrameCount);
object ppv = null;
pAudioClient.GetService(Interop.IID_IAudioCaptureClient, ref ppv);
IAudioCaptureClient pCaptureClient = (IAudioCaptureClient)(ppv);
short[] buf = new short[44100 * 10 * 2]; // ten seconds of audio
int nFrames = 0;
pAudioClient.Start();
while(true)
{
await Interop.WaitForSingleObjectAsync(hEvent);
IntPtr pData = IntPtr.Zero; int NumFramesToRead = 0; int dwFlags = 0;
pCaptureClient.GetBuffer(ref pData, ref NumFramesToRead, ref dwFlags, IntPtr.Zero, IntPtr.Zero);
int nFramesToCopy = Math.Min(NumFramesToRead, buf.Length / 2 - nFrames);
Marshal.Copy(pData, buf, nFrames * 2, nFramesToCopy * 2);
pCaptureClient.ReleaseBuffer(NumFramesToRead);
nFrames += nFramesToCopy;
if(nFrames >= buf.Length / 2) break;
}
pAudioClient.Stop();
Marshal.FinalReleaseComObject(pCaptureClient);
pCaptureClient = null;
Interop.CloseHandle(hEvent);
hEvent = IntPtr.Zero;
return buf;
}
public async Task PlaySoundOnAudioClientAsync(IAudioClient2 pAudioClient, short[] buf,IProgress progress)
{
IntPtr hEvent = Interop.CreateEventEx(IntPtr.Zero, IntPtr.Zero, 0, EventAccess.EVENT_ALL_ACCESS);
pAudioClient.SetEventHandle(hEvent);
int bufferFrameCount = 0;
pAudioClient.GetBufferSize(ref bufferFrameCount);
object ppv = null;
pAudioClient.GetService(Interop.IID_IAudioRenderClient, ref ppv);
IAudioRenderClient pRenderClient = (IAudioRenderClient)(ppv);
int nFrame = 0;
pAudioClient.Start();
while(true)
{
await Interop.WaitForSingleObjectAsync(hEvent);
int numFramesPadding = 0;
pAudioClient.GetCurrentPadding(ref numFramesPadding);
int numFramesAvailable = bufferFrameCount - numFramesPadding;
if(numFramesAvailable == 0 ) continue;
int numFramesToCopy = Math.Min(numFramesAvailable, buf.Length / 2 - nFrame);
IntPtr pData = IntPtr.Zero;
pRenderClient.GetBuffer(numFramesToCopy, ref pData);
Marshal.Copy(buf, nFrame * 2, pData, numFramesToCopy * 2);
pRenderClient.ReleaseBuffer(numFramesToCopy, 0);
nFrame += numFramesToCopy;
if(nFrame >= buf.Length / 2) break;
}
// and wait until the buffer plays out to the end
while(true)
{
int numFramesPadding = 0;
pAudioClient.GetCurrentPadding(ref numFramesPadding);
if(numFramesPadding == 0) break;
await Task.Delay(20);
}
pAudioClient.Stop();
Marshal.FinalReleaseComObject(pRenderClient);
pRenderClient = null;
Interop.CloseHandle(hEvent);
hEvent = IntPtr.Zero;
}
}
public class ActivateAudioInterfaceCompletionHandler: IActivateAudioInterfaceCompletionHandler, IAgileObject
{
private Action InitializeAction;
private TaskCompletionSource tcs = new TaskCompletionSource();
public ActivateAudioInterfaceCompletionHandler(Action InitializeAction)
{
if(InitializeAction == null)throw new NullReferenceException(@"Must do IAudioClient.Initialize in the callback; in case of recording, it might pop up a permissions prompt, and block");
this.InitializeAction = InitializeAction;
}
void IActivateAudioInterfaceCompletionHandler.ActivateCompleted(IActivateAudioInterfaceAsyncOperation activateOperation )
{
// First get the activation results, and see if anything bad happened then
int hr = 0;object unk = null;
activateOperation.GetActivateResult(ref hr, ref unk);
if(hr != 0)
{
tcs.TrySetException(Marshal.GetExceptionForHR(hr, new IntPtr(-1)));
return;
}
IAudioClient2 pAudioClient = (IAudioClient2)(unk);
// Next try to call the client//s (synchronous, blocking) initialization method. We trust that WinRT
// has invoked our callback on a thread where it//s okay to block.
try
{
InitializeAction(pAudioClient);
tcs.SetResult(pAudioClient);
}
catch(Exception ex)
{
tcs.TrySetException(ex);
}
}
public TaskAwaiter GetAwaiter()
{
return tcs.Task.GetAwaiter();
}
// Class MMDeviceEnumerator
//End Class
//
//Public Interface IMMDeviceEnumerator
// Sub EnumAudioEndpoints(dataflow As EDataFlow, dwStateMask As DeviceStateFlags, ByRef ppDevices As IMMDeviceCollection)
// Sub GetDefaultAudioEndpoint(dataflow As EDataFlow, role As ERole, ByRef ppDevice As IMMDevice)
// Sub GetDevice( pwstrId As String, ByRef ppDevice As IntPtr)
// Sub RegisterEndpointNotificationCallback(pClient As IntPtr)
// Sub UnregisterEndpointNotificationCallback(pClient As IntPtr)
//End Interface
//
//Public Interface IMMDeviceCollection
// Sub GetCount(ByRef pcDevices As Integer)
// Sub Item(nDevice As Integer, ByRef ppDevice As IMMDevice)
//End Interface
//
//Public Interface IMMDevice
// Sub Activate( iid As Guid, dwClsCtx As CLSCTX, pActivationParams As IntPtr, ByRef ppInterface As IAudioClient2)
// Sub OpenPropertyStore(stgmAccess As Integer, ByRef ppProperties As IPropertyStore)
// Sub GetId( ByRef ppstrId As String)
// Sub GetState(ByRef pdwState As Integer)
//End Interface
//
//Public Interface IPropertyStore
// //virtual HRESULT STDMETHODCALLTYPE GetCount(/*[out]*/ __RPC__out DWORD *cProps)
// Sub GetCount(ByRef cProps As Integer)
// //virtual HRESULT STDMETHODCALLTYPE GetAt(/*Runtime.InteropServices.In*/ DWORD iProp, /*[out]*/ __RPC__out PROPERTYKEY *pkey)
// Sub GetAt(iProp As Integer, ByRef pkey As IntPtr)
// //virtual HRESULT STDMETHODCALLTYPE GetValue(/*Runtime.InteropServices.In*/ __RPC__in REFPROPERTYKEY key, /*[out]*/ __RPC__out PROPVARIANT *pv)
// Sub GetValue(ByRef key As PROPERTYKEY, ByRef pv As PROPVARIANT)
// //virtual HRESULT STDMETHODCALLTYPE SetValue(/*Runtime.InteropServices.In*/ __RPC__in REFPROPERTYKEY key, /*Runtime.InteropServices.In*/ __RPC__in REFPROPVARIANT propvar)
// Sub SetValue(ByRef key As PROPERTYKEY, ByRef propvar As IntPtr)
// //virtual HRESULT STDMETHODCALLTYPE Commit()
// Sub Commit()
//End Interface
}
public static class Interop
{
public static Task WaitForSingleObjectAsync(IntPtr hEvent)
{
return Task.Run(() =>
{
int r = WaitForSingleObjectEx(hEvent, unchecked((int)0xFFFFFFFF), true);
if (r != 0) { throw new Exception("Unexpected event"); }
}
);
}
[DllImport("Mmdevapi.dll", ExactSpelling = true, PreserveSig = false)]
public static extern void ActivateAudioInterfaceAsync([MarshalAs(UnmanagedType.LPWStr)] string deviceInterfacePath, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, IntPtr activationParams, IActivateAudioInterfaceCompletionHandler completionHandler, ref IActivateAudioInterfaceAsyncOperation activationOperation);
//
//Public Sub PropVariantClear(ByRef pvar As PROPVARIANT)
//End Sub
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = false, PreserveSig = true, SetLastError = true)]
public static extern IntPtr CreateEventEx(IntPtr lpEventAttributes, IntPtr lpName, int dwFlags, EventAccess dwDesiredAccess);
[DllImport("kernel32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
public static extern int WaitForSingleObjectEx(IntPtr hEvent, int milliseconds, bool bAlertable);
//Public ReadOnly PKEY_Device_FriendlyName As New PROPERTYKEY With {.fmtid = New Guid("a45c254e-df1c-4efd-8020-67d146a850e0"), .pid = 14}
//Public ReadOnly PKEY_Device_DeviceDesc As New PROPERTYKEY With {.fmtid = New Guid("a45c254e-df1c-4efd-8020-67d146a850e0"), .pid = 2}
public static readonly PROPERTYKEY PKEY_AudioEndpoint_Supports_EventDriven_Mode = new PROPERTYKEY() { fmtid = new Guid("1da5d803-d492-4edd-8c23-e0c0ffee7f0e"), pid = 7 };
public static readonly Guid IID_IAudioClient = new Guid("1CB9AD4C-DBFA-4c32-B178-C2F568A703B2");
public static readonly Guid IID_IAudioClient2 = new Guid("726778CD-F60A-4eda-82DE-E47610CD78AA");
public static readonly Guid IID_IAudioRenderClient = new Guid("F294ACFC-3146-4483-A7BF-ADDCA7C260E2");
public static readonly Guid IID_IAudioCaptureClient = new Guid("C8ADBD64-E71E-48a0-A4DE-185C395CD317");
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("1CB9AD4C-DBFA-4c32-B178-C2F568A703B2")]
public interface IAudioClient
{
void Initialize(AUDCLNT_SHAREMODE ShareMode, AUDCLNT_FLAGS StreamFlags, long hnsBufferDuration, long hnsPeriodicity, ref WAVEFORMATEX pFormat, IntPtr AudioSessionGuid);
//virtual HRESULT STDMETHODCALLTYPE GetBufferSize(/*[out]*/ _Out_ UINT32 *pNumBufferFrames) = 0;
void GetBufferSize(ref int pNumBufferFrames);
//virtual HRESULT STDMETHODCALLTYPE GetStreamLatency(/*[out]*/ _Out_ REFERENCE_TIME *phnsLatency) = 0;
void GetStreamLatency(ref long phnsLatency);
//virtual HRESULT STDMETHODCALLTYPE GetCurrentPadding(/*[out]*/ _Out_ UINT32 *pNumPaddingFrames) = 0;
void GetCurrentPadding(ref int pNumPaddingFrames);
//virtual HRESULT STDMETHODCALLTYPE IsFormatSupported(/*[in]*/ _In_ AUDCLNT_SHAREMODE ShareMode, /*[in]*/ _In_ const WAVEFORMATEX *pFormat, /*[unique][out]*/ _Out_opt_ WAVEFORMATEX **ppClosestMatch) = 0;
void IsFormatSupported(AUDCLNT_SHAREMODE ShareMode, ref WAVEFORMATEX pFormat, ref IntPtr ppClosestMatch);
//virtual HRESULT STDMETHODCALLTYPE GetMixFormat(/*[out]*/ _Out_ WAVEFORMATEX **ppDeviceFormat) = 0;
void GetMixFormat(ref IntPtr ppDeviceFormat);
//virtual HRESULT STDMETHODCALLTYPE GetDevicePeriod(/*[out]*/ _Out_opt_ REFERENCE_TIME *phnsDefaultDevicePeriod, /*[out]*/ _Out_opt_ REFERENCE_TIME *phnsMinimumDevicePeriod) = 0;
void GetDevicePeriod(ref long phnsDefaultDevicePeriod, ref long phnsMinimumDevicePeriod);
//virtual HRESULT STDMETHODCALLTYPE Start( void) = 0;
void Start();
//virtual HRESULT STDMETHODCALLTYPE Stop( void) = 0;
void Stop();
//virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
void Reset();
//virtual HRESULT STDMETHODCALLTYPE SetEventHandle(/*[in]*/ HANDLE eventHandle) = 0;
void SetEventHandle(IntPtr eventHandle);
//virtual HRESULT STDMETHODCALLTYPE GetService(/*[in]*/ _In_ REFIID riid, /*[iid_is][out]*/ _Out_ void **ppv) = 0;
void GetService([MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.IUnknown)] ref object ppv);
}
[ComImport,InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("726778CD-F60A-4eda-82DE-E47610CD78AA")]
public interface IAudioClient2
{
void Initialize(AUDCLNT_SHAREMODE ShareMode, AUDCLNT_FLAGS StreamFlags, long hnsBufferDuration, long hnsPeriodicity, ref WAVEFORMATEX pFormat, IntPtr AudioSessionGuid);
void GetBufferSize(ref int pNumBufferFrames);
void GetStreamLatency(ref long phnsLatency);
void GetCurrentPadding(ref int pNumPaddingFrames);
void IsFormatSupported(AUDCLNT_SHAREMODE ShareMode, ref WAVEFORMATEX pFormat, ref IntPtr ppClosestMatch);
void GetMixFormat(ref IntPtr ppDeviceFormat);
void GetDevicePeriod(ref long phnsDefaultDevicePeriod, ref long phnsMinimumDevicePeriod);
void Start();
void Stop();
void Reset();
void SetEventHandle(IntPtr eventHandle);
void GetService([MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.IUnknown)] ref object ppv);
//virtual HRESULT STDMETHODCALLTYPE IsOffloadCapable(/*[in]*/ _In_ AUDIO_STREAM_CATEGORY Category, /*[in]*/ _Out_ BOOL *pbOffloadCapable) = 0;
void IsOffloadCapable(int Category, ref bool pbOffloadCapable);
//virtual HRESULT STDMETHODCALLTYPE SetClientProperties(/*[in]*/ _In_ const AudioClientProperties *pProperties) = 0;
void SetClientProperties(IntPtr pProperties);
//virtual HRESULT STDMETHODCALLTYPE GetBufferSizeLimits(/*[in]*/ _In_ const WAVEFORMATEX *pFormat, /*[in]*/ _In_ BOOL bEventDriven, /*[in]*/ _Out_ REFERENCE_TIME *phnsMinBufferDuration, /*[in]*/ _Out_ REFERENCE_TIME *phnsMaxBufferDuration) = 0;
void GetBufferSizeLimits(IntPtr pFormat, bool bEventDriven, IntPtr phnsMinBufferDuration, IntPtr phnsMaxBufferDuration);
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("F294ACFC-3146-4483-A7BF-ADDCA7C260E2")]
public interface IAudioRenderClient
{
//virtual HRESULT STDMETHODCALLTYPE GetBuffer(/*[in]*/ _In_ UINT32 NumFramesRequested, /*[out]*/ _Outptr_result_buffer_(_Inexpressible_("NumFramesRequested * pFormat->nBlockAlign")) BYTE **ppData) = 0;
void GetBuffer(int NumFramesRequested, ref IntPtr ppData);
//virtual HRESULT STDMETHODCALLTYPE ReleaseBuffer(/*[in]*/ _In_ UINT32 NumFramesWritten, /*[in]*/ _In_ DWORD dwFlags) = 0;
void ReleaseBuffer(int NumFramesWritten, int dwFlags);
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown),Guid("C8ADBD64-E71E-48a0-A4DE-185C395CD317")]
public interface IAudioCaptureClient
{
//virtual HRESULT STDMETHODCALLTYPE GetBuffer(/*[out]*/ _Outptr_result_buffer_(_Inexpressible_("*pNumFramesToRead * pFormat->nBlockAlign")) BYTE **ppData, /*[out]*/ _Out_ UINT32 *pNumFramesToRead, /*[out]*/_Out_ DWORD *pdwFlags, /*[out]*/_Out_opt_ UINT64 *pu64DevicePosition, /*[out]*/_Out_opt_ UINT64 *pu64QPCPosition) = 0;
void GetBuffer(ref IntPtr ppData, ref int pNumFramesToRead, ref int pdwFlags, IntPtr pu64DevicePosition, IntPtr pu64QPCPosition);
//virtual HRESULT STDMETHODCALLTYPE ReleaseBuffer(/*[in]*/ _In_ UINT32 NumFramesRead) = 0;
void ReleaseBuffer(int NumFramesRead);
//virtual HRESULT STDMETHODCALLTYPE GetNextPacketSize(/*[out]*/ _Out_ UINT32 *pNumFramesInNextPacket) = 0;
void GetNextPacketSize(ref int pNumFramesInNextPacket);
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("41D949AB-9862-444A-80F6-C261334DA5EB")]
public interface IActivateAudioInterfaceCompletionHandler
{
//virtual HRESULT STDMETHODCALLTYPE ActivateCompleted(/*[in]*/ _In_ IActivateAudioInterfaceAsyncOperation *activateOperation) = 0;
void ActivateCompleted(IActivateAudioInterfaceAsyncOperation activateOperation);
}
[ComImport,InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("72A22D78-CDE4-431D-B8CC-843A71199B6D")]
public interface IActivateAudioInterfaceAsyncOperation
{
//virtual HRESULT STDMETHODCALLTYPE GetActivateResult(/*[out]*/ _Out_ HRESULT *activateResult, /*[out]*/ _Outptr_result_maybenull_ IUnknown **activatedInterface) = 0;
void GetActivateResult(ref int activateResult, [MarshalAs(UnmanagedType.IUnknown)] ref object activateInterface);
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("94ea2b94-e9cc-49e0-c0ff-ee64ca8f5b90")]
public interface IAgileObject
{}
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct WAVEFORMATEX
{
public short wFormatTag;
public short nChannels;
public int nSamplesPerSec;
public int nAvgBytesPerSec;
public short nBlockAlign;
public short wBitsPerSample;
public short cbSize;
}
// Public Structure PROPVARIANT
// Dim vt As UShort
// Dim wReserved1 As UShort
// Dim wReserved2 As UShort
// Dim wReserved3 As UShort
// Dim p As IntPtr
// Dim p2 As Integer
// ReadOnly Property Value As Object
// Get
// Select Case vt
// Case 31 : Return Runtime.InteropServices.Marshal.PtrToStringUni(p) // VT_LPWSTR
// Case Else
// Throw New NotImplementedException
// End Select
// End Get
// End Property
//End Structure
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct PROPERTYKEY
{
[MarshalAs(UnmanagedType.Struct)]
public Guid fmtid;
public int pid;
public override string ToString()
{
return "{" + fmtid.ToString() + "} " + pid.ToString();
}
}
//Enum EDataFlow
// eRender = 0
// eCapture = 1
// eAll = 2
// EDataFlow_enum_count = 3
//End Enum
//Enum ERole
// eConsole = 0
// eMultimedia = 1
// eCommunications = 2
// ERole_enum_count = 3
//End Enum
//Enum StgmMode
// STGM_READ = 0
// STGM_WRITE = 1
// STGM_READWRITE = 2
//End Enum
public enum AUDCLNT_SHAREMODE
{
AUDCLNT_SHAREMODE_SHARED = 0,
AUDCLNT_SHAREMODE_EXCLUSIVE = 1
}
// Enum DeviceStateFlags
// DEVICE_STATE_ACTIVE = 1
// DEVICE_STATE_DISABLED = 2
// DEVICE_STATE_NOTPRESENT = 4
// DEVICE_STATE_UNPLUGGED = 8
// DEVICE_STATEMASK_ALL = 15
//End Enum
[Flags]
public enum AUDCLNT_FLAGS
{
AUDCLNT_STREAMFLAGS_CROSSPROCESS = 0x10000,
AUDCLNT_STREAMFLAGS_LOOPBACK = 0x20000,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK = 0x40000,
AUDCLNT_STREAMFLAGS_NOPERSIST = 0x80000,
AUDCLNT_STREAMFLAGS_RATEADJUST = 0x100000,
AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED = 0x10000000,
AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE = 0x20000000,
AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED = 0x40000000
}
[Flags]
public enum EventAccess
{
STANDARD_RIGHTS_REQUIRED = 0x000F0000,
SYNCHRONIZE = 0x00100000,
EVENT_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3),
EVENT_MODIFY_STATE = 0x0002,
}
// Enum CLSCTX
// CLSCTX_INPROC_SERVER = 1
// CLSCTX_INPROC_HANDLER = 2
// CLSCTX_LOCAL_SERVER = 4
// CLSCTX_REMOTE_SERVER = 16
// CLSCTX_ALL = CLSCTX_INPROC_SERVER Or CLSCTX_INPROC_HANDLER Or CLSCTX_LOCAL_SERVER Or CLSCTX_REMOTE_SERVER
//End Enum
}
|
|
|
|

|
I have open source C# wrappers for most of the WASAPI api in NAudio, so you are free to use that. I'm hoping to release a Win8 build of NAudio soon, which should make this easier to use.
|
|
|
|
|

|
I was looking for PCM Data in managed code using WASAPI. I even asked in msdn forum about writing an article about it. here we go.
good job.
thanks
modified 21 Sep '12 - 11:39.
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
|
How to record and play PCM audio on Windows 8 in .NET.
| Type | Article |
| Licence | Public Domain |
| First Posted | 17 Sep 2012 |
| Views | 14,986 |
| Downloads | 771 |
| Bookmarked | 13 times |
|
|