Platform SDK: DirectX

Using Streaming Buffers

A streaming buffer plays a long sound that cannot all fit into the buffer at once. As the buffer plays, old data is periodically replaced with new data.

[C++]

To play a streaming buffer, call the IDirectSoundBuffer::Play method, specifying DSBPLAY_LOOPING in the dwFlags parameter.

To halt playback, call the IDirectSoundBuffer::Stop method. This method stops the buffer immediately, so you need to be sure that all data has been played. This can be done by polling the play position or by setting a notification position.

Streaming into a buffer requires the following steps.

  1. Lock a portion of the buffer by using IDirectSoundBuffer::Lock. This method returns one or two addresses where data can be written.
  2. Write the audio data to the returned address or addresses by using a standard memory-copy routine.
  3. Unlock the buffer using IDirectSoundBuffer::Unlock.

The reason IDirectSoundBuffer::Lock might return two addresses is that you can lock any number of bytes, up to the size of the buffer, regardless of the start point. If necessary, the locked portion wraps to the beginning of the buffer. If it does, you must perform two separate memory copies.

For example, say you lock 30,000 bytes beginning at offset 20,000 in a 40,000-byte buffer. When you call Lock in this case, it returns four values.

If no wraparound is necessary, the last two values are NULL and zero respectively.

Although it's possible to lock the entire buffer, you must not do so while it is playing. Refresh only a portion of the buffer each time. For example, you might lock and write to the first quarter of the buffer as soon as the play position reaches the second quarter, and so on. You should write at least 1 second ahead of the current play position to minimize the possibility of gaps in the audio output.

The following C function writes data to a sound buffer, starting at the offset into the buffer passed in dwOffset.

BOOL AppWriteDataToBuffer( 
    LPDIRECTSOUNDBUFFER lpDsb,  // The DirectSound buffer
    DWORD dwOffset,             // Our own write cursor
    LPBYTE lpbSoundData,        // Start of our data
    DWORD dwSoundBytes)         // Size of block to copy
{ 
    LPVOID  lpvPtr1; 
    DWORD   dwBytes1; 
    LPVOID  lpvPtr2; 
    DWORD   dwBytes2; 
    HRESULT hr; 
 
    // Obtain memory address of write block. This will be in two parts
    // if the block wraps around.
    hr = lpDsb->lpVtbl->Lock(lpDsb, dwOffset, dwSoundBytes, &lpvPtr1, 
        &dwBytes1, &lpvPtr2, &dwBytes2, 0); 
 
    // If DSERR_BUFFERLOST is returned, restore and retry lock. 
    if (DSERR_BUFFERLOST == hr) 
    { 
        lpDsb->lpVtbl->Restore(lpDsb); 
        hr = lpDsb->lpVtbl->Lock(lpDsb, dwOffset, dwSoundBytes, 
            &lpvPtr1, &dwAudio1, &lpvPtr2, &dwAudio2, 0); 
    } 
    if SUCCEEDED(hr) 
    { 
        // Write to pointers. 
        CopyMemory(lpvPtr1, lpbSoundData, dwBytes1); 
        if (NULL != lpvPtr2) 
        { 
            CopyMemory(lpvPtr2, lpbSoundData+dwBytes1, dwBytes2); 
        } 
        // Release the data back to DirectSound. 
        hr = lpDsb->lpVtbl->Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, 
            dwBytes2); 
        if SUCCEEDED(hr) 
        { 
            // Success. 
            return TRUE; 
        } 
    } 
    // Lock, Unlock, or Restore failed. 
    return FALSE; 
} 
 
[Visual Basic]

To play a streaming buffer, call the DirectSoundBuffer.Play method, specifying DSBPLAY_LOOPING in the flags parameter.

To halt playback, call the DirectSoundBuffer.Stop method. This method stops the buffer immediately, so you need to be sure that all data has been played. This can be done by polling the play position or by setting a notification position.

Stream data into a buffer by using the DirectSoundBuffer.WriteBuffer method. This method lets you write data to any portion of the buffer. Although it's possible to write to the entire buffer, you must not do so while it is playing. Refresh only a portion of the buffer each time. For example, you might write to the first quarter of the buffer as soon as the current play position reaches the second quarter, and so on. You should write at least 1 second ahead of the current play position to minimize the possibility of gaps in the audio output.

The following sample code writes 1000 bytes of data from the byte array myBuffer to the beginning of the DirectSoundBuffer represented by dsb.

dsb.WriteBuffer 0, 1000, myBuffer(0), DSBLOCK_DEFAULT

WriteBuffer handles wraparound transparently. Suppose the secondary buffer has been created with a size of 10,000 bytes, and the application writes 5000 bytes to an offset past the midpoint of the buffer, as follows:

dsb.WriteBuffer 8000, 5000, myBuffer(0), DSBLOCK_DEFAULT

This call writes the first 2000 bytes to the last part of the buffer and the next 3000 bytes to the beginning of the buffer.

It is the application's responsibility to track the offset for the next data write. The easiest way to do this is by setting notification positions and writing a fixed number of bytes each time an event is triggered.

For more information, see the following topics: