Lesson: Adding a MIDI Sequencer to the Archean Synthesizer

Introduction

This lesson is about building a MIDI-based sequencer for Archean, an open-source synthesizer built on Teensy 4.0. The main idea is to use Archean’s MIDI IN input to receive MIDI CC messages from the Nakedboards MC-8 controller, which has 8 faders. Each fader sends a value from 0 to 127, and Archean converts those CC values into MIDI notes.

Prerequisites

- Nakedboards MC-8 MIDI controller.
- Archean synthesizer
- A computer with USB connection.
- Arduino IDE with Teensyduino installed
- Each fader should be assigned to a unique MIDI CC number from 0 to 7. You can set this up in the controller settings at nakedboards.online.

Understanding the Idea

The concept behind this lesson is to show how a MIDI controller can become a musical interface for a synthesizer. The MC-8 sends data from its 8 faders, and Archean receives those messages through MIDI IN. Once the data arrives, it is processed and transformed into notes that can drive the synth engine.

The important part is the mapping stage. The fader values go from 0 to 127, but the synthesizer does not use that entire range as notes. Instead, the values are converted into a narrower note range from 12 to 96 to avoid notes that are too low or too high for the instrument.

Programming

The sequencer itself is written in a separate file called sequencer.ino, where all sequencer-related functions are placed. After that, the main Element.ino file is updated as well. Inside Element.ino, there is a switch structure, and one of its cases - the third one - is assigned to the sequencer mode. This allows the sequencer to become one of the internal functional blocks of Archean.

1. Creating sequencer.ino in the Archean Project Folder

The Archean firmware consists of multiple .ino files that are compiled together by the Arduino IDE. To add the sequencer functionality, you need to create a new tab (file) named sequencer.ino inside the main Archean project folder.
2. Global Variables
const int MIDI_CH = 1;
const int NUM_STEPS = 8;

unsigned long lastStepMs = 0;
int currentStep = 0;
int currentNote = -1;
Breakdown:
- MIDInoteRecieved = true - signal to oscillator: "use MIDI notes, not keyboard/CV"
- Element - ADC potentiometer for tempo (0-1023), read globally
3. Function readTempoBpm() — Reading Tempo
int readTempoBpm(){
  int bpm = map(Element, 0, 1023, 40, 240);
  return bpm;
}
Line-by-line breakdown:
- Element — raw ADC value (0-1023 from potentiometer)
- map(input_min, input_max, output_min, output_max) — Arduino linear scaling function
- Result: potentiometer rotation = BPM 40-240 (slow to fast)

Why 40-240 BPM?
Standard range for electronic music. Too low (<20) or too high (>300) tempos are clamped in `bpmToStepMs()`.
4. Function bpmToStepMs() — Step Time Calculation
unsigned long bpmToStepMs(int bpm){
  if (bpm < 20) bpm = 20;
  if (bpm > 300) bpm = 300;
  return 60000UL / bpm;
}
Math breakdown:
60000UL / bpm = milliseconds per step at given BPM

Examples:
- BPM=60 - 60000/60 = 1000ms = 1 second per step
- BPM=120 - 60000/120 = 500ms = half second per step
- BPM=240 - 60000/240 = 250ms = quarter second per step

Division by zero protection: if (bpm < 20) bpm = 20; — minimum frequency limit.
5. Main Function Sequencer() — Sequencer Core
void Sequencer(){
  int bpm = readTempoBpm();
  unsigned long stepMs = bpmToStepMs(bpm);

  if (millis() - lastStepMs >= stepMs){
    lastStepMs += stepMs;
    sendStep(currentStep);
    currentStep = (currentStep + 1) % NUM_STEPS;
  }
}
Line-by-line logic:
1. int bpm = readTempoBpm() — read current tempo from potentiometer
2. unsigned long stepMs = bpmToStepMs(bpm) — calculate pause until next step  
3. millis() - lastStepMs >= stepMs — "has step time elapsed?"
4. lastStepMs += stepMs — accumulation (not `= millis()`), prevents drift
5. sendStep(currentStep) — send note for current step
6. currentStep = (currentStep + 1) % 8 — next step (0,1,2,3,4,5,6,7,0,1...)

Called from Element switch.
6. Function sendStep() — Sending MIDI Note
void sendStep(int step) {
  int note = stepNotes[step];
  MIDInoteRecieved = true;
  RecievedMIDINote = note;

  if (currentNote >= 0) {
    usbMIDI.sendNoteOff(currentNote, 0, MIDI_CH);
  }
  usbMIDI.sendNoteOn(note, 100, MIDI_CH);
  currentNote = note;
}
Breakdown:
int note = stepNotes[step]; // get step note from array
MIDInoteRecieved = true; // oscillator: "listen to MIDI!"
RecievedMIDINote = note; // pass note to oscillator

if (currentNote >= 0) // if previous note exists
usbMIDI.sendNoteOff(currentNote); // turn it off

usbMIDI.sendNoteOn(note, 100, 1); // turn on new note (velocity=100, channel=1)
currentNote = note; // remember active note

Important: `Note Off` before `Note On` — clean note switching without overlap.
7. Integration into the Element block
case 3:
  Sequencer();
  break;
Conclusion

- Practical synthesizer programming skills for Teensy 4 platform
- Open-source contribution capability to Archean project
- Live performance ready sequencer for electronic music production

Troubleshooting / Notes

- No MIDI communication between MC-8 and Archean.
Both devices use TRS Type A MIDI adapter — verify Type A/B compatibility.
- Sequencer doesn't respond to faders.
Check MC-8 fader CC numbers (should be 20-27). Use MIDI monitor to verify CC messages sent.