Search Tips Advanced Search |
| Delphi Graphics and Game Programming Exposed with DirectX 7.0 by John Ayres Wordware Publishing, Inc. ISBN: 1556226373 Pub Date: 12/01/99
Search this book:
Previous | Table of Contents | Next
Streaming Buffers As mentioned previously, streaming buffers are used for large sounds that will not fit into a reasonably sized chunk of memory. A good use for streaming buffers would be for playback of digitized dialog, or perhaps a digitized music track. Creating streaming buffers is very similar to creating static buffers. The application must first read in a WAV file (or resource) and retrieve its format. Then, it must set up a TDSBufferDesc structure with the appropriate attributes. There are two major differences between creating streaming buffers and creating static buffers: the flags used in the buffer description structure and the size of the buffer itself. As you would expect, the dwFlags member of the TDSBufferDesc structure will not contain the DSBCAPS_STATIC flag. However, this member should contain the DSBCAPS_GETCURRENTPOSITION2 flag to ensure the most accurate play position tracking. As for the size of the buffer, instead of setting it to the total size required to hold all of the audio data as in static buffer creation, it is set to a size large enough to hold approximately two seconds worth of audio data. Two seconds is an arbitrary number and depends on the application’s specific needs. The actual size of the buffer is dependent on the wave’s sample rate and bits per sample (the nAvgBytesPerSec member of the wave’s TWaveFormatEx structure should be used). To actually begin loading data into the buffer, we must first know where the current play and write positions are within the buffer (described earlier under Buffer Memory Format). The GetCurrentPosition method of the IDirectSoundBuffer object retrieves the offset from the beginning of the buffer for both the play and write positions. The GetCurrentPosition method is defined as:
    function GetCurrentPosition(

    lpdwCapturePosition,               // the current play position

    lpdwReadPosition: PDWORD           // the current write position

    ): HResult;                        // returns a DirectX error code
The first parameter is set to the current position of the play cursor, as an offset from the beginning of the buffer in bytes. The second parameter is set to the current position of the write cursor, again as an offset in bytes from the beginning of the buffer. How the data is loaded into the buffer is up to the application. For example, data could be written into the buffer right at the start of the write position (not recommended), or it could be written into the buffer at some point ahead of the current play position. In our examples below, we’ll write data into the buffer based on the current play position. You may have noticed how the Lock method returns two lock start offsets and two bytes locked counts. This is again due to the circular nature of buffer memory. For example, say the buffer is 5,000 bytes long, and the Lock method is called with a starting offset of 3,000 and a request to lock 3,000 bytes. The last 2,000 bytes in the buffer can be locked, but the final 1,000 requested bytes must be locked from the beginning of the buffer. This is why two offsets and two byte counts are returned. Again, depending on how your application copies data into the buffer, these values can be used to continuously copy data into the buffer in a circular format. All that remains is to play the buffer and determine exactly when to write data into the buffer. We’ve already seen how a buffer can be played. In the case of streaming buffers, it might be useful to fill the buffer with silence and start playing it immediately, especially if the sound will be looped. Filling the buffer with silence involves simply zeroing out the buffer’s memory using the techniques described above for writing data into a buffer.
| Note: We have a function in the DSUtil unit called FillBufferWithSilence that performs this task.
As for when to write data into the buffer, there are two methods available to the application: polling and notification. Polling involves using the GetCurrentPosition method as described above. GetCurrentPosition is called at regular intervals (like every pass through the game’s main loop), writing data into the buffer based on the Play or Write position returned at that moment. The advantages to this method are that it’s simple and easy to implement. The disadvantages are that unless polling is performed at regular intervals, the Play position can easily race past the point where new data should have been copied into the buffer, resulting in old data being played back and a generally messed up audio output. The other method, notification, involves using the IDirectSoundNotify interface together with a number of low-level API functions such as CreateEvent and WaitForMultipleObjects to create event objects that are signaled when the Play position reaches certain points within the buffer. The advantage to this method is that it is more efficient than continually calling the GetCurrentPosition method. The disadvantages are that it is much more difficult to set up, requires multiple threads (in some cases), and is consequently more difficult to debug. For our purposes, the polling method will be more than adequate. The following example demonstrates creating a streaming buffer and writing data into the buffer by polling the current play position.
Previous | Table of Contents | Next

Products | Contact Us | About Us | Privacy | Ad Info | Home Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.