Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Playing sounds sequentially 1

Status
Not open for further replies.

sjspain

Programmer
Sep 26, 2002
15
US
Is there a way to use PlaySound() to play several sounds sequentially (as if each call was made with the SND_SYNC flag) but still return contol to the user at the start of playing the first sound? If the SND_ASYNC flag is used, only the last sound will play, of course. What I want to do is queue up the sounds to play sequentially and return control to the user so he can stop the sounds if desired at any point during their play.

Thanks, Steve
 
I have tried this, but apparently when the SND_SYNC flag is used, whether in a separate thread or not, the system waits for the sound to finish playing before any other messages are processed. I will try it again, but I am pretty sure this is what I ran up against last night.
 
Yes and the other problem you will have is that since PlaySound() will block you may not be able to stop the playing. If you use the asynchronous flag then you will not know when the sound finishes and therefore when to start the next one.

Perhaps you could build the separate sounds into a single stream and use the asynchronous play so you can stop it upon a user command?


-pete
[sub]I just can't seem to get back my IntelliSense[/sub]
 
The problem with using the SND_ASYNC flag is that, if you have several sounds playing in a row, each call to PlaySound stops the currently playing sound to play its sound, with the result being only the last call to PlaySound is heard.

Is there a function that checks to see if the audioplayer is busy? If so, I could maybe use PeekMessage() to check for user messages while the player was busy playing a SND_ASYNC sound. "Sound" promising? ;-)

Steve
 
palbano is on track.

Though You'll have to use another SDK such as MCI. Look at waveOut.
 
Well, let me back up a minute...

The reason I want the sounds played separately is so I can call InvalidateRect() between them and "automatically" update the window.

For example, on LBUTTONUP from a Window's child button, initiate the sequence:

Draw the window
Play a sound "Welcome to my Window...blah blah blah...Look at the arrow!"
Draw an arrow on the window
Play a sound: "Do you like the arrow...blah, blah, blah..."
Draw some more
Play a sound: "Thanks for looking at my window...blah blah blah...press the button to proceed now"
return;

Stringing the sounds together won't allow me to update the window as the sounds play.

The trick is to keep the Button active so that if the user wants to press the &quot;next&quot; button of the &quot;Back&quot; button <before> the sequence is finished, he can and his message will be handled and the sequence will terminate.

If the messages are not being handled during the sound play, then the user can't stop the presentation. Surely there is a simple solution?
 
>> Surely there is a simple solution?

Now you are contradicting yourself. PlaySound() is the simple solution!

The complex solution is the Waveform API that PlaySound() is a part of? PlaySound() is a high level function for simple sound playing. If you want the type of complex control you desire you can use the lower level functions of the API, for example see: waveOutOpen()

-pete
[sub]I just can't seem to get back my IntelliSense[/sub]
 
>> Draw the window
>> Play a sound &quot;Welcome to my Window...blah blah blah...Look at the arrow!&quot;
>> Draw an arrow on the window
>> Play a sound: &quot;Do you like the arrow...blah, blah, blah...&quot;
>> Draw some more
>> Play a sound: &quot;Thanks for looking at my window...blah blah blah...press the button to proceed now&quot;
>> return;


Couldn't you PlaySound use one big long wave file and then use timers to sync your drawing events to that

I REALLYhope this helps.
Will
 
Will,

I like your idea IF the timers are on a separate thread so that the WndProc can return control to the user after PlaySound and spinning off the timer thread. I seem to recall something, though, about not being able to send messages to the queue from a new thread, so I wonder if calls to InvalidateRect() or SendMessage() from the new thread will work OK. I am pretty much a SingleThreaded kind of guy, but I will read up on it. I will let you know how it works out.

thanks,
Steve
 
>> so I wonder if calls to InvalidateRect() or SendMessage
()
>> from the new thread will work OK

No. You use PostThreadMessage() to post messages across threads then once back in the UI thread you can send messages to your windows.

-pete
[sub]I just can't seem to get back my IntelliSense[/sub]
 
Oh, I know how to get the thread ID of the new thread, but what about the original thread ID? How do I get that?

Steve
 
DWORD GetCurrentThreadId()

-pete
[sub]I just can't seem to get back my IntelliSense[/sub]
 
any difference bwtween that and

DWORD ThreadID = GetWindowThreadProcessID(hWnd, NULL);

?

In response to WM_LBUTTONUP, I have used the above and sent &ThreadID to my thread proc:

_begingthread(MyProc, 0, &ThreadID);

and in my thread proc I call:

PostThreadMessage(*pThread, WM_LBUTTONUP, 0,0); which returns successfully...but there is no action in response to the message.

So long as the button window is being displayed, isn't its thread queue active? I have not been able to figure out why my message is not being sent to the Button WndProc. Any ideas? And thanks for all your help so far.
 
First, that is not at all what i posted.

No. You use PostThreadMessage() to post messages across threads then once back in the UI thread you can send messages to your windows.

Code:
// in worker thread
PostThreadMessage(*pThread, MY_THREADMSG, 0,0L); 

// in UI thread
LPARAM myThreadMsgHandler(WPARAM wp, LPARAM lp){
  // post whatever UI message you want
  PostMessage( hWndEdit, EM_UNDO, 0, 0L);
}

Second: why are you trying to send a LBUTTON_UP message to anything let alone from a worker thread to a UI thread [bugeyed]

-pete
[sub]I just can't seem to get back my IntelliSense[/sub]
 
Well, sorry I frustrated you...

re: your &quot;Second...&quot;
Getting back to my original PlaySound problem, I am trying to play a sound, start a timer in a separate thread, paint a window, return control to the user, and when the timer thread runs out send a message to the UI thread to repeat the process with the switch variable incremented, and repeat the process for a second sound, etc, as outlined below:

In the button WndProc:

case WM_LBUTTONUP:

DWORD Thread = GetCurrentThreadID(hWnd, NULL);

switch(step) { // a global int, set to 0 at startup
case 0:
PlaySound(_T(&quot;Sound_0.wav&quot;), 0, SND_ASYNC);
_beginthread(MyProc, 0, &Thread);
InvalidateRect(GetParent(hWnd), &rt, TRUE);
step++;
case 1:
PlaySound(_T(&quot;Sound_1.wav&quot;), 0, SND_ASYNC);
_beginthread(MyProc, 0, &Thread);
InvalidateRect(GetParent(hWnd), &rt, TRUE);
step++;

case 2:
PlaySound(_T(&quot;Sound_2.wav&quot;), 0, SND_ASYNC);
_beginthread(MyProc, 0, &Thread);
InvalidateRect(GetParent(hWnd), &rt, TRUE);
step++;
default:
break;
}
if(step>2)
step=0;
return 0; // end of the handling of the WM_LBUTTONUP message

and MyProc() does:

void MyProc(PVOID, pThread) {

DWORD * pThr;
pThr = (DWORD*) pThread;

switch(step) {
case 0:
Sleep(5000);
PostMessage(*pThr, WM_LBUTTONUP, 0,0);
break; // 'step' will == 1 on return
case 1:
Sleep(5000);
PostMessage(*pThr, WM_LBUTTONUP, 0,0);
break; // 'step' will == 2 on return
case 2:
Sleep(5000);
PostMessage(*pThr, WM_LBUTTONUP, 0,0);
break; // 'step' will == 3 on return
default:
break;
}
_endthread;
}

Okay, so I thought that when I posted a message to the thread with PostthreadMessage(), the message was put in the queue just like with SendMessage() in the UI thread, but I guess that is not the case.

Where is the myThreadMsgHandler() function call placed?

Thanks,

Steve (who is not the experienced Multithreaded-Man!)
 
>> Well, sorry I frustrated you...
I see your new to Tek-Tips, welcome [cheers]

You've got a loooong way to go before we reach frustration levels [lol] After you have been here a while you’ll know what I mean.

So now lets talk about your overall strategy. I previously suggested that your requirements are such that you should be using the lower level functions for your solution. I still believe that, but with that said lets look at another technique. Since your going to use a Timer to approximate when each sound has completed playing you don’t really need a worker thread.

Start the first Sound using SND_ASYNC and start the Timer with the time for that Sound. When the Timer event fires you Stop the Timer, start the next sound and start a Timer for the next sound, repeat until finished.

Now if it’s not a problem that you either have small gaps between sounds or small overlaps between sounds this should work. If you require that gaps or overlaps do not exists then I believe the lower level API’s might be the only way your going to accomplish this.

One other thing you might try using threads would be this. If you can stop a current sound from playing by calling PlaySound() from another thread then this might do the trick.

Start the thread with a pointer to a DWORD flag value that is set to 0. In your thread function you could do this:

Play first sound in blocking mode
Upon completion if the flag is still 0
notify UI thread that the next sound is starting
Play the next sound in blocking mode
Repeat until all sounds are played or the flag is == 1

In your UI thread if the user cancels the sound playing you can use InterlockedIncrement() on the flag then call PlaySound() to stop the current sound. Again this will only work if the UI thread calling PlaySound() will cause the current sound to stop and unblock the PlaySound function in the worker thread.

If your still interested in using PostThreadMessage() and since your doing SDK development, meaning that you are handling your own message loop rather than using MFC you should read the SDK as there are details that you will have to deal with. So look in the MSDN or if you don’t have it go to msdn.Microsoft.com and look at the SDK documentation for PostThreadMessage()




-pete
[sub]I just can't seem to get back my IntelliSense[/sub]
 
Gee, thanks for all the info. I am going to look over the lower level functions again and see if they will provide me with way to do what I want.

The problem with
PlaySound
Timer
PlaySound
Timer
PlaySound

is that (if they are called in the ButtonWndProc) control is not released to the user until all the sounds have been played. This is a kiosk app, where the user is listening to a series of descriptions or explanations. If the user wants to move on and not hear all the info associated with that page, he needs to be able to do so quickly. Ie, the call that starts a new window, WM_LBUTTONUP, in this case, needs to return quickly.

I appreciate all your advice/information, and will do some more reading and experimenting as you have suggested.

Thanks again,
Steve
 
>> is that (if they are called in the ButtonWndProc)
>> control is not released to the user until all the sounds
>> have been played.

Yes. You don’t do all that in the button handler code. You only start the first sound and timer in the button handler code. Then the rest of the calls are made in the Timer Event handler code.

Code:
case WM_LBUTTONUP:
  PlaySound(...first sound, SND_ASYNC);
  SetTimer(...);
case WM_TIMER:
  KillTimer(...);
  if( more sounds){
    PlaySound( next sound, SND_ASYNC);
    SetTimer(...);
  }



-pete
[sub]I just can't seem to get back my IntelliSense[/sub]
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top