About C++ MFC CButton Asynchronous

Dear all,

I am very new to C++ with MFC Programming.
I am doing a school project which is to build a .WAV player using C++ with MFC. I must use the lower level API BUT CANNOT simply a call to "PlaySound(_T("filename.wav"), NULL, SND_FILENAME | SND_ASYNC);" to play the .WAV file.

The "play" function is already implemented, however, I don't konw how to implement the "stop" function, as when I press the "play" button (which trigger the "play" function), the "play" function LOCKs all other buttons, I cannot press the "stop" button to stop the music playing.



Are there any hints for me to implement such a "stop" function? Is it something related to "asynchronous", "multi-threading" or something else?



THANKS IN ADVANCE for any valuable reply.
when I press the "play" button (which trigger the "play" function), the "play" function LOCKs all other buttons
That's unusual. You'll have to post the code if you want help.
As there are few source code files, it may be too much codes to post, sorry about this.

Let me explain it more clearly.

When I press the "play" button, the music start to play, which last a while(the length of the time is just the length of the song.wav). after the song finished playing, the "play" button will release and other buttons can be presses again.

This is the situation.
I understand the problem.

You shouldn't post all your code, I'm only interested in the button click handler and the code that calls PlaySound.
Thanks, the following are some of the codes:

1) button click handler
void CtutoDlg::OnBnClickedPlay()
{
ManipulateWave("8id32.wav",0);

}

2)the manipulateWave() implementation:
void ManipulateWave(char *filename, int choice)
{
int i, err;
char errorMsg[200];
HWAVEOUT hAudioOut; // handle to the playback device

readWaveFile(filename);

// Open the audio playback device

err = waveOutOpen(&hAudioOut, WAVE_MAPPER, (WAVEFORMATEX *)&pcmWaveFormat, (DWORD)playSamples, 0L, CALLBACK_FUNCTION);

if (err) {
waveOutGetErrorText(err, (LPSTR)errorMsg, sizeof(errorMsg));
fprintf(stderr, "Error in WaveOutOpen: %s\n", errorMsg);
return;
}

// prepare the audio buffers

for (i = 0; i < BUFFERS; i++) { // #define BUFFERS 3
databuffer[i].lpData = new char[BUFFER_SIZE]; // #define BUFFER_SIZE 10240
databuffer[i].dwBufferLength = BUFFER_SIZE;
databuffer[i].dwFlags = 0;
memset(databuffer[i].lpData, 0, BUFFER_SIZE);
err = waveOutPrepareHeader(hAudioOut, &databuffer[i], sizeof(WAVEHDR));

if (err) {
waveOutUnprepareHeader(hAudioOut, &databuffer[i], sizeof(WAVEHDR));
waveOutClose(hAudioOut);
waveOutGetErrorText(err, (LPSTR)errorMsg, sizeof(errorMsg));
fprintf(stderr, "Error in WaveOutOpen: %s\n", errorMsg);
return;
}
}

// fill the audio buffers:
//databuffer[0], databuffer[1], databuffer[2]

if (mmioRead(hmmioIn, (databuffer[0].lpData), BUFFER_SIZE) != BUFFER_SIZE) {
fprintf(stderr, "Error reading wave data\n");
mmioClose(hmmioIn, 0);
exit(-1);
}
if (mmioRead(hmmioIn, (databuffer[1].lpData), BUFFER_SIZE) != BUFFER_SIZE) {
fprintf(stderr, "Error reading wave data\n");
mmioClose(hmmioIn, 0);
exit(-1);
}
if (mmioRead(hmmioIn, (databuffer[2].lpData), BUFFER_SIZE) != BUFFER_SIZE) {
fprintf(stderr, "Error reading wave data\n");
mmioClose(hmmioIn, 0);
exit(-1);
}

// Initially, play back 3 buffers of data

waveOutWrite(hAudioOut, &databuffer[0], sizeof(WAVEHDR));
waveOutWrite(hAudioOut, &databuffer[1], sizeof(WAVEHDR));
waveOutWrite(hAudioOut, &databuffer[2], sizeof(WAVEHDR));

while (!finished) {
// Do other things here!
// the program will continue to play by calling
// the callback function
if(choice == 0) // choice == 0 : PlayWave
finished = 0;
else if (choice == 1) // choice == 1 : StopWave
finished = 1;
}

// close mmio handle, and close the audio device

mmioClose(hmmioIn, 0);
for (i = 0; i < BUFFERS; i++) {
waveOutUnprepareHeader(hAudioOut, &databuffer[i], sizeof(WAVEHDR));
}
waveOutClose(hAudioOut);
finished = 0;
return;
}
In the button handler, kick off a timer, then use the handler for that to play the audio.

Define ID_PLAYAUDIO to some number larger than zero that doesn't clash with any existing timers. Then add a WM_TIMER handler.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void CtutoDlg::OnBnClickedPlay()
{
    StartTimer(ID_PLAYAUDIO, 5, NULL);
}

void CtutoDlg::OnTimer(UINT nTimerID)
{
    switch (nTimerID)
    {
    case ID_PLAYAUDIO:
        KillTimer(ID_PLAYAUDIO);
        ManipulateWave("8id32.wav", 0);
        break;
    }
    // ...
}
Thanks for your valuable reply first.
Is it the "StartTimer(ID_PLAYAUDIO, 5, NULL);" actually means the "SetTimer" function?

I have tried he SetTimer(ID_PLAYAUDIO, 5, NULL); with the "OnTimer" and put the ON_WM_TIMER() inside the message map,
But it still lock my UI when the music start to play.
I have tried to play the music after 5 seconds( by SetTimer(ID_PLAYAUDIO, 5000, NULL); ), after a click on the "Play" button, I can control the UI during / within the 5 seconds, however, when 5 seconds passed, the music start to play as expected, but the UI still being lock during the music playback.

I have asked some other schoolmates, they all said that I should think of "Multi-threading", "Critical Section" ... etc., is it I should try that way or you could give me more reference about "Multi-threading", "Critical Section" or anything related to these concept?

Thanks again.
If your ManipulateWave() function blocks GUI thread then WM_TIMER will not help you. A solution is to put that function implementation in a secondary thread created with _beginthreadex() or use some audio library which already does that for you.
Is it the "StartTimer(ID_PLAYAUDIO, 5, NULL);" actually means the "SetTimer" function?
Yes, sorry, my mistake.

But it still lock my UI when the music start to play.
I have tried to play the music after 5 seconds( by SetTimer(ID_PLAYAUDIO, 5000, NULL);
The ID is just a delay before the timer functions fires, we stop it in the handler.

If you're still blocked with the timer, then threads would be the way to go, but you still have the problem of stopping the thing when you hit the stopppp button.
Thanks all of you.
I am going to learn about threading first before I going on this project.
Topic archived. No new replies allowed.