Reputation: 1333
I'm studying how to pass arguments into a setTimeout (as advised here.) In the context of a while-loop which activates an HTML5 Audio element.
This works:
window.setTimeout(playTone, time, phrasePitches[x]);
Curiously, this does not work:
window.setTimeout(function(){
playTone(phrasePitches[x]);
}, time);
In the console, the timeouts occur as scheduled, each displaying:
TypeError: Cannot set property 'currentTime' of null
So for some reason, the second method does not want to accept the array... any idea what's going on here?
EDIT... the full code:
function playTone(tone){
var tone = document.getElementById(tone);
tone.currentTime = 0;
tone.play();
};
var pitchSet = new Array ("C3","D3","E3","F3","G3","A3","B3","C4");
fyshuffle (pitchSet); // The Fischer-Yater shuffle function
var i = 0;
var phrasePitches = new Array();
while (i < 4) {
phrasePitches.push(pitchSet[i]);
i++;
}
var x=0;
var time = 0;
while(x<4){
// window.setTimeout(playTone, time, phrasePitches[x]); // This works.
window.setTimeout(function(){
playTone(phrasePitches[x]);
}, time);
time += 2000;
x++;
}
Upvotes: 1
Views: 60
Reputation: 43880
This demo uses an audiosprite⁰. I used Audacity program to concatenate small MP3 files into one larger MP3 file✲. Then I took note on each fragment's start and end time in seconds. There are other ways to generate an audiosprite if the manual way seems arduous.
✲ I found MergeMP3 after creating the audiosprite, so I haven't tested it yet, but it looks far easier to use than Audacity.
Details are commented in demo and notes corresponding to references
// Reference the <audio>
var fx = document.querySelector('#fx');
/* Reference the <fieldset> (can be any element with an
|| endtag: <tag></tag>
*/
var panel = document.querySelector('#panel');
/* Map the audiosprite's frags in an object of arrays
|| Each array represents a frag which consists of:
|| 'key': [start time, end time]¹
|| TODO: replace object literal with ES6 Map²
*/
var sprite = {
'all': [0, 27],
'click': [0, .45],
'bell': [.65, 7.4],
'elevator': [7.5, 9.7],
'sonar': [10, 27]
};
// Declare default value of end time
var end = 0;
// Register the <audio> on the loadeddata event³...
fx.addEventListener('loadeddata', function(e) {
/* Object.keys chained to .forEach()⁴ iterates through
|| 'sprite' properties (key/value pairs).
*/
Object.keys(sprite).forEach(function(key, index) {
/* On each iteration, generate a <button> inside the
|| <fieldset>
|| Note the use of a template literal⁵
|| TODO: Delegate⁶ click events to panel to avoid
|| inline attribute event handlers
*/
panel.innerHTML += `<button onclick="effect('${key}')">${key}</button>`;
});
}, false);
// Register the <audio> on timeupdate event⁷...
fx.addEventListener('timeupdate', function(e) {
/* timeupdate occurs when playing position changes
|| check if the currentTime⁸ property of <audio>
|| is more than the value of 'end'
|| ('end' is the second value of a frag: sprite[key][1])
|| If it is, pause the <audio>
*/
if (fx.currentTime > end) {
fx.pause();
}
}, false);
/* This function passes the string value of 'key'
|| in sprite object
|| Each 'key' has a value of an array containing an start
|| time and an end time.
|| In short, 'key' is a reference to a frag.
*/
function effect(key) {
// if the 'key' in 'sprite' object exists...
if (sprite[key]) {
/* The currentTime of <audio> is
|| the first value of 'key'
*/
fx.currentTime = sprite[key][0];
// Assign 'end' the second value of 'key'
end = sprite[key][1];
/* Play <audio>
|| Note at this point, if done conventionally by
|| reloading the .src for each change to a separate
|| frag, fx.load() would need to be invoked
|| as well, thereby incurring another HTTP request
*/
fx.play();
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1, user-scalable=no">
<style>
</style>
</head>
<body>
<section>
<!--
An audiosprite⁰ is an audio file that is actually a
concatenation of multiple sound files
(from now on refered to as frags) played at
different times throughout its duration.
The advantages are that:
- the file is downloaded once then cached
- the src doesn't change thus only one HTTP request needed
If done the conventional way with multiple frags there
would be:
- several downloads for smaller files
- the added HTTP request everytime the src attribute changes
its url
-->
<audio id='fx' src='http://vocaroo.com/media_command.php?media=s0L5VMshEG3E&command=download_mp3' controls></audio>
<fieldset id='panel'>
<legend>Effects</legend>
</fieldset>
</section>
<!--
If using JS on the same page rather than externally,
place the JS here within a <script> block:
<script>
...:::JS code here:::...
</script>
-->
</body>
</html>
✎ Mentioned as TODO, it has not been implemented in this demo.
Upvotes: 1