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

Using DirectSound to Play Audio Stream Data

By , 9 Mar 2005
 

Introduction

This article with its code shows how to play audio stream data with DirectSound. It gives a more flexible method to control the stream data. The demo shows how to play, pause, stop, seek a small or a big WAV file.

Background

Before you read the code, you should know something about DirectSound. You can find the related material in:

MSDN\Graphics and Multimedia\DirectX\SDK Documentation\DiretX8.1(C++)\DirectX Audio

Using the code

The CMyDirectSound has the following public method:

HRESULT SetFormat(const WAVEFORMATEX WFE);
HRESULT SetCallback(LPGETAUDIOSAMPLES_PROGRESS Function_Callback, LPVOID lpData);
typedef HRESULT  (WINAPI *LPGETAUDIOSAMPLES_PROGRESS)(int iAudioChannel, 
                                                      LPBYTE lpDesBuf,
                                                      const DWORD dwRequiredSamples, 
                                                      DWORD &dwRetSamples, 
                                                      LPVOID lpData);
void Play();
void Pause();
void Stop();
void Release();
DWORD GetSamplesPlayed();

First, you should give the information of the audio data you want to play, with "SetFormat":

WAVEFORMATEX formatWav;
m_pWavFile->Open((LPTSTR)(LPCTSTR)m_strFileName, &formatWav, WAVEFILE_READ);
formatWav = *m_pWavFile->GetFormat();

if (NULL == m_pDYDS) {

  m_pDYDS = new CDYDirectSound;
}
m_pDYDS->SetFormat(formatWav);

Second, set the callback function which is to get the audio stream data:

m_pMyDS->SetCallback(GetSamples, this);

Certainly, the body of the callback function should be written by yourself. Then you can play, pause, stop the audio data.

The "GetSamplesPlayed" method will give the number of audio samples played after you begin play. You can use this method to give the playing position.

Points of Interest

I think there're two key points in this class.

1. How to control the DirectSound buffer circle?

I create a second DirectSound buffer with two seconds duration:

//Create Second Sound Buffer
dsbd.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY;
dsbd.dwBufferBytes = 2*m_WFE.nAvgBytesPerSec; //2-Second Buffer 
dsbd.lpwfxFormat = &m_WFE;

if ( FAILED(m_lpDS->CreateSoundBuffer(&dsbd, &m_lpDSB, NULL)) ) {

  OutputDebugString(_T("Create Second Sound Buffer Failed!"));
  m_strLastError = _T("MyDirectSound SetFormat Failed!");
  return;
}

And I set two DirectSound notify at 0.5 second and 1.5 second:

               (1st Notify)                          (2nd Notify)
0              0.5Second        1Second              1.5Second         2Second
|                  |               |                     |                 |
---------------------------------------------------------------------------
|                                  |                                       |
|--------------------------------------------------------------------------|
//Set Direct Sound Buffer Notify Position
DSBPOSITIONNOTIFY pPosNotify[2];
pPosNotify[0].dwOffset = m_WFE.nAvgBytesPerSec/2 - 1;
pPosNotify[1].dwOffset = 3*m_WFE.nAvgBytesPerSec/2 - 1;  
pPosNotify[0].hEventNotify = m_pHEvent[0];
pPosNotify[1].hEventNotify = m_pHEvent[1];

if ( FAILED(lpDSBNotify->SetNotificationPositions(2, pPosNotify)) ) {

  OutputDebugString(_T("Set NotificationPosition Failed!"));
  m_strLastError = _T("MyDirectSound SetFormat Failed!");
  return;
}

When you call the Play method, a timer will be triggered. The "TimerProcess" function will be called every 300 milliseconds.

//Beging Play
m_lpDSB->Play(0, 0, DSBPLAY_LOOPING);

//timeSetEvent
m_timerID = timeSetEvent(300, 100, TimerProcess, 
    (DWORD)this, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);

In the "TimerProcess" function, the next second audio stream data will be gotten when the current play cursor arrives the 1st or 2nd notify point.

void CALLBACK TimerProcess(UINT uTimerID, UINT uMsg, 
                           DWORD dwUser, DWORD dw1, DWORD dw2)
{
  CMyDirectSound *pDDS = (CMyDirectSound *)dwUser;
  pDDS->TimerCallback();  
}
//<\TimerProcess>

void CMyDirectSound::TimerCallback()
{
  LPVOID lpvAudio1 = NULL, lpvAudio2 = NULL;
  DWORD dwBytesAudio1 = 0, dwBytesAudio2 = 0;
  DWORD dwRetSamples = 0, dwRetBytes = 0;

  HRESULT hr = WaitForMultipleObjects(2, m_pHEvent, FALSE, 0);
  if(WAIT_OBJECT_0 == hr) {

    m_dwCircles1++;

    //Lock DirectSoundBuffer Second Part
    HRESULT hr = m_lpDSB->Lock(m_WFE.nAvgBytesPerSec, m_WFE.nAvgBytesPerSec,
    &lpvAudio1, &dwBytesAudio1,&lpvAudio2, &dwBytesAudio2, 0);
    if ( FAILED(hr) ) {

      m_strLastError = _T("Lock DirectSoundBuffer Failed!");
      OutputDebugString(m_strLastError);
      return;
    }    
  }
  else if (WAIT_OBJECT_0 + 1 == hr) {    

    m_dwCircles2++;

    //Lock DirectSoundBuffer First Part
    HRESULT hr = m_lpDSB->Lock(0, m_WFE.nAvgBytesPerSec, 
    &lpvAudio1, &dwBytesAudio1, &lpvAudio2, &dwBytesAudio2, 0);
    if ( FAILED(hr) ) {

      m_strLastError = _T("Lock DirectSoundBuffer Failed!");
      OutputDebugString(m_strLastError);
      return;
    }    
  }
  else {

    return;
  }

  //Get 1 Second Audio Buffer 
  m_lpGETAUDIOSAMPLES(m_lpAudioBuf, m_WFE.nSamplesPerSec, dwRetSamples, m_lpData);
  dwRetBytes = dwRetSamples*m_WFE.nBlockAlign;
  
  //If near the end of the audio data
  if (dwRetSamples < m_WFE.nSamplesPerSec) {

    DWORD dwRetBytes = dwRetSamples*m_WFE.nBlockAlign;
    memset(m_lpAudioBuf+dwRetBytes, 0, m_WFE.nAvgBytesPerSec - dwRetBytes);        
  }
  
  //Copy AudioBuffer to DirectSoundBuffer
  if (NULL == lpvAudio2) {

    memcpy(lpvAudio1, m_lpAudioBuf, dwBytesAudio1);
  }
  else {

    memcpy(lpvAudio1, m_lpAudioBuf, dwBytesAudio1);
    memcpy(lpvAudio2, m_lpAudioBuf + dwBytesAudio1, dwBytesAudio2);
  }
  
  //Unlock DirectSoundBuffer
  m_lpDSB->Unlock(lpvAudio1, dwBytesAudio1, lpvAudio2, dwBytesAudio2);
}
//<\TimerCallback>

2. How the following callback function works?

//Get Audio Buffer
  m_lpGETAUDIOSAMPLES(m_lpAudioBuf, m_WFE.nSamplesPerSec, dwRetSamples, m_lpData);

Do you remember what you have done when you set the callback function?

void CDYDirectSound::SetCallback(LPGETAUDIOSAMPLES_PROGRESS Function_Callback, 
                                 LPVOID lpData)
{
  m_lpGETAUDIOSAMPLES = Function_Callback;
  m_lpData = lpData;
}
//<\SetCallback>

Yes, you transfer the GETAUDIOSAMPLES_PROGRESS function's pointer to m_lpGETAUDIOSAMPLES.

m_pMyDS->SetCallback(GetSamples, this);

And the GetSamples is defined as:

HRESULT CALLBACK GetSamples(LPBYTE lpDesBuf, 
                            const DWORD dwRequiredSamples, 
                            DWORD &dwRetSamples, 
                            LPVOID lpData)
{
  CDirectSoundTestDlg *pDlg = (CDirectSoundTestDlg *)lpData;
  pDlg->GetAudioSamples(lpDesBuf, dwRequiredSamples, dwRetSamples);
  return 0;
}
//<\GetSamples>

HRESULT CDirectSoundTestDlg::GetAudioSamples(LPBYTE lpDesBuf,
                                             const DWORD dwRequiredSamples,
                                             DWORD &dwRetSamples)
{
  DWORD dwRequiredBytes = 0, dwRetBytes = 0;
  WAVEFORMATEX *pWFE = m_pWavFile->GetFormat();
  dwRequiredBytes = dwRequiredSamples*pWFE->nBlockAlign;
  m_pWavFile->Read(lpDesBuf, dwRequiredBytes, &dwRetBytes);
  dwRetSamples = dwRetBytes/pWFE->nBlockAlign;
  return dwRetBytes;
}
//<\GetAudioSamples>

You can write your own "GetAudioSamples" to get the audio stream data.

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

Cai Tao
China China
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

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralUsing DirectSound to Play Audio Stream Datamemberkhosrow parizi31 Aug '12 - 2:52 
QuestionLarger buffermemberrahnz29 May '12 - 15:17 
Buga little bug during play/pause switchmembertonyjiang8821519 Dec '11 - 2:17 
Questiona small problem with slider-positionmemberdumbledorehoho25 Jan '10 - 18:14 
Generalit is no good when work with windows media palyer 10membernofengyu23 Jan '10 - 20:22 
Generalthanksmembergj_code13 Aug '09 - 4:55 
Generala few question about play wave file with DirectSound and display spectrum.memberjacky_zz4 Sep '08 - 15:02 
hi, i've readed your article and i want to modify it and add spectrum support to it. but i got some questiones about it, can you help me resolve it?
 
question one:
the program open wave file and read wave file information to struct WAVEFORMATEX, the members of it like wFormatTag=1, nChannels=2, nSamplesPerSec=44100, nAvgBytesPerSec=176400, nBlockAlign=4, wBitsPerSample=16, cbSize=0. then program initial DirectSoundBuffer's size as 176400, we must set size to 176400, it can be set any number?
 
question two:
i add code to log DirectSoundBuffer's current play position and current write position to listbox control, the max value displayed in it which each of them may big than 176400, how can i get right start postion what i want to sample the buffer?
code like:
DWORD dwCurPlayPos, dwCurWritePos;
m_lpDSB->GetCurrentPosition(&dwCurPlayPos, &dwCurWritePos);
 
question three:
if i get right start postion of buffer, what right sample size may be suggest?
 
code modified by me can download here.
 
regards,
jacky_zz
QuestionAbout "GetAudioSamples"memberHuangAnbang10 Sep '07 - 18:16 
GeneralUnresolved Error for Release BuildmemberSeenu Reddi21 Feb '07 - 15:03 
QuestionThe Primary buffer is redundant????membercnxhwy23 Nov '06 - 16:11 
QuestionHow do handle block mode audio ?memberMusong22 Aug '06 - 16:54 
QuestionCalculation error in GetSamplesPlayed() ?memberAzonacun8 May '06 - 8:38 
AnswerRe: Calculation error in GetSamplesPlayed() ?memberKedzo6 Jul '06 - 11:51 
Question'dmusici.h' missing!memberWilliamZhou24 Mar '06 - 5:29 
AnswerRe: 'dmusici.h' missing!memberSreekanth Muralidharan27 Apr '06 - 19:49 
GeneralMeasuring the sound levelmembermsorc24 Feb '06 - 6:08 
QuestionUsing a Stream in stead of of File ?memberYossef30 Nov '05 - 23:07 
GeneralBug report and my fixessussIKedzo25 Oct '05 - 1:54 
GeneralConflicts with other applicationsmemberStevenDorning6 Oct '05 - 22:19 
GeneralSomething wrong when load different BitsPerSample WAV file!memberikkiu200130 Jun '05 - 23:51 
Generalabout mfc42u.libmembercaoxiang31 May '05 - 18:10 
GeneralRe: about mfc42u.libmemberYesterdayOncemore13 Feb '06 - 20:13 
GeneralRe: about mfc42u.libmemberDuncan1234529 Mar '06 - 21:59 
GeneralAudio Format..membermpallavi28 Apr '05 - 18:08 
GeneralRe: Audio Format..memberCai Tao28 Apr '05 - 21:27 
GeneralRe: Audio Format..membermpallavi29 Apr '05 - 1:45 
GeneralRe: Audio Format..sussAnonymous2 May '05 - 4:42 
GeneralRe: Audio Format..membermpallavi4 May '05 - 18:21 
Generalits urgent!membermpallavi19 Apr '05 - 18:13 
GeneralRe: its urgent!memberCai Tao20 Apr '05 - 20:48 
GeneralRe: its urgent!membermpallavi26 Apr '05 - 19:30 
GeneralRe: its urgent!memberCai Tao28 Apr '05 - 21:15 
GeneralRe: its urgent!membermpallavi28 Apr '05 - 23:16 
Generalone more difficulty...membermpallavi27 Apr '05 - 20:55 
GeneralConfused..plz helpmembermpallavi21 Mar '05 - 22:06 
GeneralRe: Confused..plz helpmemberCai Tao21 Mar '05 - 23:39 
GeneralRe: Confused..plz helpmembermpallavi22 Mar '05 - 17:25 
QuestionRe: Confused..plz helpmemberrajneshmalik30 Aug '07 - 19:44 
Questionhow to use PlaySound()?sussAnonymous20 Mar '05 - 21:09 
AnswerRe: how to use PlaySound()?membermpallavi21 Mar '05 - 18:25 
Generalquestionmemberweirdyi28 Feb '05 - 23:45 
GeneralRe: questionmemberCai Tao9 Mar '05 - 18:42 
GeneralA Jerky Sound at the end of playingmemberoiholkljio11 Feb '05 - 5:34 
GeneralRe: A Jerky Sound at the end of playingmemberCai Tao9 Mar '05 - 18:38 
Generalquestionmembersave20007 Nov '04 - 20:08 
GeneralRe: questionsussAnonymous8 Nov '04 - 14:58 
Generalquestionmembereuacela1 Oct '04 - 8:02 
GeneralRe: questionsussAnonymous3 Oct '04 - 4:32 
GeneralStreaming from a Media Servermemberdkmorris199917 Jan '05 - 16:46 
GeneralRe: Streaming from a Media ServermemberCai Tao20 Jan '05 - 15:10 

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 10 Mar 2005
Article Copyright 2004 by Cai Tao
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid