Custom Mixers

Most applications will use the DirectSound mixer; it should be sufficient for almost all mixing needs and it automatically takes advantage of any available hardware acceleration. 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.

To implement a custom mixer, the application must first set the DSSCL_WRITEPRIMARY cooperative level and then create a primary sound buffer. (See Access to the Primary Buffer.) It can then lock the buffer, write data to it, unlock it, and play it just like any other buffer. (See Playing Sounds.) Note however that the DSBPLAY_LOOPING flag must be specified or the IDirectSoundBuffer::Play call will fail.

The following example illustrates how an application might implement a custom mixer. The AppMixIntoPrimaryBuffer sample function 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 DSERR_BUFFERLOST is returned, 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 SUCCEEDED(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 SUCCEEDED(hr) 
        { 
            return TRUE; 
        } 
    } 
    // Lock or Unlock failed. 
    return FALSE; 
}