2

I am currently working on a drum machine and I am using setTimeout to make it run. Here is the heart of the code:

      var sequencerRun = function(){    
      var currentTime = 0 
      var starting = 200;

      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;
      })

The drum machine has a matrix HTML structure built using a table. When .play is clicked a scheduling process occurs, which is encapsulated in sequencerRun. This involves a run through the columns of my matrix to determine whether there should be a drum hit or not. This is done through blinker. The scheduling creates a check on each column 1 - 16 at times 0,200,...,3200 respectively. This is what creates the effect of a sequencer. I also have a setInterval that reruns this process every 3200, which is how it takes for a run to finish.

Programmatically my code seems to make sense and my hope was that it would execute on time. The thing is that my actual app tends to stutter a lot and is stuttering even more since I deployed it. Here is a deployed version of my app.

This stuttering side effect can be best heard when you click on a full row. My question here is can anyone tell if this side effect is a result of setTimeout's timing inconsistency and if so how could I go about fixing this? Or is this related to something else that I am missing?

Matthieu Brucher
  • 21,634
  • 7
  • 38
  • 62
theamateurdataanalyst
  • 2,794
  • 4
  • 38
  • 72

1 Answers1

1

I think the stuttering issue has more to do with you not preloading the instruments but rather loading them on every hit, more than it has to do with settimeout.

In any case, I think I would have solved this differently. Rather than setting a fresh timeout for each beat, create one beat timeout and put the logic in there. Something like (pseudo-code-ish, lots of stuff missing just the general idea):

var MAX_BEATS = 16; // I think you had 16 beats in your example?
var BPM = 200; 

var preloadedInstruments = [];
function preloadInstruments(){
  for(i of myInstruments) { // myInstruments would be a list of your instruments (probably just strings with a filename)
     preloadedInstruments.push(new instrument(i)); // You need an instrument class, that would preload the sound and provide a play function
  }
}
var currentbeat = 1;
function beat() {
  var activeInstruments = getActiveInstruments(currentbeat); // You could cache this also for every beat, but I think a simple loop here would be quick enough
  for(instrument of activeInstruments) {
     preloadedInstruments[instrument].play(); // play method of the instrument class called
  }
  currentbeat++;
  if (currentbeat > MAX_BEATS) { 
    currentbeat = 1;
  }
}

setInterval(beat, 60e3 / BPM);
Jan
  • 5,688
  • 3
  • 27
  • 44
  • Dude that makes sense. I am totally loading on each hit. What is `getActiveInstruments`? – theamateurdataanalyst Jun 21 '15 at 20:52
  • Well, it depends on your setup, that's why I didn't include it. The code I provided is pseudo-code-ish in the way that it shows a framework or approach rather than giving the exact code to use. What `getActiveInstruments` SHOULD do is find all the instruments that should play on the currently active beat. In your current case, loop through the `td`s of the active beat and check for a certain "ticked" class or somesuch I think. Rather than thinking of it in lines of instrument, think of it as beats and it'll become clearer. – Jan Jun 21 '15 at 20:58
  • By the way, you could still use a preloaded instrument class with your current method, I just think you'll run into more problems than if you create a specific beat timeout. You might still have stuttering (setinterval doesn't hog processes so it's not exact), but at least all the instruments will be stuttering in line. – Jan Jun 21 '15 at 21:01
  • Also, the preferred method to do this would be to run a timeout with a really short timespan and compare it to a timestamp. Because settimeout and interval will never be completely accurate. That way, even if one beat might stutter a bit you can still try to make sure you get at least the correct BPM at the end of the day. Javascript just isn't accurate in this regard. See http://stackoverflow.com/questions/196027/is-there-a-more-accurate-way-to-create-a-javascript-timer-than-settimeout – Jan Jun 21 '15 at 21:03