Using a Custom Mixer

Most applications should use the DirectSound mixer; it should be sufficient for almost all mixing needs and it automatically takes advantage of hardware acceleration, if available. However, if an application requires some other functionality that DirectSound does not provide, it can obtain write access to the primary sound buffer and mix streams directly into it. This feature is provided for completeness, and it should be useful only for a very limited set of high-performance applications. Applications that take advantage of this feature are subject to stringent performance requirements because it is difficult to avoid gaps in the audio.

To implement a custom mixer, the application should first obtain the DSSCL_WRITEPRIMARY cooperative level and then create a primary sound buffer. It can then call the IDirectSoundBuffer::Lock method, write data to the returned pointers, and then call the IDirectSoundBuffer::Unlock method to release the data back to DirectSound. Applications must explicitly play the primary buffer by calling the IDirectSoundBuffer::Play method to reproduce the sound data in the speakers. Note that the DSBPLAY_LOOPING flag must be specified or the IDirectSoundBuffer::Play call fails.

The following example illustrates how an application might implement a custom mixer. The AppMixIntoPrimaryBuffer function in the following example would have to be called at regular intervals, frequently enough to prevent the sound device from repeating blocks of data. The CustomMixer function is an application-defined function that mixes several streams together, as specified in the application-defined AppStreamInfo structure, and writes the result to the specified pointer.

BOOL AppMixIntoPrimaryBuffer(

LPAPPSTREAMINFO lpAppStreamInfo, LPDIRECTSOUNDBUFFER lpDsbPrimary,

DWORD dwDataBytes, DWORD dwOldPos, LPDWORD lpdwNewPos)

{

LPVOID lpvPtr1;

DWORD dwBytes1;

LPVOID lpvPtr2;

DWORD dwBytes2;

HRESULT hr;

// Obtain write pointer.

hr = lpDsbPrimary->lpVtbl->Lock(lpDsbPrimary, dwOldPos, dwDataBytes,

&lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);

// If we got DSERR_BUFFERLOST, restore and retry lock.

if(DSERR_BUFFERLOST == hr) {

lpDsbPrimary->lpVtbl->Restore(lpDsbPrimary);

hr = lpDsbPrimary->lpVtbl->Lock(lpDsbPrimary, dwOldPos,

dwDataBytes, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);

}

if(DS_OK == hr) {

// Mix data into the returned pointers.

CustomMixer(lpAppStreamInfo, lpvPtr1, dwBytes1);

*lpdwNewPos = dwOldPos + dwBytes1;

if(NULL != lpvPtr2) {

CustomMixer(lpAppStreamInfo, lpvPtr2, dwBytes2);

*lpdwNewPos = dwBytes2; // Because it wrapped around.

}

// Release the data back to DirectSound.

hr = lpDsbPrimary->lpVtbl->Unlock(lpDsbPrimary, lpvPtr1,

dwBytes1, lpvPtr2, dwBytes2);

if(DS_OK == hr) {

// Success.

return TRUE;

}

}

// Lock or Unlock failed.

return FALSE;

}