Last active
September 29, 2020 19:59
-
-
Save petersalomonsen/d19d7574d9fae1fd01330485644837a1 to your computer and use it in GitHub Desktop.
instrumentstemplate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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