Click here to Skip to main content
Click here to Skip to main content

A wrapper class for the DirectSound API

By , 21 Mar 2001
 

Introduction

This article focuses on giving an example of using the DirectX API in PC game software development by using a DirectSound wrapper class.

The DirectSound wrapper class

The wrapper class has the functionality to handle various aspects of sound playback.

The structure of the wrapper class

The DirectSound object uses double buffers to manipulate the sound data processing and playback. The DirectSound object creates a single primary buffer to deal with output devices to play the sound data. The DirectSound object creates multiple second buffers to load, store various sound data, and configure sound data performance. The object loads and mixes sound data from the secondary buffers and sends the processed sound data to the primary buffer to play it through the output device.

The wrapper class contains a pointer to the DirectSound object, a primary buffer object (a pointer of type DirectSoundBuffer), and the secondary buffer array (DirectSoundBuffer object array)

DirectSound object in the wrapper class

DirectSound is a COM interface, so the DirectSound object is the COM component object (a pointer to DirectSound). The DirectSound object is the core of sound playback and all of buffers, both primary buffer and secondary buffers are created by it, so it must be instantiated before doing any sound play back.

Because the DirectSound object is a COM object, it can be instantiated by either the generic way of creation COM components, or by calling DirectSoundCreate. In my wrapper class, I find the generic method of creating a COM object is more reliable. Remember: The DirectX interfaces are a subset of the COM interfaces, so before you use them, call CoInitialize/ CoInitializeEx, and call CoUninitialize at the end of your application.

// To start...
BOOL CSoundManager::CoInitializeDSound(void)
{
    HRESULT  hr;
    GUID     guID;

    // Clear the GUID
    ::memset(&guID, 0, sizeof(GUID));
 
    // Initialize COM service, create STA 
    hr = ::CoInitialize(NULL);
    ................................. 
    .................................
    // Create the IDirectSound object with the realiable way (create
    // standard COM object)
    hr = CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
              IID_IDirectSound, (void**)&m_pIDS);
    ................................. 
    ................................
    // Initialize the IDirectSound object
    hr = IDirectSound_Initialize(m_pIDS, &guID);
    ................................. 
    .................................
    return TRUE;
}

// To finish up...
void CSoundManager::CoUnInitializeDSound(void)
{
    if(m_pIDS)
    {
        m_pIDS->Release();
        m_pIDS = NULL;
        ::CoUninitialize();
    }	
}

Primary Buffer

The primary buffer is a DirectSoundBuffer object, and its main task is to receive the processed sound data from the DirectSound object and send it to the output device to play. The primary’s format settings determine the sound play effect of the output device. The primary’s format is a WAVEFORMATEX structure and must be set before play.

Second Buffer

The second buffer loads sound data and sets the sound performance, then sends the sound to the  DirectSound object. The second buffer is a DirectSoundBuffer object and is created by the DirectSound object.

In my library, I use a wrapper class of DirectSoundBuffer for the second buffer. This wrapper class has the DirectSoundBuffer object, and the methods to set sound performances, such as adjustment of volume, frequency and channel.

A special feature of the DirectSoundBuffer wrapper class is the special sound effect of Fade-In (volume up step by step) and Fade-Out (volume down step by step).

To avoid the concurrency of incoming function calls, a mutex object is used in the DirectSoundBuffer wrapper class to synchronize the function calls.

Timer Event

The DirectSound wrapper class needs a timer to monitor and manipulate the sound status and performance in a special time interval. The DirectSound wrapper class is a generic class, so it is hard to use a windows timer in the class. As an alternative, a waitable timer is used in the class. A thread is created to check the timer event when the class object is instantiated.

The test program

The test program is a Dialog box application written in MFC. It tests the various wrapper class features, such as volume, frequency, channel, looping, mixing and fade-in and fade-out, etc.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Zhaohui Xing
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 1memberMember 4024921 Sep '09 - 12:34 
.
GeneralQuestionmemberKirynx23 Jul '07 - 10:54 
Why didn't you just use CComPtr's with it to prevent memleaks in DirectX?
Generaldata Capturing from FM Tuner cardmemberamit_007_bond26 May '07 - 1:02 
Hi,
Can someone let me know, how to capture data from FM tuner card.Shall i use DirectSound or DirectShow.If there is any oher way to capture data from FM tiner card.then please let me know.
 
Thanx in Advance.
QuestionDirectSoundFullDuplexCreate8 return E_INVALIDARG???? [modified]member~Jabeen~24 May '07 - 19:42 
Hi all
 
i wrote following code for fullduplex mode of Sound but it returns E_INVALIDARG on DirectSoundFullDuplexCreate8
 

 

// Initialize COM
hr=CoInitialize(NULL);
if(FAILED(hr))
{
MessageBox("CoInitialize Fail.","Direct Sound",MB_OK|MB_ICONERROR);
return;
}
 
// enumeration of devices in list control
 
HRESULT hr=0;
int capture=0;
int render=0;
int effectIndex=0;
 
capture=m_captureList.GetCurSel(); //capture device taken from list
render=m_renderList.GetCurSel(); //render device
m_hNotificationEvent=CreateEvent(0,0,0,0);
if(m_hNotificationEvent==0)
{
MessageBox("CreateEvent Fail.","Direct Sound",MB_OK|MB_ICONERROR);
return;
}
ZeroMemory(&m_aPosNotify,sizeof(DSBPOSITIONNOTIFY)* NUM_PLAY_NOTIFICATIONS));
 
//Format and set Wave format
ZeroMemory(&m_wfxInput,sizeof(m_wfxInput));
m_wfxInput.wFormatTag=WAVE_FORMAT_PCM;
m_wfxInput.nChannels=1;
m_wfxInput.nSamplesPerSec=8000;
m_wfxInput.nBlockAlign=(1*16)/8;
m_wfxInput.wBitsPerSample=16;
m_wfxInput.nAvgBytesPerSec=8000*2;
m_wfxInput.cbSize=0;
 
//Set the notification size
m_dwNotifySize=m_wfxInput.nAvgBytesPerSec/50;
m_dwNotifySize-=m_dwNotifySize%m_wfxInput.nBlockAlign;
 
//Set the buffer sizes
m_dwOutputBufferSize=NUM_BUFFERS*m_dwNotifySize;
m_dwCaptureBufferSize=m_dwNotifySize*NUM_BUFFERS;
 
//Create DSCEFFECTDESC structures
DSCEFFECTDESC dsceffect[5];
ZeroMemory(&dsceffect,sizeof(dsceffect));
if(m_aec.GetCheck())
{
dsceffect[effectIndex].dwSize=sizeof(DSCEFFECTDESC);
dsceffect[effectIndex].dwFlags=DSCFX_LOCSOFTWARE;
dsceffect[effectIndex].guidDSCFXClass=GUID_DSCFX_CLASS_AEC;
dsceffect[effectIndex].guidDSCFXInstance=GUID_DSCFX_MS_AEC;
dsceffect[effectIndex].dwReserved1=0;
dsceffect[effectIndex].dwReserved2=0;
effectIndex++;
}
 
//Create DSCBUFFERDESC structure
DSCBUFFERDESC dscbd;
ZeroMemory(&dscbd,sizeof(DSCBUFFERDESC));
dscbd.dwSize=sizeof(DSCBUFFERDESC);
dscbd.dwFlags=DSCBCAPS_CTRLFX;
dscbd.dwBufferBytes=m_dwCaptureBufferSize;
dscbd.lpwfxFormat=&m_wfxInput;
dscbd.dwFXCount=effectIndex;
if(effectIndex)
dscbd.lpDSCFXDesc=dsceffect;
 
//Create DSBUFFERDESC structure
DSBUFFERDESC dsobd;
ZeroMemory(&dsobd,sizeof(DSBUFFERDESC));
dsobd.dwSize=sizeof(DSBUFFERDESC);
dsobd.dwFlags=DSBCAPS_CTRLFX;
dsobd.dwBufferBytes=m_dwOutputBufferSize;
dsobd.lpwfxFormat=&m_wfxInput;
dsobd.dwReserved=0;
dsobd.guid3DAlgorithm=GUID_NULL;
 
//Create DirectSoundFullDuplexCreate8
hr=DirectSoundFullDuplexCreate8(&(m_captureArray[capture].deviceGuid),&(m_renderArrayrender].deviceGuid),&dscbd,&dsobd,m_hWnd,DSSCL_PRIORITY,&m_IDSFD,&m_IDSC8,&m_IDS8,0);
if(FAILED(hr))
{
CloseHandle(m_hNotificationEvent);
m_hNotificationEvent=0;
MessageBox("DirectSoundFullDuplexCreate8 Fail.","Direct Sound",MB_OK|MB_ICONERROR);
return;
}

 

Can anybody tell me the reason of getting this error
 
I also tried
 

 
m_hr = CoCreateInstance(CLSID_DirectSoundFullDuplex, NULL,
CLSCTX_INPROC_SERVER, IID_IDirectSoundFullDuplex,
(void**)&m_IDSFD);
 
m_hr = m_IDSFD->Initialize(NULL,NULL,&dscbd,&dsobd,
this->m_hWnd,DSSCL_EXCLUSIVE ,&pDSCBuffer8,&pDSBuffer8); // it returns same E_INVALIDARG

 
I have Windows XP sp2 and DX 9.0.Can anyone figure out where lies the problem ??? and how can i make it working for full duplex mode of sound
 
Regards,
 

 
Jabeen

GeneralTo get PCM datamemberyasin1166 May '07 - 23:39 
Is there any method to get the data that is stored in the buffer in the PCM format.
I guess the data in PCM form is the current value at that instant. Is there any option to get all the values (integer values) from the buffer ?..
GeneralWell Done!membermmrezz21 Sep '06 - 19:04 
Thanks alot for this wonderful code.
 

GeneralMore sound applications playing at same timememberKedzo1 Dec '05 - 3:31 
Hi!
When i play my direct sound application while other sound application is running my wave file is played incorrectly.
I tested your application and it works great even with other sound applications playing at the same time. Is there some "thing" i should watch for? I am using streaming buffers.Could that be a reason?
 
Any help apritiated
 
Thanks!

GeneralWAV file mixingmemberNightfox16 Feb '05 - 16:05 
I have been looking for a way to mix several WAV files into a single WAV file (so that each sound from the separate WAV files plays simultaneously). Information on how to do this seems elusive. Is there a way to do this easily?
 
I've been working with DirectX (specifically, DirectMusic and DirectSound) in my application. DirectX is able to mix sounds before it passes the audio to the sound card.. Is there perhaps a way to simply have it re-direct the audio to a WAV file on the hard drive instead?
 
Eric
Generaldo not use static buffermembersuperliu27 Nov '04 - 16:25 
use a streaming buffer instead, static buffer is useful when the sound is short, however, when the sound file is large, your program could use up all the system memory; so try to use the streaming buffer and load only part of the sound into the buffer at a time.
 
Endeavor brings you freedom!
GeneralGreat JobsussAnonymous20 Jan '04 - 2:53 
Hi,
 
I have to say your class is exactly what I was looking for!
With the hint from Alan it works fine.
Thank you for sharing your skills with us...Sören Smile | :)

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 22 Mar 2001
Article Copyright 2001 by Zhaohui Xing
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid