improve csound bindings:

- better timing
- can now pass instrument to .csound(...)
- can now write instruments with csound(...)
This commit is contained in:
Felix Roos 2022-11-29 21:59:22 +01:00
parent 729e0afdbe
commit c40078ffc9
4 changed files with 236 additions and 66 deletions

60
packages/csound/csd.mjs Normal file
View file

@ -0,0 +1,60 @@
export function csd(code = '') {
return `
<CsoundSynthesizer>
<CsOptions>
-o dac --port=10000 --sample-accurate
</CsOptions>
<CsInstruments>
sr=48000
ksmps=64
nchnls=2
0dbfs=1
instr organ
iduration = p3
ifreq = p4
igain = p5
ioct = octcps(ifreq)
asig = vco2(igain, ifreq, 12, .5) ; my edit
kpwm = oscili(.1, 5)
asig = vco2(igain, ifreq, 4, .5 + kpwm)
asig += vco2(igain/4, ifreq * 2)
; filter
; idepth = 2
; acut = transegr:a(0, .005, 0, idepth, .06, -4.2, 0.001, .01, -4.2, 0) ; filter envelope
; asig = zdf_2pole(asig, cpsoct(ioct + acut), 0.5)
; amp envelope
iattack = .001
irelease = .05
asig *= linsegr:a(0, iattack, 1, iduration, 1, irelease, 0)
out(asig, asig)
endin
instr triangle
iduration = p3
ifreq = p4
igain = p5
ioct = octcps(ifreq)
asig = vco2(igain, ifreq, 12, .5)
; amp envelope
iattack = .001
irelease = .05
asig *= linsegr:a(0, iattack, 1, iduration, 1, irelease, 0)
out(asig, asig)
endin
${code}
</CsInstruments>
</CsoundSynthesizer>
`;
}

View file

@ -1,33 +1,72 @@
import { Pattern } from '@strudel.cycles/core';
import { getFrequency, logger, Pattern } from '@strudel.cycles/core';
import { Csound } from '@csound/browser'; // TODO: use dynamic import for code splitting..
import csd from './sounds.csd?raw';
import { csd } from './csd.mjs';
import { getAudioContext } from '@strudel.cycles/webaudio';
let csoundLoader;
let csoundLoader, _csound;
Pattern.prototype.csound = async function () {
if (!csoundLoader) {
csoundLoader = (async () => {
const csound = await Csound({ audioContext: getAudioContext() });
await csound.setOption('-m0');
await csound.compileCsdText(csd);
await csound.setControlChannel('main.note.amp', -12);
await csound.start();
return csound;
})();
}
const csound = await csoundLoader;
// triggers given instrument name using csound. expects csound function to be called in advance `await csound()`
Pattern.prototype._csound = function (instrument) {
instrument = instrument || 'triangle';
return this.onTrigger((time, hap, currentTime) => {
const { gain = 0.8 } = hap.value;
const deadline = time - currentTime;
const midi = toMidi(getPlayableNoteValue(hap));
if (!_csound) {
logger('[csound] not loaded yet', 'warning');
return;
}
let { gain = 0.8 } = hap.value;
gain *= 0.2;
// const midi = toMidi(getPlayableNoteValue(hap));
const freq = getFrequency(hap);
// TODO: find out how to send a precise ctx based time
// const ctime = `next_time(0.0001)+${deadline.toFixed(4)}`;
const ctime = `${deadline.toFixed(8)}`;
const cmidi = `cpsmidinn(${midi})`;
const cgain = gain ? `ampdbfs(-32 + 32*${gain})` : `0`;
const code = `schedule(1, ${ctime}, .125, ${cmidi}, ${cgain})`;
// console.log('code', code);
csound.evalCode(code);
// http://www.csounds.com/manual/html/i.html
const params = [
`"${instrument}"`, // p1: instrument name
time - currentTime, //.toFixed(precision), // p2: starting time in arbitrary unit called beats
hap.duration + 0, // p3: duration in beats
// instrument specific params:
freq, //.toFixed(precision), // p4: frequency
// -48 + gain * 24, // p5: gain
gain, // p5: gain
];
const msg = `i ${params.join(' ')}`;
// console.log('msg', msg);
_csound.inputMessage(msg);
// _csound.readScore(msg); // slower alternative
// even slower alternative:
/* const code = `schedule(${params.join(', ')})`;
_csound.evalCode(code); */
});
};
// initializes csound + can be used to reevaluate given instrument code
export async function csound(code = '') {
code = csd(code);
let isInit = false;
if (!csoundLoader) {
isInit = true;
csoundLoader = (async () => {
_csound = await Csound({ audioContext: getAudioContext() });
await _csound.setOption('-m0');
await _csound.compileCsdText(code);
await _csound.start();
})();
}
await csoundLoader;
!isInit && (await _csound?.compileCsdText(code));
}
// experimental: allows using jsx to eval csound
window.jsxPragma = function (fn, args, text) {
return fn(text);
};
// experimental: for use via JSX as <CsInstruments>...</CsInstruments>
export function CsInstruments(text) {
if (Array.isArray(text)) {
text = text[0];
}
return csound(text);
}
Pattern.prototype.define('csound', (a, pat) => pat.csound(a), { composable: false, patternified: true });

View file

@ -1,41 +0,0 @@
;; based on https://kunstmusik.github.io/icsc2022-csound-web/tutorial2-interacting-with-csound/
<CsoundSynthesizer>
<CsOptions>
-o dac --port=10000
</CsOptions>
<CsInstruments>
sr=48000
ksmps=64
nchnls=2
0dbfs=1
instr 1
ioct = octcps(p4)
kpwm = oscili(.1, 5)
asig = vco2(p5, p4, 4, .5 + kpwm)
asig += vco2(p5, p4 * 2)
idepth = 3
acut = transegr:a(0, .005, 0, idepth, .06, -4.2, 0.001, .01, -4.2, 0)
asig = zdf_2pole(asig, cpsoct(ioct + acut), 0.125)
asig *= linsegr:a(1, p3, 1, .125, 0)
out(asig, asig)
endin
opcode next_time, i, i
inext xin
itime = times:i()
iticks = round(itime / inext)
iticks += 1
iout = (iticks * inext) - itime
xout iout
endop
</CsInstruments>
</CsoundSynthesizer>