MIDI sequencer : need directions

Status
Not open for further replies.

emmanuel63

Well-known member
Hello,

I would like to build a MIDI sequencer. I don't know how to implement a solid timebase to trigger midi events.

For instance, let's consider the way I could generate the MIDI clock "clics" using usbMIDI.sendRealTime(usbMIDI.Clock) function. This message is sent 24 times per quarter note. My idea would be to use an "elapsedMillis" variable. Here is the relation between midi tempo and clics period :
clic periode (ms) = 60 / tempo / 24 * 1000
if the tempo is set to 80, I have to send a clic every 31ms. The code would be something like :

Code:
elapsedMillis ticks
byte tempo = 80;

void sendTicks() {
  if (ticks > 60 / tempo / 24 * 1000) {
    usbMIDI.sendRealTime(usbMIDI.Clock);
    ticks = 0;
  }
}

Is it the right way ?
Can I use other elapsedMillis variable to trigger other midi events ?

Thank you
Emmanuel
 
Thank you.
These examples are clear. I think I know how to start.

I have another question. What could be the way to implement shuffle function ? Shuffle adds some "swing" in the tempo. I wonder how I can achieve that.
 
a number of ways, and swing means different things, some are every even beat, and some sequencers might just add to 3rd and 7th note.

For an example (just one way of many) see here for swing on every even beat:
https://github.com/mortonkopf/Teensy_simple_drum/blob/master/Teensy_SimpleDrum_30dac.ino

in it, the rotary is mapped to a value that is converted to an amount of millis that is added to the gap between beats. beats are counter so that you know if you have an even beat (but using modulo). the beat trigger elapsed function adds the swing amount before triggering the beat / sound / etc

eg:
Code:
if ((currentClTime > (oldClTime + (clockGap))) &&
      ((!(clockStep % 2) == 0)))
  { ...}
 
I'm new to the forum so I'm not sure if I want to post any links but attack magazine did a good article on drum machine swing with Roger Linn. You can google it yourself, but here is a snippet from the article:

"Swing – applied to quantized 16th-note beats – is a big part of it. My implementation of swing has always been very simple: I merely delay the second 16th note within each 8th note. In other words, I delay all the even-numbered 16th notes within the beat (2, 4, 6, 8, etc.) In my products I describe the swing amount in terms of the ratio of time duration between the first and second 16th notes within each 8th note. For example, 50% is no swing, meaning that both 16th notes within each 8th note are given equal timing. And 66% means perfect triplet swing, meaning that the first 16th note of each pair gets 2/3 of the time, and the second 16th note gets 1/3, so the second 16th note falls on a perfect 8th note triplet."
 
I would like to ask everyones opinion on what is better for timing a sequencer, the system clock using micros() or using the update function of an AudioStream object? The examples I can find in the forums is based on using system clock. In previous sequencers I've worked on I would use the audio callback function (which is like the update function) to track time based on actual samples. That was more accurate than using system clock but that was on other systems like iOS. Has anyone here done it that way or have any feedback on which way might be better on the Teensy?
 
I would like to ask everyones opinion on what is better for timing a sequencer, the system clock using micros() or using the update function of an AudioStream object? The examples I can find in the forums is based on using system clock. In previous sequencers I've worked on I would use the audio callback function (which is like the update function) to track time based on actual samples. That was more accurate than using system clock but that was on other systems like iOS. Has anyone here done it that way or have any feedback on which way might be better on the Teensy?

For what it's worth, I've always used millis() or micros() for timing-related tasks. The very first thing I do in my loop() is grab the millis into an unsigned long variable. If I'm ever wondering "is it time to do something?", I compare the "when to do something" to my loop start. When it's time to do something I do it and then reset my "next_time_to_do_something" variable to the loop start + x ms. I always be sure to use the loop start, not another reading of millis(). That way I don't get any delay because of the time spent doing whatever it was I was doing.
 
Status
Not open for further replies.
Back
Top