mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-23 13:42:56 -04:00
Compare commits
191 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1cbf14bcdb | |||
| dd64bf0fc9 | |||
| 434eb3b256 | |||
| 8fef75385a | |||
| c66b9219a8 | |||
| 2a1f8823e8 | |||
| 3da65f90e3 | |||
| a36cc24087 | |||
| 1829d8cadf | |||
| 02efc090ef | |||
| b184348f55 | |||
| 06743cb072 | |||
| 6fa66fb1d0 | |||
| 23c0ab9648 | |||
| 6422047cff | |||
| 46c2c72637 | |||
| c088590244 | |||
| 192ba69682 | |||
| 2cc428e968 | |||
| 15c451a6d9 | |||
| b46e8c07be | |||
| 961e931c31 | |||
| b1d49552ae | |||
| d31d154841 | |||
| d03892bbcf | |||
| 0cf0c1c585 | |||
| 0695b4bee9 | |||
| 0cfec3a7bd | |||
| 6224cd40d4 | |||
| 18ae82174d | |||
| aa1b9d11dc | |||
| 0a82471112 | |||
| 8052e10f0c | |||
| 1146f6ba52 | |||
| 738cea0025 | |||
| 6c28398d20 | |||
| 4fe75143d0 | |||
| 3cd59cdea5 | |||
| 9dca7eb878 | |||
| b45fd5ce3a | |||
| 229b1470f7 | |||
| 0a349e1707 | |||
| d176e9ccf0 | |||
| 710c3acb5d | |||
| a7f5d0e0c1 | |||
| 9928526dda | |||
| 2f8111be0e | |||
| 68113937ff | |||
| c598fa026b | |||
| 491c99c348 | |||
| d49aa8242a | |||
| 9ffbc06cf9 | |||
| af52ac30ba | |||
| 7aea1b5fc8 | |||
| 500ddf3946 | |||
| 3431eaf309 | |||
| 9a13c58583 | |||
| 77a48e8351 | |||
| 6cb156d876 | |||
| d8677c6261 | |||
| 6ef9ca18e9 | |||
| 6667738dc9 | |||
| 4f7415b0ec | |||
| c6f3a8b7c7 | |||
| 4229d673c9 | |||
| 98b7859605 | |||
| a8db707440 | |||
| 873a412179 | |||
| 0778b4809e | |||
| f85fb35c66 | |||
| 5ada0daf40 | |||
| 2ed5f5aa65 | |||
| ae5e6e3ab8 | |||
| 9ba7b9b4d4 | |||
| a45f1fba45 | |||
| e6b47d2cf2 | |||
| b04a262433 | |||
| 96bafa7f0b | |||
| b10612da5c | |||
| 171f4206a8 | |||
| bd83d19197 | |||
| 95cf201c6d | |||
| 4107711dae | |||
| 67f0758cb5 | |||
| 1c99944a32 | |||
| d3ea4959b4 | |||
| 70b27fbff2 | |||
| 96c2144857 | |||
| 0655af4d4f | |||
| 7adfe6f2df | |||
| 3fc0fdbe62 | |||
| fd73571744 | |||
| 80d861c12c | |||
| 45df7bfea3 | |||
| 106772e480 | |||
| 3506de8d1a | |||
| 32456d6966 | |||
| bdc2af9733 | |||
| a7daf41934 | |||
| fe6ff2528a | |||
| 7ceb8708b0 | |||
| 4d22d957a1 | |||
| 79f1a863b6 | |||
| 98db3d37ef | |||
| c02976782b | |||
| ceb4376eb8 | |||
| 0566b7bcb3 | |||
| 6d0ecb9f5e | |||
| 0901948564 | |||
| e6d028fd6b | |||
| 945e7082cd | |||
| 0d6bdfb514 | |||
| 70712bdab6 | |||
| 53771d7f95 | |||
| 6d32d4024d | |||
| f387589342 | |||
| 47a5080352 | |||
| e7e60d3ed8 | |||
| 5314c83534 | |||
| 204c964050 | |||
| 1ede99f71e | |||
| 3c1844046a | |||
| eefa90a2bc | |||
| efa0737660 | |||
| 6232560273 | |||
| e051d383a0 | |||
| 4fb5c1828d | |||
| 6f3b2fa66b | |||
| f17459fdc3 | |||
| a053f40e59 | |||
| 9a0459fdd3 | |||
| 46a859736a | |||
| e4524a2be9 | |||
| f3ee1cc947 | |||
| 86bc28dfb3 | |||
| 1bc0934228 | |||
| f26fd18c7a | |||
| c68dba8c21 | |||
| f768e1eece | |||
| f99557cc05 | |||
| f785823e00 | |||
| 62cd2263c2 | |||
| 7157634db0 | |||
| 78f6fdf169 | |||
| e01c6dc18d | |||
| 51dd408733 | |||
| 1ff7548b52 | |||
| f7fe44063a | |||
| 0355c358b8 | |||
| 4e0a60db00 | |||
| 3604d3940c | |||
| 02b9bd82cf | |||
| 86442adb20 | |||
| 569137eb22 | |||
| 5e3a37c9e8 | |||
| 4651066472 | |||
| 8d325b96ea | |||
| 150a1b8eed | |||
| b2f63897f9 | |||
| 2ee392be9b | |||
| a2974099c1 | |||
| 041a809b07 | |||
| 2ecc6b30aa | |||
| d582bbbb60 | |||
| ca3eda8fc7 | |||
| 1bc86c359e | |||
| 1e2c26123b | |||
| 83c820a18c | |||
| 3eface706c | |||
| 9756d63cf1 | |||
| a800b32529 | |||
| 12451ba5fc | |||
| 77fee0b866 | |||
| 5671b40707 | |||
| dec039ead3 | |||
| fb81f6f268 | |||
| deb973afa5 | |||
| 2dea3911ba | |||
| e0a7fb8290 | |||
| 30e402b9f1 | |||
| f6d9ad51c6 | |||
| 34ba81a841 | |||
| d7fae2620e | |||
| fe60f34015 | |||
| b5dc65d52a | |||
| 93a4efcf6d | |||
| 96e0cce2d2 | |||
| 88f677e910 | |||
| 9f50f6ef0e | |||
| 9663c2ec85 | |||
| 7da7554493 |
@@ -10,3 +10,4 @@ paper
|
||||
pnpm-lock.yaml
|
||||
pnpm-workspace.yaml
|
||||
**/dev-dist
|
||||
website/.astro
|
||||
|
||||
+1
-1
@@ -114,7 +114,7 @@ You can run the same check with `pnpm check`
|
||||
## Package Workflow
|
||||
|
||||
The project is split into multiple [packages](https://github.com/tidalcycles/strudel/tree/main/packages) with independent versioning.
|
||||
When you run `pnpm i` on the root folder, [pnpm workspaces](https://pnpm.io/workspaces) will install all dependencies of all subpackages. This will allow any js file to import `@strudel.cycles/<package-name>` to get the local version,
|
||||
When you run `pnpm i` on the root folder, [pnpm workspaces](https://pnpm.io/workspaces) will install all dependencies of all subpackages. This will allow any js file to import `@strudel/<package-name>` to get the local version,
|
||||
allowing to develop multiple packages at the same time.
|
||||
|
||||
## Package Publishing
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
[](https://github.com/tidalcycles/strudel/actions)
|
||||
|
||||
An experiment in making a [Tidal](https://github.com/tidalcycles/tidal/) using web technologies. This software is slowly stabilising, but please continue to tread carefully.
|
||||
An experiment in making a [Tidal](https://github.com/tidalcycles/tidal/) using web technologies. This software is a bit more stable now, but please continue to tread carefully.
|
||||
|
||||
- Try it here: <https://strudel.cc>
|
||||
- Docs: <https://strudel.cc/learn>
|
||||
- Technical Blog Post: <https://loophole-letters.vercel.app/strudel>
|
||||
- 1 Year of Strudel Blog Post: <https://loophole-letters.vercel.app/strudel1year>
|
||||
- 2 Years of Strudel Blog Post: <https://strudel.cc/blog/#year-2>
|
||||
|
||||
## Running Locally
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
/>
|
||||
<div id="output"></div>
|
||||
<script type="module">
|
||||
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8');
|
||||
const strudel = await import('https://cdn.skypack.dev/@strudel/core@0.6.8');
|
||||
Object.assign(window, strudel); // assign all strudel functions to global scope to use with eval
|
||||
const input = document.getElementById('text');
|
||||
const getEvents = () => {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
/>
|
||||
<canvas id="canvas"></canvas>
|
||||
<script type="module">
|
||||
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8');
|
||||
const strudel = await import('https://cdn.skypack.dev/@strudel/core@0.6.8');
|
||||
// this adds all strudel functions to the global scope, to be used by eval
|
||||
Object.assign(window, strudel);
|
||||
// setup elements
|
||||
|
||||
@@ -16,34 +16,40 @@
|
||||
</div>
|
||||
<div id="output"></div>
|
||||
<script type="module">
|
||||
import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel.cycles/core@0.6.8';
|
||||
import { mini } from 'https://cdn.skypack.dev/@strudel.cycles/mini@0.6.0';
|
||||
import { transpiler } from 'https://cdn.skypack.dev/@strudel.cycles/transpiler@0.6.0';
|
||||
import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel/core@0.11.0';
|
||||
import { mini } from 'https://cdn.skypack.dev/@strudel/mini@0.11.0';
|
||||
import { transpiler } from 'https://cdn.skypack.dev/@strudel/transpiler@0.11.0';
|
||||
import {
|
||||
getAudioContext,
|
||||
webaudioOutput,
|
||||
initAudioOnFirstClick,
|
||||
} from 'https://cdn.skypack.dev/@strudel.cycles/webaudio@0.6.0';
|
||||
registerSynthSounds,
|
||||
} from 'https://cdn.skypack.dev/@strudel/webaudio@0.11.0';
|
||||
|
||||
initAudioOnFirstClick();
|
||||
const ctx = getAudioContext();
|
||||
const input = document.getElementById('text');
|
||||
input.innerHTML = getTune();
|
||||
|
||||
evalScope(
|
||||
const loadModules = evalScope(
|
||||
controls,
|
||||
import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8'),
|
||||
import('https://cdn.skypack.dev/@strudel.cycles/mini@0.6.0'),
|
||||
import('https://cdn.skypack.dev/@strudel.cycles/tonal@0.6.0'),
|
||||
import('https://cdn.skypack.dev/@strudel.cycles/webaudio@0.6.0'),
|
||||
import('https://cdn.skypack.dev/@strudel/core@0.11.0'),
|
||||
import('https://cdn.skypack.dev/@strudel/mini@0.11.0'),
|
||||
import('https://cdn.skypack.dev/@strudel/tonal@0.11.0'),
|
||||
import('https://cdn.skypack.dev/@strudel/webaudio@0.11.0'),
|
||||
);
|
||||
|
||||
const initAudio = Promise.all([initAudioOnFirstClick(), registerSynthSounds()]);
|
||||
|
||||
const { evaluate } = repl({
|
||||
defaultOutput: webaudioOutput,
|
||||
getTime: () => ctx.currentTime,
|
||||
transpiler,
|
||||
});
|
||||
document.getElementById('start').addEventListener('click', () => evaluate(input.value));
|
||||
document.getElementById('start').addEventListener('click', async () => {
|
||||
await loadModules;
|
||||
await initAudio;
|
||||
evaluate(input.value);
|
||||
});
|
||||
|
||||
function getTune() {
|
||||
return `await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script src="https://unpkg.com/@strudel.cycles/embed@latest"></script>
|
||||
<script src="https://unpkg.com/@strudel/embed@0.11.0"></script>
|
||||
<!-- <script src="./embed.js"></script> -->
|
||||
<strudel-repl>
|
||||
<!--
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { StrudelMirror } from '@strudel/codemirror';
|
||||
import { funk42 } from './tunes';
|
||||
import { drawPianoroll, evalScope, controls } from '@strudel.cycles/core';
|
||||
import { drawPianoroll, evalScope, controls } from '@strudel/core';
|
||||
import './style.css';
|
||||
import { initAudioOnFirstClick } from '@strudel.cycles/webaudio';
|
||||
import { transpiler } from '@strudel.cycles/transpiler';
|
||||
import { getAudioContext, webaudioOutput, registerSynthSounds } from '@strudel.cycles/webaudio';
|
||||
import { registerSoundfonts } from '@strudel.cycles/soundfonts';
|
||||
import { initAudioOnFirstClick } from '@strudel/webaudio';
|
||||
import { transpiler } from '@strudel/transpiler';
|
||||
import { getAudioContext, webaudioOutput, registerSynthSounds } from '@strudel/webaudio';
|
||||
import { registerSoundfonts } from '@strudel/soundfonts';
|
||||
|
||||
// init canvas
|
||||
const canvas = document.getElementById('roll');
|
||||
@@ -26,10 +26,10 @@ const editor = new StrudelMirror({
|
||||
initAudioOnFirstClick(); // needed to make the browser happy (don't await this here..)
|
||||
const loadModules = evalScope(
|
||||
controls,
|
||||
import('@strudel.cycles/core'),
|
||||
import('@strudel.cycles/mini'),
|
||||
import('@strudel.cycles/tonal'),
|
||||
import('@strudel.cycles/webaudio'),
|
||||
import('@strudel/core'),
|
||||
import('@strudel/mini'),
|
||||
import('@strudel/tonal'),
|
||||
import('@strudel/webaudio'),
|
||||
);
|
||||
await Promise.all([loadModules, registerSynthSounds(), registerSoundfonts()]);
|
||||
},
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@strudel/codemirror": "workspace:*",
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel.cycles/mini": "workspace:*",
|
||||
"@strudel.cycles/soundfonts": "workspace:*",
|
||||
"@strudel.cycles/tonal": "workspace:*",
|
||||
"@strudel.cycles/transpiler": "workspace:*",
|
||||
"@strudel.cycles/webaudio": "workspace:*"
|
||||
"@strudel/core": "workspace:*",
|
||||
"@strudel/mini": "workspace:*",
|
||||
"@strudel/soundfonts": "workspace:*",
|
||||
"@strudel/tonal": "workspace:*",
|
||||
"@strudel/transpiler": "workspace:*",
|
||||
"@strudel/webaudio": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { controls, repl, evalScope } from '@strudel.cycles/core';
|
||||
import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel.cycles/webaudio';
|
||||
import { transpiler } from '@strudel.cycles/transpiler';
|
||||
import { controls, repl, evalScope } from '@strudel/core';
|
||||
import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel/webaudio';
|
||||
import { transpiler } from '@strudel/transpiler';
|
||||
import tune from './tune.mjs';
|
||||
|
||||
const ctx = getAudioContext();
|
||||
@@ -10,10 +10,10 @@ initAudioOnFirstClick();
|
||||
|
||||
evalScope(
|
||||
controls,
|
||||
import('@strudel.cycles/core'),
|
||||
import('@strudel.cycles/mini'),
|
||||
import('@strudel.cycles/webaudio'),
|
||||
import('@strudel.cycles/tonal'),
|
||||
import('@strudel/core'),
|
||||
import('@strudel/mini'),
|
||||
import('@strudel/webaudio'),
|
||||
import('@strudel/tonal'),
|
||||
);
|
||||
|
||||
const { evaluate } = repl({
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
"vite": "^5.0.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel.cycles/mini": "workspace:*",
|
||||
"@strudel.cycles/transpiler": "workspace:*",
|
||||
"@strudel.cycles/webaudio": "workspace:*",
|
||||
"@strudel.cycles/tonal": "workspace:*"
|
||||
"@strudel/core": "workspace:*",
|
||||
"@strudel/mini": "workspace:*",
|
||||
"@strudel/transpiler": "workspace:*",
|
||||
"@strudel/webaudio": "workspace:*",
|
||||
"@strudel/tonal": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@strudel.cycles/monorepo",
|
||||
"name": "@strudel/monorepo",
|
||||
"version": "0.5.0",
|
||||
"private": true,
|
||||
"description": "Port of tidalcycles to javascript",
|
||||
@@ -45,12 +45,12 @@
|
||||
},
|
||||
"homepage": "https://strudel.cc",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel.cycles/mini": "workspace:*",
|
||||
"@strudel.cycles/tonal": "workspace:*",
|
||||
"@strudel.cycles/transpiler": "workspace:*",
|
||||
"@strudel.cycles/webaudio": "workspace:*",
|
||||
"@strudel.cycles/xen": "workspace:*"
|
||||
"@strudel/core": "workspace:*",
|
||||
"@strudel/mini": "workspace:*",
|
||||
"@strudel/tonal": "workspace:*",
|
||||
"@strudel/transpiler": "workspace:*",
|
||||
"@strudel/webaudio": "workspace:*",
|
||||
"@strudel/xen": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dependency-tree": "^10.0.9",
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# Packages
|
||||
|
||||
Each folder represents one of the @strudel.cycles/* packages [published to npm](https://www.npmjs.com/org/strudel.cycles).
|
||||
Each folder represents one of the @strudel/* packages [published to npm](https://www.npmjs.com/org/strudel).
|
||||
|
||||
To understand how those pieces connect, refer to the [Technical Manual](https://github.com/tidalcycles/strudel/wiki/Technical-Manual) or the individual READMEs.
|
||||
|
||||
@@ -3,6 +3,12 @@ import jsdoc from '../../doc.json';
|
||||
import { autocompletion } from '@codemirror/autocomplete';
|
||||
import { h } from './html';
|
||||
|
||||
function plaintext(str) {
|
||||
const div = document.createElement('div');
|
||||
div.innerText = str;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
const getDocLabel = (doc) => doc.name || doc.longname;
|
||||
const getInnerText = (html) => {
|
||||
var div = document.createElement('div');
|
||||
@@ -21,7 +27,7 @@ ${doc.description}
|
||||
)}
|
||||
</ul>
|
||||
<div>
|
||||
${doc.examples?.map((example) => `<div><pre>${example}</pre></div>`)}
|
||||
${doc.examples?.map((example) => `<div><pre>${plaintext(example)}</pre></div>`)}
|
||||
</div>
|
||||
</div>`[0];
|
||||
/*
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
// import { SelectionRange } from '@codemirror/state';
|
||||
import { RectangleMarker } from '@codemirror/view';
|
||||
import { layer } from '@codemirror/view';
|
||||
|
||||
// currently stuck with: https://discuss.codemirror.net/t/line-background-layer/7666
|
||||
|
||||
export const backlayer = layer({
|
||||
update: (update) => {
|
||||
return update.docChanged;
|
||||
},
|
||||
markers: (view) => {
|
||||
const offsetTop = 14; // how to know this number ? .view.documentTop is scroll relative..
|
||||
const offsetLeft = 4; // how to know this number ?
|
||||
|
||||
return view.viewportLineBlocks.map((block) => {
|
||||
const { left } = view.coordsAtPos(block.to);
|
||||
return new RectangleMarker('cm-backlayer', offsetLeft, block.top + offsetTop, left, block.height);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
/* const len = view.state.doc.length;
|
||||
const markers = RectangleMarker.forRange(
|
||||
view,
|
||||
'cm-backlayer',
|
||||
SelectionRange.fromJSON({
|
||||
from: 0,
|
||||
to: len,
|
||||
anchor: 0,
|
||||
head: len,
|
||||
empty: true,
|
||||
assoc: -1,
|
||||
bidiLevel: null,
|
||||
}),
|
||||
);
|
||||
console.log('markser', markers);
|
||||
return markers;
|
||||
*/
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
lineNumbers,
|
||||
drawSelection,
|
||||
} from '@codemirror/view';
|
||||
import { Pattern, Drawer, repl, cleanupDraw } from '@strudel.cycles/core';
|
||||
import { Pattern, Drawer, repl, cleanupDraw } from '@strudel/core';
|
||||
import { isAutoCompletionEnabled } from './autocomplete.mjs';
|
||||
import { isTooltipEnabled } from './tooltip.mjs';
|
||||
import { flash, isFlashEnabled } from './flash.mjs';
|
||||
@@ -21,7 +21,6 @@ import { keybindings } from './keybindings.mjs';
|
||||
import { initTheme, activateTheme, theme } from './themes.mjs';
|
||||
import { updateWidgets, sliderPlugin } from './slider.mjs';
|
||||
import { persistentAtom } from '@nanostores/persistent';
|
||||
import { backlayer } from './backlayer.mjs';
|
||||
|
||||
const extensions = {
|
||||
isLineWrappingEnabled: (on) => (on ? EditorView.lineWrapping : []),
|
||||
@@ -77,7 +76,6 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, roo
|
||||
history(),
|
||||
EditorView.updateListener.of((v) => onChange(v)),
|
||||
drawSelection({ cursorBlinkRate: 0 }),
|
||||
backlayer,
|
||||
Prec.highest(
|
||||
keymap.of([
|
||||
{
|
||||
|
||||
@@ -57,6 +57,9 @@ const visibleMiniLocations = StateField.define({
|
||||
// this is why we need to find a way to update the existing decorations, showing the ones that have an active range
|
||||
const haps = new Map();
|
||||
for (let hap of e.value.haps) {
|
||||
if (!hap.context?.locations || !hap.whole) {
|
||||
continue;
|
||||
}
|
||||
for (let { start, end } of hap.context.locations) {
|
||||
let id = `${start}:${end}`;
|
||||
if (!haps.has(id) || haps.get(id).whole.begin.lt(hap.whole.begin)) {
|
||||
@@ -64,7 +67,6 @@ const visibleMiniLocations = StateField.define({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visible = { atTime: e.value.atTime, haps };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,3 @@ export * from './highlight.mjs';
|
||||
export * from './flash.mjs';
|
||||
export * from './slider.mjs';
|
||||
export * from './themes.mjs';
|
||||
export * from './backlayer.mjs';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel/codemirror",
|
||||
"version": "0.9.0",
|
||||
"version": "1.0.0",
|
||||
"description": "Codemirror Extensions for Strudel",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
@@ -41,14 +41,14 @@
|
||||
"@codemirror/state": "^6.4.0",
|
||||
"@codemirror/view": "^6.23.0",
|
||||
"@lezer/highlight": "^1.2.0",
|
||||
"@nanostores/persistent": "^0.9.1",
|
||||
"@replit/codemirror-emacs": "^6.0.1",
|
||||
"@replit/codemirror-vim": "^6.1.0",
|
||||
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel/core": "workspace:*",
|
||||
"@uiw/codemirror-themes": "^4.21.21",
|
||||
"@uiw/codemirror-themes-all": "^4.21.21",
|
||||
"nanostores": "^0.9.5",
|
||||
"@nanostores/persistent": "^0.9.1"
|
||||
"nanostores": "^0.9.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^5.0.10"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ref, pure } from '@strudel.cycles/core';
|
||||
import { ref, pure } from '@strudel/core';
|
||||
import { WidgetType, ViewPlugin, Decoration } from '@codemirror/view';
|
||||
import { StateEffect, StateField } from '@codemirror/state';
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# @strudel.cycles/core
|
||||
# @strudel/core
|
||||
|
||||
This package contains the bare essence of strudel.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm i @strudel.cycles/core --save
|
||||
npm i @strudel/core --save
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
import { sequence } from '@strudel.cycles/core';
|
||||
import { sequence } from '@strudel/core';
|
||||
|
||||
const pattern = sequence('a', ['b', 'c']);
|
||||
|
||||
@@ -33,7 +33,7 @@ b: 3/2 - 7/4
|
||||
c: 7/4 - 2
|
||||
```
|
||||
|
||||
- [play with @strudel.cycles/core on codesandbox](https://codesandbox.io/s/strudel-core-test-forked-9ywhv7?file=/src/index.js).
|
||||
- [play with @strudel/core on codesandbox](https://codesandbox.io/s/strudel-core-test-forked-9ywhv7?file=/src/index.js).
|
||||
- [open color pattern example](https://raw.githack.com/tidalcycles/strudel/main/packages/core/examples/canvas.html)
|
||||
- [open minimal repl example](https://raw.githack.com/tidalcycles/strudel/main/packages/core/examples/vanilla.html)
|
||||
- [open minimal vite example](./examples/vite-vanilla-repl/)
|
||||
+171
-81
@@ -40,7 +40,7 @@ const generic_params = [
|
||||
* @name n
|
||||
* @param {number | Pattern} value sample index starting from 0
|
||||
* @example
|
||||
* s("bd sd,hh*3").n("<0 1>")
|
||||
* s("bd sd [~ bd] sd,hh*6").n("<0 1>")
|
||||
*/
|
||||
// also see https://github.com/tidalcycles/strudel/pull/63
|
||||
['n'],
|
||||
@@ -82,7 +82,7 @@ const generic_params = [
|
||||
* @name gain
|
||||
* @param {number | Pattern} amount gain.
|
||||
* @example
|
||||
* s("hh*8").gain(".4!2 1 .4!2 1 .4 1")
|
||||
* s("hh*8").gain(".4!2 1 .4!2 1 .4 1").fast(2)
|
||||
*
|
||||
*/
|
||||
['gain'],
|
||||
@@ -91,13 +91,13 @@ const generic_params = [
|
||||
*
|
||||
* @name postgain
|
||||
* @example
|
||||
* s("bd sd,hh*4")
|
||||
* s("bd sd [~ bd] sd,hh*8")
|
||||
* .compressor("-20:20:10:.002:.02").postgain(1.5)
|
||||
*
|
||||
*/
|
||||
['postgain'],
|
||||
/**
|
||||
* Like {@link gain}, but linear.
|
||||
* Like `gain`, but linear.
|
||||
*
|
||||
* @name amp
|
||||
* @param {number | Pattern} amount gain.
|
||||
@@ -114,7 +114,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} attack time in seconds.
|
||||
* @synonyms att
|
||||
* @example
|
||||
* note("c3 e3").attack("<0 .1 .5>")
|
||||
* note("c3 e3 f3 g3").attack("<0 .1 .5>")
|
||||
*
|
||||
*/
|
||||
['attack', 'att'],
|
||||
@@ -128,7 +128,7 @@ const generic_params = [
|
||||
* @name fmh
|
||||
* @param {number | Pattern} harmonicity
|
||||
* @example
|
||||
* note("c e g b")
|
||||
* note("c e g b g e")
|
||||
* .fm(4)
|
||||
* .fmh("<1 2 1.5 1.61>")
|
||||
* .scope()
|
||||
@@ -143,7 +143,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} brightness modulation index
|
||||
* @synonyms fmi
|
||||
* @example
|
||||
* note("c e g b")
|
||||
* note("c e g b g e")
|
||||
* .fm("<0 1 2 8 32>")
|
||||
* .scope()
|
||||
*
|
||||
@@ -156,7 +156,7 @@ const generic_params = [
|
||||
* @name fmenv
|
||||
* @param {number | Pattern} type lin | exp
|
||||
* @example
|
||||
* note("c e g b")
|
||||
* note("c e g b g e")
|
||||
* .fm(4)
|
||||
* .fmdecay(.2)
|
||||
* .fmsustain(0)
|
||||
@@ -171,7 +171,7 @@ const generic_params = [
|
||||
* @name fmattack
|
||||
* @param {number | Pattern} time attack time
|
||||
* @example
|
||||
* note("c e g b")
|
||||
* note("c e g b g e")
|
||||
* .fm(4)
|
||||
* .fmattack("<0 .05 .1 .2>")
|
||||
* .scope()
|
||||
@@ -184,7 +184,7 @@ const generic_params = [
|
||||
* @name fmdecay
|
||||
* @param {number | Pattern} time decay time
|
||||
* @example
|
||||
* note("c e g b")
|
||||
* note("c e g b g e")
|
||||
* .fm(4)
|
||||
* .fmdecay("<.01 .05 .1 .2>")
|
||||
* .fmsustain(.4)
|
||||
@@ -198,7 +198,7 @@ const generic_params = [
|
||||
* @name fmsustain
|
||||
* @param {number | Pattern} level sustain level
|
||||
* @example
|
||||
* note("c e g b")
|
||||
* note("c e g b g e")
|
||||
* .fm(4)
|
||||
* .fmdecay(.1)
|
||||
* .fmsustain("<1 .75 .5 0>")
|
||||
@@ -216,7 +216,7 @@ const generic_params = [
|
||||
* @name bank
|
||||
* @param {string | Pattern} bank the name of the bank
|
||||
* @example
|
||||
* s("bd sd").bank('RolandTR909') // = s("RolandTR909_bd RolandTR909_sd")
|
||||
* s("bd sd [~ bd] sd").bank('RolandTR909') // = s("RolandTR909_bd RolandTR909_sd")
|
||||
*
|
||||
*/
|
||||
['bank'],
|
||||
@@ -231,10 +231,10 @@ const generic_params = [
|
||||
* @name decay
|
||||
* @param {number | Pattern} time decay time in seconds
|
||||
* @example
|
||||
* note("c3 e3").decay("<.1 .2 .3 .4>").sustain(0)
|
||||
* note("c3 e3 f3 g3").decay("<.1 .2 .3 .4>").sustain(0)
|
||||
*
|
||||
*/
|
||||
['decay'],
|
||||
['decay', 'dec'],
|
||||
/**
|
||||
* Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset.
|
||||
*
|
||||
@@ -242,7 +242,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} gain sustain level between 0 and 1
|
||||
* @synonyms sus
|
||||
* @example
|
||||
* note("c3 e3").decay(.2).sustain("<0 .1 .4 .6 1>")
|
||||
* note("c3 e3 f3 g3").decay(.2).sustain("<0 .1 .4 .6 1>")
|
||||
*
|
||||
*/
|
||||
['sustain', 'sus'],
|
||||
@@ -267,10 +267,10 @@ const generic_params = [
|
||||
* @param {number | Pattern} frequency center frequency
|
||||
* @synonyms bandf, bp
|
||||
* @example
|
||||
* s("bd sd,hh*3").bpf("<1000 2000 4000 8000>")
|
||||
* s("bd sd [~ bd] sd,hh*6").bpf("<1000 2000 4000 8000>")
|
||||
*
|
||||
*/
|
||||
[['bandf', 'bandq'], 'bpf', 'bp'],
|
||||
[['bandf', 'bandq', 'bpenv'], 'bpf', 'bp'],
|
||||
// TODO: in tidal, it seems to be normalized
|
||||
/**
|
||||
* Sets the **b**and-**p**ass **q**-factor (resonance).
|
||||
@@ -279,7 +279,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} q q factor
|
||||
* @synonyms bandq
|
||||
* @example
|
||||
* s("bd sd").bpf(500).bpq("<0 1 2 3>")
|
||||
* s("bd sd [~ bd] sd").bpf(500).bpq("<0 1 2 3>")
|
||||
*
|
||||
*/
|
||||
// currently an alias of 'bandq' https://github.com/tidalcycles/strudel/issues/496
|
||||
@@ -293,7 +293,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample
|
||||
* @example
|
||||
* samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/Dirt-Samples/master/')
|
||||
* s("rave").begin("<0 .25 .5 .75>")
|
||||
* s("rave").begin("<0 .25 .5 .75>").fast(2)
|
||||
*
|
||||
*/
|
||||
['begin'],
|
||||
@@ -304,7 +304,7 @@ const generic_params = [
|
||||
* @name end
|
||||
* @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc..
|
||||
* @example
|
||||
* s("bd*2,oh*4").end("<.1 .2 .5 1>")
|
||||
* s("bd*2,oh*4").end("<.1 .2 .5 1>").fast(2)
|
||||
*
|
||||
*/
|
||||
['end'],
|
||||
@@ -376,7 +376,7 @@ const generic_params = [
|
||||
* @name coarse
|
||||
* @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on.
|
||||
* @example
|
||||
* s("bd sd,hh*4").coarse("<1 4 8 16 32>")
|
||||
* s("bd sd [~ bd] sd,hh*8").coarse("<1 4 8 16 32>")
|
||||
*
|
||||
*/
|
||||
['coarse'],
|
||||
@@ -463,7 +463,7 @@ const generic_params = [
|
||||
* @name cut
|
||||
* @param {number | Pattern} group cut group number
|
||||
* @example
|
||||
* s("rd*4").cut(1)
|
||||
* s("[oh hh]*4").cut(1)
|
||||
*
|
||||
*/
|
||||
['cut'],
|
||||
@@ -476,12 +476,12 @@ const generic_params = [
|
||||
* @param {number | Pattern} frequency audible between 0 and 20000
|
||||
* @synonyms cutoff, ctf, lp
|
||||
* @example
|
||||
* s("bd sd,hh*3").lpf("<4000 2000 1000 500 200 100>")
|
||||
* s("bd sd [~ bd] sd,hh*6").lpf("<4000 2000 1000 500 200 100>")
|
||||
* @example
|
||||
* s("bd*8").lpf("1000:0 1000:10 1000:20 1000:30")
|
||||
* s("bd*16").lpf("1000:0 1000:10 1000:20 1000:30")
|
||||
*
|
||||
*/
|
||||
[['cutoff', 'resonance'], 'ctf', 'lpf', 'lp'],
|
||||
[['cutoff', 'resonance', 'lpenv'], 'ctf', 'lpf', 'lp'],
|
||||
|
||||
/**
|
||||
* Sets the lowpass filter envelope modulation depth.
|
||||
@@ -489,7 +489,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_
|
||||
* @synonyms lpe
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .lpf(500)
|
||||
* .lpa(.5)
|
||||
@@ -502,7 +502,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_
|
||||
* @synonyms hpe
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .hpf(500)
|
||||
* .hpa(.5)
|
||||
@@ -515,7 +515,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_
|
||||
* @synonyms bpe
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .bpf(500)
|
||||
* .bpa(.5)
|
||||
@@ -528,7 +528,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} attack time of the filter envelope
|
||||
* @synonyms lpa
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .lpf(500)
|
||||
* .lpa("<.5 .25 .1 .01>/4")
|
||||
@@ -541,7 +541,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} attack time of the highpass filter envelope
|
||||
* @synonyms hpa
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .hpf(500)
|
||||
* .hpa("<.5 .25 .1 .01>/4")
|
||||
@@ -554,7 +554,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} attack time of the bandpass filter envelope
|
||||
* @synonyms bpa
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .bpf(500)
|
||||
* .bpa("<.5 .25 .1 .01>/4")
|
||||
@@ -567,7 +567,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} decay time of the filter envelope
|
||||
* @synonyms lpd
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .lpf(500)
|
||||
* .lpd("<.5 .25 .1 0>/4")
|
||||
@@ -581,7 +581,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} decay time of the highpass filter envelope
|
||||
* @synonyms hpd
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .hpf(500)
|
||||
* .hpd("<.5 .25 .1 0>/4")
|
||||
@@ -595,7 +595,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} decay time of the bandpass filter envelope
|
||||
* @synonyms bpd
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .bpf(500)
|
||||
* .bpd("<.5 .25 .1 0>/4")
|
||||
@@ -609,7 +609,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} sustain amplitude of the lowpass filter envelope
|
||||
* @synonyms lps
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .lpf(500)
|
||||
* .lpd(.5)
|
||||
@@ -623,7 +623,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} sustain amplitude of the highpass filter envelope
|
||||
* @synonyms hps
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .hpf(500)
|
||||
* .hpd(.5)
|
||||
@@ -637,7 +637,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} sustain amplitude of the bandpass filter envelope
|
||||
* @synonyms bps
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .bpf(500)
|
||||
* .bpd(.5)
|
||||
@@ -651,7 +651,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} release time of the filter envelope
|
||||
* @synonyms lpr
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .clip(.5)
|
||||
* .lpf(500)
|
||||
@@ -666,7 +666,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} release time of the highpass filter envelope
|
||||
* @synonyms hpr
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .clip(.5)
|
||||
* .hpf(500)
|
||||
@@ -681,7 +681,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} release time of the bandpass filter envelope
|
||||
* @synonyms bpr
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .clip(.5)
|
||||
* .bpf(500)
|
||||
@@ -695,11 +695,11 @@ const generic_params = [
|
||||
* @name ftype
|
||||
* @param {number | Pattern} type 12db (default) or 24db
|
||||
* @example
|
||||
* note("<c2 e2 f2 g2>")
|
||||
* note("c2 e2 f2 g2")
|
||||
* .sound('sawtooth')
|
||||
* .lpf(500)
|
||||
* .bpenv(4)
|
||||
* .ftype("<12db 24db>")
|
||||
* .ftype("12db 24db")
|
||||
*/
|
||||
['ftype'],
|
||||
['fanchor'],
|
||||
@@ -712,9 +712,9 @@ const generic_params = [
|
||||
* @param {number | Pattern} frequency audible between 0 and 20000
|
||||
* @synonyms hp, hcutoff
|
||||
* @example
|
||||
* s("bd sd,hh*4").hpf("<4000 2000 1000 500 200 100>")
|
||||
* s("bd sd [~ bd] sd,hh*8").hpf("<4000 2000 1000 500 200 100>")
|
||||
* @example
|
||||
* s("bd sd,hh*4").hpf("<2000 2000:25>")
|
||||
* s("bd sd [~ bd] sd,hh*8").hpf("<2000 2000:25>")
|
||||
*
|
||||
*/
|
||||
// currently an alias of 'hcutoff' https://github.com/tidalcycles/strudel/issues/496
|
||||
@@ -726,11 +726,11 @@ const generic_params = [
|
||||
* @synonyms vibrato, v
|
||||
* @param {number | Pattern} frequency of the vibrato in hertz
|
||||
* @example
|
||||
* note("a")
|
||||
* note("a e")
|
||||
* .vib("<.5 1 2 4 8 16>")
|
||||
* @example
|
||||
* // change the modulation depth with ":"
|
||||
* note("a")
|
||||
* note("a e")
|
||||
* .vib("<.5 1 2 4 8 16>:12")
|
||||
*/
|
||||
[['vib', 'vibmod'], 'vibrato', 'v'],
|
||||
@@ -750,15 +750,15 @@ const generic_params = [
|
||||
* @synonyms vmod
|
||||
* @param {number | Pattern} depth of vibrato (in semitones)
|
||||
* @example
|
||||
* note("a").vib(4)
|
||||
* note("a e").vib(4)
|
||||
* .vibmod("<.25 .5 1 2 12>")
|
||||
* @example
|
||||
* // change the vibrato frequency with ":"
|
||||
* note("a")
|
||||
* note("a e")
|
||||
* .vibmod("<.25 .5 1 2 12>:8")
|
||||
*/
|
||||
[['vibmod', 'vib'], 'vmod'],
|
||||
[['hcutoff', 'hresonance'], 'hpf', 'hp'],
|
||||
[['hcutoff', 'hresonance', 'hpenv'], 'hpf', 'hp'],
|
||||
/**
|
||||
* Controls the **h**igh-**p**ass **q**-value.
|
||||
*
|
||||
@@ -766,7 +766,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} q resonance factor between 0 and 50
|
||||
* @synonyms hresonance
|
||||
* @example
|
||||
* s("bd sd,hh*4").hpf(2000).hpq("<0 10 20 30>")
|
||||
* s("bd sd [~ bd] sd,hh*8").hpf(2000).hpq("<0 10 20 30>")
|
||||
*
|
||||
*/
|
||||
['hresonance', 'hpq'],
|
||||
@@ -777,7 +777,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} q resonance factor between 0 and 50
|
||||
* @synonyms resonance
|
||||
* @example
|
||||
* s("bd sd,hh*4").lpf(2000).lpq("<0 10 20 30>")
|
||||
* s("bd sd [~ bd] sd,hh*8").lpf(2000).lpq("<0 10 20 30>")
|
||||
*
|
||||
*/
|
||||
// currently an alias of 'resonance' https://github.com/tidalcycles/strudel/issues/496
|
||||
@@ -804,7 +804,7 @@ const generic_params = [
|
||||
* @name delay
|
||||
* @param {number | Pattern} level between 0 and 1
|
||||
* @example
|
||||
* s("bd").delay("<0 .25 .5 1>")
|
||||
* s("bd bd").delay("<0 .25 .5 1>")
|
||||
* @example
|
||||
* s("bd bd").delay("0.65:0.25:0.9 0.65:0.125:0.7")
|
||||
*
|
||||
@@ -818,7 +818,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} feedback between 0 and 1
|
||||
* @synonyms delayfb, dfb
|
||||
* @example
|
||||
* s("bd").delay(.25).delayfeedback("<.25 .5 .75 1>").slow(2)
|
||||
* s("bd").delay(.25).delayfeedback("<.25 .5 .75 1>")
|
||||
*
|
||||
*/
|
||||
['delayfeedback', 'delayfb', 'dfb'],
|
||||
@@ -829,7 +829,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} seconds between 0 and Infinity
|
||||
* @synonyms delayt, dt
|
||||
* @example
|
||||
* s("bd").delay(.25).delaytime("<.125 .25 .5 1>").slow(2)
|
||||
* s("bd bd").delay(.25).delaytime("<.125 .25 .5 1>")
|
||||
*
|
||||
*/
|
||||
['delaytime', 'delayt', 'dt'],
|
||||
@@ -856,7 +856,7 @@ const generic_params = [
|
||||
*/
|
||||
['detune', 'det'],
|
||||
/**
|
||||
* Set dryness of reverb. See {@link room} and {@link size} for more information about reverb.
|
||||
* Set dryness of reverb. See `room` and `size` for more information about reverb.
|
||||
*
|
||||
* @name dry
|
||||
* @param {number | Pattern} dry 0 = wet, 1 = dry
|
||||
@@ -868,7 +868,7 @@ const generic_params = [
|
||||
['dry'],
|
||||
// TODO: does not seem to do anything
|
||||
/*
|
||||
* Used when using {@link begin}/{@link end} or {@link chop}/{@link striate} and friends, to change the fade out time of the 'grain' envelope.
|
||||
* Used when using `begin`/`end` or `chop`/`striate` and friends, to change the fade out time of the 'grain' envelope.
|
||||
*
|
||||
* @name fadeTime
|
||||
* @param {number | Pattern} time between 0 and 1
|
||||
@@ -891,6 +891,82 @@ const generic_params = [
|
||||
*
|
||||
*/
|
||||
['freq'],
|
||||
// pitch envelope
|
||||
/**
|
||||
* Attack time of pitch envelope.
|
||||
*
|
||||
* @name pattack
|
||||
* @synonyms patt
|
||||
* @param {number | Pattern} time time in seconds
|
||||
* @example
|
||||
* note("c eb g bb").pattack("0 .1 .25 .5").slow(2)
|
||||
*
|
||||
*/
|
||||
['pattack', 'patt'],
|
||||
/**
|
||||
* Decay time of pitch envelope.
|
||||
*
|
||||
* @name pdecay
|
||||
* @synonyms pdec
|
||||
* @param {number | Pattern} time time in seconds
|
||||
* @example
|
||||
* note("<c eb g bb>").pdecay("<0 .1 .25 .5>")
|
||||
*
|
||||
*/
|
||||
['pdecay', 'pdec'],
|
||||
// TODO: how to use psustain?!
|
||||
['psustain', 'psus'],
|
||||
/**
|
||||
* Release time of pitch envelope
|
||||
*
|
||||
* @name prelease
|
||||
* @synonyms prel
|
||||
* @param {number | Pattern} time time in seconds
|
||||
* @example
|
||||
* note("<c eb g bb> ~")
|
||||
* .release(.5) // to hear the pitch release
|
||||
* .prelease("<0 .1 .25 .5>")
|
||||
*
|
||||
*/
|
||||
['prelease', 'prel'],
|
||||
/**
|
||||
* Amount of pitch envelope. Negative values will flip the envelope.
|
||||
* If you don't set other pitch envelope controls, `pattack:.2` will be the default.
|
||||
*
|
||||
* @name penv
|
||||
* @param {number | Pattern} semitones change in semitones
|
||||
* @example
|
||||
* note("c")
|
||||
* .penv("<12 7 1 .5 0 -1 -7 -12>")
|
||||
*
|
||||
*/
|
||||
['penv'],
|
||||
/**
|
||||
* Curve of envelope. Defaults to linear. exponential is good for kicks
|
||||
*
|
||||
* @name pcurve
|
||||
* @param {number | Pattern} type 0 = linear, 1 = exponential
|
||||
* @example
|
||||
* note("g1*4")
|
||||
* .s("sine").pdec(.5)
|
||||
* .penv(32)
|
||||
* .pcurve("<0 1>")
|
||||
*
|
||||
*/
|
||||
['pcurve'],
|
||||
/**
|
||||
* Sets the range anchor of the envelope:
|
||||
* - anchor 0: range = [note, note + penv]
|
||||
* - anchor 1: range = [note - penv, note]
|
||||
* If you don't set an anchor, the value will default to the psustain value.
|
||||
*
|
||||
* @name panchor
|
||||
* @param {number | Pattern} anchor anchor offset
|
||||
* @example
|
||||
* note("c c4").penv(12).panchor("<0 .5 1 .5>")
|
||||
*
|
||||
*/
|
||||
['panchor'],
|
||||
// TODO: https://tidalcycles.org/docs/configuration/MIDIOSC/control-voltage/#gate
|
||||
['gate', 'gat'],
|
||||
// ['hatgrain'],
|
||||
@@ -986,8 +1062,8 @@ const generic_params = [
|
||||
* @param {number | Pattern} number
|
||||
* @example
|
||||
* stack(
|
||||
* s("hh*3").delay(.5).delaytime(.25).orbit(1),
|
||||
* s("~ sd").delay(.5).delaytime(.125).orbit(2)
|
||||
* s("hh*6").delay(.5).delaytime(.25).orbit(1),
|
||||
* s("~ sd ~ sd").delay(.5).delaytime(.125).orbit(2)
|
||||
* )
|
||||
*/
|
||||
['orbit'],
|
||||
@@ -1000,6 +1076,8 @@ const generic_params = [
|
||||
* @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel)
|
||||
* @example
|
||||
* s("[bd hh]*2").pan("<.5 1 .5 0>")
|
||||
* @example
|
||||
* s("bd rim sd rim bd ~ cp rim").pan(sine.slow(2))
|
||||
*
|
||||
*/
|
||||
['pan'],
|
||||
@@ -1057,9 +1135,9 @@ const generic_params = [
|
||||
* @name room
|
||||
* @param {number | Pattern} level between 0 and 1
|
||||
* @example
|
||||
* s("bd sd").room("<0 .2 .4 .6 .8 1>")
|
||||
* s("bd sd [~ bd] sd").room("<0 .2 .4 .6 .8 1>")
|
||||
* @example
|
||||
* s("bd sd").room("<0.9:1 0.9:4>")
|
||||
* s("bd sd [~ bd] sd").room("<0.9:1 0.9:4>")
|
||||
*
|
||||
*/
|
||||
[['room', 'size']],
|
||||
@@ -1071,9 +1149,9 @@ const generic_params = [
|
||||
* @synonyms rlp
|
||||
* @param {number} frequency between 0 and 20000hz
|
||||
* @example
|
||||
* s("bd sd").room(0.5).rlp(10000)
|
||||
* s("bd sd [~ bd] sd").room(0.5).rlp(10000)
|
||||
* @example
|
||||
* s("bd sd").room(0.5).rlp(5000)
|
||||
* s("bd sd [~ bd] sd").room(0.5).rlp(5000)
|
||||
*/
|
||||
['roomlp', 'rlp'],
|
||||
/**
|
||||
@@ -1084,9 +1162,9 @@ const generic_params = [
|
||||
* @synonyms rdim
|
||||
* @param {number} frequency between 0 and 20000hz
|
||||
* @example
|
||||
* s("bd sd").room(0.5).rlp(10000).rdim(8000)
|
||||
* s("bd sd [~ bd] sd").room(0.5).rlp(10000).rdim(8000)
|
||||
* @example
|
||||
* s("bd sd").room(0.5).rlp(5000).rdim(400)
|
||||
* s("bd sd [~ bd] sd").room(0.5).rlp(5000).rdim(400)
|
||||
*
|
||||
*/
|
||||
['roomdim', 'rdim'],
|
||||
@@ -1098,9 +1176,9 @@ const generic_params = [
|
||||
* @synonyms rfade
|
||||
* @param {number} seconds for the reverb to fade
|
||||
* @example
|
||||
* s("bd sd").room(0.5).rlp(10000).rfade(0.5)
|
||||
* s("bd sd [~ bd] sd").room(0.5).rlp(10000).rfade(0.5)
|
||||
* @example
|
||||
* s("bd sd").room(0.5).rlp(5000).rfade(4)
|
||||
* s("bd sd [~ bd] sd").room(0.5).rlp(5000).rfade(4)
|
||||
*
|
||||
*/
|
||||
['roomfade', 'rfade'],
|
||||
@@ -1110,25 +1188,25 @@ const generic_params = [
|
||||
* @param {string | Pattern} sample to use as an impulse response
|
||||
* @synonyms ir
|
||||
* @example
|
||||
* s("bd sd").room(.8).ir("<shaker_large:0 shaker_large:2>")
|
||||
* s("bd sd [~ bd] sd").room(.8).ir("<shaker_large:0 shaker_large:2>")
|
||||
*
|
||||
*/
|
||||
[['ir', 'i'], 'iresponse'],
|
||||
/**
|
||||
* Sets the room size of the reverb, see {@link room}.
|
||||
* Sets the room size of the reverb, see `room`.
|
||||
* When this property is changed, the reverb will be recaculated, so only change this sparsely..
|
||||
*
|
||||
* @name roomsize
|
||||
* @param {number | Pattern} size between 0 and 10
|
||||
* @synonyms rsize, sz, size
|
||||
* @example
|
||||
* s("bd sd").room(.8).rsize(1)
|
||||
* s("bd sd [~ bd] sd").room(.8).rsize(1)
|
||||
* @example
|
||||
* s("bd sd").room(.8).rsize(4)
|
||||
* s("bd sd [~ bd] sd").room(.8).rsize(4)
|
||||
*
|
||||
*/
|
||||
// TODO: find out why :
|
||||
// s("bd sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc()
|
||||
// s("bd sd [~ bd] sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc()
|
||||
// .. does not work. Is it because room is only one effect?
|
||||
['roomsize', 'size', 'sz', 'rsize'],
|
||||
// ['sagogo'],
|
||||
@@ -1141,7 +1219,7 @@ const generic_params = [
|
||||
* @name shape
|
||||
* @param {number | Pattern} distortion between 0 and 1
|
||||
* @example
|
||||
* s("bd sd,hh*4").shape("<0 .2 .4 .6 .8>")
|
||||
* s("bd sd [~ bd] sd,hh*8").shape("<0 .2 .4 .6 .8>")
|
||||
*
|
||||
*/
|
||||
['shape'],
|
||||
@@ -1151,7 +1229,7 @@ const generic_params = [
|
||||
*
|
||||
* @name compressor
|
||||
* @example
|
||||
* s("bd sd,hh*4")
|
||||
* s("bd sd [~ bd] sd,hh*8")
|
||||
* .compressor("-20:20:10:.002:.02")
|
||||
*
|
||||
*/
|
||||
@@ -1166,14 +1244,14 @@ const generic_params = [
|
||||
* @name speed
|
||||
* @param {number | Pattern} speed -inf to inf, negative numbers play the sample backwards.
|
||||
* @example
|
||||
* s("bd").speed("<1 2 4 1 -2 -4>")
|
||||
* s("bd*6").speed("1 2 4 1 -2 -4")
|
||||
* @example
|
||||
* speed("1 1.5*2 [2 1.1]").s("piano").clip(1)
|
||||
*
|
||||
*/
|
||||
['speed'],
|
||||
/**
|
||||
* Used in conjunction with {@link speed}, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`.
|
||||
* Used in conjunction with `speed`, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`.
|
||||
*
|
||||
* @name unit
|
||||
* @param {number | string | Pattern} unit see description above
|
||||
@@ -1209,10 +1287,12 @@ const generic_params = [
|
||||
* Formant filter to make things sound like vowels.
|
||||
*
|
||||
* @name vowel
|
||||
* @param {string | Pattern} vowel You can use a e i o u.
|
||||
* @param {string | Pattern} vowel You can use a e i o u ae aa oe ue y uh un en an on, corresponding to [a] [e] [i] [o] [u] [æ] [ɑ] [ø] [y] [ɯ] [ʌ] [œ̃] [ɛ̃] [ɑ̃] [ɔ̃]. Aliases: aa = å = ɑ, oe = ø = ö, y = ı, ae = æ.
|
||||
* @example
|
||||
* note("c2 <eb2 <g2 g1>>").s('sawtooth')
|
||||
* note("[c2 <eb2 <g2 g1>>]*2").s('sawtooth')
|
||||
* .vowel("<a e i <o u>>")
|
||||
* @example
|
||||
* s("bd sd mt ht bd [~ cp] ht lt").vowel("[a|e|i|o|u]")
|
||||
*
|
||||
*/
|
||||
['vowel'],
|
||||
@@ -1387,17 +1467,27 @@ controls.createParams = (...names) =>
|
||||
* @param {number | Pattern} gain sustain level (0 to 1)
|
||||
* @param {number | Pattern} time release time in seconds
|
||||
* @example
|
||||
* note("<c3 bb2 f3 eb3>").sound("sawtooth").lpf(600).adsr(".1:.1:.5:.2")
|
||||
* note("[c3 bb2 f3 eb3]*2").sound("sawtooth").lpf(600).adsr(".1:.1:.5:.2")
|
||||
*/
|
||||
controls.adsr = register('adsr', (adsr, pat) => {
|
||||
adsr = !Array.isArray(adsr) ? [adsr] : adsr;
|
||||
const [attack, decay, sustain, release] = adsr;
|
||||
return pat.set({ attack, decay, sustain, release });
|
||||
});
|
||||
controls.ds = register('ds', (ds, pat) => {
|
||||
ds = !Array.isArray(ds) ? [ds] : ds;
|
||||
const [decay, sustain] = ds;
|
||||
controls.ad = register('ad', (t, pat) => {
|
||||
t = !Array.isArray(t) ? [t] : t;
|
||||
const [attack, decay = attack] = t;
|
||||
return pat.attack(attack).decay(decay);
|
||||
});
|
||||
controls.ds = register('ds', (t, pat) => {
|
||||
t = !Array.isArray(t) ? [t] : t;
|
||||
const [decay, sustain = 0] = t;
|
||||
return pat.set({ decay, sustain });
|
||||
});
|
||||
controls.ds = register('ar', (t, pat) => {
|
||||
t = !Array.isArray(t) ? [t] : t;
|
||||
const [attack, release = attack] = t;
|
||||
return pat.set({ attack, release });
|
||||
});
|
||||
|
||||
export default controls;
|
||||
|
||||
@@ -10,7 +10,7 @@ import { logger } from './logger.mjs';
|
||||
export class Cyclist {
|
||||
constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1 }) {
|
||||
this.started = false;
|
||||
this.cps = 1;
|
||||
this.cps = 0.5;
|
||||
this.num_ticks_since_cps_change = 0;
|
||||
this.lastTick = 0; // absolute time when last tick (clock callback) happened
|
||||
this.lastBegin = 0; // query begin of last tick
|
||||
@@ -41,7 +41,7 @@ export class Cyclist {
|
||||
this.lastEnd = end;
|
||||
|
||||
// query the pattern for events
|
||||
const haps = this.pattern.queryArc(begin, end);
|
||||
const haps = this.pattern.queryArc(begin, end, { _cps: this.cps });
|
||||
|
||||
const tickdeadline = phase - time; // time left until the phase is a whole number
|
||||
this.lastTick = time + tickdeadline;
|
||||
@@ -96,7 +96,7 @@ export class Cyclist {
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
setCps(cps = 1) {
|
||||
setCps(cps = 0.5) {
|
||||
if (this.cps === cps) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ export class Drawer {
|
||||
this.lastFrame = phase;
|
||||
this.visibleHaps = (this.visibleHaps || [])
|
||||
// filter out haps that are too far in the past (think left edge of screen for pianoroll)
|
||||
.filter((h) => h.whole.end >= phase - lookbehind - lookahead)
|
||||
.filter((h) => h.whole?.end >= phase - lookbehind - lookahead)
|
||||
// add new haps with onset (think right edge bars scrolling in)
|
||||
.concat(haps.filter((h) => h.hasOnset()));
|
||||
const time = phase - lookahead;
|
||||
|
||||
@@ -10,9 +10,8 @@ https://rohandrape.net/?t=hmt
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Pattern, timeCat, register, silence } from './pattern.mjs';
|
||||
import { timeCat, register, silence } from './pattern.mjs';
|
||||
import { rotate, flatten, splitAt, zipWith } from './util.mjs';
|
||||
import Fraction from './fraction.mjs';
|
||||
|
||||
const left = function (n, x) {
|
||||
const [ons, offs] = n;
|
||||
@@ -58,6 +57,7 @@ export const bjork = function (ons, steps) {
|
||||
*
|
||||
* @memberof Pattern
|
||||
* @name euclid
|
||||
* @synonyms euc
|
||||
* @param {number} pulses the number of onsets / beats
|
||||
* @param {number} steps the number of steps to fill
|
||||
* @returns Pattern
|
||||
@@ -70,6 +70,7 @@ export const bjork = function (ons, steps) {
|
||||
* Like `euclid`, but has an additional parameter for 'rotating' the resulting sequence.
|
||||
* @memberof Pattern
|
||||
* @name euclidRot
|
||||
* @synonyms eucr
|
||||
* @param {number} pulses the number of onsets / beats
|
||||
* @param {number} steps the number of steps to fill
|
||||
* @param {number} rotation offset in steps
|
||||
@@ -132,23 +133,27 @@ const _euclidRot = function (pulses, steps, rotation) {
|
||||
return b;
|
||||
};
|
||||
|
||||
export const euclid = register('euclid', function (pulses, steps, pat) {
|
||||
export const euclid = register(['euclid', 'euc'], function (pulses, steps, pat) {
|
||||
return pat.struct(_euclidRot(pulses, steps, 0));
|
||||
});
|
||||
|
||||
export const { euclidrot, euclidRot } = register(['euclidrot', 'euclidRot'], function (pulses, steps, rotation, pat) {
|
||||
return pat.struct(_euclidRot(pulses, steps, rotation));
|
||||
});
|
||||
export const { euclidrot, euclidRot } = register(
|
||||
['euclidrot', 'euclidRot', 'eucr'],
|
||||
function (pulses, steps, rotation, pat) {
|
||||
return pat.struct(_euclidRot(pulses, steps, rotation));
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Similar to `euclid`, but each pulse is held until the next pulse,
|
||||
* so there will be no gaps.
|
||||
* @name euclidLegato
|
||||
* @synonyms eucl
|
||||
* @memberof Pattern
|
||||
* @param {number} pulses the number of onsets / beats
|
||||
* @param {number} steps the number of steps to fill
|
||||
* @example
|
||||
* n("g2").decay(.1).sustain(.3).euclidLegato(3,8)
|
||||
* note("c3").euclidLegato(3,8)
|
||||
*/
|
||||
|
||||
const _euclidLegato = function (pulses, steps, rotation, pat) {
|
||||
@@ -164,7 +169,7 @@ const _euclidLegato = function (pulses, steps, rotation, pat) {
|
||||
return pat.struct(timeCat(...gapless));
|
||||
};
|
||||
|
||||
export const euclidLegato = register(['euclidLegato'], function (pulses, steps, pat) {
|
||||
export const euclidLegato = register(['euclidLegato', 'eucl'], function (pulses, steps, pat) {
|
||||
return _euclidLegato(pulses, steps, 0, pat);
|
||||
});
|
||||
|
||||
@@ -173,6 +178,7 @@ export const euclidLegato = register(['euclidLegato'], function (pulses, steps,
|
||||
* so there will be no gaps, and has an additional parameter for 'rotating'
|
||||
* the resulting sequence
|
||||
* @name euclidLegatoRot
|
||||
* @synonyms euclr
|
||||
* @memberof Pattern
|
||||
* @param {number} pulses the number of onsets / beats
|
||||
* @param {number} steps the number of steps to fill
|
||||
@@ -180,6 +186,6 @@ export const euclidLegato = register(['euclidLegato'], function (pulses, steps,
|
||||
* @example
|
||||
* note("c3").euclidLegatoRot(3,5,2)
|
||||
*/
|
||||
export const euclidLegatoRot = register(['euclidLegatoRot'], function (pulses, steps, rotation, pat) {
|
||||
export const euclidLegatoRot = register(['euclidLegatoRot', 'euclr'], function (pulses, steps, rotation, pat) {
|
||||
return _euclidLegato(pulses, steps, rotation, pat);
|
||||
});
|
||||
|
||||
@@ -30,12 +30,12 @@ export { default as drawLine } from './drawLine.mjs';
|
||||
// below won't work with runtime.mjs (json import fails)
|
||||
/* import * as p from './package.json';
|
||||
export const version = p.version; */
|
||||
logger('🌀 @strudel.cycles/core loaded 🌀');
|
||||
logger('🌀 @strudel/core loaded 🌀');
|
||||
if (globalThis._strudelLoaded) {
|
||||
console.warn(
|
||||
`@strudel.cycles/core was loaded more than once...
|
||||
`@strudel/core was loaded more than once...
|
||||
This might happen when you have multiple versions of strudel installed.
|
||||
Please check with "npm ls @strudel.cycles/core".`,
|
||||
Please check with "npm ls @strudel/core".`,
|
||||
);
|
||||
}
|
||||
globalThis._strudelLoaded = true;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/core",
|
||||
"version": "0.9.0",
|
||||
"name": "@strudel/core",
|
||||
"version": "1.0.0",
|
||||
"description": "Port of Tidal Cycles to JavaScript",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
|
||||
+122
-101
@@ -26,7 +26,7 @@ export class Pattern {
|
||||
/**
|
||||
* Create a pattern. As an end user, you will most likely not create a Pattern directly.
|
||||
*
|
||||
* @param {function} query - The function that maps a {@link State} to an array of {@link Hap}.
|
||||
* @param {function} query - The function that maps a `State` to an array of `Hap`.
|
||||
* @noAutocomplete
|
||||
*/
|
||||
constructor(query) {
|
||||
@@ -39,7 +39,7 @@ export class Pattern {
|
||||
|
||||
/**
|
||||
* Returns a new pattern, with the function applied to the value of
|
||||
* each hap. It has the alias {@link Pattern#fmap}.
|
||||
* each hap. It has the alias `fmap`.
|
||||
* @synonyms fmap
|
||||
* @param {Function} func to to apply to the value
|
||||
* @returns Pattern
|
||||
@@ -51,7 +51,7 @@ export class Pattern {
|
||||
}
|
||||
|
||||
/**
|
||||
* see {@link Pattern#withValue}
|
||||
* see `withValue`
|
||||
* @noAutocomplete
|
||||
*/
|
||||
fmap(func) {
|
||||
@@ -115,7 +115,7 @@ export class Pattern {
|
||||
}
|
||||
|
||||
/**
|
||||
* As with {@link Pattern#appBoth}, but the `whole` timespan is not the intersection,
|
||||
* As with `appBoth`, but the `whole` timespan is not the intersection,
|
||||
* but the timespan from the function of patterns that this method is called
|
||||
* on. In practice, this means that the pattern structure, including onsets,
|
||||
* are preserved from the pattern of functions (often referred to as the left
|
||||
@@ -148,7 +148,7 @@ export class Pattern {
|
||||
}
|
||||
|
||||
/**
|
||||
* As with {@link Pattern#appLeft}, but `whole` timespans are instead taken from the
|
||||
* As with `appLeft`, but `whole` timespans are instead taken from the
|
||||
* pattern of values, i.e. structure is preserved from the right hand/outer
|
||||
* pattern.
|
||||
* @param {Pattern} pat_val
|
||||
@@ -340,9 +340,9 @@ export class Pattern {
|
||||
* silence
|
||||
* @noAutocomplete
|
||||
*/
|
||||
queryArc(begin, end) {
|
||||
queryArc(begin, end, controls = {}) {
|
||||
try {
|
||||
return this.query(new State(new TimeSpan(begin, end)));
|
||||
return this.query(new State(new TimeSpan(begin, end), controls));
|
||||
} catch (err) {
|
||||
logger(`[query]: ${err.message}`, 'error');
|
||||
return [];
|
||||
@@ -387,7 +387,7 @@ export class Pattern {
|
||||
}
|
||||
|
||||
/**
|
||||
* As with {@link Pattern#withQuerySpan}, but the function is applied to both the
|
||||
* As with `withQuerySpan`, but the function is applied to both the
|
||||
* begin and end time of the query timespan.
|
||||
* @param {Function} func the function to apply
|
||||
* @returns Pattern
|
||||
@@ -398,7 +398,7 @@ export class Pattern {
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link Pattern#withQuerySpan}, but the function is applied to the timespans
|
||||
* Similar to `withQuerySpan`, but the function is applied to the timespans
|
||||
* of all haps returned by pattern queries (both `part` timespans, and where
|
||||
* present, `whole` timespans).
|
||||
* @param {Function} func
|
||||
@@ -410,7 +410,7 @@ export class Pattern {
|
||||
}
|
||||
|
||||
/**
|
||||
* As with {@link Pattern#withHapSpan}, but the function is applied to both the
|
||||
* As with `withHapSpan`, but the function is applied to both the
|
||||
* begin and end time of the hap timespans.
|
||||
* @param {Function} func the function to apply
|
||||
* @returns Pattern
|
||||
@@ -427,11 +427,11 @@ export class Pattern {
|
||||
* @noAutocomplete
|
||||
*/
|
||||
withHaps(func) {
|
||||
return new Pattern((state) => func(this.query(state)));
|
||||
return new Pattern((state) => func(this.query(state), state));
|
||||
}
|
||||
|
||||
/**
|
||||
* As with {@link Pattern#withHaps}, but applies the function to every hap, rather than every list of haps.
|
||||
* As with `withHaps`, but applies the function to every hap, rather than every list of haps.
|
||||
* @param {Function} func
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
@@ -499,7 +499,7 @@ export class Pattern {
|
||||
}
|
||||
|
||||
/**
|
||||
* As with {@link Pattern#filterHaps}, but the function is applied to values
|
||||
* As with `filterHaps`, but the function is applied to values
|
||||
* inside haps.
|
||||
* @param {Function} value_test
|
||||
* @returns Pattern
|
||||
@@ -621,7 +621,7 @@ export class Pattern {
|
||||
}
|
||||
|
||||
/**
|
||||
* More human-readable version of the {@link Pattern#firstCycleValues} accessor.
|
||||
* More human-readable version of the `firstCycleValues` accessor.
|
||||
* @noAutocomplete
|
||||
*/
|
||||
get showFirstCycle() {
|
||||
@@ -691,13 +691,13 @@ export class Pattern {
|
||||
// Methods without corresponding toplevel functions
|
||||
|
||||
/**
|
||||
* Layers the result of the given function(s). Like {@link Pattern.superimpose}, but without the original pattern:
|
||||
* Layers the result of the given function(s). Like `superimpose`, but without the original pattern:
|
||||
* @name layer
|
||||
* @memberof Pattern
|
||||
* @synonyms apply
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4"
|
||||
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*8"
|
||||
* .layer(x=>x.add("0,2"))
|
||||
* .scale('C minor').note()
|
||||
*/
|
||||
@@ -711,7 +711,7 @@ export class Pattern {
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4"
|
||||
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*8"
|
||||
* .superimpose(x=>x.add(2))
|
||||
* .scale('C minor').note()
|
||||
*/
|
||||
@@ -727,8 +727,8 @@ export class Pattern {
|
||||
* @name stack
|
||||
* @memberof Pattern
|
||||
* @example
|
||||
* s("hh*2").stack(
|
||||
* note("c2(3,8)")
|
||||
* s("hh*4").stack(
|
||||
* note("c4(5,8)")
|
||||
* )
|
||||
*/
|
||||
stack(...pats) {
|
||||
@@ -745,8 +745,8 @@ export class Pattern {
|
||||
* @memberof Pattern
|
||||
* @synonyms sequence, fastcat
|
||||
* @example
|
||||
* s("hh*2").seq(
|
||||
* note("c2(3,8)")
|
||||
* s("hh*4").seq(
|
||||
* note("c4(5,8)")
|
||||
* )
|
||||
*/
|
||||
seq(...pats) {
|
||||
@@ -759,8 +759,8 @@ export class Pattern {
|
||||
* @memberof Pattern
|
||||
* @synonyms slowcat
|
||||
* @example
|
||||
* s("hh*2").cat(
|
||||
* note("c2(3,8)")
|
||||
* s("hh*4").cat(
|
||||
* note("c4(5,8)")
|
||||
* )
|
||||
*/
|
||||
cat(...pats) {
|
||||
@@ -858,7 +858,7 @@ Pattern.prototype.arpWith = function (func) {
|
||||
* Selects indices in in stacked notes.
|
||||
* @example
|
||||
* note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>")
|
||||
* .arp("0 [0,2] 1 [0,2]").slow(2)
|
||||
* .arp("0 [0,2] 1 [0,2]")
|
||||
* */
|
||||
Pattern.prototype.arp = function (pat) {
|
||||
return this.arpWith((haps) => pat.fmap((i) => haps[i % haps.length]));
|
||||
@@ -929,14 +929,14 @@ function _composeOp(a, b, func) {
|
||||
* @memberof Pattern
|
||||
* @example
|
||||
* // Here, the triad 0, 2, 4 is shifted by different amounts
|
||||
* "0 2 4".add("<0 3 4 0>").scale('C major').note()
|
||||
* n("0 2 4".add("<0 3 4 0>")).scale("C:major")
|
||||
* // Without add, the equivalent would be:
|
||||
* // "<[0 2 4] [3 5 7] [4 6 8] [0 2 4]>".scale('C major').note()
|
||||
* // n("<[0 2 4] [3 5 7] [4 6 8] [0 2 4]>").scale("C:major")
|
||||
* @example
|
||||
* // You can also use add with notes:
|
||||
* "c3 e3 g3".add("<0 5 7 0>").note()
|
||||
* note("c3 e3 g3".add("<0 5 7 0>"))
|
||||
* // Behind the scenes, the notes are converted to midi numbers:
|
||||
* // "48 52 55".add("<0 5 7 0>").note()
|
||||
* // note("48 52 55".add("<0 5 7 0>"))
|
||||
*/
|
||||
add: [numeralArgs((a, b) => a + b)], // support string concatenation
|
||||
/**
|
||||
@@ -945,7 +945,7 @@ function _composeOp(a, b, func) {
|
||||
* @name sub
|
||||
* @memberof Pattern
|
||||
* @example
|
||||
* "0 2 4".sub("<0 1 2 3>").scale('C4 minor').note()
|
||||
* n("0 2 4".sub("<0 1 2 3>")).scale("C4:minor")
|
||||
* // See add for more information.
|
||||
*/
|
||||
sub: [numeralArgs((a, b) => a - b)],
|
||||
@@ -955,7 +955,7 @@ function _composeOp(a, b, func) {
|
||||
* @name mul
|
||||
* @memberof Pattern
|
||||
* @example
|
||||
* "1 1.5 [1.66, <2 2.33>]".mul(150).freq()
|
||||
* "<1 1.5 [1.66, <2 2.33>]>*4".mul(150).freq()
|
||||
*/
|
||||
mul: [numeralArgs((a, b) => a * b)],
|
||||
/**
|
||||
@@ -1049,9 +1049,9 @@ function _composeOp(a, b, func) {
|
||||
* Applies the given structure to the pattern:
|
||||
*
|
||||
* @example
|
||||
* note("c3,eb3,g3")
|
||||
* note("c,eb,g")
|
||||
* .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~")
|
||||
* .slow(4)
|
||||
* .slow(2)
|
||||
*/
|
||||
Pattern.prototype.struct = function (...args) {
|
||||
return this.keepif.out(...args);
|
||||
@@ -1063,7 +1063,7 @@ function _composeOp(a, b, func) {
|
||||
* Returns silence when mask is 0 or "~"
|
||||
*
|
||||
* @example
|
||||
* note("c [eb,g] d [eb,g]").mask("<1 [0 1]>").slow(2)
|
||||
* note("c [eb,g] d [eb,g]").mask("<1 [0 1]>")
|
||||
*/
|
||||
Pattern.prototype.mask = function (...args) {
|
||||
return this.keepif.in(...args);
|
||||
@@ -1075,7 +1075,7 @@ function _composeOp(a, b, func) {
|
||||
* Resets the pattern to the start of the cycle for each onset of the reset pattern.
|
||||
*
|
||||
* @example
|
||||
* s("<bd lt> sd, hh*4").reset("<x@3 x(3,8)>")
|
||||
* s("[<bd lt> sd]*2, hh*8").reset("<x@3 x(5,8)>")
|
||||
*/
|
||||
Pattern.prototype.reset = function (...args) {
|
||||
return this.keepif.trig(...args);
|
||||
@@ -1088,7 +1088,7 @@ function _composeOp(a, b, func) {
|
||||
* While reset will only reset the current cycle, restart will start from cycle 0.
|
||||
*
|
||||
* @example
|
||||
* s("<bd lt> sd, hh*4").restart("<x@3 x(3,8)>")
|
||||
* s("[<bd lt> sd]*2, hh*8").restart("<x@3 x(5,8)>")
|
||||
*/
|
||||
Pattern.prototype.restart = function (...args) {
|
||||
return this.keepif.trigzero(...args);
|
||||
@@ -1154,8 +1154,8 @@ export function isPattern(thing) {
|
||||
/* if (!thing instanceof Pattern) {
|
||||
console.warn(
|
||||
`Found Pattern that fails "instanceof Pattern" check.
|
||||
This may happen if you are using multiple versions of @strudel.cycles/core.
|
||||
Please check by running "npm ls @strudel.cycles/core".`,
|
||||
This may happen if you are using multiple versions of @strudel/core.
|
||||
Please check by running "npm ls @strudel/core".`,
|
||||
);
|
||||
console.log(thing);
|
||||
} */
|
||||
@@ -1178,7 +1178,8 @@ export function reify(thing) {
|
||||
* @return {Pattern}
|
||||
* @synonyms polyrhythm, pr
|
||||
* @example
|
||||
* stack("g3", "b3", ["e4", "d4"]).note() // "g3,b3,[e4,d4]".note()
|
||||
* stack("g3", "b3", ["e4", "d4"]).note()
|
||||
* // "g3,b3,[e4,d4]".note()
|
||||
*/
|
||||
export function stack(...pats) {
|
||||
// Array test here is to avoid infinite recursions..
|
||||
@@ -1189,7 +1190,7 @@ export function stack(...pats) {
|
||||
|
||||
/** Concatenation: combines a list of patterns, switching between them successively, one per cycle:
|
||||
*
|
||||
* synonyms: {@link cat}
|
||||
* synonyms: `cat`
|
||||
*
|
||||
* @return {Pattern}
|
||||
* @example
|
||||
@@ -1237,17 +1238,19 @@ export function slowcatPrime(...pats) {
|
||||
* @synonyms slowcat
|
||||
* @return {Pattern}
|
||||
* @example
|
||||
* cat("e5", "b4", ["d5", "c5"]).note() // "<e5 b4 [d5 c5]>".note()
|
||||
* cat("e5", "b4", ["d5", "c5"]).note()
|
||||
* // "<e5 b4 [d5 c5]>".note()
|
||||
*
|
||||
*/
|
||||
export function cat(...pats) {
|
||||
return slowcat(...pats);
|
||||
}
|
||||
|
||||
/** Like {@link Pattern.seq}, but each step has a length, relative to the whole.
|
||||
/** Like `seq`, but each step has a length, relative to the whole.
|
||||
* @return {Pattern}
|
||||
* @example
|
||||
* timeCat([3,"e3"],[1, "g3"]).note() // "e3@3 g3".note()
|
||||
* timeCat([3,"e3"],[1, "g3"]).note()
|
||||
* // "e3@3 g3".note()
|
||||
*/
|
||||
export function timeCat(...timepats) {
|
||||
const total = timepats.map((a) => a[0]).reduce((a, b) => a.add(b), Fraction(0));
|
||||
@@ -1267,7 +1270,10 @@ export function timeCat(...timepats) {
|
||||
*
|
||||
* @return {Pattern}
|
||||
* @example
|
||||
* arrange([4, "<c a f e>(3,8)"],[2, "<g a>(5,8)"]).note()
|
||||
* arrange(
|
||||
* [4, "<c a f e>(3,8)"],
|
||||
* [2, "<g a>(5,8)"]
|
||||
* ).note()
|
||||
*/
|
||||
export function arrange(...sections) {
|
||||
const total = sections.reduce((sum, [cycles]) => sum + cycles, 0);
|
||||
@@ -1279,7 +1285,7 @@ export function fastcat(...pats) {
|
||||
return slowcat(...pats)._fast(pats.length);
|
||||
}
|
||||
|
||||
/** See {@link fastcat} */
|
||||
/** See `fastcat` */
|
||||
export function sequence(...pats) {
|
||||
return fastcat(...pats);
|
||||
}
|
||||
@@ -1287,7 +1293,8 @@ export function sequence(...pats) {
|
||||
/** Like **cat**, but the items are crammed into one cycle.
|
||||
* @synonyms fastcat, sequence
|
||||
* @example
|
||||
* seq("e5", "b4", ["d5", "c5"]).note() // "e5 b4 [d5 c5]".note()
|
||||
* seq("e5", "b4", ["d5", "c5"]).note()
|
||||
* // "e5 b4 [d5 c5]".note()
|
||||
*
|
||||
*/
|
||||
export function seq(...pats) {
|
||||
@@ -1313,9 +1320,9 @@ function _sequenceCount(x) {
|
||||
* @param {number} steps how many items are placed in one cycle
|
||||
* @param {any[]} sequences one or more arrays of Patterns / values
|
||||
* @example
|
||||
* polymeterSteps(2, ["c", "d", "e", "f", "g", "f", "e", "d"])
|
||||
* .note().stack(s("bd")) // 1 cycle = 1 bd = 2 notes
|
||||
* // note("{c d e f g f e d}%2").stack(s("bd"))
|
||||
* polymeterSteps(4, ["c", "d", "e"])
|
||||
* .note().stack(s("bd"))
|
||||
* // note("{c d e}%4").stack(s("bd"))
|
||||
*/
|
||||
export function polymeterSteps(steps, ...args) {
|
||||
const seqs = args.map((a) => _sequenceCount(a));
|
||||
@@ -1463,7 +1470,7 @@ export function register(name, func, patternify = true) {
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "0.5 1.5 2.5".round().scale('C major').note()
|
||||
* n("0.5 1.5 2.5".round()).scale("C:major")
|
||||
*/
|
||||
export const round = register('round', function (pat) {
|
||||
return pat.asNumber().fmap((v) => Math.round(v));
|
||||
@@ -1477,7 +1484,7 @@ export const round = register('round', function (pat) {
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "42 42.1 42.5 43".floor().note()
|
||||
* note("42 42.1 42.5 43".floor())
|
||||
*/
|
||||
export const floor = register('floor', function (pat) {
|
||||
return pat.asNumber().fmap((v) => Math.floor(v));
|
||||
@@ -1491,7 +1498,7 @@ export const floor = register('floor', function (pat) {
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "42 42.1 42.5 43".ceil().note()
|
||||
* note("42 42.1 42.5 43".ceil())
|
||||
*/
|
||||
export const ceil = register('ceil', function (pat) {
|
||||
return pat.asNumber().fmap((v) => Math.ceil(v));
|
||||
@@ -1524,7 +1531,8 @@ export const fromBipolar = register('fromBipolar', function (pat) {
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* s("bd sd,hh*4").cutoff(sine.range(500,2000).slow(4))
|
||||
* s("[bd sd]*2,hh*8")
|
||||
* .cutoff(sine.range(500,4000))
|
||||
*/
|
||||
export const range = register('range', function (min, max, pat) {
|
||||
return pat.mul(max - min).add(min);
|
||||
@@ -1538,7 +1546,8 @@ export const range = register('range', function (min, max, pat) {
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* s("bd sd,hh*4").cutoff(sine.rangex(500,2000).slow(4))
|
||||
* s("[bd sd]*2,hh*8")
|
||||
* .cutoff(sine.rangex(500,4000))
|
||||
*/
|
||||
export const rangex = register('rangex', function (min, max, pat) {
|
||||
return pat._range(Math.log(min), Math.log(max)).fmap(Math.exp);
|
||||
@@ -1551,7 +1560,8 @@ export const rangex = register('rangex', function (min, max, pat) {
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* s("bd sd,hh*4").cutoff(sine2.range2(500,2000).slow(4))
|
||||
* s("[bd sd]*2,hh*8")
|
||||
* .cutoff(sine2.range2(500,4000))
|
||||
*/
|
||||
export const range2 = register('range2', function (min, max, pat) {
|
||||
return pat.fromBipolar()._range(min, max);
|
||||
@@ -1564,7 +1574,8 @@ export const range2 = register('range2', function (min, max, pat) {
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* ratio("1, 5:4, 3:2").mul(110).freq().s("piano").slow(2)
|
||||
* ratio("1, 5:4, 3:2").mul(110)
|
||||
* .freq().s("piano")
|
||||
*/
|
||||
export const ratio = register('ratio', (pat) =>
|
||||
pat.fmap((v) => {
|
||||
@@ -1636,7 +1647,7 @@ export const { fastGap, fastgap } = register(['fastGap', 'fastgap'], function (f
|
||||
});
|
||||
|
||||
/**
|
||||
* Similar to compress, but doesn't leave gaps, and the 'focus' can be bigger than a cycle
|
||||
* Similar to `compress`, but doesn't leave gaps, and the 'focus' can be bigger than a cycle
|
||||
* @example
|
||||
* s("bd hh sd hh").focus(1/4, 3/4)
|
||||
*/
|
||||
@@ -1667,7 +1678,7 @@ export const ply = register('ply', function (factor, pat) {
|
||||
* @param {number | Pattern} factor speed up factor
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* s("<bd sd> hh").fast(2) // s("[<bd sd> hh]*2")
|
||||
* s("bd hh sd hh").fast(2) // s("[bd hh sd hh]*2")
|
||||
*/
|
||||
export const { fast, density } = register(['fast', 'density'], function (factor, pat) {
|
||||
if (factor === 0) {
|
||||
@@ -1696,7 +1707,7 @@ export const hurry = register('hurry', function (r, pat) {
|
||||
* @param {number | Pattern} factor slow down factor
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* s("<bd sd> hh").slow(2) // s("[<bd sd> hh]/2")
|
||||
* s("bd hh sd hh").slow(2) // s("[bd hh sd hh]/2")
|
||||
*/
|
||||
export const { slow, sparsity } = register(['slow', 'sparsity'], function (factor, pat) {
|
||||
if (factor === 0) {
|
||||
@@ -1753,7 +1764,7 @@ export const lastOf = register('lastOf', function (n, func, pat) {
|
||||
*/
|
||||
|
||||
/**
|
||||
* An alias for {@link firstOf}
|
||||
* An alias for `firstOf`
|
||||
* @name every
|
||||
* @memberof Pattern
|
||||
* @param {number} n how many cycles
|
||||
@@ -1785,9 +1796,9 @@ export const apply = register('apply', function (func, pat) {
|
||||
* @example
|
||||
* s("<bd sd>,hh*2").cpm(90) // = 90 bpm
|
||||
*/
|
||||
// TODO - global clock
|
||||
// this is redefined in repl.mjs, using the current cps as divisor
|
||||
export const cpm = register('cpm', function (cpm, pat) {
|
||||
return pat._fast(cpm / 60);
|
||||
return pat._fast(cpm / 60 / 1);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -1860,7 +1871,7 @@ export const linger = register('linger', function (t, pat) {
|
||||
* Samples the pattern at a rate of n events per cycle. Useful for turning a continuous pattern into a discrete one.
|
||||
* @param {number} segments number of segments per cycle
|
||||
* @example
|
||||
* note(saw.range(0,12).segment(24)).add(40)
|
||||
* note(saw.range(40,52).segment(24))
|
||||
*/
|
||||
export const segment = register('segment', function (rate, pat) {
|
||||
return pat.struct(pure(true)._fast(rate));
|
||||
@@ -1886,7 +1897,7 @@ export const { invert, inv } = register(['invert', 'inv'], function (pat) {
|
||||
* @param {function} func
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "c3 eb3 g3".when("<0 1>/2", x=>x.sub(5)).note()
|
||||
* "c3 eb3 g3".when("<0 1>/2", x=>x.sub("5")).note()
|
||||
*/
|
||||
export const when = register('when', function (on, func, pat) {
|
||||
return on ? func(pat) : pat;
|
||||
@@ -1923,7 +1934,7 @@ export const brak = register('brak', function (pat) {
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* note("c3 d3 e3 g3").rev()
|
||||
* note("c d e g").rev()
|
||||
*/
|
||||
export const rev = register('rev', function (pat) {
|
||||
const query = function (state) {
|
||||
@@ -1993,7 +2004,7 @@ export const palindrome = register('palindrome', function (pat) {
|
||||
* @name juxBy
|
||||
* @synonyms juxby
|
||||
* @example
|
||||
* s("lt ht mt ht hh").juxBy("<0 .5 1>/2", rev)
|
||||
* s("bd lt [~ ht] mt cp ~ bd hh").juxBy("<0 .5 1>/2", rev)
|
||||
*/
|
||||
export const { juxBy, juxby } = register(['juxBy', 'juxby'], function (by, func, pat) {
|
||||
by /= 2;
|
||||
@@ -2012,7 +2023,11 @@ export const { juxBy, juxby } = register(['juxBy', 'juxby'], function (by, func,
|
||||
/**
|
||||
* The jux function creates strange stereo effects, by applying a function to a pattern, but only in the right-hand channel.
|
||||
* @example
|
||||
* s("lt ht mt ht hh").jux(rev)
|
||||
* s("bd lt [~ ht] mt cp ~ bd hh").jux(rev)
|
||||
* @example
|
||||
* s("bd lt [~ ht] mt cp ~ bd hh").jux(press)
|
||||
* @example
|
||||
* s("bd lt [~ ht] mt cp ~ bd hh").jux(iter(4))
|
||||
*/
|
||||
export const jux = register('jux', function (func, pat) {
|
||||
return pat._juxBy(1, func, pat);
|
||||
@@ -2028,7 +2043,7 @@ export const jux = register('jux', function (func, pat) {
|
||||
* @example
|
||||
* "<0 [2 4]>"
|
||||
* .echoWith(4, 1/8, (p,n) => p.add(n*2))
|
||||
* .scale('C minor').note().clip(.2)
|
||||
* .scale("C:minor").note()
|
||||
*/
|
||||
export const { echoWith, echowith, stutWith, stutwith } = register(
|
||||
['echoWith', 'echowith', 'stutWith', 'stutwith'],
|
||||
@@ -2121,7 +2136,8 @@ const { repeatCycles } = register('repeatCycles', _repeatCycles);
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "0 1 2 3".chunk(4, x=>x.add(7)).scale('A minor').note()
|
||||
* "0 1 2 3".chunk(4, x=>x.add(7))
|
||||
* .scale("A:minor").note()
|
||||
*/
|
||||
const _chunk = function (n, func, pat, back = false, fast = false) {
|
||||
const binary = Array(n - 1).fill(false);
|
||||
@@ -2146,7 +2162,8 @@ const { chunk, slowchunk, slowChunk } = register(['chunk', 'slowchunk', 'slowChu
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "0 1 2 3".chunkBack(4, x=>x.add(7)).scale('A minor').note()
|
||||
* "0 1 2 3".chunkBack(4, x=>x.add(7))
|
||||
* .scale("A:minor").note()
|
||||
*/
|
||||
export const { chunkBack, chunkback } = register(['chunkBack', 'chunkback'], function (n, func, pat) {
|
||||
return _chunk(n, func, pat, true);
|
||||
@@ -2160,10 +2177,11 @@ export const { chunkBack, chunkback } = register(['chunkBack', 'chunkback'], fun
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "<0 8> 1 2 3 4 5 6 7".fastChunk(4, x => x.color('red')).slow(4).scale("C2:major").note()
|
||||
.s("folkharp")
|
||||
* "<0 8> 1 2 3 4 5 6 7"
|
||||
* .fastChunk(4, x => x.color('red')).slow(2)
|
||||
* .scale("C2:major").note()
|
||||
*/
|
||||
const { fastchunk, fastChunk } = register(['fastchunk', 'fastChunk'], function (n, func, pat) {
|
||||
export const { fastchunk, fastChunk } = register(['fastchunk', 'fastChunk'], function (n, func, pat) {
|
||||
return _chunk(n, func, pat, false, true);
|
||||
});
|
||||
|
||||
@@ -2178,6 +2196,8 @@ export const bypass = register('bypass', function (on, pat) {
|
||||
* @param {number} offset start point of loop in cycles
|
||||
* @param {number} cycles loop length in cycles
|
||||
* @example
|
||||
* note("<c d e f>").ribbon(1, 2).fast(2)
|
||||
* @example
|
||||
* // Looping a portion of randomness
|
||||
* note(irand(8).segment(4).scale('C3 minor')).ribbon(1337, 2)
|
||||
*/
|
||||
@@ -2251,7 +2271,7 @@ export const legato = register('legato', function (value, pat) {
|
||||
* s("rhodes")
|
||||
* .chop(4)
|
||||
* .rev() // reverse order of chops
|
||||
* .loopAt(4) // fit sample into 4 cycles
|
||||
* .loopAt(2) // fit sample into 2 cycles
|
||||
*
|
||||
*/
|
||||
export const chop = register('chop', function (n, pat) {
|
||||
@@ -2269,7 +2289,7 @@ export const chop = register('chop', function (n, pat) {
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* s("numbers:0 numbers:1 numbers:2").striate(6).slow(6)
|
||||
* s("numbers:0 numbers:1 numbers:2").striate(6).slow(3)
|
||||
*/
|
||||
export const striate = register('striate', function (n, pat) {
|
||||
const slices = Array.from({ length: n }, (x, i) => i);
|
||||
@@ -2285,10 +2305,10 @@ export const striate = register('striate', function (n, pat) {
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' })
|
||||
* s("rhodes").loopAt(4)
|
||||
* s("rhodes").loopAt(2)
|
||||
*/
|
||||
// TODO - global cps clock
|
||||
const _loopAt = function (factor, pat, cps = 1) {
|
||||
const _loopAt = function (factor, pat, cps = 0.5) {
|
||||
return pat
|
||||
.speed((1 / factor) * cps)
|
||||
.unit('c')
|
||||
@@ -2303,10 +2323,10 @@ const _loopAt = function (factor, pat, cps = 1) {
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
* s("breaks165").slice(8, "0 1 <2 2*2> 3 [4 0] 5 6 7".every(3, rev)).slow(1.5)
|
||||
* s("breaks165").slice(8, "0 1 <2 2*2> 3 [4 0] 5 6 7".every(3, rev)).slow(0.75)
|
||||
* @example
|
||||
* await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
* s("breaks125/2").fit().slice([0,.25,.5,.75], "0 1 1 <2 3>")
|
||||
* s("breaks125").fit().slice([0,.25,.5,.75], "0 1 1 <2 3>")
|
||||
*/
|
||||
|
||||
export const slice = register(
|
||||
@@ -2334,50 +2354,51 @@ export const slice = register(
|
||||
* await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
* s("breaks165")
|
||||
* .splice(8, "0 1 [2 3 0]@2 3 0@2 7")
|
||||
* .hurry(0.65)
|
||||
*/
|
||||
|
||||
export const splice = register(
|
||||
'splice',
|
||||
function (npat, ipat, opat) {
|
||||
const sliced = slice(npat, ipat, opat);
|
||||
return sliced.withHap(function (hap) {
|
||||
return hap.withValue((v) => ({
|
||||
...{
|
||||
speed: (1 / v._slices / hap.whole.duration) * (v.speed || 1),
|
||||
unit: 'c',
|
||||
},
|
||||
...v,
|
||||
}));
|
||||
return new Pattern((state) => {
|
||||
// TODO - default cps to 0.5
|
||||
const cps = state.controls._cps || 1;
|
||||
const haps = sliced.query(state);
|
||||
return haps.map((hap) =>
|
||||
hap.withValue((v) => ({
|
||||
...{
|
||||
speed: (cps / v._slices / hap.whole.duration) * (v.speed || 1),
|
||||
unit: 'c',
|
||||
},
|
||||
...v,
|
||||
})),
|
||||
);
|
||||
});
|
||||
},
|
||||
false, // turns off auto-patternification
|
||||
);
|
||||
|
||||
// this function will be redefined in repl.mjs to use the correct cps value.
|
||||
// It is still here to work in cases where repl.mjs is not used
|
||||
|
||||
export const { loopAt, loopat } = register(['loopAt', 'loopat'], function (factor, pat) {
|
||||
return _loopAt(factor, pat, 1);
|
||||
return new Pattern((state) => _loopAt(factor, pat, state.controls._cps).query(state));
|
||||
});
|
||||
|
||||
// the fit function will be redefined in repl.mjs to use the correct cps value.
|
||||
// It is still here to work in cases where repl.mjs is not used
|
||||
/**
|
||||
* Makes the sample fit its event duration. Good for rhythmical loops like drum breaks.
|
||||
* Similar to loopAt.
|
||||
* Similar to `loopAt`.
|
||||
* @name fit
|
||||
* @example
|
||||
* samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' })
|
||||
* s("rhodes/4").fit()
|
||||
* s("rhodes/2").fit()
|
||||
*/
|
||||
export const fit = register('fit', (pat) =>
|
||||
pat.withHap((hap) =>
|
||||
hap.withValue((v) => ({
|
||||
...v,
|
||||
speed: 1 / hap.whole.duration,
|
||||
unit: 'c',
|
||||
})),
|
||||
pat.withHaps((haps, state) =>
|
||||
haps.map((hap) =>
|
||||
hap.withValue((v) => ({
|
||||
...v,
|
||||
speed: (state.controls._cps || 1) / hap.whole.duration,
|
||||
unit: 'c',
|
||||
})),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -160,8 +160,13 @@ export function pianoroll({
|
||||
maxMidi = max;
|
||||
valueExtent = maxMidi - minMidi + 1;
|
||||
}
|
||||
// foldValues = values.sort((a, b) => a - b);
|
||||
foldValues = values.sort((a, b) => String(a).localeCompare(String(b)));
|
||||
foldValues = values.sort((a, b) =>
|
||||
typeof a === 'number' && typeof b === 'number'
|
||||
? a - b
|
||||
: typeof a === 'number'
|
||||
? 1
|
||||
: String(a).localeCompare(String(b)),
|
||||
);
|
||||
barThickness = fold ? valueAxis / foldValues.length : valueAxis / valueExtent;
|
||||
ctx.fillStyle = background;
|
||||
ctx.globalAlpha = 1; // reset!
|
||||
|
||||
+55
-63
@@ -61,12 +61,67 @@ export function repl({
|
||||
scheduler.setPattern(pattern, autostart);
|
||||
};
|
||||
setTime(() => scheduler.now()); // TODO: refactor?
|
||||
|
||||
const stop = () => scheduler.stop();
|
||||
const start = () => scheduler.start();
|
||||
const pause = () => scheduler.pause();
|
||||
const toggle = () => scheduler.toggle();
|
||||
const setCps = (cps) => scheduler.setCps(cps);
|
||||
const setCpm = (cpm) => scheduler.setCps(cpm / 60);
|
||||
const all = function (transform) {
|
||||
allTransform = transform;
|
||||
return silence;
|
||||
};
|
||||
|
||||
// set pattern methods that use this repl via closure
|
||||
const injectPatternMethods = () => {
|
||||
Pattern.prototype.p = function (id) {
|
||||
pPatterns[id] = this;
|
||||
return this;
|
||||
};
|
||||
Pattern.prototype.q = function (id) {
|
||||
return silence;
|
||||
};
|
||||
try {
|
||||
for (let i = 1; i < 10; ++i) {
|
||||
Object.defineProperty(Pattern.prototype, `d${i}`, {
|
||||
get() {
|
||||
return this.p(i);
|
||||
},
|
||||
configurable: true,
|
||||
});
|
||||
Object.defineProperty(Pattern.prototype, `p${i}`, {
|
||||
get() {
|
||||
return this.p(i);
|
||||
},
|
||||
configurable: true,
|
||||
});
|
||||
Pattern.prototype[`q${i}`] = silence;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('injectPatternMethods: error:', err);
|
||||
}
|
||||
const cpm = register('cpm', function (cpm, pat) {
|
||||
return pat._fast(cpm / 60 / scheduler.cps);
|
||||
});
|
||||
evalScope({
|
||||
all,
|
||||
hush,
|
||||
cpm,
|
||||
setCps,
|
||||
setcps: setCps,
|
||||
setCpm,
|
||||
setcpm: setCpm,
|
||||
});
|
||||
};
|
||||
|
||||
const evaluate = async (code, autostart = true, shouldHush = true) => {
|
||||
if (!code) {
|
||||
throw new Error('no code to evaluate');
|
||||
}
|
||||
try {
|
||||
updateState({ code, pending: true });
|
||||
injectPatternMethods();
|
||||
await beforeEval?.({ code });
|
||||
shouldHush && hush();
|
||||
let { pattern, meta } = await _evaluate(code, transpiler);
|
||||
@@ -94,74 +149,11 @@ export function repl({
|
||||
afterEval?.({ code, pattern, meta });
|
||||
return pattern;
|
||||
} catch (err) {
|
||||
// console.warn(`[repl] eval error: ${err.message}`);
|
||||
logger(`[eval] error: ${err.message}`, 'error');
|
||||
updateState({ evalError: err, pending: false });
|
||||
onEvalError?.(err);
|
||||
}
|
||||
};
|
||||
const stop = () => scheduler.stop();
|
||||
const start = () => scheduler.start();
|
||||
const pause = () => scheduler.pause();
|
||||
const toggle = () => scheduler.toggle();
|
||||
const setCps = (cps) => scheduler.setCps(cps);
|
||||
const setCpm = (cpm) => scheduler.setCps(cpm / 60);
|
||||
|
||||
// the following functions use the cps value, which is why they are defined here..
|
||||
const loopAt = register('loopAt', (cycles, pat) => {
|
||||
return pat.loopAtCps(cycles, scheduler.cps);
|
||||
});
|
||||
|
||||
Pattern.prototype.p = function (id) {
|
||||
pPatterns[id] = this;
|
||||
return this;
|
||||
};
|
||||
Pattern.prototype.q = function (id) {
|
||||
return silence;
|
||||
};
|
||||
|
||||
const all = function (transform) {
|
||||
allTransform = transform;
|
||||
return silence;
|
||||
};
|
||||
try {
|
||||
for (let i = 1; i < 10; ++i) {
|
||||
Object.defineProperty(Pattern.prototype, `d${i}`, {
|
||||
get() {
|
||||
return this.p(i);
|
||||
},
|
||||
});
|
||||
Object.defineProperty(Pattern.prototype, `p${i}`, {
|
||||
get() {
|
||||
return this.p(i);
|
||||
},
|
||||
});
|
||||
Pattern.prototype[`q${i}`] = silence;
|
||||
}
|
||||
} catch (err) {
|
||||
// already defined..
|
||||
}
|
||||
|
||||
const fit = register('fit', (pat) =>
|
||||
pat.withHap((hap) =>
|
||||
hap.withValue((v) => ({
|
||||
...v,
|
||||
speed: scheduler.cps / hap.whole.duration, // overwrite speed completely?
|
||||
unit: 'c',
|
||||
})),
|
||||
),
|
||||
);
|
||||
|
||||
evalScope({
|
||||
loopAt,
|
||||
fit,
|
||||
all,
|
||||
hush,
|
||||
setCps,
|
||||
setcps: setCps,
|
||||
setCpm,
|
||||
setcpm: setCpm,
|
||||
});
|
||||
const setCode = (code) => updateState({ code });
|
||||
return { scheduler, evaluate, start, stop, pause, setCps, setPattern, setCode, toggle, state };
|
||||
}
|
||||
|
||||
+131
-36
@@ -7,7 +7,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
import { Hap } from './hap.mjs';
|
||||
import { Pattern, fastcat, reify, silence, stack, register } from './pattern.mjs';
|
||||
import Fraction from './fraction.mjs';
|
||||
import { id, _mod, clamp } from './util.mjs';
|
||||
import { id, _mod, clamp, objectMap } from './util.mjs';
|
||||
|
||||
export function steady(value) {
|
||||
// A continuous value
|
||||
@@ -27,9 +27,11 @@ export const isaw2 = isaw.toBipolar();
|
||||
*
|
||||
* @return {Pattern}
|
||||
* @example
|
||||
* "c3 [eb3,g3] g2 [g3,bb3]".note().clip(saw.slow(4))
|
||||
* note("<c3 [eb3,g3] g2 [g3,bb3]>*8")
|
||||
* .clip(saw.slow(2))
|
||||
* @example
|
||||
* saw.range(0,8).segment(8).scale('C major').slow(4).note()
|
||||
* n(saw.range(0,8).segment(8))
|
||||
* .scale('C major')
|
||||
*
|
||||
*/
|
||||
export const saw = signal((t) => t % 1);
|
||||
@@ -42,7 +44,8 @@ export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t));
|
||||
*
|
||||
* @return {Pattern}
|
||||
* @example
|
||||
* sine.segment(16).range(0,15).slow(2).scale('C minor').note()
|
||||
* n(sine.segment(16).range(0,15))
|
||||
* .scale("C:minor")
|
||||
*
|
||||
*/
|
||||
export const sine = sine2.fromBipolar();
|
||||
@@ -52,7 +55,8 @@ export const sine = sine2.fromBipolar();
|
||||
*
|
||||
* @return {Pattern}
|
||||
* @example
|
||||
* stack(sine,cosine).segment(16).range(0,15).slow(2).scale('C minor').note()
|
||||
* n(stack(sine,cosine).segment(16).range(0,15))
|
||||
* .scale("C:minor")
|
||||
*
|
||||
*/
|
||||
export const cosine = sine._early(Fraction(1).div(4));
|
||||
@@ -63,7 +67,7 @@ export const cosine2 = sine2._early(Fraction(1).div(4));
|
||||
*
|
||||
* @return {Pattern}
|
||||
* @example
|
||||
* square.segment(2).range(0,7).scale('C minor').note()
|
||||
* n(square.segment(4).range(0,7)).scale("C:minor")
|
||||
*
|
||||
*/
|
||||
export const square = signal((t) => Math.floor((t * 2) % 2));
|
||||
@@ -74,7 +78,7 @@ export const square2 = square.toBipolar();
|
||||
*
|
||||
* @return {Pattern}
|
||||
* @example
|
||||
* tri.segment(8).range(0,7).scale('C minor').note()
|
||||
* n(tri.segment(8).range(0,7)).scale("C:minor")
|
||||
*
|
||||
*/
|
||||
export const tri = fastcat(isaw, saw);
|
||||
@@ -118,8 +122,8 @@ const timeToRands = (t, n) => timeToRandsPrime(timeToIntSeed(t), n);
|
||||
/**
|
||||
* A discrete pattern of numbers from 0 to n-1
|
||||
* @example
|
||||
* run(4).scale('C4 major').note()
|
||||
* // "0 1 2 3".scale('C4 major').note()
|
||||
* n(run(4)).scale("C4:pentatonic")
|
||||
* // n("0 1 2 3").scale("C4:pentatonic")
|
||||
*/
|
||||
export const run = (n) => saw.range(0, n).floor().segment(n);
|
||||
|
||||
@@ -129,7 +133,7 @@ export const run = (n) => saw.range(0, n).floor().segment(n);
|
||||
* @name rand
|
||||
* @example
|
||||
* // randomly change the cutoff
|
||||
* s("bd sd,hh*4").cutoff(rand.range(500,2000))
|
||||
* s("bd*4,hh*8").cutoff(rand.range(500,8000))
|
||||
*
|
||||
*/
|
||||
export const rand = signal(timeToRand);
|
||||
@@ -151,36 +155,127 @@ export const _irand = (i) => rand.fmap((x) => Math.trunc(x * i));
|
||||
* @param {number} n max value (exclusive)
|
||||
* @example
|
||||
* // randomly select scale notes from 0 - 7 (= C to C)
|
||||
* irand(8).struct("x(3,8)").scale('C minor').note()
|
||||
* n(irand(8)).struct("x x*2 x x*3").scale("C:minor")
|
||||
*
|
||||
*/
|
||||
export const irand = (ipat) => reify(ipat).fmap(_irand).innerJoin();
|
||||
|
||||
/**
|
||||
* pick from the list of values (or patterns of values) via the index using the given
|
||||
* pattern of integers
|
||||
const _pick = function (lookup, pat, modulo = true) {
|
||||
const array = Array.isArray(lookup);
|
||||
const len = Object.keys(lookup).length;
|
||||
|
||||
lookup = objectMap(lookup, reify);
|
||||
|
||||
if (len === 0) {
|
||||
return silence;
|
||||
}
|
||||
return pat.fmap((i) => {
|
||||
let key = i;
|
||||
if (array) {
|
||||
key = modulo ? Math.round(key) % len : clamp(Math.round(key), 0, lookup.length - 1);
|
||||
}
|
||||
return lookup[key];
|
||||
});
|
||||
};
|
||||
|
||||
/** * Picks patterns (or plain values) either from a list (by index) or a lookup table (by name).
|
||||
* Similar to `inhabit`, but maintains the structure of the original patterns.
|
||||
* @param {Pattern} pat
|
||||
* @param {*} xs
|
||||
* @returns {Pattern}
|
||||
* @example
|
||||
* note(pick("<0 1 [2!2] 3>", ["g a", "e f", "f g f g" , "g a c d"]))
|
||||
* note("<0 1 2!2 3>".pick(["g a", "e f", "f g f g" , "g c d"]))
|
||||
* @example
|
||||
* sound("<0 1 [2,0]>".pick(["bd sd", "cp cp", "hh hh"]))
|
||||
* @example
|
||||
* sound("<0!2 [0,1] 1>".pick(["bd(3,8)", "sd sd"]))
|
||||
* @example
|
||||
* s("<a!2 [a,b] b>".pick({a: "bd(3,8)", b: "sd sd"}))
|
||||
*/
|
||||
|
||||
export const pick = (pat, xs) => {
|
||||
xs = xs.map(reify);
|
||||
if (xs.length == 0) {
|
||||
return silence;
|
||||
export const pick = function (lookup, pat) {
|
||||
// backward compatibility - the args used to be flipped
|
||||
if (Array.isArray(pat)) {
|
||||
[pat, lookup] = [lookup, pat];
|
||||
}
|
||||
return pat
|
||||
.fmap((i) => {
|
||||
const key = clamp(Math.round(i), 0, xs.length - 1);
|
||||
return xs[key];
|
||||
})
|
||||
.innerJoin();
|
||||
return __pick(lookup, pat);
|
||||
};
|
||||
|
||||
const __pick = register('pick', function (lookup, pat) {
|
||||
return _pick(lookup, pat, false).innerJoin();
|
||||
});
|
||||
|
||||
/** * The same as `pick`, but if you pick a number greater than the size of the list,
|
||||
* it wraps around, rather than sticking at the maximum value.
|
||||
* For example, if you pick the fifth pattern of a list of three, you'll get the
|
||||
* second one.
|
||||
* @param {Pattern} pat
|
||||
* @param {*} xs
|
||||
* @returns {Pattern}
|
||||
*/
|
||||
|
||||
export const pickmod = register('pickmod', function (lookup, pat) {
|
||||
return _pick(lookup, pat, true).innerJoin();
|
||||
});
|
||||
|
||||
/** * pickF lets you use a pattern of numbers to pick which function to apply to another pattern.
|
||||
* @param {Pattern} pat
|
||||
* @param {Pattern} lookup a pattern of indices
|
||||
* @param {function[]} funcs the array of functions from which to pull
|
||||
* @returns {Pattern}
|
||||
* @example
|
||||
* s("bd [rim hh]").pickF("<0 1 2>", [rev,jux(rev),fast(2)])
|
||||
* @example
|
||||
* note("<c2 d2>(3,8)").s("square")
|
||||
* .pickF("<0 2> 1", [jux(rev),fast(2),x=>x.lpf(800)])
|
||||
*/
|
||||
export const pickF = register('pickF', function (lookup, funcs, pat) {
|
||||
return pat.apply(pick(lookup, funcs));
|
||||
});
|
||||
|
||||
/** * The same as `pickF`, but if you pick a number greater than the size of the functions list,
|
||||
* it wraps around, rather than sticking at the maximum value.
|
||||
* @param {Pattern} pat
|
||||
* @param {Pattern} lookup a pattern of indices
|
||||
* @param {function[]} funcs the array of functions from which to pull
|
||||
* @returns {Pattern}
|
||||
*/
|
||||
export const pickmodF = register('pickmodF', function (lookup, funcs, pat) {
|
||||
return pat.apply(pickmod(lookup, funcs));
|
||||
});
|
||||
|
||||
/**
|
||||
* pick from the list of values (or patterns of values) via the index using the given
|
||||
/** * Picks patterns (or plain values) either from a list (by index) or a lookup table (by name).
|
||||
* Similar to `pick`, but cycles are squeezed into the target ('inhabited') pattern.
|
||||
* @param {Pattern} pat
|
||||
* @param {*} xs
|
||||
* @returns {Pattern}
|
||||
* @example
|
||||
* "<a b [a,b]>".inhabit({a: s("bd(3,8)"),
|
||||
b: s("cp sd")
|
||||
})
|
||||
* @example
|
||||
* s("a@2 [a b] a".inhabit({a: "bd(3,8)", b: "sd sd"})).slow(4)
|
||||
*/
|
||||
export const inhabit = register('inhabit', function (lookup, pat) {
|
||||
return _pick(lookup, pat, true).squeezeJoin();
|
||||
});
|
||||
|
||||
/** * The same as `inhabit`, but if you pick a number greater than the size of the list,
|
||||
* it wraps around, rather than sticking at the maximum value.
|
||||
* For example, if you pick the fifth pattern of a list of three, you'll get the
|
||||
* second one.
|
||||
* @param {Pattern} pat
|
||||
* @param {*} xs
|
||||
* @returns {Pattern}
|
||||
*/
|
||||
|
||||
export const inhabitmod = register('inhabit', function (lookup, pat) {
|
||||
return _pick(lookup, pat, false).squeezeJoin();
|
||||
});
|
||||
|
||||
/**
|
||||
* Pick from the list of values (or patterns of values) via the index using the given
|
||||
* pattern of integers. The selected pattern will be compressed to fit the duration of the selecting event
|
||||
* @param {Pattern} pat
|
||||
* @param {*} xs
|
||||
@@ -269,9 +364,9 @@ Pattern.prototype.choose2 = function (...xs) {
|
||||
* Picks one of the elements at random each cycle.
|
||||
* @returns {Pattern}
|
||||
* @example
|
||||
* chooseCycles("bd", "hh", "sd").s().fast(4)
|
||||
* chooseCycles("bd", "hh", "sd").s().fast(8)
|
||||
* @example
|
||||
* "bd | hh | sd".s().fast(4)
|
||||
* s("bd | hh | sd").fast(8)
|
||||
*/
|
||||
export const chooseCycles = (...xs) => chooseInWith(rand.segment(1), xs);
|
||||
|
||||
@@ -314,7 +409,7 @@ export const perlinWith = (pat) => {
|
||||
* @name perlin
|
||||
* @example
|
||||
* // randomly change the cutoff
|
||||
* s("bd sd,hh*4").cutoff(perlin.range(500,2000))
|
||||
* s("bd*4,hh*8").cutoff(perlin.range(500,8000))
|
||||
*
|
||||
*/
|
||||
export const perlin = perlinWith(time.fmap((v) => Number(v)));
|
||||
@@ -356,7 +451,7 @@ export const degradeBy = register('degradeBy', function (x, pat) {
|
||||
export const degrade = register('degrade', (pat) => pat._degradeBy(0.5));
|
||||
|
||||
/**
|
||||
* Inverse of {@link Pattern#degradeBy}: Randomly removes events from the pattern by a given amount.
|
||||
* Inverse of `degradeBy`: Randomly removes events from the pattern by a given amount.
|
||||
* 0 = 100% chance of removal
|
||||
* 1 = 0% chance of removal
|
||||
* Events that would be removed by degradeBy are let through by undegradeBy and vice versa (see second example).
|
||||
@@ -380,7 +475,7 @@ export const undegrade = register('undegrade', (pat) => pat._undegradeBy(0.5));
|
||||
/**
|
||||
*
|
||||
* Randomly applies the given function by the given probability.
|
||||
* Similar to {@link Pattern#someCyclesBy}
|
||||
* Similar to `someCyclesBy`
|
||||
*
|
||||
* @name sometimesBy
|
||||
* @memberof Pattern
|
||||
@@ -388,7 +483,7 @@ export const undegrade = register('undegrade', (pat) => pat._undegradeBy(0.5));
|
||||
* @param {function} function - the transformation to apply
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* s("hh(3,8)").sometimesBy(.4, x=>x.speed("0.5"))
|
||||
* s("hh*8").sometimesBy(.4, x=>x.speed("0.5"))
|
||||
*/
|
||||
|
||||
export const sometimesBy = register('sometimesBy', function (patx, func, pat) {
|
||||
@@ -406,7 +501,7 @@ export const sometimesBy = register('sometimesBy', function (patx, func, pat) {
|
||||
* @param {function} function - the transformation to apply
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* s("hh*4").sometimes(x=>x.speed("0.5"))
|
||||
* s("hh*8").sometimes(x=>x.speed("0.5"))
|
||||
*/
|
||||
export const sometimes = register('sometimes', function (func, pat) {
|
||||
return pat._sometimesBy(0.5, func);
|
||||
@@ -415,7 +510,7 @@ export const sometimes = register('sometimes', function (func, pat) {
|
||||
/**
|
||||
*
|
||||
* Randomly applies the given function by the given probability on a cycle by cycle basis.
|
||||
* Similar to {@link Pattern#sometimesBy}
|
||||
* Similar to `sometimesBy`
|
||||
*
|
||||
* @name someCyclesBy
|
||||
* @memberof Pattern
|
||||
@@ -423,7 +518,7 @@ export const sometimes = register('sometimes', function (func, pat) {
|
||||
* @param {function} function - the transformation to apply
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* s("hh(3,8)").someCyclesBy(.3, x=>x.speed("0.5"))
|
||||
* s("bd,hh*8").someCyclesBy(.3, x=>x.speed("0.5"))
|
||||
*/
|
||||
|
||||
export const someCyclesBy = register('someCyclesBy', function (patx, func, pat) {
|
||||
@@ -445,7 +540,7 @@ export const someCyclesBy = register('someCyclesBy', function (patx, func, pat)
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* s("hh(3,8)").someCycles(x=>x.speed("0.5"))
|
||||
* s("bd,hh*8").someCycles(x=>x.speed("0.5"))
|
||||
*/
|
||||
export const someCycles = register('someCycles', function (func, pat) {
|
||||
return pat._someCyclesBy(0.5, func);
|
||||
|
||||
@@ -46,6 +46,7 @@ import {
|
||||
rev,
|
||||
time,
|
||||
run,
|
||||
pick,
|
||||
} from '../index.mjs';
|
||||
|
||||
import { steady } from '../signal.mjs';
|
||||
@@ -1057,4 +1058,63 @@ describe('Pattern', () => {
|
||||
expect(slowcat(0, 1).repeatCycles(2).fast(6).firstCycleValues).toStrictEqual([0, 0, 1, 1, 0, 0]);
|
||||
});
|
||||
});
|
||||
describe('inhabit', () => {
|
||||
it('Can pattern named patterns', () => {
|
||||
expect(
|
||||
sameFirst(
|
||||
sequence('a', 'b', stack('a', 'b')).inhabit({ a: sequence(1, 2), b: sequence(10, 20, 30) }),
|
||||
sequence([1, 2], [10, 20, 30], stack([1, 2], [10, 20, 30])),
|
||||
),
|
||||
);
|
||||
});
|
||||
it('Can pattern indexed patterns', () => {
|
||||
expect(
|
||||
sameFirst(
|
||||
sequence('0', '1', stack('0', '1')).inhabit([sequence(1, 2), sequence(10, 20, 30)]),
|
||||
sequence([1, 2], [10, 20, 30], stack([1, 2], [10, 20, 30])),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('pick', () => {
|
||||
it('Can pattern named patterns', () => {
|
||||
expect(
|
||||
sameFirst(
|
||||
sequence('a', 'b', 'a', stack('a', 'b')).pick({ a: sequence(1, 2, 3, 4), b: sequence(10, 20, 30, 40) }),
|
||||
sequence(1, 20, 3, stack(4, 40)),
|
||||
),
|
||||
);
|
||||
});
|
||||
it('Can pattern indexed patterns', () => {
|
||||
expect(
|
||||
sameFirst(
|
||||
sequence(0, 1, 0, stack(0, 1)).pick([sequence(1, 2, 3, 4), sequence(10, 20, 30, 40)]),
|
||||
sequence(1, 20, 3, stack(4, 40)),
|
||||
),
|
||||
);
|
||||
});
|
||||
it('Clamps indexes', () => {
|
||||
expect(
|
||||
sameFirst(sequence(0, 1, 2, 3).pick([sequence(1, 2, 3, 4), sequence(10, 20, 30, 40)]), sequence(1, 20, 30, 40)),
|
||||
);
|
||||
});
|
||||
it('Is backwards compatible', () => {
|
||||
expect(
|
||||
sameFirst(
|
||||
pick([sequence('a', 'b'), sequence('c', 'd')], sequence(0, 1)),
|
||||
pick(sequence(0, 1), [sequence('a', 'b'), sequence('c', 'd')]),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('pickmod', () => {
|
||||
it('Wraps indexes', () => {
|
||||
expect(
|
||||
sameFirst(
|
||||
sequence(0, 1, 2, 3).pickmod([sequence(1, 2, 3, 4), sequence(10, 20, 30, 40)]),
|
||||
sequence(1, 20, 3, 40),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -86,7 +86,7 @@ export const midi2note = (n) => {
|
||||
// modulo that works with negative numbers e.g. _mod(-1, 3) = 2. Works on numbers (rather than patterns of numbers, as @mod@ from pattern.mjs does)
|
||||
export const _mod = (n, m) => ((n % m) + m) % m;
|
||||
|
||||
export function nanFallback(value, fallback) {
|
||||
export function nanFallback(value, fallback = 0) {
|
||||
if (isNaN(Number(value))) {
|
||||
logger(`"${value}" is not a number, falling back to ${fallback}`, 'warning');
|
||||
return fallback;
|
||||
@@ -316,3 +316,10 @@ export function hash2code(hash) {
|
||||
return base64ToUnicode(decodeURIComponent(hash));
|
||||
//return atob(decodeURIComponent(codeParam || ''));
|
||||
}
|
||||
|
||||
export function objectMap(obj, fn) {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(fn);
|
||||
}
|
||||
return Object.fromEntries(Object.entries(obj).map(([k, v], i) => [k, fn(v, k, i)]));
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# @strudel/csound
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getFrequency, logger, register } from '@strudel.cycles/core';
|
||||
import { getAudioContext } from '@strudel.cycles/webaudio';
|
||||
import { getFrequency, logger, register } from '@strudel/core';
|
||||
import { getAudioContext } from '@strudel/webaudio';
|
||||
import csd from './project.csd?raw';
|
||||
// import livecodeOrc from './livecode.orc?raw';
|
||||
import presetsOrc from './presets.orc?raw';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/csound",
|
||||
"version": "0.9.0",
|
||||
"name": "@strudel/csound",
|
||||
"version": "1.0.0",
|
||||
"description": "csound bindings for strudel",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
@@ -33,8 +33,8 @@
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@csound/browser": "6.18.7",
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel.cycles/webaudio": "workspace:*"
|
||||
"@strudel/core": "workspace:*",
|
||||
"@strudel/webaudio": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^5.0.10"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Invoke } from './utils.mjs';
|
||||
import { Pattern, noteToMidi } from '@strudel.cycles/core';
|
||||
import { Pattern, noteToMidi } from '@strudel/core';
|
||||
|
||||
const ON_MESSAGE = 0x90;
|
||||
const OFF_MESSAGE = 0x80;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { parseNumeral, Pattern } from '@strudel.cycles/core';
|
||||
import { parseNumeral, Pattern } from '@strudel/core';
|
||||
import { Invoke } from './utils.mjs';
|
||||
|
||||
Pattern.prototype.osc = function () {
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"url": "https://github.com/tidalcycles/strudel/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel/core": "workspace:*",
|
||||
"@tauri-apps/api": "^1.5.3"
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme"
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# @strudel.cycles/embed
|
||||
# @strudel/embed
|
||||
|
||||
This package contains a embeddable web component for the Strudel REPL.
|
||||
|
||||
## Usage
|
||||
|
||||
Either install with `npm i @strudel.cycles/embed` or just use a cdn to import the script:
|
||||
Either install with `npm i @strudel/embed` or just use a cdn to import the script:
|
||||
|
||||
```html
|
||||
<script src="https://unpkg.com/@strudel.cycles/embed@latest"></script>
|
||||
<script src="https://unpkg.com/@strudel/embed@latest"></script>
|
||||
<strudel-repl>
|
||||
<!--
|
||||
note(`[[e5 [b4 c5] d5 [c5 b4]]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/embed",
|
||||
"version": "0.2.0",
|
||||
"name": "@strudel/embed",
|
||||
"version": "1.0.0",
|
||||
"description": "Embeddable Web Component to load a Strudel REPL into an iframe",
|
||||
"main": "embed.js",
|
||||
"type": "module",
|
||||
|
||||
@@ -27,7 +27,7 @@ npm i @strudel/hydra
|
||||
Then add the import to your evalScope:
|
||||
|
||||
```js
|
||||
import { evalScope } from '@strudel.cycles/core';
|
||||
import { evalScope } from '@strudel/core';
|
||||
|
||||
evalScope(
|
||||
import('@strudel/hydra')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getDrawContext } from '@strudel.cycles/core';
|
||||
import { getDrawContext } from '@strudel/core';
|
||||
|
||||
let latestOptions;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel/hydra",
|
||||
"version": "0.9.0",
|
||||
"version": "1.0.0",
|
||||
"description": "Hydra integration for strudel",
|
||||
"main": "hydra.mjs",
|
||||
"publishConfig": {
|
||||
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel/core": "workspace:*",
|
||||
"hydra-synth": "^1.3.29"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# @strudel.cycles/midi
|
||||
# @strudel/midi
|
||||
|
||||
This package adds midi functionality to strudel Patterns.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm i @strudel.cycles/midi --save
|
||||
npm i @strudel/midi --save
|
||||
```
|
||||
|
||||
@@ -5,8 +5,8 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
*/
|
||||
|
||||
import * as _WebMidi from 'webmidi';
|
||||
import { Pattern, isPattern, logger, ref } from '@strudel.cycles/core';
|
||||
import { noteToMidi } from '@strudel.cycles/core';
|
||||
import { Pattern, isPattern, logger, ref } from '@strudel/core';
|
||||
import { noteToMidi } from '@strudel/core';
|
||||
import { Note } from 'webmidi';
|
||||
// if you use WebMidi from outside of this package, make sure to import that instance:
|
||||
export const { WebMidi } = _WebMidi;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/midi",
|
||||
"version": "0.9.0",
|
||||
"name": "@strudel/midi",
|
||||
"version": "1.0.0",
|
||||
"description": "Midi API for strudel",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
@@ -29,8 +29,8 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel.cycles/webaudio": "workspace:*",
|
||||
"@strudel/core": "workspace:*",
|
||||
"@strudel/webaudio": "workspace:*",
|
||||
"webmidi": "^3.1.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# @strudel.cycles/mini
|
||||
# @strudel/mini
|
||||
|
||||
This package contains the mini notation parser and pattern generator.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm i @strudel.cycles/mini --save
|
||||
npm i @strudel/mini --save
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
import { mini } from '@strudel.cycles/mini';
|
||||
import { mini } from '@strudel/mini';
|
||||
|
||||
const pattern = mini('a [b c*2]');
|
||||
|
||||
@@ -28,7 +28,7 @@ yields:
|
||||
(7/8 -> 1/1, 7/8 -> 1/1, c)
|
||||
```
|
||||
|
||||
[Play with @strudel.cycles/mini codesandbox](https://codesandbox.io/s/strudel-mini-example-oe9wcu?file=/src/index.js)
|
||||
[Play with @strudel/mini codesandbox](https://codesandbox.io/s/strudel-mini-example-oe9wcu?file=/src/index.js)
|
||||
|
||||
## Mini Notation API
|
||||
|
||||
|
||||
+194
-97
@@ -288,48 +288,50 @@ function peg$parse(input, options) {
|
||||
|
||||
var peg$f0 = function() { return parseFloat(text()); };
|
||||
var peg$f1 = function() { return parseInt(text()); };
|
||||
var peg$f2 = function(chars) { return new AtomStub(chars.join("")) };
|
||||
var peg$f3 = function(s) { return s };
|
||||
var peg$f4 = function(s, stepsPerCycle) { s.arguments_.stepsPerCycle = stepsPerCycle ; return s; };
|
||||
var peg$f5 = function(a) { return a };
|
||||
var peg$f6 = function(s) { s.arguments_.alignment = 'slowcat'; return s; };
|
||||
var peg$f7 = function(a) { return x => x.options_['weight'] = a };
|
||||
var peg$f8 = function(a) { return x => x.options_['reps'] = a };
|
||||
var peg$f9 = function(p, s, r) { return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) };
|
||||
var peg$f10 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'slow' }}) };
|
||||
var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) };
|
||||
var peg$f12 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a, seed: seed++ } }) };
|
||||
var peg$f13 = function(s) { return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) };
|
||||
var peg$f14 = function(s) { return x => x.options_['ops'].push({ type_: "range", arguments_ :{ element:s } }) };
|
||||
var peg$f15 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1});
|
||||
var peg$f2 = function(chars) { const s = chars.join(""); return (s === ".") || (s === "_") };
|
||||
var peg$f3 = function(chars) { return new AtomStub(chars.join("")) };
|
||||
var peg$f4 = function(s) { return s };
|
||||
var peg$f5 = function(s, stepsPerCycle) { s.arguments_.stepsPerCycle = stepsPerCycle ; return s; };
|
||||
var peg$f6 = function(a) { return a };
|
||||
var peg$f7 = function(s) { s.arguments_.alignment = 'polymeter_slowcat'; return s; };
|
||||
var peg$f8 = function(a) { return x => x.options_['weight'] = (x.options_['weight'] ?? 1) + (a ?? 2) - 1 };
|
||||
var peg$f9 = function(a) { return x => x.options_['reps'] = (x.options_['reps'] ?? 1) + (a ?? 2) - 1 };
|
||||
var peg$f10 = function(p, s, r) { return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) };
|
||||
var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'slow' }}) };
|
||||
var peg$f12 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) };
|
||||
var peg$f13 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a, seed: seed++ } }) };
|
||||
var peg$f14 = function(s) { return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) };
|
||||
var peg$f15 = function(s) { return x => x.options_['ops'].push({ type_: "range", arguments_ :{ element:s } }) };
|
||||
var peg$f16 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1});
|
||||
for (const op of ops) {
|
||||
op(result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
var peg$f16 = function(s) { return new PatternStub(s, 'fastcat'); };
|
||||
var peg$f17 = function(tail) { return { alignment: 'stack', list: tail }; };
|
||||
var peg$f18 = function(tail) { return { alignment: 'rand', list: tail, seed: seed++ }; };
|
||||
var peg$f19 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } };
|
||||
var peg$f20 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); };
|
||||
var peg$f21 = function(sc) { return sc; };
|
||||
var peg$f22 = function(s) { return { name: "struct", args: { mini:s }}};
|
||||
var peg$f23 = function(s) { return { name: "target", args : { name:s}}};
|
||||
var peg$f24 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}};
|
||||
var peg$f25 = function(a) { return { name: "stretch", args :{ amount: a}}};
|
||||
var peg$f26 = function(a) { return { name: "shift", args :{ amount: "-"+a}}};
|
||||
var peg$f27 = function(a) { return { name: "shift", args :{ amount: a}}};
|
||||
var peg$f28 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}};
|
||||
var peg$f29 = function(s) { return { name: "scale", args :{ scale: s.join("")}}};
|
||||
var peg$f30 = function(s, v) { return v};
|
||||
var peg$f31 = function(s, ss) { ss.unshift(s); return new PatternStub(ss, 'slowcat'); };
|
||||
var peg$f32 = function(sg) {return sg};
|
||||
var peg$f33 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)};
|
||||
var peg$f34 = function(sc) { return sc };
|
||||
var peg$f35 = function(c) { return c };
|
||||
var peg$f36 = function(v) { return new CommandStub("setcps", { value: v})};
|
||||
var peg$f37 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})};
|
||||
var peg$f38 = function() { return new CommandStub("hush")};
|
||||
var peg$f17 = function(s) { return new PatternStub(s, 'fastcat'); };
|
||||
var peg$f18 = function(tail) { return { alignment: 'stack', list: tail }; };
|
||||
var peg$f19 = function(tail) { return { alignment: 'rand', list: tail, seed: seed++ }; };
|
||||
var peg$f20 = function(tail) { return { alignment: 'feet', list: tail, seed: seed++ }; };
|
||||
var peg$f21 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } };
|
||||
var peg$f22 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); };
|
||||
var peg$f23 = function(sc) { return sc; };
|
||||
var peg$f24 = function(s) { return { name: "struct", args: { mini:s }}};
|
||||
var peg$f25 = function(s) { return { name: "target", args : { name:s}}};
|
||||
var peg$f26 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}};
|
||||
var peg$f27 = function(a) { return { name: "stretch", args :{ amount: a}}};
|
||||
var peg$f28 = function(a) { return { name: "shift", args :{ amount: "-"+a}}};
|
||||
var peg$f29 = function(a) { return { name: "shift", args :{ amount: a}}};
|
||||
var peg$f30 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}};
|
||||
var peg$f31 = function(s) { return { name: "scale", args :{ scale: s.join("")}}};
|
||||
var peg$f32 = function(s, v) { return v};
|
||||
var peg$f33 = function(s, ss) { ss.unshift(s); return new PatternStub(ss, 'slowcat'); };
|
||||
var peg$f34 = function(sg) {return sg};
|
||||
var peg$f35 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)};
|
||||
var peg$f36 = function(sc) { return sc };
|
||||
var peg$f37 = function(c) { return c };
|
||||
var peg$f38 = function(v) { return new CommandStub("setcps", { value: v})};
|
||||
var peg$f39 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})};
|
||||
var peg$f40 = function() { return new CommandStub("hush")};
|
||||
var peg$currPos = 0;
|
||||
var peg$savedPos = 0;
|
||||
var peg$posDetailsCache = [{ line: 1, column: 1 }];
|
||||
@@ -821,6 +823,30 @@ function peg$parse(input, options) {
|
||||
return s0;
|
||||
}
|
||||
|
||||
function peg$parsedot() {
|
||||
var s0, s1, s2, s3;
|
||||
|
||||
s0 = peg$currPos;
|
||||
s1 = peg$parsews();
|
||||
if (input.charCodeAt(peg$currPos) === 46) {
|
||||
s2 = peg$c0;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s2 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e1); }
|
||||
}
|
||||
if (s2 !== peg$FAILED) {
|
||||
s3 = peg$parsews();
|
||||
s1 = [s1, s2, s3];
|
||||
s0 = s1;
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
}
|
||||
|
||||
return s0;
|
||||
}
|
||||
|
||||
function peg$parsequote() {
|
||||
var s0;
|
||||
|
||||
@@ -913,7 +939,7 @@ function peg$parse(input, options) {
|
||||
}
|
||||
|
||||
function peg$parsestep() {
|
||||
var s0, s1, s2, s3;
|
||||
var s0, s1, s2, s3, s4;
|
||||
|
||||
s0 = peg$currPos;
|
||||
s1 = peg$parsews();
|
||||
@@ -929,8 +955,20 @@ function peg$parse(input, options) {
|
||||
}
|
||||
if (s2 !== peg$FAILED) {
|
||||
s3 = peg$parsews();
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f2(s2);
|
||||
peg$savedPos = peg$currPos;
|
||||
s4 = peg$f2(s2);
|
||||
if (s4) {
|
||||
s4 = peg$FAILED;
|
||||
} else {
|
||||
s4 = undefined;
|
||||
}
|
||||
if (s4 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f3(s2);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
}
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -966,7 +1004,7 @@ function peg$parse(input, options) {
|
||||
if (s6 !== peg$FAILED) {
|
||||
s7 = peg$parsews();
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f3(s4);
|
||||
s0 = peg$f4(s4);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1014,7 +1052,7 @@ function peg$parse(input, options) {
|
||||
}
|
||||
s8 = peg$parsews();
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f4(s4, s7);
|
||||
s0 = peg$f5(s4, s7);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1046,7 +1084,7 @@ function peg$parse(input, options) {
|
||||
s2 = peg$parseslice();
|
||||
if (s2 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f5(s2);
|
||||
s0 = peg$f6(s2);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1073,7 +1111,7 @@ function peg$parse(input, options) {
|
||||
}
|
||||
if (s2 !== peg$FAILED) {
|
||||
s3 = peg$parsews();
|
||||
s4 = peg$parsesequence();
|
||||
s4 = peg$parsepolymeter_stack();
|
||||
if (s4 !== peg$FAILED) {
|
||||
s5 = peg$parsews();
|
||||
if (input.charCodeAt(peg$currPos) === 62) {
|
||||
@@ -1086,7 +1124,7 @@ function peg$parse(input, options) {
|
||||
if (s6 !== peg$FAILED) {
|
||||
s7 = peg$parsews();
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f6(s4);
|
||||
s0 = peg$f7(s4);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1150,25 +1188,33 @@ function peg$parse(input, options) {
|
||||
}
|
||||
|
||||
function peg$parseop_weight() {
|
||||
var s0, s1, s2;
|
||||
var s0, s1, s2, s3;
|
||||
|
||||
s0 = peg$currPos;
|
||||
s1 = peg$parsews();
|
||||
if (input.charCodeAt(peg$currPos) === 64) {
|
||||
s1 = peg$c18;
|
||||
s2 = peg$c18;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
s2 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e26); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsenumber();
|
||||
if (s2 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f7(s2);
|
||||
if (s2 === peg$FAILED) {
|
||||
if (input.charCodeAt(peg$currPos) === 95) {
|
||||
s2 = peg$c10;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
s2 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e18); }
|
||||
}
|
||||
}
|
||||
if (s2 !== peg$FAILED) {
|
||||
s3 = peg$parsenumber();
|
||||
if (s3 === peg$FAILED) {
|
||||
s3 = null;
|
||||
}
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f8(s3);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1178,25 +1224,24 @@ function peg$parse(input, options) {
|
||||
}
|
||||
|
||||
function peg$parseop_replicate() {
|
||||
var s0, s1, s2;
|
||||
var s0, s1, s2, s3;
|
||||
|
||||
s0 = peg$currPos;
|
||||
s1 = peg$parsews();
|
||||
if (input.charCodeAt(peg$currPos) === 33) {
|
||||
s1 = peg$c19;
|
||||
s2 = peg$c19;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
s2 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e27); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsenumber();
|
||||
if (s2 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f8(s2);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
if (s2 !== peg$FAILED) {
|
||||
s3 = peg$parsenumber();
|
||||
if (s3 === peg$FAILED) {
|
||||
s3 = null;
|
||||
}
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f9(s3);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1246,7 +1291,7 @@ function peg$parse(input, options) {
|
||||
}
|
||||
if (s13 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f9(s3, s7, s11);
|
||||
s0 = peg$f10(s3, s7, s11);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1286,7 +1331,7 @@ function peg$parse(input, options) {
|
||||
s2 = peg$parseslice();
|
||||
if (s2 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f10(s2);
|
||||
s0 = peg$f11(s2);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1314,7 +1359,7 @@ function peg$parse(input, options) {
|
||||
s2 = peg$parseslice();
|
||||
if (s2 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f11(s2);
|
||||
s0 = peg$f12(s2);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1344,7 +1389,7 @@ function peg$parse(input, options) {
|
||||
s2 = null;
|
||||
}
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f12(s2);
|
||||
s0 = peg$f13(s2);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1368,7 +1413,7 @@ function peg$parse(input, options) {
|
||||
s2 = peg$parseslice();
|
||||
if (s2 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f13(s2);
|
||||
s0 = peg$f14(s2);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1396,7 +1441,7 @@ function peg$parse(input, options) {
|
||||
s2 = peg$parseslice();
|
||||
if (s2 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f14(s2);
|
||||
s0 = peg$f15(s2);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1422,7 +1467,7 @@ function peg$parse(input, options) {
|
||||
s3 = peg$parseslice_op();
|
||||
}
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f15(s1, s2);
|
||||
s0 = peg$f16(s1, s2);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1447,7 +1492,7 @@ function peg$parse(input, options) {
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s1 = peg$f16(s1);
|
||||
s1 = peg$f17(s1);
|
||||
}
|
||||
s0 = s1;
|
||||
|
||||
@@ -1496,7 +1541,7 @@ function peg$parse(input, options) {
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s1 = peg$f17(s1);
|
||||
s1 = peg$f18(s1);
|
||||
}
|
||||
s0 = s1;
|
||||
|
||||
@@ -1545,7 +1590,56 @@ function peg$parse(input, options) {
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s1 = peg$f18(s1);
|
||||
s1 = peg$f19(s1);
|
||||
}
|
||||
s0 = s1;
|
||||
|
||||
return s0;
|
||||
}
|
||||
|
||||
function peg$parsedot_tail() {
|
||||
var s0, s1, s2, s3, s4;
|
||||
|
||||
s0 = peg$currPos;
|
||||
s1 = [];
|
||||
s2 = peg$currPos;
|
||||
s3 = peg$parsedot();
|
||||
if (s3 !== peg$FAILED) {
|
||||
s4 = peg$parsesequence();
|
||||
if (s4 !== peg$FAILED) {
|
||||
s2 = s4;
|
||||
} else {
|
||||
peg$currPos = s2;
|
||||
s2 = peg$FAILED;
|
||||
}
|
||||
} else {
|
||||
peg$currPos = s2;
|
||||
s2 = peg$FAILED;
|
||||
}
|
||||
if (s2 !== peg$FAILED) {
|
||||
while (s2 !== peg$FAILED) {
|
||||
s1.push(s2);
|
||||
s2 = peg$currPos;
|
||||
s3 = peg$parsedot();
|
||||
if (s3 !== peg$FAILED) {
|
||||
s4 = peg$parsesequence();
|
||||
if (s4 !== peg$FAILED) {
|
||||
s2 = s4;
|
||||
} else {
|
||||
peg$currPos = s2;
|
||||
s2 = peg$FAILED;
|
||||
}
|
||||
} else {
|
||||
peg$currPos = s2;
|
||||
s2 = peg$FAILED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s1 = peg$f20(s1);
|
||||
}
|
||||
s0 = s1;
|
||||
|
||||
@@ -1561,12 +1655,15 @@ function peg$parse(input, options) {
|
||||
s2 = peg$parsestack_tail();
|
||||
if (s2 === peg$FAILED) {
|
||||
s2 = peg$parsechoose_tail();
|
||||
if (s2 === peg$FAILED) {
|
||||
s2 = peg$parsedot_tail();
|
||||
}
|
||||
}
|
||||
if (s2 === peg$FAILED) {
|
||||
s2 = null;
|
||||
}
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f19(s1, s2);
|
||||
s0 = peg$f21(s1, s2);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1586,7 +1683,7 @@ function peg$parse(input, options) {
|
||||
s2 = null;
|
||||
}
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f20(s1, s2);
|
||||
s0 = peg$f22(s1, s2);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1609,7 +1706,7 @@ function peg$parse(input, options) {
|
||||
s6 = peg$parsequote();
|
||||
if (s6 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f21(s4);
|
||||
s0 = peg$f23(s4);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1671,7 +1768,7 @@ function peg$parse(input, options) {
|
||||
s3 = peg$parsemini_or_operator();
|
||||
if (s3 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f22(s3);
|
||||
s0 = peg$f24(s3);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1704,7 +1801,7 @@ function peg$parse(input, options) {
|
||||
s5 = peg$parsequote();
|
||||
if (s5 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f23(s4);
|
||||
s0 = peg$f25(s4);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1749,7 +1846,7 @@ function peg$parse(input, options) {
|
||||
s7 = null;
|
||||
}
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f24(s3, s5, s7);
|
||||
s0 = peg$f26(s3, s5, s7);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1782,7 +1879,7 @@ function peg$parse(input, options) {
|
||||
s3 = peg$parsenumber();
|
||||
if (s3 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f25(s3);
|
||||
s0 = peg$f27(s3);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1811,7 +1908,7 @@ function peg$parse(input, options) {
|
||||
s3 = peg$parsenumber();
|
||||
if (s3 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f26(s3);
|
||||
s0 = peg$f28(s3);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1840,7 +1937,7 @@ function peg$parse(input, options) {
|
||||
s3 = peg$parsenumber();
|
||||
if (s3 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f27(s3);
|
||||
s0 = peg$f29(s3);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1869,7 +1966,7 @@ function peg$parse(input, options) {
|
||||
s3 = peg$parsenumber();
|
||||
if (s3 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f28(s3);
|
||||
s0 = peg$f30(s3);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -1911,7 +2008,7 @@ function peg$parse(input, options) {
|
||||
s5 = peg$parsequote();
|
||||
if (s5 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f29(s4);
|
||||
s0 = peg$f31(s4);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -2003,7 +2100,7 @@ function peg$parse(input, options) {
|
||||
s9 = peg$parsemini_or_operator();
|
||||
if (s9 !== peg$FAILED) {
|
||||
peg$savedPos = s7;
|
||||
s7 = peg$f30(s5, s9);
|
||||
s7 = peg$f32(s5, s9);
|
||||
} else {
|
||||
peg$currPos = s7;
|
||||
s7 = peg$FAILED;
|
||||
@@ -2020,7 +2117,7 @@ function peg$parse(input, options) {
|
||||
s9 = peg$parsemini_or_operator();
|
||||
if (s9 !== peg$FAILED) {
|
||||
peg$savedPos = s7;
|
||||
s7 = peg$f30(s5, s9);
|
||||
s7 = peg$f32(s5, s9);
|
||||
} else {
|
||||
peg$currPos = s7;
|
||||
s7 = peg$FAILED;
|
||||
@@ -2040,7 +2137,7 @@ function peg$parse(input, options) {
|
||||
}
|
||||
if (s8 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f31(s5, s6);
|
||||
s0 = peg$f33(s5, s6);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -2086,7 +2183,7 @@ function peg$parse(input, options) {
|
||||
s4 = peg$parsecomment();
|
||||
}
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f32(s1);
|
||||
s0 = peg$f34(s1);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -2108,7 +2205,7 @@ function peg$parse(input, options) {
|
||||
s5 = peg$parsemini_or_operator();
|
||||
if (s5 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f33(s1, s5);
|
||||
s0 = peg$f35(s1, s5);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -2133,7 +2230,7 @@ function peg$parse(input, options) {
|
||||
s1 = peg$parsemini_or_operator();
|
||||
if (s1 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s1 = peg$f34(s1);
|
||||
s1 = peg$f36(s1);
|
||||
}
|
||||
s0 = s1;
|
||||
if (s0 === peg$FAILED) {
|
||||
@@ -2166,7 +2263,7 @@ function peg$parse(input, options) {
|
||||
if (s2 !== peg$FAILED) {
|
||||
s3 = peg$parsews();
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f35(s2);
|
||||
s0 = peg$f37(s2);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -2191,7 +2288,7 @@ function peg$parse(input, options) {
|
||||
s3 = peg$parsenumber();
|
||||
if (s3 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f36(s3);
|
||||
s0 = peg$f38(s3);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -2220,7 +2317,7 @@ function peg$parse(input, options) {
|
||||
s3 = peg$parsenumber();
|
||||
if (s3 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f37(s3);
|
||||
s0 = peg$f39(s3);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
@@ -2246,7 +2343,7 @@ function peg$parse(input, options) {
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s1 = peg$f38();
|
||||
s1 = peg$f40();
|
||||
}
|
||||
s0 = s1;
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ DIGIT = [0-9]
|
||||
ws "whitespace" = [ \n\r\t\u00A0]*
|
||||
comma = ws "," ws
|
||||
pipe = ws "|" ws
|
||||
dot = ws "." ws
|
||||
quote = '"' / "'"
|
||||
|
||||
// ------------------ steps and cycles ---------------------------
|
||||
@@ -105,7 +106,8 @@ quote = '"' / "'"
|
||||
// single step definition (e.g bd)
|
||||
step_char "a letter, a number, \"-\", \"#\", \".\", \"^\", \"_\"" =
|
||||
unicode_letter / [0-9~] / "-" / "#" / "." / "^" / "_"
|
||||
step = ws chars:step_char+ ws { return new AtomStub(chars.join("")) }
|
||||
|
||||
step = ws chars:step_char+ ws !{ const s = chars.join(""); return (s === ".") || (s === "_") } { return new AtomStub(chars.join("")) }
|
||||
|
||||
// define a sub cycle e.g. [1 2, 3 [4]]
|
||||
sub_cycle = ws "[" ws s:stack_or_choose ws "]" ws { return s }
|
||||
@@ -119,8 +121,8 @@ polymeter_steps = "%"a:slice
|
||||
|
||||
// define a step-per-cycle timeline e.g <1 3 [3 5]>. We simply defer to a sequence and
|
||||
// change the alignment to slowcat
|
||||
slow_sequence = ws "<" ws s:sequence ws ">" ws
|
||||
{ s.arguments_.alignment = 'slowcat'; return s; }
|
||||
slow_sequence = ws "<" ws s:polymeter_stack ws ">" ws
|
||||
{ s.arguments_.alignment = 'polymeter_slowcat'; return s; }
|
||||
|
||||
// a slice is either a single step or a sub cycle
|
||||
slice = step / sub_cycle / polymeter / slow_sequence
|
||||
@@ -129,11 +131,11 @@ slice = step / sub_cycle / polymeter / slow_sequence
|
||||
// at this point, we assume we can represent them as regular sequence operators
|
||||
slice_op = op_weight / op_bjorklund / op_slow / op_fast / op_replicate / op_degrade / op_tail / op_range
|
||||
|
||||
op_weight = "@" a:number
|
||||
{ return x => x.options_['weight'] = a }
|
||||
op_weight = ws ("@" / "_") a:number?
|
||||
{ return x => x.options_['weight'] = (x.options_['weight'] ?? 1) + (a ?? 2) - 1 }
|
||||
|
||||
op_replicate = "!"a:number
|
||||
{ return x => x.options_['reps'] = a }
|
||||
op_replicate = ws "!" a:number?
|
||||
{ return x => x.options_['reps'] = (x.options_['reps'] ?? 1) + (a ?? 2) - 1 }
|
||||
|
||||
op_bjorklund = "(" ws p:slice_with_ops ws comma ws s:slice_with_ops ws comma? ws r:slice_with_ops? ws ")"
|
||||
{ return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) }
|
||||
@@ -175,9 +177,13 @@ stack_tail = tail:(comma @sequence)+
|
||||
choose_tail = tail:(pipe @sequence)+
|
||||
{ return { alignment: 'rand', list: tail, seed: seed++ }; }
|
||||
|
||||
// a foot separates subsequences, as an alternative to wrapping them in []
|
||||
dot_tail = tail:(dot @sequence)+
|
||||
{ return { alignment: 'feet', list: tail, seed: seed++ }; }
|
||||
|
||||
// if the stack contains only one element, we don't create a stack but return the
|
||||
// underlying element
|
||||
stack_or_choose = head:sequence tail:(stack_tail / choose_tail)?
|
||||
stack_or_choose = head:sequence tail:(stack_tail / choose_tail / dot_tail)?
|
||||
{ if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } }
|
||||
|
||||
polymeter_stack = head:sequence tail:stack_tail?
|
||||
@@ -287,4 +293,4 @@ Lt = [\u01C5\u01C8\u01CB\u01F2\u1F88-\u1F8F\u1F98-\u1F9F\u1FA8-\u1FAF\u1FBC\u1FC
|
||||
Lu = [\u0041-\u005A\u00C0-\u00D6\u00D8-\u00DE\u0100\u0102\u0104\u0106\u0108\u010A\u010C\u010E\u0110\u0112\u0114\u0116\u0118\u011A\u011C\u011E\u0120\u0122\u0124\u0126\u0128\u012A\u012C\u012E\u0130\u0132\u0134\u0136\u0139\u013B\u013D\u013F\u0141\u0143\u0145\u0147\u014A\u014C\u014E\u0150\u0152\u0154\u0156\u0158\u015A\u015C\u015E\u0160\u0162\u0164\u0166\u0168\u016A\u016C\u016E\u0170\u0172\u0174\u0176\u0178-\u0179\u017B\u017D\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018B\u018E-\u0191\u0193-\u0194\u0196-\u0198\u019C-\u019D\u019F-\u01A0\u01A2\u01A4\u01A6-\u01A7\u01A9\u01AC\u01AE-\u01AF\u01B1-\u01B3\u01B5\u01B7-\u01B8\u01BC\u01C4\u01C7\u01CA\u01CD\u01CF\u01D1\u01D3\u01D5\u01D7\u01D9\u01DB\u01DE\u01E0\u01E2\u01E4\u01E6\u01E8\u01EA\u01EC\u01EE\u01F1\u01F4\u01F6-\u01F8\u01FA\u01FC\u01FE\u0200\u0202\u0204\u0206\u0208\u020A\u020C\u020E\u0210\u0212\u0214\u0216\u0218\u021A\u021C\u021E\u0220\u0222\u0224\u0226\u0228\u022A\u022C\u022E\u0230\u0232\u023A-\u023B\u023D-\u023E\u0241\u0243-\u0246\u0248\u024A\u024C\u024E\u0370\u0372\u0376\u037F\u0386\u0388-\u038A\u038C\u038E-\u038F\u0391-\u03A1\u03A3-\u03AB\u03CF\u03D2-\u03D4\u03D8\u03DA\u03DC\u03DE\u03E0\u03E2\u03E4\u03E6\u03E8\u03EA\u03EC\u03EE\u03F4\u03F7\u03F9-\u03FA\u03FD-\u042F\u0460\u0462\u0464\u0466\u0468\u046A\u046C\u046E\u0470\u0472\u0474\u0476\u0478\u047A\u047C\u047E\u0480\u048A\u048C\u048E\u0490\u0492\u0494\u0496\u0498\u049A\u049C\u049E\u04A0\u04A2\u04A4\u04A6\u04A8\u04AA\u04AC\u04AE\u04B0\u04B2\u04B4\u04B6\u04B8\u04BA\u04BC\u04BE\u04C0-\u04C1\u04C3\u04C5\u04C7\u04C9\u04CB\u04CD\u04D0\u04D2\u04D4\u04D6\u04D8\u04DA\u04DC\u04DE\u04E0\u04E2\u04E4\u04E6\u04E8\u04EA\u04EC\u04EE\u04F0\u04F2\u04F4\u04F6\u04F8\u04FA\u04FC\u04FE\u0500\u0502\u0504\u0506\u0508\u050A\u050C\u050E\u0510\u0512\u0514\u0516\u0518\u051A\u051C\u051E\u0520\u0522\u0524\u0526\u0528\u052A\u052C\u052E\u0531-\u0556\u10A0-\u10C5\u10C7\u10CD\u13A0-\u13F5\u1C90-\u1CBA\u1CBD-\u1CBF\u1E00\u1E02\u1E04\u1E06\u1E08\u1E0A\u1E0C\u1E0E\u1E10\u1E12\u1E14\u1E16\u1E18\u1E1A\u1E1C\u1E1E\u1E20\u1E22\u1E24\u1E26\u1E28\u1E2A\u1E2C\u1E2E\u1E30\u1E32\u1E34\u1E36\u1E38\u1E3A\u1E3C\u1E3E\u1E40\u1E42\u1E44\u1E46\u1E48\u1E4A\u1E4C\u1E4E\u1E50\u1E52\u1E54\u1E56\u1E58\u1E5A\u1E5C\u1E5E\u1E60\u1E62\u1E64\u1E66\u1E68\u1E6A\u1E6C\u1E6E\u1E70\u1E72\u1E74\u1E76\u1E78\u1E7A\u1E7C\u1E7E\u1E80\u1E82\u1E84\u1E86\u1E88\u1E8A\u1E8C\u1E8E\u1E90\u1E92\u1E94\u1E9E\u1EA0\u1EA2\u1EA4\u1EA6\u1EA8\u1EAA\u1EAC\u1EAE\u1EB0\u1EB2\u1EB4\u1EB6\u1EB8\u1EBA\u1EBC\u1EBE\u1EC0\u1EC2\u1EC4\u1EC6\u1EC8\u1ECA\u1ECC\u1ECE\u1ED0\u1ED2\u1ED4\u1ED6\u1ED8\u1EDA\u1EDC\u1EDE\u1EE0\u1EE2\u1EE4\u1EE6\u1EE8\u1EEA\u1EEC\u1EEE\u1EF0\u1EF2\u1EF4\u1EF6\u1EF8\u1EFA\u1EFC\u1EFE\u1F08-\u1F0F\u1F18-\u1F1D\u1F28-\u1F2F\u1F38-\u1F3F\u1F48-\u1F4D\u1F59\u1F5B\u1F5D\u1F5F\u1F68-\u1F6F\u1FB8-\u1FBB\u1FC8-\u1FCB\u1FD8-\u1FDB\u1FE8-\u1FEC\u1FF8-\u1FFB\u2102\u2107\u210B-\u210D\u2110-\u2112\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u2130-\u2133\u213E-\u213F\u2145\u2183\u2C00-\u2C2E\u2C60\u2C62-\u2C64\u2C67\u2C69\u2C6B\u2C6D-\u2C70\u2C72\u2C75\u2C7E-\u2C80\u2C82\u2C84\u2C86\u2C88\u2C8A\u2C8C\u2C8E\u2C90\u2C92\u2C94\u2C96\u2C98\u2C9A\u2C9C\u2C9E\u2CA0\u2CA2\u2CA4\u2CA6\u2CA8\u2CAA\u2CAC\u2CAE\u2CB0\u2CB2\u2CB4\u2CB6\u2CB8\u2CBA\u2CBC\u2CBE\u2CC0\u2CC2\u2CC4\u2CC6\u2CC8\u2CCA\u2CCC\u2CCE\u2CD0\u2CD2\u2CD4\u2CD6\u2CD8\u2CDA\u2CDC\u2CDE\u2CE0\u2CE2\u2CEB\u2CED\u2CF2\uA640\uA642\uA644\uA646\uA648\uA64A\uA64C\uA64E\uA650\uA652\uA654\uA656\uA658\uA65A\uA65C\uA65E\uA660\uA662\uA664\uA666\uA668\uA66A\uA66C\uA680\uA682\uA684\uA686\uA688\uA68A\uA68C\uA68E\uA690\uA692\uA694\uA696\uA698\uA69A\uA722\uA724\uA726\uA728\uA72A\uA72C\uA72E\uA732\uA734\uA736\uA738\uA73A\uA73C\uA73E\uA740\uA742\uA744\uA746\uA748\uA74A\uA74C\uA74E\uA750\uA752\uA754\uA756\uA758\uA75A\uA75C\uA75E\uA760\uA762\uA764\uA766\uA768\uA76A\uA76C\uA76E\uA779\uA77B\uA77D-\uA77E\uA780\uA782\uA784\uA786\uA78B\uA78D\uA790\uA792\uA796\uA798\uA79A\uA79C\uA79E\uA7A0\uA7A2\uA7A4\uA7A6\uA7A8\uA7AA-\uA7AE\uA7B0-\uA7B4\uA7B6\uA7B8\uFF21-\uFF3A]
|
||||
|
||||
// Number, Letter
|
||||
Nl = [\u16EE-\u16F0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303A\uA6E6-\uA6EF]
|
||||
Nl = [\u16EE-\u16F0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303A\uA6E6-\uA6EF]
|
||||
|
||||
@@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
*/
|
||||
|
||||
import * as krill from './krill-parser.js';
|
||||
import * as strudel from '@strudel.cycles/core';
|
||||
import * as strudel from '@strudel/core';
|
||||
|
||||
const randOffset = 0.0003;
|
||||
|
||||
@@ -91,6 +91,10 @@ export function patternifyAST(ast, code, onEnter, offset = 0) {
|
||||
if (alignment === 'stack') {
|
||||
return strudel.stack(...children);
|
||||
}
|
||||
if (alignment === 'polymeter_slowcat') {
|
||||
const aligned = children.map((child) => child._slow(strudel.Fraction(child.__weight ?? 1)));
|
||||
return strudel.stack(...aligned);
|
||||
}
|
||||
if (alignment === 'polymeter') {
|
||||
// polymeter
|
||||
const stepsPerCycle = ast.arguments_.stepsPerCycle
|
||||
@@ -103,16 +107,13 @@ export function patternifyAST(ast, code, onEnter, offset = 0) {
|
||||
if (alignment === 'rand') {
|
||||
return strudel.chooseInWith(strudel.rand.early(randOffset * ast.arguments_.seed).segment(1), children);
|
||||
}
|
||||
const weightedChildren = ast.source_.some((child) => !!child.options_?.weight);
|
||||
if (!weightedChildren && alignment === 'slowcat') {
|
||||
return strudel.slowcat(...children);
|
||||
if (alignment === 'feet') {
|
||||
return strudel.fastcat(...children);
|
||||
}
|
||||
const weightedChildren = ast.source_.some((child) => !!child.options_?.weight);
|
||||
if (weightedChildren) {
|
||||
const weightSum = ast.source_.reduce((sum, child) => sum + (child.options_?.weight || 1), 0);
|
||||
const pat = strudel.timeCat(...ast.source_.map((child, i) => [child.options_?.weight || 1, children[i]]));
|
||||
if (alignment === 'slowcat') {
|
||||
return pat._slow(weightSum); // timecat + slow
|
||||
}
|
||||
pat.__weight = weightSum;
|
||||
return pat;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/mini",
|
||||
"version": "0.9.0",
|
||||
"name": "@strudel/mini",
|
||||
"version": "1.0.0",
|
||||
"description": "Mini notation for strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@@ -32,7 +32,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*"
|
||||
"@strudel/core": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"peggy": "^3.0.2",
|
||||
|
||||
@@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
*/
|
||||
|
||||
import { getLeafLocation, getLeafLocations, mini, mini2ast } from '../mini.mjs';
|
||||
import '@strudel.cycles/core/euclid.mjs';
|
||||
import '@strudel/core/euclid.mjs';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
describe('mini', () => {
|
||||
@@ -73,6 +73,10 @@ describe('mini', () => {
|
||||
expect(minS('a!3 b')).toEqual(['a: 0 - 1/4', 'a: 1/4 - 1/2', 'a: 1/2 - 3/4', 'b: 3/4 - 1']);
|
||||
expect(minS('[<a b c>]!3 d')).toEqual(minS('<a b c> <a b c> <a b c> d'));
|
||||
});
|
||||
it('supports replication via repeated !', () => {
|
||||
expect(minS('a ! ! b')).toEqual(['a: 0 - 1/4', 'a: 1/4 - 1/2', 'a: 1/2 - 3/4', 'b: 3/4 - 1']);
|
||||
expect(minS('[<a b c>]!! d')).toEqual(minS('<a b c> <a b c> <a b c> d'));
|
||||
});
|
||||
it('supports euclidean rhythms', () => {
|
||||
expect(minS('a(3, 8)')).toEqual(['a: 0 - 1/8', 'a: 3/8 - 1/2', 'a: 3/4 - 7/8']);
|
||||
});
|
||||
@@ -190,6 +194,16 @@ describe('mini', () => {
|
||||
it('supports patterned ranges', () => {
|
||||
expect(minS('[<0 1> .. <2 4>]*2')).toEqual(minS('[0 1 2] [1 2 3 4]'));
|
||||
});
|
||||
it('supports the . operator', () => {
|
||||
expect(minS('a . b c')).toEqual(minS('a [b c]'));
|
||||
expect(minS('a . b c . [d e f . g h]')).toEqual(minS('a [b c] [[d e f] [g h]]'));
|
||||
});
|
||||
it('supports the _ operator', () => {
|
||||
expect(minS('a _ b _ _')).toEqual(minS('a@2 b@3'));
|
||||
});
|
||||
it('_ and @ are almost interchangeable', () => {
|
||||
expect(minS('a @ b @ @')).toEqual(minS('a _2 b _3'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLeafLocation', () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# @strudel.cycles/osc
|
||||
# @strudel/osc
|
||||
|
||||
OSC output for strudel patterns! Currently only tested with super collider / super dirt.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
|
||||
import OSC from 'osc-js';
|
||||
|
||||
import { logger, parseNumeral, Pattern } from '@strudel.cycles/core';
|
||||
import { logger, parseNumeral, Pattern } from '@strudel/core';
|
||||
|
||||
let connection; // Promise<OSC>
|
||||
function connect() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/osc",
|
||||
"version": "0.9.0",
|
||||
"name": "@strudel/osc",
|
||||
"version": "1.0.0",
|
||||
"description": "OSC messaging for strudel",
|
||||
"main": "osc.mjs",
|
||||
"publishConfig": {
|
||||
@@ -36,7 +36,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel/core": "workspace:*",
|
||||
"osc-js": "^2.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel/repl",
|
||||
"version": "0.9.4",
|
||||
"version": "1.0.0",
|
||||
"description": "Strudel REPL as a Web Component",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
@@ -33,15 +33,15 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel.cycles/midi": "workspace:*",
|
||||
"@strudel.cycles/mini": "workspace:*",
|
||||
"@strudel.cycles/soundfonts": "workspace:*",
|
||||
"@strudel.cycles/tonal": "workspace:*",
|
||||
"@strudel.cycles/transpiler": "workspace:*",
|
||||
"@strudel.cycles/webaudio": "workspace:*",
|
||||
"@strudel/codemirror": "workspace:*",
|
||||
"@strudel/hydra": "workspace:*"
|
||||
"@strudel/core": "workspace:*",
|
||||
"@strudel/hydra": "workspace:*",
|
||||
"@strudel/midi": "workspace:*",
|
||||
"@strudel/mini": "workspace:*",
|
||||
"@strudel/soundfonts": "workspace:*",
|
||||
"@strudel/tonal": "workspace:*",
|
||||
"@strudel/transpiler": "workspace:*",
|
||||
"@strudel/webaudio": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-replace": "^5.0.5",
|
||||
|
||||
+16
-16
@@ -1,22 +1,22 @@
|
||||
import { controls, noteToMidi, valueToMidi, Pattern, evalScope } from '@strudel.cycles/core';
|
||||
import { registerSynthSounds, registerZZFXSounds, samples } from '@strudel.cycles/webaudio';
|
||||
import * as core from '@strudel.cycles/core';
|
||||
import { controls, noteToMidi, valueToMidi, Pattern, evalScope } from '@strudel/core';
|
||||
import { registerSynthSounds, registerZZFXSounds, samples } from '@strudel/webaudio';
|
||||
import * as core from '@strudel/core';
|
||||
|
||||
export async function prebake() {
|
||||
const modulesLoading = evalScope(
|
||||
// import('@strudel.cycles/core'),
|
||||
// import('@strudel/core'),
|
||||
core,
|
||||
import('@strudel.cycles/mini'),
|
||||
import('@strudel.cycles/tonal'),
|
||||
import('@strudel.cycles/webaudio'),
|
||||
import('@strudel/mini'),
|
||||
import('@strudel/tonal'),
|
||||
import('@strudel/webaudio'),
|
||||
import('@strudel/codemirror'),
|
||||
import('@strudel/hydra'),
|
||||
import('@strudel.cycles/soundfonts'),
|
||||
import('@strudel.cycles/midi'),
|
||||
// import('@strudel.cycles/xen'),
|
||||
// import('@strudel.cycles/serial'),
|
||||
// import('@strudel.cycles/csound'),
|
||||
// import('@strudel.cycles/osc'),
|
||||
import('@strudel/soundfonts'),
|
||||
import('@strudel/midi'),
|
||||
// import('@strudel/xen'),
|
||||
// import('@strudel/serial'),
|
||||
// import('@strudel/csound'),
|
||||
// import('@strudel/osc'),
|
||||
controls, // sadly, this cannot be exported from core directly (yet)
|
||||
);
|
||||
// load samples
|
||||
@@ -26,10 +26,10 @@ export async function prebake() {
|
||||
registerSynthSounds(),
|
||||
registerZZFXSounds(),
|
||||
//registerSoundfonts(),
|
||||
// need dynamic import here, because importing @strudel.cycles/soundfonts fails on server:
|
||||
// => getting "window is not defined", as soon as "@strudel.cycles/soundfonts" is imported statically
|
||||
// need dynamic import here, because importing @strudel/soundfonts fails on server:
|
||||
// => getting "window is not defined", as soon as "@strudel/soundfonts" is imported statically
|
||||
// seems to be a problem with soundfont2
|
||||
import('@strudel.cycles/soundfonts').then(({ registerSoundfonts }) => registerSoundfonts()),
|
||||
import('@strudel/soundfonts').then(({ registerSoundfonts }) => registerSoundfonts()),
|
||||
samples(`${ds}/tidal-drum-machines.json`),
|
||||
samples(`${ds}/piano.json`),
|
||||
samples(`${ds}/Dirt-Samples.json`),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getDrawContext, silence } from '@strudel.cycles/core';
|
||||
import { transpiler } from '@strudel.cycles/transpiler';
|
||||
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio';
|
||||
import { getDrawContext, silence } from '@strudel/core';
|
||||
import { transpiler } from '@strudel/transpiler';
|
||||
import { getAudioContext, webaudioOutput } from '@strudel/webaudio';
|
||||
import { StrudelMirror, codemirrorSettings } from '@strudel/codemirror';
|
||||
import { prebake } from './prebake.mjs';
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# @strudel.cycles/serial
|
||||
# @strudel/serial
|
||||
|
||||
This package adds webserial functionality to strudel Patterns, for e.g. sending messages to arduino microcontrollers.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/serial",
|
||||
"version": "0.9.0",
|
||||
"name": "@strudel/serial",
|
||||
"version": "1.0.0",
|
||||
"description": "Webserial API for strudel",
|
||||
"main": "serial.mjs",
|
||||
"publishConfig": {
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*"
|
||||
"@strudel/core": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^5.0.10"
|
||||
|
||||
@@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Pattern, isPattern } from '@strudel.cycles/core';
|
||||
import { Pattern, isPattern } from '@strudel/core';
|
||||
|
||||
var writeMessagers = {};
|
||||
var choosing = false;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# @strudel/soundfonts
|
||||
@@ -1,5 +1,12 @@
|
||||
import { noteToMidi, freqToMidi, getSoundIndex } from '@strudel.cycles/core';
|
||||
import { getAudioContext, registerSound, getEnvelope } from '@strudel.cycles/webaudio';
|
||||
import { noteToMidi, freqToMidi, getSoundIndex } from '@strudel/core';
|
||||
import {
|
||||
getAudioContext,
|
||||
registerSound,
|
||||
getParamADSR,
|
||||
getADSRValues,
|
||||
getPitchEnvelope,
|
||||
getVibratoOscillator,
|
||||
} from '@strudel/webaudio';
|
||||
import gm from './gm.mjs';
|
||||
|
||||
let loadCache = {};
|
||||
@@ -130,24 +137,39 @@ export function registerSoundfonts() {
|
||||
registerSound(
|
||||
name,
|
||||
async (time, value, onended) => {
|
||||
const [attack, decay, sustain, release] = getADSRValues([
|
||||
value.attack,
|
||||
value.decay,
|
||||
value.sustain,
|
||||
value.release,
|
||||
]);
|
||||
|
||||
const { duration } = value;
|
||||
const n = getSoundIndex(value.n, fonts.length);
|
||||
const { attack = 0.001, decay = 0.001, sustain = 1, release = 0.001 } = value;
|
||||
const font = fonts[n];
|
||||
const ctx = getAudioContext();
|
||||
const bufferSource = await getFontBufferSource(font, value, ctx);
|
||||
bufferSource.start(time);
|
||||
const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 0.3, time);
|
||||
bufferSource.connect(envelope);
|
||||
const stop = (releaseTime) => {
|
||||
const silentAt = releaseEnvelope(releaseTime);
|
||||
bufferSource.stop(silentAt);
|
||||
};
|
||||
const envGain = ctx.createGain();
|
||||
const node = bufferSource.connect(envGain);
|
||||
const holdEnd = time + duration;
|
||||
getParamADSR(node.gain, attack, decay, sustain, release, 0, 0.3, time, holdEnd, 'linear');
|
||||
let envEnd = holdEnd + release + 0.01;
|
||||
|
||||
// vibrato
|
||||
let vibratoOscillator = getVibratoOscillator(bufferSource.detune, value, time);
|
||||
// pitch envelope
|
||||
getPitchEnvelope(bufferSource.detune, value, time, holdEnd);
|
||||
|
||||
bufferSource.stop(envEnd);
|
||||
const stop = (releaseTime) => {};
|
||||
bufferSource.onended = () => {
|
||||
bufferSource.disconnect();
|
||||
envelope.disconnect();
|
||||
vibratoOscillator?.stop();
|
||||
node.disconnect();
|
||||
onended();
|
||||
};
|
||||
return { node: envelope, stop };
|
||||
return { node, stop };
|
||||
},
|
||||
{ type: 'soundfont', prebake: true, fonts },
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/soundfonts",
|
||||
"version": "0.9.0",
|
||||
"name": "@strudel/soundfonts",
|
||||
"version": "1.0.0",
|
||||
"description": "Soundsfont support for strudel",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
@@ -30,8 +30,8 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel.cycles/webaudio": "workspace:*",
|
||||
"@strudel/core": "workspace:*",
|
||||
"@strudel/webaudio": "workspace:*",
|
||||
"sfumato": "^0.1.2",
|
||||
"soundfont2": "^0.4.0"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Pattern, getPlayableNoteValue, noteToMidi } from '@strudel.cycles/core';
|
||||
import { getAudioContext, registerSound } from '@strudel.cycles/webaudio';
|
||||
import { Pattern, getPlayableNoteValue, noteToMidi } from '@strudel/core';
|
||||
import { getAudioContext, registerSound } from '@strudel/webaudio';
|
||||
import { loadSoundfont as _loadSoundfont, startPresetNote } from 'sfumato';
|
||||
|
||||
Pattern.prototype.soundfont = function (sf, n = 0) {
|
||||
|
||||
+132
-94
@@ -1,5 +1,5 @@
|
||||
import { getAudioContext } from './superdough.mjs';
|
||||
import { clamp } from './util.mjs';
|
||||
import { clamp, nanFallback } from './util.mjs';
|
||||
|
||||
export function gainNode(value) {
|
||||
const node = getAudioContext().createGain();
|
||||
@@ -7,78 +7,73 @@ export function gainNode(value) {
|
||||
return node;
|
||||
}
|
||||
|
||||
// alternative to getADSR returning the gain node and a stop handle to trigger the release anytime in the future
|
||||
export const getEnvelope = (attack, decay, sustain, release, velocity, begin) => {
|
||||
const gainNode = getAudioContext().createGain();
|
||||
let phase = begin;
|
||||
gainNode.gain.setValueAtTime(0, begin);
|
||||
phase += attack;
|
||||
gainNode.gain.linearRampToValueAtTime(velocity, phase); // attack
|
||||
phase += decay;
|
||||
let sustainLevel = sustain * velocity;
|
||||
gainNode.gain.linearRampToValueAtTime(sustainLevel, phase); // decay / sustain
|
||||
// sustain end
|
||||
return {
|
||||
node: gainNode,
|
||||
stop: (t) => {
|
||||
// to make sure the release won't begin before sustain is reached
|
||||
phase = Math.max(t, phase);
|
||||
// see https://github.com/tidalcycles/strudel/issues/522
|
||||
gainNode.gain.setValueAtTime(sustainLevel, phase);
|
||||
phase += release;
|
||||
gainNode.gain.linearRampToValueAtTime(0, phase); // release
|
||||
return phase;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getExpEnvelope = (attack, decay, sustain, release, velocity, begin) => {
|
||||
sustain = Math.max(0.001, sustain);
|
||||
velocity = Math.max(0.001, velocity);
|
||||
const gainNode = getAudioContext().createGain();
|
||||
gainNode.gain.setValueAtTime(0.0001, begin);
|
||||
gainNode.gain.exponentialRampToValueAtTime(velocity, begin + attack);
|
||||
gainNode.gain.exponentialRampToValueAtTime(sustain * velocity, begin + attack + decay);
|
||||
return {
|
||||
node: gainNode,
|
||||
stop: (t) => {
|
||||
// similar to getEnvelope, this will glitch if sustain level has not been reached
|
||||
gainNode.gain.exponentialRampToValueAtTime(0.0001, t + release);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export 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
|
||||
gainNode.gain.linearRampToValueAtTime(sustain * velocity, begin + attack + decay); // sustain start
|
||||
gainNode.gain.setValueAtTime(sustain * velocity, end); // sustain end
|
||||
gainNode.gain.linearRampToValueAtTime(0, end + release); // release
|
||||
// for some reason, using exponential ramping creates little cracklings
|
||||
/* let t = begin;
|
||||
gainNode.gain.setValueAtTime(0, t);
|
||||
gainNode.gain.exponentialRampToValueAtTime(velocity, (t += attack));
|
||||
const sustainGain = Math.max(sustain * velocity, 0.001);
|
||||
gainNode.gain.exponentialRampToValueAtTime(sustainGain, (t += decay));
|
||||
if (end - begin < attack + decay) {
|
||||
gainNode.gain.cancelAndHoldAtTime(end);
|
||||
} else {
|
||||
gainNode.gain.setValueAtTime(sustainGain, end);
|
||||
const getSlope = (y1, y2, x1, x2) => {
|
||||
const denom = x2 - x1;
|
||||
if (denom === 0) {
|
||||
return 0;
|
||||
}
|
||||
gainNode.gain.exponentialRampToValueAtTime(0.001, end + release); // release */
|
||||
return gainNode;
|
||||
return (y2 - y1) / (x2 - x1);
|
||||
};
|
||||
|
||||
export const getParamADSR = (param, attack, decay, sustain, release, min, max, begin, end) => {
|
||||
export const getParamADSR = (
|
||||
param,
|
||||
attack,
|
||||
decay,
|
||||
sustain,
|
||||
release,
|
||||
min,
|
||||
max,
|
||||
begin,
|
||||
end,
|
||||
//exponential works better for frequency modulations (such as filter cutoff) due to human ear perception
|
||||
curve = 'exponential',
|
||||
) => {
|
||||
attack = nanFallback(attack);
|
||||
decay = nanFallback(decay);
|
||||
sustain = nanFallback(sustain);
|
||||
release = nanFallback(release);
|
||||
const ramp = curve === 'exponential' ? 'exponentialRampToValueAtTime' : 'linearRampToValueAtTime';
|
||||
if (curve === 'exponential') {
|
||||
min = min === 0 ? 0.001 : min;
|
||||
max = max === 0 ? 0.001 : max;
|
||||
}
|
||||
const range = max - min;
|
||||
const peak = min + range;
|
||||
const sustainLevel = min + sustain * range;
|
||||
const peak = max;
|
||||
const sustainVal = min + sustain * range;
|
||||
const duration = end - begin;
|
||||
|
||||
const envValAtTime = (time) => {
|
||||
let val;
|
||||
if (attack > time) {
|
||||
let slope = getSlope(min, peak, 0, attack);
|
||||
val = time * slope + (min > peak ? min : 0);
|
||||
} else {
|
||||
val = (time - attack) * getSlope(peak, sustainVal, 0, decay) + peak;
|
||||
}
|
||||
if (curve === 'exponential') {
|
||||
val = val || 0.001;
|
||||
}
|
||||
return val;
|
||||
};
|
||||
|
||||
param.setValueAtTime(min, begin);
|
||||
param.linearRampToValueAtTime(peak, begin + attack);
|
||||
param.linearRampToValueAtTime(sustainLevel, begin + attack + decay);
|
||||
param.setValueAtTime(sustainLevel, end);
|
||||
param.linearRampToValueAtTime(min, end + Math.max(release, 0.1));
|
||||
if (attack > duration) {
|
||||
//attack
|
||||
param[ramp](envValAtTime(duration), end);
|
||||
} else if (attack + decay > duration) {
|
||||
//attack
|
||||
param[ramp](envValAtTime(attack), begin + attack);
|
||||
//decay
|
||||
param[ramp](envValAtTime(duration), end);
|
||||
} else {
|
||||
//attack
|
||||
param[ramp](envValAtTime(attack), begin + attack);
|
||||
//decay
|
||||
param[ramp](envValAtTime(attack + decay), begin + attack + decay);
|
||||
//sustain
|
||||
param.setValueAtTime(sustainVal, end);
|
||||
}
|
||||
//release
|
||||
param[ramp](min, end + release);
|
||||
};
|
||||
|
||||
export function getCompressor(ac, threshold, ratio, knee, attack, release) {
|
||||
@@ -92,38 +87,44 @@ export function getCompressor(ac, threshold, ratio, knee, attack, release) {
|
||||
return new DynamicsCompressorNode(ac, options);
|
||||
}
|
||||
|
||||
export function createFilter(
|
||||
context,
|
||||
type,
|
||||
frequency,
|
||||
Q,
|
||||
attack,
|
||||
decay,
|
||||
sustain,
|
||||
release,
|
||||
fenv,
|
||||
start,
|
||||
end,
|
||||
fanchor = 0.5,
|
||||
) {
|
||||
// changes the default values of the envelope based on what parameters the user has defined
|
||||
// so it behaves more like you would expect/familiar as other synthesis tools
|
||||
// ex: sound(val).decay(val) will behave as a decay only envelope. sound(val).attack(val).decay(val) will behave like an "ad" env, etc.
|
||||
|
||||
export const getADSRValues = (params, curve = 'linear', defaultValues) => {
|
||||
const envmin = curve === 'exponential' ? 0.001 : 0.001;
|
||||
const releaseMin = 0.01;
|
||||
const envmax = 1;
|
||||
const [a, d, s, r] = params;
|
||||
if (a == null && d == null && s == null && r == null) {
|
||||
return defaultValues ?? [envmin, envmin, envmax, releaseMin];
|
||||
}
|
||||
const sustain = s != null ? s : (a != null && d == null) || (a == null && d == null) ? envmax : envmin;
|
||||
return [Math.max(a ?? 0, envmin), Math.max(d ?? 0, envmin), Math.min(sustain, envmax), Math.max(r ?? 0, releaseMin)];
|
||||
};
|
||||
|
||||
export function createFilter(context, type, frequency, Q, att, dec, sus, rel, fenv, start, end, fanchor) {
|
||||
const curve = 'exponential';
|
||||
const [attack, decay, sustain, release] = getADSRValues([att, dec, sus, rel], curve, [0.005, 0.14, 0, 0.1]);
|
||||
const filter = context.createBiquadFilter();
|
||||
|
||||
filter.type = type;
|
||||
filter.Q.value = Q;
|
||||
filter.frequency.value = frequency;
|
||||
|
||||
// envelope is active when any of these values is set
|
||||
const hasEnvelope = att ?? dec ?? sus ?? rel ?? fenv;
|
||||
// Apply ADSR to filter frequency
|
||||
if (!isNaN(fenv) && fenv !== 0) {
|
||||
const offset = fenv * fanchor;
|
||||
|
||||
const min = clamp(2 ** -offset * frequency, 0, 20000);
|
||||
const max = clamp(2 ** (fenv - offset) * frequency, 0, 20000);
|
||||
|
||||
// console.log('min', min, 'max', max);
|
||||
|
||||
getParamADSR(filter.frequency, attack, decay, sustain, release, min, max, start, end);
|
||||
if (hasEnvelope !== undefined) {
|
||||
fenv = nanFallback(fenv, 1, true);
|
||||
fanchor = nanFallback(fanchor, 0, true);
|
||||
const fenvAbs = Math.abs(fenv);
|
||||
const offset = fenvAbs * fanchor;
|
||||
let min = clamp(2 ** -offset * frequency, 0, 20000);
|
||||
let max = clamp(2 ** (fenvAbs - offset) * frequency, 0, 20000);
|
||||
if (fenv < 0) [min, max] = [max, min];
|
||||
getParamADSR(filter.frequency, attack, decay, sustain, release, min, max, start, end, curve);
|
||||
return filter;
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
@@ -148,3 +149,40 @@ export function drywet(dry, wet, wetAmount = 0) {
|
||||
wet_gain.connect(mix);
|
||||
return mix;
|
||||
}
|
||||
|
||||
let curves = ['linear', 'exponential'];
|
||||
export function getPitchEnvelope(param, value, t, holdEnd) {
|
||||
// envelope is active when any of these values is set
|
||||
const hasEnvelope = value.pattack ?? value.pdecay ?? value.psustain ?? value.prelease ?? value.penv;
|
||||
if (!hasEnvelope) {
|
||||
return;
|
||||
}
|
||||
const penv = nanFallback(value.penv, 1, true);
|
||||
const curve = curves[value.pcurve ?? 0];
|
||||
let [pattack, pdecay, psustain, prelease] = getADSRValues(
|
||||
[value.pattack, value.pdecay, value.psustain, value.prelease],
|
||||
curve,
|
||||
[0.2, 0.001, 1, 0.001],
|
||||
);
|
||||
let panchor = value.panchor ?? psustain;
|
||||
const cents = penv * 100; // penv is in semitones
|
||||
const min = 0 - cents * panchor;
|
||||
const max = cents - cents * panchor;
|
||||
getParamADSR(param, pattack, pdecay, psustain, prelease, min, max, t, holdEnd, curve);
|
||||
}
|
||||
|
||||
export function getVibratoOscillator(param, value, t) {
|
||||
const { vibmod = 0.5, vib } = value;
|
||||
let vibratoOscillator;
|
||||
if (vib > 0) {
|
||||
vibratoOscillator = getAudioContext().createOscillator();
|
||||
vibratoOscillator.frequency.value = vib;
|
||||
const gain = getAudioContext().createGain();
|
||||
// Vibmod is the amount of vibrato, in semitones
|
||||
gain.gain.value = vibmod * 100;
|
||||
vibratoOscillator.connect(gain);
|
||||
gain.connect(param);
|
||||
vibratoOscillator.start(t);
|
||||
return vibratoOscillator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "superdough",
|
||||
"version": "0.9.12",
|
||||
"version": "1.0.0",
|
||||
"description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { noteToMidi, valueToMidi, getSoundIndex } from './util.mjs';
|
||||
import { getAudioContext, registerSound } from './index.mjs';
|
||||
import { getEnvelope } from './helpers.mjs';
|
||||
import { getADSRValues, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs';
|
||||
import { logger } from './logger.mjs';
|
||||
|
||||
const bufferCache = {}; // string: Promise<ArrayBuffer>
|
||||
@@ -243,8 +243,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
|
||||
begin = 0,
|
||||
loopEnd = 1,
|
||||
end = 1,
|
||||
vib,
|
||||
vibmod = 0.5,
|
||||
duration,
|
||||
} = value;
|
||||
// load sample
|
||||
if (speed === 0) {
|
||||
@@ -254,24 +253,15 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
|
||||
loop = s.startsWith('wt_') ? 1 : value.loop;
|
||||
const ac = getAudioContext();
|
||||
// destructure adsr here, because the default should be different for synths and samples
|
||||
const { attack = 0.001, decay = 0.001, sustain = 1, release = 0.001 } = value;
|
||||
|
||||
let [attack, decay, sustain, release] = getADSRValues([value.attack, value.decay, value.sustain, value.release]);
|
||||
//const soundfont = getSoundfontKey(s);
|
||||
const time = t + nudge;
|
||||
|
||||
const bufferSource = await getSampleBufferSource(s, n, note, speed, freq, bank, resolveUrl);
|
||||
|
||||
// vibrato
|
||||
let vibratoOscillator;
|
||||
if (vib > 0) {
|
||||
vibratoOscillator = getAudioContext().createOscillator();
|
||||
vibratoOscillator.frequency.value = vib;
|
||||
const gain = getAudioContext().createGain();
|
||||
// Vibmod is the amount of vibrato, in semitones
|
||||
gain.gain.value = vibmod * 100;
|
||||
vibratoOscillator.connect(gain);
|
||||
gain.connect(bufferSource.detune);
|
||||
vibratoOscillator.start(0);
|
||||
}
|
||||
let vibratoOscillator = getVibratoOscillator(bufferSource.detune, value, t);
|
||||
|
||||
// asny stuff above took too long?
|
||||
if (ac.currentTime > t) {
|
||||
@@ -298,26 +288,31 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
|
||||
bufferSource.loopEnd = loopEnd * bufferSource.buffer.duration - offset;
|
||||
}
|
||||
bufferSource.start(time, offset);
|
||||
const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t);
|
||||
bufferSource.connect(envelope);
|
||||
const envGain = ac.createGain();
|
||||
const node = bufferSource.connect(envGain);
|
||||
if (clip == null && loop == null && value.release == null) {
|
||||
const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value;
|
||||
duration = (end - begin) * bufferDuration;
|
||||
}
|
||||
let holdEnd = t + duration;
|
||||
|
||||
getParamADSR(node.gain, attack, decay, sustain, release, 0, 1, t, holdEnd, 'linear');
|
||||
|
||||
// pitch envelope
|
||||
getPitchEnvelope(bufferSource.detune, value, t, holdEnd);
|
||||
|
||||
const out = ac.createGain(); // we need a separate gain for the cutgroups because firefox...
|
||||
envelope.connect(out);
|
||||
node.connect(out);
|
||||
bufferSource.onended = function () {
|
||||
bufferSource.disconnect();
|
||||
vibratoOscillator?.stop();
|
||||
envelope.disconnect();
|
||||
node.disconnect();
|
||||
out.disconnect();
|
||||
onended();
|
||||
};
|
||||
const stop = (endTime, playWholeBuffer = clip === undefined && loop === undefined) => {
|
||||
let releaseTime = endTime;
|
||||
if (playWholeBuffer) {
|
||||
const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value;
|
||||
releaseTime = t + (end - begin) * bufferDuration;
|
||||
}
|
||||
const silentAt = releaseEnvelope(releaseTime);
|
||||
bufferSource.stop(silentAt);
|
||||
};
|
||||
let envEnd = holdEnd + release + 0.01;
|
||||
bufferSource.stop(envEnd);
|
||||
const stop = (endTime, playWholeBuffer) => {};
|
||||
const handle = { node: out, bufferSource, stop };
|
||||
|
||||
// cut groups
|
||||
|
||||
@@ -280,26 +280,26 @@ export const superdough = async (value, deadline, hapDuration) => {
|
||||
// low pass
|
||||
cutoff,
|
||||
lpenv,
|
||||
lpattack = 0.01,
|
||||
lpdecay = 0.01,
|
||||
lpsustain = 1,
|
||||
lprelease = 0.01,
|
||||
lpattack,
|
||||
lpdecay,
|
||||
lpsustain,
|
||||
lprelease,
|
||||
resonance = 1,
|
||||
// high pass
|
||||
hpenv,
|
||||
hcutoff,
|
||||
hpattack = 0.01,
|
||||
hpdecay = 0.01,
|
||||
hpsustain = 1,
|
||||
hprelease = 0.01,
|
||||
hpattack,
|
||||
hpdecay,
|
||||
hpsustain,
|
||||
hprelease,
|
||||
hresonance = 1,
|
||||
// band pass
|
||||
bpenv,
|
||||
bandf,
|
||||
bpattack = 0.01,
|
||||
bpdecay = 0.01,
|
||||
bpsustain = 1,
|
||||
bprelease = 0.01,
|
||||
bpattack,
|
||||
bpdecay,
|
||||
bpsustain,
|
||||
bprelease,
|
||||
bandq = 1,
|
||||
channels = [1, 2],
|
||||
//phaser
|
||||
@@ -333,6 +333,7 @@ export const superdough = async (value, deadline, hapDuration) => {
|
||||
compressorAttack,
|
||||
compressorRelease,
|
||||
} = value;
|
||||
|
||||
gain = nanFallback(gain, 1);
|
||||
|
||||
//music programs/audio gear usually increments inputs/outputs from 1, so imitate that behavior
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { midiToFreq, noteToMidi } from './util.mjs';
|
||||
import { registerSound, getAudioContext } from './superdough.mjs';
|
||||
import { gainNode, getEnvelope, getExpEnvelope } from './helpers.mjs';
|
||||
import { gainNode, getADSRValues, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs';
|
||||
import { getNoiseMix, getNoiseOscillator } from './noise.mjs';
|
||||
|
||||
const mod = (freq, range = 1, type = 'sine') => {
|
||||
@@ -29,8 +29,11 @@ export function registerSynthSounds() {
|
||||
registerSound(
|
||||
s,
|
||||
(t, value, onended) => {
|
||||
// destructure adsr here, because the default should be different for synths and samples
|
||||
let { attack = 0.001, decay = 0.05, sustain = 0.6, release = 0.01 } = value;
|
||||
const [attack, decay, sustain, release] = getADSRValues(
|
||||
[value.attack, value.decay, value.sustain, value.release],
|
||||
'linear',
|
||||
[0.001, 0.05, 0.6, 0.01],
|
||||
);
|
||||
|
||||
let sound;
|
||||
if (waveforms.includes(s)) {
|
||||
@@ -45,21 +48,24 @@ export function registerSynthSounds() {
|
||||
// turn down
|
||||
const g = gainNode(0.3);
|
||||
|
||||
// gain envelope
|
||||
const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t);
|
||||
const { duration } = value;
|
||||
|
||||
o.onended = () => {
|
||||
o.disconnect();
|
||||
g.disconnect();
|
||||
onended();
|
||||
};
|
||||
|
||||
const envGain = gainNode(1);
|
||||
let node = o.connect(g).connect(envGain);
|
||||
const holdEnd = t + duration;
|
||||
getParamADSR(node.gain, attack, decay, sustain, release, 0, 1, t, holdEnd, 'linear');
|
||||
const envEnd = holdEnd + release + 0.01;
|
||||
triggerRelease?.(envEnd);
|
||||
stop(envEnd);
|
||||
return {
|
||||
node: o.connect(g).connect(envelope),
|
||||
stop: (releaseTime) => {
|
||||
const silentAt = releaseEnvelope(releaseTime);
|
||||
triggerRelease?.(releaseTime);
|
||||
stop(silentAt);
|
||||
},
|
||||
node,
|
||||
stop: (releaseTime) => {},
|
||||
};
|
||||
},
|
||||
{ type: 'synth', prebake: true },
|
||||
@@ -99,28 +105,24 @@ export function waveformN(partials, type) {
|
||||
}
|
||||
|
||||
// expects one of waveforms as s
|
||||
export function getOscillator(
|
||||
s,
|
||||
t,
|
||||
{
|
||||
export function getOscillator(s, t, value) {
|
||||
let {
|
||||
n: partials,
|
||||
note,
|
||||
freq,
|
||||
vib = 0,
|
||||
vibmod = 0.5,
|
||||
noise = 0,
|
||||
// fm
|
||||
fmh: fmHarmonicity = 1,
|
||||
fmi: fmModulationIndex,
|
||||
fmenv: fmEnvelopeType = 'lin',
|
||||
fmenv: fmEnvelopeType = 'exp',
|
||||
fmattack: fmAttack,
|
||||
fmdecay: fmDecay,
|
||||
fmsustain: fmSustain,
|
||||
fmrelease: fmRelease,
|
||||
fmvelocity: fmVelocity,
|
||||
fmwave: fmWaveform = 'sine',
|
||||
},
|
||||
) {
|
||||
duration,
|
||||
} = value;
|
||||
let ac = getAudioContext();
|
||||
let o;
|
||||
// If no partials are given, use stock waveforms
|
||||
@@ -148,42 +150,39 @@ export function getOscillator(
|
||||
o.start(t);
|
||||
|
||||
// FM
|
||||
let stopFm, fmEnvelope;
|
||||
let stopFm;
|
||||
let envGain = ac.createGain();
|
||||
if (fmModulationIndex) {
|
||||
const { node: modulator, stop } = fm(o, fmHarmonicity, fmModulationIndex, fmWaveform);
|
||||
if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) {
|
||||
// no envelope by default
|
||||
modulator.connect(o.frequency);
|
||||
} else {
|
||||
fmAttack = fmAttack ?? 0.001;
|
||||
fmDecay = fmDecay ?? 0.001;
|
||||
fmSustain = fmSustain ?? 1;
|
||||
fmRelease = fmRelease ?? 0.001;
|
||||
fmVelocity = fmVelocity ?? 1;
|
||||
fmEnvelope = getEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t);
|
||||
if (fmEnvelopeType === 'exp') {
|
||||
fmEnvelope = getExpEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t);
|
||||
fmEnvelope.node.maxValue = fmModulationIndex * 2;
|
||||
fmEnvelope.node.minValue = 0.00001;
|
||||
}
|
||||
modulator.connect(fmEnvelope.node);
|
||||
fmEnvelope.node.connect(o.frequency);
|
||||
const [attack, decay, sustain, release] = getADSRValues([fmAttack, fmDecay, fmSustain, fmRelease]);
|
||||
const holdEnd = t + duration;
|
||||
getParamADSR(
|
||||
envGain.gain,
|
||||
attack,
|
||||
decay,
|
||||
sustain,
|
||||
release,
|
||||
0,
|
||||
1,
|
||||
t,
|
||||
holdEnd,
|
||||
fmEnvelopeType === 'exp' ? 'exponential' : 'linear',
|
||||
);
|
||||
modulator.connect(envGain);
|
||||
envGain.connect(o.frequency);
|
||||
}
|
||||
stopFm = stop;
|
||||
}
|
||||
|
||||
// Additional oscillator for vibrato effect
|
||||
let vibratoOscillator;
|
||||
if (vib > 0) {
|
||||
vibratoOscillator = getAudioContext().createOscillator();
|
||||
vibratoOscillator.frequency.value = vib;
|
||||
const gain = getAudioContext().createGain();
|
||||
// Vibmod is the amount of vibrato, in semitones
|
||||
gain.gain.value = vibmod * 100;
|
||||
vibratoOscillator.connect(gain);
|
||||
gain.connect(o.detune);
|
||||
vibratoOscillator.start(t);
|
||||
}
|
||||
let vibratoOscillator = getVibratoOscillator(o.detune, value, t);
|
||||
|
||||
// pitch envelope
|
||||
getPitchEnvelope(o.detune, value, t, t + duration);
|
||||
|
||||
let noiseMix;
|
||||
if (noise) {
|
||||
@@ -199,7 +198,7 @@ export function getOscillator(
|
||||
o.stop(time);
|
||||
},
|
||||
triggerRelease: (time) => {
|
||||
fmEnvelope?.stop(time);
|
||||
// envGain?.stop(time);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,9 +54,9 @@ export const valueToMidi = (value, fallbackValue) => {
|
||||
return fallbackValue;
|
||||
};
|
||||
|
||||
export function nanFallback(value, fallback) {
|
||||
export function nanFallback(value, fallback = 0, silent) {
|
||||
if (isNaN(Number(value))) {
|
||||
logger(`"${value}" is not a number, falling back to ${fallback}`, 'warning');
|
||||
!silent && logger(`"${value}" is not a number, falling back to ${fallback}`, 'warning');
|
||||
return fallback;
|
||||
}
|
||||
return value;
|
||||
|
||||
@@ -5,6 +5,37 @@ export var vowelFormant = {
|
||||
i: { freqs: [270, 1850, 2900, 3350, 3590], gains: [1, 0.0631, 0.0631, 0.0158, 0.0158], qs: [40, 90, 100, 120, 120] },
|
||||
o: { freqs: [430, 820, 2700, 3000, 3300], gains: [1, 0.3162, 0.0501, 0.0794, 0.01995], qs: [40, 80, 100, 120, 120] },
|
||||
u: { freqs: [370, 630, 2750, 3000, 3400], gains: [1, 0.1, 0.0708, 0.0316, 0.01995], qs: [40, 60, 100, 120, 120] },
|
||||
ae: { freqs: [650, 1515, 2400, 3000, 3350], gains: [1, 0.5, 0.1008, 0.0631, 0.0126], qs: [80, 90, 120, 130, 140] },
|
||||
aa: { freqs: [560, 900, 2570, 3000, 3300], gains: [1, 0.5, 0.0708, 0.0631, 0.0126], qs: [80, 90, 120, 130, 140] },
|
||||
oe: { freqs: [500, 1430, 2300, 3000, 3300], gains: [1, 0.2, 0.0708, 0.0316, 0.01995], qs: [40, 60, 100, 120, 120] },
|
||||
ue: { freqs: [250, 1750, 2150, 3200, 3300], gains: [1, 0.1, 0.0708, 0.0316, 0.01995], qs: [40, 60, 100, 120, 120] },
|
||||
y: { freqs: [400, 1460, 2400, 3000, 3300], gains: [1, 0.2, 0.0708, 0.0316, 0.02995], qs: [40, 60, 100, 120, 120] },
|
||||
uh: { freqs: [600, 1250, 2100, 3100, 3500], gains: [1, 0.3, 0.0608, 0.0316, 0.01995], qs: [40, 70, 100, 120, 130] },
|
||||
un: { freqs: [500, 1240, 2280, 3000, 3500], gains: [1, 0.1, 0.1708, 0.0216, 0.02995], qs: [40, 60, 100, 120, 120] },
|
||||
en: { freqs: [600, 1480, 2450, 3200, 3300], gains: [1, 0.15, 0.0708, 0.0316, 0.02995], qs: [40, 60, 100, 120, 120] },
|
||||
an: { freqs: [700, 1050, 2500, 3000, 3300], gains: [1, 0.1, 0.0708, 0.0316, 0.02995], qs: [40, 60, 100, 120, 120] },
|
||||
on: { freqs: [500, 1080, 2350, 3000, 3300], gains: [1, 0.1, 0.0708, 0.0316, 0.02995], qs: [40, 60, 100, 120, 120] },
|
||||
get æ() {
|
||||
return this.ae;
|
||||
},
|
||||
get ø() {
|
||||
return this.oe;
|
||||
},
|
||||
get ɑ() {
|
||||
return this.aa;
|
||||
},
|
||||
get å() {
|
||||
return this.aa;
|
||||
},
|
||||
get ö() {
|
||||
return this.oe;
|
||||
},
|
||||
get ü() {
|
||||
return this.ue;
|
||||
},
|
||||
get ı() {
|
||||
return this.y;
|
||||
},
|
||||
};
|
||||
if (typeof GainNode !== 'undefined') {
|
||||
class VowelNode extends GainNode {
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
# @strudel.cycles/tonal
|
||||
# @strudel/tonal
|
||||
|
||||
This package adds tonal / harmonic functions to strudel Patterns.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm i @strudel.cycles/tonal --save
|
||||
npm i @strudel/tonal --save
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
import { sequence } from '@strudel.cycles/core';
|
||||
import '@strudel.cycles/tonal';
|
||||
import { sequence } from '@strudel/core';
|
||||
import '@strudel/tonal';
|
||||
|
||||
const pattern = sequence(0, [1, 2]).scale('C major');
|
||||
|
||||
@@ -27,7 +27,7 @@ yields:
|
||||
(3/4 -> 1/1, 3/4 -> 1/1, E3)
|
||||
```
|
||||
|
||||
[play with @strudel.cycles/tonal codesandbox](https://codesandbox.io/s/strudel-tonal-example-rgc5if?file=/src/index.js)
|
||||
[play with @strudel/tonal codesandbox](https://codesandbox.io/s/strudel-tonal-example-rgc5if?file=/src/index.js)
|
||||
|
||||
## Tonal API
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/tonal",
|
||||
"version": "0.9.0",
|
||||
"name": "@strudel/tonal",
|
||||
"version": "1.0.0",
|
||||
"description": "Tonal functions for strudel",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
@@ -31,7 +31,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel/core": "workspace:*",
|
||||
"@tonaljs/tonal": "^4.7.2",
|
||||
"chord-voicings": "^0.0.1",
|
||||
"webmidi": "^3.1.8"
|
||||
|
||||
@@ -7,7 +7,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
// import { strict as assert } from 'assert';
|
||||
|
||||
import '../tonal.mjs'; // need to import this to add prototypes
|
||||
import { pure, controls, seq } from '@strudel.cycles/core';
|
||||
import { pure, controls, seq } from '@strudel/core';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { mini } from '../../mini/mini.mjs';
|
||||
const { n } = controls;
|
||||
|
||||
@@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
*/
|
||||
|
||||
import { Note, Interval, Scale } from '@tonaljs/tonal';
|
||||
import { register, _mod, silence, logger, pure, isNote } from '@strudel.cycles/core';
|
||||
import { register, _mod, silence, logger, pure, isNote } from '@strudel/core';
|
||||
import { stepInNamedScale } from './tonleiter.mjs';
|
||||
|
||||
const octavesInterval = (octaves) => (octaves <= 0 ? -1 : 1) + octaves * 7 + 'P';
|
||||
@@ -157,9 +157,9 @@ export const scaleTranspose = register('scaleTranspose', function (offset /* : n
|
||||
* .scale("C:<major minor>/2")
|
||||
* .s("piano")
|
||||
* @example
|
||||
* n(rand.range(0,12).segment(8).round())
|
||||
* n(rand.range(0,12).segment(8))
|
||||
* .scale("C:ritusen")
|
||||
* .s("folkharp")
|
||||
* .s("piano")
|
||||
*/
|
||||
|
||||
export const scale = register('scale', function (scale, pat) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { isNote, isNoteWithOctave, _mod, noteToMidi, tokenizeNote } from '@strudel.cycles/core';
|
||||
import { isNote, isNoteWithOctave, _mod, noteToMidi, tokenizeNote } from '@strudel/core';
|
||||
import { Interval, Scale } from '@tonaljs/tonal';
|
||||
|
||||
// https://codesandbox.io/s/stateless-voicings-g2tmz0?file=/src/lib.js:0-2515
|
||||
|
||||
@@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { stack, register, silence, logger } from '@strudel.cycles/core';
|
||||
import { stack, register, silence, logger } from '@strudel/core';
|
||||
import { renderVoicing } from './tonleiter.mjs';
|
||||
import _voicings from 'chord-voicings';
|
||||
import { complex, simple } from './ireal.mjs';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# @strudel.cycles/transpiler
|
||||
# @strudel/transpiler
|
||||
|
||||
This package contains a JS code transpiler with the following features:
|
||||
|
||||
@@ -9,14 +9,14 @@ This package contains a JS code transpiler with the following features:
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm i @strudel.cycles/transpiler
|
||||
npm i @strudel/transpiler
|
||||
```
|
||||
|
||||
## Use
|
||||
|
||||
```js
|
||||
import { transpiler } from '@strudel.cycles/core';
|
||||
import { evaluate } from '@strudel.cycles/core';
|
||||
import { transpiler } from '@strudel/core';
|
||||
import { evaluate } from '@strudel/core';
|
||||
|
||||
transpiler('note("c3 [e3,g3]")', { wrapAsync: false, addReturn: false, simpleLocs: true });
|
||||
/* mini('c3 [e3,g3]').withMiniLocation(7,17) */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { evaluate as _evaluate } from '@strudel.cycles/core';
|
||||
import { evaluate as _evaluate } from '@strudel/core';
|
||||
import { transpiler } from './transpiler.mjs';
|
||||
export * from './transpiler.mjs';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/transpiler",
|
||||
"version": "0.9.0",
|
||||
"name": "@strudel/transpiler",
|
||||
"version": "1.0.0",
|
||||
"description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
@@ -30,8 +30,8 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel.cycles/mini": "workspace:*",
|
||||
"@strudel/core": "workspace:*",
|
||||
"@strudel/mini": "workspace:*",
|
||||
"acorn": "^8.11.3",
|
||||
"escodegen": "^2.1.0",
|
||||
"estree-walker": "^3.0.1"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import escodegen from 'escodegen';
|
||||
import { parse } from 'acorn';
|
||||
import { walk } from 'estree-walker';
|
||||
import { isNoteWithOctave } from '@strudel.cycles/core';
|
||||
import { getLeafLocations } from '@strudel.cycles/mini';
|
||||
import { isNoteWithOctave } from '@strudel/core';
|
||||
import { getLeafLocations } from '@strudel/mini';
|
||||
|
||||
export function transpiler(input, options = {}) {
|
||||
const { wrapAsync = false, addReturn = true, emitMiniLocations = true, emitWidgets = true } = options;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel/web",
|
||||
"version": "0.9.0",
|
||||
"version": "1.0.0",
|
||||
"description": "Easy to setup, opiniated bundle of Strudel for the browser.",
|
||||
"main": "web.mjs",
|
||||
"publishConfig": {
|
||||
@@ -33,11 +33,11 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel.cycles/mini": "workspace:*",
|
||||
"@strudel.cycles/tonal": "workspace:*",
|
||||
"@strudel.cycles/transpiler": "workspace:*",
|
||||
"@strudel.cycles/webaudio": "workspace:*"
|
||||
"@strudel/core": "workspace:*",
|
||||
"@strudel/mini": "workspace:*",
|
||||
"@strudel/tonal": "workspace:*",
|
||||
"@strudel/transpiler": "workspace:*",
|
||||
"@strudel/webaudio": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^5.0.10"
|
||||
|
||||
+16
-16
@@ -1,25 +1,25 @@
|
||||
export * from '@strudel.cycles/core';
|
||||
export * from '@strudel.cycles/webaudio';
|
||||
//export * from '@strudel.cycles/soundfonts';
|
||||
export * from '@strudel.cycles/transpiler';
|
||||
export * from '@strudel.cycles/mini';
|
||||
export * from '@strudel.cycles/tonal';
|
||||
export * from '@strudel.cycles/webaudio';
|
||||
import { Pattern, evalScope, controls } from '@strudel.cycles/core';
|
||||
import { initAudioOnFirstClick, registerSynthSounds, webaudioScheduler } from '@strudel.cycles/webaudio';
|
||||
// import { registerSoundfonts } from '@strudel.cycles/soundfonts';
|
||||
import { evaluate as _evaluate } from '@strudel.cycles/transpiler';
|
||||
import { miniAllStrings } from '@strudel.cycles/mini';
|
||||
export * from '@strudel/core';
|
||||
export * from '@strudel/webaudio';
|
||||
//export * from '@strudel/soundfonts';
|
||||
export * from '@strudel/transpiler';
|
||||
export * from '@strudel/mini';
|
||||
export * from '@strudel/tonal';
|
||||
export * from '@strudel/webaudio';
|
||||
import { Pattern, evalScope, controls } from '@strudel/core';
|
||||
import { initAudioOnFirstClick, registerSynthSounds, webaudioScheduler } from '@strudel/webaudio';
|
||||
// import { registerSoundfonts } from '@strudel/soundfonts';
|
||||
import { evaluate as _evaluate } from '@strudel/transpiler';
|
||||
import { miniAllStrings } from '@strudel/mini';
|
||||
|
||||
// init logic
|
||||
export async function defaultPrebake() {
|
||||
const loadModules = evalScope(
|
||||
evalScope,
|
||||
controls,
|
||||
import('@strudel.cycles/core'),
|
||||
import('@strudel.cycles/mini'),
|
||||
import('@strudel.cycles/tonal'),
|
||||
import('@strudel.cycles/webaudio'),
|
||||
import('@strudel/core'),
|
||||
import('@strudel/mini'),
|
||||
import('@strudel/tonal'),
|
||||
import('@strudel/webaudio'),
|
||||
{ hush, evaluate },
|
||||
);
|
||||
await Promise.all([loadModules, registerSynthSounds() /* , registerSoundfonts() */]);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# @strudel.cycles/webaudio
|
||||
# @strudel/webaudio
|
||||
|
||||
This package contains helpers to make music with strudel and the Web Audio API.
|
||||
It is a thin binding to [superdough](https://www.npmjs.com/package/superdough).
|
||||
@@ -6,14 +6,14 @@ It is a thin binding to [superdough](https://www.npmjs.com/package/superdough).
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm i @strudel.cycles/webaudio --save
|
||||
npm i @strudel/webaudio --save
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
import { repl, controls } from "@strudel.cycles/core";
|
||||
import { initAudioOnFirstClick, getAudioContext, webaudioOutput } from "@strudel.cycles/webaudio";
|
||||
import { repl, controls } from "@strudel/core";
|
||||
import { initAudioOnFirstClick, getAudioContext, webaudioOutput } from "@strudel/webaudio";
|
||||
const { note } = controls;
|
||||
|
||||
initAudioOnFirstClick();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/webaudio",
|
||||
"version": "0.9.0",
|
||||
"name": "@strudel/webaudio",
|
||||
"version": "1.0.0",
|
||||
"description": "Web Audio helpers for Strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@@ -34,7 +34,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel/core": "workspace:*",
|
||||
"superdough": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Pattern, getDrawContext, clamp } from '@strudel.cycles/core';
|
||||
import { Pattern, getDrawContext, clamp } from '@strudel/core';
|
||||
import { analyser, getAnalyzerData } from 'superdough';
|
||||
|
||||
export function drawTimeScope(
|
||||
|
||||
@@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import * as strudel from '@strudel.cycles/core';
|
||||
import * as strudel from '@strudel/core';
|
||||
import { superdough, getAudioContext, setLogger, doughTrigger } from 'superdough';
|
||||
const { Pattern, logger } = strudel;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# @strudel.cycles/xen
|
||||
# @strudel/xen
|
||||
|
||||
This package adds xenharmonic / microtonal functions to strudel Patterns. Further documentation + examples will follow.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm i @strudel.cycles/xen --save
|
||||
npm i @strudel/xen --save
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/xen",
|
||||
"version": "0.9.0",
|
||||
"name": "@strudel/xen",
|
||||
"version": "1.0.0",
|
||||
"description": "Xenharmonic API for strudel",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
@@ -30,7 +30,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*"
|
||||
"@strudel/core": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^5.0.10",
|
||||
|
||||
@@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
*/
|
||||
|
||||
import Tune from './tunejs.js';
|
||||
import { register } from '@strudel.cycles/core';
|
||||
import { register } from '@strudel/core';
|
||||
|
||||
export const tune = register('tune', (scale, pat) => {
|
||||
const tune = new Tune();
|
||||
|
||||
@@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { register, _mod, parseNumeral } from '@strudel.cycles/core';
|
||||
import { register, _mod, parseNumeral } from '@strudel/core';
|
||||
|
||||
export function edo(name) {
|
||||
if (!/^[1-9]+[0-9]*edo$/.test(name)) {
|
||||
|
||||
Generated
+772
-565
File diff suppressed because it is too large
Load Diff
@@ -2,4 +2,5 @@ packages:
|
||||
# all packages in direct subdirs of packages/
|
||||
- "packages/*"
|
||||
- "examples/*"
|
||||
- "tools/dbpatch"
|
||||
- "website/"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# @strudel.cycles/tauri
|
||||
# @strudel/tauri
|
||||
|
||||
Rust source files for building native desktop apps using Tauri
|
||||
|
||||
|
||||
+3447
-1525
File diff suppressed because it is too large
Load Diff
@@ -339,402 +339,6 @@ exports[`renders tunes > tune: blippyRhodes 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders tunes > tune: bridgeIsOver 1`] = `
|
||||
[
|
||||
"[ -155/52 ⇜ (0/1 → 5/52) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ -75/26 ⇜ (0/1 → 5/26) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ -145/52 ⇜ (0/1 → 15/52) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ -35/13 ⇜ (0/1 → 5/13) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ -35/13 ⇜ (0/1 → 5/13) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ -135/52 ⇜ (0/1 → 5/13) ⇝ 25/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ -5/2 ⇜ (0/1 → 5/13) ⇝ 15/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ -125/52 ⇜ (0/1 → 5/13) ⇝ 35/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ -30/13 ⇜ (0/1 → 5/13) ⇝ 10/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ -115/52 ⇜ (0/1 → 5/13) ⇝ 45/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ -55/26 ⇜ (0/1 → 5/13) ⇝ 25/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ -105/52 ⇜ (0/1 → 5/13) ⇝ 55/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ 0/1 → 5/13 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]",
|
||||
"[ -135/52 ⇜ (0/1 → 25/52) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ -5/2 ⇜ (0/1 → 15/26) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ -125/52 ⇜ (0/1 → 35/52) | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ -30/13 ⇜ (0/1 → 10/13) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ -115/52 ⇜ (0/1 → 10/13) ⇝ 45/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ -55/26 ⇜ (0/1 → 10/13) ⇝ 25/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ -105/52 ⇜ (0/1 → 10/13) ⇝ 55/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ -25/13 ⇜ (0/1 → 10/13) ⇝ 15/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ -95/52 ⇜ (0/1 → 10/13) ⇝ 5/4 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ -45/26 ⇜ (0/1 → 10/13) ⇝ 35/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ -85/52 ⇜ (0/1 → 10/13) ⇝ 75/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ (0/1 → 1/1) ⇝ 40/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]",
|
||||
"[ (0/1 → 1/1) ⇝ 80/13 | s:mad ]",
|
||||
"[ (5/52 → 1/1) ⇝ 165/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ (5/26 → 1/1) ⇝ 85/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ (15/52 → 1/1) ⇝ 175/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ -135/52 ⇜ (5/13 → 25/52) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ -5/2 ⇜ (5/13 → 15/26) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ -125/52 ⇜ (5/13 → 35/52) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ -30/13 ⇜ (5/13 → 10/13) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 5/13 → 10/13 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]",
|
||||
"[ -115/52 ⇜ (5/13 → 45/52) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ -55/26 ⇜ (5/13 → 25/26) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ -105/52 ⇜ (5/13 → 1/1) ⇝ 55/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ (5/13 → 1/1) ⇝ 45/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ (5/13 → 1/1) ⇝ 45/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]",
|
||||
"[ (25/52 → 1/1) ⇝ 185/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ (25/52 → 1/1) ⇝ 185/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ (15/26 → 1/1) ⇝ 95/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ (15/26 → 1/1) ⇝ 95/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ (35/52 → 1/1) ⇝ 15/4 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ (35/52 → 1/1) ⇝ 15/4 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ -115/52 ⇜ (10/13 → 45/52) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ -55/26 ⇜ (10/13 → 25/26) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 10/13 → 155/156 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]",
|
||||
"[ -105/52 ⇜ (10/13 → 1/1) ⇝ 55/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ -25/13 ⇜ (10/13 → 1/1) ⇝ 15/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ -95/52 ⇜ (10/13 → 1/1) ⇝ 5/4 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ -45/26 ⇜ (10/13 → 1/1) ⇝ 35/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ -85/52 ⇜ (10/13 → 1/1) ⇝ 75/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ (10/13 → 1/1) ⇝ 50/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ (10/13 → 1/1) ⇝ 50/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]",
|
||||
"[ (45/52 → 1/1) ⇝ 205/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ (45/52 → 1/1) ⇝ 205/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ (25/26 → 1/1) ⇝ 105/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ (25/26 → 1/1) ⇝ 105/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ (155/156 → 1/1) ⇝ 15/13 | note:bb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.46296296296296297 ]",
|
||||
"[ -105/52 ⇜ (1/1 → 55/52) | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ -105/52 ⇜ (1/1 → 55/52) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ -25/13 ⇜ (1/1 → 15/13) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 155/156 ⇜ (1/1 → 15/13) | note:bb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.46296296296296297 ]",
|
||||
"[ -95/52 ⇜ (1/1 → 5/4) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ -45/26 ⇜ (1/1 → 35/26) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ -85/52 ⇜ (1/1 → 75/52) | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ 0/1 ⇜ (1/1 → 2/1) ⇝ 40/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]",
|
||||
"[ 0/1 ⇜ (1/1 → 2/1) ⇝ 80/13 | s:mad ]",
|
||||
"[ 5/52 ⇜ (1/1 → 2/1) ⇝ 165/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 5/26 ⇜ (1/1 → 2/1) ⇝ 85/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 15/52 ⇜ (1/1 → 2/1) ⇝ 175/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 5/13 ⇜ (1/1 → 2/1) ⇝ 45/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 5/13 ⇜ (1/1 → 2/1) ⇝ 45/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]",
|
||||
"[ 25/52 ⇜ (1/1 → 2/1) ⇝ 185/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 25/52 ⇜ (1/1 → 2/1) ⇝ 185/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 15/26 ⇜ (1/1 → 2/1) ⇝ 95/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 15/26 ⇜ (1/1 → 2/1) ⇝ 95/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 35/52 ⇜ (1/1 → 2/1) ⇝ 15/4 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 35/52 ⇜ (1/1 → 2/1) ⇝ 15/4 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 10/13 ⇜ (1/1 → 2/1) ⇝ 50/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 10/13 ⇜ (1/1 → 2/1) ⇝ 50/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]",
|
||||
"[ 45/52 ⇜ (1/1 → 2/1) ⇝ 205/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 45/52 ⇜ (1/1 → 2/1) ⇝ 205/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 25/26 ⇜ (1/1 → 2/1) ⇝ 105/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 25/26 ⇜ (1/1 → 2/1) ⇝ 105/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ (55/52 → 2/1) ⇝ 215/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ (55/52 → 2/1) ⇝ 215/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 15/13 → 20/13 | note:ab2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4537037037037037 ]",
|
||||
"[ (15/13 → 2/1) ⇝ 55/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ (5/4 → 2/1) ⇝ 225/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ (35/26 → 2/1) ⇝ 115/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ (75/52 → 2/1) ⇝ 235/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 20/13 → 25/13 | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]",
|
||||
"[ (25/13 → 2/1) ⇝ 30/13 | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]",
|
||||
"[ 25/13 ⇜ (2/1 → 30/13) | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]",
|
||||
"[ 0/1 ⇜ (2/1 → 3/1) ⇝ 40/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]",
|
||||
"[ 0/1 ⇜ (2/1 → 3/1) ⇝ 80/13 | s:mad ]",
|
||||
"[ 5/52 ⇜ (2/1 → 3/1) ⇝ 165/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 5/26 ⇜ (2/1 → 3/1) ⇝ 85/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 15/52 ⇜ (2/1 → 3/1) ⇝ 175/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 5/13 ⇜ (2/1 → 3/1) ⇝ 45/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 5/13 ⇜ (2/1 → 3/1) ⇝ 45/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]",
|
||||
"[ 25/52 ⇜ (2/1 → 3/1) ⇝ 185/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 25/52 ⇜ (2/1 → 3/1) ⇝ 185/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 15/26 ⇜ (2/1 → 3/1) ⇝ 95/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 15/26 ⇜ (2/1 → 3/1) ⇝ 95/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 35/52 ⇜ (2/1 → 3/1) ⇝ 15/4 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 35/52 ⇜ (2/1 → 3/1) ⇝ 15/4 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 10/13 ⇜ (2/1 → 3/1) ⇝ 50/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 10/13 ⇜ (2/1 → 3/1) ⇝ 50/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]",
|
||||
"[ 45/52 ⇜ (2/1 → 3/1) ⇝ 205/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 45/52 ⇜ (2/1 → 3/1) ⇝ 205/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 25/26 ⇜ (2/1 → 3/1) ⇝ 105/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 25/26 ⇜ (2/1 → 3/1) ⇝ 105/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 55/52 ⇜ (2/1 → 3/1) ⇝ 215/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 55/52 ⇜ (2/1 → 3/1) ⇝ 215/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 15/13 ⇜ (2/1 → 3/1) ⇝ 55/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 5/4 ⇜ (2/1 → 3/1) ⇝ 225/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 35/26 ⇜ (2/1 → 3/1) ⇝ 115/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 75/52 ⇜ (2/1 → 3/1) ⇝ 235/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 30/13 → 395/156 | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]",
|
||||
"[ 395/156 → 35/13 | note:ab2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4537037037037037 ]",
|
||||
"[ (35/13 → 3/1) ⇝ 40/13 | note:bb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.46296296296296297 ]",
|
||||
"[ 0/1 ⇜ (3/1 → 40/13) | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]",
|
||||
"[ 0/1 ⇜ (3/1 → 40/13) ⇝ 80/13 | s:mad ]",
|
||||
"[ 5/52 ⇜ (3/1 → 40/13) ⇝ 165/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 5/26 ⇜ (3/1 → 40/13) ⇝ 85/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 15/52 ⇜ (3/1 → 40/13) ⇝ 175/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 5/13 ⇜ (3/1 → 40/13) ⇝ 45/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 5/13 ⇜ (3/1 → 40/13) ⇝ 45/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]",
|
||||
"[ 25/52 ⇜ (3/1 → 40/13) ⇝ 185/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 25/52 ⇜ (3/1 → 40/13) ⇝ 185/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 15/26 ⇜ (3/1 → 40/13) ⇝ 95/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 15/26 ⇜ (3/1 → 40/13) ⇝ 95/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 35/52 ⇜ (3/1 → 40/13) ⇝ 15/4 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 35/52 ⇜ (3/1 → 40/13) ⇝ 15/4 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 10/13 ⇜ (3/1 → 40/13) ⇝ 50/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 10/13 ⇜ (3/1 → 40/13) ⇝ 50/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]",
|
||||
"[ 45/52 ⇜ (3/1 → 40/13) ⇝ 205/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 45/52 ⇜ (3/1 → 40/13) ⇝ 205/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 25/26 ⇜ (3/1 → 40/13) ⇝ 105/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 25/26 ⇜ (3/1 → 40/13) ⇝ 105/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 55/52 ⇜ (3/1 → 40/13) ⇝ 215/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 55/52 ⇜ (3/1 → 40/13) ⇝ 215/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 15/13 ⇜ (3/1 → 40/13) ⇝ 55/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 5/4 ⇜ (3/1 → 40/13) ⇝ 225/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 35/26 ⇜ (3/1 → 40/13) ⇝ 115/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 75/52 ⇜ (3/1 → 40/13) ⇝ 235/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 35/13 ⇜ (3/1 → 40/13) | note:bb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.46296296296296297 ]",
|
||||
"[ 5/52 ⇜ (40/13 → 165/52) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 5/26 ⇜ (40/13 → 85/26) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 15/52 ⇜ (40/13 → 175/52) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 5/13 ⇜ (40/13 → 45/13) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 5/13 ⇜ (40/13 → 45/13) | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]",
|
||||
"[ 25/52 ⇜ (40/13 → 45/13) ⇝ 185/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 15/26 ⇜ (40/13 → 45/13) ⇝ 95/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 35/52 ⇜ (40/13 → 45/13) ⇝ 15/4 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 10/13 ⇜ (40/13 → 45/13) ⇝ 50/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 45/52 ⇜ (40/13 → 45/13) ⇝ 205/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 25/26 ⇜ (40/13 → 45/13) ⇝ 105/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 55/52 ⇜ (40/13 → 45/13) ⇝ 215/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 40/13 → 45/13 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]",
|
||||
"[ 25/52 ⇜ (40/13 → 185/52) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 15/26 ⇜ (40/13 → 95/26) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 35/52 ⇜ (40/13 → 15/4) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 10/13 ⇜ (40/13 → 50/13) | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]",
|
||||
"[ 45/52 ⇜ (40/13 → 50/13) ⇝ 205/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 25/26 ⇜ (40/13 → 50/13) ⇝ 105/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 55/52 ⇜ (40/13 → 50/13) ⇝ 215/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 15/13 ⇜ (40/13 → 50/13) ⇝ 55/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 5/4 ⇜ (40/13 → 50/13) ⇝ 225/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 35/26 ⇜ (40/13 → 50/13) ⇝ 115/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 75/52 ⇜ (40/13 → 50/13) ⇝ 235/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 0/1 ⇜ (40/13 → 4/1) ⇝ 80/13 | s:mad ]",
|
||||
"[ (40/13 → 4/1) ⇝ 80/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ (165/52 → 4/1) ⇝ 25/4 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ (85/26 → 4/1) ⇝ 165/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ (175/52 → 4/1) ⇝ 335/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 25/52 ⇜ (45/13 → 185/52) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 15/26 ⇜ (45/13 → 95/26) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 35/52 ⇜ (45/13 → 15/4) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 10/13 ⇜ (45/13 → 50/13) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 45/13 → 50/13 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]",
|
||||
"[ 45/52 ⇜ (45/13 → 205/52) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 25/26 ⇜ (45/13 → 4/1) ⇝ 105/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 55/52 ⇜ (45/13 → 4/1) ⇝ 215/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ (45/13 → 4/1) ⇝ 85/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ (45/13 → 4/1) ⇝ 85/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ (185/52 → 4/1) ⇝ 345/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ (185/52 → 4/1) ⇝ 345/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ (95/26 → 4/1) ⇝ 175/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ (95/26 → 4/1) ⇝ 175/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ (15/4 → 4/1) ⇝ 355/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ (15/4 → 4/1) ⇝ 355/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 45/52 ⇜ (50/13 → 205/52) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 25/26 ⇜ (50/13 → 4/1) ⇝ 105/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 55/52 ⇜ (50/13 → 4/1) ⇝ 215/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 15/13 ⇜ (50/13 → 4/1) ⇝ 55/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 5/4 ⇜ (50/13 → 4/1) ⇝ 225/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 35/26 ⇜ (50/13 → 4/1) ⇝ 115/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 75/52 ⇜ (50/13 → 4/1) ⇝ 235/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ (50/13 → 4/1) ⇝ 635/156 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]",
|
||||
"[ (50/13 → 4/1) ⇝ 90/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ (50/13 → 4/1) ⇝ 90/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ (205/52 → 4/1) ⇝ 365/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ (205/52 → 4/1) ⇝ 365/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 25/26 ⇜ (4/1 → 105/26) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 25/26 ⇜ (4/1 → 105/26) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 50/13 ⇜ (4/1 → 635/156) | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]",
|
||||
"[ 55/52 ⇜ (4/1 → 215/52) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 55/52 ⇜ (4/1 → 215/52) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 15/13 ⇜ (4/1 → 55/13) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 5/4 ⇜ (4/1 → 225/52) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 35/26 ⇜ (4/1 → 115/26) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 75/52 ⇜ (4/1 → 235/52) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 0/1 ⇜ (4/1 → 5/1) ⇝ 80/13 | s:mad ]",
|
||||
"[ 40/13 ⇜ (4/1 → 5/1) ⇝ 80/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 165/52 ⇜ (4/1 → 5/1) ⇝ 25/4 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 85/26 ⇜ (4/1 → 5/1) ⇝ 165/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 175/52 ⇜ (4/1 → 5/1) ⇝ 335/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 45/13 ⇜ (4/1 → 5/1) ⇝ 85/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 45/13 ⇜ (4/1 → 5/1) ⇝ 85/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 185/52 ⇜ (4/1 → 5/1) ⇝ 345/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 185/52 ⇜ (4/1 → 5/1) ⇝ 345/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 95/26 ⇜ (4/1 → 5/1) ⇝ 175/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 95/26 ⇜ (4/1 → 5/1) ⇝ 175/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 15/4 ⇜ (4/1 → 5/1) ⇝ 355/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ 15/4 ⇜ (4/1 → 5/1) ⇝ 355/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 50/13 ⇜ (4/1 → 5/1) ⇝ 90/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 50/13 ⇜ (4/1 → 5/1) ⇝ 90/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 205/52 ⇜ (4/1 → 5/1) ⇝ 365/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 205/52 ⇜ (4/1 → 5/1) ⇝ 365/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ (105/26 → 5/1) ⇝ 185/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ (105/26 → 5/1) ⇝ 185/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 635/156 → 55/13 | note:bb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.46296296296296297 ]",
|
||||
"[ (215/52 → 5/1) ⇝ 375/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ (215/52 → 5/1) ⇝ 375/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 55/13 → 60/13 | note:ab2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4537037037037037 ]",
|
||||
"[ (55/13 → 5/1) ⇝ 95/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ (225/52 → 5/1) ⇝ 385/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ (115/26 → 5/1) ⇝ 15/2 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ (235/52 → 5/1) ⇝ 395/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ 60/13 → 5/1 | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]",
|
||||
"[ 5/1 → 70/13 | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]",
|
||||
"[ 0/1 ⇜ (5/1 → 6/1) ⇝ 80/13 | s:mad ]",
|
||||
"[ 40/13 ⇜ (5/1 → 6/1) ⇝ 80/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 165/52 ⇜ (5/1 → 6/1) ⇝ 25/4 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 85/26 ⇜ (5/1 → 6/1) ⇝ 165/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 175/52 ⇜ (5/1 → 6/1) ⇝ 335/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 45/13 ⇜ (5/1 → 6/1) ⇝ 85/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 45/13 ⇜ (5/1 → 6/1) ⇝ 85/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 185/52 ⇜ (5/1 → 6/1) ⇝ 345/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 185/52 ⇜ (5/1 → 6/1) ⇝ 345/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 95/26 ⇜ (5/1 → 6/1) ⇝ 175/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 95/26 ⇜ (5/1 → 6/1) ⇝ 175/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 15/4 ⇜ (5/1 → 6/1) ⇝ 355/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ 15/4 ⇜ (5/1 → 6/1) ⇝ 355/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 50/13 ⇜ (5/1 → 6/1) ⇝ 90/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 50/13 ⇜ (5/1 → 6/1) ⇝ 90/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 205/52 ⇜ (5/1 → 6/1) ⇝ 365/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 205/52 ⇜ (5/1 → 6/1) ⇝ 365/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 105/26 ⇜ (5/1 → 6/1) ⇝ 185/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 105/26 ⇜ (5/1 → 6/1) ⇝ 185/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 215/52 ⇜ (5/1 → 6/1) ⇝ 375/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ 215/52 ⇜ (5/1 → 6/1) ⇝ 375/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 55/13 ⇜ (5/1 → 6/1) ⇝ 95/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 225/52 ⇜ (5/1 → 6/1) ⇝ 385/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 115/26 ⇜ (5/1 → 6/1) ⇝ 15/2 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 235/52 ⇜ (5/1 → 6/1) ⇝ 395/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ (70/13 → 6/1) ⇝ 80/13 | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]",
|
||||
"[ 0/1 ⇜ (6/1 → 80/13) | s:mad ]",
|
||||
"[ 40/13 ⇜ (6/1 → 80/13) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 165/52 ⇜ (6/1 → 80/13) ⇝ 25/4 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 85/26 ⇜ (6/1 → 80/13) ⇝ 165/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 175/52 ⇜ (6/1 → 80/13) ⇝ 335/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 45/13 ⇜ (6/1 → 80/13) ⇝ 85/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 45/13 ⇜ (6/1 → 80/13) ⇝ 85/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 185/52 ⇜ (6/1 → 80/13) ⇝ 345/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 185/52 ⇜ (6/1 → 80/13) ⇝ 345/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 95/26 ⇜ (6/1 → 80/13) ⇝ 175/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 95/26 ⇜ (6/1 → 80/13) ⇝ 175/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 15/4 ⇜ (6/1 → 80/13) ⇝ 355/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ 15/4 ⇜ (6/1 → 80/13) ⇝ 355/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 50/13 ⇜ (6/1 → 80/13) ⇝ 90/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 50/13 ⇜ (6/1 → 80/13) ⇝ 90/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 205/52 ⇜ (6/1 → 80/13) ⇝ 365/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 205/52 ⇜ (6/1 → 80/13) ⇝ 365/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 105/26 ⇜ (6/1 → 80/13) ⇝ 185/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 105/26 ⇜ (6/1 → 80/13) ⇝ 185/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 215/52 ⇜ (6/1 → 80/13) ⇝ 375/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ 215/52 ⇜ (6/1 → 80/13) ⇝ 375/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 55/13 ⇜ (6/1 → 80/13) ⇝ 95/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 225/52 ⇜ (6/1 → 80/13) ⇝ 385/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 115/26 ⇜ (6/1 → 80/13) ⇝ 15/2 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 235/52 ⇜ (6/1 → 80/13) ⇝ 395/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ 70/13 ⇜ (6/1 → 80/13) | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]",
|
||||
"[ 165/52 ⇜ (80/13 → 25/4) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 85/26 ⇜ (80/13 → 165/26) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 175/52 ⇜ (80/13 → 335/52) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 45/13 ⇜ (80/13 → 85/13) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 45/13 ⇜ (80/13 → 85/13) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 185/52 ⇜ (80/13 → 85/13) ⇝ 345/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 95/26 ⇜ (80/13 → 85/13) ⇝ 175/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 15/4 ⇜ (80/13 → 85/13) ⇝ 355/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 50/13 ⇜ (80/13 → 85/13) ⇝ 90/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 205/52 ⇜ (80/13 → 85/13) ⇝ 365/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 105/26 ⇜ (80/13 → 85/13) ⇝ 185/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 215/52 ⇜ (80/13 → 85/13) ⇝ 375/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ 80/13 → 85/13 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]",
|
||||
"[ 185/52 ⇜ (80/13 → 345/52) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 95/26 ⇜ (80/13 → 175/26) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 15/4 ⇜ (80/13 → 355/52) | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ 50/13 ⇜ (80/13 → 90/13) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]",
|
||||
"[ 205/52 ⇜ (80/13 → 90/13) ⇝ 365/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 105/26 ⇜ (80/13 → 90/13) ⇝ 185/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 215/52 ⇜ (80/13 → 90/13) ⇝ 375/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 55/13 ⇜ (80/13 → 90/13) ⇝ 95/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 225/52 ⇜ (80/13 → 90/13) ⇝ 385/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 115/26 ⇜ (80/13 → 90/13) ⇝ 15/2 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 235/52 ⇜ (80/13 → 90/13) ⇝ 395/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ (80/13 → 7/1) ⇝ 120/13 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ (80/13 → 7/1) ⇝ 160/13 | s:mad ]",
|
||||
"[ (25/4 → 7/1) ⇝ 485/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ (165/26 → 7/1) ⇝ 245/26 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ (335/52 → 7/1) ⇝ 495/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 185/52 ⇜ (85/13 → 345/52) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 95/26 ⇜ (85/13 → 175/26) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 15/4 ⇜ (85/13 → 355/52) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 50/13 ⇜ (85/13 → 90/13) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 85/13 → 90/13 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]",
|
||||
"[ 205/52 ⇜ (85/13 → 7/1) ⇝ 365/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 105/26 ⇜ (85/13 → 7/1) ⇝ 185/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 215/52 ⇜ (85/13 → 7/1) ⇝ 375/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ (85/13 → 7/1) ⇝ 125/13 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ (85/13 → 7/1) ⇝ 125/13 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ (345/52 → 7/1) ⇝ 505/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ (345/52 → 7/1) ⇝ 505/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ (175/26 → 7/1) ⇝ 255/26 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ (175/26 → 7/1) ⇝ 255/26 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ (355/52 → 7/1) ⇝ 515/52 | clip:1 note:F#5 s:piano release:0.1 pan:0.6111111111111112 ]",
|
||||
"[ (355/52 → 7/1) ⇝ 515/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 205/52 ⇜ (90/13 → 7/1) ⇝ 365/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 105/26 ⇜ (90/13 → 7/1) ⇝ 185/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 215/52 ⇜ (90/13 → 7/1) ⇝ 375/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 55/13 ⇜ (90/13 → 7/1) ⇝ 95/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 225/52 ⇜ (90/13 → 7/1) ⇝ 385/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 115/26 ⇜ (90/13 → 7/1) ⇝ 15/2 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 235/52 ⇜ (90/13 → 7/1) ⇝ 395/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ (90/13 → 7/1) ⇝ 1115/156 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]",
|
||||
"[ (90/13 → 7/1) ⇝ 10/1 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ (90/13 → 7/1) ⇝ 10/1 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 205/52 ⇜ (7/1 → 365/52) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 205/52 ⇜ (7/1 → 365/52) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 105/26 ⇜ (7/1 → 185/26) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 105/26 ⇜ (7/1 → 185/26) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 90/13 ⇜ (7/1 → 1115/156) | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]",
|
||||
"[ 215/52 ⇜ (7/1 → 375/52) | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ 215/52 ⇜ (7/1 → 375/52) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 55/13 ⇜ (7/1 → 95/13) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 225/52 ⇜ (7/1 → 385/52) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 115/26 ⇜ (7/1 → 15/2) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 235/52 ⇜ (7/1 → 395/52) | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ 80/13 ⇜ (7/1 → 8/1) ⇝ 120/13 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 80/13 ⇜ (7/1 → 8/1) ⇝ 160/13 | s:mad ]",
|
||||
"[ 25/4 ⇜ (7/1 → 8/1) ⇝ 485/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 165/26 ⇜ (7/1 → 8/1) ⇝ 245/26 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 335/52 ⇜ (7/1 → 8/1) ⇝ 495/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 85/13 ⇜ (7/1 → 8/1) ⇝ 125/13 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 85/13 ⇜ (7/1 → 8/1) ⇝ 125/13 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ 345/52 ⇜ (7/1 → 8/1) ⇝ 505/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ 345/52 ⇜ (7/1 → 8/1) ⇝ 505/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ 175/26 ⇜ (7/1 → 8/1) ⇝ 255/26 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ 175/26 ⇜ (7/1 → 8/1) ⇝ 255/26 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 355/52 ⇜ (7/1 → 8/1) ⇝ 515/52 | clip:1 note:F#5 s:piano release:0.1 pan:0.6111111111111112 ]",
|
||||
"[ 355/52 ⇜ (7/1 → 8/1) ⇝ 515/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 90/13 ⇜ (7/1 → 8/1) ⇝ 10/1 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ 90/13 ⇜ (7/1 → 8/1) ⇝ 10/1 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]",
|
||||
"[ (365/52 → 8/1) ⇝ 525/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ (365/52 → 8/1) ⇝ 525/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]",
|
||||
"[ (185/26 → 8/1) ⇝ 265/26 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ (185/26 → 8/1) ⇝ 265/26 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]",
|
||||
"[ 1115/156 → 95/13 | note:bb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.46296296296296297 ]",
|
||||
"[ (375/52 → 8/1) ⇝ 535/52 | clip:1 note:F#5 s:piano release:0.1 pan:0.6111111111111112 ]",
|
||||
"[ (375/52 → 8/1) ⇝ 535/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]",
|
||||
"[ 95/13 → 100/13 | note:ab2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4537037037037037 ]",
|
||||
"[ (95/13 → 8/1) ⇝ 135/13 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]",
|
||||
"[ (385/52 → 8/1) ⇝ 545/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]",
|
||||
"[ (15/2 → 8/1) ⇝ 275/26 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]",
|
||||
"[ (395/52 → 8/1) ⇝ 555/52 | clip:1 note:F#5 s:piano release:0.1 pan:0.6111111111111112 ]",
|
||||
"[ (100/13 → 8/1) ⇝ 105/13 | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders tunes > tune: caverave 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | s:bd gain:0.8 ]",
|
||||
@@ -1507,14 +1111,14 @@ exports[`renders tunes > tune: caverave 1`] = `
|
||||
|
||||
exports[`renders tunes > tune: chop 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | s:p speed:0.03125 unit:c begin:0 end:0.0078125 pan:0 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
"[ 0/1 → 1/4 | s:p speed:0.03125 unit:c begin:0.0234375 end:0.03125 pan:1 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
"[ 1/4 → 1/2 | s:p speed:0.03125 unit:c begin:0.0078125 end:0.015625 pan:0 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
"[ 1/4 → 1/2 | s:p speed:0.03125 unit:c begin:0.015625 end:0.0234375 pan:1 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
"[ 1/2 → 3/4 | s:p speed:0.03125 unit:c begin:0.015625 end:0.0234375 pan:0 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
"[ 1/2 → 3/4 | s:p speed:0.03125 unit:c begin:0.0078125 end:0.015625 pan:1 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
"[ 3/4 → 1/1 | s:p speed:0.03125 unit:c begin:0.0234375 end:0.03125 pan:0 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
"[ 3/4 → 1/1 | s:p speed:0.03125 unit:c begin:0 end:0.0078125 pan:1 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
"[ 0/1 → 1/4 | s:p speed:0.015625 unit:c begin:0 end:0.0078125 pan:0 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
"[ 0/1 → 1/4 | s:p speed:0.015625 unit:c begin:0.0234375 end:0.03125 pan:1 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
"[ 1/4 → 1/2 | s:p speed:0.015625 unit:c begin:0.0078125 end:0.015625 pan:0 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
"[ 1/4 → 1/2 | s:p speed:0.015625 unit:c begin:0.015625 end:0.0234375 pan:1 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
"[ 1/2 → 3/4 | s:p speed:0.015625 unit:c begin:0.015625 end:0.0234375 pan:0 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
"[ 1/2 → 3/4 | s:p speed:0.015625 unit:c begin:0.0078125 end:0.015625 pan:1 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
"[ 3/4 → 1/1 | s:p speed:0.015625 unit:c begin:0.0234375 end:0.03125 pan:0 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
"[ 3/4 → 1/1 | s:p speed:0.015625 unit:c begin:0 end:0.0078125 pan:1 shape:0.4 decay:0.1 sustain:0.6 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -1545,7 +1149,7 @@ exports[`renders tunes > tune: dinofunk 1`] = `
|
||||
"[ 0/1 → 1/4 | note:Ab4 s:sawtooth cutoff:1239.2541394619345 gain:0.8 decay:0.05125097280354112 sustain:0 delay:0.2561353071307281 room:1 ]",
|
||||
"[ 0/1 → 1/4 | note:68.1 s:sawtooth cutoff:1239.2541394619345 gain:0.8 decay:0.05125097280354112 sustain:0 delay:0.2561353071307281 room:1 ]",
|
||||
"[ 0/1 → 1/2 | s:bd ]",
|
||||
"[ (0/1 → 1/1) ⇝ 8/1 | s:bass speed:0.125 unit:c clip:1 ]",
|
||||
"[ (0/1 → 1/1) ⇝ 8/1 | s:bass speed:0.0625 unit:c clip:1 ]",
|
||||
"[ (0/1 → 1/1) ⇝ 8/1 | note:b4 s:dino delay:0.8 room:0.5 ]",
|
||||
"[ 1/4 → 1/2 | s:hh ]",
|
||||
"[ 1/4 → 1/2 | note:Gb3 ]",
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
|
||||
const supabase = createClient(
|
||||
'https://pidxdsxphlhzjnzmifth.supabase.co',
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM',
|
||||
);
|
||||
|
||||
const { data } = await supabase.from('code');
|
||||
|
||||
console.log(JSON.stringify(data));
|
||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user