Skip to content

Instantly share code, notes, and snippets.

@petersalomonsen
Last active October 12, 2023 16:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save petersalomonsen/bbd4a3ac817d2e777e9f5836f1e23cdf to your computer and use it in GitHub Desktop.
Save petersalomonsen/bbd4a3ac817d2e777e9f5836f1e23cdf to your computer and use it in GitHub Desktop.
setBPM(90);
addInstrument('piano');
addInstrument('string');
addInstrument('drums');
addInstrument('guitar');
addInstrument('bass');
addInstrument('tubelead');
addInstrument('flute');
addInstrument('padsynth');
addInstrument('brass');
addInstrument('choir');
createTrack(0).play([[ 0.05, f2(0.66, 70) ],
[ 0.78, f3(0.13, 43) ],
[ 1.00, a2(0.97, 72) ],
[ 1.98, c3(0.95, 72) ],
[ 0.05, a4(2.97, 92) ],
[ 0.04, f5(2.98, 82) ],
[ 3.74, gs4(0.04, 53) ],
[ 3.74, f5(0.04, 49) ],
[ 3.75, d5(0.04, 60) ],
[ 2.98, d3(0.85, 70) ],
[ 3.94, as2(1.03, 51) ],
[ 5.03, g5(0.43, 89) ],
[ 5.03, gs4(0.45, 88) ],
[ 5.04, d5(0.46, 87) ],
[ 5.04, d3(0.98, 72) ],
[ 6.00, gs4(0.32, 86) ],
[ 5.98, gs5(0.46, 87) ],
[ 6.01, d5(0.50, 79) ],
[ 6.59, g5(0.14, 60) ],
[ 6.60, gs4(0.14, 66) ],
[ 6.03, f3(1.07, 78) ],
[ 7.03, g3(0.82, 59) ],
[ 7.95, f2(0.73, 74) ],
[ 8.73, f3(0.14, 52) ],
[ 8.93, a2(1.02, 60) ],
[ 7.63, a4(2.86, 75) ],
[ 7.62, f5(2.97, 68) ],
[ 10.59, c5(0.20, 49) ],
[ 9.96, c3(0.95, 63) ],
[ 10.93, d5(0.28, 77) ],
[ 11.26, f5(0.22, 82) ],
[ 11.60, g5(0.18, 69) ],
[ 10.95, d3(1.01, 79) ],
[ 11.91, gs5(0.18, 59) ],
[ 12.23, a5(0.32, 37) ],
[ 12.60, c6(0.27, 83) ],
[ 11.97, ds3(0.93, 59) ],
[ 12.96, d6(0.38, 87) ],
[ 13.59, f6(0.21, 88) ],
[ 12.97, d3(1.00, 70) ],
[ 13.96, f6(0.63, 88) ],
[ 14.59, c6(0.06, 64) ],
[ 14.66, d6(0.26, 60) ],
[ 13.98, c3(0.99, 75) ],
[ 14.98, f6(0.47, 94) ],
[ 14.99, a2(0.54, 74) ],
[ 15.58, g6(0.13, 74) ],
[ 15.58, c3(0.17, 68) ],
[ 16.58, d6(0.10, 69) ],
[ 16.55, gs5(0.13, 76) ],
[ 16.54, gs6(0.19, 89) ],
[ 15.94, as2(0.86, 66) ],
[ 16.87, d3(0.05, 55) ],
[ 16.97, d3(0.94, 7) ],
[ 17.94, g6(0.50, 89) ],
[ 18.51, f6(0.04, 60) ],
[ 17.94, gs5(0.66, 77) ],
[ 17.95, f3(0.83, 72) ],
[ 18.86, g3(0.98, 55) ],
[ 19.92, gs5(0.27, 81) ],
[ 19.90, g6(0.34, 95) ],
[ 19.91, d6(0.34, 87) ],
[ 20.61, d6(0.04, 50) ],
[ 20.58, gs5(0.17, 64) ],
[ 19.90, gs3(0.93, 57) ],
[ 20.58, g6(0.26, 72) ],
[ 21.24, gs5(0.17, 66) ],
[ 21.24, f6(0.19, 77) ],
[ 21.54, d6(0.18, 64) ],
[ 21.55, g6(0.18, 65) ],
[ 20.89, g3(0.99, 58) ],
[ 21.96, d6(0.04, 72) ],
[ 21.95, gs5(0.33, 87) ],
[ 22.06, d6(0.26, 12) ],
[ 21.94, gs6(0.38, 102) ],
[ 22.60, d6(0.14, 57) ],
[ 22.61, gs5(0.16, 54) ],
[ 22.62, g6(0.15, 64) ],
[ 21.94, f3(0.91, 69) ],
[ 22.91, d3(0.60, 58) ],
[ 23.59, a5(0.04, 38) ],
[ 23.64, c6(0.04, 15) ],
[ 23.62, f3(0.18, 48) ],
[ 23.64, f6(0.47, 49) ],
[ 23.66, d6(0.94, 83) ],
[ 23.87, f2(0.78, 53) ],
[ 24.63, f3(0.29, 27) ],
[ 24.63, c6(0.30, 57) ],
[ 24.62, f6(0.32, 19) ],
[ 24.97, a5(0.37, 90) ],
[ 25.62, c6(0.06, 62) ],
[ 25.59, gs5(0.19, 77) ],
[ 24.98, a2(0.93, 69) ],
[ 25.94, g5(0.37, 70) ],
[ 25.95, c6(0.35, 59) ],
[ 26.54, f5(0.12, 60) ],
[ 25.98, c3(0.91, 64) ],
[ 26.93, f5(0.26, 57) ],
[ 26.90, d5(0.30, 74) ],
[ 27.55, c5(0.19, 69) ],
[ 26.93, d3(0.95, 79) ],
[ 27.93, a4(0.27, 82) ],
[ 27.55, f5(0.94, 57) ],
[ 28.55, c5(0.04, 43) ],
[ 28.63, d5(0.05, 42) ],
[ 27.92, ds3(1.05, 71) ],
[ 29.02, f5(0.46, 98) ],
[ 29.58, g5(0.15, 82) ],
[ 29.02, d3(0.94, 72) ],
[ 29.92, gs5(0.25, 59) ],
[ 30.22, a5(0.22, 54) ],
[ 30.54, c6(0.38, 74) ],
[ 29.96, c3(1.01, 67) ],
[ 30.87, gs5(0.25, 72) ],
[ 31.24, a5(0.19, 43) ],
[ 30.96, a2(0.60, 69) ],
[ 31.61, c3(0.10, 65) ],
[ 31.53, c6(0.38, 72) ],
[ 31.85, gs5(0.10, 81) ],
[ 31.94, a5(0.45, 72) ],
[ 31.90, c3(0.62, 64) ],
[ 32.56, c4(0.16, 35) ],
[ 32.51, c6(0.56, 75) ],
[ 32.83, gs5(0.52, 83) ],
[ 33.43, g5(0.14, 44) ],
[ 33.58, gs5(0.18, 64) ],
[ 32.84, e3(0.93, 69) ],
[ 33.71, g5(0.72, 78) ],
[ 33.50, c6(0.94, 2) ],
[ 34.46, f5(0.23, 77) ],
[ 33.81, g3(1.01, 65) ],
[ 34.82, d5(0.26, 74) ],
[ 35.16, c5(0.21, 57) ],
[ 35.44, cs5(0.12, 59) ],
[ 34.83, a3(0.86, 79) ],
[ 35.85, as2(1.08, 59) ],
[ 36.95, gs4(0.11, 69) ],
[ 35.53, d5(1.53, 58) ],
[ 36.94, f5(0.14, 60) ],
[ 36.95, d3(1.01, 63) ],
[ 37.96, gs4(0.34, 81) ],
[ 37.95, g5(0.48, 78) ],
[ 37.95, d5(0.62, 79) ],
[ 38.53, f5(0.05, 48) ],
[ 38.50, gs4(0.07, 19) ],
[ 37.98, f3(0.82, 70) ],
[ 38.85, d3(0.74, 59) ],
[ 39.66, f3(0.16, 75) ],
[ 39.91, f2(0.72, 49) ],
[ 40.66, f3(0.23, 54) ],
[ 40.89, a2(0.58, 75) ],
[ 41.66, f3(0.22, 42) ],
[ 41.90, as2(0.73, 61) ],
[ 42.66, f3(0.06, 48) ],
[ 42.88, b2(0.97, 75) ],
[ 39.64, a4(4.28, 68) ],
[ 39.63, f5(4.33, 65) ],
[ 39.63, c5(4.38, 65) ],
[ 43.89, c3(1.06, 72) ],
[ 44.98, a4(0.34, 88) ],
[ 44.98, b2(0.44, 78) ],
[ 44.97, ds5(0.47, 86) ],
[ 45.65, d5(0.02, 27) ],
[ 45.64, as4(2.22, 61) ],
[ 45.67, e5(2.22, 64) ],
[ 45.66, c2(2.25, 69) ]]);
for (let n=0;n<12;n++) {
// get more and more into tune as times go by
createTrack(0).steps(4,[controlchange(30,120 - ((n + 1)*10))]);
await createTrack(2).steps(4,[
cs6(0.1,70),,,,
cs6(0.1,70),,,cs6(0.1,30),
cs6(0.1,60),,,,
cs6(0.1,70),,,cs6(0.1,30),
]);
}
loopHere();
/*
* (c) Peter Johan Salomonsen 2023
*/
import { MidSideProcessor, TriBandStereoCompressor, MonoCompressor, midiLevelToGain, DelayLineFloat, Noise, outputline, CustomExciterWaveGuide, Comb, MonoAudioPlayer, hardclip, AuxExciterWaveGuide, allocateAudioBuffer, LoPassBiQuadFilter, HiPassBiQuadFilter, noise, Freeverb, WaveGuide, AllPassFloat, beatToFrame, AudioPlayer, cos, outputline, Limiter, TriangleOscillator, PI, sin, cos, FFT, EQBand, TriBandEQ, EnvelopeState, Pan, SineOscillator, IFFTOscillator, BiQuadFilter, FilterType, Q_BUTTERWORTH, DelayLine, BandPass,SawOscillator,softclip, midichannels, MidiChannel, MidiVoice, StereoSignal, SineOscillator, Envelope } from '../mixes/globalimports';
import { SAMPLERATE } from '../environment';
const BPM: f32 = 90;
function notefreq(note: f32): f32 {
return 440 * Mathf.pow(2, (-69 + note) / 12);
}
export class MT19937 {
private static readonly N: i32 = 624;
private static readonly M: i32 = 397;
private static readonly MATRIX_A: i32 = 0x9908b0df;
private static readonly UPPER_MASK: i32 = 0x80000000;
private static readonly LOWER_MASK: i32 = 0x7fffffff;
// Constants for mag01 replacement
private static readonly MAG01_0: i32 = 0;
private static readonly MAG01_1: i32 = MT19937.MATRIX_A;
private mt: Uint32Array = new Uint32Array(MT19937.N);
private mti: i32 = MT19937.N + 1;
constructor(seed: u32 = 5489) {
this.init(seed);
}
private init(seed: u32): void {
this.mt[0] = seed >>> 0;
for (this.mti = 1; this.mti < MT19937.N; this.mti++) {
let s = this.mt[this.mti - 1] ^ (this.mt[this.mti - 1] >>> 30);
this.mt[this.mti] =
(((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253) + this.mti;
this.mt[this.mti] >>>= 0;
}
}
public next(): u32 {
let y: u32;
if (this.mti >= MT19937.N) {
let kk: i32;
for (kk = 0; kk < MT19937.N - MT19937.M; kk++) {
y = (this.mt[kk] & MT19937.UPPER_MASK) | (this.mt[kk + 1] & MT19937.LOWER_MASK);
this.mt[kk] = this.mt[kk + MT19937.M] ^ (y >>> 1) ^ ((y & 1) ? MT19937.MAG01_1 : MT19937.MAG01_0);
}
for (; kk < MT19937.N - 1; kk++) {
y = (this.mt[kk] & MT19937.UPPER_MASK) | (this.mt[kk + 1] & MT19937.LOWER_MASK);
this.mt[kk] = this.mt[kk + (MT19937.M - MT19937.N)] ^ (y >>> 1) ^ ((y & 1) ? MT19937.MAG01_1 : MT19937.MAG01_0);
}
y = (this.mt[MT19937.N - 1] & MT19937.UPPER_MASK) | (this.mt[0] & MT19937.LOWER_MASK);
this.mt[MT19937.N - 1] = this.mt[MT19937.M - 1] ^ (y >>> 1) ^ ((y & 1) ? MT19937.MAG01_1 : MT19937.MAG01_0);
this.mti = 0;
}
y = this.mt[this.mti++];
y ^= y >>> 11;
y ^= (y << 7) & 0x9d2c5680;
y ^= (y << 15) & 0xefc60000;
y ^= y >>> 18;
return y >>> 0;
}
// The function to return a float in the range [-1, 1]
random(): f32 {
let y: u32 = this.next();
// Normalize to [0, 1]
let normalized: f32 = <f32>y / 4294967295.0;
// Scale and shift to [-1, 1]
return 2.0 * normalized - 1.0;
}
}
const delayframes = (SAMPLERATE * (6/8) * 60 / BPM) as usize;
const echoLeft = new DelayLine(delayframes);
const echoRight = new DelayLine(delayframes);
const echoline = new StereoSignal();
class MultiBandEQ {
bands: StaticArray<EQBand>;
constructor(freqs: f32[]) {
this.bands = new StaticArray<EQBand>(freqs.length - 1);
for ( let n=1; n<freqs.length; n++) {
this.bands[n-1] = new EQBand(freqs[n-1], freqs[n]);
}
}
process(signal: f32, levels: f32[]): f32 {
let ret: f32 = 0;
for (let n = 0;n<this.bands.length; n++) {
ret += this.bands[n].process(signal) * levels[n];
}
return ret;
}
}
export class WaveGuideFeedbackLimit {
envExciter: Envelope;
filterExciter: BiQuadFilter = new BiQuadFilter();
delay: DelayLineFloat = new DelayLineFloat((SAMPLERATE / notefreq(1)) as i32);
filterFeedback: BiQuadFilter = new BiQuadFilter();
feedbackLevel: f32;
feedbackFilterFreq: f32;
freq: f32;
exciterenvlevel: f32;
constructor(exciterAttack: f32, exciterRelease: f32, exciterFilterFreq: f32, feedbackLevel: f32) {
this.envExciter = new Envelope(exciterAttack,
exciterRelease, 0,
exciterRelease);
this.filterExciter.update_coeffecients(FilterType.LowPass, SAMPLERATE,
exciterFilterFreq, Q_BUTTERWORTH);
this.feedbackLevel = feedbackLevel;
}
setFilterExciterFreq(freq: f32): void {
this.filterExciter.update_coeffecients(FilterType.LowPass, SAMPLERATE,
freq, Q_BUTTERWORTH);
}
start(freq: f32, feedbackFilterFreq: f32): void {
if (freq != this.freq) {
this.freq = freq;
const maxFeedbackFilterFreq: f32 = 20000;
if (feedbackFilterFreq > maxFeedbackFilterFreq as f32) {
feedbackFilterFreq = maxFeedbackFilterFreq as f32;
} else if (feedbackFilterFreq < 10) {
feedbackFilterFreq = 10;
}
this.filterFeedback.update_coeffecients(FilterType.LowPass, SAMPLERATE,
feedbackFilterFreq, Q_BUTTERWORTH);
this.filterFeedback.coeffs.calculatePhaseAndMagnitudeForFreq(freq);
const filterphase = this.filterFeedback.coeffs.phaseSamples;
this.filterFeedback.clearBuffers();
this.filterExciter.clearBuffers();
this.feedbackFilterFreq = feedbackFilterFreq;
this.delay.setNumFramesAndClear(
(SAMPLERATE /
(freq)
) - filterphase
);
this.envExciter.val = 0;
}
this.exciterenvlevel = 1;
this.envExciter.attack();
}
process(): f32 {
const envexciter = this.envExciter.next() * this.exciterenvlevel;
let exciterSignal: f32 = noise() * envexciter;
exciterSignal = this.filterExciter.process(exciterSignal);
const feedback = this.delay.read();
let signal = exciterSignal + feedback;
signal = this.filterFeedback.processForm2(signal);
this.delay.write_and_advance(
Mathf.tanh(signal * this.feedbackLevel)
);
return signal;
}
}
export class BandPassWaveGuide {
envExciter: Envelope;
filterExciter: BiQuadFilter = new BiQuadFilter();
delay: DelayLineFloat = new DelayLineFloat((SAMPLERATE / notefreq(1)) as i32);
hipass: HiPassBiQuadFilter = new HiPassBiQuadFilter();
filterFeedback: BiQuadFilter = new BiQuadFilter();
feedbackLevel: f32;
feedbackFilterFreq: f32;
freq: f32;
exciterenvlevel: f32;
constructor(exciterAttack: f32, exciterRelease: f32, exciterFilterFreq: f32, feedbackLevel: f32) {
this.envExciter = new Envelope(exciterAttack,
exciterRelease, 0,
exciterRelease);
this.filterExciter.update_coeffecients(FilterType.LowPass, SAMPLERATE,
exciterFilterFreq, Q_BUTTERWORTH);
this.feedbackLevel = feedbackLevel;
}
setFilterExciterFreq(freq: f32): void {
this.filterExciter.update_coeffecients(FilterType.LowPass, SAMPLERATE,
freq, Q_BUTTERWORTH);
}
start(freq: f32, feedbackFilterFreq: f32): void {
freq*=1.6;
if (freq != this.freq) {
this.freq = freq;
const maxFeedbackFilterFreq: f32 = 20000;
if (feedbackFilterFreq > maxFeedbackFilterFreq as f32) {
feedbackFilterFreq = maxFeedbackFilterFreq as f32;
} else if (feedbackFilterFreq < 10) {
feedbackFilterFreq = 10;
}
this.filterFeedback.update_coeffecients(FilterType.LowPass, SAMPLERATE,
feedbackFilterFreq, Q_BUTTERWORTH);
this.hipass.update(freq * 0.5 , 0.93);
this.hipass.coeffs.calculatePhaseAndMagnitudeForFreq(freq);
this.filterFeedback.coeffs.calculatePhaseAndMagnitudeForFreq(freq);
const filterphase = this.filterFeedback.coeffs.phaseSamples +
this.hipass.coeffs.phaseSamples;
this.hipass.clearBuffers();
this.filterFeedback.clearBuffers();
this.filterExciter.clearBuffers();
this.feedbackFilterFreq = feedbackFilterFreq;
this.delay.setNumFramesAndClear(
(SAMPLERATE /
(freq)
) - filterphase
);
this.envExciter.val = 0;
}
this.exciterenvlevel = 1;
this.envExciter.attack();
}
process(): f32 {
const envexciter = this.envExciter.next() * this.exciterenvlevel;
let exciterSignal: f32 = noise() * envexciter;
exciterSignal = this.filterExciter.process(exciterSignal);
const feedback = this.delay.read();
let signal = exciterSignal + feedback;
signal = this.filterFeedback.processForm2(signal);
signal = this.hipass.process(signal);
this.delay.write_and_advance(
Mathf.tanh(signal * this.feedbackLevel)
);
return signal;
}
}
const noisegen: MT19937 = new MT19937();
const exciterStartVal = Mathf.exp(Mathf.E);
export class PianoExciterWaveGuide {
filterExciter: BiQuadFilter = new BiQuadFilter();
t: f32;
vel: f32;
start(lo: f32,notefreq: f32): void {
this.t = 0;
this.vel = lo;
this.filterExciter.clearBuffers();
const filterbasefreq: f32 = (800 + 20 * Mathf.pow(notefreq,0.65)) * this.vel;
this.filterExciter.update_coeffecients(FilterType.LowPass, SAMPLERATE,
filterbasefreq, 0.9999);
}
process(): f32 {
this.t += 12.0/ SAMPLERATE;
let signal = noisegen.random() * Mathf.exp(Mathf.E-this.t);
signal = this.filterExciter.process(signal);
return signal / exciterStartVal;
}
}
const trt = Math.pow(2.0,1.0/12.0); // 12th root of 2
const logb = (b: f64,x: f64): f64 => Math.log(x) / Math.log(b); // log-base-b of x
const Ikey = (f0: f64): f64 => logb(trt,f0*trt/27.5);
class PianoWaveGuide extends WaveGuide {
exciterWaveGuide: PianoExciterWaveGuide = new PianoExciterWaveGuide();
allpasses: StaticArray<AllPassFloat> = new StaticArray<AllPassFloat>(5);
// Two delay lines for bidirectional wave propagation
delayForward: DelayLineFloat = new DelayLineFloat((SAMPLERATE / notefreq(1)) as i32);
delayBackward: DelayLineFloat = new DelayLineFloat((SAMPLERATE / notefreq(1)) as i32);
dispersionmodindex: f64 = 0;
dispersiondelta: f32;
dispersion: i32 = 0;
level: f32;
notefreq: f32;
numdispersionfilters: i32 = 6;
constructor() {
super(0.001,0.06,100,0.2);
for(let n=0;n<this.allpasses.length;n++) {
this.allpasses[n] = new AllPassFloat();
}
}
start2(note: f32, velocity: u8): void {
const velocitylevel = (velocity as f32 / 127);
const freq: f32 = notefreq(note);
this.notefreq = freq;
this.exciterWaveGuide.start(velocitylevel , freq);
let feedbackFilterFreq: f32 = 3500 + 40 * Mathf.pow(freq,0.79);
this.filterFeedback.update_coeffecients(FilterType.LowPass, SAMPLERATE, feedbackFilterFreq, Q_BUTTERWORTH);
if (freq != this.freq) {
this.filterFeedback.coeffs.calculatePhaseAndMagnitudeForFreq(freq);
const delaysize: f32 = (SAMPLERATE /
(freq)
)
- this.filterFeedback.coeffs.phaseSamples;
const B = ((this.dispersion as f32) * 0.0002) + 0.001;
const M = this.allpasses.length;
let totalDispersion = 0.0;
this.numdispersionfilters = 0;
for(let n=0;n<this.allpasses.length;n++) {
this.allpasses[n].clearBuffers();
const f0: f64 = freq * (n+1.0) as f64;
if (f0 > SAMPLERATE / 3) {
continue;
}
const Bc = Math.max(B,0.0001);
const k1 = -0.00179;
const k2 = -0.0233;
const k3 = -2.93;
const m1 = 0.0126;
const m2 = 0.0606;
const m3 = -0.00825;
const m4 = 1.97;
const wT = 2*Math.PI*f0/SAMPLERATE;
const kd = Math.exp(k1*Math.log(Bc)*Math.log(Bc) + k2*Math.log(Bc)+k3);
const Cd = Math.exp((m1*Math.log(M)+m2)*Math.log(Bc)+m3*Math.log(M)+m4);
const polydel = (a: f64, wT: f64): f64 => Math.atan(Math.sin(wT)/(a+Math.cos(wT)))/wT;
const D = Math.exp(Cd - Ikey(f0)*kd);
const a1 = (1-D)/(1+D); // By Eq. 3, have D >= 0, hence a1 >= 0 also
const Df0 = polydel(a1, wT) - polydel(1.0/a1, wT);
totalDispersion += Df0;
const dispersion: f32 = Df0 as f32;
this.allpasses[n].setDelta(dispersion);
this.numdispersionfilters++;
}
this.filterFeedback.clearBuffers();
const totalDelay = delaysize - (totalDispersion as f32);
this.delayForward.setNumFramesAndClear(totalDelay);
this.delayBackward.setNumFramesAndClear(totalDelay);
let feedbacklevel: f32 = Mathf.pow(0.26/ this.filterFeedback.coeffs.magnitude, 1 / freq);
this.feedbackLevel = feedbacklevel;
this.freq = freq;
}
}
process(): f32 {
let exciterSignal: f32 = this.exciterWaveGuide.process();
let feedbackFwd = this.delayForward.read();
let feedbackBwd = this.delayBackward.read();
feedbackFwd = this.filterFeedback.process(feedbackFwd);
let signal = (feedbackFwd + feedbackBwd) * 0.5 + exciterSignal;
for(let n=0;n<this.numdispersionfilters;n++) signal = this.allpasses[n].process(signal);
signal *= this.feedbackLevel;
this.delayForward.write_and_advance(signal);
this.delayBackward.write_and_advance(signal);
return signal;
}
}
class Piano extends MidiVoice {
env: Envelope = new Envelope(0.005, 0.17, 1.0, 0.2);
waveguide1: PianoWaveGuide = new PianoWaveGuide();
waveguide2: PianoWaveGuide = new PianoWaveGuide();
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
this.waveguide1.start2(note * 1.0 as f32, velocity);
this.waveguide2.start2(note * 1.0 as f32, velocity);
this.env.attack();
}
noteoff(): void {
this.env.release();
}
isDone(): boolean {
return this.env.isDone();
}
nextframe(): void {
let env = this.env.next();
this.waveguide1.dispersion = this.channel.controllerValues[30];
this.waveguide2.dispersion = this.channel.controllerValues[30];
const wg1: f32 = this.waveguide1.process() * env;
const wg2: f32 = this.waveguide2.process() * env;
this.channel.signal.add(wg1, wg2);
}
}
const numpianofilters = 12;
class PianoChannel extends MidiChannel {
allpassesLeft: StaticArray<AllPassFloat> = new StaticArray<AllPassFloat>(numpianofilters);
allpassesRight: StaticArray<AllPassFloat> = new StaticArray<AllPassFloat>(numpianofilters);
constructor(numvoices: i32, factoryFunc: (channel: MidiChannel, voiceindex: i32) => MidiVoice) {
super(numvoices, factoryFunc);
let delta: f32 = 4.9;
for(let n=0;n<this.allpassesLeft.length;n++) {
this.allpassesLeft[n] = new AllPassFloat();
this.allpassesLeft[n].setDelta(delta);
this.allpassesRight[n] = new AllPassFloat();
this.allpassesRight[n].setDelta(delta);
delta *= 0.9999;
}
}
preprocess(): void {
const in_gain: f32 = 4.0 * Mathf.pow(10.0, -3);
let left = this.signal.left * in_gain;
let right = this.signal.right * in_gain;
const spread: f32 = 0.90;
for (let n=0;n<this.allpassesLeft.length;n++) {
left += this.allpassesLeft[n].process(left - right * spread);
left *= 0.5;
right += this.allpassesRight[n].process(right - left * spread);
right *= 0.5;
}
this.signal.left = left;
this.signal.right = right;
}
}
class String extends MidiVoice {
env: Envelope = new Envelope(0.001, 1, 0.9, 0.3);
waveguide1: WaveGuide = new WaveGuide(0.01, 20.0, 610, 0.995);
waveguide2: WaveGuide = new WaveGuide(0.01, 20.0, 620, 0.995);
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
const freq = notefreq(note);
this.waveguide1.start(freq, freq * 4000 / Mathf.pow(note, 1.3) );
this.waveguide2.start(freq, freq * 4000 / Mathf.pow(note, 1.3) );
this.env.attack();
}
noteoff(): void {
this.env.release();
this.waveguide1.envExciter.releaseStep = 0.05;
this.waveguide2.envExciter.releaseStep = 0.05;
this.waveguide1.envExciter.release();
this.waveguide2.envExciter.release();
}
isDone(): boolean {
return this.env.isDone() &&
this.waveguide1.envExciter.isDone() &&
this.waveguide2.envExciter.isDone();
}
nextframe(): void {
const env:f32 = this.env.next() * this.velocity * 0.01;
const left =
this.waveguide1.process()
* env;
const right =
this.waveguide2.process()
* env;
this.channel.signal.add(
left, right
);
}
}
class Brass extends MidiVoice {
env: Envelope = new Envelope(0.01, 1.0, 1.0, 0.1);
waveguide1: WaveGuide = new WaveGuide(0.02, 0.15, 1000, 0.99999);
waveguide2: WaveGuide = new WaveGuide(0.03, 0.2, 5000, 1.0);
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
const freq = notefreq(note);
this.waveguide1.start(freq, freq * 10);
this.waveguide2.start(freq, freq * 8);
this.env.attack();
}
noteoff(): void {
this.env.release();
}
isDone(): boolean {
return this.env.isDone();
}
nextframe(): void {
const env = this.env.next();
let signal = (
this.waveguide1.process() +
this.waveguide2.process()
)
* env * this.velocity / 127 as f32;
this.channel.signal.add(
signal, signal
);
}
}
class Guitar extends MidiVoice {
env: Envelope = new Envelope(0.002, 1, 1.0, 0.1);
waveguide1: WaveGuide = new WaveGuide(0.002, 0.03, 2000, 0.995);
waveguide2: WaveGuide = new WaveGuide(0.002, 0.03, 2000, 0.995);
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
const freq = notefreq(note);
this.waveguide1.start(freq * 1.001, freq * 5000 / Mathf.pow(note, 1.28) );
this.waveguide2.start(freq * 0.999, freq * 5000 / Mathf.pow(note, 1.28) );
this.env.attack();
}
noteoff(): void {
this.env.release();
}
isDone(): boolean {
return this.env.isDone();
}
nextframe(): void {
const env = this.env.next();
const signal = ((
this.waveguide1.process()
)
* env * this.velocity * 0.04) as f32;
const signal2 = ((
this.waveguide2.process()
)
* env * this.velocity * 0.04) as f32;
this.channel.signal.add(
signal, signal2
);
}
}
class GuitarChannel extends MidiChannel {
bandpassleft: BandPass = new BandPass(150,4000);
bandpassright: BandPass = new BandPass(150,4000);
feedbackleft: f32 = 0;
feedbackright: f32 = 0;
preprocess(): void {
let left = this.signal.left;
let right = this.signal.right;
const feedbackleft = this.feedbackleft;
const feedbackright = this.feedbackleft;
left = softclip(left * 0.8);
right = softclip(right * 0.8);
this.feedbackleft += this.bandpassleft.process(left * 1.5);
this.feedbackright += this.bandpassright.process(right * 1.5);
left += feedbackleft;
left *= 0.5;
right += feedbackright;
right *= 0.5;
const echoamount = midiLevelToGain(this.controllerValues[30]) * 0.3;
echoline.left += (left * echoamount);
echoline.right += (right * echoamount);
this.signal.left = left;
this.signal.right = right;
}
}
class Bass extends MidiVoice {
env: Envelope = new Envelope(0.001, 1, 1.0, 0.1);
waveguide1: WaveGuide = new WaveGuide(0.001, 0.01, 200, 0.999);
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
const freq = notefreq(note);
this.waveguide1.start(freq, freq * 8000 / Mathf.pow(note, 1.7));
this.env.attack();
}
noteoff(): void {
this.env.release();
}
isDone(): boolean {
return this.env.isDone();
}
nextframe(): void {
const env = this.env.next();
const signal = (
this.waveguide1.process()
)
* env * this.velocity * 0.25 as f32;
this.channel.signal.add(
signal, signal
);
}
}
class TubeLead extends MidiVoice {
env: Envelope = new Envelope(0.05, 1, 1.0, 0.1);
waveguide1: WaveGuideFeedbackLimit = new WaveGuideFeedbackLimit(0.001, 0.01,200, -1.12);
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
const freq = notefreq(note);
this.waveguide1.start(freq, 230 * Mathf.pow(freq, 0.36 ));
this.env.attack();
}
noteoff(): void {
this.env.release();
}
isDone(): boolean {
return this.env.isDone();
}
nextframe(): void {
const env = this.env.next() * 0.02;
const signal = (
this.waveguide1.process()
)
* env * this.velocity as f32;
this.channel.signal.add(
signal, signal
);
}
}
class FluteChannel extends MidiChannel {
preprocess(): void {
let signal = this.signal.left;
echoline.left += (signal) * this.volume;
echoline.right += (signal) * this.volume;
}
}
class Flute extends MidiVoice {
env: Envelope = new Envelope(0.05, 1, 1.0, 0.01);
waveguide1: BandPassWaveGuide = new BandPassWaveGuide(0.001, 0.01,150, -1.1);
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
const freq = notefreq(note);
this.waveguide1.start(freq, 80 * Mathf.pow(freq, 0.5 ));
this.env.attack();
}
noteoff(): void {
this.env.release();
}
isDone(): boolean {
return this.env.isDone();
}
nextframe(): void {
const env = this.env.next() * 0.01;
const signal = (
this.waveguide1.process()
)
* env * this.velocity as f32;
this.channel.signal.add(
signal, signal
);
}
}
class HihatGuide extends WaveGuide {
allpasses: StaticArray<AllPassFloat> = new StaticArray<AllPassFloat>(3);
constructor(attack: f32, decay: f32, feedbackfreq: f32, feedback: f32) {
super(attack, decay, feedbackfreq, feedback);
for(var n=0;n<this.allpasses.length;n++) {
this.allpasses[n] = new AllPassFloat();
this.allpasses[n].setDelta(5);
}
}
process(): f32 {
const envexciter = this.envExciter.next() * this.exciterenvlevel;
let exciterSignal: f32 = noise() * envexciter;
exciterSignal = this.filterExciter.process(exciterSignal);
const feedback = this.delay.read();
let signal = exciterSignal + feedback;
for(let n=0;n<this.allpasses.length;n++) signal = this.allpasses[n].process(signal);
signal = this.filterFeedback.processForm2(signal);
this.delay.write_and_advance(
signal * this.feedbackLevel
);
return signal;
}
}
class Hihat extends MidiVoice {
env: Envelope = new Envelope(0.0001, 1, 1.0, 0.2);
waveguide1: HihatGuide = new HihatGuide(0.012, 0.06, 20000, 0.6);
waveguide2: HihatGuide = new HihatGuide(0.012, 0.06, 20000, 0.6);
constructor(channel: MidiChannel) {
super(channel);
this.minnote = 66;
this.maxnote = 66;
}
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
const freq: f32 = 7200;
this.waveguide1.start(freq, 20000);
this.waveguide2.start(freq, 20000);
this.env.attack();
}
noteoff(): void {
this.env.release();
}
isDone(): boolean {
return this.env.isDone();
}
nextframe(): void {
const env = this.env.next() * 0.04 * this.velocity / 30;;
let left = (
this.waveguide1.process()
)
* env as f32;
let right = (
this.waveguide2.process()
)
* env as f32;
this.channel.signal.add(
left, right
);
}
}
class OpenHihat extends MidiVoice {
env: Envelope = new Envelope(0.0001, 1, 1.0, 0.2);
waveguide1: HihatGuide = new HihatGuide(0.001, 0.5, 20000, 0.85);
waveguide2: HihatGuide = new HihatGuide(0.001, 0.5, 20000, 0.85);
constructor(channel: MidiChannel) {
super(channel);
this.minnote = 68;
this.maxnote = 68;
}
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
const freq: f32 = 7200;
this.waveguide1.start(freq, 20000);
this.waveguide2.start(freq, 20000);
this.env.attack();
}
noteoff(): void {
this.env.release();
}
isDone(): boolean {
return this.env.isDone();
}
nextframe(): void {
const env = this.env.next() * 0.04 * this.velocity / 30;
let left = (
this.waveguide1.process()
)
* env as f32;
let right = (
this.waveguide2.process()
)
* env as f32;
this.channel.signal.add(
left, right
);
}
}
class KickGuide extends WaveGuide {
allpasses: StaticArray<AllPassFloat> = new StaticArray<AllPassFloat>(4);
constructor(attack: f32, decay: f32, feedbackfreq: f32, feedback: f32) {
super(attack, decay, feedbackfreq, feedback);
for(var n=0;n<this.allpasses.length;n++) {
this.allpasses[n] = new AllPassFloat();
this.allpasses[n].setDelta(20);
}
}
start(freq:f32, filterfreq: f32):void {
super.start(freq,filterfreq);
//this.filterExciter.clearBuffers();
//this.filterFeedback.clearBuffers();
for(var n=0;n<this.allpasses.length;n++) {
//this.allpasses[n].clearBuffers();
}
}
process(): f32 {
const envexciter = this.envExciter.next() * this.exciterenvlevel;
let exciterSignal: f32 = noise() * envexciter;
exciterSignal = this.filterExciter.process(exciterSignal);
const feedback = this.delay.read();
let signal = exciterSignal + feedback;
for(let n=0;n<this.allpasses.length;n++) signal = this.allpasses[n].process(signal);
signal = this.filterFeedback.processForm2(signal);
this.delay.write_and_advance(
signal * this.feedbackLevel
);
return signal;
}
}
class Kick2 extends MidiVoice {
env: Envelope = new Envelope(0.0001, 1, 1.0, 0.4);
waveguide1: KickGuide = new KickGuide(0.001, 0.08, 100, 0.5);
waveguide2: KickGuide = new KickGuide(0.001, 0.08, 100, 0.5);
chasingvelocity: f32 = 0;
constructor(channel: MidiChannel) {
super(channel);
this.minnote = 60;
this.maxnote = 60;
}
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
const freq: f32 = 1000;
this.waveguide1.start(freq * 1.0001, 500);
this.waveguide2.start(freq * 0.9999, 500);
this.env.attack();
}
noteoff(): void {
this.env.release();
}
isDone(): boolean {
return this.env.isDone();
}
nextframe(): void {
const env = this.env.next() * 0.35 * this.chasingvelocity;
this.chasingvelocity += (this.velocity - this.chasingvelocity) * 0.01;
const left = (
this.waveguide1.process()
)
* env as f32;
const right = (
this.waveguide2.process()
)
* env as f32;
this.channel.signal.add(
left, right
);
}
}
class SnareGuide extends WaveGuide {
allpasses: StaticArray<AllPassFloat> = new StaticArray<AllPassFloat>(6);
constructor(attack: f32, decay: f32, feedbackfreq: f32, feedback: f32) {
super(attack, decay, feedbackfreq, feedback);
for(var n=0;n<this.allpasses.length;n++) {
this.allpasses[n] = new AllPassFloat();
this.allpasses[n].setDelta(15);
}
}
process(): f32 {
const envexciter = this.envExciter.next() * this.exciterenvlevel;
let exciterSignal: f32 = noise() * envexciter;
exciterSignal = this.filterExciter.process(exciterSignal);
const feedback = this.delay.read();
let signal = exciterSignal + feedback;
for(let n=0;n<this.allpasses.length;n++) signal = this.allpasses[n].process(signal);
signal = this.filterFeedback.processForm2(signal);
this.delay.write_and_advance(
signal * this.feedbackLevel
);
return signal;
}
}
class Snare2 extends MidiVoice {
env: Envelope = new Envelope(0.005, 0.5, 0.1, 0.3);
waveguide1: SnareGuide = new SnareGuide(0.005, 0.2, 10000, 0.8);
waveguide2: SnareGuide = new SnareGuide(0.005, 0.2, 10000, 0.8);
constructor(channel: MidiChannel) {
super(channel);
this.minnote = 62;
this.maxnote = 62;
}
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
const freq: f32 = 1400;
this.waveguide1.start(freq, 1660);
this.waveguide2.start(freq, 1660);
this.env.attack();
}
noteoff(): void {
this.env.release();
}
isDone(): boolean {
return this.env.isDone();
}
nextframe(): void {
const env = this.env.next() * 1.6 * this.velocity as f32 / 127 as f32;
let left = (
this.waveguide1.process()
)
* env as f32;
let right = (
this.waveguide2.process()
)
* env as f32;
this.channel.signal.add(
left, right
);
}
}
class CymbalGuide extends WaveGuide {
allpasses: StaticArray<AllPassFloat> = new StaticArray<AllPassFloat>(6);
hipass: HiPassBiQuadFilter = new HiPassBiQuadFilter();
constructor(attack: f32, decay: f32, feedbackfreq: f32, feedback: f32) {
super(attack, decay, feedbackfreq, feedback);
for(var n=0;n<this.allpasses.length;n++) {
this.allpasses[n] = new AllPassFloat();
this.allpasses[n].setDelta(11);
}
this.hipass.update_coeffecients(FilterType.HighPass, SAMPLERATE,
1200, Q_BUTTERWORTH);
}
process(): f32 {
const envexciter = this.envExciter.next() * this.exciterenvlevel;
let exciterSignal: f32 = noise() * envexciter;
exciterSignal = this.filterExciter.process(exciterSignal);
const feedback = this.delay.read();
let signal = exciterSignal + feedback;
for(let n=0;n<this.allpasses.length;n++) signal = this.allpasses[n].process(signal);
signal = this.filterFeedback.processForm2(signal);
this.delay.write_and_advance(
signal * this.feedbackLevel
);
signal = this.hipass.process(signal);
return signal * 0.00027;
}
}
class Cymbal extends MidiVoice {
env: Envelope = new Envelope(0.0001, 3.0, 1.0, 3.0);
waveguide1: CymbalGuide = new CymbalGuide(0.001, 0.15, 20000, 0.999);
waveguide2: CymbalGuide = new CymbalGuide(0.001, 0.15, 20000, 0.999);
chasingvelocity: f32 = 0;
constructor(channel: MidiChannel, note: u8) {
super(channel);
this.minnote = note;
this.maxnote = note;
}
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
const freq: f32 = SAMPLERATE / 2;
this.waveguide1.start(freq * 0.95, 20000);
this.waveguide2.start(freq * 1.05, 20000);
this.env.attack();
}
noteoff(): void {
this.env.release();
}
isDone(): boolean {
return this.env.isDone();
}
nextframe(): void {
const env = this.env.next() * 0.2 * this.chasingvelocity;
this.chasingvelocity += 0.01 * (this.velocity-this.chasingvelocity);
const left = (
this.waveguide1.process()
)
* env as f32;
const right = (
this.waveguide2.process()
)
* env as f32;
this.channel.signal.add(
left, right
);
}
}
class TomGuide extends WaveGuide {
allpasses: StaticArray<AllPassFloat> = new StaticArray<AllPassFloat>(6);
hipass: HiPassBiQuadFilter = new HiPassBiQuadFilter();
constructor(attack: f32, decay: f32, feedbackfreq: f32, feedback: f32) {
super(attack, decay, feedbackfreq, feedback);
for(var n=0;n<this.allpasses.length;n++) {
this.allpasses[n] = new AllPassFloat();
this.allpasses[n].setDelta(20000);
}
this.hipass.update_coeffecients(FilterType.HighPass, SAMPLERATE,
250, Q_BUTTERWORTH);
}
process(): f32 {
const envexciter = this.envExciter.next() * this.exciterenvlevel;
let exciterSignal: f32 = noise() * envexciter;
exciterSignal = this.filterExciter.process(exciterSignal);
const feedback = this.delay.read();
let signal = exciterSignal + feedback;
for(let n=0;n<this.allpasses.length;n++) signal = this.allpasses[n].process(signal);
signal = this.filterFeedback.processForm2(signal);
this.delay.write_and_advance(
signal * this.feedbackLevel
);
signal = this.hipass.process(signal);
return signal * 0.020;
}
}
class Tom extends MidiVoice {
env: Envelope = new Envelope(0.0001, 1, 1.0, 0.4);
waveguide1: TomGuide = new TomGuide(0.002, 0.12, 1000, 0.998);
waveguide2: TomGuide = new TomGuide(0.002, 0.12, 1000, 0.998);
constructor(channel: MidiChannel, note: u8) {
super(channel);
this.minnote = note;
this.maxnote = note;
}
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
const freq: f32 = 20000;
this.waveguide1.start(freq * 0.99, 1600 + ((this.minnote - 65 ) as f32 * 250));
this.waveguide2.start(freq * 1.01, 1600 + ((this.minnote - 65 ) as f32 * 250));
this.env.attack();
}
noteoff(): void {
this.env.release();
}
isDone(): boolean {
return this.env.isDone();
}
nextframe(): void {
const env = this.env.next() * 0.5 * this.velocity;
const left = (
this.waveguide1.process()
)
* env as f32;
const right = (
this.waveguide2.process()
)
* env as f32;
this.channel.signal.add(
left, right
);
}
}
class DrumChannel extends MidiChannel {
hipassl: BiQuadFilter = new BiQuadFilter();
hipassr: BiQuadFilter = new BiQuadFilter();
constructor(numvoices: i32, factoryFunc: (channel: MidiChannel, voiceindex: i32) => MidiVoice) {
super(numvoices, factoryFunc);
this.hipassl.update_coeffecients(FilterType.HighPass, SAMPLERATE, 30, Q_BUTTERWORTH);
this.hipassr.update_coeffecients(FilterType.HighPass, SAMPLERATE, 30, Q_BUTTERWORTH);
}
preprocess(): void {
let left = this.signal.left;
let right = this.signal.right;
left = this.hipassl.process(left);
right = this.hipassr.process(right);
this.signal.left = left;
this.signal.right = right;
//echoline.left += (left) * this.volume;
//echoline.right += (right) * this.volume;
}
}
const fft: FFT = new FFT(11);
for (let n=1; n< (fft.buffer.length / 2) - 1; n+=1) {
let v = Mathf.exp((-n as f32 ) * 0.2) * 1023 * cos((n * 5) as f32);
//fft.buffer[n].re = v;
//fft.buffer[fft.buffer.length - n].re = -v;
fft.buffer[n].im = -v;
fft.buffer[fft.buffer.length - n].im = v;
}
fft.calculateInverse();
class PadSynth extends MidiVoice {
env: Envelope = new Envelope(0.001, 2.5, 0.00, 0.2);
t: f64 = 0;
freq: f32 = 0;
spread: i32 = 9;
hprofile: StaticArray<f32> = new StaticArray<f32>(this.spread);
phase: StaticArray<f32> = new StaticArray<f32>(this.spread);
freqs: StaticArray<f32> = new StaticArray<f32>(this.spread);
level: f32 = 1.0;
constructor(channel: MidiChannel) {
super(channel);
const halfspread: f32 = (this.spread / 2) as f32;
for(let n=0;n<this.spread;n++) {
const v: f32 = 2 as f32 * (
n as f32 - halfspread);
this.hprofile[n] = Mathf.exp(v * -v);
this.phase[n] = noise() * 0x800 as f32;
}
}
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
this.env.attack();
const halfspread: f32 = (this.spread / 2) as f32;
for (let n = 0;n<this.freqs.length; n++) {
const v: f32 = 0.04 as f32 * (n as f32 - halfspread);
this.freqs[n] = notefreq((note + v));
}
this.level = (-(1/(notefreq(note)-200)) + 1) as f32;
}
noteoff(): void {
this.env.release();
}
isDone(): boolean {
const ret = this.env.isDone();
return ret;
}
nextframe(): void {
const env = this.env.next() * this.level;
let left: f32 = 0;
let right: f32 = 0;
const t = this.t;
const freq = this.freq;
for(let n = 0;n < this.spread ; n++) {
const f = this.freqs[n];
const bufferpos = t * 0x800 * f + this.phase[n];
const floorbufferpos = Math.floor(bufferpos);
const delta = (bufferpos - floorbufferpos) as f32;
const v1 = fft.buffer[((bufferpos as i64) & 0x7ff) as i32].re;
const v = v1;
const leftlevel: f32 = (((n) / 2) + 0.5) as f32;
const rightlevel: f32 = (((this.hprofile.length - n) / 2) + 0.5) as f32;
left += v* leftlevel;
right += v* rightlevel;
}
left *= env;
right *= env;
this.t += 1 / SAMPLERATE;
this.channel.signal.add(
left, right
);
}
}
class PadSynthChannel extends MidiChannel {
lopassleft: BiQuadFilter = new BiQuadFilter();
lopassright: BiQuadFilter = new BiQuadFilter();
constructor(numvoices: i32, factoryFunc: (channel: MidiChannel, voiceindex: i32) => MidiVoice) {
super(numvoices, factoryFunc);
this.lopassleft.update_coeffecients(FilterType.LowPass, SAMPLERATE,
4000, 0.3);
this.lopassright.update_coeffecients(FilterType.LowPass, SAMPLERATE,
4000, 0.3);
}
preprocess(): void {
let left = this.signal.left;
let right = this.signal.right;
const gain:f32 = 0.04;
left*=gain;
right*=gain;
left = this.lopassleft.process(left);
right = this.lopassright.process(right);
echoline.left += (left * 0.1);
echoline.right += (right * 0.1);
this.signal.left = left;
this.signal.right = right;
}
}
class Choir extends MidiVoice {
env: Envelope = new Envelope(0.05, 1, 1.0, 0.3);
waveguide1: CustomExciterWaveGuide = new CustomExciterWaveGuide();
waveguide2: CustomExciterWaveGuide = new CustomExciterWaveGuide();
exciterEnv: Envelope = new Envelope(0.15, 0.2, 0.6, 0.3);
saw: SawOscillator = new SawOscillator();
lopass: LoPassBiQuadFilter = new LoPassBiQuadFilter();
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
const freq = notefreq(note);
this.saw.frequency = freq ;
this.waveguide1.feedbackLevel = -1;
this.waveguide2.feedbackLevel = -1;
this.waveguide1.start(freq, Mathf.log(freq * 0.01) * 2000);
this.waveguide2.start(freq * 2.0, Mathf.log(freq * 0.01) * 2000);
this.lopass.update(2500,0.6);
this.env.attack();
this.exciterEnv.attack();
}
noteoff(): void {
this.env.release();
this.exciterEnv.release();
}
isDone(): boolean {
return this.env.isDone() && this.exciterEnv.isDone();
}
nextframe(): void {
const env = this.env.next() * 0.003;
const exciterSignal = this.lopass.process((noise()) * this.exciterEnv.next());
this.waveguide1.exciterSignal = exciterSignal;
this.waveguide2.exciterSignal = exciterSignal;
const signal1 = this.waveguide1.process();
const signal2 = this.waveguide2.process();
const left = (signal2 + signal1 * 0.4 ) * env * this.velocity as f32;
const right = (signal1 + signal2 * 0.4 ) * env * this.velocity as f32;
this.channel.signal.add(
left, right
);
}
}
export function initializeMidiSynth(): void {
midichannels[0] = new PianoChannel(10, (ch) => new Piano(ch));
midichannels[0].controlchange(7, 90);
midichannels[0].controlchange(10, 64);
midichannels[0].controlchange(91,80);
midichannels[1] = new MidiChannel(10, (ch) => new String(ch));
midichannels[1].controlchange(7,50);
midichannels[1].controlchange(10, 64);
midichannels[1].controlchange(91, 80);
midichannels[2] = new DrumChannel(9, (ch, ndx) => {
switch(ndx) {
case 1:
return new Hihat(ch);
case 2:
return new Snare2(ch);
case 3:
return new OpenHihat(ch);
case 4:
return new Tom(ch, 65);
case 5:
return new Tom(ch, 67);
case 6:
return new Tom(ch, 69);
case 7:
return new Tom(ch, 71);
case 8:
return new Cymbal(ch, 73);
default:
return new Kick2(ch);
}
});
midichannels[2].controlchange(7, 100);
midichannels[2].controlchange(91, 40);
midichannels[3] = new GuitarChannel(6, (ch) => new Guitar(ch));
midichannels[3].controlchange(7, 30);
midichannels[3].controlchange(10, 50);
midichannels[3].controlchange(91, 65);
midichannels[4] = new MidiChannel(2, (ch) => new Bass(ch));
midichannels[4].controlchange(7, 80);
midichannels[4].controlchange(10, 64);
midichannels[4].controlchange(91, 40);
midichannels[5] = new MidiChannel(1, (ch) => new TubeLead(ch));
midichannels[5].controlchange(7, 30);
midichannels[5].controlchange(10, 50);
midichannels[5].controlchange(91, 40);
midichannels[6] = new FluteChannel(1, (ch) => new Flute(ch));
midichannels[6].controlchange(7, 57);
midichannels[6].controlchange(10, 48);
midichannels[6].controlchange(91, 57);
midichannels[7] = new PadSynthChannel(15, (ch) => new PadSynth(ch));
midichannels[7].controlchange(7, 60);
midichannels[7].controlchange(10, 64);
midichannels[7].controlchange(91, 70);
midichannels[8] = new MidiChannel(5, (ch) => new Brass(ch));
midichannels[8].controlchange(7, 40);
midichannels[8].controlchange(10, 32);
midichannels[8].controlchange(91, 40);
midichannels[9] = new MidiChannel(10, (ch) => new Choir(ch));
midichannels[9].controlchange(7, 40);
midichannels[9].controlchange(10, 80);
midichannels[9].controlchange(91, 70);
}
const eqfreqs: f32[] = [40,150,1200,24000]
const eqlevels: f32[] = [1.2,0.7,1.2];
const eqleft = new MultiBandEQ(eqfreqs);
const eqright = new MultiBandEQ(eqfreqs);
const compressor = new TriBandStereoCompressor(0.05, 25,150,1500,20000);
compressor.compressorLow.leftCompressor.setRelaseSampleCount((0.5 * SAMPLERATE) as usize);
compressor.compressorLow.rightCompressor.setRelaseSampleCount((0.5 * SAMPLERATE) as usize);
const limiter_left = new MonoCompressor((0.005 * SAMPLERATE) as usize);
const limiter_right = new MonoCompressor((0.005 * SAMPLERATE) as usize);
const midsideprocessor = new MidSideProcessor(1.7);
export function postprocess(): void {
const gain: f32 = 1.5;
let left = outputline.left;
let right = outputline.right;
const echol = echoLeft.read() * 0.3;
const echor = echoRight.read() * 0.3;
echoLeft.write_and_advance(echol + echoline.left);
echoRight.write_and_advance(echor + echoline.right);
left+=echol;
right+=echor;
left = eqleft.process(left, eqlevels);
right = eqright.process(right, eqlevels);
left*=gain;
right*=gain;
const COMPRESS_AND_STEREO_ENHANCE = false;
if (COMPRESS_AND_STEREO_ENHANCE) {
midsideprocessor.process(left,right);
left = midsideprocessor.signal.left;
right = midsideprocessor.signal.right;
compressor.process(left, right, 0.8, 0.7, 1.0, 1.0, 1.0, 1.0);
left = compressor.stereosignal.left;
right = compressor.stereosignal.right;
const master_gain: f32 = 1.0;
left *= master_gain;
right *= master_gain;
const limiter_threshold: f32 = 0.99;
left = limiter_left.process(left, limiter_threshold, 1.0);
right = limiter_right.process(right, limiter_threshold, 1.0);
}
outputline.left = left;
outputline.right = right;
echoline.clear();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment