This is the part two of my article trilogy concerning the use of Windows Mixer controls, playing an existing wave file through some available speaker, or recording and saving a wave file from an available audio input device such as a microphone. You might be wondering about the order I am presenting these tutorials. Simple! When my father taught me to play pool, we would often come up with a scenario where there are multiple shots available. “Take the easy ones first.”, he would always say. (Not quite “Use the force!”, but it worked.). Part one (using Windows Mixers) was the easiest to understand. I admit that I did have a struggle implementing the concept. I actually had to write an MFC cpp dll and though, that worked well, I was not satisfied. I wanted to implement it in C#. After getting the code written, it was horribly unstable. I knew what was wrong; there was a critical section in the Player thread that needed synchronizing with feedback routines in the UI Thread. Finally, I stumbled upon the answer and “Voila!”. (Boy, are we in trouble.)
So, before I begin, I feel obliged to inform you that I picked up a lot of dirty words in this project. So, let me get them out now … DELEGATES, EVENTS, THREAD SAFETY, CALLBACKS, POINTERS. I'm surprised that my mom isn't trying to wash my mouth out with soap.
This app will have two threads. There is the main UI thread which all apps have, and the Player Thread which only plays the audio. Each thread will need to be aware of and safely pass information to the other thread. We will discuss this more as we proceed.
Short Answer
It works like this.
- Open the wave file, read the header, and fill in a format structure.
- Open the output device.
- Define and fill a number of WAVHDR structures. Each structure must be able to play long enough to fill in another.
- As each WAVHDR is filled, send it to the Windows Player DLL.
- When a WAVHDR is finished playing the DLL will try to inform you by calling a callback function. That function has the duty to fill in that WAVHDR and pass it back to the DLL.
- When you have finished playing, close the device and you are done.
Long Answer
- Open the audio (.wav) file and read the header. You must pull the format section from the header. (Warning: For this example, you must use only Stereo, CD quality audio .wav files only 44100 samples per second, 16 bit wide samples and 2 channels. If you want to play other formats you must carefully modify the code to identify and handle the desired format.) Now the binary reader is positioned at the beginning of the audio data and we have the format structure filled in and ready to use.
- Call waveOutOpen, give it the index of the device to be opened (The index that it was found in when we called waveOutGetDevCaps. Please refer to my previous article entitled Understanding and Controlling the Windows Mixer API). We also have the format structure to pass to it. Most important is the pointer to the WaveDelegate callback function. Here we use HandleWaveOut. This is our c# function that will fill in the freshly emptied buffer and send it back to the DLL. If you only have a small bit of audio you may use a single WAVHDR structure and you will not need this pointer.
- The WAVHDR structure contains a pointer (.lpthat you must point to a byte array that contains the audio you want to play. Here I have created four header structures and each one pointing to a byte array of audio data. Each buffer is 44100 bytes long which is enough to play for ¼ of a second of Stereo, CD quality audio. When you have filled in the buffer, call waveOutPrepareHeader to inform the DLL that a header is coming soon.
- As the header is filled in call waveOutWrite and the player will immediately start playing.
- When the DLL is finished playing a buffer, it will try to call the HandleWaveOut callback function we defined in step 2. It reads up to a predefined number of bytes from the audio file, calls waveOutPrepareHeader and waveOutWrite until we reach the end of the file or you want to stop playing. At that time, you must call waveOutUnPrepareHeader for each header that has been passed to the DLL as callback function informs you that the buffer has been played. Important: the callback section is the critical section. No other routine is allowed to even read any variable that is used here. If so the whole thing will crash. The critical section must be 'locked' with a thread safety lock like “lock” or “mutex”. When all audio data has been read and sent to the DLL, waveOutUnprepareHeader must be called for each and every WAVHDR structure.
- When all the audio data has been sent to the DLL, you must inform the UI thread that the player is done. When the UI receives a message that the player is done, the UI must call waveOutClose.
That's all folks!
Adendum

Figure 1 - General Flow.
The .wav audio file header is a Riff header which must contain,
- RIFF (four bytes that represent the ASCII value of the four characters “R”,”I”,”F”,”F”)
- File Size (four bytes that represent the file size – 8 )
- WAVE (four bytes that represent the ASCII value of the four characters “W”,”A”,”V”,”E”)
- FMT (four bytes that represent the ASCII value of the four characters “f”,”m”,”t”,” ”)
- Format Size (four bytes the represents the Int32 size of the format structure that is to follow.)
- Then comes Format Size bytes, that is, the binary representation of the Format Structure that informs us about this audio file.
- DATA (four bytes that represent the ASCII value of the four characters “d”,”a”,”t”,”a”)
- Data Size (our bytes the represents the Int32 size of the audio data that is to follow.)
- AUDIO Data (Data Size bytes of audio data)

Figure 2: RIFF Structure
- public struct WAVEFORMATEX
- {
- public ushort wFormatTag;
- public ushort nChannels;
- public uint nSamplesPerSec;
- public uint nAvgBytesPerSec;
- public ushort nBlockAlign;
- public ushort wBitsPerSample;
- public ushort cbSize;
- }

Eldack SouzaPosted Nov 19, 2019, 7:35 PM
Hell, I would love to learn C# C++, lost all my time with VB and ASM. In VB I don't need to do almost nothing, but can't do almost nothig so, in ASM I can do almost everthing, but need to do everthing little thing so.
Hamid KhanPosted Sep 29, 2017, 9:37 AM
Nice.............................................