[SOLVED] Playing an AudioClip blocking?

Started by cat, Sun 16/12/2018 18:42:15

Previous topic - Next topic

cat

Is it possible to play an audio clip in a blocking manner (i.e. synchronously)? It is part of a sound related puzzle where after a certain interaction I play a sound and when the sound is finished I do something else.

If it is not possible, I guess I could either check in repEx for the playback to finish or do a while loop in the calling function and check there.
Ideally, I would be able to combine this with a cutscene i.e. player interacts with item, sound starts, player clicks or presses escape, the action afterwards is performed immediately.

ManicMatt

If I understand that correctly, use the Wait(); command to block all actions after playing the sound. You might need a bit of trial and error to get the right timing when entering the number in the brackets. That's what I have been doing anyway, so I don't know if there is a better way. Sometimes I needed a character to animate and have him making say, a picking up sound, so I wouldn't use a blocking animation, instead I'd have the animation start as eNoBlock, then Wait, then play the sound, then Wait again, using the correct time I need to have the animation fully play out.

cat

Good point. Wait() should also work well with cutscenes.

Snarky

I believe this will work?

Code: ags
void PlayBlocking(this AudioClip*, AudioPriority pri)
{
  AudioChannel* c = this.Play(pri, eOnce);
  while(c.PlayingClip == this) Wait(1);
}

cat

I think so, but probably not within a cutscene. AFAIK wait() will be skipped in a cutscene, but this loop can't be stopped.

Crimson Wizard

#5
Quote from: cat on Mon 17/12/2018 19:18:38
I think so, but probably not within a cutscene. AFAIK wait() will be skipped in a cutscene, but this loop can't be stopped.

You may try:
Code: ags

while(c.PlayingClip == this)
{
    Wait(1);
    if (Game.SkippingCutscene)
        c.Stop();
}


Also you need to check if Play returned null, which it also will in case cutscene skip started earlier, I think.

cat


morganw

If looking for a more generic solution, I think you should be careful of blocking against an AudioChannel without any checks on it (it might get something else played on it unless you are sure nothing else could hijack it with a high priority, or just use it directly). Depending on the format and how you want to handle playback stopping early, it might be safer to read the duration immediately after playback starts (incorporating a null check for people who have the audio turned off) and then block against the duration instead of the actual playback.

cat

Also very interesting! Though, I checked the documentation of AudioChannel.LengthMs:
"This is supported by all file types, but with MIDI music it is only accurate to the nearest second."

I am playing rather short MIDI files, so this will not be accurate enough. Maybe I'll have some time later to convert them to ogg, but I'm working to a deadline with MAGS.

Snarky

Quote from: morganw on Mon 17/12/2018 19:48:00
If looking for a more generic solution, I think you should be careful of blocking against an AudioChannel without any checks on it (it might get something else played on it unless you are sure nothing else could hijack it with a high priority, or just use it directly). Depending on the format and how you want to handle playback stopping early, it might be safer to read the duration immediately after playback starts (incorporating a null check for people who have the audio turned off) and then block against the duration instead of the actual playback.

Since we're checking whether it's playing that particular clip, I don't see how those things would be a concern. The only case would be if the clip is interrupted by another copy of the same clip, which seems like the kind of edge case where you should just say "don't do that".

morganw

Depends what you are syncing the audio with, I mean that the loop can potentially end early as well as never end.
I did preface this with "generic solution" (for people searching for how to implement this in their game) because this has edge cases, as well as a missing null check on the channel.

Snarky

There should be a null check, you're right, but otherwise I think the other edge cases are "works as designed". If the point is to block for as long as that clip is playing, then exiting early if the clip is interrupted is correct, and not exiting for as long as the clip plays is arguably correct.

The right solution here really does depend on the specific requirements, though. The fact that turning off or failing to initialize sound will bypass the blocking entirely may very well not be desired behavior in many situations. If you want to block deterministically for a specific duration that matches the normal duration of the clip exactly (even for MIDI clips), there's no way to do that other than manually syncing a Wait() with the clip length, AFAIK.

ManicMatt

Quote from: Snarky on Tue 18/12/2018 18:18:40
..there's no way to do that other than manually syncing a Wait() with the clip length, AFAIK.

Its like we came full circle to my first post! :)

cat

So, for posterity, this is what I'm using and it works great, also regarding cutscenes:

Code: ags

void PlayBlocking(this AudioClip*, AudioPriority prio)
{
   AudioChannel* c = this.Play(prio, eOnce);

   if (c)
   {
      while(c.PlayingClip == this)
      {
          Wait(1);
          if (Game.SkippingCutscene)
          {
              c.Stop();
          }
      }
   }
}


Thanks, guys!

SMF spam blocked by CleanTalk