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

Thread: MIDI Sequencer

  1. #1

    MIDI Sequencer

    Hi all,

    Been looking around for some information relating to writing some code for a MIDI sequencer, and have come across some pieces however nothing in particular in regards to how it would work and how to set up, or even write the logic required.

    Does anyone have any ideas or could point me in the right direction of where to start? Anyone developed something similar before?

    Cheers
    M

  2. #2
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,351
    The MIDI part is easy... the sequencing part is potentially very complex.

    First step would be to decide how you interface should work.... knobs, sliders, button matrix?

    This library looks interesting and might make your project a little more easily achievable:

    https://github.com/adafruit/FifteenStep

    It appears to support shuffle, arbitrary loop lengths, dynamic tempo, and MIDI clock.

  3. #3
    Thanks for that Oddson. I have looked at that library which seems rather detailed, however am unable to even compile the 'Neo_mpr121' example due to the following not being declared:

    MIDIEvent event = {command, combined, arg1, arg2};
    MIDIUSB.write(event);
    MIDIUSB.flush();

  4. #4
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,351
    I'm not great with library dependencies so I'm not sure how the midi part is meant to work in standard Ardunio but this is the part you need to change to Teensydunio's usbMIDI commands anyway.

    I think the lack of a generic and public usbMIDI.send() command may complicate this... some conditional logic could translate to note-on and note-off commands or there may be a way to use the private and generic internal 'send' function from the usbMIDI library instead.

    I'm away from my Teensy setup over the holidays so I can't really help with that right now.

    An easier solution is to call note-on and -off directly instead of having an internal 'midi' function.

    You would just replace the calling commands

    Code:
    midi(channel, 0x9, pitch[i], vel[i]);  // with usbMIDI.usbMIDI.sendNoteOn(pitch[i], vel[i], channel);
    midi(channel, 0x8, pitch[i], vel[i]); //  with usbMIDI.usbMIDI.sendNoteOff(pitch[i], vel[i], channel);
    And maybe start with the basic example... are you intending to mix with neopixels?

  5. #5
    Junior Member
    Join Date
    Jul 2020
    Posts
    4
    Hello!
    First post here...thanks for a wonderful platform! Really, too much fun.
    I am also trying to bring a sequencer into a simple synth sketch written with Audio Tools.
    But first, I'm looking at the Fifteenstep library, basic example. I should probably get this working before bringing Fifteenstep into my synth.

    I have the MIDI Library and hardware set up example working from here: https://www.pjrc.com/teensy/td_libs_MIDI.html
    (what satisfaction to see those midi notes firing off)
    So i know my serial Midi output is working and wired correctly , on Serial1.

    Here's the basic example as-is:

    Code:
    // ---------------------------------------------------------------------------
    //
    // basic.ino
    //
    // A MIDI sequencer example using a standard MIDI cable and a push button
    // attached to pin 4.
    //
    // Author: Todd Treece <todd@uniontownlabs.org>
    // Copyright: (c) 2015 Adafruit Industries
    // License: GNU GPLv3
    //
    // ---------------------------------------------------------------------------
    #include "FifteenStep.h"
    
    // sequencer init
    FifteenStep seq = FifteenStep();
    
    // save button state
    int button_last = 0;
    
    void setup() {
    
      // set MIDI baud
      Serial.begin(31250);
    
      // initialize digital pin 13 as an output
      pinMode(13, OUTPUT);
    
      // initialize digital pin 4 as an input for a button
      pinMode(4, INPUT);
    
      // start sequencer and set callbacks
      seq.begin();
      seq.setMidiHandler(midi);
      seq.setStepHandler(step);
    
    }
    
    void loop() {
    
      // read the state of the button
      int button = digitalRead(4);
    
      // check for button press or release and
      // send note on or off to seqencer if needed
      if(button == HIGH && button_last == LOW) {
    
        // button pressed. play middle C preview now
        midi(0x0, 0x9, 0x3C, 0x40);
        // store note in sequence
        seq.setNote(0x0, 0x3C, 0x40);
    
      } else if(button == LOW && button_last == HIGH) {
    
        // button released. send middle C note off preview now
        midi(0x0, 0x8, 0x3C, 0x0);
        // store note off in sequence
        seq.setNote(0x0, 0x3C, 0x0);
    
      }
    
      // save button state
      button_last = button;
    
      // this is needed to keep the sequencer
      // running. there are other methods for
      // start, stop, and pausing the steps
      seq.run();
    
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    //                                                                           //
    //                         SEQUENCER CALLBACKS                               //
    //                                                                           //
    ///////////////////////////////////////////////////////////////////////////////
    
    // called when the step position changes. both the current
    // position and last are passed to the callback
    void step(int current, int last) {
    
      // blink on even steps
      if(current % 2 == 0)
        digitalWrite(13, HIGH);
      else
        digitalWrite(13, LOW);
    
    }
    
    // the callback that will be called by the sequencer when it needs
    // to send midi commands. this specific callback is designed to be
    // used with a standard midi cable.
    //
    // the following image will show you how your MIDI cable should
    // be wired to the Arduino:
    // http://arduino.cc/en/uploads/Tutorial/MIDI_bb.png
    void midi(byte channel, byte command, byte arg1, byte arg2) {
    
      if(command < 128) {
        // shift over command
        command <<= 4;
        // add channel to the command
        command |= channel;
      }
    
      // send MIDI data
      Serial.write(command);
      Serial.write(arg1);
      Serial.write(arg2);
    
    }

    Here's what I've done to bring the MIDI hardware example in, adding those bits in. I also reversed the button states, as we're not using Bounce:


    Code:
    // ---------------------------------------------------------------------------
    //
    // basic.ino
    //
    // A MIDI sequencer example using a standard MIDI cable and a push button
    // attached to pin 4- *changed to 5
    //
    // Author: Todd Treece <todd@uniontownlabs.org>
    // Copyright: (c) 2015 Adafruit Industries
    // License: GNU GPLv3
    //
    // ---------------------------------------------------------------------------
    #include "FifteenStep.h"
    #include <MIDI.h>
    
    MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI1);
    //const int channel = 1;
    
    
    // sequencer init
    FifteenStep seq = FifteenStep();
    
    // save button state
    int button_last = 0;
    
    void setup() {
    
      // set MIDI baud
      //Serial1.begin(31250);
      
    MIDI1.begin();
    
      // initialize digital pin 2 as an LED output
      pinMode(2, OUTPUT);
    
      // initialize digital pin 5 as an input for a button
      pinMode(5, INPUT);
    
      // start sequencer and set callbacks
      seq.begin();
      seq.setMidiHandler(midi);
      seq.setStepHandler(step);
    
    }
    
    void loop() {
    
      // read the state of the button
      int button = digitalRead(5);
    
      // check for button press or release and
      // send note on or off to seqencer if needed
      if(button == LOW && button_last == HIGH) {
    
        // button pressed. play middle C preview now
        midipipe(0x0, 0x9, 0x3C, 0x40);
        //(channel, noteon, pitch, velocity)??
        
        // store note in sequence
        seq.setNote(0x0, 0x3C, 0x40);
    
      } else if(button == HIGH && button_last == LOW) {
    
        // button released. send middle C note off preview now
        midipipe(0x0, 0x8, 0x3C, 0x0);
        // store note off in sequence
        seq.setNote(0x0, 0x3C, 0x0);
    
      }
    
      // save button state
      button_last = button;
    
      // this is needed to keep the sequencer
      // running. there are other methods for
      // start, stop, and pausing the steps
      seq.run();
    
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    //                                                                           //
    //                         SEQUENCER CALLBACKS                               //
    //                                                                           //
    ///////////////////////////////////////////////////////////////////////////////
    
    // called when the step position changes. both the current
    // position and last are passed to the callback
    void step(int current, int last) {
    
      // blink on even steps
      if(current % 2 == 0)
        digitalWrite(2, HIGH);
      else
        digitalWrite(2, LOW);
    
    }
    
    // the callback that will be called by the sequencer when it needs
    // to send midi commands. this specific callback is designed to be
    // used with a standard midi cable.
    //
    // the following image will show you how your MIDI cable should
    // be wired to the Arduino:
    // http://arduino.cc/en/uploads/Tutorial/MIDI_bb.png
    
    
    // MIDIcallback *comments from Fifteenstep.h*
    //
    // This defines the MIDI callback function format that is required by the
    // sequencer.
    //
    // Most of the time these arguments will represent the following:
    //
    // channel: midi channel
    // command: note on or off (0x9 or 0x8)
    // arg1: pitch value
    // arg1: velocity value
    //
    // It's possible that there will be other types of MIDI messages sent
    // to this callback in the future, so please check the command sent if
    // you are doing something other than passing on the MIDI messages to
    // a MIDI library.
    //
    
    void midi(byte channel, byte command, byte arg1, byte arg2) {
    
      if(command < 128) {
        // shift over command
        command <<= 4;
        // add channel to the command
        command |= channel;
      }
    
    
      // send MIDI data
      Serial1.write(command);
      Serial1.write(arg1);
      Serial1.write(arg2);
    
    }


    The first issue is it won't compile, seems to be a conflict between the midi callback used in the sketch and MIDI.h ?

    Code:
    Arduino: 1.8.13 (Mac OS X), TD: 1.53, Board: "Teensy 4.1, Serial + MIDI, 600 MHz, Faster, US English"
    
    
    15BASIC_AGAIN:126: error: 'void midi(byte, byte, byte, byte)' redeclared as different kind of symbol
     void midi(byte channel, byte command, byte arg1, byte arg2) {
                                                               ^
    In file included from /Applications/Teensyduino.app/Contents/Java/hardware/teensy/avr/libraries/MIDI/src/midi_Defs.h:30:0,
                     from /Applications/Teensyduino.app/Contents/Java/hardware/teensy/avr/libraries/MIDI/src/MIDI.h:30,
                     from /Users/IIIIIo/Documents/TEENSY/15BASIC_AGAIN/15BASIC_AGAIN.ino:14:
    /Applications/Teensyduino.app/Contents/Java/hardware/teensy/avr/libraries/MIDI/src/midi_Namespace.h:31:66: note: previous declaration 'namespace midi { }'
     #define BEGIN_MIDI_NAMESPACE            namespace MIDI_NAMESPACE {
                                                                      ^
    /Applications/Teensyduino.app/Contents/Java/hardware/teensy/avr/libraries/MIDI/src/midi_Namespace.h:36:1: note: in expansion of macro 'BEGIN_MIDI_NAMESPACE'
     BEGIN_MIDI_NAMESPACE
     ^
    15BASIC_AGAIN: In function 'void setup()':
    15BASIC_AGAIN:41: error: expected primary-expression before ')' token
       seq.setMidiHandler(midi);
                              ^
    15BASIC_AGAIN: In function 'void loop()':
    15BASIC_AGAIN:56: error: expected primary-expression before '(' token
         midi(0x0, 0x9, 0x3C, 0x40);
             ^
    15BASIC_AGAIN:65: error: expected primary-expression before '(' token
         midi(0x0, 0x8, 0x3C, 0x0);
             ^
    15BASIC_AGAIN: In function 'void midi(byte, byte, byte, byte)':
    15BASIC_AGAIN:126: error: 'void midi(byte, byte, byte, byte)' redeclared as different kind of symbol
     void midi(byte channel, byte command, byte arg1, byte arg2) {
                                                               ^
    In file included from /Applications/Teensyduino.app/Contents/Java/hardware/teensy/avr/libraries/MIDI/src/midi_Defs.h:30:0,
                     from /Applications/Teensyduino.app/Contents/Java/hardware/teensy/avr/libraries/MIDI/src/MIDI.h:30,
                     from /Users/IIIIIo/Documents/TEENSY/15BASIC_AGAIN/15BASIC_AGAIN.ino:14:
    /Applications/Teensyduino.app/Contents/Java/hardware/teensy/avr/libraries/MIDI/src/midi_Namespace.h:31:66: note: previous declaration 'namespace midi { }'
     #define BEGIN_MIDI_NAMESPACE            namespace MIDI_NAMESPACE {
                                                                      ^
    /Applications/Teensyduino.app/Contents/Java/hardware/teensy/avr/libraries/MIDI/src/midi_Namespace.h:36:1: note: in expansion of macro 'BEGIN_MIDI_NAMESPACE'
     BEGIN_MIDI_NAMESPACE
     ^
    'void midi(byte, byte, byte, byte)' redeclared as different kind of symbol
    
    
    This report would have more information with
    "Show verbose output during compilation"
    option enabled in File -> Preferences.



    So, wild guess, let's rename 'midi' to something else. I used 'midipipe' :

    Code:
    // ---------------------------------------------------------------------------
    //
    // basic.ino
    //
    // A MIDI sequencer example using a standard MIDI cable and a push button
    // attached to pin 4.
    //
    // Author: Todd Treece <todd@uniontownlabs.org>
    // Copyright: (c) 2015 Adafruit Industries
    // License: GNU GPLv3
    //
    // ---------------------------------------------------------------------------
    #include "FifteenStep.h"
    #include <MIDI.h>
    
    MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI1);
    //const int channel = 1;
    
    
    // sequencer init
    FifteenStep seq = FifteenStep();
    
    // save button state
    int button_last = 0;
    
    void setup() {
    
      // set MIDI baud
      //Serial1.begin(31250);
      
    MIDI1.begin();
    
      // initialize digital pin 2 as an LED output
      pinMode(2, OUTPUT);
    
      // initialize digital pin 5 as an input for a button
      pinMode(5, INPUT);
    
      // start sequencer and set callbacks
      seq.begin();
      seq.setMidiHandler(midipipe);   //changed midi to midipipe
      seq.setStepHandler(step);
    
    }
    
    void loop() {
    
      // read the state of the button
      int button = digitalRead(5);
    
      // check for button press or release and
      // send note on or off to seqencer if needed
      if(button == LOW && button_last == HIGH) {
    
        // button pressed. play middle C preview now
        midipipe(0x0, 0x9, 0x3C, 0x40);
        //(channel, noteon, pitch, velocity)??
        
        // store note in sequence
        seq.setNote(0x0, 0x3C, 0x40);
    
      } else if(button == HIGH && button_last == LOW) {
    
        // button released. send middle C note off preview now
        midipipe(0x0, 0x8, 0x3C, 0x0);
        // store note off in sequence
        seq.setNote(0x0, 0x3C, 0x0);
    
      }
    
      // save button state
      button_last = button;
    
      // this is needed to keep the sequencer
      // running. there are other methods for
      // start, stop, and pausing the steps
      seq.run();
    
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    //                                                                           //
    //                         SEQUENCER CALLBACKS                               //
    //                                                                           //
    ///////////////////////////////////////////////////////////////////////////////
    
    // called when the step position changes. both the current
    // position and last are passed to the callback
    void step(int current, int last) {
    
      // blink on even steps
      if(current % 2 == 0)
        digitalWrite(2, HIGH);
      else
        digitalWrite(2, LOW);
    
    }
    
    // the callback that will be called by the sequencer when it needs
    // to send midi commands. this specific callback is designed to be
    // used with a standard midi cable.
    //
    // the following image will show you how your MIDI cable should
    // be wired to the Arduino:
    // http://arduino.cc/en/uploads/Tutorial/MIDI_bb.png
    
    
    // MIDIcallback *comments copied from Fifteenstep.h*
    //
    // This defines the MIDI callback function format that is required by the
    // sequencer.
    //
    // Most of the time these arguments will represent the following:
    //
    // channel: midi channel
    // command: note on or off (0x9 or 0x8)
    // arg1: pitch value
    // arg1: velocity value
    //
    // It's possible that there will be other types of MIDI messages sent
    // to this callback in the future, so please check the command sent if
    // you are doing something other than passing on the MIDI messages to
    // a MIDI library.
    //
    
    void midipipe(byte channel, byte command, byte arg1, byte arg2) {
    
      if(command < 128) {
        // shift over command
        command <<= 4;
        // add channel to the command
        command |= channel;
      }
    
    
      // send MIDI data
      Serial1.write(command);
      Serial1.write(arg1);
      Serial1.write(arg2);
    
    }

    Successfully compiles, a bit of progress! But the sketch isn't working as expected.
    The LED is blinking, seems like a sequence is running somewhere, neat.
    My midi monitor on my laptop is showing I'm getting clock (at 125bpm), and an endless string of note off messages for a C-2, at velocity 0.
    Pressing my button doesn't send anything over Serial1.


    Two theories:
    My new friend midipipe is collecting bytes (note on or off, note value, velocity, and channel) meant to forward to the MIDI library I'm using to transmit actual MIDI data to Serial1. Maybe there's something amiss in the order expected and how its being printed to Serial1?

    By changing 'midi' to 'midipipe', I've broken something inside Fifteenstep that's looking for 'midi', so nothing is getting out of the sequencer besides clock.

    Can anyone help sort this out? I'm not opposed to using usbMIDI to see this example working, as long as I understand *why* it works

    All the Best!

  6. #6
    Senior Member
    Join Date
    Aug 2019
    Location
    Melbourne Australia
    Posts
    125
    Thanks for bumping this old thread, not previously seen Fifteensteps. Looks interesting, neopixels? neotrellis? What?

    Got me curious. Have not dug into what gives with the hardware yet but after a quick look at at Fifteensteps basic, got it to compile for T2++, Mega and T3.2, then threw in the Midi lib and of course it fell over like you say.

    I think one would need to weed through Fifteensteps and re-name stuff so it does not conflict with the usual Teensy Midi library and aim to get it working as it ordinarily would with the Teensy Midi library included.

    Then, in basic.ino looking at the last few lines:-

    Code:
    void midi(byte channel, byte command, byte arg1, byte arg2) {
    
      if(command < 128) {
        // shift over command
        command <<= 4;
        // add channel to the command
        command |= channel;
      }
    
      // send MIDI data
      Serial.write(command);
      Serial.write(arg1);
      Serial.write(arg2);
    
    }
    then think of how to translate the content of the variables in:-

    Code:
    void midi(byte channel, byte command, byte arg1, byte arg2)
    into Teensy Midi library compatible format.
    Make any sense?

  7. #7
    Junior Member
    Join Date
    Jul 2020
    Posts
    4
    That does make sense!
    Before going the route of renaming, I tried again attempting to use usbMIDI instead of hardware serial. The usbMIDI functions built into Teensy work so dang well in every example Iíve tried. And thereís no conflicts with the sketch as-is.

    The translation is where I seem to be getting lost...
    If arg1 is note number,
    and arg2 is velocity,
    Isnít command either noteon, or noteoff, plus whatever this is doing:

    if(command < 128) {
    // shift over command
    command <<= 4;
    // add channel to the command
    command |= channel;
    }

    I wonder how that would all format into a usbMIDI message teensy is happy with?

  8. #8
    Senior Member
    Join Date
    Aug 2019
    Location
    Melbourne Australia
    Posts
    125
    What hardware is needed to get it up and running? Would like to play with it in the light of a new day.

    Suggest fiddling with the function :-

    Code:
    void midi(byte channel, byte command, byte arg1, byte arg2)
    to print channel,command,arg1,arg2 on the serial monitor and see what it's giving us.

  9. #9
    Junior Member
    Join Date
    Jul 2020
    Posts
    4
    For hardware, i'm just using the one led and one pushbutton, usbMIDI to a laptop.
    The basic Fifteenstep example, as I understand, should sequence that one button's performance over 16 steps and send it back over midi.
    Multiple capacitive buttons and neopixels can wait until i see one button working

    I use this free midi monitoring software (almost daily) to see what midi is coming and going to and from my computers and hardware/modular synths.

    https://www.snoize.com/MIDIMonitor/

    Or, I just arm a vst in whichever DAW and see what happens with midi input. However, MIDIMonitor gives a good look at channel, note, velocity, clock...with filtering to drill down on messages we're looking for. A midi note of C-2 isn't going to be audible on any synth, MIDIMonitor helps see that clearly.

  10. #10
    Senior Member
    Join Date
    Aug 2019
    Location
    Melbourne Australia
    Posts
    125
    Uploaded it to a Teensy LC with a button on pin 4 (needed a pullup) looking at TX pin with a CRO which shows what looks like the same midi message repeated fairly rapidly and a different message is seen with a buttonpress.

    Not seeing anything resembling a sequence of different notes so it has not earned a midi connection to any MidiOx or synth.

    Next step: Dug into the example neo_mpr121 and after some sniffing found line 118 :-
    Code:
    seq.setNote(channel, pitch[i], vel[i]);
    Looks like we're setting many notes with pitch and vel arrays

    Next, in basic.ino line 51 :-
    Code:
    seq.setNote(0x0, 0x3C, 0x40);
    We are only setting one note so that is what we have, a One Note sequencer. Seems to fit as it only has one led. Lets face it it ain't really a sequencer unless it's got a row of leds on it so it seems logical to hook up some Neos and a capsense breakout. Don't have either but am curious enough to get some. Ordering.

    Will be up to ten days before taking it any further this end.

  11. #11
    Senior Member
    Join Date
    Aug 2019
    Location
    Melbourne Australia
    Posts
    125
    Curiosity bit. Uploaded to a T3.6 with Din midi out and no button or pullup on pin 4.
    Midiox sees Timing Clock 248 followed by a NoteOff for C-1.

    As for the button, treating it like a touch sense, while a finger is on it, Timing Clock, C-1 NoteOff, NoteOn with Vel of 64 and NoteOff messages are seen for C4. It behaves like a single-note sequencer and stops when you take your finger off.

    I suspect the C-1 NoteOff is a spurious emission as Timing Clock (F8) is single byte and we're still sending whatever the contents of arg1 and arg2 so it looks like Fifteensteps Midi out function needs some tidying up.

    A similar thing is likely to occur when we try to send Song Select which is a two byte message which raises the question, what happens with the un-used arg.

    The basic version does not seem to send Song Select but I expect that when more is working some issue is likely to pop up.

  12. #12
    Junior Member
    Join Date
    Jul 2020
    Posts
    4
    Similar results here, clock and lots of Noteoff for C-1 in the basic example.
    I also have a capacitive board on the way, maybe one of the newer Fifteenstep examples will work, or give more clues.
    I might also try decoupling the sequencer and 'midi' callback from midi output altogether, and send straight to myNoteOn and oscPlay functions in my audiotool-created synth.
    My end project is a sequenced synth voice coming from the teensy, not necessarily a midi-controller.

    Or, maybe this isn't the library I'm looking for, although the features are certainly there.

  13. #13
    Senior Member
    Join Date
    Aug 2019
    Location
    Melbourne Australia
    Posts
    125
    Curiously, it seems FifteenSteps thinks of 0-15 for Midi channel number. Teensy usbMidi spits out on Ch 16 if told to send on channel=0 whereas the Din midi lib spits nothing.

    Needed to resort to some name-changing library tweaks to get it to co-habit with the DinMidi library.

    Inverting the button sense seems logical as you get corresponding NoteOns and NoteOffs for press/release.
    Have yet to see evidence of it sequencing buttonpressed notes though.

Posting Permissions

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