Skip to content

Instantly share code, notes, and snippets.

@petersalomonsen
Created September 13, 2023 04:50
Show Gist options
  • Save petersalomonsen/d485d6669620e60d7399ee8feaa92537 to your computer and use it in GitHub Desktop.
Save petersalomonsen/d485d6669620e60d7399ee8feaa92537 to your computer and use it in GitHub Desktop.
WebAssembly Music: "Much"
precision highp float;
uniform vec2 resolution;
uniform float time;
uniform float targetNoteStates[128];
float wave(float x, float freq, float speed, float amp) {
return amp * sin(x * freq + time * speed);
}
void main() {
vec2 uv = (gl_FragCoord.xy / resolution.xy) * 2.0 - 1.0; // UV coordinates [-1, 1]
float x_index = (uv.x + 1.0) * 64.0; // Compute the corresponding index in targetNoteStates based on the x position
float amp_lower = 0.0;
float amp_upper = 0.0;
int index_lower = 0;
int index_upper = 0;
for (int i = 0; i < 128; ++i) {
if (float(i) <= x_index) {
amp_lower = targetNoteStates[i];
index_lower = i;
}
if (float(i) >= x_index && index_upper == 0) {
amp_upper = targetNoteStates[i];
index_upper = i;
}
}
float fraction = (x_index - float(index_lower)) / (float(index_upper - index_lower) + 0.0001);
float amp = mix(amp_lower, amp_upper, fraction); // interpolate amplitude
float freq = float(index_lower) / 64.0; // Normalize index to [0, 2] for frequency
float y = wave(uv.x, freq, 5.0, amp) * 0.2; // Add the note's wave to the total wave height, increased amplification factor
float dayNight = sin(time * 0.2); // changes from -1 to 1 over time, can adjust speed with the 0.2 constant
dayNight = dayNight * 0.5 + 0.5; // changes from 0 to 1
vec3 dayColor = vec3(0.9, 0.9, 1.0);
vec3 nightColor = vec3(0.1, 0.1, 0.3);
vec3 sunsetColor = vec3(0.9, 0.4, 0.1);
// calculate color based on time
vec3 color;
if (dayNight < 0.25) { // Night
color = mix(nightColor, sunsetColor, dayNight / 0.25);
} else if (dayNight < 0.75) { // Day
color = mix(sunsetColor, dayColor, (dayNight - 0.25) / 0.5);
} else { // Transition to night
color = mix(dayColor, nightColor, (dayNight - 0.75) / 0.25);
}
// mix in the y component of the uv for the sea color
color *= (uv.y + y + 0.5);
// darken below the horizon
if(uv.y + y < -0.1) {
color *= 0.7;
}
gl_FragColor = vec4(color, 1.0);
}
/*
* Copyright (c) 2023 - Peter Johan Salomonsen
*/
setBPM(124);
addInstrument('piano');
addInstrument('string');
addInstrument('drums');
addInstrument('guitar');
addInstrument('bass');
addInstrument('tubelead');
addInstrument('flute');
addInstrument('padsynth');
addInstrument('brass');
addInstrument('choir');
//solo(0);
//solo(2);
//solo(6);
const beat = () => createTrack(2).steps(4,[
c5,,,,
c5,,,,
[c5],,,,
[c5],,,,
]);
const beat2 = () => createTrack(2).steps(4,[
c5,,fs5(0.5,50),fs5(0.5,5),
c5,,fs5(0.2,20),,
[c5],,fs5(0.5,50),,
[c5],fs5(0.5,5),fs5(0.2,20),,
]);
const beatwithsnare1 = () => createTrack(2).steps(4,[
c5,,fs5(0.5,50),fs5(0.5,5),
c5,,gs5(0.2,20),,
[c5],,fs5(0.5,50),,
[c5,d5],fs5(0.5,5),gs5(0.2,20),,
]);
const beatwithsnare2 = () => createTrack(2).steps(4,[
c5,,fs5(0.5,50),fs5(0.5,5),
[c5,d5],,gs5(0.2,20),,
[c5],,[fs5(0.5,50)],,
[c5,d5],fs5(0.5,5),gs5(0.2,20),,
c5,,fs5(0.5,50),fs5(0.5,5),
[c5,d5],,gs5(0.2,20),,
[c5],,fs5(0.5,50),,
[c5,d5],fs5(0.5,5),gs5(0.2,20),d5(0.1,30),
]);
const padsynth1 = () => createTrack(7).play([[ 0.34, controlchange(64, 127) ],
[ 0.03, f6(0.85, 88) ],
[ 0.03, c6(0.88, 77) ],
[ 0.02, gs5(0.91, 78) ],
[ 0.06, f2(0.93, 83) ],
[ 1.96, controlchange(64, 0) ],
[ 2.44, controlchange(64, 127) ],
[ 2.01, ds5(0.92, 83) ],
[ 2.00, ds6(0.94, 83) ],
[ 2.01, as5(0.93, 88) ],
[ 2.03, g2(0.94, 84) ],
[ 3.45, controlchange(64, 0) ],
[ 4.02, controlchange(64, 127) ],
[ 3.48, ds5(3.08, 86) ],
[ 3.47, c6(3.10, 93) ],
[ 6.54, as5(0.98, 93) ],
[ 3.49, gs2(4.17, 86) ],
[ 7.49, c6(0.28, 75) ],
[ 8.05, controlchange(64, 0) ],
[ 8.31, controlchange(64, 127) ],
[ 8.01, as5(0.92, 96) ],
[ 8.02, cs6(0.91, 81) ],
[ 8.05, as2(0.94, 86) ],
[ 8.05, f5(1.01, 83) ],
[ 10.01, controlchange(64, 0) ],
[ 10.31, controlchange(64, 127) ],
[ 10.04, c6(0.58, 83) ],
[ 10.04, f5(0.63, 87) ],
[ 10.02, gs5(0.72, 83) ],
[ 10.07, f2(0.71, 82) ],
[ 11.58, controlchange(64, 0) ],
[ 12.21, controlchange(64, 127) ],
[ 11.54, cs2(4.34, 84) ],
[ 11.52, f5(4.38, 94) ],
[ 11.53, cs5(4.37, 88) ],
[ 15.94, controlchange(64, 0) ]]);
const flute1 = () => createTrack(6).play([[ 0.53, gs7(0.36, 89) ],
[ 1.04, gs7(0.31, 94) ],
[ 1.52, gs7(0.29, 86) ],
[ 2.02, gs7(0.51, 82) ],
[ 2.53, g7(0.57, 103) ],
[ 3.45, ds7(0.63, 94) ],
[ 4.30, ds7(0.27, 63) ],
[ 4.48, f7(0.31, 72) ],
[ 4.86, c7(1.36, 100) ],
[ 8.52, cs7(0.37, 102) ],
[ 8.99, cs7(0.31, 44) ],
[ 9.47, cs7(0.36, 99) ],
[ 10.01, cs7(0.58, 101) ],
[ 10.51, c7(0.53, 97) ],
[ 11.48, gs6(0.72, 96) ],
[ 12.40, gs6(0.27, 83) ],
[ 12.54, as6(0.37, 78) ],
[ 12.95, f6(1.29, 100) ]]);
const bass1 = () => createTrack(4).play([[ 0.02, f2(1.67, 89) ],
[ 2.01, g2(1.15, 84) ],
[ 3.53, gs2(2.69, 81) ],
[ 6.55, gs2(0.41, 89) ],
[ 6.98, gs3(0.52, 93) ],
[ 7.50, gs2(0.26, 86) ],
[ 8.03, as2(1.66, 89) ],
[ 10.03, f2(1.29, 87) ],
[ 11.56, cs2(2.86, 84) ],
[ 14.60, cs2(0.37, 94) ],
[ 14.98, cs3(0.59, 89) ],
[ 15.55, cs2(0.38, 91) ]].quantize(4));
const strings1 = () => createTrack(1).play([[ 0.07, c5(1.66, 89) ],
[ 0.10, gs5(1.79, 86) ],
[ 0.10, f5(1.82, 88) ],
[ 0.11, f4(1.94, 89) ],
[ 2.04, as4(1.23, 96) ],
[ 2.05, ds4(1.28, 83) ],
[ 2.04, g5(1.38, 89) ],
[ 3.51, c5(4.17, 99) ],
[ 3.50, gs5(4.39, 91) ],
[ 2.04, ds5(5.96, 77) ],
[ 3.55, gs4(4.46, 89) ],
[ 8.05, cs5(1.85, 94) ],
[ 8.07, f5(1.83, 89) ],
[ 8.07, gs5(1.85, 93) ],
[ 8.06, cs4(1.97, 83) ],
[ 10.09, c5(1.11, 100) ],
[ 10.11, gs5(1.24, 93) ],
[ 10.08, f4(1.48, 92) ],
[ 12.13, cs4(3.8, 78) ],
[ 11.63, gs4(4.3, 89) ],
[ 10.12, f5(5.8, 98) ],
[ 11.59, cs5(4.0, 93) ]]);
for (var n=0;n<2;n++) {
padsynth1();
await beat();
await beat();
await beat();
await beat();
}
for (var n=0;n<2;n++) {
padsynth1();
strings1();
bass1();
await beat2();
await beat2();
await beat2();
await beat2();
}
for (var n=0;n<2;n++) {
padsynth1();
flute1();
bass1();
strings1();
await beat2();
await beat2();
await beat2();
await beat2();
}
for (var n=0;n<2;n++) {
padsynth1();
bass1();
strings1();
createTrack(0).steps(4,[
f5,gs5,c6,f6,gs6,f6,c6,gs5,
ds5,g5,as5,ds6,g6,ds6,as5,g5,
ds5,gs5,c6,ds6,gs6,ds6,c6,gs5,
ds5,gs5,c6,ds6,gs6,ds6,c6,gs5,
f5,as5,cs6,f6,as6,f6,cs6,as5,
f5,gs5,c6,f6,gs6,f6,c6,gs5,
cs5,f5,gs6,cs6,f6,cs6,gs6,f5,
cs5,f5,gs6,cs6,f6(0.99),cs6(0.74),gs6(0.49),f5(0.24),
]);
await beatwithsnare1();
await beatwithsnare1();
await beatwithsnare1();
await beatwithsnare1();
}
const padsynth2 = () => createTrack(7).play([[ 0.20, controlchange(64, 127) ],
[ 0.05, c6(0.88, 87) ],
[ 0.04, gs6(0.94, 88) ],
[ 0.07, f6(0.94, 89) ],
[ 2.13, controlchange(64, 0) ],
[ 2.38, controlchange(64, 127) ],
[ 2.04, g6(0.59, 84) ],
[ 2.01, ds6(0.67, 73) ],
[ 2.04, as5(0.69, 76) ],
[ 3.61, controlchange(64, 0) ],
[ 6.32, controlchange(64, 127) ],
[ 3.50, gs5(3.66, 63) ],
[ 3.54, c6(3.63, 74) ],
[ 3.54, ds6(3.69, 66) ],
[ 8.11, controlchange(64, 0) ],
[ 8.65, controlchange(64, 127) ],
[ 8.08, f6(1.11, 84) ],
[ 8.06, gs5(1.15, 82) ],
[ 8.04, cs6(1.19, 82) ],
[ 10.11, controlchange(64, 0) ],
[ 10.43, controlchange(64, 127) ],
[ 10.05, c6(1.08, 84) ],
[ 10.08, gs5(1.12, 81) ],
[ 10.08, ds6(1.16, 73) ],
[ 11.45, controlchange(64, 0) ],
[ 11.81, controlchange(64, 127) ],
[ 11.55, f6(1.28, 89) ],
[ 11.53, gs5(1.32, 84) ],
[ 11.51, cs6(1.38, 87) ],
[ 13.43, controlchange(64, 0) ],
[ 15.10, controlchange(64, 127) ],
[ 13.48, f6(2.12, 89) ],
[ 13.45, cs6(2.17, 83) ],
[ 13.44, as5(2.1, 91) ],
[ 15.90, controlchange(64, 0) ]].quantize(4));
const choir2 = () => createTrack(9).play([[ 0.0, c7(1.99, 94) ],
[ 0.0, gs7(1.99, 88) ],
[ 0.0, f7(1.99, 88) ],
[ 2.00, g7(1.49, 97) ],
[ 2.00, ds7(1.49, 86) ],
[ 2.00, as6(1.49, 89) ],
[ 3.5, gs6(4.49, 82) ],
[ 3.5, c7(4.49, 93) ],
[ 3.5, ds7(4.49, 81) ],
[ 8.00, gs6(1.9, 94) ],
[ 8.00, f7(1.9, 92) ],
[ 8.00, cs7(1.9, 102) ],
[ 10.00, c7(1.49, 100) ],
[ 10.00, gs6(1.49, 83) ],
[ 10.01, ds7(1.49, 89) ],
[ 11.5, gs6(1.99, 91) ],
[ 11.5, f7(1.99, 94) ],
[ 11.5, cs7(1.99, 104) ],
[ 13.5, f7(2.49, 84) ],
[ 13.5, as6(2.49, 96) ],
[ 13.5, cs7(2.49, 104) ]].fixVelocity(100));
const bass2 = () => createTrack(4).play([[ 0.0, f2(0.45, 99) ],
[ 0.87, f2(0.32, 70) ],
[ 1.0, f3(0.46, 109) ],
[ 1.5, f2(0.20, 92) ],
[ 2.0, ds2(0.46, 88) ],
[ 2.52, ds3(0.54, 83) ],
[ 3.49, ds3(0.48, 88) ],
[ 3.97, gs2(0.45, 82) ],
[ 4.74, gs2(0.30, 77) ],
[ 5.01, gs3(0.48, 94) ],
[ 5.55, gs2(0.45, 87) ],
[ 6.50, gs2(0.51, 76) ],
[ 7.01, ds3(0.56, 82) ],
[ 7.50, gs3(0.46, 87) ],
[ 8.00, as2(0.50, 82) ],
[ 8.85, as2(0.30, 88) ],
[ 9.09, as3(0.50, 93) ],
[ 9.53, as2(0.21, 76) ],
[ 10.01, f2(0.54, 90) ],
[ 10.57, e3(0.04, 32) ],
[ 10.51, f3(0.55, 95) ],
[ 11.50, f3(0.15, 82) ],
[ 11.98, cs2(0.49, 84) ],
[ 12.75, cs2(0.30, 83) ],
[ 13.00, cs3(0.32, 93) ],
[ 13.51, as1(0.44, 94) ],
[ 14.48, as1(0.54, 78) ],
[ 15.04, f2(0.45, 74) ],
[ 15.47, as2(0.49, 93) ]].quantize(4).fixVelocity(100));
const guitar2 = () => createTrack(3).play([[ 0.51, ds5(0.16, 51) ],
[ 0.53, ds6(0.20, 64) ],
[ 0.69, f5(0.24, 64) ],
[ 0.69, f6(0.29, 67) ],
[ 0.92, gs6(0.33, 76) ],
[ 0.93, gs5(0.33, 82) ],
[ 1.55, gs5(1.06, 76) ],
[ 1.55, gs6(1.07, 88) ],
[ 2.59, as5(0.59, 94) ],
[ 2.57, as6(0.67, 88) ],
[ 3.46, ds6(0.27, 86) ],
[ 3.52, ds5(0.28, 76) ],
[ 4.41, ds6(0.23, 63) ],
[ 4.42, ds5(0.29, 69) ],
[ 4.72, f5(0.23, 74) ],
[ 4.68, f6(0.29, 75) ],
[ 4.92, gs5(0.60, 77) ],
[ 4.94, gs6(0.57, 82) ],
[ 5.50, as6(0.73, 108) ],
[ 5.49, as5(0.78, 101) ],
[ 6.48, c7(1.28, 89) ],
[ 6.49, c6(1.33, 93) ],
[ 8.53, ds6(0.21, 82) ],
[ 8.58, ds5(0.29, 77) ],
[ 8.76, f6(0.27, 79) ],
[ 8.81, f5(0.34, 65) ],
[ 9.01, gs6(0.34, 83) ],
[ 9.10, gs5(0.28, 86) ],
[ 9.56, gs5(1.07, 87) ],
[ 9.58, gs6(1.06, 91) ],
[ 10.62, as5(0.60, 92) ],
[ 10.61, as6(0.61, 104) ],
[ 11.51, ds6(0.40, 92) ],
[ 11.56, ds5(0.43, 83) ],
[ 12.46, ds6(0.23, 71) ],
[ 12.44, ds5(0.34, 72) ],
[ 12.71, f6(0.30, 70) ],
[ 12.73, f5(0.28, 69) ],
[ 12.94, gs5(0.56, 77) ],
[ 12.95, gs6(0.56, 78) ],
[ 13.51, as6(0.55, 99) ],
[ 13.50, as5(0.67, 96) ],
[ 14.49, c6(0.28, 92) ],
[ 14.46, c7(0.68, 88) ],
[ 14.99, as6(0.55, 83) ],
[ 15.04, as5(0.50, 91) ]].quantize(4));
for (var n=0;n<2;n++) {
guitar2();
bass2();
padsynth2();
choir2();
await beatwithsnare2();
await beatwithsnare2();
}
const flute3 = () => createTrack(6).play([[ 0.03, c8(0.73, 95) ],
[ 1.01, gs7(0.54, 106) ],
[ 1.94, ds7(0.21, 78) ],
[ 2.05, f7(0.43, 87) ],
[ 2.48, ds7(0.71, 94) ],
[ 3.47, gs7(2.18, 91) ],
[ 6.52, f7(0.40, 89) ],
[ 6.98, gs7(0.65, 91) ],
[ 7.54, as7(0.27, 94) ],
[ 8.00, as7(0.24, 94) ],
[ 8.15, c8(0.67, 78) ],
[ 9.05, gs7(0.48, 104) ],
[ 9.94, ds7(0.22, 68) ],
[ 10.08, f7(0.45, 92) ],
[ 10.51, ds7(0.63, 93) ],
[ 11.48, gs7(0.55, 91) ],
[ 12.50, gs7(0.33, 91) ],
[ 12.99, as7(0.35, 102) ],
[ 13.76, as7(0.22, 96) ],
[ 13.89, c8(0.26, 97) ],
[ 14.10, as7(0.87, 96) ],
[ 14.95, gs7(0.74, 99) ]]);
const strings3 = () => createTrack(1).play([[ 0.04, f5(1.66, 87) ],
[ 0.01, gs5(1.78, 89) ],
[ 0.02, c6(1.80, 95) ],
[ 1.98, g5(1.31, 94) ],
[ 1.98, ds5(1.33, 89) ],
[ 1.97, as5(1.39, 91) ],
[ 3.52, ds5(4.20, 86) ],
[ 3.54, c6(4.29, 87) ],
[ 3.52, gs5(4.37, 91) ],
[ 8.09, f5(1.70, 98) ],
[ 8.06, as5(1.75, 93) ],
[ 8.09, cs6(1.75, 96) ],
[ 10.01, f5(1.32, 89) ],
[ 10.02, gs5(1.32, 94) ],
[ 10.03, c6(1.33, 88) ],
[ 11.57, f5(1.93, 78) ],
[ 11.55, gs5(1.98, 78) ],
[ 11.57, cs6(1.97, 91) ],
[ 14.01, cs6(1.92, 93) ],
[ 14.00, as5(1.93, 96) ],
[ 14.02, f5(1.92, 99) ]]);
for (var n=0;n<2;n++) {
strings3();
flute3();
bass2();
padsynth2();
choir2();
await beatwithsnare2();
await beatwithsnare2();
}
const piano4 = () => createTrack(0).play([[ 0.23, controlchange(64, 127) ],
[ 0.08, f5(0.46, 60) ],
[ 0.56, ds7(0.25, 89) ],
[ 0.53, c6(0.36, 54) ],
[ 1.03, ds7(0.24, 78) ],
[ 1.01, gs5(0.46, 69) ],
[ 1.50, c6(0.22, 60) ],
[ 1.49, ds7(0.26, 72) ],
[ 1.99, controlchange(64, 0) ],
[ 2.24, controlchange(64, 127) ],
[ 2.05, g5(0.47, 72) ],
[ 2.02, ds7(0.58, 84) ],
[ 2.47, ds6(0.47, 57) ],
[ 2.48, cs7(0.50, 73) ],
[ 2.98, as5(0.54, 54) ],
[ 3.04, c7(0.52, 59) ],
[ 3.73, controlchange(64, 0) ],
[ 3.54, ds6(0.28, 72) ],
[ 4.04, controlchange(64, 127) ],
[ 3.52, gs6(0.60, 82) ],
[ 4.04, gs5(0.47, 61) ],
[ 4.50, ds7(0.25, 93) ],
[ 4.53, ds6(0.37, 68) ],
[ 4.95, ds7(0.25, 73) ],
[ 5.00, c6(0.44, 49) ],
[ 5.53, ds7(0.20, 72) ],
[ 5.49, ds6(0.34, 64) ],
[ 6.00, gs5(0.42, 62) ],
[ 6.01, ds7(0.52, 71) ],
[ 6.45, ds6(0.40, 48) ],
[ 6.47, cs7(0.55, 72) ],
[ 7.02, c7(0.05, 62) ],
[ 7.04, c6(0.47, 38) ],
[ 7.25, c7(0.26, 2) ],
[ 7.51, ds6(0.13, 58) ],
[ 7.89, controlchange(64, 0) ],
[ 7.47, gs6(0.62, 81) ],
[ 8.15, controlchange(64, 127) ],
[ 8.00, cs5(0.48, 59) ],
[ 8.49, ds7(0.28, 89) ],
[ 8.52, gs5(0.36, 64) ],
[ 8.97, ds7(0.26, 78) ],
[ 9.05, f5(0.42, 49) ],
[ 9.53, gs5(0.23, 69) ],
[ 9.57, ds7(0.18, 71) ],
[ 10.05, controlchange(64, 0) ],
[ 10.31, controlchange(64, 127) ],
[ 10.10, f5(0.43, 60) ],
[ 10.01, ds7(0.56, 76) ],
[ 10.53, c6(0.50, 57) ],
[ 10.53, cs7(0.56, 71) ],
[ 11.05, gs5(0.55, 68) ],
[ 11.09, c7(0.52, 64) ],
[ 11.59, c6(0.14, 69) ],
[ 12.01, controlchange(64, 0) ],
[ 11.59, gs6(0.54, 88) ],
[ 12.25, controlchange(64, 127) ],
[ 12.06, cs5(0.38, 52) ],
[ 12.48, gs5(0.44, 71) ],
[ 12.51, gs6(0.46, 63) ],
[ 13.05, as6(0.19, 81) ],
[ 13.05, f5(0.36, 58) ],
[ 13.51, gs5(0.19, 61) ],
[ 13.58, as6(0.29, 43) ],
[ 13.90, controlchange(64, 0) ],
[ 13.84, c7(0.19, 78) ],
[ 14.17, controlchange(64, 127) ],
[ 13.99, as6(0.49, 88) ],
[ 13.96, as4(0.54, 68) ],
[ 14.47, gs6(0.28, 81) ],
[ 14.55, f5(0.35, 49) ],
[ 15.02, cs5(0.48, 58) ],
[ 15.00, f6(0.54, 42) ],
[ 15.55, f5(0.26, 78) ],
[ 15.54, ds6(0.35, 77) ],
[ 15.88, controlchange(64, 0) ]].quantize(4));
const bass1_2 = () => createTrack(4).play([[ 0.02, f2(1.67, 89) ],
[ 2.01, g2(1.15, 84) ],
[ 3.53, gs2(2.69, 81) ],
[ 6.55, gs2(0.41, 89) ],
[ 6.98, gs3(0.52, 93) ],
[ 7.50, gs2(0.26, 86) ],
[ 8.03, as2(1.66, 89) ],
[ 10.03, f2(1.29, 87) ],
[ 11.56, cs2(2.25, 84) ],
[ 14.60, as1(0.37, 94) ],
[ 14.98, as2(0.59, 89) ],
[ 15.55, as1(0.38, 91) ]].quantize(4));
for (var n=0;n<2;n++) {
bass1_2();
createTrack(2).steps(4,[
f5(0.1,100),,,f5(0.2,80),
,,,,
b5(0.1,100),,,b5(0.2,80),
,,,,
].repeat(3));
piano4();
choir2();
await beat2();
await beat2();
await beat2();
await beat2();
}
const padsynth4 = () => createTrack(7).play([[ 0.67, controlchange(64, 127) ],
[ 0.56, ds7(0.34, 92) ],
[ 1.09, ds7(0.28, 88) ],
[ 1.57, ds7(0.24, 77) ],
[ 2.11, controlchange(64, 0) ],
[ 2.21, controlchange(64, 127) ],
[ 2.02, ds7(0.60, 87) ],
[ 2.53, cs7(0.47, 71) ],
[ 2.99, c7(0.49, 82) ],
[ 3.77, controlchange(64, 0) ],
[ 3.98, controlchange(64, 127) ],
[ 3.50, gs6(0.80, 83) ],
[ 4.49, ds7(0.23, 87) ],
[ 5.04, ds7(0.23, 81) ],
[ 5.48, ds7(0.24, 88) ],
[ 5.99, ds7(0.59, 83) ],
[ 6.47, cs7(0.48, 78) ],
[ 6.99, c7(0.48, 70) ],
[ 7.79, controlchange(64, 0) ],
[ 8.07, controlchange(64, 127) ],
[ 7.49, gs6(0.68, 94) ],
[ 8.55, ds7(0.24, 88) ],
[ 9.02, ds7(0.26, 71) ],
[ 9.52, ds7(0.25, 77) ],
[ 10.00, ds7(0.59, 76) ],
[ 10.52, cs7(0.37, 73) ],
[ 10.97, c7(0.48, 64) ],
[ 11.67, controlchange(64, 0) ],
[ 11.87, controlchange(64, 127) ],
[ 11.49, gs6(0.67, 78) ],
[ 12.52, gs6(0.44, 64) ],
[ 13.06, as6(0.24, 86) ],
[ 13.69, as6(0.20, 66) ],
[ 14.02, controlchange(64, 0) ],
[ 13.93, c7(0.17, 87) ],
[ 14.19, controlchange(64, 127) ],
[ 14.08, as6(0.47, 86) ],
[ 14.57, gs6(0.25, 76) ],
[ 15.04, f6(0.47, 44) ],
[ 15.99, controlchange(64, 0) ]].fixVelocity(60));
const strings4 = () => createTrack(1).play([[ 0.09, c6(1.70, 94) ],
[ 0.10, f6(1.75, 93) ],
[ 0.08, gs5(1.78, 98) ],
[ 2.11, g5(1.58, 93) ],
[ 2.09, ds6(1.70, 88) ],
[ 2.08, as5(1.72, 96) ],
[ 4.04, gs5(3.61, 96) ],
[ 4.05, ds6(3.70, 94) ],
[ 4.02, c6(3.80, 95) ],
[ 8.02, as5(1.76, 94) ],
[ 8.04, f5(1.78, 97) ],
[ 8.03, cs6(1.80, 88) ],
[ 10.05, f5(1.67, 87) ],
[ 10.05, gs5(1.67, 94) ],
[ 10.06, c6(1.74, 88) ],
[ 12.07, f5(1.85, 94) ],
[ 12.06, cs6(1.87, 94) ],
[ 12.07, gs5(1.87, 96) ],
[ 14.14, f5(1.84, 84) ],
[ 14.15, cs6(1.84, 81) ],
[ 14.16, as5(1.83, 94) ]].fixVelocity(110));
// transition flute
createTrack(6).play([[ 16.48, ds7(0.55, 82) ],
[ 17.01, gs7(0.40, 82) ],
[ 17.45, ds7(0.19, 51) ],
[ 17.98, gs7(0.25, 80) ],
[ 18.10, as7(0.89, 80) ],
[ 19.02, c8(0.47, 75) ],
[ 19.49, ds7(1.21, 75) ],
[ 21.53, ds8(1.85, 75) ],
[ 23.29, cs8(0.22, 61) ],
[ 23.52, ds8(0.30, 82) ],
[ 23.72, cs8(0.17, 68) ],
[ 23.89, c8(0.31, 82) ],
[ 24.11, cs8(0.82, 83) ],
[ 25.25, cs8(0.34, 57) ],
[ 25.48, ds8(0.50, 78) ],
[ 25.98, c8(0.64, 82) ],
[ 26.53, gs7(0.61, 83) ],
[ 27.99, cs8(1.26, 80) ],
[ 29.30, c8(0.28, 72) ],
[ 29.53, cs8(0.30, 66) ],
[ 29.73, ds8(0.48, 82) ],
[ 30.17, c8(0.43, 87) ],
[ 30.57, gs7(0.53, 84) ]]);
for (var n=0;n<2;n++) {
if (n == 1) {
strings4();
createTrack(2).steps(4, [
,,,,
,,,,
,,,,
,,,,
,,,,
,,,,
,,,,
,,,,
,,,,
,,,,
,,,,
,,,,
d5(0.1,10),d5(0.1,20),d5(0.1,30),d5(0.1,40),
,d5(0.1,60),d5(0.1,70),d5(0.1,80),
d5(0.1,90),d5(0.1,40),d5(0.1,100),d5(0.1,80),
,d5,d5(0.1,110),
]);
}
padsynth4();
bass1_2();
createTrack(2).steps(4,[
f5(0.1,100),,,f5(0.2,80),
,,,,
b5(0.1,100),,,b5(0.2,80),
,,,,
].repeat(3));
piano4();
choir2();
await beatwithsnare2();
await beatwithsnare2();
}
for (var n=0;n<2;n++) {
guitar2();
bass2();
padsynth2();
choir2();
await beatwithsnare2();
await beatwithsnare2();
}
for (var n=0;n<2;n++) {
strings3();
flute3();
bass2();
padsynth2();
choir2();
await beatwithsnare2();
await beatwithsnare2();
}
const strings5 = () => createTrack(1).play([[ 0.05, f6(1.67, 94) ],
[ 0.03, gs6(1.76, 87) ],
[ 0.05, c7(1.82, 95) ],
[ 2.03, g6(1.31, 97) ],
[ 2.05, ds6(1.34, 84) ],
[ 2.02, as6(1.40, 92) ],
[ 3.56, ds6(4.08, 88) ],
[ 3.57, c7(4.31, 94) ],
[ 3.55, gs6(4.39, 96) ],
[ 8.01, cs7(1.93, 99) ],
[ 8.01, f6(1.93, 94) ],
[ 7.97, as6(2.00, 92) ],
[ 10.04, f6(1.31, 15) ],
[ 10.08, c7(1.30, 87) ],
[ 10.07, gs6(1.38, 91) ],
[ 11.67, f6(1.64, 88) ],
[ 11.66, gs6(1.67, 83) ],
[ 11.67, cs7(1.68, 101) ],
[ 13.50, cs7(2.49, 103) ],
[ 13.48, as6(2.49, 94) ],
[ 13.50, f6(2.49, 94) ]]);
for (var n=0;n<2;n++) {
strings5();
guitar2();
bass2();
padsynth2();
choir2();
await beatwithsnare2();
await beatwithsnare2();
}
const piano5 = () => createTrack(0).play([[ 0.20, controlchange(64, 127) ],
[ 0.06, ds7(0.47, 93) ],
[ 0.52, gs6(0.40, 62) ],
[ 1.48, gs6(0.44, 57) ],
[ 0.10, f5(1.85, 68) ],
[ 2.04, controlchange(64, 0) ],
[ 1.04, ds7(1.01, 81) ],
[ 2.24, controlchange(64, 127) ],
[ 2.00, as6(0.48, 73) ],
[ 2.82, as6(0.20, 66) ],
[ 3.01, c7(0.32, 79) ],
[ 2.03, g5(1.80, 67) ],
[ 4.04, controlchange(64, 0) ],
[ 4.34, controlchange(64, 127) ],
[ 3.47, ds6(3.07, 69) ],
[ 6.53, as6(0.98, 88) ],
[ 4.03, gs5(4.01, 57) ],
[ 8.06, controlchange(64, 0) ],
[ 7.47, c7(0.60, 83) ],
[ 8.29, controlchange(64, 127) ],
[ 8.01, cs7(0.46, 87) ],
[ 8.47, f6(0.53, 59) ],
[ 9.00, cs7(0.50, 94) ],
[ 8.04, as5(1.79, 81) ],
[ 9.97, controlchange(64, 0) ],
[ 9.47, f6(0.54, 48) ],
[ 10.16, controlchange(64, 127) ],
[ 10.01, c7(0.41, 84) ],
[ 10.01, f5(0.77, 64) ],
[ 11.03, cs7(0.43, 92) ],
[ 12.16, controlchange(64, 0) ],
[ 14.04, controlchange(64, 127) ],
[ 11.5, cs5(3.65, 68) ],
[ 11.48, f6(4.26, 78) ],
[ 15.76, controlchange(64, 0) ]]);
padsynth1();
piano5();
bass1();
flute1();
await beat2();
await beat2();
await beat2();
await beat2();
createTrack(1).steps(4, [
f3(2),,,,
,,,,
g3(6/4),,,,
,,gs3(18/4),,
,,,,
,,,,
,,,,
,,,,
as3(2),,,,
,,,,
f3(6/4),,,,
,,cs3(12/4),,
,,,,
,,,,
,,[as3(4/4,50),f5(4/4,30)]
]);
bass1_2();
piano5();
flute1();
await beat2();
await beat2();
await beat2();
await beat2();
await createTrack(2).steps(1,[[c5,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);
}
class XorshiftPRNG {
private state: Uint32Array;
constructor(seed: u32) {
this.state = new Uint32Array(2);
this.init(seed);
}
init(seed: u32): void {
this.state[0] = seed;
this.state[1] = 0x9e3779b9;
}
t(): u32 {
let t = this.state[0] + this.state[1];
this.state[0] = this.state[1];
this.state[1] = t;
t ^= t << 23;
t ^= t >> 17;
t ^= this.state[0];
t ^= this.state[0] >> 26;
return t;
}
random(): f32 {
return ((this.t() / Math.pow(2, 32)) - 0.5) as f32;
}
}
let seed = 1;
class MultiXorshiftPRNG {
prngs: StaticArray<XorshiftPRNG> = new StaticArray<XorshiftPRNG>(8);
constructor() {
for (let n=0;n<this.prngs.length;n++) {
this.prngs[n] = new XorshiftPRNG(seed);
seed ++;
}
}
random(): f32 {
let t: u32;
t = this.prngs[0].t();
for (let n=1;n<this.prngs.length;n++) {
t ^= this.prngs[n].t();
}
return ((t / Math.pow(2, 32)) - 0.5) as f32;
}
}
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 exciterStartVal = Mathf.exp(Mathf.E);
export class PianoExciterWaveGuide {
filterExciter: BiQuadFilter = new BiQuadFilter();
noise: MultiXorshiftPRNG = new MultiXorshiftPRNG();
t: f32;
vel: f32;
start(lo: f32,notefreq: f32): void {
this.t = 0;
this.vel = lo;
this.filterExciter.clearBuffers();
const filterbasefreq: f32 = (7000 + Mathf.pow(notefreq , 0.9)) * this.vel;
this.filterExciter.update_coeffecients(FilterType.LowPass, SAMPLERATE,
filterbasefreq, Q_BUTTERWORTH);
}
process(): f32 {
this.t += 20/ SAMPLERATE;
let signal = this.noise.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>(3);
dispersionmodindex: f64 = 0.0;
dispersiondelta: f32;
level: f32;
notefreq: f32;
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 =7000 + Mathf.pow(freq, 1.05);
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 = 0.0001;
const M = this.allpasses.length;
const f0: f64 = freq;
const Bc = Math.max(B,0.000001);
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);
const dispersion: f32 = Df0 as f32;
for(let n=0;n<this.allpasses.length;n++) {
this.allpasses[n].clearBuffers();
this.allpasses[n].setDelta(dispersion);
}
this.filterFeedback.clearBuffers();
this.delay.setNumFramesAndClear(delaysize - (dispersion * (M as f32)));
let feedbacklevel: f32 = Mathf.pow(0.2/ this.filterFeedback.coeffs.magnitude, 1 / freq);
this.feedbackLevel = feedbacklevel;
this.freq = freq;
}
}
process(): f32 {
let exciterSignal: f32 = this.exciterWaveGuide.process();
let feedback = this.delay.read();
let signal = exciterSignal;
signal+=this.filterFeedback.process(feedback);
for(let n=0;n<this.allpasses.length;n++) signal = this.allpasses[n].process(signal);
this.delay.write_and_advance(
signal * this.feedbackLevel
);
return signal;
}
}
class Piano extends MidiVoice {
env: Envelope = new Envelope(0.0005, 0.17, 1.0, 0.17);
waveguide1: PianoWaveGuide = new PianoWaveGuide();
waveguide2: PianoWaveGuide = new PianoWaveGuide();
noteon(note: u8, velocity: u8): void {
super.noteon(note, velocity);
this.waveguide1.start2(note as f32, velocity);
this.waveguide2.start2(note as f32, velocity);
this.env.attack();
}
noteoff(): void {
this.env.release();
}
isDone(): boolean {
return this.env.isDone();
}
nextframe(): void {
let env = this.env.next();
const wg1: f32 = this.waveguide1.process() * env;
const wg2: f32 = this.waveguide2.process() * env;
this.channel.signal.add(wg1, wg2);
}
}
const numpianofilters = 30;
const feedback: f32 = 0.04;
const dampening: f32 = 0.1;
class PianoChannel extends MidiChannel {
allpassesLeft: StaticArray<Comb> = new StaticArray<Comb>(numpianofilters);
allpassesRight: StaticArray<Comb> = new StaticArray<Comb>(numpianofilters);
hipassLeft: BiQuadFilter = new BiQuadFilter();
hipassRight: BiQuadFilter = new BiQuadFilter();
constructor(numvoices: i32, factoryFunc: (channel: MidiChannel, voiceindex: i32) => MidiVoice) {
super(numvoices, factoryFunc);
let combsize: usize = SAMPLERATE * 0.000055 as usize;
if (combsize == 0) combsize = 1;
const hipassfreq: f32 = 150;
for(let n=0;n<this.allpassesLeft.length;n++) {
this.allpassesLeft[n] = new Comb(combsize);
this.allpassesLeft[n].set_feedback(feedback);
this.allpassesLeft[n].set_dampening(dampening);
this.allpassesRight[n] = new Comb(combsize);
this.allpassesRight[n].set_feedback(feedback);
this.allpassesRight[n].set_dampening(dampening);
}
this.hipassLeft.update_coeffecients(FilterType.HighPass, SAMPLERATE, hipassfreq, Q_BUTTERWORTH);
this.hipassRight.update_coeffecients(FilterType.HighPass, SAMPLERATE, hipassfreq, Q_BUTTERWORTH);
}
preprocess(): void {
const in_gain: f32 = 3 * Mathf.pow(10.0, -4);
let in_left = this.signal.left * in_gain;
let in_right = this.signal.right * in_gain;
let left = this.hipassLeft.process(in_left);
let right = this.hipassRight.process(in_right);
const spread: f32 = 0.99999;
for (let n=0;n<this.allpassesLeft.length;n++) {
left += this.allpassesLeft[n].tick(left * (1.0-spread) + right * spread);
left *= (1-feedback);
right += this.allpassesRight[n].tick(right * (1.0-spread) + left * spread);
right *= (1-feedback);
}
const combgain: f32 = 2* Mathf.pow(10.0, -9);
this.signal.left = in_left + left * combgain;
this.signal.right = in_right + right * combgain;
}
}
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, 120);
midichannels[0].controlchange(10, 64);
midichannels[0].controlchange(91,60);
midichannels[1] = new MidiChannel(10, (ch) => new String(ch));
midichannels[1].controlchange(7,50);
midichannels[1].controlchange(10, 64);
midichannels[1].controlchange(91, 40);
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, 10);
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, 20);
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);
limiter_left.setRelaseSampleCount((0.05 * SAMPLERATE) as usize);
limiter_right.setRelaseSampleCount((0.05 * SAMPLERATE) as usize);
const midsideprocessor = new MidSideProcessor(1.7);
export function postprocess(): void {
const gain: f32 = 3.0;
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 = true;
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.2;
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