diff --git a/package.json b/package.json index f4a1f1be..9a28ee30 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "test": "npm run test --workspaces --if-present && cd repl && npm run test", "bootstrap": "lerna bootstrap", "setup": "npm i && npm run bootstrap && cd repl && npm i && cd ../tutorial && npm i", + "snapshot": "cd repl && npm run snapshot", "repl": "cd repl && npm run dev", "osc": "cd packages/osc && npm run server", "build": "rm -rf out && cd repl && npm run build && cd ../tutorial && npm run build", diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 034376cd..f45fbd3c 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -755,19 +755,23 @@ const generic_params = [ const _name = (name, ...pats) => sequence(...pats).withValue((x) => ({ [name]: x })); -const _setter = (func) => +const _setter = (func, name) => function (...pats) { + if (!pats.length) { + return this.fmap((value) => ({ [name]: value })); + } return this.set(func(...pats)); }; generic_params.forEach(([type, name, description]) => { controls[name] = (...pats) => _name(name, ...pats); - Pattern.prototype[name] = _setter(controls[name]); + Pattern.prototype[name] = _setter(controls[name], name); }); // create custom param controls.createParam = (name) => { - Pattern.prototype[name] = _setter(controls[name]); + const func = (...pats) => _name(name, ...pats); + Pattern.prototype[name] = _setter(func, name); return (...pats) => _name(name, ...pats); }; diff --git a/packages/core/gist.js b/packages/core/gist.js index 67924762..9df2dda2 100644 --- a/packages/core/gist.js +++ b/packages/core/gist.js @@ -6,7 +6,7 @@ This program is free software: you can redistribute it and/or modify it under th // this is a shortcut to eval code from a gist // why? to be able to shorten strudel code + e.g. be able to change instruments after links have been generated -export default (route) => - fetch(`https://gist.githubusercontent.com/${route}?cachebust=${Date.now()}`) +export default (route, cache = true) => + fetch(`https://gist.githubusercontent.com/${route}?cachebust=${cache ? '' : Date.now()}`) .then((res) => res.text()) .then((code) => eval(code)); diff --git a/packages/core/hap.mjs b/packages/core/hap.mjs index d82ed662..a0107d87 100644 --- a/packages/core/hap.mjs +++ b/packages/core/hap.mjs @@ -86,11 +86,9 @@ export class Hap { } showWhole() { - return `${this.whole == undefined ? '~' : this.whole.show()}: ${this.value}`; - } - - showWhole() { - return `${this.whole == undefined ? '~' : this.whole.show()}: ${this.value}`; + return `${this.whole == undefined ? '~' : this.whole.show()}: ${ + typeof this.value === 'object' ? JSON.stringify(this.value) : this.value + }`; } combineContext(b) { diff --git a/packages/core/index.mjs b/packages/core/index.mjs index 73977401..e64d3215 100644 --- a/packages/core/index.mjs +++ b/packages/core/index.mjs @@ -15,5 +15,5 @@ export * from './state.mjs'; export * from './timespan.mjs'; export * from './util.mjs'; export * from './speak.mjs'; -export * as gist from './gist.js'; +export { default as gist } from './gist.js'; // export * from './value.mjs'; diff --git a/packages/core/package-lock.json b/packages/core/package-lock.json index 3426c161..a3e4c0fa 100644 --- a/packages/core/package-lock.json +++ b/packages/core/package-lock.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/core", - "version": "0.1.0", + "version": "0.1.2", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/packages/core/package.json b/packages/core/package.json index 02a8f121..a42a93e2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/core", - "version": "0.1.0", + "version": "0.1.2", "description": "Port of Tidal Cycles to JavaScript", "main": "index.mjs", "type": "module", diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index f6afb7df..29558f46 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1049,6 +1049,9 @@ export class Pattern { .unit('c') .slow(factor); } + onTrigger(onTrigger) { + return this._withHap((hap) => hap.setContext({ ...hap.context, onTrigger })); + } } // TODO - adopt value.mjs fully.. @@ -1386,6 +1389,7 @@ export function pr(args) { } export const add = curry((a, pat) => pat.add(a)); +export const chop = curry((a, pat) => pat.chop(a)) export const chunk = curry((a, pat) => pat.chunk(a)); export const chunkBack = curry((a, pat) => pat.chunkBack(a)); export const div = curry((a, pat) => pat.div(a)); diff --git a/packages/embed/package.json b/packages/embed/package.json index 35f5e816..420338e8 100644 --- a/packages/embed/package.json +++ b/packages/embed/package.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/embed", - "version": "0.1.0", + "version": "0.1.1", "description": "Embeddable Web Component to load a Strudel REPL into an iframe", "main": "embed.js", "type": "module", diff --git a/packages/eval/README.md b/packages/eval/README.md index 6b11cb84..a65558b0 100644 --- a/packages/eval/README.md +++ b/packages/eval/README.md @@ -15,9 +15,11 @@ npm i @strudel.cycles/eval --save ```js import { evaluate, extend } from '@strudel.cycles/eval'; -import * as strudel from '@strudel.cycles/core'; -extend(strudel); // add strudel to eval scope +evalScope( + import('@strudel.cycles/core'), + // import other strudel packages here +); // add strudel to eval scope async function run(code) { const { pattern } = await evaluate(code); diff --git a/packages/eval/package-lock.json b/packages/eval/package-lock.json index bedeb9aa..8da61459 100644 --- a/packages/eval/package-lock.json +++ b/packages/eval/package-lock.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/eval", - "version": "0.1.1", + "version": "0.1.3", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/packages/eval/package.json b/packages/eval/package.json index 49349015..43d66ab2 100644 --- a/packages/eval/package.json +++ b/packages/eval/package.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/eval", - "version": "0.1.1", + "version": "0.1.3", "description": "Code evaluator for strudel", "main": "index.mjs", "type": "module", @@ -28,7 +28,7 @@ }, "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { - "@strudel.cycles/core": "^0.1.0", + "@strudel.cycles/core": "^0.1.2", "estraverse": "^5.3.0", "shift-ast": "^6.1.0", "shift-codegen": "^7.0.3", diff --git a/packages/midi/package-lock.json b/packages/midi/package-lock.json index 181c8a39..10248f1e 100644 --- a/packages/midi/package-lock.json +++ b/packages/midi/package-lock.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/midi", - "version": "0.1.1", + "version": "0.1.3", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/packages/midi/package.json b/packages/midi/package.json index ca06cb56..34511a74 100644 --- a/packages/midi/package.json +++ b/packages/midi/package.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/midi", - "version": "0.1.1", + "version": "0.1.3", "description": "Midi API for strudel", "main": "index.mjs", "repository": { @@ -21,7 +21,7 @@ }, "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { - "@strudel.cycles/tone": "^0.1.1", + "@strudel.cycles/tone": "^0.1.3", "tone": "^14.7.77", "webmidi": "^2.5.2" } diff --git a/packages/mini/package.json b/packages/mini/package.json index 4eb52c4f..35677057 100644 --- a/packages/mini/package.json +++ b/packages/mini/package.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/mini", - "version": "0.1.1", + "version": "0.1.3", "description": "Mini notation for strudel", "main": "index.mjs", "type": "module", @@ -25,8 +25,8 @@ }, "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { - "@strudel.cycles/core": "^0.1.0", - "@strudel.cycles/eval": "^0.1.1", - "@strudel.cycles/tone": "^0.1.1" + "@strudel.cycles/core": "^0.1.2", + "@strudel.cycles/eval": "^0.1.3", + "@strudel.cycles/tone": "^0.1.3" } } diff --git a/packages/osc/osc.mjs b/packages/osc/osc.mjs index e3b12823..72eb4e7f 100644 --- a/packages/osc/osc.mjs +++ b/packages/osc/osc.mjs @@ -22,13 +22,15 @@ let startedAt = -1; */ Pattern.prototype.osc = function () { return this._withHap((hap) => { - const onTrigger = (time, hap, currentTime, cps, cycle, delta) => { + const onTrigger = (time, hap, currentTime, cps) => { + const cycle = hap.wholeOrPart().begin.valueOf(); + const delta = hap.duration.valueOf(); // time should be audio time of onset // currentTime should be current time of audio context (slightly before time) if (startedAt < 0) { startedAt = Date.now() - currentTime * 1000; } - const controls = Object.assign({}, { cps: cps, cycle: cycle, delta: delta }, hap.value); + const controls = Object.assign({}, { cps, cycle, delta }, hap.value); const keyvals = Object.entries(controls).flat(); const ts = Math.floor(startedAt + (time + latency) * 1000); const message = new OSC.Message('/dirt/play', ...keyvals); diff --git a/packages/osc/package-lock.json b/packages/osc/package-lock.json index 31d931c0..3f0ff34c 100644 --- a/packages/osc/package-lock.json +++ b/packages/osc/package-lock.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/osc", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/packages/osc/package.json b/packages/osc/package.json index b7e9bb29..efb48ee7 100644 --- a/packages/osc/package.json +++ b/packages/osc/package.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/osc", - "version": "0.1.0", + "version": "0.1.1", "description": "OSC messaging for strudel", "main": "osc.mjs", "scripts": { diff --git a/packages/react/package-lock.json b/packages/react/package-lock.json index aafcf807..188f0a57 100644 --- a/packages/react/package-lock.json +++ b/packages/react/package-lock.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/react", - "version": "0.1.2", + "version": "0.1.4", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/packages/react/package.json b/packages/react/package.json index 8db298be..906bf1c8 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/react", - "version": "0.1.2", + "version": "0.1.4", "description": "React components for strudel", "main": "dist/index.cjs.js", "module": "dist/index.es.js", @@ -38,9 +38,9 @@ "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { "@codemirror/lang-javascript": "^0.19.0", - "@strudel.cycles/core": "*", - "@strudel.cycles/eval": "^0.1.1", - "@strudel.cycles/tone": "^0.1.1", + "@strudel.cycles/core": "^0.1.2", + "@strudel.cycles/eval": "^0.1.3", + "@strudel.cycles/tone": "^0.1.3", "react-codemirror6": "^1.1.0", "react-hook-inview": "^4.5.0" }, diff --git a/packages/react/src/hooks/useRepl.mjs b/packages/react/src/hooks/useRepl.mjs index 8db4bb0c..99e697ee 100644 --- a/packages/react/src/hooks/useRepl.mjs +++ b/packages/react/src/hooks/useRepl.mjs @@ -57,14 +57,7 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw: onDrawP /* console.warn('no instrument chosen', event); throw new Error(`no instrument chosen for ${JSON.stringify(event)}`); */ } else { - onTrigger( - time, - event, - currentTime, - 1 /* cps */, - event.wholeOrPart().begin.valueOf(), - event.duration.valueOf(), - ); + onTrigger(time, event, currentTime, 1 /* cps */); } } catch (err) { console.warn(err); diff --git a/packages/serial/package.json b/packages/serial/package.json index 041f09e9..95a49e27 100644 --- a/packages/serial/package.json +++ b/packages/serial/package.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/serial", - "version": "0.1.0", + "version": "0.1.1", "description": "Webserial API for strudel", "main": "serial.mjs", "repository": { diff --git a/packages/tonal/package-lock.json b/packages/tonal/package-lock.json index ec776af9..107b200d 100644 --- a/packages/tonal/package-lock.json +++ b/packages/tonal/package-lock.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/tonal", - "version": "0.1.1", + "version": "0.1.3", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/packages/tonal/package.json b/packages/tonal/package.json index 829ec13a..2936c39a 100644 --- a/packages/tonal/package.json +++ b/packages/tonal/package.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/tonal", - "version": "0.1.1", + "version": "0.1.3", "description": "Tonal functions for strudel", "main": "index.mjs", "type": "module", @@ -25,7 +25,7 @@ }, "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { - "@strudel.cycles/core": "^0.1.0", + "@strudel.cycles/core": "^0.1.2", "@tonaljs/tonal": "^4.6.5", "webmidi": "^3.0.15" } diff --git a/packages/tone/package-lock.json b/packages/tone/package-lock.json index 59333f9c..d2ee8b51 100644 --- a/packages/tone/package-lock.json +++ b/packages/tone/package-lock.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/tone", - "version": "0.1.1", + "version": "0.1.3", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/packages/tone/package.json b/packages/tone/package.json index 7c61d22a..67c9ab84 100644 --- a/packages/tone/package.json +++ b/packages/tone/package.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/tone", - "version": "0.1.1", + "version": "0.1.3", "description": "Tone.js API for strudel", "main": "index.mjs", "type": "module", @@ -22,7 +22,7 @@ }, "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { - "@strudel.cycles/core": "^0.1.0", + "@strudel.cycles/core": "^0.1.2", "@tonejs/piano": "^0.2.1", "chord-voicings": "^0.0.1", "tone": "^14.7.77" diff --git a/packages/webaudio/index.mjs b/packages/webaudio/index.mjs index 65a4b3ea..a3a321ed 100644 --- a/packages/webaudio/index.mjs +++ b/packages/webaudio/index.mjs @@ -7,3 +7,4 @@ This program is free software: you can redistribute it and/or modify it under th export * from './clockworker.mjs'; export * from './scheduler.mjs'; export * from './webaudio.mjs'; +export * from './sampler.mjs'; diff --git a/packages/webaudio/package-lock.json b/packages/webaudio/package-lock.json index bf7928c4..0fdffcef 100644 --- a/packages/webaudio/package-lock.json +++ b/packages/webaudio/package-lock.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/webaudio", - "version": "0.1.1", + "version": "0.1.3", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/packages/webaudio/package.json b/packages/webaudio/package.json index ee3fece6..73970f13 100644 --- a/packages/webaudio/package.json +++ b/packages/webaudio/package.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/webaudio", - "version": "0.1.1", + "version": "0.1.3", "description": "Web Audio helpers for Strudel", "main": "index.mjs", "type": "module", @@ -28,6 +28,6 @@ }, "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { - "@strudel.cycles/core": "^0.1.0" + "@strudel.cycles/core": "^0.1.2" } } diff --git a/packages/webdirt/sampler.mjs b/packages/webaudio/sampler.mjs similarity index 96% rename from packages/webdirt/sampler.mjs rename to packages/webaudio/sampler.mjs index 39a15fea..30e94169 100644 --- a/packages/webdirt/sampler.mjs +++ b/packages/webaudio/sampler.mjs @@ -1,6 +1,8 @@ const bufferCache = {}; // string: Promise const loadCache = {}; // string: Promise +export const getCachedBuffer = (url) => bufferCache[url]; + export const loadBuffer = (url, ac) => { if (!loadCache[url]) { loadCache[url] = fetch(url) @@ -91,7 +93,7 @@ export const loadGithubSamples = async (path, nameFn) => { * */ -export const samples = (sampleMap, baseUrl = '') => { +export const samples = (sampleMap, baseUrl = sampleMap._base) => { sampleCache.current = { ...sampleCache.current, ...Object.fromEntries( diff --git a/packages/webaudio/scheduler.mjs b/packages/webaudio/scheduler.mjs index dac8c409..e4719307 100644 --- a/packages/webaudio/scheduler.mjs +++ b/packages/webaudio/scheduler.mjs @@ -10,18 +10,20 @@ import { State, TimeSpan } from '@strudel.cycles/core'; export class Scheduler { worker; pattern; - constructor({ audioContext, interval = 0.2, onEvent }) { + constructor({ audioContext, interval = 0.2, onEvent, latency = 0.2 }) { this.worker = new ClockWorker( audioContext, (begin, end) => { - this.pattern.query(new State(new TimeSpan(begin, end))).forEach((e) => { + this.pattern.query(new State(new TimeSpan(begin + latency, end + latency))).forEach((e) => { if (!e.part.begin.equals(e.whole.begin)) { return; } + if (e.context.onTrigger) { + // TODO: kill first param, as it's contained in e + e.context.onTrigger(e.whole.begin, e, audioContext.currentTime, 1 /* cps */); + } if (onEvent) { onEvent?.(e); - } else { - console.warn('unplayable event: no audio node nor onEvent callback', e); } }); }, diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index de24a6d7..77d7e235 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -6,8 +6,11 @@ This program is free software: you can redistribute it and/or modify it under th // import { Pattern, getFrequency, patternify2 } from '@strudel.cycles/core'; import * as strudel from '@strudel.cycles/core'; -import { Tone } from '@strudel.cycles/tone'; -const { Pattern, getFrequency, patternify2 } = strudel; +import { fromMidi } from '@strudel.cycles/core'; +import { loadBuffer } from './sampler.mjs'; +const { Pattern } = strudel; + +// export const getAudioContext = () => Tone.getContext().rawContext; let audioContext; export const getAudioContext = () => { @@ -17,9 +20,15 @@ export const getAudioContext = () => { return audioContext; }; -const lookahead = 0.2; +const getFilter = (type, frequency, Q) => { + const filter = getAudioContext().createBiquadFilter(); + filter.type = type; + filter.frequency.value = frequency; + filter.Q.value = Q; + return filter; +}; -const adsr = (attack, decay, sustain, release, velocity, begin, end) => { +const getADSR = (attack, decay, sustain, release, velocity, begin, end) => { const gainNode = getAudioContext().createGain(); gainNode.gain.setValueAtTime(0, begin); gainNode.gain.linearRampToValueAtTime(velocity, begin + attack); // attack @@ -30,65 +39,115 @@ const adsr = (attack, decay, sustain, release, velocity, begin, end) => { return gainNode; }; -Pattern.prototype.withAudioNode = function (createAudioNode) { - return this._withHap((hap) => { - return hap.setContext({ - ...hap.context, - createAudioNode: (t, e) => createAudioNode(t, e, hap.context.createAudioNode?.(t, hap)), - }); - }); -}; - -Pattern.prototype._wave = function (type) { - return this.withAudioNode((t, e) => { - const osc = getAudioContext().createOscillator(); - osc.type = type; - const f = getFrequency(e); - osc.frequency.value = f; // expects frequency.. - const begin = t ?? e.whole.begin.valueOf() + lookahead; - const end = begin + e.duration.valueOf(); - osc.start(begin); - osc.stop(end); // release? - return osc; - }); -}; -Pattern.prototype.adsr = function (a = 0.01, d = 0.05, s = 1, r = 0.01) { - return this.withAudioNode((t, e, node) => { - const velocity = e.context?.velocity || 1; - const begin = t ?? e.whole.begin.valueOf() + lookahead; - const end = begin + e.duration.valueOf() + lookahead; - const envelope = adsr(a, d, s, r, velocity, begin, end); - node?.connect(envelope); - return envelope; - }); -}; -Pattern.prototype._filter = function (type = 'lowpass', frequency = 1000) { - return this.withAudioNode((t, e, node) => { - const filter = getAudioContext().createBiquadFilter(); - filter.type = type; - filter.frequency.value = frequency; - node?.connect(filter); - return filter; - }); -}; - -Pattern.prototype.filter = function (type, frequency) { - return patternify2(Pattern.prototype._filter)(reify(type), reify(frequency), this); -}; - Pattern.prototype.out = function () { - const master = getAudioContext().createGain(); - master.gain.value = 0.1; - master.connect(getAudioContext().destination); - return this.withAudioNode((t, e, node) => { - if (!node) { - console.warn('out: no source! call .osc() first'); + return this.onTrigger(async (t, hap, ct) => { + const ac = getAudioContext(); + // calculate correct time (tone.js workaround) + t = ac.currentTime + t - ct; + // destructure value + let { + freq, + s, + n = 0, + gain = 1, + cutoff, + resonance = 1, + hcutoff, + hresonance = 1, + bandf, + bandq = 1, + pan, + attack = 0.001, + decay = 0, + sustain = 1, + release = 0.001, + speed = 1, // sample playback speed + begin = 0, + end = 1, + } = hap.value; + // the chain will hold all audio nodes that connect to each other + const chain = []; + if (!s || ['sine', 'square', 'triangle', 'sawtooth'].includes(s)) { + // get frequency + if (!freq && typeof n === 'number') { + freq = fromMidi(n); // + 48); + } + if (!freq && typeof n === 'string') { + freq = fromMidi(toMidi(n)); + } + // make oscillator + const o = ac.createOscillator(); + o.type = s || 'triangle'; + o.frequency.value = Number(freq); + o.start(t); + o.stop(t + hap.duration + release); + chain.push(o); + // level down oscillators as they are really loud compared to samples i've tested + const g = ac.createGain(); + g.gain.value = 0.5; + chain.push(g); + // TODO: make adsr work with samples without pops + // envelope + const adsr = getADSR(attack, decay, sustain, release, 1, t, t + hap.duration); + chain.push(adsr); + } else { + // load sample + const samples = getLoadedSamples(); + if (!samples) { + console.warn('no samples loaded'); + return; + } + const bank = samples?.[s]; + if (!bank) { + console.warn('sample not found:', s, 'try one of ' + Object.keys(samples)); + return; + } else { + if (speed === 0) { + // no playback + return; + } + if (!s) { + console.warn('no sample specified'); + return; + } + const bank = samples[s]; + const sampleUrl = bank[n % bank.length]; + let buffer = await loadBuffer(sampleUrl, ac); + if (ac.currentTime > t) { + console.warn('sample still loading:', s, n); + return; + } + const src = ac.createBufferSource(); + src.buffer = buffer; + src.playbackRate.value = Math.abs(speed); + // TODO: nudge, unit, cut, loop + + let duration = src.buffer.duration; + const offset = begin * duration; + duration = ((end - begin) * duration) / Math.abs(speed); + src.start(t, offset, duration); + src.stop(t + duration); + chain.push(src); + } } - node?.connect(master); - })._withHap((hap) => { - const onTrigger = (time, e) => e.context?.createAudioNode?.(time, e); - return hap.setContext({ ...hap.context, onTrigger }); + // filters + cutoff !== undefined && chain.push(getFilter('lowpass', cutoff, resonance)); + hcutoff !== undefined && chain.push(getFilter('highpass', hcutoff, hresonance)); + bandf !== undefined && chain.push(getFilter('bandpass', bandf, bandq)); + // TODO vowel + // TODO delay / delaytime / delayfeedback + // panning + if (pan !== undefined) { + const panner = ac.createStereoPanner(); + panner.pan.value = 2 * pan - 1; + chain.push(panner); + } + // master out + const master = ac.createGain(); + master.gain.value = 0.8 * gain; + chain.push(master); + chain.push(ac.destination); + // connect chain elements together + chain.slice(1).reduce((last, current) => last.connect(current), chain[0]); }); }; - -Pattern.prototype.define('wave', (type, pat) => pat.wave(type), { patternified: true }); diff --git a/packages/webdirt/index.mjs b/packages/webdirt/index.mjs index 46a6887f..edafebd8 100644 --- a/packages/webdirt/index.mjs +++ b/packages/webdirt/index.mjs @@ -1,2 +1 @@ export * from './webdirt.mjs'; -export * from './sampler.mjs'; diff --git a/packages/webdirt/package-lock.json b/packages/webdirt/package-lock.json index db873ab6..af4f4be1 100644 --- a/packages/webdirt/package-lock.json +++ b/packages/webdirt/package-lock.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/webdirt", - "version": "0.1.0", + "version": "0.1.2", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/packages/webdirt/package.json b/packages/webdirt/package.json index 62cb55bc..e378c70e 100644 --- a/packages/webdirt/package.json +++ b/packages/webdirt/package.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/webdirt", - "version": "0.1.0", + "version": "0.1.2", "description": "WebDirt integration for Strudel", "main": "index.mjs", "type": "module", @@ -22,7 +22,7 @@ }, "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { - "@strudel.cycles/core": "^0.1.0", + "@strudel.cycles/core": "^0.1.2", "WebDirt": "github:dktr0/WebDirt" } } diff --git a/packages/webdirt/webdirt.mjs b/packages/webdirt/webdirt.mjs index 36637818..467ce416 100644 --- a/packages/webdirt/webdirt.mjs +++ b/packages/webdirt/webdirt.mjs @@ -1,7 +1,7 @@ import * as strudel from '@strudel.cycles/core'; const { Pattern } = strudel; import * as WebDirt from 'WebDirt'; -import { getLoadedSamples, loadBuffer, getLoadedBuffer } from './sampler.mjs'; +import { getLoadedSamples, loadBuffer, getLoadedBuffer } from '@strudel.cycles/webaudio'; let webDirt; diff --git a/packages/xen/package.json b/packages/xen/package.json index 948179ab..58c0c6f8 100644 --- a/packages/xen/package.json +++ b/packages/xen/package.json @@ -1,6 +1,6 @@ { "name": "@strudel.cycles/xen", - "version": "0.1.1", + "version": "0.1.3", "description": "Xenharmonic API for strudel", "main": "index.mjs", "scripts": { @@ -24,6 +24,6 @@ }, "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { - "@strudel.cycles/core": "^0.1.0" + "@strudel.cycles/core": "^0.1.2" } } diff --git a/repl/.gitignore b/repl/.gitignore index a547bf36..7e93270e 100644 --- a/repl/.gitignore +++ b/repl/.gitignore @@ -22,3 +22,5 @@ dist-ssr *.njsproj *.sln *.sw? + +oldtunes.mjs \ No newline at end of file diff --git a/repl/package.json b/repl/package.json index 42238c0b..c0afb29a 100644 --- a/repl/package.json +++ b/repl/package.json @@ -3,7 +3,7 @@ "private": true, "version": "0.0.0", "scripts": { - "dev": "vite", + "dev": "vite --host", "start": "vite", "build": "vite build", "preview": "vite preview", diff --git a/repl/src/App.jsx b/repl/src/App.jsx index df8cc780..8a818af4 100644 --- a/repl/src/App.jsx +++ b/repl/src/App.jsx @@ -13,7 +13,8 @@ import './App.css'; import logo from './logo.svg'; import * as tunes from './tunes.mjs'; import * as WebDirt from 'WebDirt'; -import { loadWebDirt, resetLoadedSamples } from '@strudel.cycles/webdirt'; +import { loadWebDirt } from '@strudel.cycles/webdirt'; +import { resetLoadedSamples, getAudioContext } from '@strudel.cycles/webaudio'; evalScope( Tone, @@ -139,7 +140,13 @@ function App() {

Strudel {isEmbedded ? 'Mini ' : ''}REPL

-