Skip to content

Instantly share code, notes, and snippets.

@nick-thompson
Created December 14, 2021 17:47
Show Gist options
  • Save nick-thompson/bdee96e795ae25a639877a977cb0f783 to your computer and use it in GitHub Desktop.
Save nick-thompson/bdee96e795ae25a639877a977cb0f783 to your computer and use it in GitHub Desktop.
An example Elementary pattern for writing "MIDI Events" to an in-memory sequence-based synthesizer that ultimately renders its output as a series of el.seq nodes.
// A simple polyphonic Synth which records note events against internal
// sequence data.
//
// After recording a series of events, call the render function to build
// a series of synth voices based on the aggregated sequence data.
export default class Synth {
constructor(key, numVoices) {
this.voices = Array.from({length: numVoices}).map(function(x, i) {
return {
key: `${key}:v:${i}`,
gate: 0,
freq: 440,
velo: 0,
data: {
gateSeq: [],
freqSeq: [],
veloSeq: [],
}
};
});
this.nextVoice = 0;
}
step() {
this.voices.forEach(function(voice, i) {
voice.data.gateSeq.push(voice.gate);
voice.data.freqSeq.push(voice.freq);
voice.data.veloSeq.push(voice.velo);
})
}
noteOn(freq, velo) {
this.voices[this.nextVoice].gate = 1.0;
this.voices[this.nextVoice].freq = freq;
this.voices[this.nextVoice].velo = velo;
if (++this.nextVoice >= this.voices.length) {
this.nextVoice -= this.voices.length;
}
}
noteOff(freq) {
for (let i = 0; i < this.voices.length; ++i) {
if (this.voices[i].freq === freq) {
this.voices[i].gate = 0;
}
}
}
render(renderFunc) {
return this.voices.map(function(voice, i) {
return renderFunc(voice.key, voice.data.gateSeq, voice.data.freqSeq, voice.data.veloSeq, i);
});
}
}
let syn = new Synth('syn', 4);
let lastTick = 0;
// Imagine we have a big array of midi events here and want to iterate
// over it, acting against our synth instance
for (let i = 0; i < events.length; ++i) {
// We want our synth to understand how many midi ticks have passed since the last event,
// so we "step" as many times as necessary to arrive at the current event time
while (lastTick < events[i].tickTime) {
syn.step();
}
// Now we can record our current event
if (events[i].type === 'noteOn') {
syn.noteOn(events[i].noteFrequency, events[i].velocity);
}
if (events[i].type === 'noteOff') {
syn.noteOff(events[i].noteFrequench);
}
}
// Finally we can render our poly-voice pattern with whatever we want:
core.render(
el.add(...syn.render(function(key, gs, fs, vs, i) {
let t = el.metro({interval: 20});
let env = el.adsr(0.01, 0.1, 0, 0.1, el.seq({key: `${key}:gs`, seq: gs, hold: true}, t));
let tone = el.cycle(el.seq({key: `${key}:fs`, seq: fs, hold: true}, t));
return el.mul(env, tone);
});
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment