Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 7 of 7

Thread: MIDI sequencer : need directions

  1. #1
    Senior Member
    Join Date
    Dec 2018
    Posts
    170

    MIDI sequencer : need directions

    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

  2. #2
    Senior Member mortonkopf's Avatar
    Join Date
    Apr 2013
    Location
    London, uk
    Posts
    954
    You have stumbled upon the question that invariably comes with Midi sequencer building. See here for examples:

    https://forum.pjrc.com/threads/66500...ght=midi+clock

    and
    https://forum.pjrc.com/threads/53467...ght=midi+clock


    NB, using elapsed micros rather than millis is preferable, and avoiding interval timers is also wise if you are not happy with handling their potential side effects.

  3. #3
    Senior Member
    Join Date
    Dec 2018
    Posts
    170
    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.

  4. #4
    Senior Member mortonkopf's Avatar
    Join Date
    Apr 2013
    Location
    London, uk
    Posts
    954
    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...Drum_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)))
      { ...}

  5. #5
    Junior Member
    Join Date
    Oct 2021
    Posts
    8
    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."

  6. #6
    Junior Member
    Join Date
    Oct 2021
    Posts
    8
    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?

  7. #7
    Junior Member
    Join Date
    Aug 2019
    Posts
    11
    Quote Originally Posted by rcon View Post
    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.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •