xxxxxxxxxx
275
// csound.js is the Csound WASM module
const csoundjs =
"https://cdn.jsdelivr.net/npm/@csound/browser@6.18.5/dist/csound.js";
// csound is the Csound engine object (null as we start)
let csound = null;
// audio context
let audio_context = null;
// source URL
const srcurl = "https://vlazzarini.github.io/vanilla/10.Tonnetz/";
// CSD file name
const csd = "./gm.csd";
const sfont = "./gm.sf2";
// this is the JS function to start Csound
async function startEngine(func = null) {
// if the Csound object is not initialised
if (csound == null) {
// import the Csound method from csound.js
const { Csound } = await import(csoundjs);
// create a Csound engine object
csound = await Csound();
// get audio context
audio_context = await csound.getAudioContext();
// set realtime audio (dac) output
await csound.setOption("-odac");
// set realtime MIDI input
await csound.setOption("-M0");
// copy the sfont file to the Csound local filesystem
await copyUrlToLocal(srcurl + sfont, sfont);
// copy the CSD file to the Csound local filesystem
await copyUrlToLocal(srcurl + csd, csd);
// compile csound code
await csound.compileCsd(csd);
// start the engine
await csound.start();
}
startSequencer(func);
}
// copy URL to local file
async function copyUrlToLocal(src, dest) {
// fetch the file
let srcfile = await fetch(src, { cache: "no-store" });
// get the file data as an array
let dat = await srcfile.arrayBuffer();
// write the data as a new file in the filesystem
await csound.fs.writeFile(dest, new Uint8Array(dat));
}
function midi(stat,b1,b2)
{
csound.midiMessage(stat,b1,b2);
}
function midiProgram(n, chn = 1){
if(chn >= 1 && chn <= 16)
csound.midiMessage(chn+191,n,0);
}
class Instrument {
constructor(pgm, chn, rel = 0.5) {
this.pgm = pgm;
this.chn = chn;
this.rel = rel;
}
event(time, note, amp) {
let rel = this.rel;
let prog = this.pgm;
let instr = 10 + note / 100000 + this.chn / 100;
if (amp <= 0) instr *= -1;
if (this.chn == 9) {
if (prog == 7) krel = 2;
else rel = 0.5;
if (note == 29 || note == 30) instr = 10.97;
else if (note == 42 || note == 44 || note == 46 || note == 49)
instr = 10.91;
else if (note == 71 || note == 72) instr = 10.92;
else if (note == 73 || note == 74) instr = 10.93;
else if (note == 78 || note == 79) instr = 10.94;
else if (note == 80 || note == 81) instr = 10.95;
else if (note == 86 || note == 87) instr = 10.96;
prog = 317 + this.pgm / 8;
}
return (
"i" +
instr +
" " +
time +
" -1 " +
note +
" " +
128 * amp +
" " +
prog +
" " +
" " +
this.chn +
" " +
rel
);
}
play(time, note = 60, amp = 1) {
csound.inputMessage(this.event(time, note, amp));
return note;
}
stop(time, note = 60) {
csound.inputMessage(this.event(time, note, 0));
return note;
}
}
const piano = new Instrument(0, 16);
const organ = new Instrument(18, 17);
const guitar = new Instrument(24, 18);
const bass = new Instrument(35, 19);
const synth = new Instrument(92, 20);
const flute = new Instrument(73, 21);
const horn = new Instrument(60, 22);
const strings = new Instrument(48, 23);
const violin = new Instrument(40, 24);
const drums = new Instrument(16, 9);
const cello = new Instrument(42, 25);
const brass = new Instrument(60, 26);
const oboe = new Instrument(68, 27);
const bassoon = new Instrument(70, 28);
const clarinet = new Instrument(71, 29);
const marimba = new Instrument(12, 30);
const snare = 40;
const kick = 35;
const crash = 49;
const cymbal = 51;
const tom = 45;
function notes(start) {
let l = [];
for (let i = start; i < 127; i += 12) l.push(i);
return l;
}
const C = notes(0);
const Cs = notes(1);
const Db = Cs;
const D = notes(2);
const Ds = notes(3);
const Eb = Ds;
const E = notes(4);
const Es = notes(5);
const F = Es;
const Fs = notes(6);
const G = notes(7);
const Gs = notes(8);
const Ab = Gs;
const A = notes(9);
const As = notes(10);
const Bb = As;
const B = notes(12);
const O = -999;
const H = 999;
const MSEC = 1000;
let BPM = 60.0;
function secs(b) {
return (b * 60.0) / BPM;
}
function beats(s) {
return (s * BPM) / 60.0;
}
function audioClock() {
if (audio_context) return audio_context.currentTime;
else return 0;
}
let runSequencer = false;
let runClock = false;
let sequencerTime = 0.0;
function sequencerClock(ref){
if(!runClock) return;
let t = secs(1);
let delta = audioClock() - (t + ref);
if (delta >= 0) {
sequencerTime = t + audioClock() - delta;
setTimeout(sequencerClock.bind(null, audioClock() - delta));
} else setTimeout(sequencerClock.bind(null, ref));
}
function sequencer(instr, notes, amp, bbs, ref, n = 0, h = 0) {
if(!runClock) {
runClock = true;
sequencerClock(ref);
}
if(!runSequencer) {
for(let i = 0; i < length; i++) {
let note = n ? notes[n-1] : notes[n-1];
if(note >= 0 && note < 128) instr.stop(0, note);
n = n > 0 ? n - 1 : length - 1;
}
runClock = false;
return;
}
let t = secs(bbs);
let delta = audioClock() - (t + ref);
if (delta >= 0) {
let note = notes[n];
let sched = t - delta;
n = n != notes.length - 1 ? n + 1 : 0;
if(note >= 0 && note < 128) {
if(sched >= 0) {
instr.play(sched, note, amp);
if(instr != drums && notes[n] < 128)
instr.stop(sched+t, note);
else h = note;
}
}
else if(note >= 128) {
console.log(n)
if(notes[n] < 128) {
instr.stop(sched+t, h);
}
}
setTimeout(
sequencer.bind(null, instr, notes, amp, bbs, audioClock() - delta, n, h)
);
} else setTimeout(sequencer.bind(null, instr, notes, amp, bbs, ref, n, h));
}
let seqFunc = null;
function startSequencer(func = null) {
if(func != null) {
if(isSequencerRunning()) {
stopSequencer();
setTimeout(startSequencer.bind(null, func), secs(1)*50);
}
else {
runSequencer = true;
func();
seqFunc = func;
}
}
}
function stopSequencer() {
runSequencer = false;
}
function isSequencerRunning() {
return runSequencer;
}
function syncClock(t, bbs) {
return t - secs(bbs*2-2);
}
function startClock() {
return runClock ? sequencerTime : audioClock() + 1;
}
function setBpm(bpm) {
let delay = secs(1)*50;
if(isSequencerRunning()) {
stopSequencer();
setTimeout(()=> {
BPM = bpm;
startSequencer(seqFunc);
}, delay);
} else BPM = bpm;
}