Stop audio on slide revisit – an alternative with javascript and a hidden button
So I was trying to do this with a few key ideas in mind:
- I need the audio to be slide audio, since I'm using closed captions on some slides
- I did not want to create custom variables for each slide, to record if it is visited or not. I am making a framework with templates that will be used for many courses, so I need it to be generic and just ... work without having to create a new variable, each time i copy and re-use a slide.
- I did not want to pause the timeline. Some slides may need to use the timeline regardless of the sound playing.
The first thing I needed was a way to make a way to store what slides are visited and not. I wanted it more generic than a custom made variable for each slide, so I opted for a js Map(). This stores two items [key, value] and you can easily .get and .set values. So in the On enter-advanced action I create a tab that executes a javascript. I create the map, if it does not already exist. I then loop through with a for-lop, using cpInfoSlideCount and set the keys to "i" and the value to "notVisited". I set the initial value of i to 1 instead of 0, so that it corresponds with how CP count the slides, starting with 1 and not 0.
I then need a way to stop slide audio, preferably without pausing the project. Now, all buttons has this as an option. So, I created a shape-button with No action on success and ticked "Stop slide audio" under its Options. I called the button btn_stopAudio, hide it, set not to pause and to show for the entire project. So I have an invisible tool that when "clicked" will stop the slide audio. Now all you have to do, is to "click" the button using javascript.
//Store the button in a variable. Otherwise, the script can't reach the button, although it is set to show for the rest of the project
// querySelectorAll used this way, captures all elements in the scene whose name starts with btn_stopAudio. It stores them in an “array” starting with the canvas element (stopAudio[0].id == object_namec). The second is the object in the scene (stopAudio[1].id == object_name)
var stopAudio = document.querySelectorAll("[id^='btn_stopAudio']");
//Create the map, if it is not already created
if(!mapSlideVisited){
var mapSlideVisited = new Map();
for(i = 1; i < cpInfoSlideCount+1; i++){
mapSlideVisited.set(i, "notVisited");
}
}
Unfortunately, the javascript executes so quickly, that it is finished running, once the slide audio begins. I therefore had to add some silence to the beginning of my sound files. I added 0,5 sek. This is non the less useful, otherwise the sound doesn't begin playing too abruptly, in my opinion. I therefore had to set a timeout for the following functions.
//wait 100 ms
setTimeout(function() { funcStopAudio(); }, 100);
function funcStopAudio(){
//stop sound if slide status = "visited"
if("visited" == mapSlideVisited.get(cpInfoCurrentSlide)){
cp.clickHandler(stopAudio[1]); //The clickHandler takes the object, not the ID of the object.
}
}
//wait 200 ms
setTimeout(function() { funcSetSlideVisited(); }, 200);
function funcSetSlideVisited(){
//Set the slide status to "visited"
mapSlideVisited.set(cpInfoCurrentSlide, "visited");
}
I do hate to have to add an invisible button as a workaround to a project. But I already have one, to load an external js using cpextra. It just seems that all these workarounds should be unnecessary, and that CP should give us the tools, variables and access to make stuff like this happen. I'm hoping Project Charm will make something like this a lot easier.
I've tested the solution on our LMS (Moodle), and it seems to work fine. Happy to hear any thoughts you might have on the solution.
Here's the complete js in the advanced action. I add this to a tab in the on enter advanced action on all slides (except for the first line) that should only play slide audio on the first slide visit:
//This first line only goes on the first slide of the project. The rest goes on all you need the sound to stop on, for the second visit.
var stopAudio = document.querySelectorAll("[id^='btn_stopAudio']");
if(!mapSlideVisited){
var mapSlideVisited = new Map();
for(i = 1; i < cpInfoSlideCount+1; i++){
mapSlideVisited.set(i, "notVisited");
}
}
setTimeout(function() { funcStopAudio(); }, 100);
function funcStopAudio(){
//stop sound if slide status = "visited"
if("visited" == mapSlideVisited.get(cpInfoCurrentSlide)){
cp.clickHandler(stopAudio[1]);
}
}
setTimeout(function() { funcSetSlideVisited(); }, 200);
function funcSetSlideVisited(){
//Set the current slides status to "visited"
mapSlideVisited.set(cpInfoCurrentSlide, "visited");
}
