Skip to content

Instantly share code, notes, and snippets.

@petersalomonsen
Last active September 29, 2020 19:59
Show Gist options
  • Save petersalomonsen/d19d7574d9fae1fd01330485644837a1 to your computer and use it in GitHub Desktop.
Save petersalomonsen/d19d7574d9fae1fd01330485644837a1 to your computer and use it in GitHub Desktop.
instrumentstemplate
import { SineOscillator } from '../synth/sineoscillator.class';
import { SawOscillator } from '../synth/sawoscillator.class';
import { Envelope } from '../synth/envelope.class';
import { StereoSignal } from "../synth/stereosignal.class";
import { notefreq } from '../synth/note';
import { Noise } from '../synth/noise.class';
import { BandPass } from '../fx/bandpass';
import { SAMPLERATE } from '../environment';
import { BiQuadFilter, FilterType, Q_BUTTERWORTH } from '../synth/biquad';
import { createInstrumentArray } from '../common/mixcommon';
import { Freeverb } from '../fx/freeverb';
import { DelayLine } from '../fx/delayline';
import { TriBandEQ } from '../fx/tribandeq';
import { WaveShaper } from '../synth/shaper';
export const PATTERN_SIZE_SHIFT: usize = 4;
export const BEATS_PER_PATTERN_SHIFT: usize = 2;
export class Snare {
private _note: f32;
private velocity: f32;
readonly noise: Noise = new Noise();
readonly env1: Envelope = new Envelope(0.001, 1.0, 0.8, 0.3);
readonly bp1: BandPass = new BandPass(200, 350);
readonly env2: Envelope = new Envelope(0.001, 0.08, 0.06, 1);
readonly bp2: BandPass = new BandPass(3000, 7000);
readonly env3: Envelope = new Envelope(0.001, 0.05, 0.01, 0.1);
readonly bp3: BandPass = new BandPass(10, 150);
readonly signal: StereoSignal = new StereoSignal();
constructor() {
}
set note(note: f32) {
if(note > 1) {
this.velocity = note / 16;
this.env1.attack();
this.env2.attack();
this.env3.attack();
} else {
this.env1.release();
this.env2.release();
this.env3.release();
}
this._note = note;
}
get note(): f32 {
return this._note;
}
next(): void {
let env1: f32 = this.env1.next();
let env2: f32 = this.env2.next();
let env3: f32 = this.env3.next();
let osc: f32 = this.noise.next();
let sig1 = this.bp1.process(osc) * env1;
let sig2 = this.bp2.process(osc) * env2 * 2;
let sig3 = this.bp3.process(osc) * env3 * 16;
this.signal.left = this.velocity * (sig1 + sig2 * 0.8 + sig3);
this.signal.right = this.velocity * (sig1 * 0.8 + sig2 + sig3);
}
}
export class Kick {
private _note: f32;
private velocity: f32;
readonly noise: Noise = new Noise();
readonly env2: Envelope = new Envelope(0.001, 0.01, 0.0, 1);
readonly bp2: BandPass = new BandPass(4000, 5000);
readonly env3: Envelope = new Envelope(0.001, 0.1, 0.05, 0.1);
readonly bp3: BandPass = new BandPass(10, 100);
readonly signal: StereoSignal = new StereoSignal();
constructor() {
}
set note(note: f32) {
if(note > 1) {
this.velocity = note / 16;
this.env2.attack();
this.env3.attack();
} else {
this.env2.release();
this.env3.release();
}
this._note = note;
}
get note(): f32 {
return this._note;
}
next(): void {
let env2: f32 = this.env2.next();
let env3: f32 = this.env3.next();
let osc: f32 = this.noise.next();
let sig2 = this.bp2.process(osc) * env2 ;
let sig3 = this.bp3.process(osc) * env3 * 8;
this.signal.left = this.velocity * (-sig2 + sig3);
this.signal.right = this.velocity * ( + sig2 - sig3);
}
}
export class Bass {
private _note: f32;
readonly envelope: Envelope = new Envelope(0.01, 0.3, 0.8, 0.2);
readonly filterenv: Envelope = new Envelope(0.01, 0.2, 0.1, 0.2);
readonly sawoscillator: SawOscillator = new SawOscillator();
readonly filter: BiQuadFilter = new BiQuadFilter();
readonly lpfilter: BiQuadFilter = new BiQuadFilter();
readonly band1: BandPass = new BandPass(1000, 2000);
readonly signal: StereoSignal = new StereoSignal();
constructor() {
}
set note(note: f32) {
if(note > 1) {
this.sawoscillator.frequency = notefreq(note);
this.envelope.attack();
this.filterenv.attack();
} else {
this.envelope.release();
this.filterenv.release();
}
this._note = note;
}
get note(): f32 {
return this._note;
}
next(): void {
let env: f32 = this.envelope.next();
if(env === 0) {
this.signal.clear();
return;
}
let filterenv = this.filterenv.next();
let signal = this.sawoscillator.next();
signal *= env;
this.lpfilter.update_coeffecients(FilterType.LowPass, SAMPLERATE,
this.sawoscillator.frequency + (16 * this.sawoscillator.frequency * filterenv), Q_BUTTERWORTH);
const band1freq = this.sawoscillator.frequency * 4;
this.band1.update_frequencies(band1freq, band1freq + env * this.sawoscillator.frequency);
let band1 = this.band1.process(signal);
signal = this.lpfilter.process(signal);
this.signal.left = signal * 2 + band1;
this.signal.right = signal * 2 - band1;
}
}
export class SoftPad {
private _note: f32;
readonly envelope: Envelope = new Envelope(0.2, 0.6, 0.75, 0.5);
readonly filterenvelope: Envelope = new Envelope(0.8, 1.0, 0.6, 0.5);
readonly hipassfilterenvelope: Envelope = new Envelope(0.02, 3, 0.2, 2.0);
readonly sawoscillator: SawOscillator = new SawOscillator();
readonly sawoscillator2: SawOscillator = new SawOscillator();
readonly sawoscillator3: SawOscillator = new SawOscillator();
readonly sawoscillator4: SawOscillator = new SawOscillator();
readonly sawoscillator5: SawOscillator = new SawOscillator();
readonly lfo: SineOscillator = new SineOscillator();
readonly filterl: BiQuadFilter = new BiQuadFilter();
readonly filterr: BiQuadFilter = new BiQuadFilter();
readonly signal: StereoSignal = new StereoSignal();
set note(note: f32) {
if(note > 1) {
this.lfo.frequency = 1;
this.lfo.position = 0;
this.sawoscillator.frequency = notefreq(note);
this.sawoscillator2.frequency = notefreq(note + 0.03);
this.sawoscillator3.frequency = notefreq(note - 0.03);
this.sawoscillator4.frequency = notefreq(note + 0.06);
this.sawoscillator5.frequency = notefreq(note - 0.06);
this.envelope.attack();
this.filterenvelope.attack();
this.hipassfilterenvelope.attack();
this._note = note;
} else {
this.envelope.release();
this.filterenvelope.release();
this.hipassfilterenvelope.release();
}
}
get note(): f32 {
return this._note;
}
next(): void {
let env: f32 = this.envelope.next();
if(env === 0) {
this.signal.clear();
return;
}
const lfo: f32 = this.lfo.next();
const note = this.note;
if(note<2) {
return;
}
this.sawoscillator2.frequency = notefreq(note + 0.05 + (0.02 * lfo));
this.sawoscillator3.frequency = notefreq(note - 0.05 - (0.02 * lfo));
this.sawoscillator4.frequency = notefreq(note + 0.1 + (0.03 * lfo));
this.sawoscillator5.frequency = notefreq(note - 0.1 - (0.03 * lfo));
let osc: f32 = this.sawoscillator.next();
let osc2: f32 = this.sawoscillator2.next();
let osc3: f32 = this.sawoscillator3.next();
let osc4: f32 = this.sawoscillator4.next();
let osc5: f32 = this.sawoscillator5.next();
let left = env * (osc + osc2 + osc5);
let right = env * (osc + osc3 + osc4 );
const filterlfo = lfo + 1;
this.filterl.update_coeffecients(FilterType.LowPass, SAMPLERATE,
200 + this.filterenvelope.next() * filterlfo * 2000 + 20 * (127 - this.note), Q_BUTTERWORTH);
this.filterr.update_coeffecients(FilterType.LowPass, SAMPLERATE,
200 + this.filterenvelope.next() * filterlfo * 2000 + 20 * (this.note), Q_BUTTERWORTH);
this.signal.left = this.filterl.process(left );
this.signal.right = this.filterr.process(right );
}
}
class SineLead {
private _note: f32;
readonly osc: SineOscillator = new SineOscillator();
readonly lfo: SineOscillator = new SineOscillator();
readonly env1: Envelope = new Envelope(0.02, 0.10, 0.2, 0.3);
readonly noiseenv: Envelope = new Envelope(0.01, 0.02, 0.1, 0.3);
readonly signal: StereoSignal = new StereoSignal();
private noise: Noise = new Noise();
hpfilterl: BiQuadFilter = new BiQuadFilter();
hpfilterr: BiQuadFilter = new BiQuadFilter();
private bandpass: BandPass = new BandPass(400,2000);
private shaper: WaveShaper = new WaveShaper();
constructor() {
this.hpfilterl.update_coeffecients(FilterType.HighPass, SAMPLERATE, 7000, 0.5);
this.hpfilterr.update_coeffecients(FilterType.HighPass, SAMPLERATE, 7000, 0.5);
}
set note(note: f32) {
if(note > 1) {
this.osc.frequency = notefreq(note);
this.lfo.frequency = 8;
this._note = note;
this.env1.attack();
this.noiseenv.attack();
} else {
this.env1.release();
this.noiseenv.release();
}
}
get note(): f32 {
return this._note;
}
next(): void {
if(this.env1.state === 4) {
this.signal.clear();
return;
}
const env1: f32 = this.env1.next();
const noiseenv: f32 = this.noiseenv.next();
let lfo: f32 = this.lfo.next();
let osc: f32 = this.osc.next();
osc *= env1;
let noiseleft: f32 = this.noise.next() * noiseenv;
let noiseright: f32 = this.noise.next() * noiseenv;
noiseleft = this.hpfilterl.process(noiseleft);
noiseright = this.hpfilterr.process(noiseright);
const pan = this._note / 127;
this.bandpass.update_frequencies(300,2000 + 1300 * lfo);
osc = this.bandpass.process(osc);
osc = this.shaper.process(osc);
let left = osc * pan;
let right = osc * (1 - pan);
this.signal.left = left + noiseleft;
this.signal.right = right + noiseright;
}
}
const snare = new Snare();
const kick = new Kick();
const bass = new Bass();
const sinelead = new SineLead();
const pads: SoftPad[] = createInstrumentArray<SoftPad>(6, () => new SoftPad());
export function setChannelValue(channel: usize, value: f32): void {
const channelfunctions:usize[] = [
changetype<usize>((val: f32): void => { kick.note = val; }),
changetype<usize>((val: f32): void => { snare.note = val; }),
changetype<usize>((val: f32): void => { bass.note = val; }),
changetype<usize>((val: f32): void => { sinelead.note = val; }),
changetype<usize>((val: f32): void => { pads[0].note = val; }),
changetype<usize>((val: f32): void => { pads[1].note = val; }),
changetype<usize>((val: f32): void => { pads[2].note = val; }),
changetype<usize>((val: f32): void => { pads[3].note = val; }),
changetype<usize>((val: f32): void => { pads[4].note = val; }),
changetype<usize>((val: f32): void => { pads[5].note = val; }),
];
changetype<(val: f32) => void>(
channelfunctions[channel]
)(value);
}
// main line
const mainline = new StereoSignal();
// reverb
const reverbline = new StereoSignal();
const freeverb = new Freeverb();
// echo
const delayframes = (SAMPLERATE * (6/8) * 60 / 100) as usize;
const delayLeft: DelayLine = new DelayLine(delayframes);
const delayRight: DelayLine = new DelayLine(delayframes);
const echoline = new StereoSignal();
// equalizer
const tribandeqleft = new TriBandEQ(20,400,6500,19500);
const tribandeqright = new TriBandEQ(20,400,6500,19500);
export function mixernext(leftSampleBufferPtr: usize, rightSampleBufferPtr: usize): void {
mainline.clear();
reverbline.clear();
echoline.clear();
snare.next();
mainline.addStereoSignal(snare.signal, 0.5, 0.5);
kick.next();
mainline.addStereoSignal(kick.signal, 0.5, 0.5);
bass.next();
mainline.addStereoSignal(bass.signal, 0.5, 0.5);
sinelead.next();
mainline.addStereoSignal(sinelead.signal, 0.5, 0.5);
echoline.addStereoSignal(sinelead.signal, 0.5, 0.5);
pads.forEach(pad => {
pad.next();
mainline.addStereoSignal(pad.signal, 0.45, 0.5);
echoline.addStereoSignal(pad.signal, 0.3, 0.5);
});
// process echo
echoline.left += delayRight.read() * 0.7;
echoline.right += delayLeft.read() * 0.7;
delayLeft.write_and_advance(echoline.left);
delayRight.write_and_advance(echoline.right);
// process reverb
freeverb.tick(reverbline);
let left = mainline.left + reverbline.left + echoline.left;
let right = mainline.right + reverbline.right + echoline.right;
left = tribandeqleft.process(left,1.0, 1.0, 1.0);
right = tribandeqright.process(right,1.0, 1.0, 1.0);
store<f32>(leftSampleBufferPtr, left);
store<f32>(rightSampleBufferPtr, right);
}
global.bpm = 100;
global.pattern_size_shift = 4;
addInstrument('kick', {type: 'number'});
addInstrument('snare', {type: 'number'});
addInstrument('bass', {type: 'note'});
addInstrument('sinelead', {type: 'note'});
const padVoices = [];
for(let n=1;n<=6;n++) {
padVoices.push('pad'+n);
addInstrument('pad'+n, {type: 'note'});
}
addInstrumentGroup('pads', padVoices);
playPatterns({
pads: pp(4, [[c6,e6],,,[e6,a6]], 4),
bass: pp(4, [a2,,a2,,a3,,a2]),
kick: pp(4, [
100,,,,
,,80,,
100,,,30,
,,,,
]),
snare: pp(4, [
,,,,
100,,,,
,,,,
100
])
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment