4

I am trying to understand Web Audio API timing and scheduling methods.

But i still have not understood the Oscillator Node's stop() method completely.

Here i'm trying to schedule to play 4 oscillator with tempo of 120 BPM.

But it seems like as soon as the stop() method kicks in on the release time, it stops all the oscillators.

Here is the code:

var context = new webkitAudioContext();
var now = context.currentTime;
var tempo = 120;
var releaseTime = 0.5;
var secondsPerBeat = 60.0 / tempo;

for(var i = 0; i < 4; i++){
    var now = context.currentTime;
    var osc = context.createOscillator();
    osc.connect(context.destination);
    osc.start(now + (i*secondsPerBeat));
    var now = context.currentTime;
    osc.stop(now + releaseTime);
}

Why is that happening and how i can prevent this?

Thanks

jfunk
  • 7,176
  • 4
  • 37
  • 38
zya
  • 830
  • 11
  • 25
  • Are you sure they are stopping at the exact same moment? I think your problem is that a loop is really quick, so you, as a human, see them stop at the exact same moment, whereas they are not. – MisterJ Jul 12 '13 at 08:49
  • i don't understand the issue : you ask them to stop at now+releaseTime...and so they do. ? – GameAlchemist Jul 12 '13 at 08:50
  • @VincentPiel was assuming that when the for loop is called, it must have created four different oscillator nodes, but its clear that is one. The confusing thing is the similarity of the buffer source and the oscillator node, because on BufferSourceNode when you do the exact same thing it creates new buffers and plays and stops without interfering with the other scheduled notes. – zya Jul 12 '13 at 08:53
  • @MisterJ basically what i want to do is, i want the oscillator node to play 4 times in a bar with the release time of 0.5 seconds. – zya Jul 12 '13 at 08:55
  • Yes I do understand what you want, but as @VincentPiel and myself stated, the problem there is that your loop will execute in much more less than a seconde, then your oscillators will stop, from a human sight, at the same moment, whereas they are in fact stopping with a too little delay for us to see it. If you want to figure out if what I told you is the truth, just try changing the releaseTime for a test, in your loop before `osc.stop(now + releaseTime);` add `releaseTime+=i;`. – MisterJ Jul 12 '13 at 08:59
  • just try to change your stop for : osc.stop(now + releaseTime+ i*secondsPerBeat); i think this is what you mean : they should release 'releaseTime' after they started. – GameAlchemist Jul 12 '13 at 09:00
  • @VincentPiel thanks a lot. i really appreciate your help. Just another quick question: so do you confirm that this loop creates 4 different oscillator nodes and deletes them whenever the stop function is called? so i dont need to worry about CPU usage if i want to repeat this forever? – zya Jul 12 '13 at 09:19
  • http://stackoverflow.com/questions/864516/what-is-javascript-garbage-collection – MisterJ Jul 12 '13 at 09:29
  • i edited my answer for possible issue with garbage. For cpu, only playing node can eat up cpu. – GameAlchemist Jul 12 '13 at 09:31

2 Answers2

2

First thing, about Javascript : there is no block scope in Js, so maybe it will be clearer to put all vars definitions at the start of current execution context.
Secondly, you do start your sound with a delay, but stop them at the very same time, which is not what you seek.
Thirdly, the currentTime will be almost the same within a for loop : you cannot rely on the for loop to induce a delay.

var context = new webkitAudioContext();
var tempo = 120;
var releaseTime = 0.5;
var secondsPerBeat = 60.0 / tempo;

var now = context.currentTime;
var i = 0, startTime=0, osc = null;

for(i = 0; i < 4; i++) {
    startTime = now + (i*secondsPerBeat) ;
    osc = context.createOscillator();
    osc.connect(context.destination);
    osc.start();
    osc.stop(startTime + releaseTime);
} 

Soon enough you'll want to write a function to create your oscillator to further clear up the code.

Edit : about the lifetime of your objects, best is to look at the specs :
https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
look at the 4.2.3. Lifetime part.

to sum it up : if playing or connected to a referenced context, they will survive, and die otherwise.

You can either :
- keep the same context, and store the 4 oscillator within an array to just start/stop them after when needed.
- or recreate a new context + new oscillators each time.

(but you cannot keep creating new oscillators on same context or they pile up since they are still connected, and use too much memory).

GameAlchemist
  • 18,995
  • 7
  • 36
  • 59
  • You can't call oscillator start more than once. The best way to do this, I find, is to hook the oscillator up to a gain node, and then toggle that gain node from 0 (no note) to some non-zero value to play it. – PinkElephantsOnParade Mar 09 '14 at 18:58
  • i think i'm not suggesting to start them more than once. In the (short) code presented, i create and launch 4 of them. Otherwise you would confirm that 1) audio resources gets recollected even if they are still reachable from the global object in javascript (i.e. they cannot be js-recollected / they are in use) and that 2) connecting the osc. to a connected gain node will keep it alive ? – GameAlchemist Mar 09 '14 at 20:01
1

I was having trouble with something similar to this as well. What I found is that you need to both stop() AND ALSO disconnect() every oscillator (or other buffer producing node type) instance or the instances will linger and will interfere with the playback of any new instances.

jfunk
  • 7,176
  • 4
  • 37
  • 38