Play/Pause multiple audio tracks along a timeline

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 - #20 by MaxZieb

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: <audio>: The Embed Audio element - HTML: HyperText Markup Language | MDN

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: <audio>: The Embed Audio element - HTML: HyperText Markup Language | MDN

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

The first part of acceptance is admitting there is a problem. :wink: joking!

Seriously though @Jonathan how hard would this realistically take to implement? Is this a feature that would be difficult to add?

Thanks again for paying attention to my little issue and admitting it needs to be tackled in the app.

Aside from never being able to accurately answer that until the feature is complete, and our general policy of not making promises about upcoming features/releases: I think the reason we didn't do this originally was more from a UI side (action menu complexity) than it being a difficult task. It is on the radar :slight_smile:.

2 Likes

Fair enough Jonathan, thanks!

1 Like

Hi Mike!

Didn't forget You - I had a job that was something of a Sisyphean task - but that's done and now on to your request to start/stop multiple audio tracks along a timeline.

Live Demo here.
Hype Project: playPauseMultipleAudio_JHSv3.hype.zip (367.6 KB)

Overview

In the Scene we see several elements (Please see Fig.1 Below for reference):

  1. A red rectangle that rotates, representing animations on the timeline.

  2. Three soundtrack controls, one for each soundtrack in this Demo. It is not necessary to have these soundtrack controls visible. They are in the innerHTML of the rectangle ("rect") called "Audio Tag Holder". You can have multiple audio tags (soundtracks) in one rect. You can simply drag this rect off screen to hide it or remove the "Controls" from the audio tag itself. It would be good to have this rect offscreen in any event to reduce clutter.

  3. A "Start Playing" button, which when clicked, starts the "Main Timeline" and starts the second audio track ("dig-Vondra.mp3" - voiceover) playing by triggering the function "playSnd2". After this button is clicked it disappears to be replaced by the "Pause" button. This "Pause" btn is a toggle that will pause and play the sound tracks. When this "Pause" button is clicked it will place into a list (called an "array") any currently playing sound tracks... a "Play" button then "appears" (same button just the name changes).

At 3 seconds into the "Main Timeline" the "playSnd1" function is triggered via a timeline action which plays audio track 1 ("dig-wind1.mp3" - wind sound effect).

When the "Play" button is clicked all the soundtracks that were placed into the array will start playing from the exact place in each soundtrack as when the "Pause" button was clicked. In this Demo there will be at most two soundtracks triggered. The third soundtrack not used directly but is a "control group" (more on this under "Details" below "Fig.1"

Fig.1
Screen Shot 2021-02-17 at 9.51.35 PM


Details

Inside the rect "Audio Tag Holder" is where, as previously mentioned, the audio tags reside (Please see Fig.2). This is where You can remove the "controls" for an audio track. You will also note each audio tag has the class "voiceOver". This can be changed as desired - but it will need to match the class specified in the function "playPauseAudio" which does most of the work in this Demo (more on this in a bit).

Note: Audio track "CanCan.mp3" was used here as a control. It has the class "voiceOver" but it is not playing anywhere. It was used to test that only audio tracks (class "voiceOver") that were currently playing would be selected. Audio tracks (class "voiceOver") that were not playing would not be selected.

Fig.2
Screen Shot 2021-02-17 at 10.27.26 PM



function "playPauseAudio"

This function does most of the work in this Demo (Please see Fig.3). As You seem unfamiliar with JavaScript I will not write up details here with one important exception (and the reason why I am showing it at all):

In line 5 is where we select all (querySelectorAll) of the audio tags with our desired class ".voiceOver" - note the "dot" in front of ".voiceOver" which is how we indicate class in this JavaScript notation (the audio tag's class notation does not have a dot):

var audios = document.querySelectorAll('.voiceOver');

Fig.3
Screen Shot 2021-02-17 at 11.36.27 PM

You can change this class to another name, just make sure the class in the audio tag agrees with the class in line 5 of this function. There are rules for naming of classes read more about them here.

OR if You wanted to select ALL the audio tags regardless of class You could just use the following in line five of the code:

var audios = document.querySelectorAll('audio');

Note that we are referencing the audio tag here only - no "dot" used (which indicates class).

You do not need to manipulate any other part of this code for things to work as presented in this Demo.

3 Likes

Jim, I can't thank you enough, it works wonderfully!

The only issue I have at this point is that I need to be able to clear the array so that I can continue to use the same JS calls for every slide. So for instance, I have a timeline action to trigger my audio playback, so I'm using the audio id of "slide(xx)". In my first slide that equates to slide01 - slide07.

I named them generically enough that I could reuse those same ids for each slide, and I'm hopefully not needing to make unique audio id's for every single instance throughout the presentation. Is there a way to 'flush out' the array after moving to another scene?

Regardless, it's a solution that works, so thank you!

1 Like

Hi Mike!

Glad You have found the Demo to be of use!

I will be checking in with You here tomorrow afternoon (Monday, 22nd - PST). Briefly - there is already a flush mechanism for the array in place. I will go over the ID scenario with You at that time as well.

2 Likes

Hi Mike!

So let us note here that IDs are unique for a given element on a page. Hype Scenes (in a single Project) are really all on one page. So IDs will have to be unique for every Scene in your project - ditto for individual Layouts which will need unique IDs as well.

Why don't You post here on the Forum a simple sample project with say two Scenes, each with maybe 3 slides and a couple of audios for each slide in the Scene, that is representative of your over all design. This way I can better understand your set-up.

If I can see an example we might be able to come up with an efficient solution. Without a Demo of yours to reference I will be guessing - instead of understanding - your needs (though I have a reasonably good idea).

The Forum has a 3MB upload limit, but a simple Demo should come in under that (small images & short audio tracks). If your images are of a confidential nature You can PM me - or use images/audio tracks that do not have this restriction.

1 Like

Ah very good, thanks for clarifying those points. I was afraid that would be the case, it's good to know my assumptions were correct on this.

I'd be happy to throw something together when I have a moment to spare, right now we're trying to wrap up a few things so that has most of my attention, but if you think there might be a more optimal way to accomplish what I'm trying to do I'm all for it.

Thanks again for all your help Jim, I'll be sure to hit up this thread again soon!

1 Like