mirror of
https://codeberg.org/uzu/strudel
synced 2026-10-25 07:26:16 -04:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3c32b3b0aa | |||
| c492e21e1d |
@@ -420,7 +420,7 @@ export const binaryNL = (n, nBits = 16) => {
|
||||
* .partials(randL(8))
|
||||
*/
|
||||
export const randL = (n) => {
|
||||
return signal((t, controls) => (nVal) => getRandsAtTime(t, nVal, controls.randSeed).map(Math.abs)).appLeft(reify(n));
|
||||
return signal((t) => (nVal) => getRandsAtTime(t, nVal).map(Math.abs)).appLeft(reify(n));
|
||||
};
|
||||
|
||||
export const randrun = (n) => {
|
||||
@@ -1143,26 +1143,3 @@ export const perx = new Pattern(function (state) {
|
||||
const n = Fraction(1).div(state.span.duration);
|
||||
return [new Hap(undefined, state.span, Math.log(n) / Math.log(2) + 1)];
|
||||
});
|
||||
|
||||
/**
|
||||
* A signal that goes from `0` to `1` over one cycle, and then stays at `1` forever.
|
||||
* Mainly useful in combination with other functions that reset the cycle. See also
|
||||
* `close`.
|
||||
* @tags temporal
|
||||
* @example
|
||||
* sound("bd sd [- bd] sd".fast(2))
|
||||
* .lpf(open.slow(4).range(300,3000)) // close envelope over 4 cycles
|
||||
* .restart("x/4") // restart every 4 cycles
|
||||
*/
|
||||
export const open = signal((t) => t.min(1));
|
||||
|
||||
/**
|
||||
* The same as `open`, but goes from `1` to `0` over one cycle, and then
|
||||
* stays on `0` forever.
|
||||
* @tags temporal
|
||||
* @example
|
||||
* sound("bd sd [- bd] sd".fast(2))
|
||||
* .lpf(close.slow(4).range(300,3000)) // close envelope over 4 cycles
|
||||
* .restart("x/4") // restart every 4 cycles
|
||||
*/
|
||||
export const close = signal((t) => Fraction(1).sub(t).max(0));
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
},
|
||||
"homepage": "https://codeberg.org/uzu/strudel/",
|
||||
"dependencies": {
|
||||
"web-tree-sitter": "^0.24.7",
|
||||
"tree-sitter-haskell": "^0.23.1"
|
||||
"web-tree-sitter": "^0.24.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tree-sitter-haskell": "^0.23.1",
|
||||
"vite": "^6.0.11"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import Parser from 'web-tree-sitter';
|
||||
import treeSitterWasm from 'web-tree-sitter/tree-sitter.wasm?url';
|
||||
import grammarWasm from 'tree-sitter-haskell/tree-sitter-haskell.wasm?url';
|
||||
|
||||
let base;
|
||||
let base = '/';
|
||||
export function setBase(path) {
|
||||
base = path;
|
||||
}
|
||||
@@ -12,15 +10,11 @@ let isReady = false,
|
||||
async function _loadParser() {
|
||||
await Parser.init({
|
||||
locateFile(scriptName, scriptDirectory) {
|
||||
if (base) {
|
||||
return `${base}${scriptName}`;
|
||||
}
|
||||
return treeSitterWasm;
|
||||
return `${base}${scriptName}`;
|
||||
},
|
||||
});
|
||||
parser = new Parser();
|
||||
let grammarUrl = base ? `${base}tree-sitter-haskell.wasm` : grammarWasm;
|
||||
const Lang = await Parser.Language.load(grammarUrl);
|
||||
const Lang = await Parser.Language.load(`${base}tree-sitter-haskell.wasm`);
|
||||
parser.setLanguage(Lang);
|
||||
isReady = true;
|
||||
return parser;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { registerChainEffect, getFrequencyFromValue, getWorklet, logger, registerWorklet } from 'superdough';
|
||||
import workletUrl from './worklet.mjs?audioworklet';
|
||||
|
||||
let kabel;
|
||||
async function initKabelsalat() {
|
||||
const { SalatRepl } = await import('@kabelsalat/web');
|
||||
logger('[kabelsalat] ready');
|
||||
kabel = new SalatRepl({ localScope: true });
|
||||
return kabel;
|
||||
}
|
||||
|
||||
initKabelsalat().catch((err) => {
|
||||
logger(`kabelsalat init failed: ${err}`);
|
||||
});
|
||||
|
||||
const compileKabel = (code) => {
|
||||
if (!kabel) {
|
||||
throw new Error('kabelsalat still loading');
|
||||
}
|
||||
const node = kabel.evaluate(code);
|
||||
return node.compile({ log: false });
|
||||
};
|
||||
|
||||
registerWorklet(workletUrl);
|
||||
|
||||
registerChainEffect('workletSrc', ({ audioContext, fx, value, chainID, start, gateEnd, releaseEnd }) => {
|
||||
const workletSrc = fx.workletSrc
|
||||
.replace(/\bpat\[(\d+)\]/g, (_, i) => fx.workletInputs[i])
|
||||
.replaceAll('sFreq', getFrequencyFromValue(value))
|
||||
.replaceAll('sGate', `cc('strudel-gate-${chainID}')`);
|
||||
const { src, ugens, registers } = compileKabel(workletSrc);
|
||||
const workletNode = getWorklet(audioContext, 'generic-processor', {}, { outputChannelCount: [2] });
|
||||
workletNode.port.postMessage({
|
||||
src,
|
||||
schema: { ugens, registers },
|
||||
start,
|
||||
gateEnd,
|
||||
end: releaseEnd,
|
||||
});
|
||||
return workletNode;
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@strudel/kabelsalat",
|
||||
"version": "1.2.6",
|
||||
"description": "kabelsalat integration for superdough",
|
||||
"main": "kabelsalat.mjs",
|
||||
"type": "module",
|
||||
"publishConfig": {
|
||||
"main": "dist/kabelsalat.mjs"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://codeberg.org/uzu/strudel.git"
|
||||
},
|
||||
"keywords": [
|
||||
"titdalcycles",
|
||||
"strudel",
|
||||
"pattern",
|
||||
"livecoding",
|
||||
"algorave"
|
||||
],
|
||||
"author": "Felix Roos <flix91@gmail.com>",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"bugs": {
|
||||
"url": "https://codeberg.org/uzu/strudel/issues"
|
||||
},
|
||||
"homepage": "https://codeberg.org/uzu/strudel#readme",
|
||||
"dependencies": {
|
||||
"superdough": "workspace:*",
|
||||
"@kabelsalat/lib": "^0.4.1",
|
||||
"@kabelsalat/web": "^0.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^6.0.11",
|
||||
"vite-plugin-bundle-audioworklet":"workspace:*"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import { resolve } from 'path';
|
||||
import bundleAudioWorkletPlugin from 'vite-plugin-bundle-audioworklet';
|
||||
import { dependencies } from './package.json';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [bundleAudioWorkletPlugin()],
|
||||
build: {
|
||||
lib: {
|
||||
entry: resolve(__dirname, 'kabelsalat.mjs'),
|
||||
formats: ['es'],
|
||||
fileName: (ext) => ({ es: 'kabelsalat.mjs' })[ext],
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [...Object.keys(dependencies)],
|
||||
},
|
||||
target: 'esnext',
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,83 @@
|
||||
import * as ugens from '@kabelsalat/lib/src/ugens.js';
|
||||
|
||||
const blockSize = 128;
|
||||
|
||||
const UGENS = new Map(Object.entries(ugens));
|
||||
|
||||
class GenericProcessor extends AudioWorkletProcessor {
|
||||
constructor() {
|
||||
super();
|
||||
this.playPos = 0;
|
||||
const channels = 16;
|
||||
this.outputs = new Array(channels).fill(0);
|
||||
this.sources = new Array(channels).fill(0);
|
||||
this.gateEnded = false;
|
||||
this.started = false;
|
||||
this.port.onmessage = (event) => {
|
||||
let {
|
||||
src,
|
||||
schema: { ugens, registers },
|
||||
start,
|
||||
gateEnd,
|
||||
end,
|
||||
} = event.data;
|
||||
this.start = start;
|
||||
this.gateEnd = gateEnd;
|
||||
this.end = end;
|
||||
this.registers = new Array(registers).fill(0);
|
||||
this.src = `o.fill(0); // reset outputs\n${src}`;
|
||||
this.nodes = [];
|
||||
for (let i = 0; i < ugens.length; i++) {
|
||||
const ugen = ugens[i];
|
||||
const nodeClass = UGENS.get(ugen.type);
|
||||
const node = new nodeClass(i, ugen, sampleRate);
|
||||
if (node.type === 'cc' && ugen.inputs?.[0]?.includes('strudel-gate')) {
|
||||
node.setValue(1);
|
||||
this.gateNode = node;
|
||||
}
|
||||
this.nodes[i] = node;
|
||||
}
|
||||
this.genSample = new Function(
|
||||
'time',
|
||||
'nodes',
|
||||
'input',
|
||||
'r', // registers
|
||||
'o', // outputs
|
||||
's', // sources
|
||||
this.src,
|
||||
);
|
||||
};
|
||||
}
|
||||
process(inputs, outputs) {
|
||||
const input = inputs[0]?.[0];
|
||||
if (currentTime >= this.end) {
|
||||
return false;
|
||||
} else if (this.genSample === undefined || currentTime < this.start) {
|
||||
// pending
|
||||
return true;
|
||||
}
|
||||
this.started = true;
|
||||
if (!this.gateEnded && currentTime > this.gateEnd) {
|
||||
this.gateNode?.setValue(0);
|
||||
this.gateEnded = true;
|
||||
}
|
||||
const output = outputs[0];
|
||||
const outL = output[0];
|
||||
const outR = output[1];
|
||||
for (let n = 0; n < blockSize; n++) {
|
||||
this.genSample(this.playPos, this.nodes, input ? input[n] : 0, this.registers, this.outputs, this.sources);
|
||||
const left = this.outputs[0];
|
||||
const right = this.outputs[1];
|
||||
// Spread to stereo if possible; else mixdown to mono
|
||||
if (outR) {
|
||||
outL[n] = left;
|
||||
outR[n] = right;
|
||||
} else {
|
||||
outL[n] = 0.5 * (left + right);
|
||||
}
|
||||
this.playPos += 1 / sampleRate;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
registerProcessor('generic-processor', GenericProcessor);
|
||||
@@ -36,8 +36,6 @@
|
||||
"vite-plugin-bundle-audioworklet": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@kabelsalat/lib": "^0.4.1",
|
||||
"@kabelsalat/web": "^0.4.1",
|
||||
"nanostores": "^0.11.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
gainNode,
|
||||
getCompressor,
|
||||
getDistortion,
|
||||
getFrequencyFromValue,
|
||||
getLfo,
|
||||
getWorklet,
|
||||
releaseAudioNode,
|
||||
@@ -63,6 +62,11 @@ export function registerSound(key, onTrigger, data = {}) {
|
||||
soundMap.setKey(key, { onTrigger, data });
|
||||
}
|
||||
|
||||
const chainEffects = {};
|
||||
export function registerChainEffect(name, effect) {
|
||||
chainEffects[name] = effect;
|
||||
}
|
||||
|
||||
let gainCurveFunc = (val) => val;
|
||||
|
||||
export function applyGainCurve(val) {
|
||||
@@ -260,14 +264,6 @@ export function loadWorklets() {
|
||||
return workletsLoading;
|
||||
}
|
||||
|
||||
let kabel;
|
||||
async function initKabelsalat() {
|
||||
const { SalatRepl } = await import('@kabelsalat/web');
|
||||
logger('[kabelsalat] ready');
|
||||
kabel = new SalatRepl({ localScope: true });
|
||||
return kabel;
|
||||
}
|
||||
|
||||
// this function should be called on first user interaction (to avoid console warning)
|
||||
export async function initAudio(options = {}) {
|
||||
const {
|
||||
@@ -314,7 +310,6 @@ export async function initAudio(options = {}) {
|
||||
} catch (err) {
|
||||
console.warn('could not load AudioWorklet effects', err);
|
||||
}
|
||||
await initKabelsalat();
|
||||
logger('[superdough] ready');
|
||||
}
|
||||
let audioReady;
|
||||
@@ -450,14 +445,6 @@ class Chain {
|
||||
}
|
||||
}
|
||||
|
||||
const compileKabel = (code) => {
|
||||
if (!kabel) {
|
||||
throw new Error('kabelsalat not loaded');
|
||||
}
|
||||
const node = kabel.evaluate(code);
|
||||
return node.compile({ log: false });
|
||||
};
|
||||
|
||||
export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) => {
|
||||
// mapping from main FX and numbered FX chains to nodes
|
||||
const nodes = { main: {} };
|
||||
@@ -633,17 +620,21 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
gain *= velocity; // velocity currently only multiplies with gain. it might do other things in the future
|
||||
delaytime = delaytime ?? cycleToSeconds(delaysync, cps);
|
||||
|
||||
// Kabelsalat
|
||||
if (fx.workletSrc !== undefined) {
|
||||
const workletNode = getWorklet(ac, 'generic-processor', {}, { outputChannelCount: [2] });
|
||||
chain.connect(workletNode);
|
||||
const workletSrc = fx.workletSrc
|
||||
.replace(/\bpat\[(\d+)\]/g, (_, i) => fx.workletInputs[i])
|
||||
.replaceAll('sFreq', getFrequencyFromValue(value))
|
||||
.replaceAll('sGate', `cc('strudel-gate-${chainID}')`);
|
||||
/* global compileKabel */
|
||||
const { src, ugens, registers } = compileKabel(workletSrc);
|
||||
workletNode.port.postMessage({ src, schema: { ugens, registers }, start: t, gateEnd: end, end: endWithRelease });
|
||||
// custom chain effect sink (used for kabelsalat)
|
||||
for (let key in chainEffects) {
|
||||
const effect = chainEffects[key];
|
||||
if (fx[key] !== undefined) {
|
||||
const node = effect({
|
||||
audioContext: ac,
|
||||
fx,
|
||||
chainID,
|
||||
value,
|
||||
start: t,
|
||||
gateEnd: end,
|
||||
releaseEnd: endWithRelease,
|
||||
});
|
||||
node && chain.connect(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (fx.stretch !== undefined) {
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
import OLAProcessor from './ola-processor';
|
||||
import FFT from './fft.js';
|
||||
import { getDistortionAlgorithm } from './helpers.mjs';
|
||||
import * as ugens from '@kabelsalat/lib/src/ugens.js';
|
||||
|
||||
const UGENS = new Map(Object.entries(ugens));
|
||||
|
||||
const blockSize = 128;
|
||||
const PI = Math.PI;
|
||||
@@ -1492,81 +1489,3 @@ class TransientProcessor extends AudioWorkletProcessor {
|
||||
}
|
||||
|
||||
registerProcessor('transient-processor', TransientProcessor);
|
||||
|
||||
class GenericProcessor extends AudioWorkletProcessor {
|
||||
constructor() {
|
||||
super();
|
||||
this.playPos = 0;
|
||||
const channels = 16;
|
||||
this.outputs = new Array(channels).fill(0);
|
||||
this.sources = new Array(channels).fill(0);
|
||||
this.gateEnded = false;
|
||||
this.started = false;
|
||||
this.port.onmessage = (event) => {
|
||||
let {
|
||||
src,
|
||||
schema: { ugens, registers },
|
||||
start,
|
||||
gateEnd,
|
||||
end,
|
||||
} = event.data;
|
||||
this.start = start;
|
||||
this.gateEnd = gateEnd;
|
||||
this.end = end;
|
||||
this.registers = new Array(registers).fill(0);
|
||||
this.src = `o.fill(0); // reset outputs\n${src}`;
|
||||
this.nodes = [];
|
||||
for (let i = 0; i < ugens.length; i++) {
|
||||
const ugen = ugens[i];
|
||||
const nodeClass = UGENS.get(ugen.type);
|
||||
const node = new nodeClass(i, ugen, sampleRate);
|
||||
if (node.type === 'cc' && ugen.inputs?.[0]?.includes('strudel-gate')) {
|
||||
node.setValue(1);
|
||||
this.gateNode = node;
|
||||
}
|
||||
this.nodes[i] = node;
|
||||
}
|
||||
this.genSample = new Function(
|
||||
'time',
|
||||
'nodes',
|
||||
'input',
|
||||
'r', // registers
|
||||
'o', // outputs
|
||||
's', // sources
|
||||
this.src,
|
||||
);
|
||||
};
|
||||
}
|
||||
process(inputs, outputs) {
|
||||
const input = inputs[0]?.[0];
|
||||
if (currentTime >= this.end) {
|
||||
return false;
|
||||
} else if (this.genSample === undefined || currentTime < this.start) {
|
||||
// pending
|
||||
return true;
|
||||
}
|
||||
this.started = true;
|
||||
if (!this.gateEnded && currentTime > this.gateEnd) {
|
||||
this.gateNode?.setValue(0);
|
||||
this.gateEnded = true;
|
||||
}
|
||||
const output = outputs[0];
|
||||
const outL = output[0];
|
||||
const outR = output[1];
|
||||
for (let n = 0; n < blockSize; n++) {
|
||||
this.genSample(this.playPos, this.nodes, input ? input[n] : 0, this.registers, this.outputs, this.sources);
|
||||
const left = this.outputs[0];
|
||||
const right = this.outputs[1];
|
||||
// Spread to stereo if possible; else mixdown to mono
|
||||
if (outR) {
|
||||
outL[n] = left;
|
||||
outR[n] = right;
|
||||
} else {
|
||||
outL[n] = 0.5 * (left + right);
|
||||
}
|
||||
this.playPos += 1 / sampleRate;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
registerProcessor('generic-processor', GenericProcessor);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"version": "0.2.0",
|
||||
"private": true,
|
||||
"description": "Experimental Tidal Code interpreter",
|
||||
"main": "tidal.mjs",
|
||||
"module": "tidal.mjs",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -94,6 +94,9 @@ export function getWidgetID(widgetConfig) {
|
||||
|
||||
function widgetWithLocation(node, widgetConfig) {
|
||||
const id = getWidgetID(widgetConfig);
|
||||
// Store the unique ID back into the config so it's available for widget management
|
||||
// This is critical for block-based evaluation to match existing widgets with new ones
|
||||
widgetConfig.id = id;
|
||||
// add loc as identifier to first argument
|
||||
// the sliderWithID function is assumed to be sliderWithID(id, value, min?, max?)
|
||||
node.arguments.unshift({
|
||||
|
||||
Generated
+25
-9
@@ -359,13 +359,13 @@ importers:
|
||||
|
||||
packages/hs2js:
|
||||
dependencies:
|
||||
tree-sitter-haskell:
|
||||
specifier: ^0.23.1
|
||||
version: 0.23.1(tree-sitter@0.21.1)
|
||||
web-tree-sitter:
|
||||
specifier: ^0.24.7
|
||||
version: 0.24.7
|
||||
devDependencies:
|
||||
tree-sitter-haskell:
|
||||
specifier: ^0.23.1
|
||||
version: 0.23.1(tree-sitter@0.21.1)
|
||||
vite:
|
||||
specifier: ^6.0.11
|
||||
version: 6.0.11(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0)
|
||||
@@ -389,6 +389,25 @@ importers:
|
||||
specifier: ^6.0.11
|
||||
version: 6.0.11(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0)
|
||||
|
||||
packages/kabelsalat:
|
||||
dependencies:
|
||||
'@kabelsalat/lib':
|
||||
specifier: ^0.4.1
|
||||
version: 0.4.1
|
||||
'@kabelsalat/web':
|
||||
specifier: ^0.4.1
|
||||
version: 0.4.1
|
||||
superdough:
|
||||
specifier: workspace:*
|
||||
version: link:../superdough
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^6.0.11
|
||||
version: 6.0.11(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0)
|
||||
vite-plugin-bundle-audioworklet:
|
||||
specifier: workspace:*
|
||||
version: link:../vite-plugin-bundle-audioworklet
|
||||
|
||||
packages/midi:
|
||||
dependencies:
|
||||
'@strudel/core':
|
||||
@@ -589,12 +608,6 @@ importers:
|
||||
|
||||
packages/superdough:
|
||||
dependencies:
|
||||
'@kabelsalat/lib':
|
||||
specifier: ^0.4.1
|
||||
version: 0.4.1
|
||||
'@kabelsalat/web':
|
||||
specifier: ^0.4.1
|
||||
version: 0.4.1
|
||||
nanostores:
|
||||
specifier: ^0.11.3
|
||||
version: 0.11.3
|
||||
@@ -821,6 +834,9 @@ importers:
|
||||
'@strudel/hydra':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/hydra
|
||||
'@strudel/kabelsalat':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/kabelsalat
|
||||
'@strudel/midi':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/midi
|
||||
|
||||
@@ -2440,43 +2440,6 @@ exports[`runs examples > example "clip" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "close" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | s:bd cutoff:3000 ]",
|
||||
"[ 1/8 → 1/4 | s:sd cutoff:2915.625 ]",
|
||||
"[ 5/16 → 3/8 | s:bd cutoff:2789.0625 ]",
|
||||
"[ 3/8 → 1/2 | s:sd cutoff:2746.875 ]",
|
||||
"[ 1/2 → 5/8 | s:bd cutoff:2662.5 ]",
|
||||
"[ 5/8 → 3/4 | s:sd cutoff:2578.125 ]",
|
||||
"[ 13/16 → 7/8 | s:bd cutoff:2451.5625 ]",
|
||||
"[ 7/8 → 1/1 | s:sd cutoff:2409.375 ]",
|
||||
"[ 1/1 → 9/8 | s:bd cutoff:2325 ]",
|
||||
"[ 9/8 → 5/4 | s:sd cutoff:2240.625 ]",
|
||||
"[ 21/16 → 11/8 | s:bd cutoff:2114.0625 ]",
|
||||
"[ 11/8 → 3/2 | s:sd cutoff:2071.875 ]",
|
||||
"[ 3/2 → 13/8 | s:bd cutoff:1987.5 ]",
|
||||
"[ 13/8 → 7/4 | s:sd cutoff:1903.125 ]",
|
||||
"[ 29/16 → 15/8 | s:bd cutoff:1776.5625 ]",
|
||||
"[ 15/8 → 2/1 | s:sd cutoff:1734.375 ]",
|
||||
"[ 2/1 → 17/8 | s:bd cutoff:1650 ]",
|
||||
"[ 17/8 → 9/4 | s:sd cutoff:1565.625 ]",
|
||||
"[ 37/16 → 19/8 | s:bd cutoff:1439.0625 ]",
|
||||
"[ 19/8 → 5/2 | s:sd cutoff:1396.875 ]",
|
||||
"[ 5/2 → 21/8 | s:bd cutoff:1312.5 ]",
|
||||
"[ 21/8 → 11/4 | s:sd cutoff:1228.125 ]",
|
||||
"[ 45/16 → 23/8 | s:bd cutoff:1101.5625 ]",
|
||||
"[ 23/8 → 3/1 | s:sd cutoff:1059.375 ]",
|
||||
"[ 3/1 → 25/8 | s:bd cutoff:975 ]",
|
||||
"[ 25/8 → 13/4 | s:sd cutoff:890.625 ]",
|
||||
"[ 53/16 → 27/8 | s:bd cutoff:764.0625 ]",
|
||||
"[ 27/8 → 7/2 | s:sd cutoff:721.875 ]",
|
||||
"[ 7/2 → 29/8 | s:bd cutoff:637.5 ]",
|
||||
"[ 29/8 → 15/4 | s:sd cutoff:553.125 ]",
|
||||
"[ 61/16 → 31/8 | s:bd cutoff:426.5625 ]",
|
||||
"[ 31/8 → 4/1 | s:sd cutoff:384.375 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "coarse" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | s:hh coarse:1 ]",
|
||||
@@ -8764,43 +8727,6 @@ exports[`runs examples > example "onTriggerTime" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "open" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | s:bd cutoff:300 ]",
|
||||
"[ 1/8 → 1/4 | s:sd cutoff:384.375 ]",
|
||||
"[ 5/16 → 3/8 | s:bd cutoff:510.9375 ]",
|
||||
"[ 3/8 → 1/2 | s:sd cutoff:553.125 ]",
|
||||
"[ 1/2 → 5/8 | s:bd cutoff:637.5 ]",
|
||||
"[ 5/8 → 3/4 | s:sd cutoff:721.875 ]",
|
||||
"[ 13/16 → 7/8 | s:bd cutoff:848.4375 ]",
|
||||
"[ 7/8 → 1/1 | s:sd cutoff:890.625 ]",
|
||||
"[ 1/1 → 9/8 | s:bd cutoff:975 ]",
|
||||
"[ 9/8 → 5/4 | s:sd cutoff:1059.375 ]",
|
||||
"[ 21/16 → 11/8 | s:bd cutoff:1185.9375 ]",
|
||||
"[ 11/8 → 3/2 | s:sd cutoff:1228.125 ]",
|
||||
"[ 3/2 → 13/8 | s:bd cutoff:1312.5 ]",
|
||||
"[ 13/8 → 7/4 | s:sd cutoff:1396.875 ]",
|
||||
"[ 29/16 → 15/8 | s:bd cutoff:1523.4375 ]",
|
||||
"[ 15/8 → 2/1 | s:sd cutoff:1565.625 ]",
|
||||
"[ 2/1 → 17/8 | s:bd cutoff:1650 ]",
|
||||
"[ 17/8 → 9/4 | s:sd cutoff:1734.375 ]",
|
||||
"[ 37/16 → 19/8 | s:bd cutoff:1860.9375 ]",
|
||||
"[ 19/8 → 5/2 | s:sd cutoff:1903.125 ]",
|
||||
"[ 5/2 → 21/8 | s:bd cutoff:1987.5 ]",
|
||||
"[ 21/8 → 11/4 | s:sd cutoff:2071.875 ]",
|
||||
"[ 45/16 → 23/8 | s:bd cutoff:2198.4375 ]",
|
||||
"[ 23/8 → 3/1 | s:sd cutoff:2240.625 ]",
|
||||
"[ 3/1 → 25/8 | s:bd cutoff:2325 ]",
|
||||
"[ 25/8 → 13/4 | s:sd cutoff:2409.375 ]",
|
||||
"[ 53/16 → 27/8 | s:bd cutoff:2535.9375 ]",
|
||||
"[ 27/8 → 7/2 | s:sd cutoff:2578.125 ]",
|
||||
"[ 7/2 → 29/8 | s:bd cutoff:2662.5 ]",
|
||||
"[ 29/8 → 15/4 | s:sd cutoff:2746.875 ]",
|
||||
"[ 61/16 → 31/8 | s:bd cutoff:2873.4375 ]",
|
||||
"[ 31/8 → 4/1 | s:sd cutoff:2915.625 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "orbit" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/6 | s:hh delay:0.5 delaytime:0.25 orbit:1 ]",
|
||||
|
||||
@@ -80,7 +80,6 @@ export default defineConfig({
|
||||
/^https:\/\/freesound\.org\/.*/i,
|
||||
/^https:\/\/cdn\.freesound\.org\/.*/i,
|
||||
/^https:\/\/shabda\.ndre\.gr\/.*/i,
|
||||
/^https:\/\/felixroos\.github\.io\/webaudiofontdata\/.*/i,
|
||||
].some((regex) => regex.test(url)),
|
||||
handler: 'CacheFirst',
|
||||
options: {
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
"@strudel/webaudio": "workspace:*",
|
||||
"@strudel/mondo": "workspace:*",
|
||||
"@strudel/xen": "workspace:*",
|
||||
"@strudel/kabelsalat": "workspace:*",
|
||||
"@supabase/supabase-js": "^2.48.1",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"@tailwindcss/postcss": "^4.0.0",
|
||||
|
||||
@@ -89,6 +89,7 @@ export function loadModules() {
|
||||
import('@strudel/mqtt'),
|
||||
import('@strudel/mondo'),
|
||||
import('@strudel/dough'),
|
||||
import('@strudel/kabelsalat'),
|
||||
];
|
||||
if (isTauri()) {
|
||||
modules = modules.concat([
|
||||
|
||||
Reference in New Issue
Block a user