Making a simple 8 step sequencer Published: 20th January 2025

I bought my first ever synthesiser a couple of months ago and have been playing around with synthesising sounds learning the basics of sound design. To have some structure in my research and learning I started with synthesising percussive sounds like kick drums, cymbals, hi-hats, etc. Synthesising a kick drum sound is one thing but adding movement to the sound on its own a separate technique which is not really possible with my Moog Mavis.
Moog Mavis is a perfect synth to learn the basics of sound synthesis. It is a semi modular synth packed with a lot features like its VCO with pulse width modulation, waveform mixing, 24dB Low Pass Filter, audio rate LFO (which can act as a second VCO), 24 point patch bay which helps extend the base functions to a greater extent, 4 stage (ADSR) envelope generator, wavefolding circuit, allowing for additive synthesis, patchable sample and hold circuit etc. It does lack certain capabilities like a noise source, sequencer, modifying the slope of the filter, fewer modulation options, etc. But given the price of the Mavis it does pack a lot of functionalities and is an excellent synth for getting into sound synthesis.
Mavis primarily uses subtractive synthesis to generate sounds and I was able to generate kick drum sounds by skipping the primary VCO and using LFO as the source of my sound and using the attenuator to controls how much the envelope affects the pitch with some extra processing like compression on Ableton but not having an ability to trigger the sound on its own limits the exploration I can do in designing the sound.

In comes the sequencer. But first what is a sequencer anyway?
In sound synthesis we have oscillators, filters, amplifiers that create and shape the sound. These modules give us the ability to craft the timbre of the sound but to create music out of the sound we need to control when the sound starts and stops. Music is just organised sound. In the musical structure sounds start, repeat, and stop and start again. We also need a way to program in what pitch to play and how they change over time. A sequencer helps us program these attributes of a sound. Sequencers tell the synthesiser modules when to trigger sounds and what parameters to change for that trigger. The most basic way a sequencer helps in synthesis is through gate signals. A gate signal acts like an on/off switch. It is one of the main signal types that are passed around in modular synthesisers. It jumps to high level which is typically 5 volts when a sequencer jumps to the next step in the sequence. When a gate signal is applied to an envelope generator, the start of the gate tells the envelope to go through its stages Attack and Decay, while the gate stays high the envelope stays in its Sustain stage and when the gate is low the envelope moves to the Release stage. Beyond this sequencers can also help control other parameters of the sound. While one sequence controls the sound trigger through envelope generators other sequence can control the pitch of the sound and another can control the cutoff filter through something called Control Voltage (CV). So sequencers play a crucial role in defining the overall sound coming out of a synthesiser and adding a movement to it.

Moog Mavis does not come with a sequencer. Moog has other synths like its DFAM (drum machine), Mother 32 which come with their own sequencers built into the machines. But we are in the world of modular synthesis. There is a reason it is called modular and that gives endless possibilities to explore. We can build our modules and hook into our modular synthesiser setup. But how do we even create a sequencer? Do we get small resistors, transistors, capacitors, breadboards and bunch of wires to make a sequencer circuit? Of course we can.

Though I have studied electronics in my year 11, 12, and first year of engineering I am more of a software engineer than an electrical engineer so I found a way to do less electronics and more programming to build my step sequencer.
Electro Smith makes hardware and software tools for audio applications. They are particularly known for their creating the Daisy platform which has become an important tool in modern DIY audio electronics community. Their flagship product, the Daisy, is a powerful microcomputer platform specifically designed for audio applications. Think of it as a specialized "brain" for audio devices it's like having a tiny but powerful music computer that can be programmed to process and generate sound in various ways. Daisy supports a number of languages including C++, Arduino, and Max/MSP, etc. to program audio on it.
This is Electro Smith Daisy Pod. It takes the daisy microprocessor and adds a set of physical controls and connections like LEDs, switch buttons, knobs, etc. to quickly get started with audio programming.

Electro Smith Daisy Pod circuit diagram

So here is my setup for a simple 8 step sequencer which for now only sends a gate output using a daisy pod:

  • Use the encoder to step through each step in the sequence
  • Press the encoder to enter or leave edit mode
  • Once in edit mode use the switch number 2 to turn on/off a step in the sequence
  • For every on step in the sequence LED 2 turns green
  • LED 1 shows 8 different colours for each step in the sequence
  • Use knob 1 to change the tempo of the step

Here is the simple c++ method running on Daisy Pod to generate a gate signal for steps in a sequence:


constexpr uint8_t NUM_STEPS = 8;
constexpr float MIN_TEMPO = 100;
constexpr float MAX_TEMPO = 400;

enum Mode
{
    STEP_EDIT,
    GATE_VIEW
};

struct
{
    uint8_t currentStep;
    uint8_t editStep;
    bool active[NUM_STEPS];
    float phase;
    float tempo;
    Mode mode;
    bool gateState;
} sequencer;

Color stepColors[NUM_STEPS];

void updateLeds()
{
    // edit mode: show selected step
    if (sequencer.mode == STEP_EDIT)
    {
        hw.led1.SetColor(stepColors[sequencer.editStep]);
        if (sequencer.active[sequencer.editStep])
        {
            hw.led2.Set(0, 1, 0); // Green
        }
        else
        {
            hw.led2.Set(0, 0, 0); // Off
        }
    }
    // play mode: show active step + gate state
    else
    {
        hw.led1.SetColor(stepColors[sequencer.currentStep]);
        // led2 shows gate state (green when active)
        if (sequencer.gateState)
        {
            hw.led2.Set(0, 1, 0); // Green
        }
        else
        {
            hw.led2.Set(0, 0, 0); // Off
        }
    }
    hw.UpdateLeds();
}

void AudioCallback(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t size)
{
    hw.ProcessAllControls();
    sequencer.tempo = MIN_TEMPO + hw.knob1.Process() * MAX_TEMPO;

    if (hw.encoder.RisingEdge())
    {
        sequencer.mode = sequencer.mode == STEP_EDIT ? GATE_VIEW : STEP_EDIT;
        updateLeds();
    }

    if (sequencer.mode == STEP_EDIT)
    {
        int32_t inc = hw.encoder.Increment();
        if (inc != 0)
        {
            sequencer.editStep = (sequencer.editStep + inc + NUM_STEPS) % NUM_STEPS;
            updateLeds();
        }
    }

    if (hw.button2.RisingEdge() && sequencer.mode == STEP_EDIT)
    {
        sequencer.active[sequencer.editStep] = !sequencer.active[sequencer.editStep];
        updateLeds();
    }

    /**
     * when we say tempo is 120BPM each beat represent a quarter note
     * in most electronic music and hence sequencers steps need to align with eighth note
     * let's say the tempo is 120
     * tempo/60 = 2 beats per second
     * then 2/2 makes each beat take eight of a note
     * without the last division /2 each step would last a full beat i.e. a quarter note
     */
    float phaseInc = (sequencer.tempo / 60.0f / 2.0f) / hw.AudioSampleRate();

    for (size_t i = 0; i < size; i++)
    {
        sequencer.phase += phaseInc;

        if (sequencer.phase >= 1.0f)
        {
            sequencer.phase -= 1.0f;
            sequencer.currentStep = (sequencer.currentStep + 1) % NUM_STEPS;
            sequencer.gateState = sequencer.active[sequencer.currentStep];

            // Update LEDs only when step changes in view mode
            if (sequencer.mode == GATE_VIEW)
            {
                updateLeds();
            }
        }

        // create a gate pulse that stays high for 50% of the step duration
        // this ensures a clear rising edge at the start of each active step
        float gateThreshold = 0.5f; // 50% duty cycle
        bool trigger = (sequencer.phase < gateThreshold) && sequencer.gateState;
        out[0][i] = out[1][i] = trigger ? 0.95f : -0.95f;
    }
}
                        

Tempo refers to the overall speed or pace of the sound/music. If you were to tap your foot to the music it's how fast or slow these regular movements would be. Tempo is measures in Beats Per Minute (BPM). Each beat represents a quarter not and this comes from western classical music notation where the quarter note is considered the basic unit of time. When you say "1-2-3-4" to keep time, each number typically represents a quarter note. However in most modern electronic music the most common rhythmic subdivision is the eighth note. This is because:

  • Most electronic drum patterns are built around a fundamental rhythm called the "sixteenth note grid," where a 4/4 bar is divided into 16 equal parts. The kick drum often falls on quarter notes (beats 1-2-3-4), while the hi-hats typically play eighth notes (the "and" between each beat). This creates the characteristic "boom-tss-boom-tss" pattern heard in electronic music.
  • When you hear a basic house music pattern, what feels like the basic "pulse" is usually happening at the eighth note level. The iconic electronic music rhythm:
  • Kick on the quarter notes (1-2-3-4)
  • Open hi-hat on the eighth notes between (the "and" counts)
  • Snare/clap on beats 2 and 4

So when we make a step sequencer with 8 steps, having each step represent an eighth note means:

  • One complete sequence = 4 quarter notes = 1 bar of music
  • Each step = 1/8 of the bar = eighth note

This is an important piece of conversion we need to make in our code to correctly calculate the tempo and the length of each step for that tempo.
To calculate the frequency of each step we divide the current tempo by 60 to convert BPM to beats per second and divide by 2 again because without the last division each step would last a full beat i.e. a quarter note.

Now that we know how long is each step, let's emit a gate signal for each active step in the sequence. Moog Mavis Gate input works with the Eurorack modular voltage levels and audio interface voltage levels. In a Eurorack system, gate signals typically swing between -5V and +5V or even up to +8V. However, the Mavis is more flexible - it can also work with the lower voltage levels that come from audio interfaces.


Daisy Pod works with an audio sample rate of 48kHz. In each audio sample the phase increases by
1.0 / 48000 = 0.0000208333... The phase variable acts like a musical metronome that counts from 0 to 1. When it reaches 1, several things happen:

  1. The phase resets back to 0 (phase -= 1.0f)
  2. The sequencer moves to the next step: sequencer.currentStep = (sequencer.currentStep + 1) % NUM_STEPS;
  3. The gate output updates based on whether the new step is active

During each step, phase goes from 0 to 1. Checking if phase is below 0.5 essentially divides each step into two equal parts. Think of it like splitting a musical note into its "pressed" and "released" states and when it's below 0.5, the gate is high (if that step is active). This creates a perfect 50% duty cycle meaning the gate signal is high for exactly half of each step's duration.

At 120 BPM, here's what happens in real time:

  • Each beat represents a quarter note
  • One beat takes 0.5 seconds (60 seconds / 120 beats = 0.5 seconds per beat)
  • The division by 2 means each step represents an eighth note, not a quarter note.
  • At 120 BPM, each eighth note (step) takes 0.25 seconds (half of a quarter note)
  • For each step Gate is high for 0.125 seconds (half of 0.25) and Gate is low for 0.125 seconds
  • sequence has 8 steps, so one complete sequence takes: 8 steps × 0.25 seconds = 2 seconds total

In musical terms, if we are in 4/4 time (the most common time signature), this means:

  • Each bar (measure) contains 4 quarter notes or 8 eighth notes
  • Your 8-step sequence exactly fills one bar of music
  • Every 2 seconds, the sequence completes one full bar

By using phase this way is it creates perfectly timed transitions regardless of the tempo. Whether you are running at 40 BPM or 340 BPM the phase accumulation automatically adjusts to keep everything in sync with your audio rate, preventing any timing jitter that could affect the rhythmic precision.

Once we know when to output a signal based on the phase we output a voltage of +0.95V and -0.95V. In digital audio systems like your Daisy Pod, there is a concept called "headroom" - a small safety margin below the absolute maximum voltage the system can handle. The maximum theoretical voltage range in most audio systems is ±1.0 volt, but using exactly ±1.0 could cause problems.
First, using 0.95 provides a small buffer against clipping. Digital-to-analog converters (DACs) can behave unpredictably when pushed to their absolute limits. They might introduce distortion or fail to reach the exact maximum voltage consistently. By staying slightly below the maximum, you ensure the signal remains clean and reliable.
Second, real-world electronic components have manufacturing tolerances and slight variations. The 5% headroom (the difference between 0.95 and 1.0) helps accommodate these variations. It's like leaving a little extra space when parking your car - it's better to have a small margin of safety than to risk bumping into something.
Third, when signals pass through multiple stages of audio equipment (like from my Daisy Pod to the Mavis), each stage might introduce tiny amounts of gain or amplification. If I started at exactly ±1.0V and any stage added even a tiny bit of gain, I would get clipping. Starting at ±0.95V gives the signal room to breathe through the entire signal chain.

This gives a simple step sequencer which outputs a gate signal for each active step in the sequence. Next I'll be adding the functionality to output a pitch CV to change the pitch of each active step in the sequence.
Article coming soon...