Play/Pause multiple audio tracks along a timeline

I'm a fairly new Hype user and my background is on the art side so I have very little to no coding experience. I've tried piecing various things together from other threads but can't seem to come up with a good solution to my issue.

I have numerous scenes that all string together to form a training presentation. Hype is great for this, and I really like that I can break things up between scenes that can be easily reconfigured to flow together in any arrangement the client needs.

I'm using timeline actions to call VO clips at various points in the timeline. This has worked tremendously (if only I could see the waveform it would be perfect), and lets me make timing adjustments to my animations much easier than if I used one long audio clip for the entire scene, which leads me to my issue - The client would love to be able to pause the presentation at any point during playback.

I can easily pause the timeline animation using a mouse click action on a persistent symbol pause button, but audio has been especially tricky since I'm using multiple audio files along the timeline. Is there a way for me to pause all audio, regardless of name or position on the timeline? If not, what would be the best way for me to handle this? My audio files are named based on the scene (I call them slides in this case since my wireframe was based on a powerpoint), so in Slide 2 I have Slide02-01.mp3 to Slide02-07.mp3, and follow that same naming convention for other scenes.

Any help in solving this would be greatly appreciated, thanks!

If it was me, I'd probably use https://howlerjs.com to manage the audio. It involves coding, but that's what's needed for greater control. Howler.js does make it easier manage audio. I might even look into using Audio Sprites to make the multiple audio files.

Audio Sprites
Easily define and control segments of files with audio sprites for more precise playback and lower resources.

Very cool, thanks! Looks like this would be very effective for what I’m doing, if not even a bit overkill.

Is my only recourse to look at external solutions like this or could I achieve what I’m looking for with just Hype? Everything else is working so wonderfully, this is the first thing I’ve run up against that has me scratching my head.

If this really is the only option I’ll have to get some coder colleagues involved to help me get it put together.

I appreciate your help!


Hi Michael!

Checkout this Demo: stopMultipleAudio.hype.zip (235.8 KB)

Just Hype & JavaScript - no external Libraries. I've used two different file types: ".mp4" & ".mp3".

Note: I've included audio controls in this demo for each file for ease of manipulation & visibility on the stage, but obviously not needed in the real production.


Two different approaches to handling a sound file in this Demo:

The ".mp4" has its class ("audio") set in the Hype interface ("Identity Inspector") and the ".mp3" is referenced from a rectangle's innerHTML with the following <audio> tag code:

<audio class="audio" width="400" controls="controls" preload="auto">
	<source src="${resourcesFolderName}/canCan.mp3" type="audio/mpeg">
</audio>

Note the class ("audio") is here set in the tag itself - not in the Hype interface - unlike the ".mp4" file. Also note the ".mp4" file shows up in the Timeline, whereas the ".mp3" file itself does not.

The ".mp4" file also could have had an audio tag set up; just wanted to show alternate set-ups here.


The "Play/Pause" button, which acts as a toggle, will play/pause all audio that has a class of "audio" and uses the following code (function stop_AllAudio):

var audios = document.querySelectorAll('.audio');
	
	if (audios.length != 0){
		for (var i=0; i < audios.length; i++){
			(audios[i].paused) ? audios[i].play() : audios[i].pause();
		}
	}

Note the "querySelectorAll" is selecting by a class named "audio" which we have given to our two sound files... BUT if You were to remove the "dot" before "audio" in the code You would be searching for an <audio> tag instead which would select for the ".mp3" file (no need for a class distinction).

2 Likes

Pause all audio is definitely a missing feature.

Beyond what has already been suggested, if you want to stick with Hype's built-in audio system, you could also create a custom behavior that holds actions to pause all audio. Then this would only need to be triggered once to get all of them. You could also place it in a persistent symbol so it appears on all scenes and you always have access.

Beyond what has already been suggested, if you want to stick with Hype's built-in audio system, you could also create a custom behavior that holds actions to pause all audio.

Thanks Jonathan, yeah I tried looking into using custom behaviors to tackle this (I'm actually using them with persistent symbols for my play/pause toggle) but I'm not certain what constitutes a hold action in this case. I only see the ability to stop audio entirely, and in doing so you have to specify which track to stop, not all audio in general. Obviously this due to my lack of knowledge on how to drive this sort of setup, would you mind elaborating a little on how I could accomplish this?

Thanks for the help, this forum is great for us artists not as versed in the coding side of things!

Thanks for your thorough description and example @JimScott! This works great but I need the audio to be played in sequence, and currently everything I'm using is in MP3, so I was calling these via an action at specific points in the timeline so they would play in sync to the animations. Is there a way to trigger these at specific times on the timeline like I'm currently doing, and then have the play/pause button only affect the audio currently being played?

Also just to be clear - I would need to create a separate shape for each of the audio tracks I need to play correct? So in the instance I mentioned above for Slide 2, I would have 7 shapes, all referencing a different mp3 file in their inner HTML?

I also tried the mp4 route and converted several mp3's to mp4 and placed them in the timeline, but then I'm unable to play these (autoplay is disabled without mute) at their specified times without invoking the play button first, which is an undesired effect since there is no interaction expected in these sequences.

Sorry for all of the confusion and questions, I truly appreciate your help with this!

To clarify that you are correct - you can only stop, not pause with the built-in functionality.

You can place multiple snippets of <audio> within a single rectangle's HTML. So just a single rectangle per scene would be enough.

You're definitely going to need to setup your interaction in such a way that audio plays in response to a user interaction. Without that, you would need to specifically handle 'unmuting' that mp4 video's automatically-playing muted content with an encouraging button.

Ah, that's unfortunate to hear - I was hoping I was just doing it wrong :slight_smile:

If you have any other ideas I'd certainly appreciate them.

Thanks @jonathan

You can place multiple snippets of <audio> within a single rectangle's HTML. So just a single rectangle per scene would be enough.

Thanks for clarifying this @Daniel. Regarding the other question I had, is there a way to trigger these via an action on the timeline, or perhaps some other means? Currently I'm using Play Sound actions to play them in sequence. Obviously I'd still need the ability to play/pause them like @JimScott outlined above.

EDIT: Just to clarify, I'm asking this with the assumption I'd stick to using MP3's due to the limitations you outlined with MP4.

Much appreciated!

The JavaScript for playing an audio element (myaudioelement) present on the scene is the following:

var myAudio = document.getElementById("myaudioelement");
myAudio.play()

So the above JS function could be run as a function triggered as a Hype Timeline Action, but most browsers will likely say 'user interaction needed'. So the play(); function would ideally happen in response to a click/tap.

You can chain any number of audio elements to play as long as you have the IDs:

var myAudio = document.getElementById("audio"); 
var myAudio2 = document.getElementById("audio2"); 

myAudio.play();
myAudio2.play();

And if you have the IDs, you can also make a toggle, so that each audio element can be set to pause if it's playing, or play if it is paused:

  if (myAudio.paused) { 
    myAudio.play(); 
    } 
  else { 
    myAudio.pause(); 
  }

@JimScott's tip of using document.querySelectorAll('.audio'); is helpful here, since it would handle each audio element with the class 'audio' on the page, instead of needing to know IDs.

Not to complicate things further, but @MaxZieb did some work on abstracting some of the hard parts of custom audio in this extension: Extend Tumult Hype with JavaScript: hypeDocument.extensions

1 Like

Thank you for the thorough explanation Daniel. I have a few questions regarding the implementation of this into a test scene I'm trying to put together.

In the shape tag, is the "class" the same as the "ElementID" I'm defining in the JS function you mentioned? In my example I've copied the code @JimScott provided for the shape which has the following:

<audio class="audio" width="400" controls="controls" preload="auto">
<source src="${resourcesFolderName}/canCan.mp3" type="audio/mpeg">

In my timeline I created the following JS function:

var myAudio = document.getElementById("audio");
myAudio.play()

After testing it (as you stated) it is not starting on it's own, so I went ahead and assigned the JS to a button click to test it, but it's still not triggering, which leads me to believe I'm just setting it up wrong.

Again, pardon my lack of knowledge how this is supposed to work.

As an aside, how is it that Hype's built-in Play Sound action works? I'm consistently getting timeline action audio to playback so long as I observe the following pattern:

  1. In my initial scene I have a "Start" button that jumps to my next scene (on click).

  2. In the following scene, I define an "On Scene Load" event of starting my background audio track, which loops throughout all the remaining scenes.

  3. After that I can call a "Play Sound" timeline action at any point on any of the following scenes and they will play back without any further user interaction, but only if I've followed the previous two steps.

If I just place a play sound action on a subsequent timeline but don't have an initial On Scene Load > Play Sound at some point in my scenes, those actions will not trigger without a button click.

Can this behavior be replicated by manually triggering an audio file via a JS function or is Hype doing something internally different when triggering an action on the timeline?

Sorry for my long-winded response, thanks again for all your help!

you need a closing </audio> tag. This has more info: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio

No. A class is inside of class="X" and an id would be within id="x"

Sharing a document with these tests sertup would be great so we can see exactly what you're doing.

This should work as long as you don't have any scene transitions (instant transition only).

You're 'engaging in media' so the browser is thinking 'hey, this guy really wants to play sound'. Chrome calls this the Media Engagement Index. But you may see different behavior in different browsers when it comes to this. Sometimes this engagement is linked to what you do on a domain.

You can probably play quiet static as an on scene load action until the browser algorithms become self aware :ghost:

you need a closing </audio> tag. This has more info: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio

Sorry Daniel yes I did have that, I just didn't include it in the codeblock, apologies.

No. A class is inside of class="X" and an id would be within id="x"

Thanks, this makes perfect sense.

Sharing a document with these tests sertup would be great so we can see exactly what you're doing.

Thanks for offering, I can't provide my actual project so I'm attaching a small example I put together to illustrate where I'm at with this. Please don't judge based on the voiceovers, I'm just using translated Lorem ipsum and it's... very interesting out of context :wink: .

Scene_PausePlay.zip (540.8 KB)

Currently I can get a single audio snippet to pause and play with the timeline, however my goal would be to potentially combine all the timeline action triggers into one JS, as well as provide a way to pause and play whatever content the timeline is currently at (if possible I'd like to incorporate my play/pause buttons into a persistent symbol).

The previous example provided by @JimScott regarding the document.querySelectorAll('.audio') worked great if I wanted to pause everything, but unfortunately it also plays everything back simultaneously as well and doesn't observe when these sounds are triggered on the timeline.

I really appreciate the help Daniel!

The easy way would be to not have multiple separate audio tracks if possible, so you can just pause / unpause a single track. But if that's not possible, you'll need to use the Hype timeline, the audio track durations, and do some calculation based on what should be playing.

The 'pause' audio button is easy: Just pause all playing audio elements:

var myAudio1 = document.getElementById("sample1");
var myAudio2 = document.getElementById("sample2");
var myAudio3 = document.getElementById("sample3");

myAudio1.pause();
myAudio2.pause();
myAudio3.pause();

And also pause the timeline.

Make sure you use a different 'var' value that is the same wherever you wish to control the audio.

The basic answer for a smarter 'unpause' button is something like:

if (hypeDocument.currentTimeInTimelineNamed('Main Timeline') > 14 && < 24)) { 
	myAudio2.play(); 
}

This says play audio2 only if the timeline is greater than 14 and less than 24.

You could potentially make this smarter and less manual: You'll need to know 'what track is playing at this point in the timeline' so you can unpause correctly without replaying each audio track again, but you'll need to know the track's order in playback to determine what should be playing at that time.

You can get properties of the Hype timeline itself (its duration and current position) by using these two functions:

hypeDocument.currentTimeInTimelineNamed('timelineName')

Returns the current time of the specified timeline in seconds.

hypeDocument.durationForTimelineNamed('timelineName')

Returns the duration of the specified timeline in seconds.


To know which audio elements to unpause when, you might want to look at the hypeDocument.currentTimeInTimelineNamed('timelineName') and then calculate which audio should be playing. I'm not sure if you wanted to do this all programmatically, but you could pull in the duration of each of the audio tracks, the combined duration of these tracks, and then an approximate relationship between these tracks and the timeline based on their ID.

You can get the duration of an audio track (audioID) by using:

setTimeout(function () {
    var audio = document.getElementById('audioID');
    console.log("audio", audio.duration);
}, 100);

More info: https://stackoverflow.com/questions/11203773/how-can-i-get-the-html5-audios-duration-time

You could theoretically combine these 'duration' values to determine at what point an audio element should be unpaused.

2 Likes

Wow, ok this is fairly involved for someone like myself with little to no javascript experience. I'll take a look at what you've suggested and see if I can't piece something together. If not I might see if I can snag someone more knowledgeable that I know to help me out :slight_smile: .

Thanks again Daniel for taking the time to patiently help me out with all this, everyone on the forum has been great, glad to be a part of what's going on here.

Also, might I make a suggestion that you guys add a pause all audio feature at some point in the future? :wink:

2 Likes

Hi to All!

Thought experiment as I currently do not have the time to try this out...

"Play/Pause" Btn toggle

• First Click - "Pause" currently playing audio tracks
Iterate through the class (e.g. "audio") given to the desired audio tracks. If a given track is playing put that audio track into an array & pause the audio track.

• Second Click - "Play" all tracks in the Array
Every track in the Array would be played from the point it was paused at and then removed from the Array for the next "Pause".

Won't have time to work this through until later this weekend - seems straightforward enough.

@mhermes I am putting this idea out for other Forum members as I understand that You are new to JavaScript

2 Likes

@JimScott the fact you're even trying to tackle this is so humbling, much appreciated. Also I wholeheartedly concur - far outside of my understanding! :slight_smile:

1 Like

Of course this is a good request, and a bit embarrassing it is not currently in Hype :slight_smile:.

2 Likes