mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-26 04:58:27 +00:00
Merge pull request #275 from tidalcycles/csound
add basic csound output
This commit is contained in:
commit
01eca562c5
7215
package-lock.json
generated
7215
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
119
packages/csound/csound.mjs
Normal file
119
packages/csound/csound.mjs
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import { getFrequency, logger, Pattern } from '@strudel.cycles/core';
|
||||||
|
import { getAudioContext } from '@strudel.cycles/webaudio';
|
||||||
|
import csd from './project.csd?raw';
|
||||||
|
// import livecodeOrc from './livecode.orc?raw';
|
||||||
|
import presetsOrc from './presets.orc?raw';
|
||||||
|
|
||||||
|
let csoundLoader, _csound;
|
||||||
|
|
||||||
|
// triggers given instrument name using csound.
|
||||||
|
Pattern.prototype._csound = function (instrument) {
|
||||||
|
instrument = instrument || 'triangle';
|
||||||
|
init(); // not async to support csound inside other patterns + to be able to call pattern methods after it
|
||||||
|
// TODO: find a alternative way to wait for csound to load (to wait with first time playback)
|
||||||
|
return this.onTrigger((time, hap) => {
|
||||||
|
if (!_csound) {
|
||||||
|
logger('[csound] not loaded yet', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof hap.value !== 'object') {
|
||||||
|
throw new Error('csound only support objects as hap values');
|
||||||
|
}
|
||||||
|
let { gain = 0.8 } = hap.value;
|
||||||
|
gain *= 0.2;
|
||||||
|
|
||||||
|
const freq = Math.round(getFrequency(hap));
|
||||||
|
const controls = Object.entries({ ...hap.value, freq })
|
||||||
|
.flat()
|
||||||
|
.join('/');
|
||||||
|
// TODO: find out how to send a precise ctx based time
|
||||||
|
// http://www.csounds.com/manual/html/i.html
|
||||||
|
const params = [
|
||||||
|
`"${instrument}"`, // p1: instrument name
|
||||||
|
time - getAudioContext().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
|
||||||
|
gain, // p5: gain
|
||||||
|
`"${controls}"`, // p6 controls as string (like superdirt osc message)
|
||||||
|
];
|
||||||
|
const msg = `i ${params.join(' ')}`;
|
||||||
|
_csound.inputMessage(msg);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// initializes csound + can be used to reevaluate given instrument code
|
||||||
|
export async function csound(code = '') {
|
||||||
|
await init();
|
||||||
|
if (code) {
|
||||||
|
code = `${code}`;
|
||||||
|
// ^ ^
|
||||||
|
// wrapping in backticks makes sure it works when calling as templated function
|
||||||
|
await _csound?.evalCode(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern.prototype.define('csound', (a, pat) => pat.csound(a), { composable: false, patternified: true });
|
||||||
|
|
||||||
|
function eventLogger(type, args) {
|
||||||
|
const [msg] = args;
|
||||||
|
if (
|
||||||
|
type === 'message' &&
|
||||||
|
(['[commit: HEAD]'].includes(msg) ||
|
||||||
|
msg.startsWith('--Csound version') ||
|
||||||
|
msg.startsWith('libsndfile') ||
|
||||||
|
msg.startsWith('sr =') ||
|
||||||
|
msg.startsWith('0dBFS') ||
|
||||||
|
msg.startsWith('audio buffered') ||
|
||||||
|
msg.startsWith('writing') ||
|
||||||
|
msg.startsWith('SECTION 1:'))
|
||||||
|
) {
|
||||||
|
// ignore
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let logType = 'info';
|
||||||
|
if (msg.startsWith('error:')) {
|
||||||
|
logType = 'error';
|
||||||
|
}
|
||||||
|
logger(`[csound] ${msg || ''}`, logType);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
if (window.__csound__) {
|
||||||
|
// allows using some other csound instance
|
||||||
|
_csound = window.__csound__;
|
||||||
|
} else {
|
||||||
|
const { Csound } = await import('@csound/browser');
|
||||||
|
_csound = await Csound({ audioContext: getAudioContext() });
|
||||||
|
}
|
||||||
|
_csound.removeAllListeners('message');
|
||||||
|
['message'].forEach((k) => _csound.on(k, (...args) => eventLogger(k, args)));
|
||||||
|
await _csound.setOption('-m0d'); // see -m flag https://csound.com/docs/manual/CommandFlags.html
|
||||||
|
await _csound.setOption('--sample-accurate');
|
||||||
|
await _csound.compileCsdText(csd);
|
||||||
|
// await _csound.compileOrc(livecodeOrc);
|
||||||
|
await _csound.compileOrc(presetsOrc);
|
||||||
|
await _csound.start();
|
||||||
|
return _csound;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
csoundLoader = csoundLoader || load();
|
||||||
|
return csoundLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
let orcCache = {};
|
||||||
|
export async function loadOrc(url) {
|
||||||
|
await init();
|
||||||
|
if (typeof url !== 'string') {
|
||||||
|
throw new Error('loadOrc: expected url string');
|
||||||
|
}
|
||||||
|
if (url.startsWith('github:')) {
|
||||||
|
const [_, path] = url.split('github:');
|
||||||
|
url = `https://raw.githubusercontent.com/${path}`;
|
||||||
|
}
|
||||||
|
if (!orcCache[url]) {
|
||||||
|
orcCache[url] = await fetch(url).then((res) => res.text());
|
||||||
|
}
|
||||||
|
await _csound.compileOrc(orcCache[url]);
|
||||||
|
}
|
||||||
2226
packages/csound/livecode.orc
Normal file
2226
packages/csound/livecode.orc
Normal file
File diff suppressed because it is too large
Load Diff
32
packages/csound/package.json
Normal file
32
packages/csound/package.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "@strudel.cycles/csound",
|
||||||
|
"version": "0.3.0",
|
||||||
|
"description": "csound bindings for strudel",
|
||||||
|
"main": "csound.mjs",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"No tests present.\" && exit 0"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/tidalcycles/strudel.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"tidalcycles",
|
||||||
|
"strudel",
|
||||||
|
"pattern",
|
||||||
|
"livecoding",
|
||||||
|
"algorave"
|
||||||
|
],
|
||||||
|
"author": "Felix Roos <flix91@gmail.com>",
|
||||||
|
"contributors": [
|
||||||
|
"Alex McLean <alex@slab.org>"
|
||||||
|
],
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/tidalcycles/strudel/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||||
|
"dependencies": {
|
||||||
|
"@csound/browser": "^6.18.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
96
packages/csound/presets.orc
Normal file
96
packages/csound/presets.orc
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
; returns value of given key in given "string map"
|
||||||
|
; keymap("freq", "note/c3/freq/220/gain/0.5")
|
||||||
|
; yields "220"
|
||||||
|
opcode keymap, S, SS
|
||||||
|
Skey, Smap xin
|
||||||
|
idelimiter = strindex(Smap, strcat(Skey, "/"))
|
||||||
|
ifrom = idelimiter + strlen(Skey) + 1
|
||||||
|
Svalue = strsub(Smap, ifrom, strlen(Smap))
|
||||||
|
Svalue = strsub(Svalue, 0, strindex(Svalue, "/"))
|
||||||
|
xout Svalue
|
||||||
|
endop
|
||||||
|
|
||||||
|
; TODO add incredibly dope synths
|
||||||
|
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
|
||||||
|
|
||||||
|
instr pad
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
gisine ftgen 0, 0, 4096, 10, 1
|
||||||
|
|
||||||
|
instr bow
|
||||||
|
kpres = 2
|
||||||
|
krat = 0.16
|
||||||
|
kvibf = 6.12723
|
||||||
|
|
||||||
|
kvib linseg 0, 0.5, 0, 1, 1, p3-0.5, 1
|
||||||
|
kvamp = kvib * 0.01
|
||||||
|
asig wgbow .7, p4, kpres, krat, kvibf, kvamp, gisine
|
||||||
|
asig = asig*p5
|
||||||
|
outs asig, asig
|
||||||
|
endin
|
||||||
|
|
||||||
|
|
||||||
|
instr Meta
|
||||||
|
Smap = strget(p6)
|
||||||
|
Sinstrument = keymap("s", Smap)
|
||||||
|
schedule(Sinstrument, 0, p3, p4, p5)
|
||||||
|
; TODO find a way to pipe Sinstrument through effects
|
||||||
|
endin
|
||||||
10
packages/csound/project.csd
Normal file
10
packages/csound/project.csd
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<CsoundSynthesizer>
|
||||||
|
<CsInstruments>
|
||||||
|
|
||||||
|
sr=48000
|
||||||
|
ksmps=64
|
||||||
|
nchnls=2
|
||||||
|
0dbfs=1
|
||||||
|
|
||||||
|
</CsInstruments>
|
||||||
|
</CsoundSynthesizer>
|
||||||
@ -42,6 +42,7 @@ const modules = [
|
|||||||
import('@strudel.cycles/osc'),
|
import('@strudel.cycles/osc'),
|
||||||
import('@strudel.cycles/serial'),
|
import('@strudel.cycles/serial'),
|
||||||
import('@strudel.cycles/soundfonts'),
|
import('@strudel.cycles/soundfonts'),
|
||||||
|
import('@strudel.cycles/csound'),
|
||||||
];
|
];
|
||||||
|
|
||||||
evalScope(
|
evalScope(
|
||||||
|
|||||||
@ -88,6 +88,9 @@ const toneHelpersMocked = {
|
|||||||
strudel.Pattern.prototype.osc = function () {
|
strudel.Pattern.prototype.osc = function () {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
strudel.Pattern.prototype.csound = function () {
|
||||||
|
return this;
|
||||||
|
};
|
||||||
strudel.Pattern.prototype.tone = function () {
|
strudel.Pattern.prototype.tone = function () {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
@ -170,6 +173,8 @@ evalScope(
|
|||||||
{
|
{
|
||||||
// gist,
|
// gist,
|
||||||
// euclid,
|
// euclid,
|
||||||
|
csound: id,
|
||||||
|
loadOrc: id,
|
||||||
mini,
|
mini,
|
||||||
getDrawContext,
|
getDrawContext,
|
||||||
getAudioContext,
|
getAudioContext,
|
||||||
|
|||||||
@ -1379,6 +1379,20 @@ exports[`renders tunes > tune: chop 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`renders tunes > tune: csoundDemo 1`] = `
|
||||||
|
[
|
||||||
|
"0/1 -> 1/1: {\\"note\\":\\"D3\\"}",
|
||||||
|
"-1/4 -> 1/4: {\\"note\\":\\"Bb3\\"}",
|
||||||
|
"1/4 -> 5/4: {\\"note\\":\\"F3\\"}",
|
||||||
|
"0/1 -> 1/2: {\\"note\\":\\"F4\\"}",
|
||||||
|
"1/2 -> 3/2: {\\"note\\":\\"C4\\"}",
|
||||||
|
"-1/4 -> 1/4: {\\"note\\":\\"A4\\"}",
|
||||||
|
"1/4 -> 3/4: {\\"note\\":\\"A4\\"}",
|
||||||
|
"1/4 -> 3/4: {\\"note\\":\\"A4\\"}",
|
||||||
|
"3/4 -> 7/4: {\\"note\\":\\"E4\\"}",
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`renders tunes > tune: delay 1`] = `
|
exports[`renders tunes > tune: delay 1`] = `
|
||||||
[
|
[
|
||||||
"0/1 -> 1/2: {\\"s\\":\\"bd\\",\\"delay\\":0,\\"delaytime\\":0.16,\\"delayfeedback\\":0.8,\\"speed\\":-1}",
|
"0/1 -> 1/2: {\\"s\\":\\"bd\\",\\"delay\\":0,\\"delaytime\\":0.16,\\"delayfeedback\\":0.8,\\"speed\\":-1}",
|
||||||
@ -8054,6 +8068,36 @@ exports[`renders tunes > tune: juxUndTollerei 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`renders tunes > tune: loungeSponge 1`] = `
|
||||||
|
[
|
||||||
|
"0/1 -> 3/8: {\\"note\\":\\"B3\\",\\"cutoff\\":1396}",
|
||||||
|
"0/1 -> 3/8: {\\"note\\":\\"D4\\",\\"cutoff\\":1396}",
|
||||||
|
"0/1 -> 3/8: {\\"note\\":\\"E4\\",\\"cutoff\\":1396}",
|
||||||
|
"0/1 -> 3/8: {\\"note\\":\\"G4\\",\\"cutoff\\":1396}",
|
||||||
|
"3/8 -> 3/4: {\\"note\\":\\"B3\\",\\"cutoff\\":1396}",
|
||||||
|
"3/8 -> 3/4: {\\"note\\":\\"D4\\",\\"cutoff\\":1396}",
|
||||||
|
"3/8 -> 3/4: {\\"note\\":\\"E4\\",\\"cutoff\\":1396}",
|
||||||
|
"3/8 -> 3/4: {\\"note\\":\\"G4\\",\\"cutoff\\":1396}",
|
||||||
|
"3/4 -> 1/1: {\\"note\\":\\"B3\\",\\"cutoff\\":1396}",
|
||||||
|
"3/4 -> 1/1: {\\"note\\":\\"D4\\",\\"cutoff\\":1396}",
|
||||||
|
"3/4 -> 1/1: {\\"note\\":\\"E4\\",\\"cutoff\\":1396}",
|
||||||
|
"3/4 -> 1/1: {\\"note\\":\\"G4\\",\\"cutoff\\":1396}",
|
||||||
|
"0/1 -> 1/4: {\\"note\\":\\"C2\\",\\"gain\\":1}",
|
||||||
|
"1/4 -> 1/2: {\\"note\\":\\"C2\\",\\"gain\\":4}",
|
||||||
|
"1/2 -> 3/4: {\\"note\\":\\"C2\\",\\"gain\\":1}",
|
||||||
|
"3/4 -> 1/1: {\\"note\\":\\"C2\\",\\"gain\\":4}",
|
||||||
|
"0/1 -> 3/16: {\\"note\\":\\"A4\\"}",
|
||||||
|
"3/4 -> 15/16: {\\"note\\":\\"A5\\"}",
|
||||||
|
"-1/4 -> -1/16: {\\"note\\":\\"E5\\"}",
|
||||||
|
"1/2 -> 11/16: {\\"note\\":\\"C5\\"}",
|
||||||
|
"0/1 -> 1/2: {\\"s\\":\\"bd\\",\\"bank\\":\\"RolandTR909\\"}",
|
||||||
|
"1/2 -> 1/1: {\\"s\\":\\"bd\\",\\"bank\\":\\"RolandTR909\\"}",
|
||||||
|
"1/4 -> 1/2: {\\"s\\":\\"hh\\",\\"bank\\":\\"RolandTR909\\"}",
|
||||||
|
"3/4 -> 1/1: {\\"s\\":\\"hh\\",\\"bank\\":\\"RolandTR909\\"}",
|
||||||
|
"1/2 -> 1/1: {\\"s\\":\\"cp\\",\\"bank\\":\\"RolandTR909\\"}",
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`renders tunes > tune: meltingsubmarine 1`] = `
|
exports[`renders tunes > tune: meltingsubmarine 1`] = `
|
||||||
[
|
[
|
||||||
"0/1 -> 3/2: {\\"s\\":\\"bd\\",\\"speed\\":0.7519542165100574}",
|
"0/1 -> 3/2: {\\"s\\":\\"bd\\",\\"speed\\":0.7519542165100574}",
|
||||||
|
|||||||
@ -764,3 +764,57 @@ note("c3 eb3 g3 bb3").palindrome()
|
|||||||
.room(.6)
|
.room(.6)
|
||||||
.delay(.5).delaytime(.1).delayfeedback(.4)
|
.delay(.5).delaytime(.1).delayfeedback(.4)
|
||||||
.pianoroll()`;
|
.pianoroll()`;
|
||||||
|
|
||||||
|
export const csoundDemo = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
// by Felix Roos
|
||||||
|
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('CoolSynth')`;
|
||||||
|
|
||||||
|
export const loungeSponge = `
|
||||||
|
// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
// by Felix Roos
|
||||||
|
// livecode.orc by Steven Yi
|
||||||
|
await loadOrc('github:kunstmusik/csound-live-code/master/livecode.orc')
|
||||||
|
|
||||||
|
stack(
|
||||||
|
note("<C^7 A7 Dm7 Fm7>/2".voicings())
|
||||||
|
.cutoff(sine.range(500,2000).round().slow(16))
|
||||||
|
.euclidLegato(3,8).csound('FM1')
|
||||||
|
,
|
||||||
|
note("<C2 A1 D2 F2>/2").ply(8).csound('Bass').gain("1 4 1 4")
|
||||||
|
,
|
||||||
|
note("0 7 [4 3] 2".fast(2/3).off(".25 .125",add("<2 4 -3 -1>"))
|
||||||
|
.slow(2).scale('A4 minor'))
|
||||||
|
.legato(.25).csound('SynHarp')
|
||||||
|
,
|
||||||
|
s("bd*2,[~ hh]*2,~ cp").bank('RolandTR909')
|
||||||
|
)`;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user