1

The code is running okay, but I'm sure there is a more efficient way to do this. At the moment it is working okay in Firefox, but falls to pieces in Chrome. The boolean values are checked at each step, which are 110ms apart, and the outcome whether the item in the array is true or false determines whether a sound is triggered or not (unique to that array). Something in the code is stacking when Chrome spikes, which is causing all kinds of glitches and leaks... I can't seem to figure out a way to bundle this all into one check on each loop, rather than running all these if statements. Is it possible?

var loop = [{
    "sound": "one",
    "pattern": [true, false, false, false],
    "volume": 100
    },{
    "sound": "two",
    "pattern": [false, true, false, true],
    "volume": 100 
    },{
    "sound": "three",
    "pattern": [true, true, true, true],
    "volume": 100 
    }]

var s = 0;
function startLoop(){   
    if (playing === true){  
        setTimeout(function(){          
            if (typeof loop[0] !== "undefined") {
                if (loop[0].pattern[s] === true){
                    soundset.play(loop[0].sound);
                }
            }
            if (typeof loop[1] !== "undefined") {
                if (loop[1].pattern[s] === true){
                    soundset.play(loop[1].sound);               
                }
            }
            if (typeof loop[2] !== "undefined") {
                if (loop[2].pattern[s] === true){
                    soundset.play(loop[2].sound);               
                }
            }
            s++;
            if (s < loop[0].pattern.length){
                startLoop();
            } else {
                s = 0;
                startLoop();
            }

        }, 110)
    } else {return;}
};

The typeof loop[x] !== "undefined" is in place in case loops above x are not in place. The plan was to have about 10 of these running, but this way of checking each step is struggling at only three.

3
  • 1
    looks like an infinite loop here. what is playing? when is it true? every 10th of a second startLoop() is called which calls itself 4 times, everytime. look into for loop in javascript. Commented Dec 10, 2018 at 18:23
  • 2
    Most likely you have a timer (probably setInterval() or something) that's running the startLoop() function, which also includes a timer. Running multiple timers is very risky as they easily start stacking up and cause memory leaks that way (this is most likely also the reason for your bugs, cause as they stack, multiple are running simultaneously). So be very carefull with that and try to avoid it unless you know exactly what you're doing. Commented Dec 10, 2018 at 18:24
  • It's a drum machine, so the loop is essential. It is true when the "play" button is pressed, and break is called when the stop button is pressed. This "loop" is the only one that needs to be running (it's actually a very simple program). I guess a for loop would be better - it was the original plan but I couldn't think off the top of my head how reset back to 0 when the end of the loop was reached, and start the for loop again without calling a function. Is the if stacking acceptable then? I'll try and implement a for loop instead, and see if it runs any better! Commented Dec 10, 2018 at 18:51

2 Answers 2

0
  • Use forEach() instead of hardcoding loop[0], loop[1], etc…. That way you can have endless instruments in your track.
  • Clear your tick timeout when stopping (or pausing) your playback.
  • Preload your sounds (and populate them into each respective loop object.audio).
  • Instead of true and false you can use 0 and 1 i to simply and cleanly instruct boolean values.
  • Volume goes from 0.0 to 1.0
  • You can use Modulo oparator % to loopback to 0 your s ticker index.

var bpm = 60;             // Beats per minute
var div = 4;              // "4/4 division" (the pattern length)
var s = 0;
var tickTimeout = null;

var loop = [{
  name: "kick",           // it's a sound/file name
  pattern: [1, 0, 0, 0],  // can be expressed using 0 and 1
  volume: 1               // volume goes from 0.0 to 1.0, remember?
},{
  name: "snare",
  pattern: [0, 1, 0, 1],
  volume: 1 
},{
  name: "hihat",
  pattern: [1, 1, 1, 1],
  volume: 1 
}];

// Preload sounds
loop.map(function(obj){
  obj.audio = new Audio("../audio/drumset-rock/"+ obj.name +".mp3");
  obj.audio.volume = obj.volume;
});

// Play tick
function play(){ 
  var names = "";                     // Demo only
  loop.forEach(function(obj) {        
    if (obj.pattern[s]) {
      obj.audio.play();               // Play sound if in pattern
      names += " "+ obj.name          // Demo only
    }
  });
  console.log(s +': '+ names);        // Demo only

  s = ++s % div;                      // Increment and loopback
  tickTimeout = setTimeout(play, 1000 * 60 / bpm);  // Next tick at bpm
}

function stop() {
  clearTimeout(tickTimeout);
}

play();         // Start loop
// stop();      // Use to immediately stop drum machine

Sign up to request clarification or add additional context in comments.

1 Comment

So many pointers in the right direction - many thanks! I did notice that something was going up infinitely in the performance analysis in Firefox.. I guess it was the fact the timeout wasn't being cleared!
0

Try this:

var loop = [
    {
        "sound": "one",
        "pattern": [true, false, false, false],
        "volume": 100
    },{
        "sound": "two",
        "pattern": [false, true, false, true],
        "volume": 100 
    },{
        "sound": "three",
        "pattern": [true, true, true, true],
        "volume": 100 
    }
];

var s = 0;
function startLoop(){   
    if (playing !== true)
        return;
    setTimeout(function(){ 
        for(const i in loop){
            if(loop[i].pattern.hasOwnProperty(s)){
                if(loop[i].pattern[s]){
                    soundset.play(loop[i].sound);
                }
            }else{
                s = 0;
            }
        }
        s++;
        startLoop();
    }, 110);
};

1 Comment

That works so much better! So is it the i in loop that targets all loop objects at the same time? It's still running a bit clunky, but I think that may be due to the fact that my audio sprite is too big (about 200 sounds...). I'll try breaking it up into smaller variables.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.