Q's & A's - November 1995

by Linden deCarmo

I received questions from several developers in response to my article on Direct Audio RouTines (DART) that appeared in the last issue of The Developer Connection News. If you follow these tips to help you harness the power of DART, you'll have DART programs that are not only robust and efficient, but are also good citizens that can cooperate with other multimedia and DART-related programs.



rc = mciSendCommand( usDeviceID, MCI_MIXSETUP, MCI_WAIT ! MCI_MIXSETUP_INIT, ( PVOID ) &MixSetupParms, 0 ); if ( ULONG_LOWD( rc ) == MCIERR_UNSUPPORTED_FUNCTION ) { printf('DART unavailable--higher version of OS/2 required'); } ''Sample Code 1. Detecting the availability of DART functions in the Amp-Mixer''
 * Question:How does DART relate to the Amp-Mixer device?
 * Answer:Because DART functions are handled by the media control interface (MCI) Amplifier-Mixer (or Amp-Mixer) device, you must first open this driver before you can use DART (the terms Amp-Mixer and DART are used interchangeably in this article). Once the mixer device is opened, you can perform DART functions, issue normal mixer calls (such as volume control), and detect if DART is supported in the release of OS/2 your application is using (see Sample Code 1).


 * Question: How many DART buffers can I use?
 * Answer:The number and size of buffers you use should be tailored to your application's needs. For example, certain programs (such as games) cannot afford any significant delay between the time you send a buffer to DART and when the buffer is played. As a result, these applications typically use small buffers (for example, between 256 and 1024 bytes) to minimize latency. By contrast, applications that send streams of data to the Amp-Mixer (such as a music player) can use much larger buffers (for example, between 1024 and 4096 bytes) because latency is not an issue. In fact, larger buffers are better for overall system performance because fewer interrupts must be processed by the audio driver. Whether your program uses large buffers or small buffers, one thing you shouldn't do is constantly change the ulBufSize field - this can cause problems with direct memory access (DMA) based devices because they might have to reprogram the DMA controller.
 * Besides buffer size, you should only allocate the number of buffers you require for operation. To explain, unlike the virtual memory you obtain with DosAllocMem, DART memory is locked (or made non-pageable) and is a precious commodity. If your program only requires 30 buffers and you request 300, you might prevent other DART applications from obtaining the memory they desire (future versions of OS/2 Warp and OS/2 Warp Connect (PowerPC Edition) might not have these restrictions).


 * Question: How is DART memory different from memory allocated with DosAllocMem?
 * Answer:DART memory should be used intelligently - under normal circumstances, you should allocate memory once at the start of the program and free the memory before your program terminates. You do not have to release this memory if you switch between playback and record modes or even if you switch between digital audio and MIDI modes. The Direct Audio playback and record sample in the \TOOLKIT\BETA\SAMPLES\ENTOOLKT\AUDIO\DAUDIO\* directory on your accompanying DevCon CD-ROMs illustrates the proper steps to switch between playback and record modes.
 * If your application has already allocated buffers and wishes to send smaller buffers to the audio driver, you do not have to reallocate the buffers - simply update the ulBufSize field of each buffer you send to DART. If you request larger buffers, you must free the existing set of buffers and reallocate the larger buffer size.

MCI_MIX_BUFFER MyBuffers[ MAX_BUFFERS ]; for ( ulLoop = 0; ulLoop < ulNumBuffers; ulLoop++ ) { rc = mmioRead ( hmmio, MyBuffers[ ulLoop ]->.pBuffer, MyBuffers[ ulLoop ]->.ulBufferLength); if ( ]rc ) { exit( rc ); } // We can attach buffer specific information to each buffer // via the ulUserParm field. When the buffer is returned // it can be identified via the ulUserParm field. MyBuffers[ ulLoop ]->.ulUserParm = ulLoop; MyBuffers[ ulLoop ]->.ulReserved1 = 3; MyBuffers[ ulLoop ]->.ulReserved2 = 6; } ''Sample Code 2. Attaching custom information to each buffer''
 * Question: What is the ulUserParm field used for?
 * Answer:The ulUserParm field of the MIX_BUFFER_PARM structure lets you attach custom information to each buffer you send to DART. When the buffer has been either filled or emptied, the mixer device will return the buffer to you with the ulUserParm value you attached. After you receive the buffer, you can use the contents of the ulUserParm field to synchronize the audio with video, text, and other media.

LONG APIENTRY MyEvent ( ULONG ulStatus,                        PMCI_MIX_BUFFER pBuffer,                         ULONG ulFlags ) {  ULONG ulSearchLoop = 0; static LONG lDebug = 0; // on input, we will receive three parameters: // ulStatus. Detailed error message: // can contain one of the following values: // ERROR_DEVICE_UNDERRUN or ERROR_DEVICE_OVERRUN // pBuffer. Buffer that was returned. NOTE: this can be NULL if  // only an error gets returned. // ulFlags: can contain one or more of the following values: // MIX_STREAM_ERROR // MIX_READ_COMPLETE // MIX_WRITE_COMPLETE switch( ulFlags ) {    case MIX_STREAM_ERROR ! MIX_READ_COMPLETE : // error occur in device {        // we have received an OVERRUN event during recording // this event should terminate recording. if ( ulStatus == ERROR_DEVICE_UNDERRUN) {            // report terminal error printf('We can't recover--giving up on recording'); }        break; }    case MIX_STREAM_ERROR ! MIX_WRITE_COMPLETE: // error occur in device {        // underruns are not catastrophic--we can recover from these. if ( ulStatus == ERROR_DEVICE_UNDERRUN) {            printf('Received underrun--supplier thread not fast enough'); }      } } ''Sample Code 3. How to process underrun error conditions''
 * Question: Are DART errors always fatal?
 * Answer:You should be able to handle non-catastrophic DART errors. To explain, certain errors (such as data underruns) can occur during normal program operation. A data underrun happens when your application is unable to feed the audio driver data fast enough and it has nothing to process. This situation is not fatal and if it happens infrequently, your users will never notice the underrun. However, if too many underruns occur, users will hear choppy audio. By contrast, overrun events happen when your application is recording and the driver has no memory in which to place the audio data. Since overruns involve the loss of data, they are non-recoverable, and you should send an MCI_STOP command to the mixer to restore the device to a safe state.


 * Question: What is the MIX_BUFFER_EOS flag used for?
 * Answer:For efficiency purposes, most audio drivers expect to have at least two buffers in their possession before they actually start processing data. Therefore, unless specifically instructed otherwise, DART will not start the driver unless it has two or more audio buffers. If you want to send a single buffer to DART, you must use the MIX_BUFFER_EOS flag in the MCI_MIX_BUFFER structure to force it to start playing or recording data.


 * Question: Why should I stop the audio device?
 * Answer: It is important to understand what happens when you explicitly stop the audio device (with the MCI_STOP and MCI_PAUSE commands) and when the device is forced to do an implicit stop. After you issue the MCI_STOP command and it returns to your application, all buffers that you previously sent are considered to be returned to your application (even if you didn't physically receive them in your event procedure). In addition, the device's current time is reset to 0. If you issue the MCI_PAUSE message, the device's current time remains constant and no buffers are returned to your application.
 * If your application receives an underrun or overrun event, DART automatically pauses the audio device. If this is not done, certain DMA-based audio devices will continuously replay the audio data until they are sent new information. Because of this, if you receive an underrun event and want to restart the device by sending a single buffer, be sure to attach the MIX_BUFFER_EOS flag to force the device to restart.


 * Question: My audio driver doesn't work with DART. What are my alternatives?
 * Answer:While Direct Audio is compatible with any OS/2 audio driver, certain drivers have bugs that surface only in DART applications. To explain, when OS/2 streams audio data, it always sends page-aligned buffers (which are multiples of 4K). By contrast, DART applications can send buffers of any size. If these buffers are smaller than 4K or are not multiples of 4K, the Sound Blaster 16 and Sound Blaster AWE32 drivers that ship with OS/2 Warp and OS/2 Warp Connect might have problems (the latest drivers available from Creative Labs through CompuServe (GO BLASTER) or on the Internet (ftp.creaf.com) fix this problem). If your audio device driver exhibits similar symptoms, be sure to obtain the latest one before expending too much energy debugging these problems.


 * Question: How can one obtain accurate timing?
 * Answer:If your application needs accurate timing position and you can't wait for a buffer to return from the driver with a time stamp, you can use MCI_STATUS_POSITION to obtain a reading of the device's current position (in milliseconds). After the driver processes a buffer, the total cumulative time increases until it either runs out of data or you send an MCI_STOP command to reset the time to 0.


 * Question: How are DART applications debugged?
 * Answer:If you experience hangs in your DART application, you probably have one of the following problems:
 * Mismatch in buffers - Your application is losing count of the number of buffers it has processed and, as a result, might expect to receive more buffers when no more will be returned.
 * Hang in the audio driver - It is possible that the audio device driver is not returning a buffer. See Question 8 above to ensure that you have the latest drivers.