diff --git a/packages/csound/csd.mjs b/packages/csound/csd.mjs
new file mode 100644
index 00000000..67adbb3e
--- /dev/null
+++ b/packages/csound/csd.mjs
@@ -0,0 +1,60 @@
+export function csd(code = '') {
+ return `
+
+
+-o dac --port=10000 --sample-accurate
+
+
+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}
+
+
+
+`;
+}
diff --git a/packages/csound/csound.mjs b/packages/csound/csound.mjs
index 297e5b51..80b0fddf 100644
--- a/packages/csound/csound.mjs
+++ b/packages/csound/csound.mjs
@@ -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 ...
+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 });
diff --git a/packages/csound/sounds.csd b/packages/csound/sounds.csd
deleted file mode 100644
index 8cbd4d52..00000000
--- a/packages/csound/sounds.csd
+++ /dev/null
@@ -1,41 +0,0 @@
-;; based on https://kunstmusik.github.io/icsc2022-csound-web/tutorial2-interacting-with-csound/
-
-
--o dac --port=10000
-
-
-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
-
-
-
-
\ No newline at end of file
diff --git a/repl/src/tunes.mjs b/repl/src/tunes.mjs
index c9224d7a..c65224e8 100644
--- a/repl/src/tunes.mjs
+++ b/repl/src/tunes.mjs
@@ -991,3 +991,115 @@ export const juxUndTollerei = `note("c3 eb3 g3 bb3").palindrome()
.room(.6)
.delay(.5).delaytime(.1).delayfeedback(.4)
.pianoroll()`;
+
+export const csoundTest = `await csound\`
+instr sawtooth
+ iduration = p3
+ ifreq = p4
+ igain = p5
+ ioct = octcps(ifreq)
+
+ asig = vco2(igain, ifreq, 0)
+
+ ; amp envelope
+ iattack = .5
+ irelease = .1
+ asig *= linsegr:a(0, iattack, 1, iduration, 1, irelease, 0)
+
+ idepth = 2
+ acut = transegr:a(0, .005, 0, idepth, .06, -4.2, 0.001, .01, -4.2, 0)
+ asig = zdf_2pole(asig, 1000, 2)
+
+ out(asig, asig)
+endin\`
+
+stack(
+ note("/2".voicings()).s('sawtooth'),
+ note("/2").s('sawtooth')
+)
+.csound('sawtooth')`;
+
+export const csoundTest2 = `await csound\`
+instr organ
+ iduration = p3
+ ifreq = p4
+ igain = p5
+ ioct = octcps(ifreq)
+
+ kpwm = oscili(.5, 2)
+ asig = vco2(igain, ifreq, 4, .5 + kpwm)
+ asig += vco2(igain/4, ifreq * 2)
+
+ iattack = .01
+ irelease = .005
+ asig *= linsegr:a(0, iattack, 1, iduration, 0, irelease, 0)
+
+ out(asig, asig)
+endin\`
+
+"<0 2 [4 6](3,4,1) 3*2>"
+.off(1/4, add(2))
+.off(1/2, add(6))
+.scale('D minor')
+.legato(perlin.range(.2,2).slow(8))
+// .echo(4, 1/8, .5)
+.note()
+.pianoroll()
+.csound('organ');`;
+
+export const csoundTest3 = `await csound\`
+instr CoolSynth
+ iduration = p3
+ ifreq = p4
+ igain = p5
+ ioct = octcps(ifreq)
+
+ kpwm = oscili(.05, 8)
+ asig = vco2(igain, ifreq, 4, .5 + kpwm)
+ asig += vco2(igain, ifreq * 2)
+
+ 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 + 2), 0.5)
+
+ iattack = .01
+ isustain = .5
+ idecay = .1
+ irelease = .1
+ asig *= linsegr:a(0, iattack, 1, idecay, isustain, iduration, isustain, irelease, 0)
+
+ out(asig, asig)
+endin\`
+
+"<0 2 [4 6](3,4,1) 3*2>"
+.off(1/4, add(2))
+.off(1/2, add(6))
+.scale('D minor')
+.note()
+//.pianoroll()
+.csound("/4")`;
+
+export const csoundTest4 = `await csound()
+
+stack(
+ note("/2".voicings()).csound('organ').gain(.5),
+ note("/2".superimpose(add(.1))).s('sawtooth').cutoff(800).resonance(10).shape(.3)
+)`;
+
+export const csoundMixed = `await csound()
+
+stack(
+ "/2".voicings()
+ .add(rand.range(-.1,.1)).note()
+ .csound('organ').gain(1).struct("[~@2 x]*2").legato(.25)
+ ,
+ "/2"
+ .superimpose(add(rand.range(-.1,.1))).note()
+ .s('sawtooth').cutoff(perlin.range(200,500)).resonance(10)
+ .struct("x(4,6,1) x(5,6,2)")
+ .decay(.1).sustain(0)
+ ,
+ s("bd*2,hh:1(4,6),[~ sd]/2")
+ .room(.5)
+ .speed(perlin.range(.9,1.1).slow(4))
+).slow(2)`;