Skip to main content

Topic: How to code with WASAPI ? (Read 881 times) previous topic - next topic

0 Members and 1 Guest are viewing this topic.
How to code with WASAPI ?
Hi there,
I'm coding a program that has to do some audio input/output. This is the first time for me to code with it, and i want to use WASAPI to do that.
After reading several pages about WASAPI on MSDN, i wonder what the so called push/event mode (from foobar2000 preference page) means.
In the example code from this page we can see there is a main loop, and it SLEEPs for a while each time before filling the audio buffer.
Is this the so called PUSH mode ? If so, then how to implement EVENT mode ? by what functions/APIs ?
thanks
  • Last Edit: 28 November, 2017, 07:39:57 PM by Happy Water

  • knik
  • [*][*]
  • Developer
Re: How to code with WASAPI ?
Reply #1
This might help:
https://msdn.microsoft.com/en-us/library/windows/desktop/dd370844(v=vs.85).aspx
Quote
In addition, the IAudioClient::Initialize method supports an AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag that enables an application's buffer-servicing thread to schedule its execution to occur when a new buffer becomes available from the audio device. By using these features, an application thread can reduce uncertainty about when it will execute, thereby decreasing the risk of glitches in a low-latency audio stream.

Re: How to code with WASAPI ?
Reply #2
Thanks for the reply, that really helps.

I copied example code from this page, and tried to play 44.1KHz/16bit WAV files with it, it works.
But when i try to modify the code to play with 44.1KHz/24bit WAV files, things get weird.
The sound is very noisy, i can barely recognize the music from the noise.
And i can say the music has been speed up quite a lot, maybe around 2x.

From 16bit to 24bit, i just changed these things below:
the WAVEFORMATEX
Code: [Select]
	*fx = (WAVEFORMATEX*)malloc(sizeof(WAVEFORMATEX));
(**fx).wFormatTag = WAVE_FORMAT_PCM;
(**fx).cbSize = 0;
(**fx).nAvgBytesPerSec = 44100 * 6;  //for 16bit audio, it was '44100 * 4'
(**fx).nBlockAlign = 6;  //for 16bit audio, it was '4'
(**fx).nChannels = 2;
(**fx).nSamplesPerSec = 44100;
(**fx).wBitsPerSample = 24; //for 16bit audio, it was '16'
And when i need to fill the buffer, i just copy bytes(keep their original order) from the "data chunk" from a WAV file.
My audio chip is "Realtek High Definition Audio" on board audio chip.
is that meaning Realtek on board audio chip can't be configured that way? or did i do sth. wrong? can anyone help me.. please.
  • Last Edit: 30 November, 2017, 06:47:54 PM by Happy Water

  • kode54
  • [*][*][*][*][*]
  • Administrator
Re: How to code with WASAPI ?
Reply #3
Most devices using WASAPI prefer 24 bits padded to 32 bits per sample, or don't even care if you fill the lower bits with extra precision that'll possibly be thrown out anyway.

Re: How to code with WASAPI ?
Reply #4
Most devices using WASAPI prefer 24 bits padded to 32 bits per sample, or don't even care if you fill the lower bits with extra precision that'll possibly be thrown out anyway.
The audio chip i'm using may not reach 24bit precision, but now i just focus on the API.
How can I fill the audio buffer to achieve 24bit playback? Should i use any kind of sample packing?(maybe something like: L_MostSB L_MidSB R_MostSB R_MidSB L_LSB R_LSB ....repeat..) or any kind of padding?

help me, please..

  • knik
  • [*][*]
  • Developer
Re: How to code with WASAPI ?
Reply #5
I can't spot any obvious flaws in your code, looks more or less how I would write it, but I never played with WASAPI.
Maybe just a buggy driver or something, I have no idea.

Re: How to code with WASAPI ?
Reply #6
Ok, I've asked my friend to try my code with his audio interface, and it works well with no byte padding, just keep the original byte order from the WAV files.
His audio interface is "Accurate Audio DIP-A600 ", which is based on XMOS USB Audio 2.0 solution. He also tried my code with his on board chip, also a Realtek, it can't work well, just like mine.

It seems that Realtek audio chip has a buggy driver, isn't it? Maybe we should stick to 16bit with it.

After all, it's time for me to finish my code, thanks for your reply, it really helps.
  • Last Edit: 01 December, 2017, 11:32:34 AM by Happy Water

  • [JAZ]
  • [*][*][*][*][*]
Re: How to code with WASAPI ?
Reply #7
With WASAPI, you have to know this:
In shared mode, audio from multiple applications can be mixed, but a fixed samplerate and bitdepth is set for mixing, and is the responsibility of applications to check that configuration, and resample the audio to these values. Generally, in this case, applications can send the audio in floating point which is later resampled to the target bitdepth.
In exclusive mode, a single application can access the audio, and is able to set the desired samplerate and bitrate, but needs to ask the driver which modes are supported and use one of them.

You didn't say in which context is your application being developed ( some school/university project, own personal interest, part of a paid product, some open source initiative...).

I say so, because there already exist libraries that implement these things, like portaudio ( http://www.portaudio.com/ ), or you could check some other open source projects, like Psycle.
In case of Psycle, the WASAPI implementation can be found in this file:  https://sourceforge.net/p/psycle/code/HEAD/tree/trunk/psycle/src/psycle/host/WasapiDriver.cpp
Psycle's use-case is different from yours, because in Psycle, the audio is in 32bit floating point, and with an unusual range of -32768.0 to 32768.0 (Many years ago, that was considered good in order to convert it easily to 16bits. Not too good for other cases).

The 24bits format is quite a special format. Soundcards don't necessarily support the storage-friendly one (i.e. 3 bytes per sample). As you've been told, they usually understand the 24bits padded to 32bits format. It's up to the implementation to specify if they use the bottom-padded one or the top-padded one.  (That used to depend if the hardware was big-endian or little endian).

As such, you should not consider the Realtek issue as a bug in the driver, but a misuse of the API from your part.

  • knik
  • [*][*]
  • Developer
Re: How to code with WASAPI ?
Reply #8
I just discovered WAVEFORMATEX may be not enough to setup 24-bit playback:
https://msdn.microsoft.com/en-us/library/dd757713(v=vs.85).aspx
Quote
Formats that support more than two channels or sample sizes of more than 16 bits can be described in a WAVEFORMATEXTENSIBLE structure, which includes the WAVEFORMAT structure.

Re: How to code with WASAPI ?
Reply #9
Sorry for my late reply.

I was coding with WASAPI in exclusive mode, and i've tried to play 24bit audio with it.
My code failed to work on my own computer (with realtek audio chip), but worked fine on my friend's computer with a XMOS audio device.
I just sent him Binary Exe for testing, not source code. You may say Realtek chips need word padding, yes, i understand.
But the tricky part is, i can hear the music from the noise, and it has been speed up, maybe 10% or somewhat.
To figure out the byte padding pattern, I even tried to fill the buffer with only 8bit data, and only left channel data.
However, no matter how hard i tried, the best result i got was the SPEED UP audio.
If it was just a byte padding or byte order problem, the out put sound SHOULDN'T be speed up, as each time you GetBuffer(), you can know the buffer size in SAMPLEs.
That is why i say Realtek seems having a buggy driver, however, maybe i'm still wrong in some parts.

Actually, I was coding a Emulator of a CPU (or you can say a Computer) that is designed by myself.
I use WASAPI to emulate the audio device, and it's quite important to use EVENT mode, since many real world audio chips works in this way (Interrupt).
On the other hand, exclusive mode gives a constant audio buffer length, which is also similar to real world audio chips.
(when using shared mode, you have to check the buffer length each time after call to GetBuffer().)

At the first time i come to the audio part, i tried WaveOut() and it has very large latency, for about half a second.
Half a second may sounds not bad, but i want the latency smaller than 30ms, meaning about 30Hz around or even higher, close to real world audio chips.
You can guess that i want the audio device IRQ as a timer, so that it would be quite straightforward to implement A/V synchronize when doing video playing back.
If the audio latency is large, i'll have to prepare another timer and the video may become jerky.

Now i've almost finished coding my Emulator and i have implemented simple audio/video playback on it.
For the audio part, i just expose a WAVEFORMATE struct to the Guest System in the form of PCI bus configuration space registers,
and just copy data from Guest System (DMA) to fill the audio buffer without padding/ordering.
As you may expect, it can't play 24bit audio properly on my computer, i just round the audio data to 16bit when i have to work with it.
  • Last Edit: 12 December, 2017, 03:20:10 AM by Happy Water

  • [JAZ]
  • [*][*][*][*][*]
Re: How to code with WASAPI ?
Reply #10
Ok, I see.

I haven't had problems with my integrated realtek on a old laptop or my desktop, but as I say, I normally use floating mode.

Said that, I recommend you to take a look at this part of the code, since it might be what knik said, which is an incorrect initialization of the waveformat:

https://sourceforge.net/p/psycle/code/HEAD/tree/trunk/psycle/src/psycle/host/AudioDriver.cpp#l74

Re: How to code with WASAPI ?
Reply #11
Ok, I see.

I haven't had problems with my integrated realtek on a old laptop or my desktop, but as I say, I normally use floating mode.

Said that, I recommend you to take a look at this part of the code, since it might be what knik said, which is an incorrect initialization of the waveformat:

https://sourceforge.net/p/psycle/code/HEAD/tree/trunk/psycle/src/psycle/host/AudioDriver.cpp#l74
Ok, I tried your code and it works pretty good.
Code: [Select]
PrepareWaveFormat(fx,
2,  //nChannel
48000,  //Sampling Rate
32,  //bits
24  //validBits
);

I also tried IsFormatSupported with your code. If i prepare waveformat like this:
Code: [Select]
PrepareWaveFormat(fx,
2,  //nChannel
48000,  //Sampling Rate
24,  //bits, meaning no byte padding.
24  //validBits
);
The IsFormatSupported method will return "AUDCLNT_E_UNSUPPORTED_FORMAT".
So i can test whether the audio chip needs byte padding by IsFormatSupported.

Then my last question, how can i know whether it needs a bottom-padding or top-padding ?
Is it up to the CPU (x86) or to the audio chip (Realtek)?

  • [JAZ]
  • [*][*][*][*][*]
Re: How to code with WASAPI ?
Reply #12
Then my last question, how can i know whether it needs a bottom-padding or top-padding ?
Is it up to the CPU (x86) or to the audio chip (Realtek)?

If I have to tell the truth, right now I don't remember.  Checking at my code, it seems that you need bottom padding. Also, remember that you're on little endian, so bottom padding means the first byte.