Topic : Low level Digital Audio API
Author : Unknown
Page : << Previous 2  
Go to page :


Go through all of those devices, displaying their names */
for (i = 0; i < iNumDevs; i++)
{
    /* Get info about the next device */
    if (!waveInGetDevCaps(i, &wic, sizeof(WAVEINCAPS)))
    {
        /* Display its Device ID and name */
        printf("Device ID #%u: %s\r\n", i, wic.szPname);
    }
}


Recording Digital Audio
The device's driver manages the actual recording of data. You can start and stop this process with waveInStart() and waveInStop(). While a driver records digital audio, it stores data into a small fixed-size buffer (for example 16K). When that buffer is full, the driver "signals" your program that the buffer is full and needs to be processed by your program (for example, your program may save that 16K of data to a disk file if your program is doing hard disk recording). The driver then goes on to store another "block" (ie, 16K section) of data into a second, similiarly-sized buffer. It's assumed that your program is simultaneously processing that first buffer of data, while the driver is recording into the second buffer. It's also assumed that your program finishes processing that first buffer before the second buffer is full. When the driver fills that second buffer, it again signals your program that now the second buffer needs to be processed. While your program is processing the second buffer, the driver is storing more audio data into the now-empty, first buffer. Etc. This all happens nonstop, so the process of recording digital audio is that two (or more if desired) buffers are constantly being filled by the driver (alternating between the 2 buffers), while your program is constantly processing each buffer immediately upon being signaled that the buffer is full. So, you end up dealing with a series of "blocks of data".
In fact, your program supplies each buffer to the driver, using waveInAddBuffer() (and waveInPrepareHeader() to initialize it). You supply the first 2 buffers to the driver using waveInAddBuffer() before recording. Every time that you're signaled that a buffer is filled, you need to use waveInAddBuffer() to indicate what buffer the driver will use after it finishes filling whatever buffer it is currently filling. (For double-buffering, that will be the same buffer that you're currently processing).

You can download my WaveIn C example to show how to record a (raw) digital audio file using double-buffering. Included are the Project Workspace files for Visual C++ 4.0, but since it is a console app, any Windows C compiler should be able to compile it. Remember that all apps should include MMSYSTEM.H and link with WINMM.LIB (or MMSYSTEM.LIB if Win3.1). This is a ZIP archive. Use an unzip utility that supports long filenames.

Playing Digital Audio
Playback is also done via "blocks of data". Here, your application reads a block of data from the WAVE file on disk (for example, you may read the next 16K of the file into a 16K buffer). (You must use waveOutPrepareHeader() to initialize the buffer before reading into it). You pass this block to the driver for playback via waveOutWrite(). While the driver is playing this block, you're reading in another block of data into a second buffer. When the driver is finished playing the first block, it signals your program that it needs another block, and your driver passes that second buffer via waveOutWrite(). Your program will now read in the next block of data into the first buffer while the driver is playing the second buffer. Etc. Again, this is all non-stop until the WAVE is fully played (at which point you can call waveOutReset() to stop the driver's playback process).

So how does the driver "signal" your program? You've got a few choices. You can choose to have the driver send messages to your program's Window, for example, the MM_WOM_DONE message is sent each time the driver finishes playing a given buffer. Parameters with that message include the address of the given buffer (actually the address of the WAVEHDR structure which encompasses the buffer) and the device's handle (ie, the handle supplied to you when you opened the device). Or, you can have the driver automatically call a particular function in your program (ie, a "callback") passing such parameters. There are a couple of other choices such as having the driver use event signals or start a particular thread in your program.

You tell the driver how you want to be signaled by setting certain flags in one of the arguments to waveInOpen() or waveOutOpen().


Setting volume and other parameters
There are other APIs that you'll likely want to use, such as waveOutSetVolume to set the volume for playback. Or, you may prefer to use the Mixer API (if the card's driver supports it) so that you can mute unneeded inputs/outputs, and adjust other parameters of a particular input/output line, or to determine what types of lines are available for recording (ie, analog microphone input, digital input, etc).


Page : << Previous 2