The exhaustive information about WaveForm Audio can be found in [8]. Windows API mechanisms used here are described in [11].
WaveForm Audio is a standard member of Windows and Windows NT/2000. Even though all the routines are the components of a separate library (winmm.dll), these function set may be treated as the "sound system call set". As a basic set of functions, the module requires filling large structures. Consequently, the simplest program using this mechanisms would have more than fifty lines, but it gives programmer almost full control over sound devices. The control, that is portable among all the sound devices having drivers for the OS. WaveForm Audio was chosen by the author to be the engine of multimedia terminal's Audio Engine.
WaveForm Audio is the set of functions as well as data structures. To manage sound devices over WaveForm Audio one can use a kind of virtual device called waveform-audio device (further called just a "device"). There are two types of such devices: input device used while recording, and output device used while playing sounds. These devices are treated independently. Every device is represented by a special handle: of the type HWAVEOUT for the output device, and of the type HWAVEIN for the input device. The variable of these types are used to identity the sound devices. The full list of Waveform functions and the example how they are used is in Appendix B.
WaveForm Audio contains mechanisms that should be used as the body of initial procedures. They allow to check if sound devices are present in the system, and to find out how many of them may be used. Programmer can get their capabilities and choose one of them. If none of them can be used, further procedures could not be performed.
Before usage, waveform-audio device must be opened. waveInOpen and waveOutOpen functions are needed to open input and output device. Parameters such as sampling rate, number of channels, bits per sample, the type of encoding, etc. are specified when calling these functions. The other parameter passed to the functions is the type of signallization between OS the sound functions. It can be:
| Event mechanism |
| Callback mechanism |
| Thread Messages mechanism |
| Windows Messages mechanism |
| No signallization |
Capturing is organized as follows:
Special headers are prepared (waveInPrepareHeader). This header contains a buffer (later filled with samples) which is prepared by user (e.g. by using malloc). Header contains also the buffer length and additional parameters.
Prepared header is added to the system buffers (waveInAddBuffer). There can be many system buffers.
Function waveInStart is called to start recording. In this exact moment OS gets first buffer from the set of system buffers and begins recording. When filling of the buffer is finished OS begins recording to the next buffer and so forth until all the system buffers are filled. After every buffer filled system also sends a signal to the application that buffer has been filled. Next buffer may be added to the system (waveInAddBuffer) to get continuous signal or another procedures, like copying or encoding may be performed after this signal.
The fragment of the capturing (recording) procedures is illustrated in Figure 1-2. In this version Event mechanism is used.
int i=0, No_SYSTEM_BUFFFERS;
if (waveInOpen(&hwi,
0, (LPWAVEFORMATEX)&pwf,
(DWORD) rip->eventh, 0, CALLBACK_EVENT )) {
/* ... error handling ... */
}
/* ... Preparing system buffers (sb) ... */
for(i=0; i<No_SYSTEM_BUFFERS; i++){
sb[i]->lpData = (LPBYTE) malloc(system_buf_len);
sb[i]->dwBufferLength = system_buf_len;
sb[i]->dwBytesRecorded = 0;
sb[i]->dwUser = 0;
sb[i]->dwFlags = 0;
sb[i]->dwLoops = 0;
if(!sb[i]->lpData)
/* ... error handling ... */
if (waveInPrepareHeader(hwi, sb[i],
sizeof(WAVEHDR))) {
/* ... error handling ... */
}
if (waveInAddBuffer(hwi, sb[i],
sizeof(WAVEHDR))) {
/* ... error handling ... */
}
}
waveInStart(hwi);
ResetEvent(eventh);
while(1)
{
WaitForSingleObject(eventh, INFINITE);
/* ... sb[i] processing ... */
if (ret = waveInAddBuffer(hwi, sb[i],
sizeof(WAVEHDR))) {
/* ... error handling ... */
}
if(i == No_SYSTEM_BUFFFERS - 1)
i=0;
else
i++;
if(stop_thread_flag)
break;
}Figure 1-2. The C-code of the capturing loop.
Playing can be organized in simpler way:
Headers must also be prepared (waveOutPrepareHeader). Similar parameters are specified.
Buffers (field of the header structure) are filled with blocks of samples.
waveOutPause is called to tell OS that we do not want to start playing immediately after first waveOutWrite.
waveOutWrite is called with the parameter pointing to buffer we would like to play. We can call this function more than once to supply continuous playing.
waveOutRestart must be called to start playing.
The fragment of the playing loop is illustrated in Figure 1-3. In this version Event mechanism is used.
int i=0, No_SYSTEM_BUFFFERS, BUFFER_LEN;
if (waveOutOpen(&hwo,
0 /*default*/, (LPWAVEFORMATEX)&pwf,
(DWORD) eventh, 0, CALLBACK_EVENT )) {
/* ... error handling */
}
/* .... allocation of sb buffers .... */
for(i=0; i<NoSYSTEM_BUFFERS; i++){ /* header preparation */
sb[i]->lpData = (LPBYTE) malloc(BUFFER_LEN);
sb[i]->dwBufferLength = system_buf_len;
sb[i]->dwBytesRecorded = 0;
sb[i]->dwUser = 0;
sb[i]->dwFlags = 0;
sb[i]->dwLoops = 1;
if(!sb[i]->lpData)
/* ... error handling ... */
if (waveOutPrepareHeader(rip->hwo,
sb[i], sizeof(WAVEHDR))) {
/* ... error handling */
}
}
while(1)
{
/* ... process previous sound block ... */
/* ... prepare lacking frames if needed */
if (waveOutWrite(hwo, sb[i], sizeof(WAVEHDR))) {
/* ... error handling ... */
}
if(i == No_SYSTEM_BUFFFERS-1)
i=0;
else
i++;
if(stop_thread_flag)
break;
WaitForSingleObject(risp->eventh, INFINITE);
} Figure 1-3. The C-code of the playing loop
When we want to stop playing or recording we must:
Call waveOutReset or waveInReset to free all system buffers (idle or being filled in the moment).
Call waveOutClose or waveInClose to tell the operating system that devices are no longer needed. Other processes can access sound devices after this call.
Call waveOutUnprepareHeader or waveInUnprepareHeader for every previously prepared header to unprepare them. After this call all data allocated for the use of buffers may be freed.