Arduino-based polyphonic step sequencer build [video]

Hi everyone,

I finally have a prototype build of the step sequencer I’ve been working on. Here’s a video:

It’s a midi sequencer with 8 steps, but it also has variable, quantised step length, quantised notes, and a polyphonic mode. You can pick root notes and scale mode, from major, lydian, mixolydian, minor, dorian, phrygian and a chromatic option.

There’s a harmony mode too, which adds an extra note to the chords.

You can also shift the octave up and down by two, and have the harmony note follow the octave or be an octave higher.

I’m still learning how to code, so it’s a bit inefficient, messy etc and the changes get delayed a bit. I also couldn’t get the buffered screen library to work, both for memory issues and due to incompatibility with midi.h, apparently. There’s stuff to work on anyway…

Thanks to @craigyb too for helping me figure out some earlier issues…!

There’s code and a messy emulation on wokwi

But code on GitHub plus stripboard layout and schematic to follow. Between a young child and a full time job I don’t have a whole lot of time to work on things, but will try to upload those soon.

13 Likes

I’ve fixed the delay issue… I didn’t realise that the longer delay between multiplexer reads was only required for the simulation!! Also fixed a couple of other issues and removed a redundant feature.

Here’s the code on GitHub. I’ll upload the schematic/layout soon.

5 Likes

Nice project !

While looking at your code I saw that some of the code is repeated several times.
One example is this bit of code:

>  if (note != prior_note){
>     digitalWrite(n8led, LOW);
>     MIDI.sendNoteOff(currentnote, 0, 1);
>     MIDI.sendNoteOff((currentnote - 12), 0, 2);
>     MIDI.sendNoteOff((currentnote - 12), 0, 1);
>     MIDI.sendNoteOff(current3rd, 0, 1);
>     MIDI.sendNoteOff(current5th, 0, 1);
>     MIDI.sendNoteOff(currentharm, 0, 1); 
>   }

Another bit of code that is repeated several times is this:

    if (mode == "mono") {  
      MIDI.sendNoteOn(currentnote, 127, 1);
    } else {
      MIDI.sendNoteOn(currentnote, 127, 1);
      MIDI.sendNoteOn((currentnote - 12), 127, 1);
      MIDI.sendNoteOn(current3rd, 127, 1);
      MIDI.sendNoteOn(current5th, 127, 1);
      if (harmonyoff == 0) {
      } else {
        MIDI.sendNoteOn(currentharm, 127, 1);
      }
    }

You might consider putting those lines in subroutines and call them whenever you need. That makes the total code shorter and will make finding bugs easier as the odds of typo’s is decreased a bit.

Obviously when you are writing code from scratch and you want things to just work, then repetitions of code are likely to be more frequent. However, once the code is working (also to reduce the risk of runnig into memory shortages) you might want to rework the code and get rid of those.

Tip: whenever you copy bits of code, consider whether it is worth introducing a subroutine and calling that instead of repeating that bit of code.

Extra function calls will only cost a few microseconds, so if timing is not critical at that level, that should not hold you back.

Talking about timing, I would be interested in knowing how accurate the timing of your sequencer is. Have you measured this in any way (other than by ear) ?

Furthermore, your sequencer could be the heart of a multi device setup. Are you planning on having it output midi clock so that other devices (or parts thereof) can adhere to its pace?

2 Likes

Thanks, that makes sense. I’ll have a go at making that change.

Re timing, the best I’ve done is play a metronome app alongside it. It seems OK, and good enough for me… but changing some of the knobs while it’s playing does seem to cause a little bit of temporary slow-down. It’s only really noticeable when changing the scale mode, as it then loops back through to update all the note values too. But I can live with that. Do you know a good way to test it?

I am adding in a 2PPQN sync out option, which I’m going to test with a Pocket Operator. I’d like to add MIDI clock, and I’m going to try.

At some point, I’d like to swap it over to using the uClock library, as that should make the timing more reliable… particularly for MIDI clock. I honestly couldn’t figure out how to do what I wanted to do using it before, though I’m sure it is possible.

I’ve use a crude way of printing time stamps once and looked at their delta’s. They stayed within parts of a millisecond that time. That was OK for my use case.

User interaction can easily have an effect on timing if the code immediately responds to any changes. What I’m experimenting with is that I’m:
1: defering some of the actions that should take place to the start of the new loop and
2: quantising analog values to the number of values that are relevant. I’ve written a bit about a potentiometer class in some code snippets but since then I’ve made a quantised potentiometer class. This allows for choosing the number of values you want a potentiometer to have at max. So now I won’t get up to 1024 intermediary values that will take time to process but don’t change anything. Furthermode it is easier to check with the pot whether its value has changed and only if it has, then do something.

A nice side effect of 1 is, that my timing does not need to be perfect.

1 Like

It is a trade-off, as I could defer stuff until the start of the new loop if I didn’t want to have a screen updating to reflect live changes.

I’ve added MIDI clock including start/stop to the code and added that to GitHub.

If I run it into my iPad and monitor the incoming BPM, it does vary slightly by around 0.1-0.3 BPM as it’s running. I think that’s probably fine for what I’ll be doing…

I also made the suggested changes to reduce the code somewhat.

1 Like

The MIDI library you are using, is that the so called “fortyseveneffects library” by Francois Best?

Yes, that’s the one. Why do you ask?

I’m having a very frustrating day - I had a play around with uClock and got it working, was syncing up quite well. I’ve uploaded the code to the GitHub site.

A few hours later, I wanted to make a couple of minor changes, which seemed to break everything.

However, now even known working past versions of the code, like the most current version on GitHub, don’t seem to be working.

It seems to be connected just to pots/switches connected to one of the two multiplexers… when I first built it I didn’t ground the unused pins, and it got extremely hot. I had a similar issue then, but it resolved itself. Wondering if I did some damage that’s reappeared now for some reason.

I’ve ordered some new ones, lets see…

The thing about arduino code is that the fact that there is an include line which includes a file called “MIDI.h” does not uniquely identify the library that is linked. There might be variants of the library that also have an include file with the same name, or you have some code of your own put into a MIDI.h file. It all boils down to which search path the arduino IDE uses to find and link the library in the end. I’ve been using VSC (with platform io for the arduino bit) for a while now and this uses a project file that specifies the name and version number of the libraries it wants to link. That takes away the ambiguity.
I was wondering whether you have found any documentation other than the class descriptions (they are of no help to me when trying to get an overview of how to use the library) that describes the philosopy / approach to working with the fortysevenfx library.

I know by experience that you should take care of the unused pins for the chip can respond eratically to noise on those pins. I’ve not experienced a chip getting hot that way. Maybe if there occurs an oscillation as an effect of pins not being at defined levels that that can heat things up. Just a wild guess.

You can maybe test the IC’s on a bread board step by step by putting LEDs + serial resistors at their in/outputs and visualise the signal flow using a simple (slow stepping) arduino project.

2 Likes

That’s a good point about the MIDI.h which I hadn’t considered. I’ll add a comment in for now.

My multiplexer(s) should be with me tomorrow, so I’ll hold off on additional testing for now - it still works in Wokwi, and I’ve done some rudimentary trouble-shooting to check it wasn’t a wiring issue/short. If a new 4067 doesn’t fix it, I will build that tester, thanks.

Right now I’m trying to resolve a bug when the sequence is started, that appeared after changing over to uClock - it runs really fast for the first step, it almost steps over it. But for every subsequent repetition of the step it’s fine, it only happens when first hitting play.

I noticed that it did this both for the sequencer and for MIDI clock out - when switching the sequence on, it rushes for a second before normalising, but everything stayed in sync even though it momentarily ran fast.

The behaviour appears on the Wokwi simulation too - you can see the first step when turning it on runs fast, and the serial out runs fast too for a second.

I wondered if it was because I’m starting the uClock clock as I start the sequence. If I start the clock in setup, rather than on sequence start, the issue resolves.

However, I then get inconsistent step lengths for the first step 1 - I imagine because the ‘step’ tick I’m counting to progress notes is only 1/16th notes, and if the clock is just running away in the background I might start the sequence running somewhere in-between a tick.

I suppose an option would be to delay the sequence starting until a tick, but does anyone have any other ideas? Ideally I think I’d like to start everything going at once but keep it all in time.

I extensively use 4067 mux chips and I’ve never experienced them getting hot when inputs/outputs are not connected. But I would always recommend that the unused inputs are grounded when used as an input device.

I’ve recently had issues too with some harris CD74HC4067 chips giving poor signals on the outputs when used in a demux. Changed them for some CD74HC4067 TSOP24-300 chips mounted on carriers and they work perfectly.

3 Likes

I was thinking that or you missed some sort of initialisations step in your code.
I’m not familiar with uClock, but would it be possible to start the uClock in the setup of the sketch and have your device only use it when it needs it?

My sequencer determines the cycle time of the clock by measuring the mean time between several IRQs (triggered by some external pulse) or by measuring the mean time between midi clock messages. This makes the clock more stable, but also makes it lag behind a bit if I change the BPM of the clock. Because I rarely do that, most times the clock is fine.

3 Likes

Yeah, I’m wondering whether there was a short on the stripboard or something - either way replacing the 4067 has completely resolved my issues.

I’ve also changed it to play on the next 16th note tick, and that seems to have sorted any syncing issues I had. Though I haven’t tested it rigorously, I’ve been going via an iRig MIDI into my iPad and playing with Ruismaker. But BPM and start stop are syncing up nicely - see video below.

I’ve also added a baseline/lead generator. When it’s not playing, if you flick this on it will create a melody quantised to the root/mode choice, using 16th, 8th, quarter and half notes only. Makes a decent baseline generator. You can tweak individual steps after it’s been randomised, and turning the switch off returns you to your knob sequence.

I’ve set it to use only those time intervals as it seemed the most useful - I tested using longer note lengths but it was too all over the place. What I’d like to do in future is change the switch to a three position on-off-on, set at 2.5v - 0v - 5v, and have an additional option for say 1 to 4 bar per step chord sequences for pads etc.

One outstanding bug is that a note can hang if you change the time to be shorter after a note is playing. For example, if you have your knob set at 1/16th, and the randomiser has allocated 1/4th note to it, and you turn the time knob while that step is playing, the condition to move to the next note will never be met.

As I’m writing this I realise I can probably resolve this by having a second time variable that is equal to the step’s time variable but set only when the step is first played, then any changes to the time will only come into play on repeats of the sequence. I’ll make that change soon.

2 Likes

cool stuff!​​​​​​​​​​

2 Likes

Will draw up a proper schematic soon too.

2 Likes

Schematic now on GitHub too.

4 Likes

Here’s another, better vid outlining the features with a couple sync out demos using a pocket operator and an iPad:

3 Likes

I like this a lot. I worked on a project last year where I made a generator that produces midi on 4 channels, have a look here:

and seeing your video I think I’m going to pick it up again and add some more features.

3 Likes

Thanks, glad you like it!

What sort of things are you thinking of adding?

The fit and finish on your work is amazing, something I’d like to work on. I’ll also have to look at things like cv out… was thinking of spinning the random bassline generator aspect into a module with clock in and cv out.

1 Like

The generator uses random numbers to produce notes and choose variations of note length, velocity and octave. I’d like to add some chord functions.

1 Like