Can't toggle start()/stop() for ToneJS oscillators - javascript

I have some JS cobbled together with the ToneJS library to toggle some sounds on/off.
function togglePlay() {
const status = Tone.Transport.state; // Is either 'started', 'stopped' or 'paused'
const element = document.getElementById("play-stop-toggle");
if (status == "started") {
element.innerText = "PLAY";
Tone.Transport.stop()
} else {
element.innerText = "STOP";
Tone.Transport.start()
}
document.querySelector('#status').textContent = status;
}
var filter = new Tone.Filter({
type: 'lowpass',
Q: 12
}).toMaster()
var fmOsc = new Tone.AMOscillator("Ab3", "sine", "square").toMaster()
var fmOsc2 = new Tone.AMOscillator("Cb3", "sine", "square").toMaster()
var fmOsc3 = new Tone.AMOscillator("Eb3", "sine", "square").toMaster()
var synth = new Tone.MembraneSynth().toMaster()
//create a loop
var loop = new Tone.Loop(function(time){
// synth.triggerAttackRelease("A1", "8n", time)
// fmOsc.start()
fmOsc2.start()
fmOsc3.start();
}, "4n")
//play the loop between 0-2m on the transport
loop.start(0)
// loop.start(0).stop('2m')
Inside the loop I have a drum beat that I have commented out currently. When it was commented in, the togglePlay() function would start and stop it as expected.
However, the fmOsc2 and fmOsc3 functions will start when I toggle start, but do not terminate when I toggle stop.
For reference, here is what the HTML side looks like: <div class="trackPlay" id="play-stop-toggle" onclick="togglePlay()">PLAY</div>
How can I get the fmOsc2 and fmOsc3 functions to toggle with the button state?

The Tone Transport is not meant to be used in this way, either for starting or stopping oscillators (it can start/stop oscillators, of course, but I don't think that it's doing what you want it to do) :-)
If you add a console.log within the function that you're passing to Tone.Loop, you will see that it's being called repeatedly i.e., you're calling fmOsc.start() over and over again. (It does make sense to do the repeating drum beat in that Loop though)
Calling Tone.Transport.stop() does not stop the oscillators -- it only stops the Loop / Transport function (i.e., the "time keeping"). The drum beat makes it obvious -- pressing Stop in your UI kills the drums, but the oscillators keep oscillating.
The easiest / most direct (and only?) way of stopping the oscillators is to call .stop() on them:
if (status == "started") {
element.innerText = "PLAY";
Tone.Transport.stop()
fmOsc2.stop()
fmOsc3.stop()
} else {
// ... [snip] ...

Related

opponent moves promise in multiplayer game

I have spent the last four days studying promises, coroutines, fibers, continuations, etc.
I am still unable to see how to resolve my multiplayer turn-based card game moves, in which the starting player is effectively the game 'controller' of up to five, either AI or human players.
The code below works but has one problem:-
it cannot detect human oppo's card moves and therefore continues playing without them, which, of course makes a mess.
Can anyone please suggest either a change to my overall concept or a way to use promise or one of any other 'synchronising' constructs?
Here are the four key areas of my code:
function oppoPlays () {
// can only go through here if game starter
if (joiner!="") {return;}
for (pp=1; pp<numberofplayers; pp++) {
if (oppoType[pp] == "AI") {
// an AI player moves
.
.
} else {
// non-AI player
var yourTurnmsg="It's "+playerNames[pp]+"'s turn";
// tell human player that it's their turn
$("#text_message").val(yourTurnmsg).trigger(jQuery.Event('keypress', { keyCode: 13, which: 13 }));
// how to detect human oppo's card moved?
}
}
}
// chat functionality
$("#text_message").on("keypress", function(e) {
if (e.keyCode == 13){
payload = new Object();
payload.action = 'chat_text';
payload.chat_text = tmsg; // It's michael29's turn
payload.user_id = playerNames[pp];
payload.game_no = game_no;
socket.send(JSON.stringify(payload));
}
});
// socket gets oppo's response
function checkJson(res, sttr_id, game_no) {
if(res.action=="game_move"){
// find player
var pp=playerNames.indexOf(res.user_id);
cpos=res.cardno;
playCard_oppo(pp, cpos);
}
}
// turn an oppo's card face up and update scores
function playCard_oppo(pp, cardno) {
// and move it to the stack
topoc= parseInt($("#oppo_card" + cardno).css('top'));
leftoc=parseInt($("#oppo_card" + cardno).css('left'));
$("#oppo_card" + cardno).css({ top: topoc, left: leftoc, opacity: "50%" });
.
.
if (joiner=="") {
// tell oppoPlays fn that the card has moved
}
}
The game is similar to uno in concept but with a scoring component
(aimed to help children with basic arithmetic).
Consider having a board state that is a global and player/AI moves modify it. Then, when it is time for the AI opponent to make a move, it consults the current board state and decides the move.
If your board state is just represented by elements on the page you'll need a way to scan it and calculate a useful in-memory representation of the board state. Without details of your implementation it's hard to be more specific.
Maybe start thinking in terms of a play-cycle, something like this :
(host) Broadcasts game state.
(host) Awaits confirmation from all clients that broadcast was received.
(clients) Render game state.
(host) Receives confirmations then informs next client (and hence its player) that it is his/her/its turn.
(host) Awaits player's move
(client) Unlocks UI allowing player to make move.
(player) Makes move.
(client) Sends move command and re-locks UI.
(host) Receives move command and modifies game state accordingly.
Then back to 1.
Also, think of AI players as just a special case of human players. If you can get it right for humans, then (re)introducing AI should be fairly simple.
The solution hinged around two things:-
separating the AI player code from the human player code;
adding and removing a window event that's triggered after a human's move is detected.
The condensed code now looks like this:-
// if this is game starter give each player a turn
if (joiner == "") {
// there's always at least one
pp = 1;
if (oppoType[pp] == "AI") { AIplays(); } else { humanPlays(); }
}
function humanPlays () {
// tell human player that it's their turn
var yourTurnmsg="It's "+playerNames[pp]+"'s turn"
$("#text_message").val(yourTurnmsg).trigger(jQuery.Event('keypress', { keyCode: 13, which: 13 }));
//window.addEventListener("humanPlayed", function(evnt) {
$(window).on("humanPlayed", function(evnt) {
endOfTurn();
});
}
function endOfTurn () {
if (!(winner)) {
if (pp++ != numberofplayers) {
if (oppoType[pp] == "AI") {
setTimeout(function (){ $("#clickForNextPlayer").show(); }, 1000);
} else {
$("#clickForNextPlayer").trigger('click');
}
}
}
}
// click for next player
$("#clickForNextPlayer").on('click', function() {
$("#clickForNextPlayer").hide();
$(window).off("humanPlayed");
if (pp == numberofplayers) {
// uncover outStack for game starter to play
$("#outStackcover").hide();
return;
}
if (oppoType[pp] == "AI") { AIplays(); } else { humanPlays(); }
});
function AIplays () {
AIcardno = chooseCard(pp, diffLevel);
.
.
if ($("#chatWindow").is(":visible")) {
payload = new Object();
payload.action="game_move";
payload.game_no=gamestarted;
payload.user_id=playerNames[pp];
payload.cardno=AIcardno;
socket.send(JSON.stringify(payload));
}
$("#oppo_card" + cc).css('background-image', "url(JerseyTeam" + playerNumbers[(pp == numberofplayers ? 1 : pp)] + ".gif)");
outStackturn();
endOfTurn();
}

function increment sync with video (or auto increment)

I'm busy with a webdoc that I'm partially creating on hype, the video are hosted on vimeo (so I need to use the vimeo api for some tasks like seekto) but my difficulties should be limited to js.
the objective is to display a given image at a given time interval of the video.
With my code below, I do get the string "test", "success" and "confirmed success" at the right time in my div id=popimgbox, and I can seek back and forth in the video and still get the right "answear", if I may say so.
Now, I have images that are all stored in the same folder, and all named popimgX.jpg, with X being a number.
I want
to store the URLs of my images in a variable let's say "popimgurl"
that my variable is updated (by a function???) in order to contain the URL of a given immage for a given interval of time of the video
to still be able seekto back and forth in the video and get the right URL at the right time
To do so I created a function increment, and a pair of variable. With the code below, my popimgurl variable is indeed updated once the video reach 3 seconds, but it do not increment only once... untill the video reach 6 seconds, when I want to update my popimgurl variable once again.
I tried to use for with js break and js closure but did not manage for some understandable reasons after thought;
I did quite some try with switch, but I'm stuck with the fact that the case must be string or single numerical value, not numerical interval or comparaison.
thank's in advance for your help :-)
var iframe = $('#vplayer_1')[0];
var player = $f(iframe);
var status = $('.status');
fired = 0;
//my try to sync increment
var dia = (function () {
var n = 0;
return function increment() {return n += 1;}
})();
function dian(){
popimgurl = '${resourcesFolderName}/popimg'+ dia() +'.jpg';
popimgloader = '<img src ="' + popimgurl + '">';
}
// When the player is ready, add listeners for pause, finish, and playProgress
player.addEvent('ready', function() {
status.text('ready');
player.addEvent('pause', onPause);
player.addEvent('finish', onFinish);
player.addEvent('playProgress', onPlayProgress);
});
// Call the API when a button is pressed
$('button').bind('click', function() {
player.api($(this).text().toLowerCase());
});
function onPause(id) {
status.text('paused');
}
function onFinish(id) {
status.text('finished');
}
function onPlayProgress(data, id) {
status.text(data.seconds + 's played');
//my chapters, when I want the img to change within popimgbox
if (data.seconds >= 1) {
popimgbox.innerHTML = "test";
}
if (data.seconds >= 3) {
// popimgbox.style.display = "success"
dian();
popimgbox.innerHTML = popimgurl;
}
if (data.seconds >= 6) {
// popimgbox.style.display = "confirmed success"
dian();
popimgbox.innerHTML = popimgurl;
}
}
PS1: disclamer, I'm a beginer coder, i do my best so excuse my french if my question isn't well formulated or if the answer is somewhere but I was unable to see/understand it
PS2 : i did quite a try with popcornjs, but not way to make it work with vimeoapi and within hype, quite frustrated ;-)
PS3: as this is my first post I would like to thank's you all for the great support available here; I owe you most ;-)
Finally I'll answer myself.
It's a solution that only stand for vimeo, as this is what I use to host my videos, but very little changes have to be done to work with the html5 <video> tag as well.
First you need to define your variables and your intervals:
var intervals =[11.56, 44.08, 115, 125, 127.92, 177.72];
var index;
Then you need to add an event listener timeupdate that return the elapsed time , filter intrevals according to the elapsed time data.seconds or seconds and define the value of index as the indexOf the last entry of your filtered array intervals
player.on('timeupdate', function(data) {
seconds = data.seconds;
index = intervals.indexOf(intervals.filter(function(nb) {
return seconds < nb;
})[0]);
if (diaIndex == -1) {
// do something if seconds > the higher value of your last interval
}
And that's it !
Now for
seconds = [0, 11.56[ --> index = 0
seconds = [11.56, 44.08[ --> index = 1
seconds = [44.08, 115[ --> index = 2
and so on
Now we can use index as a variable for instance to display a given image :
var imgNum = 0;
function diaplayImg(index) {
if(index === imgNum) {
return;
// this will avoid that the same image is releaded on every timeupdate events
}
else {
imgNum =+ index
document.getElementById('myImageWraper').innerHTML = "<img src='img" + imgNum+ ".png'>"
};
}
Don't forget, you need to call the function displayImage() in your timeupdate event listener, it will then be fired every ±250ms but the image won't be reloaded each time
PS : vimeo has realeased its new api between my question and my answer so the question use the old api while the answer use the new one

Drum machine sequencing involving jQuery

Suppose I have the following code:
var blinker = function(element){
if(stopped){
return;
} else {
var sampleMapping = {'0': 'bass.wav',
'1': 'clap(2).wav',
'2': 'hihat(4).wav',
'3': 'tom(9).wav',
'4': 'hihat.wav',
'5': 'ArpEC1.wav'}
if (element.attr('value') === 'hit'){
var sample = element.attr('id');
instrument(sample);
}
element.fadeOut(200);
element.fadeIn(200);
}
}
var sequencerRun = function(){
var currentTime = 0;
var starting = 200;
var startTime = 0;
for(var k = 0; k < 16; k++){
$(".instrument td .beat" + k).each(function(){
setTimeout(blinker, currentTime,$(this));
})
currentTime += starting;
}
}
var timerId, setInt;
var runSeq = function(){
setInt = setInterval(sequencerRun,3200);
}
$('.play').click(function(){
stopped = false
sequencerRun();
runSeq();
})
$('.stop').click(function(){
clearInterval(setInt);
stopped = true;
})
I have writting code to carry out a sequencer run in my drum machine. A deployed version of my app can be seen here
So what I have done is created a matrix using a table HTML structure. When play is clicked sequencerRun is invoked and runs once. After this first run setInterval takes care invoking all sequencerRun until a user clicks stop. There is a flag within these click event listeners which I will come back to.
The heart of this sequencing is with sequencerRun. For each column in my matrix, which corresponds to a beat, I am scheduling a check on that column to see if any element has been selected for future play. sequencerRun only takes care of scheduling. More explicitly:
setTimeout(blinker,0, $(this)) is the same as check the first column right away
setTimeout(blinker,0, $(this)) is the same as check the second column at time 200
setTimeout(blinker,0, $(this)) is the same as check the third column at time 400
....
setTimeout(blinker,0, $(this)) is the same as check the sixteenth column at time 3200
Now what blinker does is checks to see if stopped, the original flag I created is true or false. It becomes true when clicking stop which then means that the column check is not carried out and nothing is play. If false then the normal process of the drum machine is carried out.
Now here's the problem. If you go to the deployed version of the app this flagging introduces a nasty bug. That bug is if you press play the sequencer starts, but if click play again another run is started which is not how a drum machine is supposed to work. Essentialy what I would like to see is if I press play no other sequencerRun should kick off if I press play again. Also if you click play, stop, play I get the same side effect. I think my main problems are the fact that sequencerRun schedules future beats right away and my flagging. Any one have any idea on how to tackle this?
Just make use of your stopped variable within the .play click handler:
$('.play').click(function(){
if (stopped) {
stopped = false
sequencerRun();
runSeq();
}
});
If the stopped flag is false, clicking play again will do nothing. To stop the rest of the queued sequence when you click stop, you'll need to clear the timeouts that have been set. First of all, make sure you keep a handle to each of them:
var sequenceTimeouts = [];
var sequencerRun = function(){
var currentTime = 0;
var starting = 200;
var startTime = 0;
//reset array
sequenceTimeouts = [];
for(var k = 0; k < 16; k++){
$(".instrument td .beat" + k).each(function(){
//push each timeout into the array
sequenceTimeouts.push(setTimeout(blinker, currentTime,$(this)));
})
currentTime += starting;
}
}
Then within your stop click handler, clear each of those:
$.each(sequenceTimeouts, function(i, timeout) {
clearTimeout(timeout);
});

how to bypass the setTimeout throttle in a hidden tab?

I'm am doing a heavy "scientific" (ie, not displaying data) webgl computation. Webgl can't be put in a worker, and doing a lot of webgl blocks the whole browser so I sliced my computation in chunks, and I compute each chunk in a setTimeout() function (after calling getError() to flush the opengl queue). I leave a bit of time in between the chunks so that the browser has time to flush some UI events from the main UI queue and it makes the whole thing feel a bit less sluggish.
My problem is that when the tab is hidden, the setTimeout gets throttled to a one second period which is way too slow for me.
Is there a better solution than what I did? Obviously requestAnimationFrame() doesn't work, since it's never called back in hidden tabs (and it's too slow in visible).
Is there a non-throttled time event in the hidden state? I tried to use window.postMessage() but it's still too fast and the whole browser feels slow.
here is the current state of my research:
function drawTile(sequenceIndex) {
if (sequenceIndex < sequence.length) {
var x = sequence[sequenceIndex][0];
var y = sequence[sequenceIndex][1];
setTilePos(x, y);
modelStage.render(renderer, modelBuffer);
minkowskiPass.render(renderer, minkowskiBuffer, modelBuffer);
copyPass.quad.position.x = x;
copyPass.quad.position.y = y;
copyPass.render(renderer, null, minkowskiBuffer);
var gl = renderer.getContext();
gl.getError();
sequenceIndex++;
if (document.visibilityState != "hidden") {
setTimeout(function () {
drawTile(sequenceIndex);
}, 10);
} else {
//window.postMessage is not rate limited then the tab is hidden
// we need to slow the computation by an event, otherwise the whole browser is unresponsive.
$(window).one('message', function () {
drawTile(sequenceIndex);
});
window.postMessage('lol', '*');
}
} else
console.timeEnd('computation');
}
console.time('computation');
drawTile(0);
Here's another convoluted workaround for anyone who needs it; you can use the Web Audio API to generate function calls:
var setTimeout2 = (function () {
var samples = 2048;
var fns = [];
var context = new AudioContext();
var source = context.createBufferSource();
var node = context.createScriptProcessor(samples, 1, 1);
// This gets fired every ~46 milliseconds. You can change
// `samples` to another valid value (256, 512, 1024, 2048,
// 4096, 8192, or 16384); then it'll get called every
// `samples / context.sampleRate` seconds (~46 ms for
// `samples == 2048` and `context.sampleRate == 44100`).
node.onaudioprocess = function (e) {
fns = fns.filter(function (fn) {
return !fn(Date.now() - fn.t);
});
};
source.connect(node);
node.connect(context.destination);
window.do_not_garbage_collect = [context, source, node];
return function (fn) {
fn.t = Date.now();
fns.push(fn);
};
}());
// Use like this:
setTimeout2(function (t) {
console.log(t);
// End after 1 second.
if (t > 1000)
return true;
})
Perhaps have a worker thread also run a
postMessage loop and a fraction of the time (every n iterations), either pause or resume the main thread?

Web Audio API resume from pause

I often read that it's not possible to pause/resume audio files with the Web Audio API.
But now I saw a example where they actually made it possible to pause and resume it. I tried to figure out what how they did it. I thought maybe source.looping = falseis the key, but it wasn't.
For now my audio is always re-playing from the start.
This is my current code
var context = new (window.AudioContext || window.webkitAudioContext)();
function AudioPlayer() {
this.source = context.createBufferSource();
this.analyser = context.createAnalyser();
this.stopped = true;
}
AudioPlayer.prototype.setBuffer = function(buffer) {
this.source.buffer = buffer;
this.source.looping = false;
};
AudioPlayer.prototype.play = function() {
this.source.connect(this.analyser);
this.analyser.connect(context.destination);
this.source.noteOn(0);
this.stopped = false;
};
AudioPlayer.prototype.stop = function() {
this.analyser.disconnect();
this.source.disconnect();
this.stopped = true;
};
Does anybody know what to do, to get it work?
Oskar's answer and ayke's comment are very helpful, but I was missing a code example. So I wrote one: http://jsfiddle.net/v3syS/2/ I hope it helps.
var url = 'http://thelab.thingsinjars.com/web-audio-tutorial/hello.mp3';
var ctx = new webkitAudioContext();
var buffer;
var sourceNode;
var startedAt;
var pausedAt;
var paused;
function load(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function() {
ctx.decodeAudioData(request.response, onBufferLoad, onBufferError);
};
request.send();
};
function play() {
sourceNode = ctx.createBufferSource();
sourceNode.connect(ctx.destination);
sourceNode.buffer = buffer;
paused = false;
if (pausedAt) {
startedAt = Date.now() - pausedAt;
sourceNode.start(0, pausedAt / 1000);
}
else {
startedAt = Date.now();
sourceNode.start(0);
}
};
function stop() {
sourceNode.stop(0);
pausedAt = Date.now() - startedAt;
paused = true;
};
function onBufferLoad(b) {
buffer = b;
play();
};
function onBufferError(e) {
console.log('onBufferError', e);
};
document.getElementById("toggle").onclick = function() {
if (paused) play();
else stop();
};
load(url);
In current browsers (Chrome 43, Firefox 40) there are now 'suspend' and 'resume' methods available for AudioContext:
var audioCtx = new AudioContext();
susresBtn.onclick = function() {
if(audioCtx.state === 'running') {
audioCtx.suspend().then(function() {
susresBtn.textContent = 'Resume context';
});
} else if(audioCtx.state === 'suspended') {
audioCtx.resume().then(function() {
susresBtn.textContent = 'Suspend context';
});
}
}
(modified example code from https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/suspend)
Actually the web-audio API can do the pause and play task for you. It knows the current state of the audio context (running or suspended), so you can do this in this easy way:
susresBtn.onclick = function() {
if(audioCtx.state === 'running') {
audioCtx.suspend()
} else if(audioCtx.state === 'suspended') {
audioCtx.resume()
}
}
I hope this can help.
Without spending any time checking the source of your example, I'd say you'll want to use the noteGrainOn method of the AudioBufferSourceNode (https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#methodsandparams-AudioBufferSourceNode)
Just keep track of how far into the buffer you were when you called noteOff, and then do noteGrainOn from there when resuming on a new AudioBufferSourceNode.
Did that make sense?
EDIT:
See comments below for updated API calls.
EDIT 2, 2019: See MDN for updated API calls; https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode/start
For chrome fix, every time you want to play sound, set it like:
if(audioCtx.state === 'suspended') {
audioCtx.resume().then(function() {
audio.play();
});
}else{
audio.play();
}
The lack of a built-in pause functionality in the WebAudio API seems like a major oversight to me. Possibly, in the future it will be possible to do this using the planned MediaElementSource, which will let you hook up an element (which supports pausing) to Web Audio. For now, most workarounds seem to be based on remembering playback time (such as described in imbrizi's answer). Such a workaround has issues when looping sounds (does the implementation loop gapless or not?), and when you allow dynamically change the playbackRate of sounds (as both affect timing). Another, equally hack-ish and technically incorrect, but much simpler workaround you can use is:
source.playbackRate = paused?0.0000001:1;
Unfortunately, 0 is not a valid value for playbackRate (which would actually pause the sound). However, for many practical purposes, some very low value, like 0.000001, is close enough, and it won't produce any audible output.
UPDATE: This is only valid for Chrome. Firefox (v29) does not yet implement the MediaElementAudioSourceNode.mediaElement property.
Assuming that you already have the AudioContext reference and your media source (e.g. via AudioContext.createMediaElementSource() method call), you can call MediaElement.play() and MediaElement.pause()on your source, e.g.
source.mediaElement.pause();
source.mediaElement.play();
No need for hacks and workarounds, it's supported.
If you are working with an <audio> tag as your source, you should not call pause directly on the audio element in your JavaScript, that will stop playback.
In 2017, using ctx.currentTime works well for keeping track of the point in the song. The code below uses one button (songStartPause) that toggles between a play & pause button. I used global variables for simplicity's sake. The variable musicStartPoint keeps track of what time you're at in the song. The music api keeps track of time in seconds.
Set your initial musicStartPoint at 0 (beginning of the song)
var ctx = new webkitAudioContext();
var buff, src;
var musicLoaded = false;
var musicStartPoint = 0;
var songOnTime, songEndTime;
var songOn = false;
songStartPause.onclick = function() {
if(!songOn) {
if(!musicLoaded) {
loadAndPlay();
musicLoaded = true;
} else {
play();
}
songOn = true;
songStartPause.innerHTML = "||" //a fancy Pause symbol
} else {
songOn = false;
src.stop();
setPausePoint();
songStartPause.innerHTML = ">" //a fancy Play symbol
}
}
Use ctx.currentTime to subtract the time the song ends from when it started, and append this length of time to however far you were in the song initially.
function setPausePoint() {
songEndTime = ctx.currentTime;
musicStartPoint += (songEndTime - songOnTime);
}
Load/play functions.
function loadAndPlay() {
var req = new XMLHttpRequest();
req.open("GET", "//mymusic.com/unity.mp3")
req.responseType = "arraybuffer";
req.onload = function() {
ctx.decodeAudioData(req.response, function(buffer) {
buff = buffer;
play();
})
}
req.send();
}
function createBuffer() {
src = ctx.createBufferSource();
src.buffer = buff;
}
function connectNodes() {
src.connect(ctx.destination);
}
Lastly, the play function tells the song to start at the specified musicStartPoint (and to play it immediately), and also sets the songOnTime variable.
function play(){
createBuffer()
connectNodes();
songOnTime = ctx.currentTime;
src.start(0, musicStartPoint);
}
*Sidenote: I know it might look cleaner to set songOnTime up in the click function, but I figure it makes sense to grab the time code as close as possible to src.start, just like how we grab the pause time as close as possible to src.stop.
I didn't follow the full discussion, but I will soon. I simply headed over HAL demo to understand. For those who now do like me, I would like to tell
1 - how to make this code working now.
2 - a trick to get pause/play, from this code.
1 : replace noteOn(xx) with start(xx) and put any valid url in sound.load(). I think it's all I've done. You will get a few errors in the console that are pretty directive. Follow them. Or not : sometimes you can ignore them, it works now : it's related to the -webkit prefix in some function. New ones are given.
2 : at some point, when it works, you may want to pause the sound.
It will work. But, as everybody knows, a new pressing on play would raise an error. As a result, the code in this.play() after the faulty source_.start(0) is not executed.
I simply enclosed those line in a try/catch :
this.play = function() {
analyser_ = context_.createAnalyser();
// Connect the processing graph: source -> analyser -> destination
source_.connect(analyser_);
analyser_.connect(context_.destination);
try{
source_.start(0);
}
catch(e){
this.playing = true;
(function callback(time) {
processAudio_(time);
reqId_ = window.webkitRequestAnimationFrame(callback);
})();
}
And it works : you can use play/pause.
I would like to mention that this HAL simulation is really incredible. Follow those simple steps, it's worth it !

Categories

Resources