2

I am trying to build a loop sequencer that utilises the web audio API and many different musical loops of different lengths. The general idea is that the loops are chosen at random (to a degree), then played back in in a specific order for a beginning, middle and end section. There are four channels - bass, drums, lead, and FX. Just before the end of the last section a new set of samples are chosen and played back using the same formula.

I then intend to add a visualiser and maybe some automation - i.e maybe reverb for the final few bars.

I have to tried to follow the 'Tale of Two Clocks' method but as I am relatively new to programming - and to javascript - I am having a hard time implementing it. I think it might make things more complicated than necessary to complete this.

My initial idea was to create a for loop with the play method inside that specified the amount of times I want a loop to play, but I have found that the samples will instead all play at once. As per the 'Tale of Two Clocks', I am avoiding using setTimeout() and setInterval() as much as possible as it will only lead to lag.

My second idea was to call all the play methods at once, except each is cued at a certain point in the 'song', for example

START SECTION

playlead(time, buffer);

playdrums(time + length*2, buffer);

MIDDLE SECTION

playlead(time + length*10, buffer);

playdrums(time + length*10, buffer);

playlead2(time + length*10, buffer);

playbass(time + length*10, buffer);

and so on. This has caused its own set of problems however and seems like a fairly inefficient way to go about it.

It feels like a may be missing an obvious solution but after quite a bit of research, I have still not been able to solve it. It seems like the easiest thing to do would be to make the playSound function in my for loop wait for the sample to finish before iterating again but this is a lot harder to do than it seems. Please if anyone could offer up any advice I would be very grateful as I am at wits end with this. Thank you in advance!

//Just to add, the issue I am having in my second method is that in order to play even just the start section realistically the code will have to look something like this to play for around 30 seconds:

///START SECTION ( for 30 seconds or so)

playlead(time, buffer);
playlead(time + length, buffer);
playlead(time +  length*2, buffer);
playlead(time, + length*3, buffer);

playbass(time, buffer);
playbass(time + length, buffer);
playbass(time +  length*2, buffer);
playbass(time, + length*3, buffer);

playlead1(time, buffer);
playlead1(time + length, buffer);
playlead1(time +  length*2, buffer);
playlead1(time, + length*3, buffer);

playdrums(time, buffer);
playdrums(time + length, buffer);
playdrums(time +  length*2, buffer);
playdrums(time, + length*3, buffer);

this is 16 calls to the play() method at once. For the full song to play there will more than likely be 4 sections before being rebuffered and asked to play again. That is at least 16*4 (64) calls at the same time straight after loading. I am guessing that with a spectograph, automated audioParam's and volume/effects sliders included this might cause some issues?Thanks.

//// Here is an example of the code which most of my problems are stemming from:

function LeadStartSection
(buffer, time, gainNode, bufferList) 
{

console.log("playing lead Start section");   
    var timer = setTimeout(function() {    //checks when audiobuffer has finished
    console.log('sample times out at' + timer);
    }, buffer.duration * 1000);  

    var Length = buffer.duration;           
    console.log(Length);
    var n = 1;

////Example of second method/////
 playLead(buffer, time, gainNode,n, bufferList);
 playLead(buffer, time + Length, gainNode, Length,n, bufferList);
 playLead(buffer, time + Length*2, gainNode, Length,n, bufferList);
 playLead(buffer, time + Length*3, gainNode, Length, n+2, bufferList);
}

///example of what I would like to do///

for (i=o; i<leadPlayCount; i++)
{
PlayLead(buffer, gainNode, Length, bufferList);
}

*in first example n is passed to the function because the middle section will be cued once n = n+2.

*in the second example I am having trouble calling playLead so that each loop starts just after the one before it ends. Instead, the method is called 4 times at once. it does not sound too good!I was thinking that maybe there was a way that the for loop would not continue until it recieved a return for the playLead method but it seems i am wrong.

I hope this might give you a bit more of an understanding of where I am at. Please ask if there is anything else you would like me to include. thanks a million for any help!

  • I think we need more information, like a small sample of actual code that isn't working. From what I can tell, your second idea should work. "its own set of problems" is pretty vague. – aldel Mar 06 '15 at 18:57
  • added a little bit extra there @aldel, hopefully this might help!let me know if there is anything else that might be important. thanks. – Brian Diolún Mar 09 '15 at 15:47
  • It's still hard to tell. It sounds like maybe you think calling sourcenode.start() inside a loop, with the same arguments each time, should result in the sound being played several times in succession, rather than all at once. But since you don't show any calls to sourcenode.start(), I can't be sure. Instead you show calls to functions like playLead, first with two arguments, then six, then four, and I can't tell what they're all supposed to mean. – aldel Mar 09 '15 at 17:36
  • Also, your code is hard to read because it isn't indented properly. – aldel Mar 09 '15 at 17:36
  • As far as making lots of calls at once, I'm not aware of any reason why that should be a problem. Maybe there is a limit, but it should be quite high if so. – aldel Mar 09 '15 at 17:38
  • Thank you for your help and speedy response, and my apologies if my code is hard to read. The playLead method simply takes in a buffer, attaches it to an audiobuffer node, and calls source.start(). Ignoring all the code above the 'what i would like to do' section, If i use a 'for' loop similar to the one above to call 'playLead' it will play the buffer 4 times at once, rather than in succession as I would like it to do. Is there any way you would recommend achieving this? If not than I may try the second method I mentioned earlier. thanks again! I might try using that second method so. – Brian Diolún Mar 09 '15 at 18:17

1 Answers1

1

Ok, I think you might just be misunderstanding how loops work, or how the Audio API works, or how JavaScript works. If you have a function like this:

function playBuffer(time, buffer) {
  var node = context.createBufferSource();
  node.buffer = buffer;
  node.connect(context.destination);
  node.start(time);
}

then, to play the same buffer several times in a row, you could do this:

playBuffer(context.currentTime, buffer);
playBuffer(context.currentTime + buffer.duration, buffer);
playBuffer(context.currentTime + 2 * buffer.duration, buffer);
playBuffer(context.currentTime + 3 * buffer.duration, buffer);

or:

for (var i = 0; i < 4; i++) {
  playBuffer(context.currentTime + i * buffer.duration, buffer);
}

The two are equivalent.

So basically you need to give a start time to buffer.start that is in the future, i.e., greater than context.currentTime, or it will start immediately. Whether it's called from a loop or not, all the calls to buffer.start happen "now", but when the sound actually starts playing is determined by the first argument to buffer.start().

aldel
  • 6,489
  • 1
  • 27
  • 32
  • Sorry for the late reply, I have been away. Thank you for the comprehensive answer, this has actually helped me loads. I now have everything scheduled as you recommended. Cheers :) – Brian Diolún Mar 17 '15 at 09:17