Multi-Mode MIDI Step Sequencer

I’ve revisited my Arduino MIDI Step Sequencer and added different play modes and LED indicators, but I started to quickly run out of IO pins – so I dug out a Pro Mega 2560 board that I’ve been meaning to try out for a while.  You can read all the details below.

  • Update: There is now a panel version with more functionality in the code.  Full details here.

Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

These are the key Arduino tutorials for the main concepts used in this project:

If you are new to Arduino, see the Getting Started pages.

Parts list

  • Pro Mega 2560
  • 9x 10kΩ potentiometers
  • 8x SPST switches
  • 8x LEDs
  • 8x resistors for LEDs (I used 2kΩ)
  • 4x three-position switches (or just jumper wires)
  • 1x push switch
  • Breadboards and jumper wires
  • MIDI out – see Arduino MIDI Interfaces

The Circuit

8 Step Sequencer 2560_bb

As you can see from the diagram there are four main sections to this project:

  • The sequencer controls, comprising: one potentiometer, one “on/off” switch, and one LED for each step – eight of each in total.  I’ve only only shown four on one breadboard above, you’ll want to add a second breadboard and hook up their power rails to get 8 steps.
  • The mode switches: four three-position switches are used to choose the various modes.
  • The tempo/step controls.
  • The MIDI OUT interface.

I wanted more IO than you can really support on a standard Arduino, so I looked at a few different options:

But then I remembered buying some Pro Mega 2560 boards a while back.  These are basically an Arduino Mega 2560 in a small form factor.  You can read more about them here, but in summary, they look like this:

httprobotdyn.compubmedia0g-00005641mega-pro-ch340gatmega2560photophototop0g-00005641mega-pro-ch340gatmega2560no-pinheaders - Copy

They are based on the ATmega2560 and have a ridiculous number of IO pins, including 16 analog inputs and four serial ports(!) and you can buy them online for around £10 each.

As you can see, I even managed to find a Fritzing part for them, which was excellent.

Sequencer Control

8 Step Sequencer 2560 - Control

For this part of the circuit, there is a common ground and then a switch, LED and resistor, and potentiometer in “potential divider” mode, hooked up to three IO pins.  This pattern is then repeated for each of the eight steps.

They all hook up to the Pro Mega in three banks of eight as follows:

8 Step Sequencer 2560 - Control Pins

          • A0, A2, A4, A6, A8, A10, A12, A14 = potentiometers.
          • D32, D34, D36, D38, D40, D42, D44, D46 = LED anodes.
          • D33, D35, D37, D39, D41, D43, D45, D47 = switches.

The switches are all connected to IO pins configured in INPUT_PULLUP mode, so don’t need their own resistors, but this means that when reading them, “on” means LOW and “off” means HIGH.

The LEDs are connected each with their anode via a 2kΩ resistor to the microcontroller and their cathode to GND.

The potentiometers are connected to 5V and GND with the “wiper” connected to the analog input pins.  Here is my sequencer section.

IMG_5101

Sequencer Mode Switches

8 Step Sequencer 2560 - Modes

I have four “three position” switches, each using two digital IO input pins in INPUT_PULLUP mode.  When “up” these switches connect the top pin to the middle pin; when “down” connect the bottom pin to the middle pin; and when left in the middle connect neither pin to anything else.

If I therefore connect the middle pin to GND and the top and bottom pins to two IO pins, this gives me three states for each switch as follows:

          Pin 1   Pin 2
UP LOW HIGH
MIDDLE HIGH HIGH
DOWN HIGH LOW

The switches are connected to the Pro Mega as follows:

  • Trigger mode: D29, D31
  • Run mode: D28, D30
  • “Switch” mode: D24, D26
  • Range mode: D25, D27

In my testing I just used a jumper wire to hook the appropriate signal to GND to activate the switch as can be seen below. Notice how all the GNDs are linked together.

IMG_5102

Tempo and Gate

Tempo is simply another potentiometer connected to A1.

The Step button is another INPUT_PULLUP connection to D23, so again is active LOW (when the button connects D23 to GND).

The gate input is a standard INPUT connection to D22 and is considered “triggered” when it goes from LOW to HIGH, the idea being that this could be connected up to an external clock if required.

MIDI OUT

As the Pro Mega 2560 has four serial ports, I’ve used the second one (UART 1) for MIDI which leaves the first one (UART 0) available for serial debugging purposes over the USB port.  It is quite a luxury not to have to disable my MIDI link to use Serial.print() or to upload a new sketch!

I’m using one of my off-the-shelf MIDI modules connected to TX1 (D18), 5V and GND.

The Code

The code was written in stages.  First, I ported over the Arduino MIDI Step Sequencer and updated the pin definitions to check the basics were all working.

The Main Sequencer

I’ve used arrays to store the pin combinations as follows:

int seqpot[NUM_STEPS] = {A0,A2,A4,A6,A8,A10,A12,A14};
int seqsw[NUM_STEPS] = {32,34,36,38,40,42,44,46};
int seqled[NUM_STEPS] = {33,35,37,39,41,43,45,47};

The basic operation of the main loop is as follows:

IF there is a note playing THEN
Send the MIDI noteOff

Light the LED for this step
IF (seq switch is on) THEN
Read the pot for this step
Send the MIDI noteOn for the value of the pot

Delay for a bit to reflect the tempo
Turn off the LED for the step
Move on to the next step

The Sequencer Modes

Then to add in the extra functionality, there is a “control loop” part required to read the mode switches. They are all controlled via four state variables:

int trigmode;
int runmode;
int swmode;
int octmode;

Each of these can have one of three values – 0, 1 or 2 to reflect the three variations possible, dependent on the switch settings, (with zero being the default “middle” case each time), as described below.

 

Run

Trigger

Switches

Range

0

Normal

Free Running

Silence

Full range

1

Reverse

Sequence

Skipped

Four octaves

2

Cycle

Single Step

Repeated

One octave

Then it was just a case of reading the pairs of switches and setting the mode state variables accordingly.  Here is an example, reading the run mode switches. Remember they are “active LOW” so “if (!sw)” means “IF the switch is ON, i.e. LOW”.

// Run mode switches
int sw1 = digitalRead(runsw[0]);
int sw2 = digitalRead(runsw[1]);
if (!sw1) {
runmode = R_REV;
} else if (!sw2) {
runmode = R_CYCL;
} else {
runmode = R_NORM;
}

I probably didn’t need to, but I set up the code to only check one pair of switches on each pass through the main loop.

Integrating the different modes into the code, though, was, how shall I put it… entertaining!

Octave Range Modes

The range mode was relatively simple, that was largely using the map() function to map the analog pot value for each step onto the required range of MIDI notes.  I have a special case before all this to check for pot readings less than MIN_POT_READING to give me a clear “zero” range of values.

int playing;
if (octmode == O_ONE) {
playing = map(potReading, MIN_POT_READING, 1023, O_ONE_START, O_ONE_START+12);
} else if (octmode == O_FOUR) {
playing = map(potReading, MIN_POT_READING, 1023, O_FOUR_START, O_FOUR_START+48);
} else {
playing = map(potReading, MIN_POT_READING, 1023, 0, numNotes-1);
}

The Run and Switch Modes

The “run” and “switch” modes are intertwined a little.  The basic operating “run” modes are as follows:

  • Normal – just play through the sequence in the order 1 to 8.
  • Reverse – play through in the order 8 to 1.
  • Cycle – play through in the order 1 to 8 and back to 1 again.

The different switch modes are as follows:

  • Silence – when activated the step is still played, but played “silently”.  This is effectively the same as turning the pot for that step all the way down to zero.
  • Skip – when activated the step is skipped completely. Useful for smaller patterns of 1 to 7 notes.
  • Repeat – when activated the step is played twice each time.  If you have repeated notes in your pattern this sort of gives you extra steps.

The main loop has to therefore do something like the following:

Read the 'switch mode' switch
IF switch is OFF THEN
Play the step normally
IF switch is ON but swmode is SILENT THEN
Play a silent step

IF switch is ON and swmode is SKIP THEN
Don't to the "tempo delay" - skip straight to the next step
ELSE
Do the "tempo delay"

IF switch is ON and swmode is REPEAT THEN
Only move to the next step after the next time round

Handling the “next time round” thing for the repeat was getting complicated until I realised I could add a new “hidden” mode to swmode, so I introduced a fourth state – S_RPT2 – which is set on the first time through when the switch for the step is activated and I’m in REPEAT mode.  The step is only advanced when the S_RPT2 pass through the code is complete, when I can then set swmode back to the original S_RPT state.  It is a little more complicated than that – S_RPT2 needs to be taken into consideration elsewhere in the code too, but that is the essence of what is going on.

Once that is sorted out, implementing the RUN modes just boils down to how I increment the step when the time comes.  The basic idea is as follows – this assumes all the logic related to the switch mode/repeats/etc described above has now been done, and I am ready to actually move to the next step in the sequence.

Pick the direction for the next step depending on the RUN mode:
Normal mode: Dir = 1
Reverse mode: Dir = -1
Cycle mode:
IF at start of sequence, Dir = 1
IF at end of sequence, Dir = -1
ELSE don't change Dir, leave it to whatever it was

Next Step = Old Step + Dir
IF >= NUM_STEPS reset to 0
IF < 0 reset to (NUM_STEPS-1)

As long as the “direction” variable is global – i.e. remembered between scans – then all is well.

Note that this will only play the first and last notes once in the CYCLE mode – so the full sequence is actually 14 steps, not 16, although of course you could set the SWITCH mode to REPEAT and play those two steps twice if you wanted to.

The Trigger Modes

So about that trigger… this is a little more challenging, but again the basic idea is:

Look for the low-high edge on the GATE input
OR the high-low transition for the STEP button

IF in Free TRIGGER mode, THEN
Carry on and play the step as normal

IF in Single Step TRIGGER mode, THEN
Only carry on if triggered

IF in Sequence TRIGGER mode, THEN
IF about to play the first note THEN
Only carry on if triggered
ELSE
Carry on anyway

It gets a little more complicated when I have to take into account the interactions with the other modes – for example when in REVERSE or CYCLE run mode, then the “first note” for a sequence trigger can be the last step in the sequence, not the first.  Also, when in REPEAT mode for the switch, I only wait for a trigger on the first time playing a note.

Handling Tempo

In theory, handling the tempo should have been relatively straight forward in that I largely need to map the reading of the pot (0 to 1023) onto a value for the Arduino’s delay function.

As the delay is in milliseconds, a value of 250 is 1/4 of a second which gives 240 beats per minute, and so on.

However, I was finding that a linear scaling wasn’t particularly useful!  So I initially messed around with a custom map() function that gave me more of an exponential return, but in the end opted to look for four independent ranges.  This ended up being implemented as a tempomap() function to return the value to use with the delay() as follows.

int tempomap (int i) {
if (i<256) {
// Slowest range of tempos: 5000 to 2000
return map (i, 0, 256, 5000, 2000);
} else if (i<512) {
// Mid range of tempos: 2000 to 800
return map (i, 256, 512, 2000, 800);
} else if (i<768) {
// Next mid range of tempos: 800 to 300
return map (i, 512, 768, 800, 300);
} else {
// Fastest range 300 to 50
return map (i, 768, 1023, 300, 50);
}
}

Summary

This all sounds complicated, and there is no getting away from having to cope with how the four different modes (each with three options) interact with each other, but the actual code is quite heavily commented so hopefully everything is explained where it is needed.

The MIDI side of things is pretty easy in comparison with everything else – it is only using a MIDI.sendNoteOn() and MIDI.sendNoteOff().  The only thing that is slightly different to any other example is telling the MIDI library to use the second serial port rather than the default.

MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);

If I wanted to use the third or fourth UARTs, then I’d use Serial2 or Serial3.  The default (UART0) is simply Serial as with a “normal” Arduino.

I think that is about it.

Find it on GitHub here.

Closing Thoughts

The Pro Mega 2560 is a great little board – there are so many options.  Some things I’m wondering about:

  • I could add more IO!  One option might be a way to select the MIDI channel or possibly a display.
  • I have four serial ports at my disposal, I could output four separate MIDI feeds.
  • I’m still chewing over alternative modes too – maybe some kind of algorithmic choosing of steps.
  • I’m also wondering about some randomness – either in terms of which note is played on each step (adjustable by the pot), or maybe in terms of which step to play next.
  • Another possibility is to support expansion to a second set of 8 steps linked somehow in to the first.

I’ve not bothered with any kind of gate output as the main goal was to produce a MIDI sequencer, but that is something else to consider, although to be honest there are probably much simpler ways to achieve that (just google “Baby 8 sequencer” for one example).

Note that I’ve also not really tried the GATE input yet. I need to wire up some kind of clock circuit to try that but I didn’t have anything to hand at present.

Also, the breadboard connections to the pots is a little unreliable – sometimes I get some “cross resistance” or floating due to dodgy connections.

However for now, I’m very pleased with how this works. But this is all really just a prelude to me putting this into some kind of panel, so watch this space.

Kevin

IMG_5103

Leave a comment