Created
September 29, 2020 20:04
-
-
Save petersalomonsen/068e011b94f9d6e2372b0d6fa756e831 to your computer and use it in GitHub Desktop.
AssemblyScript midi synth starter template - https://petersalomonsen.com/webassemblymusic/livecodev2/?gist=068e011b94f9d6e2372b0d6fa756e831
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
/** | |
* Copyright 2020 - Peter Johan Salomonsen | |
*/ | |
setBPM(120); | |
addInstrument('pad'); | |
addInstrument('drums'); | |
addInstrument('sawtap'); | |
addInstrument('distlead'); | |
addInstrument('bass'); | |
addInstrument('organ'); | |
// set reverb | |
createTrack(6).steps(4, [ | |
controlchange(91, 50) | |
,,,,]); | |
// midi recording of organ | |
createTrack(5).play([[ 3.07, b5(0.86, 89) ], | |
[ 3.06, g5(0.96, 88) ], | |
[ 3.07, d5(4.30, 87) ], | |
[ 3.99, a5(3.59, 64) ], | |
[ 4.00, fs5(3.60, 76) ], | |
[ 11.01, d6(0.06, 84) ], | |
[ 11.03, ds6(0.12, 83) ], | |
[ 11.03, g6(0.44, 83) ], | |
[ 11.17, e6(0.50, 79) ], | |
[ 7.60, c5(4.55, 79) ], | |
[ 7.59, e5(4.60, 68) ], | |
[ 7.59, g5(4.62, 72) ], | |
[ 11.99, g6(0.31, 93) ], | |
[ 11.98, d6(0.38, 84) ], | |
[ 13.01, as5(0.14, 102) ], | |
[ 13.04, d6(0.35, 84) ], | |
[ 13.12, b5(0.43, 72) ], | |
[ 13.92, a5(0.52, 98) ], | |
[ 13.91, d6(0.58, 98) ], | |
[ 14.44, g5(0.16, 69) ], | |
[ 14.97, e5(0.56, 87) ], | |
[ 15.50, d5(0.35, 55) ], | |
[ 14.98, g5(0.89, 83) ]]); | |
// midi recording of distortion synth | |
createTrack(3).play([[ 0.02, g2(0.45, 62) ], | |
[ 0.55, g3(0.42, 63) ], | |
[ 0.54, g4(0.45, 57) ], | |
[ 1.05, g2(0.39, 59) ], | |
[ 1.01, d5(0.53, 70) ], | |
[ 1.54, g5(0.43, 90) ], | |
[ 1.57, g3(0.41, 65) ], | |
[ 2.03, g2(0.45, 60) ], | |
[ 2.01, g4(0.62, 64) ], | |
[ 2.53, g3(0.44, 64) ], | |
[ 2.53, d5(0.44, 64) ], | |
[ 3.03, g2(0.41, 68) ], | |
[ 2.99, g5(0.51, 87) ], | |
[ 3.50, g3(0.26, 72) ], | |
[ 3.52, g4(0.43, 67) ], | |
[ 4.00, d3(0.37, 70) ], | |
[ 4.01, g5(0.43, 88) ], | |
[ 4.43, d4(0.48, 62) ], | |
[ 4.42, a4(0.58, 64) ], | |
[ 4.99, d3(0.38, 70) ], | |
[ 4.97, d5(0.46, 60) ], | |
[ 5.47, d4(0.41, 68) ], | |
[ 5.45, g5(0.47, 79) ], | |
[ 5.96, d3(0.36, 74) ], | |
[ 5.92, a4(0.82, 59) ], | |
[ 6.40, d4(0.48, 67) ], | |
[ 6.43, d5(0.54, 55) ], | |
[ 6.97, d3(0.33, 75) ], | |
[ 6.94, a5(0.42, 90) ], | |
[ 7.41, d4(0.18, 67) ], | |
[ 7.37, a4(0.52, 72) ], | |
[ 7.92, a5(0.27, 89) ], | |
[ 7.91, a2(0.45, 74) ], | |
[ 8.48, a3(0.46, 58) ], | |
[ 8.50, g4(0.50, 59) ], | |
[ 9.02, a2(0.39, 68) ], | |
[ 8.96, c5(0.52, 72) ], | |
[ 9.50, a3(0.49, 67) ], | |
[ 9.47, g5(0.53, 89) ], | |
[ 10.05, a2(0.43, 70) ], | |
[ 10.01, g4(0.92, 74) ], | |
[ 10.54, a3(0.45, 69) ], | |
[ 10.52, c5(0.53, 68) ], | |
[ 11.03, a2(0.35, 67) ], | |
[ 11.01, g5(0.49, 83) ], | |
[ 11.48, a3(0.39, 70) ], | |
[ 11.49, g4(0.45, 75) ], | |
[ 11.98, c3(0.41, 70) ], | |
[ 12.00, g5(0.46, 84) ], | |
[ 12.47, c4(0.54, 58) ], | |
[ 12.46, g4(0.57, 64) ], | |
[ 13.04, c3(0.37, 74) ], | |
[ 13.01, c5(0.54, 77) ], | |
[ 13.48, g5(0.21, 74) ], | |
[ 13.54, c4(0.45, 60) ], | |
[ 14.04, c3(0.42, 77) ], | |
[ 14.02, c6(0.51, 79) ], | |
[ 14.52, c4(0.48, 69) ], | |
[ 14.48, b5(0.59, 77) ], | |
[ 15.04, c3(0.41, 74) ], | |
[ 15.00, a5(0.48, 90) ], | |
[ 15.51, c4(0.38, 77) ], | |
[ 15.47, g5(0.45, 93) ]].quantize(4)); | |
// drums | |
await createTrack(1).steps(4, [ | |
c5,,,,d5,,,, | |
c5,,,,d5,,,, | |
c5,,,,d5,,,, | |
c5,,,,d5,,d5,, | |
c5,,,,d5,,,, | |
c5,,,,d5,,,, | |
c5,,,,d5,,,, | |
c5,,,,d5,d5,d5,, | |
]); | |
loopHere(); |
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 { cos, outputline, FFT, TriBandEQ, Pan, SineOscillator, IFFTOscillator, BiQuadFilter, FilterType, Q_BUTTERWORTH, Noise, DelayLine, BandPass,SawOscillator,softclip, midichannels, MidiChannel, MidiVoice, StereoSignal, Freeverb, SineOscillator, Envelope, notefreq } from '../mixes/globalimports'; | |
import { SAMPLERATE } from '../environment'; | |
const delayframes = (SAMPLERATE * (6/8) * 60 / 100) as usize; | |
let delayLeft: DelayLine = new DelayLine(delayframes); | |
let delayRight: DelayLine = new DelayLine(delayframes); | |
const echoline = new StereoSignal(); | |
const eqleft = new TriBandEQ(20, 600, 6000, 19000); | |
const eqright = new TriBandEQ(20, 600, 6000, 19000); | |
export class Hihat extends MidiVoice { | |
private velocity: f32; | |
readonly envelope: Envelope = new Envelope(0.0, 0.07, 0, 0.1); | |
readonly noise: Noise = new Noise(); | |
readonly filter: BiQuadFilter = new BiQuadFilter(); | |
readonly signal: StereoSignal = new StereoSignal(); | |
constructor(channel: MidiChannel) { | |
super(channel); | |
this.minnote = 66; | |
this.maxnote = 66; | |
} | |
noteon(note: u8, velocity: u8):void { | |
if(note > 1) { | |
this.velocity = velocity as f32 / 32; | |
this.envelope.attack(); | |
} | |
} | |
noteoff(): void { | |
this.envelope.release(); | |
} | |
isDone(): boolean { | |
return this.envelope.isDone(); | |
} | |
nextframe(): void { | |
let env: f32 = this.envelope.next(); | |
let osc: f32 = this.noise.next(); | |
let signal = this.velocity * 2 * env * osc; | |
this.filter.update_coeffecients(FilterType.HighPass, SAMPLERATE, | |
10000 + 2000 * env, Q_BUTTERWORTH); | |
signal = this.filter.process(signal); | |
this.channel.signal.add(signal, signal); | |
} | |
} | |
export class Kick2 extends MidiVoice { | |
private velocity: f32; | |
readonly noise: Noise = new Noise(); | |
readonly env2: Envelope = new Envelope(0.001, 0.01, 0.0, 0.0); | |
readonly bp2: BandPass = new BandPass(4000, 5000); | |
readonly env3: Envelope = new Envelope(0.001, 0.2, 0.1, 0.1); | |
readonly bp3: BandPass = new BandPass(10, 100); | |
constructor(channel: MidiChannel) { | |
super(channel); | |
this.minnote = 60; | |
this.maxnote = 60; | |
} | |
noteon(note: u8, velocity: u8): void { | |
super.noteon(note, velocity); | |
this.velocity = (velocity as f32) / 40; | |
this.env2.attack(); | |
this.env3.attack(); | |
} | |
noteoff(): void { | |
this.env2.release(); | |
this.env3.release(); | |
} | |
isDone(): boolean { | |
return this.env3.isDone(); | |
} | |
nextframe(): void { | |
let env2: f32 = this.env2.next(); | |
let env3: f32 = this.env3.next(); | |
const osc: f32 = this.noise.next(); | |
const sig = this.velocity * ( | |
this.bp2.process(osc) * env2 + | |
this.bp3.process(osc) * env3 * 8); | |
this.channel.signal.add(sig, sig); | |
} | |
} | |
class SawSpread extends MidiVoice { | |
osc: SawOscillator = new SawOscillator(); | |
osc2: SawOscillator = new SawOscillator(); | |
osc3: SawOscillator = new SawOscillator(); | |
osc4: SawOscillator = new SawOscillator(); | |
osc5: SawOscillator = new SawOscillator(); | |
env: Envelope = new Envelope(0.02, 0.4, 0.5, 1.0); | |
bp2: BandPass = new BandPass(20, 5000); | |
noteon(note: u8, velocity: u8): void { | |
super.noteon(note, velocity); | |
const adjustednote = note; | |
this.osc.frequency = notefreq(adjustednote); | |
this.osc2.frequency = notefreq(adjustednote as f32 + 0.1); | |
this.osc3.frequency = notefreq(adjustednote as f32 - 0.1); | |
this.osc4.frequency = notefreq(adjustednote as f32 + 0.15); | |
this.osc5.frequency = notefreq(adjustednote as f32 - 0.15); | |
this.env.attack(); | |
} | |
noteoff(): void { | |
this.env.release(); | |
} | |
isDone(): boolean { | |
return this.env.isDone(); | |
} | |
nextframe(): void { | |
const osc1 = this.osc.next(); | |
const osc2 = this.osc2.next(); | |
const osc3 = this.osc3.next(); | |
const osc4 = this.osc4.next(); | |
const osc5 = this.osc5.next(); | |
const left = this.bp2.process(( | |
osc1 + osc2 + osc3 * 0.5 + osc4 | |
) | |
* this.env.next() | |
* this.velocity * 0.01); | |
const right = this.bp2.process(( | |
osc1 + osc2 *0.5 + osc3 + osc5 | |
) | |
* this.env.next() | |
* this.velocity * 0.01); | |
this.channel.signal.add(left, right); | |
} | |
} | |
class SawTap extends MidiVoice { | |
osc: SawOscillator = new SawOscillator(); | |
env: Envelope = new Envelope(0.001, 0.1, 0.05, 0.1); | |
noteon(note: u8, velocity: u8): void { | |
super.noteon(note, velocity); | |
const adjustednote = note -12; | |
this.osc.frequency = notefreq(adjustednote); | |
this.env.attack(); | |
} | |
noteoff(): void { | |
this.env.release(); | |
} | |
isDone(): boolean { | |
return this.env.isDone(); | |
} | |
nextframe(): void { | |
const signal = this.osc.next() * this.env.next() | |
* this.velocity * 0.01; | |
this.channel.signal.add(signal, signal); | |
} | |
} | |
class SawDist extends MidiVoice { | |
osc: SawOscillator = new SawOscillator(); | |
osc2: SawOscillator = new SawOscillator(); | |
env: Envelope = new Envelope(0.01, 0.5, 0.7, 0.1); | |
noteon(note: u8, velocity: u8): void { | |
super.noteon(note, velocity); | |
this.osc.frequency = notefreq(note as f32-0.07); | |
this.osc2.frequency = notefreq(note as f32+0.07); | |
this.env.attack(); | |
} | |
noteoff(): void { | |
this.env.release(); | |
} | |
isDone(): boolean { | |
return this.env.isDone(); | |
} | |
nextframe(): void { | |
const signal = (this.osc.next() + this.osc2.next()) * | |
this.env.next() | |
* this.velocity * 0.01; | |
this.channel.signal.left += signal; | |
} | |
} | |
class SawTapChannel extends MidiChannel { | |
feedback: f32 = 0; | |
preprocess(): void { | |
let signal = this.signal.left * 0.25; | |
echoline.add(signal, signal); | |
} | |
} | |
class SawDistChannel extends MidiChannel { | |
feedback: f32 = 0; | |
preprocess(): void { | |
let signal = this.signal.left + this.feedback; | |
signal = softclip(signal *3) * 0.5; | |
this.feedback = signal * 0.8; | |
signal*=0.3; | |
this.signal.left = signal; | |
this.signal.right = signal; | |
echoline.add(signal, signal); | |
} | |
} | |
export class Snare2 extends MidiVoice { | |
private velocity: f32; | |
readonly noise: Noise = new Noise(); | |
readonly env1: Envelope = new Envelope(0.001, 0.4, 0.0, 0.3); | |
readonly bp1: BandPass = new BandPass(200, 350); | |
readonly env2: Envelope = new Envelope(0.001, 0.08, 0.00, 0.2); | |
readonly bp2: BandPass = new BandPass(3000, 8000); | |
readonly env3: Envelope = new Envelope(0.001, 0.05, 0.00, 0.1); | |
readonly bp3: BandPass = new BandPass(10, 150); | |
constructor(channel: MidiChannel) { | |
super(channel); | |
this.minnote = 62; | |
this.maxnote = 62; | |
} | |
noteon(note: u8, velocity: u8): void { | |
super.noteon(note, velocity); | |
this.velocity = velocity as f32 / 64; | |
this.env1.attack(); | |
this.env2.attack(); | |
this.env3.attack(); | |
} | |
noteoff(): void { | |
this.env1.release(); | |
this.env2.release(); | |
this.env3.release(); | |
} | |
isDone(): boolean { | |
return this.env1.isDone(); | |
} | |
nextframe(): 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 * 6; | |
let sig2 = this.bp2.process(osc) * env2 * 2; | |
let sig3 = this.bp3.process(osc) * env3 * 4; | |
this.channel.signal.add( | |
this.velocity * (sig1 + sig2 * 0.8 + sig3), | |
this.velocity * (sig1 * 0.8 + sig2 + sig3) | |
); | |
} | |
} | |
export class SawBass3 extends MidiVoice { | |
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(); | |
noteon(note: u8, velocity: u8): void { | |
super.noteon(note, velocity); | |
this.sawoscillator.frequency = notefreq(note); | |
this.envelope.attack(); | |
this.filterenv.attack(); | |
} | |
noteoff(): void { | |
this.envelope.release(); | |
this.filterenv.release(); | |
} | |
nextframe(): 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.channel.signal.add(signal * 2 + band1, | |
signal * 2 - band1); | |
} | |
} | |
export class Organ extends MidiVoice { | |
readonly envelope: Envelope = new Envelope(0.01, 0.8, 0.5, 0.1); | |
readonly lfo: SineOscillator = new SineOscillator(); | |
readonly lfo2: SineOscillator = new SineOscillator(); | |
readonly lfo3: SineOscillator = new SineOscillator(); | |
readonly oscleft: IFFTOscillator = new IFFTOscillator(4); | |
readonly oscright: IFFTOscillator = new IFFTOscillator(4); | |
pan: Pan = new Pan(); | |
framecount: i32 = 0; | |
noteon(note: u8, velocity: u8): void { | |
super.noteon(note, velocity); | |
this.oscleft.frequency = notefreq(note); | |
this.oscright.frequency = notefreq(note); | |
this.lfo.frequency = 64; | |
this.lfo2.frequency = 128; | |
this.lfo3.frequency = 256; | |
this.pan.setPan(note as f32/ 127.0); | |
this.envelope.attack(); | |
} | |
noteoff(): void { | |
this.envelope.release(); | |
} | |
nextframe(): void { | |
const env: f32 = this.envelope.next(); | |
if((this.framecount & 0xff) === 0) { | |
this.oscleft.createWave( | |
[0,0,this.lfo2.next() * env,0], | |
[ | |
env, | |
this.lfo.next()* env, | |
this.lfo2.next() * env, | |
this.lfo3.next() * env | |
]); | |
this.oscleft.fft.calculateInverse(); | |
this.oscright.createWave( | |
[0,0,0,0,this.lfo3.next() * env], | |
[env, | |
this.lfo.next()* env, | |
this.lfo3.next() * env, | |
this.lfo2.next() * env | |
]); | |
this.oscright.fft.calculateInverse(); | |
} | |
this.framecount ++; | |
this.channel.signal.add( | |
0.29 * this.oscleft.next() * this.pan.leftLevel, | |
0.29 * this.oscright.next() * this.pan.rightLevel | |
); | |
} | |
} | |
export function initializeMidiSynth(): void { | |
midichannels[0] = new MidiChannel(10, (ch) => new SawSpread(ch)); | |
midichannels[1] = new MidiChannel(3, (ch, ndx) => { | |
switch(ndx) { | |
case 0: | |
return new Snare2(ch); | |
case 1: | |
return new Hihat(ch); | |
default: | |
return new Kick2(ch); | |
} | |
}); | |
midichannels[2] = new SawTapChannel(2, (ch) => new SawTap(ch)); | |
midichannels[3] = new SawDistChannel(3, (ch) => new SawDist(ch)); | |
midichannels[4] = new MidiChannel(1, (ch) => new SawBass3(ch)); | |
midichannels[5] = new MidiChannel(10, (ch) => new Organ(ch)); | |
} | |
export function postprocess(): void { | |
echoline.left += delayRight.read() * 0.7; | |
echoline.right += delayLeft.read() * 0.7; | |
delayLeft.write_and_advance(echoline.left); | |
delayRight.write_and_advance(echoline.right); | |
outputline.add(echoline.left, echoline.right); | |
outputline.left = eqleft.process(outputline.left, 0.7, 0.6, 0.7); | |
outputline.right = eqright.process(outputline.right, 0.7, 0.6, 0.7); | |
echoline.clear(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment