Compare commits

..

21 Commits

Author SHA1 Message Date
Jade Rowland 1bae027aaa Merge branch 'tidalcycles:main' into ableton_link_integration 2023-10-05 23:07:41 -04:00
Felix Roos 3a69a2561c half working cps link 2023-10-02 21:21:11 +02:00
Felix Roos 30a0059ef3 remove todo 2023-10-02 20:59:35 +02:00
Felix Roos b1fcf6ca34 cleanup 2023-10-02 20:59:10 +02:00
Felix Roos f2534310e8 fix: default cyclist 2023-10-02 20:57:25 +02:00
Felix Roos 25be8fca0d encapsulate cyclist 2023-10-02 20:56:45 +02:00
Felix Roos f089037f18 add cmake instructions 2023-10-02 08:35:41 +02:00
Jade Rowland e818ca9f03 Merge branch 'tidalcycles:main' into ableton_link_integration 2023-10-01 20:44:09 -04:00
Jade Rowland 0efcf84c46 cleaning up 2023-10-01 20:30:47 -04:00
Jade Rowland 5db39b8e9e fix clock being set to 0 2023-10-01 19:45:44 -04:00
Jade Rowland 2127a8f642 cleaning things up 2023-10-01 19:19:49 -04:00
Jade Rowland 1d85d60b11 cleaning up 2023-10-01 00:46:40 -04:00
Jade Rowland d5c6cb85ea use cps instead of bpm 2023-09-30 22:18:31 -04:00
Jade Rowland fe8c4e2bcd cyclist bridge 2023-09-29 23:27:43 -04:00
Jade Rowland 6eda7af14b play sync now accurate 2023-09-29 01:01:01 -04:00
Jade Rowland 6719c6127b figuring out backend integration 2023-09-27 15:28:23 -04:00
Jade Rowland 172006a3b3 improving listener 2023-09-25 22:32:31 -04:00
Jade Rowland 209122b8f7 start offset 2023-09-25 19:29:14 -04:00
Jade Rowland d7fe491c84 play stop working 2023-09-22 14:27:03 -04:00
Jade Rowland db36636ab2 play working kinda 2023-09-21 23:08:30 -04:00
Jade Rowland 789cad3e97 figuring things out 2023-09-21 22:57:05 -04:00
130 changed files with 4267 additions and 8156 deletions
+1 -2
View File
@@ -19,5 +19,4 @@ vite.config.js
**/dev-dist
**/dist
/src-tauri/target/**/*
reverbGen.mjs
hydra.mjs
reverbGen.mjs
+3 -3
View File
@@ -13,7 +13,7 @@ To get in touch with the contributors, either
## Ask a Question
If you have any questions about strudel, make sure you've glanced through the
[docs](https://strudel.cc/learn/) to find out if it answers your question.
[docs](https://strudel.tidalcycles.org/learn/) to find out if it answers your question.
If not, use one of the Communication Channels above!
Don't be afraid to ask! Your question might be of great value for other people too.
@@ -31,7 +31,7 @@ Use one of the Communication Channels listed above.
## Improve the Docs
If you find some weak spots in the [docs](https://strudel.cc/workshop/getting-started/),
If you find some weak spots in the [docs](https://strudel.tidalcycles.org/workshop/getting-started/),
you can edit each file directly on github via the "Edit this page" link located in the right sidebar.
## Propose a Feature
@@ -83,7 +83,7 @@ Please report any problems you've had with the setup instructions!
To make sure the code changes only where it should, we are using prettier to unify the code style.
- You can format all files at once by running `pnpm codeformat` from the project root
- You can format all files at once by running `pnpm prettier` from the project root
- Run `pnpm format-check` from the project root to check if all files are well formatted
If you use VSCode, you can
+2 -2
View File
@@ -4,8 +4,8 @@
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.
- Try it here: <https://strudel.cc>
- Docs: <https://strudel.cc/learn>
- Try it here: <https://strudel.tidalcycles.org/>
- Docs: <https://strudel.tidalcycles.org/learn/>
- Technical Blog Post: <https://loophole-letters.vercel.app/strudel>
- 1 Year of Strudel Blog Post: <https://loophole-letters.vercel.app/strudel1year>
+1 -1
View File
@@ -47,7 +47,7 @@ If you want to automatically deploy your site on push, go to `deploy.yml` and ch
## running locally
- install dependencies with `npm run setup`
- run dev server with `npm run repl` and open `http://localhost:4321/strudel/swatch/`
- run dev server with `npm run repl` and open `http://localhost:3000/strudel/swatch/`
## tests fail?
+1 -1
View File
@@ -43,7 +43,7 @@
"bugs": {
"url": "https://github.com/tidalcycles/strudel/issues"
},
"homepage": "https://strudel.cc",
"homepage": "https://strudel.tidalcycles.org",
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/mini": "workspace:*",
-88
View File
@@ -1,88 +0,0 @@
import { createRoot } from 'react-dom/client';
import jsdoc from '../../doc.json';
// import { javascriptLanguage } from '@codemirror/lang-javascript';
import { autocompletion } from '@codemirror/autocomplete';
const getDocLabel = (doc) => doc.name || doc.longname;
const getInnerText = (html) => {
var div = document.createElement('div');
div.innerHTML = html;
return div.textContent || div.innerText || '';
};
export function Autocomplete({ doc }) {
return (
<div className="prose dark:prose-invert max-h-[400px] overflow-auto">
<h3 className="pt-0 mt-0">{getDocLabel(doc)}</h3>
<div dangerouslySetInnerHTML={{ __html: doc.description }} />
<ul>
{doc.params?.map(({ name, type, description }, i) => (
<li key={i}>
{name} : {type.names?.join(' | ')} {description ? <> - {getInnerText(description)}</> : ''}
</li>
))}
</ul>
<div>
{doc.examples?.map((example, i) => (
<div key={i}>
<pre
className="cursor-pointer"
onMouseDown={(e) => {
console.log('ola!');
navigator.clipboard.writeText(example);
e.stopPropagation();
}}
>
{example}
</pre>
</div>
))}
</div>
</div>
);
}
const jsdocCompletions = jsdoc.docs
.filter(
(doc) =>
getDocLabel(doc) &&
!getDocLabel(doc).startsWith('_') &&
!['package'].includes(doc.kind) &&
!['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag)),
)
// https://codemirror.net/docs/ref/#autocomplete.Completion
.map((doc) /*: Completion */ => ({
label: getDocLabel(doc),
// detail: 'xxx', // An optional short piece of information to show (with a different style) after the label.
info: () => {
const node = document.createElement('div');
// if Autocomplete is non-interactive, it could also be rendered at build time..
// .. using renderToStaticMarkup
createRoot(node).render(<Autocomplete doc={doc} />);
return node;
},
type: 'function', // https://codemirror.net/docs/ref/#autocomplete.Completion.type
}));
export const strudelAutocomplete = (context /* : CompletionContext */) => {
let word = context.matchBefore(/\w*/);
if (word.from == word.to && !context.explicit) return null;
return {
from: word.from,
options: jsdocCompletions,
/* options: [
{ label: 'match', type: 'keyword' },
{ label: 'hello', type: 'variable', info: '(World)' },
{ label: 'magic', type: 'text', apply: '⠁⭒*.✩.*⭒⠁', detail: 'macro' },
], */
};
};
export function isAutoCompletionEnabled(on) {
return on
? [
autocompletion({ override: [strudelAutocomplete] }),
//javascriptLanguage.data.of({ autocomplete: strudelAutocomplete }),
]
: []; // autocompletion({ override: [] })
}
+40 -169
View File
@@ -1,78 +1,36 @@
import { closeBrackets } from '@codemirror/autocomplete';
// import { search, highlightSelectionMatches } from '@codemirror/search';
import { history } from '@codemirror/commands';
import { defaultKeymap } from '@codemirror/commands';
import { javascript } from '@codemirror/lang-javascript';
import { defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language';
import { Compartment, EditorState } from '@codemirror/state';
import { EditorView, highlightActiveLineGutter, highlightActiveLine, keymap, lineNumbers } from '@codemirror/view';
import { Pattern, Drawer, repl, cleanupDraw } from '@strudel.cycles/core';
import { isAutoCompletionEnabled } from './Autocomplete';
import { flash, isFlashEnabled } from './flash.mjs';
import { highlightMiniLocations, isPatternHighlightingEnabled, updateMiniLocations } from './highlight.mjs';
import { keybindings } from './keybindings.mjs';
import { theme } from './themes.mjs';
import { updateWidgets, sliderPlugin } from './slider.mjs';
const extensions = {
isLineWrappingEnabled: (on) => (on ? EditorView.lineWrapping : []),
isLineNumbersDisplayed: (on) => (on ? lineNumbers() : []),
theme,
isAutoCompletionEnabled,
isPatternHighlightingEnabled,
isFlashEnabled,
keybindings,
};
const compartments = Object.fromEntries(Object.keys(extensions).map((key) => [key, new Compartment()]));
import { EditorState } from '@codemirror/state';
import { EditorView, highlightActiveLineGutter, keymap, lineNumbers } from '@codemirror/view';
import { Drawer, repl } from '@strudel.cycles/core';
import { flashField, flash } from './flash.mjs';
import { highlightExtension, highlightMiniLocations } from './highlight.mjs';
import { oneDark } from './themes/one-dark';
// https://codemirror.net/docs/guide/
export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, settings, root }) {
const initialSettings = Object.keys(compartments).map((key) =>
compartments[key].of(extensions[key](parseBooleans(settings[key]))),
);
export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, theme = oneDark, root }) {
let state = EditorState.create({
doc: initialCode,
extensions: [
/* search(),
highlightSelectionMatches(), */
...initialSettings,
theme,
javascript(),
sliderPlugin,
// indentOnInput(), // works without. already brought with javascript extension?
// bracketMatching(), // does not do anything
closeBrackets(),
lineNumbers(),
highlightExtension,
highlightActiveLineGutter(),
highlightActiveLine(),
syntaxHighlighting(defaultHighlightStyle),
history(),
keymap.of(defaultKeymap),
flashField,
EditorView.updateListener.of((v) => onChange(v)),
keymap.of([
{
key: 'Ctrl-Enter',
run: () => onEvaluate?.(),
},
{
key: 'Alt-Enter',
run: () => onEvaluate?.(),
run: () => onEvaluate(),
},
{
key: 'Ctrl-.',
run: () => onStop?.(),
run: () => onStop(),
},
{
key: 'Alt-.',
run: (_, e) => {
e.preventDefault();
onStop?.();
},
},
/* {
key: 'Ctrl-Shift-.',
run: () => (onPanic ? onPanic() : onStop?.()),
},
{
key: 'Ctrl-Shift-Enter',
run: () => (onReEvaluate ? onReEvaluate() : onEvaluate?.()),
}, */
]),
],
});
@@ -85,158 +43,71 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, set
export class StrudelMirror {
constructor(options) {
const { root, initialCode = '', onDraw, drawTime = [-2, 2], prebake, settings, ...replOptions } = options;
const { root, initialCode = '', onDraw, drawTime = [-2, 2], prebake, ...replOptions } = options;
this.code = initialCode;
this.root = root;
this.miniLocations = [];
this.widgets = [];
this.painters = [];
this.onDraw = onDraw;
const self = this;
this.drawer = new Drawer((haps, time) => {
const currentFrame = haps.filter((hap) => time >= hap.whole.begin && time <= hap.endClipped);
this.highlight(currentFrame, time);
this.onDraw?.(haps, time, currentFrame, this.painters);
onDraw?.(haps, time, currentFrame);
}, drawTime);
// this approach might not work with multiple repls on screen..
Pattern.prototype.onPaint = function (onPaint) {
self.painters.push(onPaint);
return this;
};
this.prebaked = prebake();
// this.drawFirstFrame();
const prebaked = prebake();
prebaked.then(async () => {
if (!onDraw) {
return;
}
const { scheduler, evaluate } = await this.repl;
// draw first frame instantly
prebaked.then(async () => {
await evaluate(this.code, false);
this.drawer.invalidate(scheduler);
onDraw?.(this.drawer.visibleHaps, 0, []);
});
});
this.repl = repl({
...replOptions,
onToggle: (started) => {
onToggle: async (started) => {
replOptions?.onToggle?.(started);
const { scheduler } = await this.repl;
if (started) {
this.drawer.start(this.repl.scheduler);
this.drawer.start(scheduler);
} else {
this.drawer.stop();
updateMiniLocations(this.editor, []);
cleanupDraw(false);
}
},
beforeEval: async () => {
cleanupDraw();
this.painters = [];
await this.prebaked;
await replOptions?.beforeEval?.();
await prebaked;
},
afterEval: (options) => {
// remember for when highlighting is toggled on
this.miniLocations = options.meta?.miniLocations;
this.widgets = options.meta?.widgets;
updateWidgets(this.editor, this.widgets);
updateMiniLocations(this.editor, this.miniLocations);
replOptions?.afterEval?.(options);
this.drawer.invalidate();
},
});
this.editor = initEditor({
root,
settings,
initialCode,
onChange: (v) => {
if (v.docChanged) {
this.code = v.state.doc.toString();
this.repl.setCode(this.code);
}
this.code = v.state.doc.toString();
},
onEvaluate: () => this.evaluate(),
onStop: () => this.stop(),
});
}
async drawFirstFrame() {
if (!this.onDraw) {
return;
}
// draw first frame instantly
await this.prebaked;
try {
await this.repl.evaluate(this.code, false);
this.drawer.invalidate(this.repl.scheduler);
this.onDraw?.(this.drawer.visibleHaps, 0, []);
} catch (err) {
console.warn('first frame could not be painted');
}
}
async evaluate() {
const { evaluate } = await this.repl;
this.flash();
await this.repl.evaluate(this.code);
await evaluate(this.code);
}
async stop() {
this.repl.scheduler.stop();
}
async toggle() {
if (this.repl.scheduler.started) {
this.repl.scheduler.stop();
} else {
this.evaluate();
}
const { scheduler } = await this.repl;
scheduler.stop();
}
flash(ms) {
flash(this.editor, ms);
}
highlight(haps, time) {
highlightMiniLocations(this.editor, time, haps);
}
setFontSize(size) {
this.root.style.fontSize = size + 'px';
}
setFontFamily(family) {
this.root.style.fontFamily = family;
}
reconfigureExtension(key, value) {
if (!extensions[key]) {
console.warn(`extension ${key} is not known`);
return;
}
value = parseBooleans(value);
const newValue = extensions[key](value, this);
this.editor.dispatch({
effects: compartments[key].reconfigure(newValue),
});
}
setLineWrappingEnabled(enabled) {
this.reconfigureExtension('isLineWrappingEnabled', enabled);
}
setLineNumbersDisplayed(enabled) {
this.reconfigureExtension('isLineNumbersDisplayed', enabled);
}
setTheme(theme) {
this.reconfigureExtension('theme', theme);
}
setAutocompletionEnabled(enabled) {
this.reconfigureExtension('isAutoCompletionEnabled', enabled);
}
updateSettings(settings) {
this.setFontSize(settings.fontSize);
this.setFontFamily(settings.fontFamily);
for (let key in extensions) {
this.reconfigureExtension(key, settings[key]);
}
}
changeSetting(key, value) {
if (extensions[key]) {
this.reconfigureExtension(key, value);
return;
} else if (key === 'fontFamily') {
this.setFontFamily(value);
} else if (key === 'fontSize') {
this.setFontSize(value);
}
}
setCode(code) {
const changes = { from: 0, to: this.editor.state.doc.length, insert: code };
this.editor.dispatch({ changes });
highlightMiniLocations(this.editor.view, time, haps);
}
}
function parseBooleans(value) {
return { true: true, false: false }[value] ?? value;
}
-2
View File
@@ -33,5 +33,3 @@ export const flash = (view, ms = 200) => {
view.dispatch({ effects: setFlash.of(false) });
}, ms);
};
export const isFlashEnabled = (on) => (on ? flashField : []);
-9
View File
@@ -124,12 +124,3 @@ const miniLocationHighlights = EditorView.decorations.compute([miniLocations, vi
});
export const highlightExtension = [miniLocations, visibleMiniLocations, miniLocationHighlights];
export const isPatternHighlightingEnabled = (on, config) => {
on &&
config &&
setTimeout(() => {
updateMiniLocations(config.editor, config.miniLocations);
}, 100);
return on ? highlightExtension : [];
};
-31
View File
@@ -1,31 +0,0 @@
import { Prec } from '@codemirror/state';
import { keymap, ViewPlugin } from '@codemirror/view';
// import { searchKeymap } from '@codemirror/search';
import { emacs } from '@replit/codemirror-emacs';
import { vim } from '@replit/codemirror-vim';
import { vscodeKeymap } from '@replit/codemirror-vscode-keymap';
import { defaultKeymap, historyKeymap } from '@codemirror/commands';
const vscodePlugin = ViewPlugin.fromClass(
class {
constructor() {}
},
{
provide: () => {
return Prec.highest(keymap.of([...vscodeKeymap]));
},
},
);
const vscodeExtension = (options) => [vscodePlugin].concat(options ?? []);
const keymaps = {
vim,
emacs,
vscode: vscodeExtension,
};
export function keybindings(name) {
const active = keymaps[name];
return [keymap.of(defaultKeymap), keymap.of(historyKeymap), active ? active() : []];
// keymap.of(searchKeymap),
}
+1 -9
View File
@@ -33,21 +33,13 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@codemirror/autocomplete": "^6.6.0",
"@codemirror/commands": "^6.2.4",
"@codemirror/lang-javascript": "^6.1.7",
"@codemirror/language": "^6.6.0",
"@codemirror/search": "^6.0.0",
"@codemirror/state": "^6.2.0",
"@codemirror/view": "^6.10.0",
"@lezer/highlight": "^1.1.4",
"@replit/codemirror-emacs": "^6.0.1",
"@replit/codemirror-vim": "^6.0.14",
"@replit/codemirror-vscode-keymap": "^6.0.2",
"@strudel.cycles/core": "workspace:*",
"@uiw/codemirror-themes": "^4.19.16",
"@uiw/codemirror-themes-all": "^4.19.16",
"react-dom": "^18.2.0"
"@strudel.cycles/core": "workspace:*"
},
"devDependencies": {
"vite": "^4.3.3"
-484
View File
@@ -1,484 +0,0 @@
import {
abcdef,
androidstudio,
atomone,
aura,
bespin,
darcula,
dracula,
duotoneDark,
eclipse,
githubDark,
gruvboxDark,
materialDark,
nord,
okaidia,
solarizedDark,
sublime,
tokyoNight,
tokyoNightStorm,
vscodeDark,
xcodeDark,
bbedit,
duotoneLight,
githubLight,
gruvboxLight,
materialLight,
noctisLilac,
solarizedLight,
tokyoNightDay,
xcodeLight,
} from '@uiw/codemirror-themes-all';
import strudelTheme from './themes/strudel-theme';
import bluescreen, { settings as bluescreenSettings } from './themes/bluescreen';
import blackscreen, { settings as blackscreenSettings } from './themes/blackscreen';
import whitescreen, { settings as whitescreenSettings } from './themes/whitescreen';
import teletext, { settings as teletextSettings } from './themes/teletext';
import algoboy, { settings as algoboySettings } from './themes/algoboy';
import terminal, { settings as terminalSettings } from './themes/terminal';
export const themes = {
strudelTheme,
bluescreen,
blackscreen,
whitescreen,
teletext,
algoboy,
terminal,
abcdef,
androidstudio,
atomone,
aura,
bespin,
darcula,
dracula,
duotoneDark,
eclipse,
githubDark,
gruvboxDark,
materialDark,
nord,
okaidia,
solarizedDark,
sublime,
tokyoNight,
tokyoNightStorm,
vscodeDark,
xcodeDark,
bbedit,
duotoneLight,
githubLight,
gruvboxLight,
materialLight,
noctisLilac,
solarizedLight,
tokyoNightDay,
xcodeLight,
};
// lineBackground is background with 50% opacity, to make sure the selection below is visible
export const settings = {
strudelTheme: {
background: '#222',
lineBackground: '#22222299',
foreground: '#fff',
// foreground: '#75baff',
caret: '#ffcc00',
selection: 'rgba(128, 203, 196, 0.5)',
selectionMatch: '#036dd626',
// lineHighlight: '#8a91991a', // original
lineHighlight: '#00000050',
gutterBackground: 'transparent',
// gutterForeground: '#8a919966',
gutterForeground: '#8a919966',
},
bluescreen: bluescreenSettings,
blackscreen: blackscreenSettings,
whitescreen: whitescreenSettings,
teletext: teletextSettings,
algoboy: algoboySettings,
terminal: terminalSettings,
abcdef: {
background: '#0f0f0f',
lineBackground: '#0f0f0f99',
foreground: '#defdef',
caret: '#00FF00',
selection: '#515151',
selectionMatch: '#515151',
gutterBackground: '#555',
gutterForeground: '#FFFFFF',
lineHighlight: '#314151',
},
androidstudio: {
background: '#282b2e',
lineBackground: '#282b2e99',
foreground: '#a9b7c6',
caret: '#00FF00',
selection: '#343739',
selectionMatch: '#343739',
lineHighlight: '#343739',
},
atomone: {
background: '#272C35',
lineBackground: '#272C3599',
foreground: '#9d9b97',
caret: '#797977',
selection: '#ffffff30',
selectionMatch: '#2B323D',
gutterBackground: '#272C35',
gutterForeground: '#465063',
gutterBorder: 'transparent',
lineHighlight: '#2B323D',
},
aura: {
background: '#21202e',
lineBackground: '#21202e99',
foreground: '#edecee',
caret: '#a277ff',
selection: '#3d375e7f',
selectionMatch: '#3d375e7f',
gutterBackground: '#21202e',
gutterForeground: '#edecee',
gutterBorder: 'transparent',
lineHighlight: '#a394f033',
},
bbedit: {
light: true,
background: '#FFFFFF',
lineBackground: '#FFFFFF99',
foreground: '#000000',
caret: '#FBAC52',
selection: '#FFD420',
selectionMatch: '#FFD420',
gutterBackground: '#f5f5f5',
gutterForeground: '#4D4D4C',
gutterBorder: 'transparent',
lineHighlight: '#00000012',
},
bespin: {
background: '#28211c',
lineBackground: '#28211c99',
foreground: '#9d9b97',
caret: '#797977',
selection: '#36312e',
selectionMatch: '#4f382b',
gutterBackground: '#28211c',
gutterForeground: '#666666',
lineHighlight: 'rgba(255, 255, 255, 0.1)',
},
darcula: {
background: '#2B2B2B',
lineBackground: '#2B2B2B99',
foreground: '#f8f8f2',
caret: '#FFFFFF',
selection: 'rgba(255, 255, 255, 0.1)',
selectionMatch: 'rgba(255, 255, 255, 0.2)',
gutterBackground: 'rgba(255, 255, 255, 0.1)',
gutterForeground: '#999',
gutterBorder: 'transparent',
lineHighlight: 'rgba(255, 255, 255, 0.1)',
},
dracula: {
background: '#282a36',
lineBackground: '#282a3699',
foreground: '#f8f8f2',
caret: '#f8f8f0',
selection: 'rgba(255, 255, 255, 0.1)',
selectionMatch: 'rgba(255, 255, 255, 0.2)',
gutterBackground: '#282a36',
gutterForeground: '#6D8A88',
gutterBorder: 'transparent',
lineHighlight: 'rgba(255, 255, 255, 0.1)',
},
duotoneLight: {
light: true,
background: '#faf8f5',
lineBackground: '#faf8f599',
foreground: '#b29762',
caret: '#93abdc',
selection: '#e3dcce',
selectionMatch: '#e3dcce',
gutterBackground: '#faf8f5',
gutterForeground: '#cdc4b1',
gutterBorder: 'transparent',
lineHighlight: '#EFEFEF',
},
duotoneDark: {
background: '#2a2734',
lineBackground: '#2a273499',
foreground: '#6c6783',
caret: '#ffad5c',
selection: 'rgba(255, 255, 255, 0.1)',
gutterBackground: '#2a2734',
gutterForeground: '#545167',
lineHighlight: '#36334280',
},
eclipse: {
light: true,
background: '#fff',
lineBackground: '#ffffff99',
foreground: '#000',
caret: '#FFFFFF',
selection: '#d7d4f0',
selectionMatch: '#d7d4f0',
gutterBackground: '#f7f7f7',
gutterForeground: '#999',
lineHighlight: '#e8f2ff',
gutterBorder: 'transparent',
},
githubLight: {
light: true,
background: '#fff',
lineBackground: '#ffffff99',
foreground: '#24292e',
selection: '#BBDFFF',
selectionMatch: '#BBDFFF',
gutterBackground: '#fff',
gutterForeground: '#6e7781',
},
githubDark: {
background: '#0d1117',
lineBackground: '#0d111799',
foreground: '#c9d1d9',
caret: '#c9d1d9',
selection: '#003d73',
selectionMatch: '#003d73',
lineHighlight: '#36334280',
},
gruvboxDark: {
background: '#282828',
lineBackground: '#28282899',
foreground: '#ebdbb2',
caret: '#ebdbb2',
selection: '#bdae93',
selectionMatch: '#bdae93',
lineHighlight: '#3c3836',
gutterBackground: '#282828',
gutterForeground: '#7c6f64',
},
gruvboxLight: {
light: true,
background: '#fbf1c7',
lineBackground: '#fbf1c799',
foreground: '#3c3836',
caret: '#af3a03',
selection: '#ebdbb2',
selectionMatch: '#bdae93',
lineHighlight: '#ebdbb2',
gutterBackground: '#ebdbb2',
gutterForeground: '#665c54',
gutterBorder: 'transparent',
},
materialDark: {
background: '#2e3235',
lineBackground: '#2e323599',
foreground: '#bdbdbd',
caret: '#a0a4ae',
selection: '#d7d4f0',
selectionMatch: '#d7d4f0',
gutterBackground: '#2e3235',
gutterForeground: '#999',
gutterActiveForeground: '#4f5b66',
lineHighlight: '#545b61',
},
materialLight: {
light: true,
background: '#FAFAFA',
lineBackground: '#FAFAFA99',
foreground: '#90A4AE',
caret: '#272727',
selection: '#80CBC440',
selectionMatch: '#FAFAFA',
gutterBackground: '#FAFAFA',
gutterForeground: '#90A4AE',
gutterBorder: 'transparent',
lineHighlight: '#CCD7DA50',
},
noctisLilac: {
light: true,
background: '#f2f1f8',
lineBackground: '#f2f1f899',
foreground: '#0c006b',
caret: '#5c49e9',
selection: '#d5d1f2',
selectionMatch: '#d5d1f2',
gutterBackground: '#f2f1f8',
gutterForeground: '#0c006b70',
lineHighlight: '#e1def3',
},
nord: {
background: '#2e3440',
lineBackground: '#2e344099',
foreground: '#FFFFFF',
caret: '#FFFFFF',
selection: '#3b4252',
selectionMatch: '#e5e9f0',
gutterBackground: '#2e3440',
gutterForeground: '#4c566a',
gutterActiveForeground: '#d8dee9',
lineHighlight: '#4c566a',
},
okaidia: {
background: '#272822',
lineBackground: '#27282299',
foreground: '#FFFFFF',
caret: '#FFFFFF',
selection: '#49483E',
selectionMatch: '#49483E',
gutterBackground: '#272822',
gutterForeground: '#FFFFFF70',
lineHighlight: '#00000059',
},
solarizedLight: {
light: true,
background: '#fdf6e3',
lineBackground: '#fdf6e399',
foreground: '#657b83',
caret: '#586e75',
selection: '#dfd9c8',
selectionMatch: '#dfd9c8',
gutterBackground: '#00000010',
gutterForeground: '#657b83',
lineHighlight: '#dfd9c8',
},
solarizedDark: {
background: '#002b36',
lineBackground: '#002b3699',
foreground: '#93a1a1',
caret: '#839496',
selection: '#173541',
selectionMatch: '#aafe661a',
gutterBackground: '#00252f',
gutterForeground: '#839496',
lineHighlight: '#173541',
},
sublime: {
background: '#303841',
lineBackground: '#30384199',
foreground: '#FFFFFF',
caret: '#FBAC52',
selection: '#4C5964',
selectionMatch: '#3A546E',
gutterBackground: '#303841',
gutterForeground: '#FFFFFF70',
lineHighlight: '#00000059',
},
tokyoNightDay: {
light: true,
background: '#e1e2e7',
lineBackground: '#e1e2e799',
foreground: '#3760bf',
caret: '#3760bf',
selection: '#99a7df',
selectionMatch: '#99a7df',
gutterBackground: '#e1e2e7',
gutterForeground: '#3760bf',
gutterBorder: 'transparent',
lineHighlight: '#5f5faf11',
},
tokyoNightStorm: {
background: '#24283b',
lineBackground: '#24283b99',
foreground: '#7982a9',
caret: '#c0caf5',
selection: '#6f7bb630',
selectionMatch: '#1f2335',
gutterBackground: '#24283b',
gutterForeground: '#7982a9',
gutterBorder: 'transparent',
lineHighlight: '#292e42',
},
tokyoNight: {
background: '#1a1b26',
lineBackground: '#1a1b2699',
foreground: '#787c99',
caret: '#c0caf5',
selection: '#515c7e40',
selectionMatch: '#16161e',
gutterBackground: '#1a1b26',
gutterForeground: '#787c99',
gutterBorder: 'transparent',
lineHighlight: '#1e202e',
},
vscodeDark: {
background: '#1e1e1e',
lineBackground: '#1e1e1e99',
foreground: '#9cdcfe',
caret: '#c6c6c6',
selection: '#6199ff2f',
selectionMatch: '#72a1ff59',
lineHighlight: '#ffffff0f',
gutterBackground: '#1e1e1e',
gutterForeground: '#838383',
gutterActiveForeground: '#fff',
},
xcodeLight: {
light: true,
background: '#fff',
lineBackground: '#ffffff99',
foreground: '#3D3D3D',
selection: '#BBDFFF',
selectionMatch: '#BBDFFF',
gutterBackground: '#fff',
gutterForeground: '#AFAFAF',
lineHighlight: '#EDF4FF',
},
xcodeDark: {
background: '#292A30',
lineBackground: '#292A3099',
foreground: '#CECFD0',
caret: '#fff',
selection: '#727377',
selectionMatch: '#727377',
lineHighlight: '#2F3239',
},
};
function getColors(str) {
const colorRegex = /#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3})/g;
const colors = [];
let match;
while ((match = colorRegex.exec(str)) !== null) {
const color = match[0];
if (!colors.includes(color)) {
colors.push(color);
}
}
return colors;
}
// TODO: remove
export function themeColors(theme) {
return getColors(stringifySafe(theme));
}
function getCircularReplacer() {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}
return value;
};
}
function stringifySafe(json) {
return JSON.stringify(json, getCircularReplacer());
}
export function injectStyle(rule) {
const newStyle = document.createElement('style');
document.head.appendChild(newStyle);
const styleSheet = newStyle.sheet;
const ruleIndex = styleSheet.insertRule(rule, 0);
return () => styleSheet.deleteRule(ruleIndex);
}
export const theme = (theme) => themes[theme] || themes.strudelTheme;
-41
View File
@@ -1,41 +0,0 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from '@uiw/codemirror-themes';
export const settings = {
background: '#9bbc0f',
foreground: '#0f380f', // whats that?
caret: '#0f380f',
selection: '#306230',
selectionMatch: '#ffffff26',
lineHighlight: '#8bac0f',
lineBackground: '#9bbc0f50',
//lineBackground: 'transparent',
gutterBackground: 'transparent',
gutterForeground: '#0f380f',
light: true,
customStyle: '.cm-line { line-height: 1 }',
};
export default createTheme({
theme: 'light',
settings,
styles: [
{ tag: t.keyword, color: '#0f380f' },
{ tag: t.operator, color: '#0f380f' },
{ tag: t.special(t.variableName), color: '#0f380f' },
{ tag: t.typeName, color: '#0f380f' },
{ tag: t.atom, color: '#0f380f' },
{ tag: t.number, color: '#0f380f' },
{ tag: t.definition(t.variableName), color: '#0f380f' },
{ tag: t.string, color: '#0f380f' },
{ tag: t.special(t.string), color: '#0f380f' },
{ tag: t.comment, color: '#0f380f' },
{ tag: t.variableName, color: '#0f380f' },
{ tag: t.tagName, color: '#0f380f' },
{ tag: t.bracket, color: '#0f380f' },
{ tag: t.meta, color: '#0f380f' },
{ tag: t.attributeName, color: '#0f380f' },
{ tag: t.propertyName, color: '#0f380f' },
{ tag: t.className, color: '#0f380f' },
{ tag: t.invalid, color: '#0f380f' },
{ tag: [t.unit, t.punctuation], color: '#0f380f' },
],
});
-38
View File
@@ -1,38 +0,0 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from '@uiw/codemirror-themes';
export const settings = {
background: 'black',
foreground: 'white', // whats that?
caret: 'white',
selection: '#ffffff20',
selectionMatch: '#036dd626',
lineHighlight: '#ffffff10',
lineBackground: '#00000050',
gutterBackground: 'transparent',
gutterForeground: '#8a919966',
};
export default createTheme({
theme: 'dark',
settings,
styles: [
{ tag: t.keyword, color: 'white' },
{ tag: t.operator, color: 'white' },
{ tag: t.special(t.variableName), color: 'white' },
{ tag: t.typeName, color: 'white' },
{ tag: t.atom, color: 'white' },
{ tag: t.number, color: 'white' },
{ tag: t.definition(t.variableName), color: 'white' },
{ tag: t.string, color: 'white' },
{ tag: t.special(t.string), color: 'white' },
{ tag: t.comment, color: 'white' },
{ tag: t.variableName, color: 'white' },
{ tag: t.tagName, color: 'white' },
{ tag: t.bracket, color: 'white' },
{ tag: t.meta, color: 'white' },
{ tag: t.attributeName, color: 'white' },
{ tag: t.propertyName, color: 'white' },
{ tag: t.className, color: 'white' },
{ tag: t.invalid, color: 'white' },
{ tag: [t.unit, t.punctuation], color: 'white' },
],
});
-41
View File
@@ -1,41 +0,0 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from '@uiw/codemirror-themes';
export const settings = {
background: '#051DB5',
lineBackground: '#051DB550',
foreground: 'white', // whats that?
caret: 'white',
selection: 'rgba(128, 203, 196, 0.5)',
selectionMatch: '#036dd626',
// lineHighlight: '#8a91991a', // original
lineHighlight: '#00000050',
gutterBackground: 'transparent',
// gutterForeground: '#8a919966',
gutterForeground: '#8a919966',
};
export default createTheme({
theme: 'dark',
settings,
styles: [
{ tag: t.keyword, color: 'white' },
{ tag: t.operator, color: 'white' },
{ tag: t.special(t.variableName), color: 'white' },
{ tag: t.typeName, color: 'white' },
{ tag: t.atom, color: 'white' },
{ tag: t.number, color: 'white' },
{ tag: t.definition(t.variableName), color: 'white' },
{ tag: t.string, color: 'white' },
{ tag: t.special(t.string), color: 'white' },
{ tag: t.comment, color: 'white' },
{ tag: t.variableName, color: 'white' },
{ tag: t.tagName, color: 'white' },
{ tag: t.bracket, color: 'white' },
{ tag: t.meta, color: 'white' },
{ tag: t.attributeName, color: 'white' },
{ tag: t.propertyName, color: 'white' },
{ tag: t.className, color: 'white' },
{ tag: t.invalid, color: 'white' },
{ tag: [t.unit, t.punctuation], color: 'white' },
],
});
+139
View File
@@ -0,0 +1,139 @@
import { EditorView } from '@codemirror/view';
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language';
import { tags as t } from '@lezer/highlight';
// Using https://github.com/one-dark/vscode-one-dark-theme/ as reference for the colors
const chalky = '#e5c07b',
coral = '#e06c75',
cyan = '#56b6c2',
invalid = '#ffffff',
ivory = '#abb2bf',
stone = '#7d8799', // Brightened compared to original to increase contrast
malibu = '#61afef',
sage = '#98c379',
whiskey = '#d19a66',
violet = '#c678dd',
darkBackground = '#21252b',
highlightBackground = '#2c313a',
background = '#282c34',
tooltipBackground = '#353a42',
selection = '#3E4451',
cursor = '#528bff';
/// The colors used in the theme, as CSS color strings.
export const color = {
chalky,
coral,
cyan,
invalid,
ivory,
stone,
malibu,
sage,
whiskey,
violet,
darkBackground,
highlightBackground,
background,
tooltipBackground,
selection,
cursor,
};
/// The editor theme styles for One Dark.
export const oneDarkTheme = EditorView.theme(
{
'&': {
color: ivory,
backgroundColor: background,
},
'.cm-content': {
caretColor: cursor,
},
'.cm-cursor, .cm-dropCursor': { borderLeftColor: cursor },
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection':
{ backgroundColor: selection },
'.cm-panels': { backgroundColor: darkBackground, color: ivory },
'.cm-panels.cm-panels-top': { borderBottom: '2px solid black' },
'.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' },
'.cm-searchMatch': {
backgroundColor: '#72a1ff59',
outline: '1px solid #457dff',
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: '#6199ff2f',
},
'.cm-activeLine': { backgroundColor: '#6699ff0b' },
'.cm-selectionMatch': { backgroundColor: '#aafe661a' },
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
backgroundColor: '#bad0f847',
},
'.cm-gutters': {
backgroundColor: background,
color: stone,
border: 'none',
},
'.cm-activeLineGutter': {
backgroundColor: highlightBackground,
},
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
border: 'none',
color: '#ddd',
},
'.cm-tooltip': {
border: 'none',
backgroundColor: tooltipBackground,
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent',
borderBottomColor: 'transparent',
},
'.cm-tooltip .cm-tooltip-arrow:after': {
borderTopColor: tooltipBackground,
borderBottomColor: tooltipBackground,
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
backgroundColor: highlightBackground,
color: ivory,
},
},
},
{ dark: true },
);
/// The highlighting style for code in the One Dark theme.
export const oneDarkHighlightStyle = HighlightStyle.define([
{ tag: t.keyword, color: violet },
{ tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], color: coral },
{ tag: [t.function(t.variableName), t.labelName], color: malibu },
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: whiskey },
{ tag: [t.definition(t.name), t.separator], color: ivory },
{ tag: [t.typeName, t.className, t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: chalky },
{ tag: [t.operator, t.operatorKeyword, t.url, t.escape, t.regexp, t.link, t.special(t.string)], color: cyan },
{ tag: [t.meta, t.comment], color: stone },
{ tag: t.strong, fontWeight: 'bold' },
{ tag: t.emphasis, fontStyle: 'italic' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
{ tag: t.link, color: stone, textDecoration: 'underline' },
{ tag: t.heading, fontWeight: 'bold', color: coral },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: whiskey },
{ tag: [t.processingInstruction, t.string, t.inserted], color: sage },
{ tag: t.invalid, color: invalid },
]);
/// Extension to enable the One Dark theme (both the editor theme and
/// the highlight style).
export const oneDark = [oneDarkTheme, syntaxHighlighting(oneDarkHighlightStyle)];
-45
View File
@@ -1,45 +0,0 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from '@uiw/codemirror-themes';
export default createTheme({
theme: 'dark',
settings: {
background: '#222',
foreground: '#75baff', // whats that?
caret: '#ffcc00',
selection: 'rgba(128, 203, 196, 0.5)',
selectionMatch: '#036dd626',
// lineHighlight: '#8a91991a', // original
lineHighlight: '#00000050',
gutterBackground: 'transparent',
// gutterForeground: '#8a919966',
gutterForeground: '#8a919966',
},
styles: [
{ tag: t.keyword, color: '#c792ea' },
{ tag: t.operator, color: '#89ddff' },
{ tag: t.special(t.variableName), color: '#eeffff' },
// { tag: t.typeName, color: '#f07178' }, // original
{ tag: t.typeName, color: '#c3e88d' },
{ tag: t.atom, color: '#f78c6c' },
// { tag: t.number, color: '#ff5370' }, // original
{ tag: t.number, color: '#c3e88d' },
{ tag: t.definition(t.variableName), color: '#82aaff' },
{ tag: t.string, color: '#c3e88d' },
// { tag: t.special(t.string), color: '#f07178' }, // original
{ tag: t.special(t.string), color: '#c3e88d' },
{ tag: t.comment, color: '#7d8799' },
// { tag: t.variableName, color: '#f07178' }, // original
{ tag: t.variableName, color: '#c792ea' },
// { tag: t.tagName, color: '#ff5370' }, // original
{ tag: t.tagName, color: '#c3e88d' },
{ tag: t.bracket, color: '#525154' },
// { tag: t.bracket, color: '#a2a1a4' }, // original
{ tag: t.meta, color: '#ffcb6b' },
{ tag: t.attributeName, color: '#c792ea' },
{ tag: t.propertyName, color: '#c792ea' },
{ tag: t.className, color: '#decb6b' },
{ tag: t.invalid, color: '#ffffff' },
{ tag: [t.unit, t.punctuation], color: '#82aaff' },
],
});
-50
View File
@@ -1,50 +0,0 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from '@uiw/codemirror-themes';
let colorA = '#6edee4';
//let colorB = 'magenta';
let colorB = 'white';
let colorC = 'red';
let colorD = '#f8fc55';
export const settings = {
background: '#000000',
foreground: colorA, // whats that?
caret: colorC,
selection: colorD,
selectionMatch: colorA,
lineHighlight: '#6edee440', // panel bg
lineBackground: '#00000040',
gutterBackground: 'transparent',
gutterForeground: '#8a919966',
customStyle: '.cm-line { line-height: 1 }',
};
let punctuation = colorD;
let mini = colorB;
export default createTheme({
theme: 'dark',
settings,
styles: [
{ tag: t.keyword, color: colorA },
{ tag: t.operator, color: mini },
{ tag: t.special(t.variableName), color: colorA },
{ tag: t.typeName, color: colorA },
{ tag: t.atom, color: colorA },
{ tag: t.number, color: mini },
{ tag: t.definition(t.variableName), color: colorA },
{ tag: t.string, color: mini },
{ tag: t.special(t.string), color: mini },
{ tag: t.comment, color: punctuation },
{ tag: t.variableName, color: colorA },
{ tag: t.tagName, color: colorA },
{ tag: t.bracket, color: punctuation },
{ tag: t.meta, color: colorA },
{ tag: t.attributeName, color: colorA },
{ tag: t.propertyName, color: colorA }, // methods
{ tag: t.className, color: colorA },
{ tag: t.invalid, color: colorC },
{ tag: [t.unit, t.punctuation], color: punctuation },
],
});
-36
View File
@@ -1,36 +0,0 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from '@uiw/codemirror-themes';
export const settings = {
background: 'black',
foreground: '#41FF00', // whats that?
caret: '#41FF00',
selection: '#ffffff20',
selectionMatch: '#036dd626',
lineHighlight: '#ffffff10',
gutterBackground: 'transparent',
gutterForeground: '#8a919966',
};
export default createTheme({
theme: 'dark',
settings,
styles: [
{ tag: t.keyword, color: '#41FF00' },
{ tag: t.operator, color: '#41FF00' },
{ tag: t.special(t.variableName), color: '#41FF00' },
{ tag: t.typeName, color: '#41FF00' },
{ tag: t.atom, color: '#41FF00' },
{ tag: t.number, color: '#41FF00' },
{ tag: t.definition(t.variableName), color: '#41FF00' },
{ tag: t.string, color: '#41FF00' },
{ tag: t.special(t.string), color: '#41FF00' },
{ tag: t.comment, color: '#41FF00' },
{ tag: t.variableName, color: '#41FF00' },
{ tag: t.tagName, color: '#41FF00' },
{ tag: t.bracket, color: '#41FF00' },
{ tag: t.meta, color: '#41FF00' },
{ tag: t.attributeName, color: '#41FF00' },
{ tag: t.propertyName, color: '#41FF00' },
{ tag: t.className, color: '#41FF00' },
{ tag: t.invalid, color: '#41FF00' },
],
});
-38
View File
@@ -1,38 +0,0 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from '@uiw/codemirror-themes';
export const settings = {
background: 'white',
foreground: 'black', // whats that?
caret: 'black',
selection: 'rgba(128, 203, 196, 0.5)',
selectionMatch: '#ffffff26',
lineHighlight: '#cccccc50',
lineBackground: '#ffffff50',
gutterBackground: 'transparent',
gutterForeground: 'black',
light: true,
};
export default createTheme({
theme: 'light',
settings,
styles: [
{ tag: t.keyword, color: 'black' },
{ tag: t.operator, color: 'black' },
{ tag: t.special(t.variableName), color: 'black' },
{ tag: t.typeName, color: 'black' },
{ tag: t.atom, color: 'black' },
{ tag: t.number, color: 'black' },
{ tag: t.definition(t.variableName), color: 'black' },
{ tag: t.string, color: 'black' },
{ tag: t.special(t.string), color: 'black' },
{ tag: t.comment, color: 'black' },
{ tag: t.variableName, color: 'black' },
{ tag: t.tagName, color: 'black' },
{ tag: t.bracket, color: 'black' },
{ tag: t.meta, color: 'black' },
{ tag: t.attributeName, color: 'black' },
{ tag: t.propertyName, color: 'black' },
{ tag: t.className, color: 'black' },
{ tag: t.invalid, color: 'black' },
],
});
+4 -108
View File
@@ -86,16 +86,6 @@ const generic_params = [
*
*/
['gain'],
/**
* Gain applied after all effects have been processed.
*
* @name postgain
* @example
* s("bd sd,hh*4")
* .compressor("-20:20:10:.002:.02").postgain(1.5)
*
*/
['postgain'],
/**
* Like {@link gain}, but linear.
*
@@ -380,62 +370,6 @@ const generic_params = [
*
*/
['coarse'],
['phaserrate', 'phasr'], // superdirt only
/**
* Phaser audio effect that approximates popular guitar pedals.
*
* @name phaser
* @synonyms ph
* @param {number | Pattern} speed speed of modulation
* @example
* n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5)
* .phaser("<1 2 4 8>")
*
*/
[['phaser', 'phaserdepth', 'phasercenter', 'phasersweep'], 'ph'],
/**
* The frequency sweep range of the lfo for the phaser effect. Defaults to 2000
*
* @name phasersweep
* @synonyms phs
* @param {number | Pattern} phasersweep most useful values are between 0 and 4000
* @example
* n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5)
* .phaser(2).phasersweep("<800 2000 4000>")
*
*/
['phasersweep', 'phs'],
/**
* The center frequency of the phaser in HZ. Defaults to 1000
*
* @name phasercenter
* @synonyms phc
* @param {number | Pattern} centerfrequency in HZ
* @example
* n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5)
* .phaser(2).phasercenter("<800 2000 4000>")
*
*/
['phasercenter', 'phc'],
/**
* The amount the signal is affected by the phaser effect. Defaults to 0.75
*
* @name phaserdepth
* @synonyms phd
* @param {number | Pattern} depth number between 0 and 1
* @example
* n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5)
* .phaser(2).phaserdepth("<0 .5 .75 1>")
*
*/
['phaserdepth', 'phd', 'phasdp'], // also a superdirt control
/**
* choose the channel the pattern is sent to in superdirt
*
@@ -923,12 +857,7 @@ const generic_params = [
*
*/
['lsize'],
/**
* Sets the displayed text for an event on the pianoroll
*
* @name label
* @param {string} label text to display
*/
// label for pianoroll
['activeLabel'],
[['label', 'activeLabel']],
// ['lfo'],
@@ -1091,16 +1020,6 @@ const generic_params = [
*
*/
['roomfade', 'rfade'],
/**
* Sets the sample to use as an impulse response for the reverb.
* @name iresponse
* @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>")
*
*/
[['ir', 'i'], 'iresponse'],
/**
* Sets the room size of the reverb, see {@link room}.
* When this property is changed, the reverb will be recaculated, so only change this sparsely..
@@ -1132,21 +1051,6 @@ const generic_params = [
*
*/
['shape'],
/**
* Dynamics Compressor. The params are `compressor("threshold:ratio:knee:attack:release")`
* More info [here](https://developer.mozilla.org/en-US/docs/Web/API/DynamicsCompressorNode?retiredLocale=de#instance_properties)
*
* @name compressor
* @example
* s("bd sd,hh*4")
* .compressor("-20:20:10:.002:.02")
*
*/
[['compressor', 'compressorRatio', 'compressorKnee', 'compressorAttack', 'compressorRelease']],
['compressorKnee'],
['compressorRatio'],
['compressorAttack'],
['compressorRelease'],
/**
* Changes the speed of sample playback, i.e. a cheap way of changing pitch.
*
@@ -1231,6 +1135,9 @@ const generic_params = [
*/
['tremolodepth', 'tremdp'],
['tremolorate', 'tremr'],
// TODO: doesn't seem to do anything
['phaserdepth', 'phasdp'],
['phaserrate', 'phasr'],
['fshift'],
['fshiftnote'],
@@ -1355,17 +1262,6 @@ generic_params.forEach(([names, ...aliases]) => {
controls.createParams = (...names) =>
names.reduce((acc, name) => Object.assign(acc, { [name]: controls.createParam(name) }), {});
/**
* ADSR envelope: Combination of Attack, Decay, Sustain, and Release.
*
* @name adsr
* @param {number | Pattern} time attack time in seconds
* @param {number | Pattern} time decay time in seconds
* @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")
*/
controls.adsr = register('adsr', (adsr, pat) => {
adsr = !Array.isArray(adsr) ? [adsr] : adsr;
const [attack, decay, sustain, release] = adsr;
+1 -1
View File
@@ -146,7 +146,7 @@ export class Drawer {
);
}
invalidate(scheduler = this.scheduler) {
if (!scheduler || !scheduler.pattern) {
if (!scheduler) {
return;
}
this.scheduler = scheduler;
@@ -3,6 +3,6 @@
This folder demonstrates how to set up a strudel repl using vite and vanilla JS + codemirror. Run it using:
```sh
pnpm i
pnpm dev
npm i
npm run dev
```
@@ -1,6 +1,6 @@
export const bumpStreet = `// froos - "22 bump street", licensed with CC BY-NC-SA 4.0
await samples('github:felixroos/samples/main')
await samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
await samples('https://strudel.tidalcycles.org/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
"<[0,<6 7 9>,13,<17 20 22 26>]!2>/2"
// make it 22 edo
@@ -34,7 +34,7 @@ await samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tid
export const trafficFlam = `// froos - "traffic flam", licensed with CC BY-NC-SA 4.0
await samples('github:felixroos/samples/main')
await samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
await samples('https://strudel.tidalcycles.org/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
addVoicings('hip', {
m11: ['2M 3m 4P 7m'],
@@ -70,7 +70,7 @@ export const funk42 = `// froos - how to funk in 42 lines of code
// thanks to peach for the transcription: https://www.youtube.com/watch?v=8eiPXvIgda4
await samples('github:felixroos/samples/main')
await samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
await samples('https://strudel.tidalcycles.org/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
setcps(.5)
+2 -3
View File
@@ -29,10 +29,9 @@
"bugs": {
"url": "https://github.com/tidalcycles/strudel/issues"
},
"homepage": "https://strudel.cc",
"homepage": "https://strudel.tidalcycles.org",
"dependencies": {
"fraction.js": "^4.2.0",
"nanostores": "^0.8.1"
"fraction.js": "^4.2.0"
},
"gitHead": "0e26d4e741500f5bae35b023608f062a794905c2",
"devDependencies": {
+4 -76
View File
@@ -2100,43 +2100,23 @@ export const { iterBack, iterback } = register(['iterBack', 'iterback'], functio
return _iter(times, pat, true);
});
/**
* Repeats each cycle the given number of times.
* @name repeatCycles
* @memberof Pattern
* @returns Pattern
* @example
* note(irand(12).add(34)).segment(4).repeatCycles(2).s("gm_acoustic_guitar_nylon")
*/
const _repeatCycles = function (n, pat) {
return slowcat(...Array(n).fill(pat));
};
const { repeatCycles } = register('repeatCycles', _repeatCycles);
/**
* Divides a pattern into a given number of parts, then cycles through those parts in turn, applying the given function to each part in turn (one part per cycle).
* @name chunk
* @synonyms slowChunk, slowchunk
* @memberof Pattern
* @returns Pattern
* @example
* "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 _chunk = function (n, func, pat, back = false) {
const binary = Array(n - 1).fill(false);
binary.unshift(true);
// Invert the 'back' because we want to shift the pattern forwards,
// and so time backwards
const binary_pat = _iter(n, sequence(...binary), !back);
if (!fast) {
pat = pat.repeatCycles(n);
}
const binary_pat = _iter(n, sequence(...binary), back);
return pat.when(binary_pat, func);
};
const { chunk, slowchunk, slowChunk } = register(['chunk', 'slowchunk', 'slowChunk'], function (n, func, pat) {
return _chunk(n, func, pat, false, false);
export const chunk = register('chunk', function (n, func, pat) {
return _chunk(n, func, pat, false);
});
/**
@@ -2152,21 +2132,6 @@ export const { chunkBack, chunkback } = register(['chunkBack', 'chunkback'], fun
return _chunk(n, func, pat, true);
});
/**
* Like `chunk`, but the cycles of the source pattern aren't repeated
* for each set of chunks.
* @name fastChunk
* @synonyms fastchunk
* @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")
*/
const { fastchunk, fastChunk } = register(['fastchunk', 'fastChunk'], function (n, func, pat) {
return _chunk(n, func, pat, false, true);
});
// TODO - redefine elsewhere in terms of mask
export const bypass = register('bypass', function (on, pat) {
on = Boolean(parseInt(on));
@@ -2191,9 +2156,6 @@ export const duration = register('duration', function (value, pat) {
/**
* Sets the color of the hap in visualizations like pianoroll or highlighting.
* @name color
* @synonyms colour
* @param {string} color Hexadecimal or CSS color name
*/
// TODO: move this to controls https://github.com/tidalcycles/strudel/issues/288
export const { color, colour } = register(['color', 'colour'], function (color, pat) {
@@ -2255,14 +2217,6 @@ export const chop = register('chop', function (n, pat) {
return pat.squeezeBind(func);
});
/**
* Cuts each sample into the given number of parts, triggering progressive portions of each sample at each loop.
* @name striate
* @memberof Pattern
* @returns Pattern
* @example
* s("numbers:0 numbers:1 numbers:2").striate(6).slow(6)
*/
export const striate = register('striate', function (n, pat) {
const slices = Array.from({ length: n }, (x, i) => i);
const slice_objects = slices.map((i) => ({ begin: i / n, end: (i + 1) / n }));
@@ -2395,29 +2349,3 @@ export const ref = (accessor) =>
pure(1)
.withValue(() => reify(accessor()))
.innerJoin();
let fadeGain = (p) => (p < 0.5 ? 1 : 1 - (p - 0.5) / 0.5);
/**
* Cross-fades between left and right from 0 to 1:
* - 0 = (full left, no right)
* - .5 = (both equal)
* - 1 = (no left, full right)
*
* @name xfade
* @example
* xfade(s("bd*2"), "<0 .25 .5 .75 1>", s("hh*8"))
*/
export let xfade = (a, pos, b) => {
pos = reify(pos);
a = reify(a);
b = reify(b);
let gaina = pos.fmap((v) => ({ gain: fadeGain(v) }));
let gainb = pos.fmap((v) => ({ gain: fadeGain(1 - v) }));
return stack(a.mul(gaina), b.mul(gainb));
};
// the prototype version is actually flipped so left/right makes sense
Pattern.prototype.xfade = function (pos, b) {
return xfade(this, pos, b);
};
+1 -36
View File
@@ -56,40 +56,6 @@ Pattern.prototype.pianoroll = function (options = {}) {
// this function allows drawing a pianoroll without ties to Pattern.prototype
// it will probably replace the above in the future
/**
* Displays a midi-style piano roll
*
* @name pianoroll
* @param {Object} options Object containing all the optional following parameters as key value pairs:
* @param {integer} cycles number of cycles to be displayed at the same time - defaults to 4
* @param {number} playhead location of the active notes on the time axis - 0 to 1, defaults to 0.5
* @param {boolean} vertical displays the roll vertically - 0 by default
* @param {boolean} labels displays labels on individual notes (see the label function) - 0 by default
* @param {boolean} flipTime reverse the direction of the roll - 0 by default
* @param {boolean} flipValues reverse the relative location of notes on the value axis - 0 by default
* @param {number} overscan lookup X cycles outside of the cycles window to display notes in advance - 1 by default
* @param {boolean} hideNegative hide notes with negative time (before starting playing the pattern) - 0 by default
* @param {boolean} smear notes leave a solid trace - 0 by default
* @param {boolean} fold notes takes the full value axis width - 0 by default
* @param {string} active hexadecimal or CSS color of the active notes - defaults to #FFCA28
* @param {string} inactive hexadecimal or CSS color of the inactive notes - defaults to #7491D2
* @param {string} background hexadecimal or CSS color of the background - defaults to transparent
* @param {string} playheadColor hexadecimal or CSS color of the line representing the play head - defaults to white
* @param {boolean} fill notes are filled with color (otherwise only the label is displayed) - 0 by default
* @param {boolean} fillActive active notes are filled with color - 0 by default
* @param {boolean} stroke notes are shown with colored borders - 0 by default
* @param {boolean} strokeActive active notes are shown with colored borders - 0 by default
* @param {boolean} hideInactive only active notes are shown - 0 by default
* @param {boolean} colorizeInactive use note color for inactive notes - 1 by default
* @param {string} fontFamily define the font used by notes labels - defaults to 'monospace'
* @param {integer} minMidi minimum note value to display on the value axis - defaults to 10
* @param {integer} maxMidi maximum note value to display on the value axis - defaults to 90
* @param {boolean} autorange automatically calculate the minMidi and maxMidi parameters - 0 by default
*
* @example
* note("C2 A2 G2").euclid(5,8).s('piano').clip(1).color('salmon').pianoroll({vertical:1, labels:1})
*/
export function pianoroll({
time,
haps,
@@ -120,7 +86,6 @@ export function pianoroll({
hideInactive = 0,
colorizeInactive = 1,
fontFamily,
clear = true,
ctx,
} = {}) {
const w = ctx.canvas.width;
@@ -166,7 +131,7 @@ export function pianoroll({
barThickness = fold ? valueAxis / foldValues.length : valueAxis / valueExtent;
ctx.fillStyle = background;
ctx.globalAlpha = 1; // reset!
if (clear && !smear) {
if (!smear) {
ctx.clearRect(0, 0, w, h);
ctx.fillRect(0, 0, w, h);
}
+7 -47
View File
@@ -4,22 +4,6 @@ import { logger } from './logger.mjs';
import { setTime } from './time.mjs';
import { evalScope } from './evaluate.mjs';
import { register } from './pattern.mjs';
import { atom, computed } from 'nanostores';
export const $replstate = atom({
schedulerError: undefined,
evalError: undefined,
code: '// LOADING',
activeCode: '// LOADING',
pattern: undefined,
miniLocations: [],
widgets: [],
pending: true,
started: false,
});
export const $repldirty = computed($replstate, (s) => s.code !== s.activeCode);
export const setReplState = (key, value) => $replstate.set({ ...$replstate.get(), [key]: value });
export function repl({
interval,
@@ -32,18 +16,17 @@ export function repl({
transpiler,
onToggle,
editPattern,
createCyclist,
}) {
const scheduler = new Cyclist({
const cyclistParams = {
interval,
onTrigger: getTrigger({ defaultOutput, getTime }),
onError: onSchedulerError,
getTime,
onToggle: (started) => {
setReplState('started', started);
onToggle?.(started);
},
});
let playPatterns = [];
onToggle,
};
const scheduler = createCyclist?.(cyclistParams) || new Cyclist(cyclistParams);
const setPattern = (pattern, autostart = true) => {
pattern = editPattern?.(pattern) || pattern;
scheduler.setPattern(pattern, autostart);
@@ -54,37 +37,21 @@ export function repl({
throw new Error('no code to evaluate');
}
try {
setReplState('code', code);
setReplState('pending', true);
await beforeEval?.({ code });
playPatterns = [];
let { pattern, meta } = await _evaluate(code, transpiler);
if (playPatterns.length) {
pattern = pattern.stack(...playPatterns);
}
logger(`[eval] code updated`);
setPattern(pattern, autostart);
setReplState('miniLocations', meta?.miniLocations || []);
setReplState('widgets', meta?.widgets || []);
setReplState('activeCode', code);
setReplState('pattern', pattern);
setReplState('evalError', undefined);
setReplState('schedulerError', undefined);
setReplState('pending', false);
afterEval?.({ code, pattern, meta });
return pattern;
} catch (err) {
// console.warn(`[repl] eval error: ${err.message}`);
logger(`[eval] error: ${err.message}`, 'error');
setReplState('evalError', err);
setReplState('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);
@@ -93,11 +60,6 @@ export function repl({
return pat.loopAtCps(cycles, scheduler.cps);
});
const play = register('play', (pat) => {
playPatterns.push(pat);
return pat;
});
const fit = register('fit', (pat) =>
pat.withHap((hap) =>
hap.withValue((v) => ({
@@ -111,15 +73,13 @@ export function repl({
evalScope({
loopAt,
fit,
play,
setCps,
setcps: setCps,
setCpm,
setcpm: setCpm,
});
const setCode = (c) => setReplState('code', c);
return { scheduler, evaluate, start, stop, pause, setCps, setPattern, setCode, toggle };
return { scheduler, evaluate, start, stop, pause, setCps, setPattern };
}
export const getTrigger =
+2 -54
View File
@@ -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 } from './util.mjs';
export function steady(value) {
// A continuous value
@@ -155,62 +155,12 @@ export const _irand = (i) => rand.fmap((x) => Math.trunc(x * i));
*/
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
* @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"]))
*/
export const pick = (pat, xs) => {
xs = xs.map(reify);
if (xs.length == 0) {
return silence;
}
return pat
.fmap((i) => {
const key = clamp(Math.round(i), 0, xs.length - 1);
return xs[key];
})
.innerJoin();
};
/**
* 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
* @returns {Pattern}
* @example
* note(squeeze("<0@2 [1!2] 2>", ["g a", "f g f g" , "g a c d"]))
*/
export const squeeze = (pat, xs) => {
xs = xs.map(reify);
if (xs.length == 0) {
return silence;
}
return pat
.fmap((i) => {
const key = _mod(Math.round(i), xs.length);
return xs[key];
})
.squeezeJoin();
};
export const __chooseWith = (pat, xs) => {
xs = xs.map(reify);
if (xs.length == 0) {
return silence;
}
return pat.range(0, xs.length).fmap((i) => {
const key = Math.min(Math.max(Math.floor(i), 0), xs.length - 1);
return xs[key];
});
return pat.range(0, xs.length).fmap((i) => xs[Math.floor(i)]);
};
/**
* Choose from the list of values (or patterns of values) using the given
@@ -218,8 +168,6 @@ export const __chooseWith = (pat, xs) => {
* @param {Pattern} pat
* @param {*} xs
* @returns {Pattern}
* @example
* note("c2 g2!2 d2 f1").s(chooseWith(sine.fast(2), ["sawtooth", "triangle", "bd:6"]))
*/
export const chooseWith = (pat, xs) => {
return __chooseWith(pat, xs).outerJoin();
+3 -3
View File
@@ -67,9 +67,9 @@ Pattern.prototype.spiral = function (options = {}) {
// logSpiral = true,
} = options;
function spiral({ ctx, time, haps, drawTime, clear }) {
function spiral({ ctx, time, haps, drawTime }) {
const [w, h] = [ctx.canvas.width, ctx.canvas.height];
clear && ctx.clearRect(0, 0, w * 2, h * 2);
ctx.clearRect(0, 0, w * 2, h * 2);
const [cx, cy] = [w / 2, h / 2];
const settings = {
margin: size / stretch,
@@ -114,5 +114,5 @@ Pattern.prototype.spiral = function (options = {}) {
});
}
return this.onPaint((ctx, time, haps, drawTime, options) => spiral({ ctx, time, haps, drawTime, ...options }));
return this.onPaint((ctx, time, haps, drawTime) => spiral({ ctx, time, haps, drawTime }));
};
-19
View File
@@ -1003,23 +1003,4 @@ describe('Pattern', () => {
);
});
});
describe('chunk', () => {
it('Processes each cycle of the source pattern multiple times, once for each chunk', () => {
expect(sequence(0, 1, 2, 3).slow(2).chunk(2, add(10)).fast(4).firstCycleValues).toStrictEqual([
10, 1, 0, 11, 12, 3, 2, 13,
]);
});
});
describe('fastChunk', () => {
it('Unlike chunk, cycles of the source pattern proceed cycle-by-cycle', () => {
expect(sequence(0, 1, 2, 3).slow(2).fastChunk(2, add(10)).fast(4).firstCycleValues).toStrictEqual([
10, 1, 2, 13, 10, 1, 2, 13,
]);
});
});
describe('repeatCycles', () => {
it('Repeats each cycle of the source pattern the given number of times', () => {
expect(slowcat(0, 1).repeatCycles(2).fast(6).firstCycleValues).toStrictEqual([0, 0, 1, 1, 0, 0]);
});
});
});
+2
View File
@@ -46,5 +46,7 @@ export const cleanupUi = () => {
const container = document.getElementById('code');
if (container) {
container.style = '';
// TODO: find a way to remove that duplication..
container.className = 'grow flex text-gray-100 relative overflow-auto cursor-text pb-0'; // has to match App.tsx
}
};
+2 -1
View File
@@ -43,7 +43,8 @@ function createClock(
clear();
};
const getPhase = () => phase;
const setPhase = (_phase) => (phase = _phase);
// setCallback
return { setDuration, start, stop, pause, duration, interval, getPhase, minLatency };
return { setDuration, start, stop, pause, duration, interval, getPhase, setPhase, minLatency };
}
export default createClock;
+1 -1
View File
@@ -137,7 +137,7 @@ export async function loadOrc(url) {
export const csoundm = register('csoundm', (instrument, pat) => {
let p1 = instrument;
if (typeof instrument === 'string') {
p1 = `"${instrument}"`;
p1 = `"{instrument}"`;
}
init(); // not async to support csound inside other patterns + to be able to call pattern methods after it
return pat.onTrigger((tidal_time, hap) => {
+77
View File
@@ -0,0 +1,77 @@
import { Cyclist } from '@strudel.cycles/core';
import { logger } from '../core/logger.mjs';
import { Invoke } from '../../website/src/tauri.mjs';
import { listen } from '@tauri-apps/api/event';
export class CyclistBridge extends Cyclist {
constructor(params) {
super(params);
this.start_timer;
this.abeLinkListener = listen('abelink-event', async (e) => {
const payload = e?.payload;
if (payload == null) {
return;
}
const { started, cps, phase, timestamp } = payload;
if (cps !== this.cps) {
this.setCps(cps);
}
// TODO: I'm not sure how to hook this up this phase adjustment in Strudel
// a phase adjustment message is sent every 30 seconds from backend to keep clocks in sync
const phaseDiff = Math.abs(phase - this.clock.getPhase());
if (phaseDiff > 0.1) {
console.log('set phase from', this.clock.getPhase(), 'to', phase);
// hmmm this seems wrong...
//this.clock.setPhase(phase);
}
if (this.started !== started && started != null) {
if (started) {
// the time delay in ms that seems to occur when starting the clock. Unsure if this is standard across all clients
const evaluationTime = 140;
// when start message comes from abelink, delay starting cyclist clock until the start of the next abelink phase
this.start_timer = window.setTimeout(() => {
// TODO: evaluate the code so if another source triggers the play there will not be an error
logger('[cyclist] start');
this.clock.start();
this.setStarted(true);
}, timestamp - Date.now() - evaluationTime);
} else {
this.stop();
}
}
});
}
start() {
if (!this.pattern) {
throw new Error('Scheduler: no pattern set! call .setPattern first.');
}
const linkmsg = {
cps: this.cps,
started: true,
timestamp: Date.now(),
phase: this.clock.getPhase(),
};
Invoke('sendabelinkmsg', { linkmsg });
}
stop() {
logger('[cyclist] stop');
this.clock.stop();
this.lastEnd = 0;
this.setStarted(false);
const linkmsg = {
// TODO: change this to value of "main" clock cps
cps: 0,
started: false,
timestamp: Date.now(),
phase: this.clock.getPhase(),
};
Invoke('sendabelinkmsg', { linkmsg });
}
}
+1
View File
@@ -7,3 +7,4 @@ This program is free software: you can redistribute it and/or modify it under th
export * from './midibridge.mjs';
export * from './utils.mjs';
export * from './oscbridge.mjs';
export * from './cyclistbridge.mjs';
+1 -1
View File
@@ -6,7 +6,7 @@ class Strudel extends HTMLElement {
setTimeout(() => {
const code = (this.innerHTML + '').replace('<!--', '').replace('-->', '').trim();
const iframe = document.createElement('iframe');
const src = `https://strudel.cc/#${encodeURIComponent(btoa(code))}`;
const src = `https://strudel.tidalcycles.org/#${encodeURIComponent(btoa(code))}`;
// const src = `http://localhost:3000/#${encodeURIComponent(btoa(code))}`;
iframe.setAttribute('src', src);
iframe.setAttribute('width', '600');
-29
View File
@@ -1,29 +0,0 @@
# @strudel/hydra
This package integrates [hydra-synth](https://www.npmjs.com/package/hydra-synth) into strudel.
## Usage in Strudel
This package is imported into strudel by default. To activate Hydra, place this code at the top of your code:
```js
await initHydra();
```
Then you can use hydra below!
## Usage via npm
```sh
npm i @strudel/hydra
```
Then add the import to your evalScope:
```js
import { evalScope } from '@strudel.cycles/core';
evalScope(
import('@strudel/hydra')
)
```
-15
View File
@@ -1,15 +0,0 @@
import { getDrawContext } from '@strudel.cycles/core';
export async function initHydra() {
if (!document.getElementById('hydra-canvas')) {
const { canvas: testCanvas } = getDrawContext();
await import('https://unpkg.com/hydra-synth');
const hydraCanvas = testCanvas.cloneNode(true);
hydraCanvas.id = 'hydra-canvas';
testCanvas.after(hydraCanvas);
new Hydra({ canvas: hydraCanvas, detectAudio: false });
s0.init({ src: testCanvas });
}
}
export const H = (p) => () => p.queryArc(getTime(), getTime())[0].value;
-43
View File
@@ -1,43 +0,0 @@
{
"name": "@strudel/hydra",
"version": "0.9.0",
"description": "Hydra integration for strudel",
"main": "hydra.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"server": "node server.js",
"tidal-sniffer": "node tidal-sniffer.js",
"client": "npx serve -p 4321",
"build-bin": "npx pkg server.js --targets node16-macos-x64,node16-win-x64,node16-linux-x64 --out-path bin",
"build": "vite build",
"prepublishOnly": "npm run build"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tidalcycles/strudel.git"
},
"keywords": [
"tidalcycles",
"strudel",
"pattern",
"livecoding",
"algorave"
],
"author": "Felix Roos <flix91@gmail.com>",
"license": "AGPL-3.0-or-later",
"bugs": {
"url": "https://github.com/tidalcycles/strudel/issues"
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"hydra-synth": "^1.3.29"
},
"devDependencies": {
"pkg": "^5.8.1",
"vite": "^4.3.3"
}
}
-19
View File
@@ -1,19 +0,0 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'hydra.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
+2 -2
View File
@@ -32,7 +32,7 @@ yields:
## Mini Notation API
See "Mini Notation" in the [Strudel Tutorial](https://strudel.cc/learn/mini-notation)
See "Mini Notation" in the [Strudel Tutorial](https://strudel.tidalcycles.org/learn/mini-notation)
## Building the Parser
@@ -40,5 +40,5 @@ The parser [krill-parser.js] is generated from [krill.pegjs](./krill.pegjs) usin
To generate the parser, run
```js
npm run build:parser
npm build:parser
```
File diff suppressed because one or more lines are too long
+1 -24
View File
@@ -103,8 +103,7 @@ quote = '"' / "'"
// ------------------ steps and cycles ---------------------------
// single step definition (e.g bd)
step_char "a letter, a number, \"-\", \"#\", \".\", \"^\", \"_\"" =
unicode_letter / [0-9~] / "-" / "#" / "." / "^" / "_"
step_char = [0-9a-zA-Z~] / "-" / "#" / "." / "^" / "_"
step = ws chars:step_char+ ws { return new AtomStub(chars.join("")) }
// define a sub cycle e.g. [1 2, 3 [4]]
@@ -266,25 +265,3 @@ hush = "hush"
// ---------------------- statements ----------------------------
statement = mini_definition / command
// ---------------------- unicode ----------------------------
unicode_letter = Lu / Ll / Lt / Lm / Lo / Nl
// Letter, Lowercase
Ll = [\u0061-\u007A\u00B5\u00DF-\u00F6\u00F8-\u00FF\u0101\u0103\u0105\u0107\u0109\u010B\u010D\u010F\u0111\u0113\u0115\u0117\u0119\u011B\u011D\u011F\u0121\u0123\u0125\u0127\u0129\u012B\u012D\u012F\u0131\u0133\u0135\u0137-\u0138\u013A\u013C\u013E\u0140\u0142\u0144\u0146\u0148-\u0149\u014B\u014D\u014F\u0151\u0153\u0155\u0157\u0159\u015B\u015D\u015F\u0161\u0163\u0165\u0167\u0169\u016B\u016D\u016F\u0171\u0173\u0175\u0177\u017A\u017C\u017E-\u0180\u0183\u0185\u0188\u018C-\u018D\u0192\u0195\u0199-\u019B\u019E\u01A1\u01A3\u01A5\u01A8\u01AA-\u01AB\u01AD\u01B0\u01B4\u01B6\u01B9-\u01BA\u01BD-\u01BF\u01C6\u01C9\u01CC\u01CE\u01D0\u01D2\u01D4\u01D6\u01D8\u01DA\u01DC-\u01DD\u01DF\u01E1\u01E3\u01E5\u01E7\u01E9\u01EB\u01ED\u01EF-\u01F0\u01F3\u01F5\u01F9\u01FB\u01FD\u01FF\u0201\u0203\u0205\u0207\u0209\u020B\u020D\u020F\u0211\u0213\u0215\u0217\u0219\u021B\u021D\u021F\u0221\u0223\u0225\u0227\u0229\u022B\u022D\u022F\u0231\u0233-\u0239\u023C\u023F-\u0240\u0242\u0247\u0249\u024B\u024D\u024F-\u0293\u0295-\u02AF\u0371\u0373\u0377\u037B-\u037D\u0390\u03AC-\u03CE\u03D0-\u03D1\u03D5-\u03D7\u03D9\u03DB\u03DD\u03DF\u03E1\u03E3\u03E5\u03E7\u03E9\u03EB\u03ED\u03EF-\u03F3\u03F5\u03F8\u03FB-\u03FC\u0430-\u045F\u0461\u0463\u0465\u0467\u0469\u046B\u046D\u046F\u0471\u0473\u0475\u0477\u0479\u047B\u047D\u047F\u0481\u048B\u048D\u048F\u0491\u0493\u0495\u0497\u0499\u049B\u049D\u049F\u04A1\u04A3\u04A5\u04A7\u04A9\u04AB\u04AD\u04AF\u04B1\u04B3\u04B5\u04B7\u04B9\u04BB\u04BD\u04BF\u04C2\u04C4\u04C6\u04C8\u04CA\u04CC\u04CE-\u04CF\u04D1\u04D3\u04D5\u04D7\u04D9\u04DB\u04DD\u04DF\u04E1\u04E3\u04E5\u04E7\u04E9\u04EB\u04ED\u04EF\u04F1\u04F3\u04F5\u04F7\u04F9\u04FB\u04FD\u04FF\u0501\u0503\u0505\u0507\u0509\u050B\u050D\u050F\u0511\u0513\u0515\u0517\u0519\u051B\u051D\u051F\u0521\u0523\u0525\u0527\u0529\u052B\u052D\u052F\u0560-\u0588\u10D0-\u10FA\u10FD-\u10FF\u13F8-\u13FD\u1C80-\u1C88\u1D00-\u1D2B\u1D6B-\u1D77\u1D79-\u1D9A\u1E01\u1E03\u1E05\u1E07\u1E09\u1E0B\u1E0D\u1E0F\u1E11\u1E13\u1E15\u1E17\u1E19\u1E1B\u1E1D\u1E1F\u1E21\u1E23\u1E25\u1E27\u1E29\u1E2B\u1E2D\u1E2F\u1E31\u1E33\u1E35\u1E37\u1E39\u1E3B\u1E3D\u1E3F\u1E41\u1E43\u1E45\u1E47\u1E49\u1E4B\u1E4D\u1E4F\u1E51\u1E53\u1E55\u1E57\u1E59\u1E5B\u1E5D\u1E5F\u1E61\u1E63\u1E65\u1E67\u1E69\u1E6B\u1E6D\u1E6F\u1E71\u1E73\u1E75\u1E77\u1E79\u1E7B\u1E7D\u1E7F\u1E81\u1E83\u1E85\u1E87\u1E89\u1E8B\u1E8D\u1E8F\u1E91\u1E93\u1E95-\u1E9D\u1E9F\u1EA1\u1EA3\u1EA5\u1EA7\u1EA9\u1EAB\u1EAD\u1EAF\u1EB1\u1EB3\u1EB5\u1EB7\u1EB9\u1EBB\u1EBD\u1EBF\u1EC1\u1EC3\u1EC5\u1EC7\u1EC9\u1ECB\u1ECD\u1ECF\u1ED1\u1ED3\u1ED5\u1ED7\u1ED9\u1EDB\u1EDD\u1EDF\u1EE1\u1EE3\u1EE5\u1EE7\u1EE9\u1EEB\u1EED\u1EEF\u1EF1\u1EF3\u1EF5\u1EF7\u1EF9\u1EFB\u1EFD\u1EFF-\u1F07\u1F10-\u1F15\u1F20-\u1F27\u1F30-\u1F37\u1F40-\u1F45\u1F50-\u1F57\u1F60-\u1F67\u1F70-\u1F7D\u1F80-\u1F87\u1F90-\u1F97\u1FA0-\u1FA7\u1FB0-\u1FB4\u1FB6-\u1FB7\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FC7\u1FD0-\u1FD3\u1FD6-\u1FD7\u1FE0-\u1FE7\u1FF2-\u1FF4\u1FF6-\u1FF7\u210A\u210E-\u210F\u2113\u212F\u2134\u2139\u213C-\u213D\u2146-\u2149\u214E\u2184\u2C30-\u2C5E\u2C61\u2C65-\u2C66\u2C68\u2C6A\u2C6C\u2C71\u2C73-\u2C74\u2C76-\u2C7B\u2C81\u2C83\u2C85\u2C87\u2C89\u2C8B\u2C8D\u2C8F\u2C91\u2C93\u2C95\u2C97\u2C99\u2C9B\u2C9D\u2C9F\u2CA1\u2CA3\u2CA5\u2CA7\u2CA9\u2CAB\u2CAD\u2CAF\u2CB1\u2CB3\u2CB5\u2CB7\u2CB9\u2CBB\u2CBD\u2CBF\u2CC1\u2CC3\u2CC5\u2CC7\u2CC9\u2CCB\u2CCD\u2CCF\u2CD1\u2CD3\u2CD5\u2CD7\u2CD9\u2CDB\u2CDD\u2CDF\u2CE1\u2CE3-\u2CE4\u2CEC\u2CEE\u2CF3\u2D00-\u2D25\u2D27\u2D2D\uA641\uA643\uA645\uA647\uA649\uA64B\uA64D\uA64F\uA651\uA653\uA655\uA657\uA659\uA65B\uA65D\uA65F\uA661\uA663\uA665\uA667\uA669\uA66B\uA66D\uA681\uA683\uA685\uA687\uA689\uA68B\uA68D\uA68F\uA691\uA693\uA695\uA697\uA699\uA69B\uA723\uA725\uA727\uA729\uA72B\uA72D\uA72F-\uA731\uA733\uA735\uA737\uA739\uA73B\uA73D\uA73F\uA741\uA743\uA745\uA747\uA749\uA74B\uA74D\uA74F\uA751\uA753\uA755\uA757\uA759\uA75B\uA75D\uA75F\uA761\uA763\uA765\uA767\uA769\uA76B\uA76D\uA76F\uA771-\uA778\uA77A\uA77C\uA77F\uA781\uA783\uA785\uA787\uA78C\uA78E\uA791\uA793-\uA795\uA797\uA799\uA79B\uA79D\uA79F\uA7A1\uA7A3\uA7A5\uA7A7\uA7A9\uA7AF\uA7B5\uA7B7\uA7B9\uA7FA\uAB30-\uAB5A\uAB60-\uAB65\uAB70-\uABBF\uFB00-\uFB06\uFB13-\uFB17\uFF41-\uFF5A]
// Letter, Modifier
Lm = [\u02B0-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0374\u037A\u0559\u0640\u06E5-\u06E6\u07F4-\u07F5\u07FA\u081A\u0824\u0828\u0971\u0E46\u0EC6\u10FC\u17D7\u1843\u1AA7\u1C78-\u1C7D\u1D2C-\u1D6A\u1D78\u1D9B-\u1DBF\u2071\u207F\u2090-\u209C\u2C7C-\u2C7D\u2D6F\u2E2F\u3005\u3031-\u3035\u303B\u309D-\u309E\u30FC-\u30FE\uA015\uA4F8-\uA4FD\uA60C\uA67F\uA69C-\uA69D\uA717-\uA71F\uA770\uA788\uA7F8-\uA7F9\uA9CF\uA9E6\uAA70\uAADD\uAAF3-\uAAF4\uAB5C-\uAB5F\uFF70\uFF9E-\uFF9F]
// Letter, Other
Lo = [\u00AA\u00BA\u01BB\u01C0-\u01C3\u0294\u05D0-\u05EA\u05EF-\u05F2\u0620-\u063F\u0641-\u064A\u066E-\u066F\u0671-\u06D3\u06D5\u06EE-\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u0800-\u0815\u0840-\u0858\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0972-\u0980\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u09FC\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0-\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60-\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0-\u0CE1\u0CF1-\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32-\u0E33\u0E40-\u0E45\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065-\u1066\u106E-\u1070\u1075-\u1081\u108E\u1100-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17DC\u1820-\u1842\u1844-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE-\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C77\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5-\u1CF6\u2135-\u2138\u2D30-\u2D67\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3006\u303C\u3041-\u3096\u309F\u30A1-\u30FA\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEF\uA000-\uA014\uA016-\uA48C\uA4D0-\uA4F7\uA500-\uA60B\uA610-\uA61F\uA62A-\uA62B\uA66E\uA6A0-\uA6E5\uA78F\uA7F7\uA7FB-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD-\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9E0-\uA9E4\uA9E7-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA6F\uAA71-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5-\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADC\uAAE0-\uAAEA\uAAF2\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF66-\uFF6F\uFF71-\uFF9D\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]
// Letter, Titlecase
Lt = [\u01C5\u01C8\u01CB\u01F2\u1F88-\u1F8F\u1F98-\u1F9F\u1FA8-\u1FAF\u1FBC\u1FCC\u1FFC]
// Letter, Uppercase
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]
+2 -2
View File
@@ -34,6 +34,6 @@ Now open the REPL and type:
s("<bd sd> hh").osc()
```
or just [click here](https://strudel.cc/#cygiPGJkIHNkPiBoaCIpLm9zYygp)...
or just [click here](https://strudel.tidalcycles.org/#cygiPGJkIHNkPiBoaCIpLm9zYygp)...
You can read more about [how to use Superdirt with Strudel the Tutorial](https://strudel.cc/learn/input-output/#superdirt-api)
You can read more about [how to use Superdirt with Strudel the Tutorial](https://strudel.tidalcycles.org/learn/input-output/#superdirt-api)
+1 -1
View File
@@ -37,7 +37,7 @@ function connect() {
/**
*
* Sends each hap as an OSC message, which can be picked up by SuperCollider or any other OSC-enabled software.
* For more info, read [MIDI & OSC in the docs](https://strudel.cc/learn/input-output)
* For more info, read [MIDI & OSC in the docs](https://strudel.tidalcycles.org/learn/input-output)
*
* @name osc
* @memberof Pattern
+3 -3
View File
@@ -21,12 +21,12 @@ import { samples, initAudioOnFirstClick } from '@strudel.cycles/webaudio';
async function prebake() {
await samples(
'https://strudel.cc/tidal-drum-machines.json',
'https://strudel.tidalcycles.org/tidal-drum-machines.json',
'github:ritchse/tidal-drum-machines/main/machines/'
);
await samples(
'https://strudel.cc/EmuSP12.json',
'https://strudel.cc/EmuSP12/'
'https://strudel.tidalcycles.org/EmuSP12.json',
'https://strudel.tidalcycles.org/EmuSP12/'
);
}
+28 -23
View File
@@ -1,5 +1,5 @@
import { controls, evalScope } from '@strudel.cycles/core';
import { CodeMirror, useHighlighting, useStrudel, flash } from '@strudel.cycles/react';
import { CodeMirror, useHighlighting, useKeydown, useStrudel, flash } from '@strudel.cycles/react';
import {
getAudioContext,
initAudioOnFirstClick,
@@ -93,6 +93,32 @@ function App() {
});
const error = evalError || schedulerError;
useKeydown(
useCallback(
async (e) => {
if (e.ctrlKey || e.altKey) {
if (e.code === 'Enter') {
e.preventDefault();
flash(view);
await evaluate(code);
if (e.shiftKey) {
panic();
scheduler.stop();
scheduler.start();
}
if (!scheduler.started) {
scheduler.start();
}
} else if (e.code === 'Period') {
scheduler.stop();
panic();
e.preventDefault();
}
}
},
[scheduler, evaluate, view, code],
),
);
return (
<div>
<nav className="z-[12] w-full flex justify-center fixed bottom-0">
@@ -110,28 +136,7 @@ function App() {
</div>
{error && <p>error {error.message}</p>}
</nav>
<CodeMirror
value={code}
onChange={setCode}
onViewChanged={setView}
onEvaluate={async () => {
flash(view);
await evaluate(code);
if (!scheduler.started) {
scheduler.start();
}
}}
onReEvaluate={async () => {
await evaluate(code);
panic();
scheduler.stop();
scheduler.start();
}}
onStop={() => {
scheduler.stop();
panic();
}}
/>
<CodeMirror value={code} onChange={setCode} onViewChanged={setView} />
</div>
);
}
-5
View File
@@ -33,17 +33,12 @@
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@codemirror/autocomplete": "^6.6.0",
"@codemirror/commands": "^6.0.0",
"@codemirror/lang-javascript": "^6.1.7",
"@codemirror/language": "^6.0.0",
"@codemirror/lint": "^6.0.0",
"@codemirror/search": "^6.0.0",
"@codemirror/state": "^6.2.0",
"@codemirror/view": "^6.10.0",
"@lezer/highlight": "^1.1.4",
"@replit/codemirror-emacs": "^6.0.1",
"@replit/codemirror-vim": "^6.0.14",
"@replit/codemirror-vscode-keymap": "^6.0.2",
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
@@ -26,6 +26,7 @@ export function Autocomplete({ doc }) {
<pre
className="cursor-pointer"
onMouseDown={(e) => {
console.log('ola!');
navigator.clipboard.writeText(example);
e.stopPropagation();
}}
+2 -69
View File
@@ -1,15 +1,12 @@
import { autocompletion } from '@codemirror/autocomplete';
import { Prec } from '@codemirror/state';
import { javascript, javascriptLanguage } from '@codemirror/lang-javascript';
import { ViewPlugin, EditorView, keymap } from '@codemirror/view';
import { EditorView } from '@codemirror/view';
import { emacs } from '@replit/codemirror-emacs';
import { vim } from '@replit/codemirror-vim';
import { vscodeKeymap } from '@replit/codemirror-vscode-keymap';
import _CodeMirror from '@uiw/react-codemirror';
import React, { useCallback, useMemo } from 'react';
import strudelTheme from '../themes/strudel-theme';
import { strudelAutocomplete } from './Autocomplete';
import { strudelTooltip } from './Tooltip';
import {
highlightExtension,
flashField,
@@ -30,15 +27,10 @@ export default function CodeMirror({
onViewChanged,
onSelectionChange,
onDocChange,
onEvaluate,
onReEvaluate,
onPanic,
onStop,
theme,
keybindings,
isLineNumbersDisplayed,
isAutoCompletionEnabled,
isTooltipEnabled,
isLineWrappingEnabled,
fontSize = 18,
fontFamily = 'monospace',
@@ -69,25 +61,11 @@ export default function CodeMirror({
[onSelectionChange],
);
const vscodePlugin = ViewPlugin.fromClass(
class {
constructor(view) {}
},
{
provide: (plugin) => {
return Prec.highest(keymap.of([...vscodeKeymap]));
},
},
);
const vscodeExtension = (options) => [vscodePlugin].concat(options ?? []);
const extensions = useMemo(() => {
let _extensions = [...staticExtensions];
let bindings = {
vim,
emacs,
vscode: vscodeExtension,
};
if (bindings[keybindings]) {
@@ -99,58 +77,13 @@ export default function CodeMirror({
} else {
_extensions.push(autocompletion({ override: [] }));
}
if (isTooltipEnabled) {
_extensions.push(strudelTooltip);
}
//_extensions.push([keymap.of({})]);
if (isLineWrappingEnabled) {
_extensions.push(EditorView.lineWrapping);
}
_extensions.push(
keymap.of([
{
key: 'Ctrl-Enter',
run: () => onEvaluate?.(),
},
{
key: 'Alt-Enter',
run: () => onEvaluate?.(),
},
{
key: 'Ctrl-.',
run: () => onStop?.(),
},
{
key: 'Alt-.',
run: (_, e) => {
e.preventDefault();
onStop?.();
},
},
{
key: 'Ctrl-Shift-.',
run: () => (onPanic ? onPanic() : onStop?.()),
},
{
key: 'Ctrl-Shift-Enter',
run: () => (onReEvaluate ? onReEvaluate() : onEvaluate?.()),
},
]),
);
return _extensions;
}, [
keybindings,
isAutoCompletionEnabled,
isTooltipEnabled,
isLineWrappingEnabled,
onEvaluate,
onReEvaluate,
onStop,
onPanic,
]);
}, [keybindings, isAutoCompletionEnabled, isLineWrappingEnabled]);
const basicSetup = useMemo(() => ({ lineNumbers: isLineNumbersDisplayed }), [isLineNumbersDisplayed]);
+22 -5
View File
@@ -10,6 +10,7 @@ import { Icon } from './Icon';
import './style.css';
import { logger } from '@strudel.cycles/core';
import useEvent from '../hooks/useEvent.mjs';
import useKeydown from '../hooks/useKeydown.mjs';
const getTime = () => getAudioContext().currentTime;
@@ -91,6 +92,27 @@ export function MiniRepl({
getTime: () => scheduler.now(),
});
// keyboard shortcuts
useKeydown(
useCallback(
async (e) => {
if (view?.hasFocus) {
if (e.ctrlKey || e.altKey) {
if (e.code === 'Enter') {
e.preventDefault();
flash(view);
await activateCode();
} else if (e.key === '.' || e.code === 'Period') {
stop();
e.preventDefault();
}
}
}
},
[activateCode, stop, view],
),
);
const [log, setLog] = useState([]);
useLogger(
useCallback((e) => {
@@ -142,11 +164,6 @@ export function MiniRepl({
fontSize={fontSize}
keybindings={keybindings}
isLineNumbersDisplayed={isLineNumbersDisplayed}
onEvaluate={() => {
flash(view);
activateCode();
}}
onStop={() => stop()}
/>
)}
{error && <div className="text-right p-1 text-md text-red-200">{error.message}</div>}
-69
View File
@@ -1,69 +0,0 @@
import { createRoot } from 'react-dom/client';
import { hoverTooltip } from '@codemirror/view';
import jsdoc from '../../../../doc.json';
import { Autocomplete } from './Autocomplete';
const getDocLabel = (doc) => doc.name || doc.longname;
let ctrlDown = false;
// Record Control key event to trigger or block the tooltip depending on the state
window.addEventListener(
'keyup',
function (e) {
if (e.key == 'Control') {
ctrlDown = false;
}
},
true,
);
window.addEventListener(
'keydown',
function (e) {
if (e.key == 'Control') {
ctrlDown = true;
}
},
true,
);
export const strudelTooltip = hoverTooltip(
(view, pos, side) => {
// Word selection from CodeMirror Hover Tooltip example https://codemirror.net/examples/tooltip/#hover-tooltips
let { from, to, text } = view.state.doc.lineAt(pos);
let start = pos,
end = pos;
while (start > from && /\w/.test(text[start - from - 1])) {
start--;
}
while (end < to && /\w/.test(text[end - from])) {
end++;
}
if ((start == pos && side < 0) || (end == pos && side > 0)) {
return null;
}
let word = text.slice(start - from, end - from);
// Get entry from Strudel documentation
let entry = jsdoc.docs.filter((doc) => getDocLabel(doc) === word)[0];
if (!entry) {
return null;
}
if (!ctrlDown) {
return null;
}
return {
pos: start,
end,
above: false,
arrow: true,
create(view) {
let dom = document.createElement('div');
dom.className = 'strudel-tooltip';
createRoot(dom).render(<Autocomplete doc={entry} />);
return { dom };
},
};
},
{ hoverTime: 10 },
);
-4
View File
@@ -28,7 +28,3 @@
footer {
z-index: 0 !important;
}
.strudel-tooltip {
padding: 5px;
}
+2
View File
@@ -19,6 +19,7 @@ function useStrudel({
drawContext,
drawTime = [-2, 2],
paintOptions = {},
createCyclist, // (params) => Cyclist
}) {
const id = useMemo(() => s4(), []);
canvasId = canvasId || `canvas-${id}`;
@@ -45,6 +46,7 @@ function useStrudel({
onEvalError?.(err);
},
getTime,
createCyclist,
drawContext,
transpiler,
editPattern,
+9 -9
View File
@@ -6,23 +6,23 @@ This program is free software: you can redistribute it and/or modify it under th
import { Pattern, isPattern } from '@strudel.cycles/core';
var writeMessagers = {};
var writeMessage;
var choosing = false;
export async function getWriter(name, br) {
export async function getWriter(br = 38400) {
if (choosing) {
return;
}
choosing = true;
if (name in writeMessagers) {
return writeMessagers[name];
if (writeMessage) {
return writeMessage;
}
if ('serial' in navigator) {
const port = await navigator.serial.requestPort();
await port.open({ baudRate: br });
const encoder = new TextEncoder();
const writer = port.writable.getWriter();
writeMessagers[name] = function (message, chk) {
writeMessage = function (message, chk) {
const encoded = encoder.encode(message);
if (!chk) {
writer.write(encoded);
@@ -63,10 +63,10 @@ function crc16(data) {
return crc & 0xffff;
}
Pattern.prototype.serial = function (br = 115200, sendcrc = false, singlecharids = false, name = 'default') {
Pattern.prototype.serial = function (br = 38400, sendcrc = false, singlecharids = false) {
return this.withHap((hap) => {
if (!(name in writeMessagers)) {
getWriter(name, br);
if (!writeMessage) {
getWriter(br);
}
const onTrigger = (time, hap, currentTime) => {
var message = '';
@@ -108,7 +108,7 @@ Pattern.prototype.serial = function (br = 115200, sendcrc = false, singlecharids
const offset = (time - currentTime + latency) * 1000;
window.setTimeout(function () {
writeMessagers[name](message, chk);
writeMessage(message, chk);
}, offset);
};
return hap.setContext({ ...hap.context, onTrigger, dominantTrigger: true });
+1 -5
View File
@@ -1,7 +1,7 @@
# superdough
superdough is a simple web audio sampler and synth, intended for live coding.
It is the default output of [strudel](https://strudel.cc/).
It is the default output of [strudel](https://strudel.tidalcycles.org/).
This package has no ties to strudel and can be used to quickly bake your own music system on the web.
## Install
@@ -67,10 +67,6 @@ superdough({ s: 'bd', delay: 0.5 }, 0, 1);
- `crush`: amplitude bit crusher using given number of bits
- `shape`: distortion effect from 0 (none) to 1 (full). might get loud!
- `pan`: stereo panning from 0 (left) to 1 (right)
- `phaser`: sets the speed of the modulation
- `phaserdepth`: the amount the signal is affected by the phaser effect.
- `phasersweep`: the frequency sweep range of the lfo for the phaser effect.
- `phasercenter`: the amount the signal is affected by the phaser effect.
- `vowel`: vowel filter. possible values: "a", "e", "i", "o", "u"
- `delay`: delay mix
- `delayfeedback`: delay feedback
-11
View File
@@ -78,17 +78,6 @@ export const getParamADSR = (param, attack, decay, sustain, release, min, max, b
param.linearRampToValueAtTime(min, end + Math.max(release, 0.1));
};
export function getCompressor(ac, threshold, ratio, knee, attack, release) {
const options = {
threshold: threshold ?? -3,
ratio: ratio ?? 10,
knee: knee ?? 10,
attack: attack ?? 0.005,
release: release ?? 0.05,
};
return new DynamicsCompressorNode(ac, options);
}
export function createFilter(
context,
type,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "superdough",
"version": "0.9.10",
"version": "0.9.9",
"description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.",
"main": "index.mjs",
"type": "module",
+18 -35
View File
@@ -1,47 +1,30 @@
import reverbGen from './reverbGen.mjs';
if (typeof AudioContext !== 'undefined') {
AudioContext.prototype.adjustLength = function (duration, buffer) {
const newLength = buffer.sampleRate * duration;
const newBuffer = this.createBuffer(buffer.numberOfChannels, buffer.length, buffer.sampleRate);
for (let channel = 0; channel < buffer.numberOfChannels; channel++) {
let oldData = buffer.getChannelData(channel);
let newData = newBuffer.getChannelData(channel);
for (let i = 0; i < newLength; i++) {
newData[i] = oldData[i] || 0;
}
}
return newBuffer;
};
AudioContext.prototype.createReverb = function (duration, fade, lp, dim, ir) {
AudioContext.prototype.generateReverb = reverbGen.generateReverb;
AudioContext.prototype.createReverb = function (duration, fade, lp, dim) {
const convolver = this.createConvolver();
convolver.generate = (d = 2, fade = 0.1, lp = 15000, dim = 1000, ir) => {
convolver.generate = (d, fade, lp, dim) => {
this.generateReverb(
{
audioContext: this,
sampleRate: 44100,
numChannels: 2,
decayTime: d,
fadeInTime: fade,
lpFreqStart: lp,
lpFreqEnd: dim,
},
(buffer) => {
convolver.buffer = buffer;
},
);
convolver.duration = d;
convolver.fade = fade;
convolver.lp = lp;
convolver.dim = dim;
convolver.ir = ir;
if (ir) {
convolver.buffer = this.adjustLength(d, ir);
} else {
reverbGen.generateReverb(
{
audioContext: this,
numChannels: 2,
decayTime: d,
fadeInTime: fade,
lpFreqStart: lp,
lpFreqEnd: dim,
},
(buffer) => {
convolver.buffer = buffer;
},
);
}
};
convolver.generate(duration, fade, lp, dim, ir);
convolver.generate(duration, fade, lp, dim);
return convolver;
};
}
+22 -23
View File
@@ -16,14 +16,14 @@ var reverbGen = {};
/** Generates a reverb impulse response.
@param {!Object} params TODO: Document the properties.
@param {!function(!AudioBuffer)} callback Function to call when
the impulse response has been generated. The impulse response
is passed to this function as its parameter. May be called
immediately within the current execution context, or later. */
@param {!Object} params TODO: Document the properties.
@param {!function(!AudioBuffer)} callback Function to call when
the impulse response has been generated. The impulse response
is passed to this function as its parameter. May be called
immediately within the current execution context, or later. */
reverbGen.generateReverb = function (params, callback) {
var audioContext = params.audioContext || new AudioContext();
var sampleRate = audioContext.sampleRate;
var sampleRate = params.sampleRate || 44100;
var numChannels = params.numChannels || 2;
// params.decayTime is the -60dB fade time. We let it go 50% longer to get to -90dB.
var totalTime = params.decayTime * 1.5;
@@ -48,13 +48,12 @@ reverbGen.generateReverb = function (params, callback) {
/** Creates a canvas element showing a graph of the given data.
@param {!Float32Array} data An array of numbers, or a Float32Array.
@param {number} width Width in pixels of the canvas.
@param {number} height Height in pixels of the canvas.
@param {number} min Minimum value of data for the graph (lower edge).
@param {number} max Maximum value of data in the graph (upper edge).
@return {!CanvasElement} The generated canvas element. */
@param {!Float32Array} data An array of numbers, or a Float32Array.
@param {number} width Width in pixels of the canvas.
@param {number} height Height in pixels of the canvas.
@param {number} min Minimum value of data for the graph (lower edge).
@param {number} max Maximum value of data in the graph (upper edge).
@return {!CanvasElement} The generated canvas element. */
reverbGen.generateGraph = function (data, width, height, min, max) {
var canvas = document.createElement('canvas');
canvas.width = width;
@@ -73,13 +72,13 @@ reverbGen.generateGraph = function (data, width, height, min, max) {
/** Applies a constantly changing lowpass filter to the given sound.
@private
@param {!AudioBuffer} input
@param {number} lpFreqStart
@param {number} lpFreqEnd
@param {number} lpFreqEndAt
@param {!function(!AudioBuffer)} callback May be called
immediately within the current execution context, or later.*/
@private
@param {!AudioBuffer} input
@param {number} lpFreqStart
@param {number} lpFreqEnd
@param {number} lpFreqEndAt
@param {!function(!AudioBuffer)} callback May be called
immediately within the current execution context, or later.*/
var applyGradualLowpass = function (input, lpFreqStart, lpFreqEnd, lpFreqEndAt, callback) {
if (lpFreqStart == 0) {
callback(input);
@@ -111,8 +110,8 @@ var applyGradualLowpass = function (input, lpFreqStart, lpFreqEnd, lpFreqEndAt,
};
/** @private
@param {!AudioBuffer} buffer
@return {!Array.<!Float32Array>} An array containing the Float32Array of each channel's samples. */
@param {!AudioBuffer} buffer
@return {!Array.<!Float32Array>} An array containing the Float32Array of each channel's samples. */
var getAllChannelData = function (buffer) {
var channels = [];
for (var i = 0; i < buffer.numberOfChannels; i++) {
@@ -122,7 +121,7 @@ var getAllChannelData = function (buffer) {
};
/** @private
@return {number} A random number from -1 to 1. */
@return {number} A random number from -1 to 1. */
var randomSample = function () {
return Math.random() * 2 - 1;
};
+2 -46
View File
@@ -57,13 +57,13 @@ export const getSampleBufferSource = async (s, n, note, speed, freq, bank, resol
const bufferSource = ac.createBufferSource();
bufferSource.buffer = buffer;
const playbackRate = 1.0 * Math.pow(2, transpose / 12);
// bufferSource.playbackRate.value = Math.pow(2, transpose / 12);
bufferSource.playbackRate.value = playbackRate;
return bufferSource;
};
export const loadBuffer = (url, ac, s, n = 0) => {
const label = s ? `sound "${s}:${n}"` : 'sample';
url = url.replace('#', '%23');
if (!loadCache[url]) {
logger(`[sampler] load ${label}..`, 'load-sample', { url });
const timestamp = Date.now();
@@ -146,12 +146,7 @@ function getSamplesPrefixHandler(url) {
* sd: '808sd/SD0010.WAV'
* }, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/');
* s("[bd ~]*2, [~ hh]*2, ~ sd")
* @example
* samples('shabda:noise,chimp:2')
* s("noise <chimp:0*2 chimp:1>")
* @example
* samples('shabda/speech/fr-FR/f:chocolat')
* s("chocolat*4")
*
*/
export const samples = async (sampleMap, baseUrl = sampleMap._base || '', options = {}) => {
@@ -161,34 +156,11 @@ export const samples = async (sampleMap, baseUrl = sampleMap._base || '', option
if (handler) {
return handler(sampleMap);
}
if (sampleMap.startsWith('bubo:')) {
const [_, repo] = sampleMap.split(':');
sampleMap = `github:Bubobubobubobubo/dough-${repo}`;
}
if (sampleMap.startsWith('github:')) {
let [_, path] = sampleMap.split('github:');
path = path.endsWith('/') ? path.slice(0, -1) : path;
if (path.split('/').length === 2) {
// assume main as default branch if none set
path += '/main';
}
sampleMap = `https://raw.githubusercontent.com/${path}/strudel.json`;
}
if (sampleMap.startsWith('shabda:')) {
let [_, path] = sampleMap.split('shabda:');
sampleMap = `https://shabda.ndre.gr/${path}.json?strudel=1`;
}
if (sampleMap.startsWith('shabda/speech')) {
let [_, path] = sampleMap.split('shabda/speech');
path = path.startsWith('/') ? path.substring(1) : path;
let [params, words] = path.split(':');
let gender = 'f';
let language = 'en-GB';
if (params) {
[language, gender] = params.split('/');
}
sampleMap = `https://shabda.ndre.gr/speech/${words}.json?gender=${gender}&language=${language}&strudel=1'`;
}
if (typeof fetch !== 'function') {
// not a browser
return;
@@ -239,8 +211,6 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
begin = 0,
loopEnd = 1,
end = 1,
vib,
vibmod = 0.5,
} = value;
// load sample
if (speed === 0) {
@@ -256,19 +226,6 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
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);
}
// asny stuff above took too long?
if (ac.currentTime > t) {
logger(`[sampler] still loading sound "${s}:${n}"`, 'highlight');
@@ -300,7 +257,6 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
envelope.connect(out);
bufferSource.onended = function () {
bufferSource.disconnect();
vibratoOscillator?.stop();
envelope.disconnect();
out.disconnect();
onended();
+17 -109
View File
@@ -9,21 +9,17 @@ import './reverb.mjs';
import './vowel.mjs';
import { clamp } from './util.mjs';
import workletsUrl from './worklets.mjs?url';
import { createFilter, gainNode, getCompressor } from './helpers.mjs';
import { createFilter, gainNode } from './helpers.mjs';
import { map } from 'nanostores';
import { logger } from './logger.mjs';
import { loadBuffer } from './sampler.mjs';
export const soundMap = map();
export function registerSound(key, onTrigger, data = {}) {
soundMap.setKey(key, { onTrigger, data });
}
export function getSound(s) {
return soundMap.get()[s];
}
export const resetLoadedSounds = () => soundMap.set({});
let audioContext;
@@ -50,7 +46,6 @@ export const panic = () => {
};
let workletsLoading;
function loadWorklets() {
if (workletsLoading) {
return workletsLoading;
@@ -94,7 +89,6 @@ export async function initAudioOnFirstClick(options) {
let delays = {};
const maxfeedback = 0.98;
function getDelay(orbit, delaytime, delayfeedback, t) {
if (delayfeedback > maxfeedback) {
//logger(`delayfeedback was clamped to ${maxfeedback} to save your ears`);
@@ -112,79 +106,30 @@ function getDelay(orbit, delaytime, delayfeedback, t) {
return delays[orbit];
}
// each orbit will have its own lfo
const phaserLFOs = {};
function getPhaser(orbit, t, speed = 1, depth = 0.5, centerFrequency = 1000, sweep = 2000) {
//gain
const ac = getAudioContext();
const lfoGain = ac.createGain();
lfoGain.gain.value = sweep;
//LFO
if (phaserLFOs[orbit] == null) {
phaserLFOs[orbit] = ac.createOscillator();
phaserLFOs[orbit].frequency.value = speed;
phaserLFOs[orbit].type = 'sine';
phaserLFOs[orbit].start();
}
phaserLFOs[orbit].connect(lfoGain);
if (phaserLFOs[orbit].frequency.value != speed) {
phaserLFOs[orbit].frequency.setValueAtTime(speed, t);
}
//filters
const numStages = 2; //num of filters in series
let fOffset = 0;
const filterChain = [];
for (let i = 0; i < numStages; i++) {
const filter = ac.createBiquadFilter();
filter.type = 'notch';
filter.gain.value = 1;
filter.frequency.value = centerFrequency + fOffset;
filter.Q.value = 2 - Math.min(Math.max(depth * 2, 0), 1.9);
lfoGain.connect(filter.detune);
fOffset += 282;
if (i > 0) {
filterChain[i - 1].connect(filter);
}
filterChain.push(filter);
}
return filterChain[filterChain.length - 1];
}
let reverbs = {};
let hasChanged = (now, before) => now !== undefined && now !== before;
function getReverb(orbit, duration, fade, lp, dim, ir) {
function getReverb(orbit, duration = 2, fade, lp, dim) {
// If no reverb has been created for a given orbit, create one
if (!reverbs[orbit]) {
const ac = getAudioContext();
const reverb = ac.createReverb(duration, fade, lp, dim, ir);
const reverb = ac.createReverb(duration, fade, lp, dim);
reverb.connect(getDestination());
reverbs[orbit] = reverb;
}
if (
hasChanged(duration, reverbs[orbit].duration) ||
hasChanged(fade, reverbs[orbit].fade) ||
hasChanged(lp, reverbs[orbit].lp) ||
hasChanged(dim, reverbs[orbit].dim) ||
reverbs[orbit].ir !== ir
reverbs[orbit].duration !== duration ||
reverbs[orbit].fade !== fade ||
reverbs[orbit].lp !== lp ||
reverbs[orbit].dim !== dim
) {
// only regenerate when something has changed
// avoids endless regeneration on things like
// stack(s("a"), s("b").rsize(8)).room(.5)
// this only works when args may stay undefined until here
// setting default values breaks this
reverbs[orbit].generate(duration, fade, lp, dim, ir);
reverbs[orbit].generate(duration, fade, lp, dim);
}
return reverbs[orbit];
}
export let analyser, analyserData /* s = {} */;
export function getAnalyser(/* orbit, */ fftSize = 2048) {
if (!analyser /*s [orbit] */) {
const analyserNode = getAudioContext().createAnalyser();
@@ -240,7 +185,6 @@ export const superdough = async (value, deadline, hapDuration) => {
bank,
source,
gain = 0.8,
postgain = 1,
// filters
ftype = '12db',
fanchor = 0.5,
@@ -268,12 +212,6 @@ export const superdough = async (value, deadline, hapDuration) => {
bpsustain = 1,
bprelease = 0.01,
bandq = 1,
//phaser
phaser,
phaserdepth = 0.75,
phasersweep,
phasercenter,
//
coarse,
crush,
@@ -285,20 +223,13 @@ export const superdough = async (value, deadline, hapDuration) => {
delaytime = 0.25,
orbit = 1,
room,
roomfade,
roomlp,
roomdim,
roomsize,
ir,
i = 0,
roomfade = 0.1,
roomlp = 15000,
roomdim = 1000,
roomsize = 2,
velocity = 1,
analyze, // analyser wet
fft = 8, // fftSize 0 - 10
compressor: compressorThreshold,
compressorRatio,
compressorKnee,
compressorAttack,
compressorRelease,
} = value;
gain *= velocity; // legacy fix for velocity
let toDisconnect = []; // audio nodes that will be disconnected when the source has ended
@@ -308,7 +239,6 @@ export const superdough = async (value, deadline, hapDuration) => {
if (bank && s) {
s = `${bank}_${s}`;
}
// get source AudioNode
let sourceNode;
if (source) {
@@ -328,7 +258,6 @@ export const superdough = async (value, deadline, hapDuration) => {
// this can be used for things like speed(0) in the sampler
return;
}
if (ac.currentTime > t) {
logger('[webaudio] skip hap: still loading', ac.currentTime - t);
return;
@@ -415,25 +344,15 @@ export const superdough = async (value, deadline, hapDuration) => {
crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush }));
shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape }));
compressorThreshold !== undefined &&
chain.push(
getCompressor(ac, compressorThreshold, compressorRatio, compressorKnee, compressorAttack, compressorRelease),
);
// panning
if (pan !== undefined) {
const panner = ac.createStereoPanner();
panner.pan.value = 2 * pan - 1;
chain.push(panner);
}
// phaser
if (phaser !== undefined && phaserdepth > 0) {
const phaserFX = getPhaser(orbit, t, phaser, phaserdepth, phasercenter, phasersweep);
chain.push(phaserFX);
}
// last gain
const post = gainNode(postgain);
const post = gainNode(1);
chain.push(post);
post.connect(getDestination());
@@ -445,19 +364,8 @@ export const superdough = async (value, deadline, hapDuration) => {
}
// reverb
let reverbSend;
if (room > 0) {
let roomIR;
if (ir !== undefined) {
let url;
let sample = getSound(ir);
if (Array.isArray(sample)) {
url = sample.data.samples[i % sample.data.samples.length];
} else if (typeof sample === 'object') {
url = Object.values(sample.data.samples).flat()[i % Object.values(sample.data.samples).length];
}
roomIR = await loadBuffer(url, ac, ir, 0);
}
const reverbNode = getReverb(orbit, roomsize, roomfade, roomlp, roomdim, roomIR);
if (room > 0 && roomsize > 0) {
const reverbNode = getReverb(orbit, roomsize, roomfade, roomlp, roomdim);
reverbSend = effectSend(post, reverbNode, room);
}
+7 -8
View File
@@ -73,13 +73,13 @@ export function waveformN(partials, type) {
const ac = getAudioContext();
const osc = ac.createOscillator();
const terms = {
sawtooth: (n) => [0, -1 / n],
square: (n) => [0, n % 2 === 0 ? 0 : 1 / n],
triangle: (n) => [n % 2 === 0 ? 0 : 1 / (n * n), 0],
const amplitudes = {
sawtooth: (n) => 1 / n,
square: (n) => (n % 2 === 0 ? 0 : 1 / n),
triangle: (n) => (n % 2 === 0 ? 0 : 1 / (n * n)),
};
if (!terms[type]) {
if (!amplitudes[type]) {
throw new Error(`unknown wave type ${type}`);
}
@@ -87,9 +87,8 @@ export function waveformN(partials, type) {
imag[0] = 0;
let n = 1;
while (n <= partials) {
const [r, i] = terms[type](n);
real[n] = r;
imag[n] = i;
real[n] = amplitudes[type](n);
imag[n] = 0;
n++;
}
+1 -1
View File
@@ -31,4 +31,4 @@ yields:
## Tonal API
See "Tonal API" in the [Strudel Tutorial](https://strudel.cc/learn/tonal)
See "Tonal API" in the [Strudel Tutorial](https://strudel.tidalcycles.org/learn/tonal)
+5 -17
View File
@@ -7,19 +7,6 @@ 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 } from '@strudel.cycles/core';
const octavesInterval = (octaves) => (octaves <= 0 ? -1 : 1) + octaves * 7 + 'P';
function scaleStep(step, scale) {
scale = scale.replaceAll(':', ' ');
step = Math.ceil(step);
const { intervals, tonic } = Scale.get(scale);
const { pc, oct = 3 } = Note.get(tonic);
const octaveOffset = Math.floor(step / intervals.length);
const scaleStep = _mod(step, intervals.length);
const interval = Interval.add(intervals[scaleStep], octavesInterval(octaveOffset));
return Note.transpose(pc + oct, interval);
}
// transpose note inside scale by offset steps
// function scaleOffset(scale: string, offset: number, note: string) {
function scaleOffset(scale, offset, note) {
@@ -163,12 +150,13 @@ export const scale = register('scale', function (scale, pat) {
return pat.withHap((hap) => {
const isObject = typeof hap.value === 'object';
let note = isObject ? hap.value.n : hap.value;
if (isObject) {
delete hap.value.n; // remove n so it won't cause trouble
}
const asNumber = Number(note);
if (!isNaN(asNumber)) {
note = scaleStep(asNumber, scale);
// TODO: worth keeping for supporting ':' in (non-mininotation) strings?
scale = scale.replaceAll(':', ' ');
let [tonic, scaleName] = Scale.tokenize(scale);
const { pc, oct = 3 } = Note.get(tonic);
note = scaleOffset(pc + ' ' + scaleName, asNumber, pc + oct);
}
return hap.withValue(() => (isObject ? { ...hap.value, note } : note)).setContext({ ...hap.context, scale });
});
+2 -2
View File
@@ -51,7 +51,7 @@ document.getElementById('play').addEventListener('click',
)
```
You can learn [more about the `samples` function here](https://strudel.cc/learn/samples#loading-custom-samples).
You can learn [more about the `samples` function here](https://strudel.tidalcycles.org/learn/samples#loading-custom-samples).
### Evaluating Code
@@ -72,7 +72,7 @@ document.getElementById('play').addEventListener('stop',
### Double vs Single Quotes
There is a tiny difference between the [Strudel REPL](https://strudel.cc/) and `@strudel/web`.
There is a tiny difference between the [Strudel REPL](https://strudel.tidalcycles.org/) and `@strudel/web`.
In the REPL you can use 'single quotes' for regular JS strings and "double quotes" for mini notation patterns.
In `@strudel/web`, it does not matter which types of quotes you're using.
There will probably be an escapte hatch for that in the future.
@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="https://strudel.cc/favicon.ico" />
<link rel="icon" type="image/svg+xml" href="https://strudel.tidalcycles.org/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@strudel/web REPL Example</title>
</head>
+1 -1
View File
@@ -33,4 +33,4 @@ document.getElementById("stop").addEventListener("click", () => scheduler.stop()
[Play with the example codesandbox](https://codesandbox.io/s/amazing-dawn-gclfwg?file=/src/index.js).
Read more in the docs about [samples](https://strudel.cc/learn/samples/), [synths](https://strudel.cc/learn/synths/) and [effects](https://strudel.cc/learn/effects/).
Read more in the docs about [samples](https://strudel.tidalcycles.org/learn/samples/), [synths](https://strudel.tidalcycles.org/learn/synths/) and [effects](https://strudel.tidalcycles.org/learn/effects/).
+3 -28
View File
@@ -3,7 +3,7 @@ import { analyser, getAnalyzerData } from 'superdough';
export function drawTimeScope(
analyser,
{ align = true, color = 'white', thickness = 3, scale = 0.25, pos = 0.75, trigger = 0 } = {},
{ align = true, color = 'white', thickness = 3, scale = 0.25, pos = 0.75, next = 1, trigger = 0 } = {},
) {
const ctx = getDrawContext();
const dataArray = getAnalyzerData('time');
@@ -22,9 +22,10 @@ export function drawTimeScope(
const sliceWidth = (canvas.width * 1.0) / bufferSize;
let x = 0;
for (let i = triggerIndex; i < bufferSize; i++) {
const v = dataArray[i] + 1;
const y = (pos - scale * (v - 1)) * canvas.height;
const y = (scale * (v - 1) + pos) * canvas.height;
if (i === 0) {
ctx.moveTo(x, y);
@@ -70,18 +71,6 @@ function clearScreen(smear = 0, smearRGB = `0,0,0`) {
}
}
/**
* Renders an oscilloscope for the frequency domain of the audio signal.
* @name fscope
* @param {string} color line color as hex or color name. defaults to white.
* @param {number} scale scales the y-axis. Defaults to 0.25
* @param {number} pos y-position relative to screen height. 0 = top, 1 = bottom of screen
* @param {number} lean y-axis alignment where 0 = top and 1 = bottom
* @param {number} min min value
* @param {number} max max value
* @example
* s("sawtooth").fscope()
*/
Pattern.prototype.fscope = function (config = {}) {
return this.analyze(1).draw(() => {
clearScreen(config.smear);
@@ -89,20 +78,6 @@ Pattern.prototype.fscope = function (config = {}) {
});
};
/**
* Renders an oscilloscope for the time domain of the audio signal.
* @name scope
* @synonyms tscope
* @param {object} config optional config with options:
* @param {boolean} align if 1, the scope will be aligned to the first zero crossing. defaults to 1
* @param {string} color line color as hex or color name. defaults to white.
* @param {number} thickness line thickness. defaults to 3
* @param {number} scale scales the y-axis. Defaults to 0.25
* @param {number} pos y-position relative to screen height. 0 = top, 1 = bottom of screen
* @param {number} trigger amplitude value that is used to align the scope. defaults to 0.
* @example
* s("sawtooth").scope()
*/
Pattern.prototype.tscope = function (config = {}) {
return this.analyze(1).draw(() => {
clearScreen(config.smear);
+1 -1
View File
@@ -199,7 +199,7 @@ interfaces.
# Links
The Strudel REPL is available at <https://strudel.cc>,
The Strudel REPL is available at <https://strudel.tidalcycles.org>,
including an interactive tutorial. The repository is at
<https://github.com/tidalcycles/strudel>, all the code is open source
under the GPL-3.0 License.
+1 -1
View File
@@ -127,7 +127,7 @@ For the future, it is planned to integrate alternative sound engines such as Gli
# Links
The Strudel REPL is available at <https://strudel.cc>, including an interactive tutorial.
The Strudel REPL is available at <https://strudel.tidalcycles.org>, including an interactive tutorial.
The repository is at <https://github.com/tidalcycles/strudel>, all the code is open source under the GPL-3.0 License.
# Acknowledgments
+2 -2
View File
@@ -717,8 +717,8 @@ fun ahead.</p>
<h1 data-number="11" id="links"><span
class="header-section-number">11</span> Links</h1>
<p>The Strudel REPL is available at <a
href="https://strudel.cc"
class="uri">https://strudel.cc</a>, including an
href="https://strudel.tidalcycles.org"
class="uri">https://strudel.tidalcycles.org</a>, including an
interactive tutorial. The repository is at <a
href="https://github.com/tidalcycles/strudel"
class="uri">https://github.com/tidalcycles/strudel</a>, all the code is
+1 -1
View File
@@ -450,7 +450,7 @@ While Haskell's type system makes it a great language for the ongoing developmen
# Links
The Strudel REPL is available at <https://strudel.cc>, including an interactive tutorial.
The Strudel REPL is available at <https://strudel.tidalcycles.org>, including an interactive tutorial.
The repository is at <https://github.com/tidalcycles/strudel>, all the code is open source under the AGPL-3.0 License.
# Acknowledgments
+1232 -1963
View File
File diff suppressed because it is too large Load Diff
+113 -91
View File
@@ -99,6 +99,7 @@ version = "0.1.0"
dependencies = [
"midir",
"rosc",
"rusty_link",
"serde",
"serde_json",
"tauri",
@@ -163,6 +164,28 @@ version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]]
name = "bindgen"
version = "0.63.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36d860121800b2a9a94f9b5604b332d5cffb234ce17609ea479d723dbc9d3885"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn 1.0.109",
"which",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -285,6 +308,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfb"
version = "0.7.3"
@@ -334,6 +366,26 @@ dependencies = [
"winapi",
]
[[package]]
name = "clang-sys"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "cmake"
version = "0.1.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
dependencies = [
"cc",
]
[[package]]
name = "cocoa"
version = "0.24.1"
@@ -637,6 +689,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "embed-resource"
version = "2.1.1"
@@ -1431,12 +1489,28 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.146"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "line-wrap"
version = "0.1.1"
@@ -1727,17 +1801,6 @@ dependencies = [
"objc_exception",
]
[[package]]
name = "objc-foundation"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
dependencies = [
"block",
"objc",
"objc_id",
]
[[package]]
name = "objc_exception"
version = "0.1.2"
@@ -1825,6 +1888,12 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "percent-encoding"
version = "2.3.0"
@@ -2201,30 +2270,6 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
[[package]]
name = "rfd"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea"
dependencies = [
"block",
"dispatch",
"glib-sys",
"gobject-sys",
"gtk-sys",
"js-sys",
"lazy_static",
"log",
"objc",
"objc-foundation",
"objc_id",
"raw-window-handle",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"windows 0.37.0",
]
[[package]]
name = "rosc"
version = "0.10.1"
@@ -2241,6 +2286,12 @@ version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_version"
version = "0.4.0"
@@ -2270,6 +2321,16 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
[[package]]
name = "rusty_link"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "212e275351a46badd88b67ab8a4a8e17580317c728c361299b245cb51debe81c"
dependencies = [
"bindgen",
"cmake",
]
[[package]]
name = "ryu"
version = "1.0.13"
@@ -2463,6 +2524,12 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "shlex"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
@@ -2731,7 +2798,6 @@ dependencies = [
"percent-encoding",
"rand 0.8.5",
"raw-window-handle",
"rfd",
"semver",
"serde",
"serde_json",
@@ -3287,18 +3353,6 @@ dependencies = [
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.87"
@@ -3423,6 +3477,17 @@ dependencies = [
"windows-metadata",
]
[[package]]
name = "which"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
dependencies = [
"either",
"libc",
"once_cell",
]
[[package]]
name = "winapi"
version = "0.3.9"
@@ -3454,19 +3519,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647"
dependencies = [
"windows_aarch64_msvc 0.37.0",
"windows_i686_gnu 0.37.0",
"windows_i686_msvc 0.37.0",
"windows_x86_64_gnu 0.37.0",
"windows_x86_64_msvc 0.37.0",
]
[[package]]
name = "windows"
version = "0.39.0"
@@ -3573,12 +3625,6 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a"
[[package]]
name = "windows_aarch64_msvc"
version = "0.39.0"
@@ -3597,12 +3643,6 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1"
[[package]]
name = "windows_i686_gnu"
version = "0.39.0"
@@ -3621,12 +3661,6 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c"
[[package]]
name = "windows_i686_msvc"
version = "0.39.0"
@@ -3645,12 +3679,6 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.39.0"
@@ -3681,12 +3709,6 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.39.0"
+2 -1
View File
@@ -17,10 +17,11 @@ tauri-build = { version = "1.4.0", features = [] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.4.0", features = [ "dialog-all", "clipboard-write-text", "fs-all"] }
tauri = { version = "1.4.0", features = ["fs-all"] }
midir = "0.9.1"
tokio = { version = "1.29.0", features = ["full"] }
rosc = "0.10.1"
rusty_link = "0.3.6"
[features]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
+2 -1
View File
@@ -4,7 +4,8 @@ Rust source files for building native desktop apps using Tauri
## Usage
Install [Rust](https://rustup.rs/) on your system.
- Install [Rust](https://rustup.rs/) on your system.
- Install `cmake` on your system. OSX: `brew install cmake`, Linux: `sudo apt-get install cmake`
From the project root:
+175
View File
@@ -0,0 +1,175 @@
use std::time::{ Duration, SystemTime, UNIX_EPOCH };
use rusty_link::{ AblLink, SessionState };
use std::sync::Arc;
use tokio::sync::Mutex;
use serde::Deserialize;
use std::thread::sleep;
use crate::loggerbridge::Logger;
use tauri::Window;
fn bpm_to_cps(bpm: f64) -> f64 {
let cpm = bpm / 4.0;
return cpm / 60.0;
}
fn cps_to_bpm(cps: f64) -> f64 {
let cpm = cps * 60.0;
return cpm * 4.0;
}
fn current_unix_time() -> Duration {
let current_unix_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
return current_unix_time;
}
#[derive(Deserialize, Clone, serde::Serialize)]
pub struct LinkMsg {
pub started: bool,
pub cps: f64,
pub timestamp: u64,
pub phase: f64,
}
pub struct AbeLinkStateContainer {
pub abelink: Arc<Mutex<AbeLinkState>>,
}
pub struct AbeLinkState {
pub link: AblLink,
pub session_state: SessionState,
pub running: bool,
pub quantum: f64,
pub window: Arc<Window>,
}
impl AbeLinkState {
pub fn new(window: Arc<Window>) -> Self {
Self {
link: AblLink::new(120.0),
session_state: SessionState::new(),
running: true,
quantum: 4.0,
window,
}
}
pub fn unix_time_at_next_phase(&self) -> u64 {
let link_time_stamp = self.link.clock_micros();
let quantum = self.quantum;
let beat = self.session_state.beat_at_time(link_time_stamp, quantum);
let phase = self.session_state.phase_at_time(link_time_stamp, quantum);
let internal_time_at_next_phase = self.session_state.time_at_beat(beat + (quantum - phase), quantum);
let time_offset = Duration::from_micros((internal_time_at_next_phase - link_time_stamp) as u64);
let current_unix_time = current_unix_time();
let unix_time_at_next_phase = (current_unix_time + time_offset).as_millis();
return unix_time_at_next_phase as u64;
}
pub fn cps(&self) -> f64 {
let bpm = self.session_state.tempo();
let cps = bpm_to_cps(bpm);
return cps;
}
pub fn capture_app_state(&mut self) {
self.link.capture_app_session_state(&mut self.session_state);
}
pub fn commit_app_state(&mut self) {
self.link.commit_app_session_state(&self.session_state);
}
pub fn send(&self, payload: LinkMsg) {
let _ = self.window.emit("abelink-event", payload);
}
pub fn send_started(&self) {
let cps = self.cps();
let started = self.session_state.is_playing();
let payload = LinkMsg {
cps,
started,
timestamp: self.unix_time_at_next_phase(),
phase: 0.0,
};
self.send(payload);
}
pub fn send_cps(&self) {
let cps = self.cps();
let started = self.session_state.is_playing();
let phase = self.session_state.phase_at_time(self.link.clock_micros(), self.quantum);
let payload = LinkMsg {
cps,
started,
timestamp: current_unix_time().as_millis() as u64,
phase,
};
self.send(payload);
}
pub fn send_phase(&self) {
self.send_started();
}
}
pub fn init(_logger: Logger, abelink: Arc<Mutex<AbeLinkState>>) {
tauri::async_runtime::spawn(async move {
let mut prev_is_started = false;
let mut prev_cps = 0.0;
let mut time_since_last_phase_send = 0;
let sleep_time = 10;
/* .......................................................................
Evaluate Abelink State and send messages back to JS side when needed.
........................................................................*/
loop {
let mut state = abelink.lock().await;
state.capture_app_state();
if state.link.is_enabled() == false {
state.link.enable(true);
state.link.enable_start_stop_sync(true);
}
let started = state.session_state.is_playing();
if started != prev_is_started {
state.send_started();
prev_is_started = started;
} else if state.cps() != prev_cps && state.cps() != 0.0 {
state.send_cps();
prev_cps = state.cps();
// a phase sync message needs to be sent to strudel every 30 seconds to keep clock drift at bay
} else if time_since_last_phase_send > 30000 {
state.send_phase();
time_since_last_phase_send = 0;
}
drop(state);
sleep(Duration::from_millis(sleep_time));
time_since_last_phase_send = time_since_last_phase_send + sleep_time;
}
});
}
// Called from JS
#[tauri::command]
pub async fn sendabelinkmsg(linkmsg: LinkMsg, state: tauri::State<'_, AbeLinkStateContainer>) -> Result<(), String> {
let mut abelink = state.abelink.lock().await;
abelink.capture_app_state();
let started = abelink.session_state.is_playing();
let time_stamp = abelink.link.clock_micros();
let quantum = abelink.quantum;
let linkmsg_bpm = cps_to_bpm(linkmsg.cps);
if linkmsg.started != started {
abelink.session_state.set_is_playing_and_request_beat_at_time(linkmsg.started, time_stamp as u64, 0.0, quantum);
}
if linkmsg_bpm != abelink.session_state.tempo() && linkmsg_bpm != 0.0 {
abelink.session_state.set_tempo(linkmsg_bpm, time_stamp);
}
abelink.commit_app_state();
drop(abelink);
Ok(())
}
+20 -3
View File
@@ -4,8 +4,10 @@
mod midibridge;
mod oscbridge;
mod loggerbridge;
mod ablelinkbridge;
use std::sync::Arc;
use ablelinkbridge::AbeLinkState;
use loggerbridge::Logger;
use tauri::Manager;
use tokio::sync::mpsc;
@@ -21,6 +23,7 @@ fn main() {
let (async_output_transmitter_midi, async_output_receiver_midi) = mpsc::channel(1);
let (async_input_transmitter_osc, async_input_receiver_osc) = mpsc::channel(1);
let (async_output_transmitter_osc, async_output_receiver_osc) = mpsc::channel(1);
tauri::Builder
::default()
.manage(midibridge::AsyncInputTransmit {
@@ -29,17 +32,31 @@ fn main() {
.manage(oscbridge::AsyncInputTransmit {
inner: Mutex::new(async_input_transmitter_osc),
})
.invoke_handler(tauri::generate_handler![midibridge::sendmidi, oscbridge::sendosc])
.invoke_handler(tauri::generate_handler![midibridge::sendmidi, oscbridge::sendosc, ablelinkbridge::sendabelinkmsg])
.setup(|app| {
let window = Arc::new(app.get_window("main").unwrap());
let logger = Logger { window };
let logger = Logger { window: window.clone() };
midibridge::init(
logger.clone(),
async_input_receiver_midi,
async_output_receiver_midi,
async_output_transmitter_midi
);
oscbridge::init(logger, async_input_receiver_osc, async_output_receiver_osc, async_output_transmitter_osc);
oscbridge::init(
logger.clone(),
async_input_receiver_osc,
async_output_receiver_osc,
async_output_transmitter_osc
);
// This state must be declared in the setup so it can be shared between invoked commands and the initialized function
let abelink = Arc::new(Mutex::new(AbeLinkState::new(window)));
app.manage(ablelinkbridge::AbeLinkStateContainer {
abelink: abelink.clone(),
});
ablelinkbridge::init(logger.clone(), abelink);
Ok(())
})
.run(tauri::generate_context!())
+1 -7
View File
@@ -3,7 +3,7 @@
"build": {
"beforeBuildCommand": "npm run build",
"beforeDevCommand": "npm run dev",
"devPath": "http://localhost:4321",
"devPath": "http://localhost:3000",
"distDir": "../website/dist",
"withGlobalTauri": true
},
@@ -13,12 +13,6 @@
},
"tauri": {
"allowlist": {
"dialog": {
"all": true
},
"clipboard": {
"writeText": true
},
"all": false,
"fs": {
"all": true,
+101 -573
View File
@@ -633,15 +633,6 @@ exports[`runs examples > example "addVoicings" example index 0 1`] = `
]
`;
exports[`runs examples > example "adsr" example index 0 1`] = `
[
"[ 0/1 → 1/1 | note:c3 s:sawtooth cutoff:600 ]",
"[ 1/1 → 2/1 | note:bb2 s:sawtooth cutoff:600 ]",
"[ 2/1 → 3/1 | note:f3 s:sawtooth cutoff:600 ]",
"[ 3/1 → 4/1 | note:eb3 s:sawtooth cutoff:600 ]",
]
`;
exports[`runs examples > example "almostAlways" example index 0 1`] = `
[
"[ 0/1 → 1/8 | s:hh speed:0.5 ]",
@@ -1080,31 +1071,6 @@ exports[`runs examples > example "chooseCycles" example index 1 1`] = `
]
`;
exports[`runs examples > example "chooseWith" example index 0 1`] = `
[
"[ 0/1 → 1/5 | note:c2 s:bd n:6 ]",
"[ 1/5 → 2/5 | note:g2 s:sawtooth ]",
"[ 2/5 → 3/5 | note:g2 s:triangle ]",
"[ 3/5 → 4/5 | note:d2 s:bd n:6 ]",
"[ 4/5 → 1/1 | note:f1 s:sawtooth ]",
"[ 1/1 → 6/5 | note:c2 s:bd n:6 ]",
"[ 6/5 → 7/5 | note:g2 s:sawtooth ]",
"[ 7/5 → 8/5 | note:g2 s:triangle ]",
"[ 8/5 → 9/5 | note:d2 s:bd n:6 ]",
"[ 9/5 → 2/1 | note:f1 s:sawtooth ]",
"[ 2/1 → 11/5 | note:c2 s:bd n:6 ]",
"[ 11/5 → 12/5 | note:g2 s:sawtooth ]",
"[ 12/5 → 13/5 | note:g2 s:triangle ]",
"[ 13/5 → 14/5 | note:d2 s:bd n:6 ]",
"[ 14/5 → 3/1 | note:f1 s:sawtooth ]",
"[ 3/1 → 16/5 | note:c2 s:bd n:6 ]",
"[ 16/5 → 17/5 | note:g2 s:sawtooth ]",
"[ 17/5 → 18/5 | note:g2 s:triangle ]",
"[ 18/5 → 19/5 | note:d2 s:bd n:6 ]",
"[ 19/5 → 4/1 | note:f1 s:sawtooth ]",
]
`;
exports[`runs examples > example "chop" example index 0 1`] = `
[
"[ 0/1 → 1/1 | s:rhodes begin:0.75 end:1 speed:0.25 unit:c ]",
@@ -1115,27 +1081,6 @@ exports[`runs examples > example "chop" example index 0 1`] = `
`;
exports[`runs examples > example "chunk" example index 0 1`] = `
[
"[ 0/1 → 1/4 | note:A4 ]",
"[ 1/4 → 1/2 | note:B3 ]",
"[ 1/2 → 3/4 | note:C4 ]",
"[ 3/4 → 1/1 | note:D4 ]",
"[ 1/1 → 5/4 | note:A3 ]",
"[ 5/4 → 3/2 | note:B4 ]",
"[ 3/2 → 7/4 | note:C4 ]",
"[ 7/4 → 2/1 | note:D4 ]",
"[ 2/1 → 9/4 | note:A3 ]",
"[ 9/4 → 5/2 | note:B3 ]",
"[ 5/2 → 11/4 | note:C5 ]",
"[ 11/4 → 3/1 | note:D4 ]",
"[ 3/1 → 13/4 | note:A3 ]",
"[ 13/4 → 7/2 | note:B3 ]",
"[ 7/2 → 15/4 | note:C4 ]",
"[ 15/4 → 4/1 | note:D5 ]",
]
`;
exports[`runs examples > example "chunkBack" example index 0 1`] = `
[
"[ 0/1 → 1/4 | note:A4 ]",
"[ 1/4 → 1/2 | note:B3 ]",
@@ -1156,6 +1101,27 @@ exports[`runs examples > example "chunkBack" example index 0 1`] = `
]
`;
exports[`runs examples > example "chunkBack" example index 0 1`] = `
[
"[ 0/1 → 1/4 | note:A4 ]",
"[ 1/4 → 1/2 | note:B3 ]",
"[ 1/2 → 3/4 | note:C4 ]",
"[ 3/4 → 1/1 | note:D4 ]",
"[ 1/1 → 5/4 | note:A3 ]",
"[ 5/4 → 3/2 | note:B4 ]",
"[ 3/2 → 7/4 | note:C4 ]",
"[ 7/4 → 2/1 | note:D4 ]",
"[ 2/1 → 9/4 | note:A3 ]",
"[ 9/4 → 5/2 | note:B3 ]",
"[ 5/2 → 11/4 | note:C5 ]",
"[ 11/4 → 3/1 | note:D4 ]",
"[ 3/1 → 13/4 | note:A3 ]",
"[ 13/4 → 7/2 | note:B3 ]",
"[ 7/2 → 15/4 | note:C4 ]",
"[ 15/4 → 4/1 | note:D5 ]",
]
`;
exports[`runs examples > example "clip" example index 0 1`] = `
[
"[ 0/1 → 1/4 | note:c s:piano clip:0.5 ]",
@@ -1219,35 +1185,6 @@ exports[`runs examples > example "compress" example index 0 1`] = `
]
`;
exports[`runs examples > example "compressor" example index 0 1`] = `
[
"[ 0/1 → 1/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 0/1 → 1/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 1/4 → 1/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 1/2 → 3/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 1/2 → 1/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 3/4 → 1/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 1/1 → 5/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 1/1 → 3/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 5/4 → 3/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 3/2 → 7/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 3/2 → 2/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 7/4 → 2/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 2/1 → 9/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 2/1 → 5/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 9/4 → 5/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 5/2 → 11/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 5/2 → 3/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 11/4 → 3/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 3/1 → 13/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 3/1 → 7/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 13/4 → 7/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 7/2 → 15/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 7/2 → 4/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
"[ 15/4 → 4/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]",
]
`;
exports[`runs examples > example "cosine" example index 0 1`] = `
[
"[ 0/1 → 1/8 | note:Eb4 ]",
@@ -1857,19 +1794,6 @@ exports[`runs examples > example "fast" example index 0 1`] = `
]
`;
exports[`runs examples > example "fastChunk" example index 0 1`] = `
[
"[ 0/1 → 1/2 | note:C2 s:folkharp ]",
"[ 1/2 → 1/1 | note:D2 s:folkharp ]",
"[ 1/1 → 3/2 | note:E2 s:folkharp ]",
"[ 3/2 → 2/1 | note:F2 s:folkharp ]",
"[ 2/1 → 5/2 | note:G2 s:folkharp ]",
"[ 5/2 → 3/1 | note:A2 s:folkharp ]",
"[ 3/1 → 7/2 | note:B2 s:folkharp ]",
"[ 7/2 → 4/1 | note:C3 s:folkharp ]",
]
`;
exports[`runs examples > example "fastGap" example index 0 1`] = `
[
"[ 0/1 → 1/4 | s:bd ]",
@@ -2143,15 +2067,6 @@ exports[`runs examples > example "freq" example index 1 1`] = `
]
`;
exports[`runs examples > example "fscope" example index 0 1`] = `
[
"[ 0/1 → 1/1 | s:sawtooth analyze:1 ]",
"[ 1/1 → 2/1 | s:sawtooth analyze:1 ]",
"[ 2/1 → 3/1 | s:sawtooth analyze:1 ]",
"[ 3/1 → 4/1 | s:sawtooth analyze:1 ]",
]
`;
exports[`runs examples > example "ftype" example index 0 1`] = `
[
"[ 0/1 → 1/1 | note:c2 s:sawtooth cutoff:500 bpenv:4 ftype:12db ]",
@@ -2439,19 +2354,6 @@ exports[`runs examples > example "irand" example index 0 1`] = `
]
`;
exports[`runs examples > example "iresponse" example index 0 1`] = `
[
"[ 0/1 → 1/2 | s:bd room:0.8 ir:shaker_large i:0 ]",
"[ 1/2 → 1/1 | s:sd room:0.8 ir:shaker_large i:0 ]",
"[ 1/1 → 3/2 | s:bd room:0.8 ir:shaker_large i:2 ]",
"[ 3/2 → 2/1 | s:sd room:0.8 ir:shaker_large i:2 ]",
"[ 2/1 → 5/2 | s:bd room:0.8 ir:shaker_large i:0 ]",
"[ 5/2 → 3/1 | s:sd room:0.8 ir:shaker_large i:0 ]",
"[ 3/1 → 7/2 | s:bd room:0.8 ir:shaker_large i:2 ]",
"[ 7/2 → 4/1 | s:sd room:0.8 ir:shaker_large i:2 ]",
]
`;
exports[`runs examples > example "iter" example index 0 1`] = `
[
"[ 0/1 → 1/4 | note:A3 ]",
@@ -3319,204 +3221,6 @@ exports[`runs examples > example "perlin" example index 0 1`] = `
]
`;
exports[`runs examples > example "phaser" example index 0 1`] = `
[
"[ 0/1 → 1/8 | note:D3 s:sawtooth release:0.5 phaser:1 ]",
"[ 1/8 → 1/4 | note:E3 s:sawtooth release:0.5 phaser:1 ]",
"[ 1/4 → 3/8 | note:F#3 s:sawtooth release:0.5 phaser:1 ]",
"[ 3/8 → 1/2 | note:A3 s:sawtooth release:0.5 phaser:1 ]",
"[ 1/2 → 5/8 | note:B3 s:sawtooth release:0.5 phaser:1 ]",
"[ 5/8 → 3/4 | note:D4 s:sawtooth release:0.5 phaser:1 ]",
"[ 3/4 → 7/8 | note:E4 s:sawtooth release:0.5 phaser:1 ]",
"[ 7/8 → 1/1 | note:F#4 s:sawtooth release:0.5 phaser:1 ]",
"[ 1/1 → 9/8 | note:D3 s:sawtooth release:0.5 phaser:2 ]",
"[ 9/8 → 5/4 | note:E3 s:sawtooth release:0.5 phaser:2 ]",
"[ 5/4 → 11/8 | note:F#3 s:sawtooth release:0.5 phaser:2 ]",
"[ 11/8 → 3/2 | note:A3 s:sawtooth release:0.5 phaser:2 ]",
"[ 3/2 → 13/8 | note:B3 s:sawtooth release:0.5 phaser:2 ]",
"[ 13/8 → 7/4 | note:D4 s:sawtooth release:0.5 phaser:2 ]",
"[ 7/4 → 15/8 | note:E4 s:sawtooth release:0.5 phaser:2 ]",
"[ 15/8 → 2/1 | note:F#4 s:sawtooth release:0.5 phaser:2 ]",
"[ 2/1 → 17/8 | note:D3 s:sawtooth release:0.5 phaser:4 ]",
"[ 17/8 → 9/4 | note:E3 s:sawtooth release:0.5 phaser:4 ]",
"[ 9/4 → 19/8 | note:F#3 s:sawtooth release:0.5 phaser:4 ]",
"[ 19/8 → 5/2 | note:A3 s:sawtooth release:0.5 phaser:4 ]",
"[ 5/2 → 21/8 | note:B3 s:sawtooth release:0.5 phaser:4 ]",
"[ 21/8 → 11/4 | note:D4 s:sawtooth release:0.5 phaser:4 ]",
"[ 11/4 → 23/8 | note:E4 s:sawtooth release:0.5 phaser:4 ]",
"[ 23/8 → 3/1 | note:F#4 s:sawtooth release:0.5 phaser:4 ]",
"[ 3/1 → 25/8 | note:D3 s:sawtooth release:0.5 phaser:8 ]",
"[ 25/8 → 13/4 | note:E3 s:sawtooth release:0.5 phaser:8 ]",
"[ 13/4 → 27/8 | note:F#3 s:sawtooth release:0.5 phaser:8 ]",
"[ 27/8 → 7/2 | note:A3 s:sawtooth release:0.5 phaser:8 ]",
"[ 7/2 → 29/8 | note:B3 s:sawtooth release:0.5 phaser:8 ]",
"[ 29/8 → 15/4 | note:D4 s:sawtooth release:0.5 phaser:8 ]",
"[ 15/4 → 31/8 | note:E4 s:sawtooth release:0.5 phaser:8 ]",
"[ 31/8 → 4/1 | note:F#4 s:sawtooth release:0.5 phaser:8 ]",
]
`;
exports[`runs examples > example "phasercenter" example index 0 1`] = `
[
"[ 0/1 → 1/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
"[ 1/8 → 1/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
"[ 1/4 → 3/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
"[ 3/8 → 1/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
"[ 1/2 → 5/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
"[ 5/8 → 3/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
"[ 3/4 → 7/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
"[ 7/8 → 1/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
"[ 1/1 → 9/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
"[ 9/8 → 5/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
"[ 5/4 → 11/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
"[ 11/8 → 3/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
"[ 3/2 → 13/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
"[ 13/8 → 7/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
"[ 7/4 → 15/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
"[ 15/8 → 2/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
"[ 2/1 → 17/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
"[ 17/8 → 9/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
"[ 9/4 → 19/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
"[ 19/8 → 5/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
"[ 5/2 → 21/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
"[ 21/8 → 11/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
"[ 11/4 → 23/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
"[ 23/8 → 3/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
"[ 3/1 → 25/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
"[ 25/8 → 13/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
"[ 13/4 → 27/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
"[ 27/8 → 7/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
"[ 7/2 → 29/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
"[ 29/8 → 15/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
"[ 15/4 → 31/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
"[ 31/8 → 4/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
]
`;
exports[`runs examples > example "phaserdepth" example index 0 1`] = `
[
"[ 0/1 → 1/8 | note:D3 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
"[ 1/8 → 1/4 | note:E3 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
"[ 1/4 → 3/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
"[ 3/8 → 1/2 | note:A3 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
"[ 1/2 → 5/8 | note:B3 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
"[ 5/8 → 3/4 | note:D4 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
"[ 3/4 → 7/8 | note:E4 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
"[ 7/8 → 1/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
"[ 1/1 → 9/8 | note:D3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
"[ 9/8 → 5/4 | note:E3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
"[ 5/4 → 11/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
"[ 11/8 → 3/2 | note:A3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
"[ 3/2 → 13/8 | note:B3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
"[ 13/8 → 7/4 | note:D4 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
"[ 7/4 → 15/8 | note:E4 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
"[ 15/8 → 2/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
"[ 2/1 → 17/8 | note:D3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
"[ 17/8 → 9/4 | note:E3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
"[ 9/4 → 19/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
"[ 19/8 → 5/2 | note:A3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
"[ 5/2 → 21/8 | note:B3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
"[ 21/8 → 11/4 | note:D4 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
"[ 11/4 → 23/8 | note:E4 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
"[ 23/8 → 3/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
"[ 3/1 → 25/8 | note:D3 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
"[ 25/8 → 13/4 | note:E3 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
"[ 13/4 → 27/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
"[ 27/8 → 7/2 | note:A3 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
"[ 7/2 → 29/8 | note:B3 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
"[ 29/8 → 15/4 | note:D4 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
"[ 15/4 → 31/8 | note:E4 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
"[ 31/8 → 4/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
]
`;
exports[`runs examples > example "phasersweep" example index 0 1`] = `
[
"[ 0/1 → 1/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
"[ 1/8 → 1/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
"[ 1/4 → 3/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
"[ 3/8 → 1/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
"[ 1/2 → 5/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
"[ 5/8 → 3/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
"[ 3/4 → 7/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
"[ 7/8 → 1/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
"[ 1/1 → 9/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
"[ 9/8 → 5/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
"[ 5/4 → 11/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
"[ 11/8 → 3/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
"[ 3/2 → 13/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
"[ 13/8 → 7/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
"[ 7/4 → 15/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
"[ 15/8 → 2/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
"[ 2/1 → 17/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
"[ 17/8 → 9/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
"[ 9/4 → 19/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
"[ 19/8 → 5/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
"[ 5/2 → 21/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
"[ 21/8 → 11/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
"[ 11/4 → 23/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
"[ 23/8 → 3/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
"[ 3/1 → 25/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
"[ 25/8 → 13/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
"[ 13/4 → 27/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
"[ 27/8 → 7/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
"[ 7/2 → 29/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
"[ 29/8 → 15/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
"[ 15/4 → 31/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
"[ 31/8 → 4/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
]
`;
exports[`runs examples > example "pianoroll" example index 0 1`] = `
[
"[ 0/1 → 1/8 | note:C2 s:piano clip:1 ]",
"[ (1/4 → 1/3) ⇝ 3/8 | note:C2 s:piano clip:1 ]",
"[ 1/4 ⇜ (1/3 → 3/8) | note:A2 s:piano clip:1 ]",
"[ 3/8 → 1/2 | note:A2 s:piano clip:1 ]",
"[ (5/8 → 2/3) ⇝ 3/4 | note:A2 s:piano clip:1 ]",
"[ 5/8 ⇜ (2/3 → 3/4) | note:G2 s:piano clip:1 ]",
"[ 3/4 → 7/8 | note:G2 s:piano clip:1 ]",
"[ 1/1 → 9/8 | note:C2 s:piano clip:1 ]",
"[ (5/4 → 4/3) ⇝ 11/8 | note:C2 s:piano clip:1 ]",
"[ 5/4 ⇜ (4/3 → 11/8) | note:A2 s:piano clip:1 ]",
"[ 11/8 → 3/2 | note:A2 s:piano clip:1 ]",
"[ (13/8 → 5/3) ⇝ 7/4 | note:A2 s:piano clip:1 ]",
"[ 13/8 ⇜ (5/3 → 7/4) | note:G2 s:piano clip:1 ]",
"[ 7/4 → 15/8 | note:G2 s:piano clip:1 ]",
"[ 2/1 → 17/8 | note:C2 s:piano clip:1 ]",
"[ (9/4 → 7/3) ⇝ 19/8 | note:C2 s:piano clip:1 ]",
"[ 9/4 ⇜ (7/3 → 19/8) | note:A2 s:piano clip:1 ]",
"[ 19/8 → 5/2 | note:A2 s:piano clip:1 ]",
"[ (21/8 → 8/3) ⇝ 11/4 | note:A2 s:piano clip:1 ]",
"[ 21/8 ⇜ (8/3 → 11/4) | note:G2 s:piano clip:1 ]",
"[ 11/4 → 23/8 | note:G2 s:piano clip:1 ]",
"[ 3/1 → 25/8 | note:C2 s:piano clip:1 ]",
"[ (13/4 → 10/3) ⇝ 27/8 | note:C2 s:piano clip:1 ]",
"[ 13/4 ⇜ (10/3 → 27/8) | note:A2 s:piano clip:1 ]",
"[ 27/8 → 7/2 | note:A2 s:piano clip:1 ]",
"[ (29/8 → 11/3) ⇝ 15/4 | note:A2 s:piano clip:1 ]",
"[ 29/8 ⇜ (11/3 → 15/4) | note:G2 s:piano clip:1 ]",
"[ 15/4 → 31/8 | note:G2 s:piano clip:1 ]",
]
`;
exports[`runs examples > example "pick" example index 0 1`] = `
[
"[ 0/1 → 1/2 | note:g ]",
"[ 1/2 → 1/1 | note:a ]",
"[ 1/1 → 3/2 | note:e ]",
"[ 3/2 → 2/1 | note:f ]",
"[ 2/1 → 9/4 | note:f ]",
"[ 9/4 → 5/2 | note:g ]",
"[ 5/2 → 11/4 | note:f ]",
"[ 11/4 → 3/1 | note:g ]",
"[ 3/1 → 13/4 | note:g ]",
"[ 13/4 → 7/2 | note:a ]",
"[ 7/2 → 15/4 | note:c ]",
"[ 15/4 → 4/1 | note:d ]",
]
`;
exports[`runs examples > example "ply" example index 0 1`] = `
[
"[ 0/1 → 1/4 | s:bd ]",
@@ -3589,35 +3293,6 @@ exports[`runs examples > example "polymeterSteps" example index 0 1`] = `
]
`;
exports[`runs examples > example "postgain" example index 0 1`] = `
[
"[ 0/1 → 1/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 0/1 → 1/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 1/4 → 1/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 1/2 → 3/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 1/2 → 1/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 3/4 → 1/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 1/1 → 5/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 1/1 → 3/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 5/4 → 3/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 3/2 → 7/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 3/2 → 2/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 7/4 → 2/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 2/1 → 9/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 2/1 → 5/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 9/4 → 5/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 5/2 → 11/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 5/2 → 3/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 11/4 → 3/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 3/1 → 13/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 3/1 → 7/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 13/4 → 7/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 7/2 → 15/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 7/2 → 4/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
"[ 15/4 → 4/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]",
]
`;
exports[`runs examples > example "press" example index 0 1`] = `
[
"[ 0/1 → 1/2 | s:hh ]",
@@ -3862,27 +3537,6 @@ exports[`runs examples > example "release" example index 0 1`] = `
]
`;
exports[`runs examples > example "repeatCycles" example index 0 1`] = `
[
"[ 0/1 → 1/4 | note:42 s:gm_acoustic_guitar_nylon ]",
"[ 1/4 → 1/2 | note:38 s:gm_acoustic_guitar_nylon ]",
"[ 1/2 → 3/4 | note:35 s:gm_acoustic_guitar_nylon ]",
"[ 3/4 → 1/1 | note:38 s:gm_acoustic_guitar_nylon ]",
"[ 1/1 → 5/4 | note:42 s:gm_acoustic_guitar_nylon ]",
"[ 5/4 → 3/2 | note:38 s:gm_acoustic_guitar_nylon ]",
"[ 3/2 → 7/4 | note:35 s:gm_acoustic_guitar_nylon ]",
"[ 7/4 → 2/1 | note:38 s:gm_acoustic_guitar_nylon ]",
"[ 2/1 → 9/4 | note:42 s:gm_acoustic_guitar_nylon ]",
"[ 9/4 → 5/2 | note:36 s:gm_acoustic_guitar_nylon ]",
"[ 5/2 → 11/4 | note:39 s:gm_acoustic_guitar_nylon ]",
"[ 11/4 → 3/1 | note:41 s:gm_acoustic_guitar_nylon ]",
"[ 3/1 → 13/4 | note:42 s:gm_acoustic_guitar_nylon ]",
"[ 13/4 → 7/2 | note:36 s:gm_acoustic_guitar_nylon ]",
"[ 7/2 → 15/4 | note:39 s:gm_acoustic_guitar_nylon ]",
"[ 15/4 → 4/1 | note:41 s:gm_acoustic_guitar_nylon ]",
]
`;
exports[`runs examples > example "reset" example index 0 1`] = `
[
"[ 0/1 → 1/4 | s:hh ]",
@@ -4244,42 +3898,6 @@ exports[`runs examples > example "samples" example index 1 1`] = `
]
`;
exports[`runs examples > example "samples" example index 2 1`] = `
[
"[ 0/1 → 1/2 | s:noise ]",
"[ 1/2 → 3/4 | s:chimp n:0 ]",
"[ 3/4 → 1/1 | s:chimp n:0 ]",
"[ 1/1 → 3/2 | s:noise ]",
"[ 3/2 → 2/1 | s:chimp n:1 ]",
"[ 2/1 → 5/2 | s:noise ]",
"[ 5/2 → 11/4 | s:chimp n:0 ]",
"[ 11/4 → 3/1 | s:chimp n:0 ]",
"[ 3/1 → 7/2 | s:noise ]",
"[ 7/2 → 4/1 | s:chimp n:1 ]",
]
`;
exports[`runs examples > example "samples" example index 3 1`] = `
[
"[ 0/1 → 1/4 | s:chocolat ]",
"[ 1/4 → 1/2 | s:chocolat ]",
"[ 1/2 → 3/4 | s:chocolat ]",
"[ 3/4 → 1/1 | s:chocolat ]",
"[ 1/1 → 5/4 | s:chocolat ]",
"[ 5/4 → 3/2 | s:chocolat ]",
"[ 3/2 → 7/4 | s:chocolat ]",
"[ 7/4 → 2/1 | s:chocolat ]",
"[ 2/1 → 9/4 | s:chocolat ]",
"[ 9/4 → 5/2 | s:chocolat ]",
"[ 5/2 → 11/4 | s:chocolat ]",
"[ 11/4 → 3/1 | s:chocolat ]",
"[ 3/1 → 13/4 | s:chocolat ]",
"[ 13/4 → 7/2 | s:chocolat ]",
"[ 7/2 → 15/4 | s:chocolat ]",
"[ 15/4 → 4/1 | s:chocolat ]",
]
`;
exports[`runs examples > example "saw" example index 0 1`] = `
[
"[ 0/1 → 1/4 | note:c3 clip:0.03125 ]",
@@ -4324,96 +3942,96 @@ exports[`runs examples > example "saw" example index 1 1`] = `
exports[`runs examples > example "scale" example index 0 1`] = `
[
"[ 0/1 → 1/6 | note:C3 ]",
"[ 1/6 → 1/3 | note:E3 ]",
"[ 1/3 → 1/2 | note:G3 ]",
"[ 1/2 → 2/3 | note:B3 ]",
"[ 2/3 → 5/6 | note:G3 ]",
"[ 5/6 → 1/1 | note:E3 ]",
"[ 1/1 → 7/6 | note:C3 ]",
"[ 7/6 → 4/3 | note:E3 ]",
"[ 4/3 → 3/2 | note:G3 ]",
"[ 3/2 → 5/3 | note:B3 ]",
"[ 5/3 → 11/6 | note:G3 ]",
"[ 11/6 → 2/1 | note:E3 ]",
"[ 2/1 → 13/6 | note:C3 ]",
"[ 13/6 → 7/3 | note:E3 ]",
"[ 7/3 → 5/2 | note:G3 ]",
"[ 5/2 → 8/3 | note:B3 ]",
"[ 8/3 → 17/6 | note:G3 ]",
"[ 17/6 → 3/1 | note:E3 ]",
"[ 3/1 → 19/6 | note:C3 ]",
"[ 19/6 → 10/3 | note:E3 ]",
"[ 10/3 → 7/2 | note:G3 ]",
"[ 7/2 → 11/3 | note:B3 ]",
"[ 11/3 → 23/6 | note:G3 ]",
"[ 23/6 → 4/1 | note:E3 ]",
"[ 0/1 → 1/6 | n:0 note:C3 ]",
"[ 1/6 → 1/3 | n:2 note:E3 ]",
"[ 1/3 → 1/2 | n:4 note:G3 ]",
"[ 1/2 → 2/3 | n:6 note:B3 ]",
"[ 2/3 → 5/6 | n:4 note:G3 ]",
"[ 5/6 → 1/1 | n:2 note:E3 ]",
"[ 1/1 → 7/6 | n:0 note:C3 ]",
"[ 7/6 → 4/3 | n:2 note:E3 ]",
"[ 4/3 → 3/2 | n:4 note:G3 ]",
"[ 3/2 → 5/3 | n:6 note:B3 ]",
"[ 5/3 → 11/6 | n:4 note:G3 ]",
"[ 11/6 → 2/1 | n:2 note:E3 ]",
"[ 2/1 → 13/6 | n:0 note:C3 ]",
"[ 13/6 → 7/3 | n:2 note:E3 ]",
"[ 7/3 → 5/2 | n:4 note:G3 ]",
"[ 5/2 → 8/3 | n:6 note:B3 ]",
"[ 8/3 → 17/6 | n:4 note:G3 ]",
"[ 17/6 → 3/1 | n:2 note:E3 ]",
"[ 3/1 → 19/6 | n:0 note:C3 ]",
"[ 19/6 → 10/3 | n:2 note:E3 ]",
"[ 10/3 → 7/2 | n:4 note:G3 ]",
"[ 7/2 → 11/3 | n:6 note:B3 ]",
"[ 11/3 → 23/6 | n:4 note:G3 ]",
"[ 23/6 → 4/1 | n:2 note:E3 ]",
]
`;
exports[`runs examples > example "scale" example index 1 1`] = `
[
"[ 0/1 → 1/4 | note:C3 s:piano ]",
"[ 0/1 → 1/4 | note:C4 s:piano ]",
"[ 1/4 → 1/2 | note:G3 s:piano ]",
"[ 1/2 → 3/4 | note:E3 s:piano ]",
"[ 1/2 → 3/4 | note:C4 s:piano ]",
"[ 3/4 → 1/1 | note:G3 s:piano ]",
"[ 1/1 → 5/4 | note:C3 s:piano ]",
"[ 1/1 → 5/4 | note:C4 s:piano ]",
"[ 5/4 → 3/2 | note:G3 s:piano ]",
"[ 3/2 → 7/4 | note:E3 s:piano ]",
"[ 3/2 → 7/4 | note:C4 s:piano ]",
"[ 7/4 → 2/1 | note:G3 s:piano ]",
"[ 2/1 → 9/4 | note:C3 s:piano ]",
"[ 2/1 → 9/4 | note:C4 s:piano ]",
"[ 9/4 → 5/2 | note:G3 s:piano ]",
"[ 5/2 → 11/4 | note:Eb3 s:piano ]",
"[ 5/2 → 11/4 | note:C4 s:piano ]",
"[ 11/4 → 3/1 | note:G3 s:piano ]",
"[ 3/1 → 13/4 | note:C3 s:piano ]",
"[ 3/1 → 13/4 | note:C4 s:piano ]",
"[ 13/4 → 7/2 | note:G3 s:piano ]",
"[ 7/2 → 15/4 | note:Eb3 s:piano ]",
"[ 7/2 → 15/4 | note:C4 s:piano ]",
"[ 15/4 → 4/1 | note:G3 s:piano ]",
"[ 0/1 → 1/4 | n:0 note:C3 s:piano ]",
"[ 0/1 → 1/4 | n:7 note:C4 s:piano ]",
"[ 1/4 → 1/2 | n:4 note:G3 s:piano ]",
"[ 1/2 → 3/4 | n:2 note:E3 s:piano ]",
"[ 1/2 → 3/4 | n:7 note:C4 s:piano ]",
"[ 3/4 → 1/1 | n:4 note:G3 s:piano ]",
"[ 1/1 → 5/4 | n:0 note:C3 s:piano ]",
"[ 1/1 → 5/4 | n:7 note:C4 s:piano ]",
"[ 5/4 → 3/2 | n:4 note:G3 s:piano ]",
"[ 3/2 → 7/4 | n:2 note:E3 s:piano ]",
"[ 3/2 → 7/4 | n:7 note:C4 s:piano ]",
"[ 7/4 → 2/1 | n:4 note:G3 s:piano ]",
"[ 2/1 → 9/4 | n:0 note:C3 s:piano ]",
"[ 2/1 → 9/4 | n:7 note:C4 s:piano ]",
"[ 9/4 → 5/2 | n:4 note:G3 s:piano ]",
"[ 5/2 → 11/4 | n:2 note:Eb3 s:piano ]",
"[ 5/2 → 11/4 | n:7 note:C4 s:piano ]",
"[ 11/4 → 3/1 | n:4 note:G3 s:piano ]",
"[ 3/1 → 13/4 | n:0 note:C3 s:piano ]",
"[ 3/1 → 13/4 | n:7 note:C4 s:piano ]",
"[ 13/4 → 7/2 | n:4 note:G3 s:piano ]",
"[ 7/2 → 15/4 | n:2 note:Eb3 s:piano ]",
"[ 7/2 → 15/4 | n:7 note:C4 s:piano ]",
"[ 15/4 → 4/1 | n:4 note:G3 s:piano ]",
]
`;
exports[`runs examples > example "scale" example index 2 1`] = `
[
"[ 0/1 → 1/8 | note:C5 s:folkharp ]",
"[ 1/8 → 1/4 | note:F3 s:folkharp ]",
"[ 1/4 → 3/8 | note:F4 s:folkharp ]",
"[ 3/8 → 1/2 | note:A3 s:folkharp ]",
"[ 1/2 → 5/8 | note:F3 s:folkharp ]",
"[ 5/8 → 3/4 | note:C4 s:folkharp ]",
"[ 3/4 → 7/8 | note:A4 s:folkharp ]",
"[ 7/8 → 1/1 | note:G4 s:folkharp ]",
"[ 1/1 → 9/8 | note:F4 s:folkharp ]",
"[ 9/8 → 5/4 | note:D3 s:folkharp ]",
"[ 5/4 → 11/8 | note:D3 s:folkharp ]",
"[ 11/8 → 3/2 | note:D4 s:folkharp ]",
"[ 3/2 → 13/8 | note:F3 s:folkharp ]",
"[ 13/8 → 7/4 | note:A3 s:folkharp ]",
"[ 7/4 → 15/8 | note:D4 s:folkharp ]",
"[ 15/8 → 2/1 | note:C5 s:folkharp ]",
"[ 2/1 → 17/8 | note:A3 s:folkharp ]",
"[ 17/8 → 9/4 | note:C3 s:folkharp ]",
"[ 9/4 → 19/8 | note:G4 s:folkharp ]",
"[ 19/8 → 5/2 | note:F3 s:folkharp ]",
"[ 5/2 → 21/8 | note:F4 s:folkharp ]",
"[ 21/8 → 11/4 | note:D4 s:folkharp ]",
"[ 11/4 → 23/8 | note:D5 s:folkharp ]",
"[ 23/8 → 3/1 | note:G3 s:folkharp ]",
"[ 3/1 → 25/8 | note:C3 s:folkharp ]",
"[ 25/8 → 13/4 | note:D5 s:folkharp ]",
"[ 13/4 → 27/8 | note:A3 s:folkharp ]",
"[ 27/8 → 7/2 | note:A4 s:folkharp ]",
"[ 7/2 → 29/8 | note:C5 s:folkharp ]",
"[ 29/8 → 15/4 | note:F5 s:folkharp ]",
"[ 15/4 → 31/8 | note:D3 s:folkharp ]",
"[ 31/8 → 4/1 | note:A3 s:folkharp ]",
"[ 0/1 → 1/8 | n:10 note:C5 s:folkharp ]",
"[ 1/8 → 1/4 | n:2 note:F3 s:folkharp ]",
"[ 1/4 → 3/8 | n:7 note:F4 s:folkharp ]",
"[ 3/8 → 1/2 | n:4 note:A3 s:folkharp ]",
"[ 1/2 → 5/8 | n:2 note:F3 s:folkharp ]",
"[ 5/8 → 3/4 | n:5 note:C4 s:folkharp ]",
"[ 3/4 → 7/8 | n:9 note:A4 s:folkharp ]",
"[ 7/8 → 1/1 | n:8 note:G4 s:folkharp ]",
"[ 1/1 → 9/8 | n:7 note:F4 s:folkharp ]",
"[ 9/8 → 5/4 | n:1 note:D3 s:folkharp ]",
"[ 5/4 → 11/8 | n:1 note:D3 s:folkharp ]",
"[ 11/8 → 3/2 | n:6 note:D4 s:folkharp ]",
"[ 3/2 → 13/8 | n:2 note:F3 s:folkharp ]",
"[ 13/8 → 7/4 | n:4 note:A3 s:folkharp ]",
"[ 7/4 → 15/8 | n:6 note:D4 s:folkharp ]",
"[ 15/8 → 2/1 | n:10 note:C5 s:folkharp ]",
"[ 2/1 → 17/8 | n:4 note:A3 s:folkharp ]",
"[ 17/8 → 9/4 | n:0 note:C3 s:folkharp ]",
"[ 9/4 → 19/8 | n:8 note:G4 s:folkharp ]",
"[ 19/8 → 5/2 | n:2 note:F3 s:folkharp ]",
"[ 5/2 → 21/8 | n:7 note:F4 s:folkharp ]",
"[ 21/8 → 11/4 | n:6 note:D4 s:folkharp ]",
"[ 11/4 → 23/8 | n:11 note:D5 s:folkharp ]",
"[ 23/8 → 3/1 | n:3 note:G3 s:folkharp ]",
"[ 3/1 → 25/8 | n:0 note:C3 s:folkharp ]",
"[ 25/8 → 13/4 | n:11 note:D5 s:folkharp ]",
"[ 13/4 → 27/8 | n:4 note:A3 s:folkharp ]",
"[ 27/8 → 7/2 | n:9 note:A4 s:folkharp ]",
"[ 7/2 → 29/8 | n:10 note:C5 s:folkharp ]",
"[ 29/8 → 15/4 | n:12 note:F5 s:folkharp ]",
"[ 15/4 → 31/8 | n:1 note:D3 s:folkharp ]",
"[ 31/8 → 4/1 | n:4 note:A3 s:folkharp ]",
]
`;
@@ -4438,15 +4056,6 @@ exports[`runs examples > example "scaleTranspose" example index 0 1`] = `
]
`;
exports[`runs examples > example "scope" example index 0 1`] = `
[
"[ 0/1 → 1/1 | s:sawtooth analyze:1 ]",
"[ 1/1 → 2/1 | s:sawtooth analyze:1 ]",
"[ 2/1 → 3/1 | s:sawtooth analyze:1 ]",
"[ 3/1 → 4/1 | s:sawtooth analyze:1 ]",
]
`;
exports[`runs examples > example "segment" example index 0 1`] = `
[
"[ 0/1 → 1/24 | note:40.25 ]",
@@ -4884,25 +4493,6 @@ exports[`runs examples > example "square" example index 0 1`] = `
]
`;
exports[`runs examples > example "squeeze" example index 0 1`] = `
[
"[ 0/1 → 1/1 | note:g ]",
"[ 1/1 → 2/1 | note:a ]",
"[ 2/1 → 17/8 | note:f ]",
"[ 17/8 → 9/4 | note:g ]",
"[ 9/4 → 19/8 | note:f ]",
"[ 19/8 → 5/2 | note:g ]",
"[ 5/2 → 21/8 | note:f ]",
"[ 21/8 → 11/4 | note:g ]",
"[ 11/4 → 23/8 | note:f ]",
"[ 23/8 → 3/1 | note:g ]",
"[ 3/1 → 13/4 | note:g ]",
"[ 13/4 → 7/2 | note:a ]",
"[ 7/2 → 15/4 | note:c ]",
"[ 15/4 → 4/1 | note:d ]",
]
`;
exports[`runs examples > example "squiz" example index 0 1`] = `
[
"[ 0/1 → 1/4 | squiz:2 s:bd ]",
@@ -4974,23 +4564,6 @@ exports[`runs examples > example "stack" example index 0 2`] = `
]
`;
exports[`runs examples > example "striate" example index 0 1`] = `
[
"[ 0/1 → 1/3 | s:numbers n:0 begin:0 end:0.16666666666666666 ]",
"[ 1/3 → 2/3 | s:numbers n:1 begin:0 end:0.16666666666666666 ]",
"[ 2/3 → 1/1 | s:numbers n:2 begin:0 end:0.16666666666666666 ]",
"[ 1/1 → 4/3 | s:numbers n:0 begin:0.16666666666666666 end:0.3333333333333333 ]",
"[ 4/3 → 5/3 | s:numbers n:1 begin:0.16666666666666666 end:0.3333333333333333 ]",
"[ 5/3 → 2/1 | s:numbers n:2 begin:0.16666666666666666 end:0.3333333333333333 ]",
"[ 2/1 → 7/3 | s:numbers n:0 begin:0.3333333333333333 end:0.5 ]",
"[ 7/3 → 8/3 | s:numbers n:1 begin:0.3333333333333333 end:0.5 ]",
"[ 8/3 → 3/1 | s:numbers n:2 begin:0.3333333333333333 end:0.5 ]",
"[ 3/1 → 10/3 | s:numbers n:0 begin:0.5 end:0.6666666666666666 ]",
"[ 10/3 → 11/3 | s:numbers n:1 begin:0.5 end:0.6666666666666666 ]",
"[ 11/3 → 4/1 | s:numbers n:2 begin:0.5 end:0.6666666666666666 ]",
]
`;
exports[`runs examples > example "struct" example index 0 1`] = `
[
"[ 0/1 → 1/4 | note:c3 ]",
@@ -5447,51 +5020,6 @@ exports[`runs examples > example "withValue" example index 0 1`] = `
]
`;
exports[`runs examples > example "xfade" example index 0 1`] = `
[
"[ 0/1 → 1/8 | s:hh gain:0 ]",
"[ 0/1 → 1/2 | s:bd gain:1 ]",
"[ 1/8 → 1/4 | s:hh gain:0 ]",
"[ 1/4 → 3/8 | s:hh gain:0 ]",
"[ 3/8 → 1/2 | s:hh gain:0 ]",
"[ 1/2 → 5/8 | s:hh gain:0 ]",
"[ 1/2 → 1/1 | s:bd gain:1 ]",
"[ 5/8 → 3/4 | s:hh gain:0 ]",
"[ 3/4 → 7/8 | s:hh gain:0 ]",
"[ 7/8 → 1/1 | s:hh gain:0 ]",
"[ 1/1 → 9/8 | s:hh gain:0.5 ]",
"[ 1/1 → 3/2 | s:bd gain:1 ]",
"[ 9/8 → 5/4 | s:hh gain:0.5 ]",
"[ 5/4 → 11/8 | s:hh gain:0.5 ]",
"[ 11/8 → 3/2 | s:hh gain:0.5 ]",
"[ 3/2 → 13/8 | s:hh gain:0.5 ]",
"[ 3/2 → 2/1 | s:bd gain:1 ]",
"[ 13/8 → 7/4 | s:hh gain:0.5 ]",
"[ 7/4 → 15/8 | s:hh gain:0.5 ]",
"[ 15/8 → 2/1 | s:hh gain:0.5 ]",
"[ 2/1 → 17/8 | s:hh gain:1 ]",
"[ 2/1 → 5/2 | s:bd gain:1 ]",
"[ 17/8 → 9/4 | s:hh gain:1 ]",
"[ 9/4 → 19/8 | s:hh gain:1 ]",
"[ 19/8 → 5/2 | s:hh gain:1 ]",
"[ 5/2 → 21/8 | s:hh gain:1 ]",
"[ 5/2 → 3/1 | s:bd gain:1 ]",
"[ 21/8 → 11/4 | s:hh gain:1 ]",
"[ 11/4 → 23/8 | s:hh gain:1 ]",
"[ 23/8 → 3/1 | s:hh gain:1 ]",
"[ 3/1 → 25/8 | s:hh gain:1 ]",
"[ 3/1 → 7/2 | s:bd gain:0.5 ]",
"[ 25/8 → 13/4 | s:hh gain:1 ]",
"[ 13/4 → 27/8 | s:hh gain:1 ]",
"[ 27/8 → 7/2 | s:hh gain:1 ]",
"[ 7/2 → 29/8 | s:hh gain:1 ]",
"[ 7/2 → 4/1 | s:bd gain:0.5 ]",
"[ 29/8 → 15/4 | s:hh gain:1 ]",
"[ 15/4 → 31/8 | s:hh gain:1 ]",
"[ 31/8 → 4/1 | s:hh gain:1 ]",
]
`;
exports[`runs examples > example "zoom" example index 0 1`] = `
[
"[ 0/1 → 1/6 | s:hh ]",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -228,5 +228,5 @@ export const testCycles = {
festivalOfFingers3: 16,
};
// fixed: https://strudel.cc/?DBp75NUfSxIn (missing .note())
// bug: https://strudel.cc/?xHaKTd1kTpCn + https://strudel.cc/?o5LLePbx8kiQ
// fixed: https://strudel.tidalcycles.org/?DBp75NUfSxIn (missing .note())
// bug: https://strudel.tidalcycles.org/?xHaKTd1kTpCn + https://strudel.tidalcycles.org/?o5LLePbx8kiQ
+1 -1
View File
@@ -4,7 +4,7 @@ import data from './dbdump.json';
describe('renders shared tunes', async () => {
data.forEach(({ id, code, hash }) => {
const url = `https://strudel.cc/?${hash}`;
const url = `https://strudel.tidalcycles.org/?${hash}`;
it(`shared tune ${id} ${url}`, async ({ expect }) => {
if (code.includes('import(')) {
console.log('skip', url);
+2 -2
View File
@@ -1,6 +1,6 @@
# Strudel Website
This is the website for Strudel, deployed at [strudel.cc](https://strudel.cc).
This is the website for Strudel, deployed at [strudel.tidalcycles.org](https://strudel.tidalcycles.org/).
It includes the REPL live coding editor and the documentation site.
## Run locally
@@ -56,7 +56,7 @@ All commands are run from the root of the project, from a terminal:
| Command | Action |
| :--------------------- | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
+1 -1
View File
@@ -1,7 +1,7 @@
/*
Strudel - javascript-based environment for live coding algorithmic (musical) patterns
https://strudel.cc / https://github.com/tidalcycles/strudel/
https://strudel.tidalcycles.org / https://github.com/tidalcycles/strudel/
Copyright (C) Strudel contributors
https://github.com/tidalcycles/strudel/graphs/contributors
+1 -2
View File
@@ -11,7 +11,7 @@ import tailwind from '@astrojs/tailwind';
import AstroPWA from '@vite-pwa/astro';
// import { visualizer } from 'rollup-plugin-visualizer';
const site = `https://strudel.cc/`; // root url without a path
const site = `https://strudel.tidalcycles.org/`; // root url without a path
const base = '/'; // base path of the strudel site
// this rehype plugin converts relative anchor links to absolute ones
@@ -50,7 +50,6 @@ export default defineConfig({
mdx(options),
tailwind(),
AstroPWA({
experimental: { directoryAndTrailingSlashHandler: true },
registerType: 'autoUpdate',
injectRegister: 'auto',
workbox: {
+8 -9
View File
@@ -13,9 +13,9 @@
},
"dependencies": {
"@algolia/client-search": "^4.17.0",
"@astrojs/mdx": "^1.1.3",
"@astrojs/react": "^3.0.4",
"@astrojs/tailwind": "^5.0.2",
"@astrojs/mdx": "^0.19.0",
"@astrojs/react": "^2.1.1",
"@astrojs/tailwind": "^3.1.1",
"@docsearch/css": "^3.3.4",
"@docsearch/react": "^3.3.4",
"@headlessui/react": "^1.7.14",
@@ -28,14 +28,13 @@
"@strudel.cycles/mini": "workspace:*",
"@strudel.cycles/osc": "workspace:*",
"@strudel.cycles/react": "workspace:*",
"@strudel/codemirror": "workspace:*",
"@strudel.cycles/serial": "workspace:*",
"@strudel.cycles/soundfonts": "workspace:*",
"@strudel.cycles/tonal": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@strudel.cycles/xen": "workspace:*",
"@strudel/hydra": "workspace:*",
"@strudel/codemirror": "workspace:*",
"@strudel/desktopbridge": "workspace:*",
"@supabase/supabase-js": "^2.21.0",
"@tailwindcss/forms": "^0.5.3",
@@ -45,7 +44,7 @@
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.1",
"@uiw/codemirror-themes-all": "^4.19.16",
"astro": "^3.4.2",
"astro": "^2.3.2",
"canvas": "^2.11.2",
"claviature": "^0.1.0",
"fraction.js": "^4.2.0",
@@ -60,9 +59,9 @@
"tailwindcss": "^3.3.2"
},
"devDependencies": {
"@vite-pwa/astro": "^0.1.4",
"@vite-pwa/astro": "^0.0.5",
"html-escaper": "^3.0.3",
"vite-plugin-pwa": "^0.16.5",
"workbox-window": "^7.0.0"
"vite-plugin-pwa": "^0.14.7",
"workbox-window": "^6.5.4"
}
}
+1 -1
View File
@@ -1 +1 @@
strudel.cc
strudel.tidalcycles.org
+5 -10
View File
@@ -6,7 +6,7 @@ export const SITE = {
export const OPEN_GRAPH = {
image: {
src: 'https://strudel.cc/icon.png',
src: 'https://strudel.tidalcycles.org/icon.png',
alt: 'Strudel Logo',
},
};
@@ -70,13 +70,14 @@ export const SIDEBAR: Sidebar = {
{ text: 'MIDI & OSC', link: 'learn/input-output' },
],
More: [
{ text: 'Recipes', link: 'recipes/recipes' },
{ text: 'Mini-Notation', link: 'learn/mini-notation' },
{ text: 'Coding syntax', link: 'learn/code' },
{ text: 'Offline', link: 'learn/pwa' },
{ text: 'Patterns', link: 'technical-manual/patterns' },
{ text: 'Pattern Alignment', link: 'technical-manual/alignment' },
{ text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' },
{ text: 'Music metadata', link: 'learn/metadata' },
{ text: 'CSound', link: 'learn/csound' },
{ text: 'Hydra', link: 'learn/hydra' },
],
'Pattern Functions': [
{ text: 'Introduction', link: 'functions/intro' },
@@ -88,13 +89,7 @@ export const SIDEBAR: Sidebar = {
{ text: 'Accumulation', link: 'learn/accumulation' },
{ text: 'Tonal Functions', link: 'learn/tonal' },
],
Understand: [
{ text: 'Coding syntax', link: 'learn/code' },
{ text: 'Pitch', link: 'understand/pitch' },
{ text: 'Cycles', link: 'understand/cycles' },
{ text: 'Pattern Alignment', link: 'technical-manual/alignment' },
{ text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' },
],
Understand: [{ text: 'Pitch', link: 'understand/pitch' }],
Development: [
{ text: 'REPL', link: 'technical-manual/repl' },
{ text: 'Sounds', link: 'technical-manual/sounds' },
+7 -7
View File
@@ -1,26 +1,26 @@
.mini-repl .cm-activeLine,
.mini-repl .cm-activeLineGutter {
.cm-activeLine,
.cm-activeLineGutter {
background-color: transparent !important;
}
.mini-repl .cm-theme {
.cm-theme {
background-color: var(--background);
border: 1px solid var(--lineHighlight);
padding: 2px;
}
.mini-repl .cm-scroller {
.cm-scroller {
font-family: inherit !important;
}
.mini-repl .cm-gutters {
.cm-gutters {
display: none !important;
}
.mini-repl .cm-cursorLayer {
.cm-cursorLayer {
animation-name: inherit !important;
}
.mini-repl .cm-cursor {
.cm-cursor {
border-left: 2px solid currentcolor !important;
}
+23 -6
View File
@@ -1,14 +1,31 @@
import { noteToMidi } from '@strudel.cycles/core';
import { evalScope, controls, noteToMidi } from '@strudel.cycles/core';
import { initAudioOnFirstClick } from '@strudel.cycles/webaudio';
import { useEffect, useState } from 'react';
import { prebake } from '../repl/prebake';
import { themes } from '../repl/themes.mjs';
import { themes, settings } from '../repl/themes.mjs';
import './MiniRepl.css';
import { useSettings } from '../settings.mjs';
import Claviature from '@components/Claviature';
let init;
let modules;
if (typeof window !== 'undefined') {
init = prebake();
modules = evalScope(
controls,
import('@strudel.cycles/core'),
import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/midi'),
import('@strudel.cycles/xen'),
import('@strudel.cycles/webaudio'),
import('@strudel.cycles/osc'),
import('@strudel.cycles/csound'),
import('@strudel.cycles/soundfonts'),
);
}
if (typeof window !== 'undefined') {
initAudioOnFirstClick();
prebake();
}
export function MiniRepl({
@@ -28,12 +45,12 @@ export function MiniRepl({
useEffect(() => {
// we have to load this package on the client
// because codemirror throws an error on the server
Promise.all([import('@strudel.cycles/react'), init])
Promise.all([import('@strudel.cycles/react'), modules])
.then(([res]) => setRepl(() => res.MiniRepl))
.catch((err) => console.error(err));
}, []);
return Repl ? (
<div className="mb-4 mini-repl">
<div className="mb-4">
<Repl
tune={tune}
hideOutsideView={true}
+23 -23
View File
@@ -25,7 +25,7 @@ import Box from '@components/Box.astro';
lpf = **l**ow **p**ass **f**ilter
- Ändere `lpf` in 200. Hörst du, wie der Bass dumpfer klingt? Es klingt so, als würde die Musik hinter einer geschlossenen Tür spielen 🚪
- Ändere `lpf` in 200. Hörst du wie der Bass dumpfer klingt? Es klingt so ähnlich als würde die Musik hinter einer geschlossenen Tür laufen 🚪
- Lass uns nun die Tür öffnen: Ändere `lpf` in 5000. Der Klang wird dadurch viel heller und schärfer ✨🪩
</Box>
@@ -42,9 +42,9 @@ lpf = **l**ow **p**ass **f**ilter
<Box>
- Füg noch mehr `lpf` Werte hinzu
- Das Pattern in `lpf` ändert nicht den Rhythmus der Basslinie
- Das pattern in `lpf` ändert nicht den Rhythmus der Bassline
Später sehen wir, wie man mit Wellenformen Dinge automatisieren kann.
Später sehen wir wie man mit Wellenformen Dinge automatisieren kann.
</Box>
@@ -73,7 +73,7 @@ Später sehen wir, wie man mit Wellenformen Dinge automatisieren kann.
Bei Rhythmen ist die Dynamik (= Veränderungen der Lautstärke) sehr wichtig.
- Entferne `.gain(...)` und achte darauf, wie es viel flacher klingt.
- Entferne `.gain(...)` und achte darauf wie es viel flacher klingt.
- Mach es rückgängig (strg+z dann strg+enter)
</Box>
@@ -99,13 +99,13 @@ Lass uns die obigen Beispiele kombinieren:
<Box>
Versuche die einzelnen Teile innerhalb von `stack` zu erkennen. Schau dir an wie die Kommas gesetzt sind.
Versuche die einzelnen Teile innerhalb `stack` zu erkennen, schau dir an wie die Kommas gesetzt sind.
Die 3 Teile (Drums, Bass, Akkorde) sind genau wie vorher, nur in einem `stack`, getrennt durch Kommas.
Die 3 Teile (Drums, Bass, Akkorde) sind genau wie vorher, nur in einem `stack`, getrennt durch Kommas
</Box>
**Den Sound formen mit ADSR-Hüllkurve**
**Den Sound formen mit ADSR Hüllkurve**
<MiniRepl
hideHeader
@@ -120,14 +120,14 @@ Die 3 Teile (Drums, Bass, Akkorde) sind genau wie vorher, nur in einem `stack`,
<Box>
Versuche herauszufinden, was die Zahlen machen. Probier folgendes:
Versuche herauszufinden was die Zahlen machen. Probier folgendes:
- attack: `.5` vs `0`
- decay: `.5` vs `0`
- sustain: `1` vs `.25` vs `0`
- release: `0` vs `.5` vs `1`
Kannst du erraten, was die einzelnen Werte machen?
Kannst du erraten was die einzelnen Werte machen?
</Box>
@@ -142,7 +142,7 @@ Kannst du erraten, was die einzelnen Werte machen?
</QA>
**adsr-Kurznotation**
**adsr Kurznotation**
<MiniRepl
hideHeader
@@ -169,9 +169,9 @@ Kannst du erraten, was die einzelnen Werte machen?
Probier verschiedene `delay` Werte zwischen 0 und 1. Übrigens: `.5` ist kurz für `0.5`.
Was passiert, wenn du `.delay(".8:.125")` schreibst? Kannst du erraten, was die zweite Zahl macht?
Was passiert wenn du `.delay(".8:.125")` schreibst? Kannst du erraten was die zweite Zahl macht?
Was passiert, wenn du `.delay(".8:.06:.8")` schreibst? Kannst du erraten, was die dritte Zahl macht?
Was passiert wenn du `.delay(".8:.06:.8")` schreibst? Kannst du erraten was die dritte Zahl macht?
</Box>
@@ -181,7 +181,7 @@ Was passiert, wenn du `.delay(".8:.06:.8")` schreibst? Kannst du erraten, was di
- a: Lautstärke des Delays
- b: Verzögerungszeit
- c: Feedback (je kleiner, desto schneller verschwindet das Delay)
- c: Feedback (je kleiner desto schneller verschwindet das Delay)
</QA>
@@ -203,7 +203,7 @@ Füg auch ein Delay hinzu!
</Box>
**kleiner Dub-Tune**
**kleiner dub tune**
<MiniRepl
hideHeader
@@ -238,7 +238,7 @@ Für echten Dub fehlt noch der Bass:
<Box>
Füg `.hush()` ans Ende eines Patterns im stack...
Füg `.hush()` ans ende eines Patterns im stack...
</Box>
@@ -258,25 +258,25 @@ Füg `.hush()` ans Ende eines Patterns im stack...
**fast and slow = schnell und langsam**
Mit `fast` und `slow` kann man das Tempo eines Patterns außerhalb der Mini-Notation ändern:
Mit `fast` und `slow` kann man das tempo eines patterns außerhalb der Mini-Notation ändern:
<MiniRepl hideHeader client:visible tune={`sound("bd*2,~ rim").slow(2)`} />
<Box>
Ändere den `slow`-Wert. Ersetze `slow` durch `fast`.
Ändere den `slow` Wert. Tausche `slow` durch `fast`.
Was passiert, wenn du den Wert automatisierst? z.b. `.fast("<1 [2 4]>")` ?
Was passiert wenn du den Wert automatisierst? z.b. `.fast("<1 [2 4]>")` ?
</Box>
Übrigens, innerhalb der Mini-Notation: `fast` ist `*` und `slow` ist `/`.
Übrigens, innerhalb der Mini-Notation, `fast` ist `*` und `slow` ist `/`.
<MiniRepl hideHeader client:visible tune={`sound("[bd*2,~ rim]*<1 [2 4]>")`} />
## Automation mit Signalen
Anstatt Werte schrittweise zu automatisieren, können wir auch sogenannte Signale benutzen:
Anstatt Werte schrittweise zu automatisieren können wir auch sogenannte Signale benutzen:
<MiniRepl hideHeader client:visible tune={`sound("hh*16").gain(sine)`} punchcard punchcardLabels={false} />
@@ -296,7 +296,7 @@ Signale bewegen sich standardmäßig zwischen 0 und 1. Wir können das mit `rang
<MiniRepl hideHeader client:visible tune={`sound("hh*8").lpf(saw.range(500, 2000))`} />
`range` ist nützlich wenn wir Funktionen mit einem anderen Wertebereich als 0 und 1 automatisieren wollen (z.b. `lpf`)
`range` ist nützlich wenn wir Funktionen mit einem anderen Wertebereich als 0 und 1 automatisieren wollen (z.b. lpf)
<Box>
@@ -322,7 +322,7 @@ Die ganze Automation braucht nun 8 cycle bis sie sich wiederholt.
## Rückblick
| Name | Beispiel |
| name | example |
| ----- | -------------------------------------------------------------------------------------------------- |
| lpf | <MiniRepl hideHeader client:visible tune={`note("c2 c3").s("sawtooth").lpf("<400 2000>")`} /> |
| vowel | <MiniRepl hideHeader client:visible tune={`note("c3 eb3 g3").s("sawtooth").vowel("<a e i o>")`} /> |
@@ -333,4 +333,4 @@ Die ganze Automation braucht nun 8 cycle bis sie sich wiederholt.
| speed | <MiniRepl hideHeader client:visible tune={`s("bd rim").speed("<1 2 -1 -2>")`} /> |
| range | <MiniRepl hideHeader client:visible tune={`s("hh*16").lpf(saw.range(200,4000))`} /> |
Lass uns nun die für Tidal typischen [Pattern-Effekte anschauen](/de/workshop/pattern-effects).
Lass uns nun die für Tidal typischen [Pattern Effekte anschauen](/de/workshop/pattern-effects).
@@ -277,7 +277,7 @@ Das haben wir bisher gelernt:
| Schneller | \* | <MiniRepl hideHeader client:visible tune={`sound("bd sd*2 cp*3")`} /> |
| Parallel | , | <MiniRepl hideHeader client:visible tune={`sound("bd*2, hh*2 [hh oh]")`} /> |
Die mit Apostrophen umgebene Mini-Notation benutzt man normalerweise in einer sogenannten Funktion.
Die mit Apostrophen umgebene Mini-Notation benutzt man normalerweise in eine sogenannten Funktion.
Die folgenden Funktionen haben wir bereits gesehen:
| Name | Description | Example |
@@ -22,7 +22,7 @@ in der Muster eine Rolle spielen.
Du brauchst keine Erfahrung in JavaScript oder Tidal Cycles um mit Strudel Musik zu machen.
Dieser interaktive Workshop leitet dich spielerisch durch die Grundlagen von Strudel.
Der beste Ort um mit Strudel Musik zu machen ist das [Strudel REPL](https://strudel.cc/).
Der beste Ort um mit Strudel Musik zu machen ist das [Strudel REPL](https://strudel.tidalcycles.org/).
## Was kann man mit Strudel machen?
@@ -66,7 +66,7 @@ Hier ist ein Beispiel wie Strudel klingen kann:
Mehr Beispiele gibt es [hier](/examples).
Du kannst auch im [Strudel REPL](https://strudel.cc/) auf `shuffle` klicken um ein zufälliges Beispiel zu hören.
Du kannst auch im [Strudel REPL](https://strudel.tidalcycles.org/) auf `shuffle` klicken um ein zufälliges Beispiel zu hören.
## Workshop
@@ -1,5 +1,5 @@
---
title: Pattern-Effekte
title: Pattern Effekte
layout: ../../../layouts/MainLayout.astro
---
@@ -7,11 +7,11 @@ import { MiniRepl } from '@src/docs/MiniRepl';
import Box from '@components/Box.astro';
import QA from '@components/QA';
# Pattern-Effekte
# Pattern Effekte
Bis jetzt sind die meisten Funktionen, die wir kennengelernt haben, ähnlich wie Funktionen in anderen Musik Programmen: Sequencing von Sounds, Noten und Effekten.
Bis jetzt sind die meisten Funktionen die wir kennengelernt haben ähnlich wie Funktionen in anderen Musik Programmen: Sequencing von Sounds, Noten und Effekten.
In diesem Kapitel beschäftigen wir uns mit Funktionen die weniger herkömmlich oder auch einzigartig sind.
In diesem Kapitel beschäftigen wir uns mit Funktionen die weniger herkömmlich oder auch enzigartig sind.
**rev = rückwärts abspielen**
@@ -21,7 +21,7 @@ In diesem Kapitel beschäftigen wir uns mit Funktionen die weniger herkömmlich
<MiniRepl hideHeader client:visible tune={`n("0 1 [4 3] 2").sound("jazz").jux(rev)`} />
So würde man das ohne `jux` schreiben:
So würde man das ohne jux schreiben:
<MiniRepl
hideHeader
@@ -32,7 +32,7 @@ So würde man das ohne `jux` schreiben:
)`}
/>
Lass uns visualisieren, was hier passiert:
Lass uns visualisieren was hier passiert:
<MiniRepl
hideHeader
@@ -54,7 +54,7 @@ Schreibe `//` vor eine der beiden Zeilen im `stack`!
<MiniRepl hideHeader client:visible tune={`note("c2, eb3 g3 [bb3 c4]").sound("piano").slow("1,2,3")`} />
Das hat den gleichen Effekt, wie:
Das hat den gleichen Effekt wie:
<MiniRepl
hideHeader
@@ -161,11 +161,11 @@ Probier `ply` mit einem pattern zu automatisieren, z.b. `"<1 2 1 3>"`
<Box>
In der Notation `x=>x.`, ist `x` das Pattern, das wir bearbeiten.
In der notation `x=>x.`, das `x` ist das Pattern das wir bearbeiten.
</Box>
`off` ist auch nützlich für Sounds:
`off` ist auch nützlich für sounds:
<MiniRepl
hideHeader
@@ -174,10 +174,10 @@ In der Notation `x=>x.`, ist `x` das Pattern, das wir bearbeiten.
.off(1/8, x=>x.speed(1.5).gain(.25))`}
/>
| Name | Beschreibung | Beispiel |
| name | description | example |
| ---- | --------------------------------- | ---------------------------------------------------------------------------------------------- |
| rev | rückwärts | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6").scale("C:minor").rev()`} /> |
| jux | einen Stereo-Kanal modifizieren | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6").scale("C:minor").jux(rev)`} /> |
| add | addiert Zahlen oder Noten | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6".add("<0 1 2 1>")).scale("C:minor")`} /> |
| ply | multipliziert jedes Element x mal | <MiniRepl hideHeader client:visible tune={`s("bd sd").ply("<1 2 3>")`} /> |
| off | verzögert eine modifizierte Kopie | <MiniRepl hideHeader client:visible tune={`s("bd sd, hh*4").off(1/8, x=>x.speed(2))`} /> |
| jux | ein stereo-kanal modifizieren | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6").scale("C:minor").jux(rev)`} /> |
| add | addiert zahlen oder noten | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6".add("<0 1 2 1>")).scale("C:minor")`} /> |
| ply | multipliziert jedes element x mal | <MiniRepl hideHeader client:visible tune={`s("bd sd").ply("<1 2 3>")`} /> |
| off | verzögert eine modifizierte kopie | <MiniRepl hideHeader client:visible tune={`s("bd sd, hh*4").off(1/8, x=>x.speed(2))`} /> |
+25 -25
View File
@@ -7,19 +7,19 @@ import { MiniRepl } from '../../../docs/MiniRepl';
# Workshop Rückblick
Diese Seite ist eine Auflistung aller im Workshop vorgestellten Funktionen.
Diese Seite ist eine Auflistung aller im Workshop enthaltenen Funktionen.
## Mini Notation
| Konzept | Syntax | Beispiel |
| Concept | Syntax | Example |
| --------------------- | -------- | -------------------------------------------------------------------------------- |
| Sequenz | space | <MiniRepl hideHeader client:visible tune={`sound("bd bd sn hh")`} /> |
| Sample-Nummer | :x | <MiniRepl hideHeader client:visible tune={`sound("hh:0 hh:1 hh:2 hh:3")`} /> |
| Sequence | space | <MiniRepl hideHeader client:visible tune={`sound("bd bd sn hh")`} /> |
| Sample Nummer | :x | <MiniRepl hideHeader client:visible tune={`sound("hh:0 hh:1 hh:2 hh:3")`} /> |
| Pausen | ~ | <MiniRepl hideHeader client:visible tune={`sound("metal ~ jazz jazz:1")`} /> |
| Unter-Sequenzen | \[\] | <MiniRepl hideHeader client:visible tune={`sound("bd wind [metal jazz] hh")`} /> |
| Unter-Unter-Sequenzen | \[\[\]\] | <MiniRepl hideHeader client:visible tune={`sound("bd [metal [jazz sn]]")`} /> |
| Unter-Sequences | \[\] | <MiniRepl hideHeader client:visible tune={`sound("bd wind [metal jazz] hh")`} /> |
| Unter-Unter-Sequences | \[\[\]\] | <MiniRepl hideHeader client:visible tune={`sound("bd [metal [jazz sn]]")`} /> |
| Schneller | \* | <MiniRepl hideHeader client:visible tune={`sound("bd sn*2 cp*3")`} /> |
| Verlangsamen | \/ | <MiniRepl hideHeader client:visible tune={`note("[c a f e]/2")`} /> |
| Slow down | \/ | <MiniRepl hideHeader client:visible tune={`note("[c a f e]/2")`} /> |
| Parallel | , | <MiniRepl hideHeader client:visible tune={`sound("bd*2, hh*2 [hh oh]")`} /> |
| Alternieren | \<\> | <MiniRepl hideHeader client:visible tune={`note("c <e g>")`} /> |
| Verlängern | @ | <MiniRepl hideHeader client:visible tune={`note("c@3 e")`} /> |
@@ -27,23 +27,23 @@ Diese Seite ist eine Auflistung aller im Workshop vorgestellten Funktionen.
## Sounds
| Name | Beschreibung | Beispiel |
| Name | Description | Example |
| ----- | -------------------------- | ---------------------------------------------------------------------------------- |
| sound | spielt den Sound mit Namen | <MiniRepl hideHeader client:visible tune={`sound("bd sd")`} /> |
| bank | wählt die Soundbank | <MiniRepl hideHeader client:visible tune={`sound("bd sd").bank("RolandTR909")`} /> |
| n | wählt Sample mit Nummer | <MiniRepl hideHeader client:visible tune={`n("0 1 4 2").sound("jazz")`} /> |
| sound | spielt den sound mit namen | <MiniRepl hideHeader client:visible tune={`sound("bd sd")`} /> |
| bank | wählt die soundbank | <MiniRepl hideHeader client:visible tune={`sound("bd sd").bank("RolandTR909")`} /> |
| n | wählt sample mit nummer | <MiniRepl hideHeader client:visible tune={`n("0 1 4 2").sound("jazz")`} /> |
## Noten
## Notes
| Name | Beschreibung | Beispiel |
| Name | Description | Example |
| --------- | ---------------------------------- | -------------------------------------------------------------------------------------------- |
| note | wählt Note per Zahl oder Buchstabe | <MiniRepl hideHeader client:visible tune={`note("b g e c").sound("piano")`} /> |
| n + scale | wählt Note n in Skala | <MiniRepl hideHeader client:visible tune={`n("6 4 2 0").scale("C:minor").sound("piano")`} /> |
| stack | spielt mehrere Patterns parallel | <MiniRepl hideHeader client:visible tune={`stack(s("bd sd"),note("c eb g"))`} /> |
| note | wählt note per zahl oder buchstabe | <MiniRepl hideHeader client:visible tune={`note("b g e c").sound("piano")`} /> |
| n + scale | wählt note n in skala | <MiniRepl hideHeader client:visible tune={`n("6 4 2 0").scale("C:minor").sound("piano")`} /> |
| stack | spielt mehrere patterns parallel | <MiniRepl hideHeader client:visible tune={`stack(s("bd sd"),note("c eb g"))`} /> |
## Audio-Effekte
## Audio Effekte
| Name | Beispiele |
| name | example |
| ----- | -------------------------------------------------------------------------------------------------- |
| lpf | <MiniRepl hideHeader client:visible tune={`note("c2 c3").s("sawtooth").lpf("<400 2000>")`} /> |
| vowel | <MiniRepl hideHeader client:visible tune={`note("c3 eb3 g3").s("sawtooth").vowel("<a e i o>")`} /> |
@@ -54,15 +54,15 @@ Diese Seite ist eine Auflistung aller im Workshop vorgestellten Funktionen.
| speed | <MiniRepl hideHeader client:visible tune={`s("bd rim").speed("<1 2 -1 -2>")`} /> |
| range | <MiniRepl hideHeader client:visible tune={`s("hh*16").lpf(saw.range(200,4000))`} /> |
## Pattern-Effekte
## Pattern Effects
| Name | Beschreibung | Beispiel |
| name | description | example |
| ---- | --------------------------------- | ---------------------------------------------------------------------------------------------- |
| cpm | Tempo in Cycles pro Minute | <MiniRepl hideHeader client:visible tune={`sound("bd sd").cpm(90)`} /> |
| cpm | tempo in cycles pro minute | <MiniRepl hideHeader client:visible tune={`sound("bd sd").cpm(90)`} /> |
| fast | schneller | <MiniRepl hideHeader client:visible tune={`sound("bd sd").fast(2)`} /> |
| slow | langsamer | <MiniRepl hideHeader client:visible tune={`sound("bd sd").slow(2)`} /> |
| rev | rückwärts | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6").scale("C:minor").rev()`} /> |
| jux | einen Stereo-Kanal modifizieren | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6").scale("C:minor").jux(rev)`} /> |
| add | addiert Zahlen oder Noten | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6".add("<0 1 2 1>")).scale("C:minor")`} /> |
| ply | jedes Element schneller machen | <MiniRepl hideHeader client:visible tune={`s("bd sd").ply("<1 2 3>")`} /> |
| off | verzögert eine modifizierte Kopie | <MiniRepl hideHeader client:visible tune={`s("bd sd, hh*4").off(1/8, x=>x.speed(2))`} /> |
| jux | ein stereo-kanal modifizieren | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6").scale("C:minor").jux(rev)`} /> |
| add | addiert zahlen oder noten | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6".add("<0 1 2 1>")).scale("C:minor")`} /> |
| ply | jedes element schneller machen | <MiniRepl hideHeader client:visible tune={`s("bd sd").ply("<1 2 3>")`} /> |
| off | verzögert eine modifizierte kopie | <MiniRepl hideHeader client:visible tune={`s("bd sd, hh*4").off(1/8, x=>x.speed(2))`} /> |
@@ -60,12 +60,4 @@ import { JsDoc } from '../../docs/JsDoc';
<JsDoc client:idle name="invert" h={0} />
## pick
<JsDoc client:idle name="pick" h={0} />
## squeeze
<JsDoc client:idle name="squeeze" h={0} />
After Conditional Modifiers, let's see what [Accumulation Modifiers](/learn/accumulation) have to offer.

Some files were not shown because too many files have changed in this diff Show More