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:

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.

Hello, i am working on a sound-play program, your code is good and help me a lots.Thank you . I found something wrong in frequent switching play and pause , i had tried this very fast and the sound i heard the sound is just the data last puted , and in loops , but if i kept it playing, i will fixed back in about 1s. So it`s not a big problem.

Hello~ Cai Tao!I'm a freshman in DirectSound programming~Your project is really a terrific guide for me.I find that when I drag the slider to a specific position, it won't stay there but instead move on a few more distance. I guess the problem lies in void CDirectSoundTestDlg::TimerCallback(){DWORD dwSamplesPlayed = m_pMyDS->GetSamplesPlayed();m_ctrlPlaySlider.SetPos(dwSamplesPlayed+m_iBeginPlayPos);...}

when the slider is dragged to a new place, i see m_iBeginPlayPos is exactly that position. However dwSamplesPlayed is not zero, the slider will "move on a dwSamplesPlayed distance".

I'm now trying to revise the project as a practise.Thank you, Cai~! Your project helps me a lot^^

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:

I get the following error:MyDirectSound.obj : error LNK2001: unresolved external symbol _IID_IDirectSoundNotifywhen building with Release option. It does not happen with unicode or debug builds.Anyone knows why ? When building Microsoft code samples (like StreamData), thoughIID_IDirectSoundNotify is used, I do not get the same error with release build.

during my debugging the project, I found the primary buffer which was created as a local variable and it doesn't be used anymore.So I discarded it and the project worked as well as the former.Is it possible that the primary buffer is redundant in this application? I was a fresh man in dsound, so I do not know how to explain this appearance.I hope some one can give some answer,thanks!

First off, I'd like to thank yah for the wonderful code Cai, very well done and very useful. I do have one concern though I'm trying to hunt down.

I've written an audio controller class with wave visualization and all the bells and whistles associated with it. I call your Play command to start playback and poll every 50ms or so the GetSamplesPlayed() routine of yours to find out how far into the audio I've actually played so I can move my waveform, cursors, and just about everything else.

On a fast machine with plenty of memory, she works flawlessly everytime. On slower machines though, the values returned by GetSamplesPlayed() are erratic. From looking into the code and the circumstances, it appears that when the buffers fill up to a certain point (because of the slower CPU), the routine starts reporting values that are smaller then where it should be, then catches up with itself. Say I'm playing a wave file. The values are going fine, updating properly and incrementing in the right amounts. Suddenly the control reports that it's playing at a position a full second and a half (and sometimes more) behind its current position, then a second later re-updates itself back to where it should be, aka it jumps back and forth from a previous position to the correct position. The only thing I can think of is that theres a calculation error somewhere in the GetSamplesPlayed() routine in here:

I can't quite figure out what the code is doing with the values in this section, besides obviously having something to do with the buffers. Is there any chance you can either explain what this section is doing, or see if there's a simple calculation mistake causing the problem? Or anyone else for that matter? I'd really appreciate it, great work!

C. Russell

[Edit]

Did some more searching, what I assume is supposed to happen, is m_dwCircles2 and 1 are incremented everytime their respective buffer section is filled with data for playback, and as such (if my assumption is correct) should always be within 1 value of eachother, aka (dw1=0,dw2=0 , dw1=1,dw2=0 , dw1=1,dw2=1, dw1=2,dw2=1, etc...). If this assumption is correct, then for some reason, right before the jitters hit, they become out of sync. I'm showing dw1=16 and dw2=14, which seems to be fine right up to the point where Case D (see code above) is executed. Once that increment is done, all hell breaks loose. Hope that helps some figuring out what's gone wrong.

GetSamples method works fine if events are triggered correctly.But they are not. I think it has to do something with DirectSoundNotify which is responsible for sync proces. I am not sure about slower machine problem but this application will play incorectly even with fast maschine if you play two sounds or more in the same time.The only reason I could think of is DirectSoundNotify.It reports when certain part of buffer is reached. But when you have two or more applications you have two or more secondary buffers and it will notify when any of them gets to certain point instead of watching only your secondary buffer.And then hell breaks loose....I changed applicaton and kicked this Notification aproach and it worked fine

I downloaded the source code and compiled it using "Rebuild All",but there will always come up with an error as the following:h:\my_project\mydirectsound_demo\mydirectsound_demo\stdafx.h(24) : fatal error C1083: Cannot open include file: 'dmusici.h': No such file or directoryError executing cl.exe.so how to solve it?Thanks you very much!

I need to develop the software to measure the level of audio signal to make a decision when the sound level falls below some threshold (e.g. signal from mice disappear or people stop talking in room and leave it).

Can anybody point me on how to do this? Any help (link, theory, code, etc.) would be appreciated.

Thank you for this article.I am a newbee in Sound.1) Is there a way to use this sort of code to work with a stream incoming to the computer (like from RS-232) ? 2) So I would like to start analysing incoming sound. What's the approach you advice me ?

First I want to thank you on your article.It was very helpfull since this is my first time playing with sounds.While looking thru code and playing some waves I found few bugs.If you think I got something wrong please correct me.Also, anyone with advice is welcomed.

Noitce: all testing I did was with very short wave files, max 5 sec(like the ones you can find in WINDOWS/MEDIA directory )

Bugs:

1) When pausing a wave file that is currently playing and then moving scroll to some other position,will result in playing wave file incorrectly.

2) When opening mono wave file and after that opening stereo wave file, file will be played incorretly Notice: you may not notice this if you first play stereo file and then mono

3) If there is an error when opening file, you want be able to play any file after that.

4) Sometimes(depending on pause position) when you play file and pause it and then play it again you will hear strange sound(bip) Notice: This might be hard to discover(read 4th fix for more information)

5) Hitting play botton mulitiple times while file is currently playing causes scroll to go faster and can result with strange sounds

My fixes and comments:

1)This happens because you dont call Stop function and you dont kill timers

2)I think this has something to do with COM objects(dont know much about them).Since you sometimes want to create new secondary sound buffer(with different waveformat)I think you should just release IDirectSound pointer to COM before you want to create a new secondary buffer.In your code you took the old secondary sound buffer reference and called CreateSoundBuffer passing the new waveformat to function.You didnt get any error and it seems that buffer is created right(I checked it with GetFormat) but for some "COM"(or whatever) reason it doesnt play secondary buffer correctly.It might have to do something with creating COM with DirectSoundCreate metod.Remember,this is only my teory!

I fixed this by releasing the m_lpDS (pointer to directsound COM object) befor reusing it.Begining of your SetFormat should look like this:

void CMyDirectSound::SetFormat(WAVEFORMATEX WFE){ m_WFE = WFE;

if(m_lpDS!=NULL) /*ADDED CODE { m_lpDS->Release(); } */

...

3)Well this one was easy.You just frogot to clear lastError so program thinks error is still there.

I fixed this by adding this incredibly complicated function in CMyDirectSound.cpp

4)This thing took me some time to figure out but I think I fixed it.I noticed this bug when I was playing Windows XP ShutDown wave from upper mentioned directory.When I paused the play somewhere near middle of the file and then when I let it play I would hear strange bip.Its not easy to discover this,you will probably need to play and pause file(Windows XP ShutDown) 10 times(or more),but its there.I think it has to do something with timers, at least that is the way I fixed it. MY teory is this,when you play the file and then pause it you killed timers,so, if you did this just when notification point has been reached(or little after,but must be before timer callback ocured) and your timer didnt done callback (because you paused and killed timer) to fill the new data to buffer,when you hit play it takes some time(in your program minimum is 300 miliseconds)for timer to makecallback again and call TimerCallback(which will refill buffer with new data) and that time might not be enough.Again,just my teory!

My fix: I just lowerd the timer period to 200 miliseconds and it seems to work

5)Hiting play multiple times can cause strange things(your scroll goes faster and your wave file might be played incorrectly).I think reason why this happens is because you reset timers mulitiple times or whatever.My advise is to control thiswith flags.

My fix:I just added bool playflag variable as private member of CDirectSoundTestDlg class.I initialized it to TRUE in CDirectSoundTestDlg constructor and than added it in following functions:

I just want to add few more comments and ask you few quetions.I read somewhere that you should not relay on DirectSoundCreate metod to create the COM object and releasing them for you when your aplication ends.You should do these things by yourself.I fallowed instructions from MSDN on how to do it with CoInitializeEx and so on, but it throws some exception so I didnt look at it anymore.Did you try it maybe?

Also I wonder why you went with this "notify points" approach. For me, it seems better not to depend on notification events(I say this because bug number 4),but then again if you do it rigth way,its not important.I read in some page where they use timer and set it to report every 1/4 of secondary sound buffer time length(if buffer is 2sec,it will report every 500 milisec.) and then they just check how much free space is there in secondary sound buffer and they fill free space. So,this way they dont depend on DirectSound notification events to tell them when to fill new data because they check secondary sound buffer by themselves.Do you have some experience with this metod?

p.s.What is m_iDB variable in CMyDirectSound class for? You frogot to erase it?

mfc42u.lib is the unicode version of the MFC library. When you installed your compiler, you probably didn't tick the "Install unicode libraries" box. This doesn't matter, though ... just build the non-unicode version instead.

Hi Cai Tao(I also sent you a mail regarding this.)A wave file with Audio Format: "CCITT A-Law" cannot be played using your application.. How do i play compressed wave files?.. Can we modify the WaveClass ?.. WaveClass seems to take care of those waveforms whose wFormatTag value is other than WAVE_FORMAT_PCM... But the file is not played.. I will be very grateful if you help me in this matter.. My project completion date is coming closer and i am stuck with this problem.. Thanking youpallavi

I'm sorry that I don't know the audio format: "CCITT A-Law". I think that you have to design and code a "CCITTClass" just like the "WaveClass" to uncompressed the "CCITT A-Law" audio to WAVE_FORMAT_PCM audio. Also you have to write your own "GetAudioSamples" to get the uncompressed WAVEE_FORMAT_PCM audio stream data from "CCITTClass".

You can use google to see if there's someone else has written a "CCITTClass".

// Now, read those extra bytes into the structure, if cbExtraAlloc != 0. if( mmioRead( m_hmmio, (CHAR*)(((BYTE*)&(m_pwfx->cbSize))+sizeof(WORD)), cbExtraBytes ) != cbExtraBytes ) { SAFE_DELETE( m_pwfx ); return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL ); } }.......................When i try to play the wave file with A-Law format, else part is executed.. because i got the wFormatTag value as 6 in the logfile.. But the file is not played.. Does this code take care of files with formats other than PCM..I really don't know much about DirectSound.. This is the first time i am coding such application.. Your classes and suggestions were a great help.. Thank you very much..Best regardsPallavi

Hello Cai..Thank you..I modified the code as per told by you.. Now the file is played but the voice is not heard.. Instead noise with varying levels is heard.. I am trying to learn the wave file format.. I tried putting:wFormatTag=WAVE_FORMAT_ALAW;But it didn't work.I am still trying.. Could hear the voice when you did those modifications?What About WAVEFORMATEXTENSIBLE..? I think it takes care of compressed waveforms..Thanx for your help.Regards..Pallavi.

Hi CaiI need to maintain a logfile which should contain the start and end of recording times. Using DirectSound i am getting same start and end times. Where should the code writing current times to the log file appear?Its urgentThank you..pallavi

If the "time" means the start position or the endposition in the wav file where you begin or endplayback, you can try to save the first audio sampleyou start to palyback, and"CMyDirectSound::GetSamplesPlayed()" when you stop toget the audio samples you have playbacked. Then:the end audio sample = the first audio sample +GetSamplesPlayed()

CTime getTime=CTime::GetCurrentTime();CString endTime=getTime.Format( "%H:%M:%S, %B %d, %Y" );WriteLogFile(endTime);..................But the startTime and endTime contain the same values.. if the playback is been done in a separate thread is it possible to record the timings of the playback?.. Please tell me how to do it..Thank youpallavi

Hi Cai..I am still not able to save the current times before and after the playback (both times are same even if the playback is for more than 10 secs) in the log file ..I have another query to ask you.. This application plays only PCM encoded wave files .. I want to play a file which is encoded using CCITT A-law.. What changes need to be done to the code? Its urgent.. Please c if you can help me to do these two things..Thank you pallavi

I downloaded the DirectSoundTest and it works fine.. i wanted similar functionality in my application which uses propertysheets.. is it possible?

Secondly, how to add "external dependency" to a project.. Is this application using DirectMusic tool wizard?.. playing wave file is just a part of my project.. I did it using PlaySound function.. but i don't know how to get volume slider, pause and stop functionalies... can you plz tell me how to go about it?...

Thank you so much Cai..I think the questions were showing my lack of knowledge in this topic but you answered them.. thanx.. Actually i am doing MCA and this project is part of my final semester.. I am learning vc++ simultaneously.. i thought it is a better option then VB..But its very vast..Some lines about my project.. the first part-> The telephone will be connected to USB port using an adapter.. I have to record and save the voice in a wave file.. the dialed number should appear on the screenThe second part-> I have to play these recorded wave files.. I think i have to use Microsoft tapi for the telephone part.. still in the learning stage..Awaiting for some suggestions from you.. Regards pallavi

hii want to play two sound file same time and want to listen the sound of these file on separtly on left and right speaker,means sound of one file should play on leftspeaker and second file should play on right speaker

In "Tools->Options->Directories->Include Files", you should place "E:\DXSDK\INCLUDE" before "C:\Program Files\Microsoft Visual Studio\VC98\INCLUDE", otherwise the compiler will search "C:\Program Files\Microsoft Visual Studio\VC98\INCLUDE" first and the error will happen.

By the way, the Unicode Release version has a bug. It lacks some libs. You can add "dxerr8.lib" and "dxguid.lib" at "Project->Settings...->Link->Object/Library modules". Otherwise you will meet the following error: