Arpeggiator

An arpeggiator (arp) is a feature available on several synthesizers that automatically steps through a sequence of notes based on an input chord, thus creating an arpeggio.

Let's go over one of the ways we can achieve a traditional arpeggiator inside Sattern.

As we've discussed in the Patterns section, Patterns are polyphonic. This means that with every note held the Pattern.onSequence callback will be called. For our arpeggiator program we only want to have one "audible" pattern playing a list of held notes at any given time. Let's begin by adding some code to our existing project to keep track of currently held notes.

// main.js
// Arpeggiator
//
// Created by Jacob Sologub on 15 Jun 2020.
// Copyright © 2020 Jacob Sologub. All rights reserved.

const sampler = new sattern.Sampler();
sattern.graph.add (sampler);

const sound = new sattern.Sound ("./mcg_mp_064.wav", {
    lowKey: 0, highKey: 127, rootKey: 64
});
sampler.add (sound);

const reverb = new sattern.SOULPatch ("./Reverb.soulpatch");
sattern.graph.add (reverb);
sattern.graph.connect (sampler.output, reverb.input);

const pattern = new sattern.Pattern();
sattern.add (pattern);
pattern.onSequence = function (context) {
    // Intentionally empty.
};

const triggerKey = new sattern.Note ("C4").key;
const currentlyHeldKeys = new Array (127);

sattern.onNoteOn = function (note) {
    if (note.key != triggerKey) {
        currentlyHeldKeys [note.key] = true;
    }
};

sattern.onNoteOff = function (note) {
    currentlyHeldKeys [note.key] = false;
};

Sattern has a few global callbacks for monitoring/responding to incoming Controller, PitchWheel, Aftertouch, ChannelPressure and Note on/off messages. As you can see on lines #28 - 36 I've assigned two callbacks, sattern.onNoteOn and sattern.onNoteOff to keep track of the currently held keys. When a note on is received, we mark the key index of our currentlyHeldKeys array with true. When we receive a note off message, we mark the key index of our currentlyHeldKeys array with false. We've also defined a "trigger key" constant on line #25 that will be used to determine whether or not the arp should be active. I've chosen "C4" or midi note 72 as my trigger key to enable/disable pattern playback but you can use whatever makes sense for your current setup. The lowest key on your MIDI controller would probably be a better choice, but I'm using (12) "Midi Keyboard" inside the Sattern UI to trigger notes with my computer keyboard.

(12) "Midi Keyboard" maps the top two rows of a standard qwerty keyboard to MIDI notes and can be triggered with your keyboard when (12) "Midi Keyboard" has keyboard focus (click on the keyboard component once to grab the keyboard focus). "C4" is the "A" key on the keyboard.

Now let's add the actual code to generate the Step objects inside our pattern.onSequence callback.

// main.js
// Arpeggiator
//
// Created by Jacob Sologub on 15 Jun 2020.
// Copyright © 2020 Jacob Sologub. All rights reserved.

const sampler = new sattern.Sampler();
sattern.graph.add (sampler);

const sound = new sattern.Sound ("./mcg_mp_064.wav", {
    lowKey: 0, highKey: 127, rootKey: 64
});
sampler.add (sound);

const reverb = new sattern.SOULPatch ("./Reverb.soulpatch");
sattern.graph.add (reverb);
sattern.graph.connect (sampler.output, reverb.input);

const pattern = new sattern.Pattern();
sattern.add (pattern);
pattern.onSequence = function (context) {
    if (context.note.key == triggerKey) {
        const steps = [];

        for (let i = 0; i < currentlyHeldKeys.length; ++i) {
            if (currentlyHeldKeys [i] == true) {
                const step = new sattern.Step ([sampler, new sattern.Note (i)], 3, 2);
                steps.push (step);
            }
        }

        context.clockMultiplier = 2.0;
        
        if (steps.length) {
            return steps;
        }

        return new sattern.Step ([undefined, new sattern.Note()], 1, 1);
    }
};

const triggerKey = new sattern.Note ("C4").key;
const currentlyHeldKeys = new Array (127);

sattern.onNoteOn = function (note) {
    if (note.key != triggerKey) {
        currentlyHeldKeys [note.key] = true;
    }
};

sattern.onNoteOff = function (note) {
    currentlyHeldKeys [note.key] = false;
};

Last updated