Using Streaming Buffers

[The feature associated with this page, DirectSound, is a legacy feature. It has been superseded by WASAPI and Audio Graphs. Media Casting have been optimized for Windows 10 and Windows 11. Microsoft strongly recommends that new code use Media Casting instead of DirectSound, when possible. Microsoft suggests that existing code that uses the legacy APIs be rewritten to use the new APIs if possible.]

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.

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

To halt playback, call the IDirectSoundBuffer8::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. Ascertain whether the buffer is ready to receive new data. This can be done either by polling the play cursor or by waiting for a notification.

  2. Lock a portion of the buffer by using IDirectSoundBuffer8::Lock. This method returns one or two addresses where data can now be written.

  3. Write the audio data to the returned address or addresses by using a standard memory-copy routine.

  4. Unlock the buffer using IDirectSoundBuffer8::Unlock.

The reason IDirectSoundBuffer8::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 around to the beginning of the buffer. If it does, you have to 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:

  • The memory address of offset 20,000.

  • The number of bytes locked from that point to the end of the buffer; that is, 20,000 bytes. You write this number of bytes to the first address.

  • The memory address of offset 0.

  • The number of bytes locked from that point; that is, 10,000 bytes. You write this number of bytes to the second address.

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

Although it's possible to lock the entire buffer, you must not do so while it is playing. Normally you refresh only a fraction of the buffer each time. For example, you might lock and write to the first quarter of the buffer as soon as the play cursor reaches the second quarter, and so on. You must never write to the part of the buffer that lies between the play cursor and the write cursor.

The following function writes data to a sound buffer, starting at the position passed in dwOffset:

    BOOL AppWriteDataToBuffer( 
        LPDIRECTSOUNDBUFFER8 lpDsb,  // The 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->Lock(dwOffset, dwSoundBytes, &lpvPtr1, 
          &dwBytes1, &lpvPtr2, &dwBytes2, 0); 
     
      // If the buffer was lost, restore and retry lock. 
     
      if (DSERR_BUFFERLOST == hr) 
      { 
        lpDsb->Restore(); 
        hr = lpDsb->Lock(dwOffset, dwSoundBytes, 
            &lpvPtr1, &dwBytes1,
            &lpvPtr2, &dwBytes2, 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->Unlock(lpvPtr1, dwBytes1, lpvPtr2, 
        dwBytes2); 
        if (SUCCEEDED(hr)) 
        { 
          // Success. 
          return TRUE; 
        } 
      } 
     
      // Lock, Unlock, or Restore failed. 
     
      return FALSE; 
    } 

See Also