Merge branch 'main' into pattern_selection

This commit is contained in:
Jade (Rose) Rowland 2024-01-19 00:23:06 -05:00 committed by GitHub
commit 500ddf3946
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
111 changed files with 1165 additions and 538 deletions

View file

@ -114,7 +114,7 @@ You can run the same check with `pnpm check`
## Package Workflow ## Package Workflow
The project is split into multiple [packages](https://github.com/tidalcycles/strudel/tree/main/packages) with independent versioning. The project is split into multiple [packages](https://github.com/tidalcycles/strudel/tree/main/packages) with independent versioning.
When you run `pnpm i` on the root folder, [pnpm workspaces](https://pnpm.io/workspaces) will install all dependencies of all subpackages. This will allow any js file to import `@strudel.cycles/<package-name>` to get the local version, When you run `pnpm i` on the root folder, [pnpm workspaces](https://pnpm.io/workspaces) will install all dependencies of all subpackages. This will allow any js file to import `@strudel/<package-name>` to get the local version,
allowing to develop multiple packages at the same time. allowing to develop multiple packages at the same time.
## Package Publishing ## Package Publishing

View file

@ -7,7 +7,7 @@
/> />
<div id="output"></div> <div id="output"></div>
<script type="module"> <script type="module">
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8'); const strudel = await import('https://cdn.skypack.dev/@strudel/core@0.6.8');
Object.assign(window, strudel); // assign all strudel functions to global scope to use with eval Object.assign(window, strudel); // assign all strudel functions to global scope to use with eval
const input = document.getElementById('text'); const input = document.getElementById('text');
const getEvents = () => { const getEvents = () => {

View file

@ -8,7 +8,7 @@
/> />
<canvas id="canvas"></canvas> <canvas id="canvas"></canvas>
<script type="module"> <script type="module">
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8'); const strudel = await import('https://cdn.skypack.dev/@strudel/core@0.6.8');
// this adds all strudel functions to the global scope, to be used by eval // this adds all strudel functions to the global scope, to be used by eval
Object.assign(window, strudel); Object.assign(window, strudel);
// setup elements // setup elements

View file

@ -16,34 +16,40 @@
</div> </div>
<div id="output"></div> <div id="output"></div>
<script type="module"> <script type="module">
import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel.cycles/core@0.6.8'; import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel/core@0.11.0';
import { mini } from 'https://cdn.skypack.dev/@strudel.cycles/mini@0.6.0'; import { mini } from 'https://cdn.skypack.dev/@strudel/mini@0.11.0';
import { transpiler } from 'https://cdn.skypack.dev/@strudel.cycles/transpiler@0.6.0'; import { transpiler } from 'https://cdn.skypack.dev/@strudel/transpiler@0.11.0';
import { import {
getAudioContext, getAudioContext,
webaudioOutput, webaudioOutput,
initAudioOnFirstClick, initAudioOnFirstClick,
} from 'https://cdn.skypack.dev/@strudel.cycles/webaudio@0.6.0'; registerSynthSounds,
} from 'https://cdn.skypack.dev/@strudel/webaudio@0.11.0';
initAudioOnFirstClick();
const ctx = getAudioContext(); const ctx = getAudioContext();
const input = document.getElementById('text'); const input = document.getElementById('text');
input.innerHTML = getTune(); input.innerHTML = getTune();
evalScope( const loadModules = evalScope(
controls, controls,
import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8'), import('https://cdn.skypack.dev/@strudel/core@0.11.0'),
import('https://cdn.skypack.dev/@strudel.cycles/mini@0.6.0'), import('https://cdn.skypack.dev/@strudel/mini@0.11.0'),
import('https://cdn.skypack.dev/@strudel.cycles/tonal@0.6.0'), import('https://cdn.skypack.dev/@strudel/tonal@0.11.0'),
import('https://cdn.skypack.dev/@strudel.cycles/webaudio@0.6.0'), import('https://cdn.skypack.dev/@strudel/webaudio@0.11.0'),
); );
const initAudio = Promise.all([initAudioOnFirstClick(), registerSynthSounds()]);
const { evaluate } = repl({ const { evaluate } = repl({
defaultOutput: webaudioOutput, defaultOutput: webaudioOutput,
getTime: () => ctx.currentTime, getTime: () => ctx.currentTime,
transpiler, transpiler,
}); });
document.getElementById('start').addEventListener('click', () => evaluate(input.value)); document.getElementById('start').addEventListener('click', async () => {
await loadModules;
await initAudio;
evaluate(input.value);
});
function getTune() { function getTune() {
return `await samples('github:tidalcycles/Dirt-Samples/master') return `await samples('github:tidalcycles/Dirt-Samples/master')

View file

@ -1,4 +1,4 @@
<script src="https://unpkg.com/@strudel.cycles/embed@latest"></script> <script src="https://unpkg.com/@strudel/embed@0.11.0"></script>
<!-- <script src="./embed.js"></script> --> <!-- <script src="./embed.js"></script> -->
<strudel-repl> <strudel-repl>
<!-- <!--

View file

@ -1,11 +1,11 @@
import { StrudelMirror } from '@strudel/codemirror'; import { StrudelMirror } from '@strudel/codemirror';
import { funk42 } from './tunes'; import { funk42 } from './tunes';
import { drawPianoroll, evalScope, controls } from '@strudel.cycles/core'; import { drawPianoroll, evalScope, controls } from '@strudel/core';
import './style.css'; import './style.css';
import { initAudioOnFirstClick } from '@strudel.cycles/webaudio'; import { initAudioOnFirstClick } from '@strudel/webaudio';
import { transpiler } from '@strudel.cycles/transpiler'; import { transpiler } from '@strudel/transpiler';
import { getAudioContext, webaudioOutput, registerSynthSounds } from '@strudel.cycles/webaudio'; import { getAudioContext, webaudioOutput, registerSynthSounds } from '@strudel/webaudio';
import { registerSoundfonts } from '@strudel.cycles/soundfonts'; import { registerSoundfonts } from '@strudel/soundfonts';
// init canvas // init canvas
const canvas = document.getElementById('roll'); const canvas = document.getElementById('roll');
@ -26,10 +26,10 @@ const editor = new StrudelMirror({
initAudioOnFirstClick(); // needed to make the browser happy (don't await this here..) initAudioOnFirstClick(); // needed to make the browser happy (don't await this here..)
const loadModules = evalScope( const loadModules = evalScope(
controls, controls,
import('@strudel.cycles/core'), import('@strudel/core'),
import('@strudel.cycles/mini'), import('@strudel/mini'),
import('@strudel.cycles/tonal'), import('@strudel/tonal'),
import('@strudel.cycles/webaudio'), import('@strudel/webaudio'),
); );
await Promise.all([loadModules, registerSynthSounds(), registerSoundfonts()]); await Promise.all([loadModules, registerSynthSounds(), registerSoundfonts()]);
}, },

View file

@ -13,11 +13,11 @@
}, },
"dependencies": { "dependencies": {
"@strudel/codemirror": "workspace:*", "@strudel/codemirror": "workspace:*",
"@strudel.cycles/core": "workspace:*", "@strudel/core": "workspace:*",
"@strudel.cycles/mini": "workspace:*", "@strudel/mini": "workspace:*",
"@strudel.cycles/soundfonts": "workspace:*", "@strudel/soundfonts": "workspace:*",
"@strudel.cycles/tonal": "workspace:*", "@strudel/tonal": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*", "@strudel/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*" "@strudel/webaudio": "workspace:*"
} }
} }

View file

@ -1,6 +1,6 @@
import { controls, repl, evalScope } from '@strudel.cycles/core'; import { controls, repl, evalScope } from '@strudel/core';
import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel.cycles/webaudio'; import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel/webaudio';
import { transpiler } from '@strudel.cycles/transpiler'; import { transpiler } from '@strudel/transpiler';
import tune from './tune.mjs'; import tune from './tune.mjs';
const ctx = getAudioContext(); const ctx = getAudioContext();
@ -10,10 +10,10 @@ initAudioOnFirstClick();
evalScope( evalScope(
controls, controls,
import('@strudel.cycles/core'), import('@strudel/core'),
import('@strudel.cycles/mini'), import('@strudel/mini'),
import('@strudel.cycles/webaudio'), import('@strudel/webaudio'),
import('@strudel.cycles/tonal'), import('@strudel/tonal'),
); );
const { evaluate } = repl({ const { evaluate } = repl({

View file

@ -13,10 +13,10 @@
"vite": "^5.0.10" "vite": "^5.0.10"
}, },
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*", "@strudel/core": "workspace:*",
"@strudel.cycles/mini": "workspace:*", "@strudel/mini": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*", "@strudel/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*", "@strudel/webaudio": "workspace:*",
"@strudel.cycles/tonal": "workspace:*" "@strudel/tonal": "workspace:*"
} }
} }

View file

@ -1,5 +1,5 @@
{ {
"name": "@strudel.cycles/monorepo", "name": "@strudel/monorepo",
"version": "0.5.0", "version": "0.5.0",
"private": true, "private": true,
"description": "Port of tidalcycles to javascript", "description": "Port of tidalcycles to javascript",
@ -45,12 +45,12 @@
}, },
"homepage": "https://strudel.cc", "homepage": "https://strudel.cc",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*", "@strudel/core": "workspace:*",
"@strudel.cycles/mini": "workspace:*", "@strudel/mini": "workspace:*",
"@strudel.cycles/tonal": "workspace:*", "@strudel/tonal": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*", "@strudel/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*", "@strudel/webaudio": "workspace:*",
"@strudel.cycles/xen": "workspace:*" "@strudel/xen": "workspace:*"
}, },
"devDependencies": { "devDependencies": {
"dependency-tree": "^10.0.9", "dependency-tree": "^10.0.9",

View file

@ -1,5 +1,5 @@
# Packages # Packages
Each folder represents one of the @strudel.cycles/* packages [published to npm](https://www.npmjs.com/org/strudel.cycles). Each folder represents one of the @strudel/* packages [published to npm](https://www.npmjs.com/org/strudel).
To understand how those pieces connect, refer to the [Technical Manual](https://github.com/tidalcycles/strudel/wiki/Technical-Manual) or the individual READMEs. To understand how those pieces connect, refer to the [Technical Manual](https://github.com/tidalcycles/strudel/wiki/Technical-Manual) or the individual READMEs.

View file

@ -12,7 +12,7 @@ import {
lineNumbers, lineNumbers,
drawSelection, drawSelection,
} from '@codemirror/view'; } from '@codemirror/view';
import { Pattern, Drawer, repl, cleanupDraw } from '@strudel.cycles/core'; import { Pattern, Drawer, repl, cleanupDraw } from '@strudel/core';
import { isAutoCompletionEnabled } from './autocomplete.mjs'; import { isAutoCompletionEnabled } from './autocomplete.mjs';
import { isTooltipEnabled } from './tooltip.mjs'; import { isTooltipEnabled } from './tooltip.mjs';
import { flash, isFlashEnabled } from './flash.mjs'; import { flash, isFlashEnabled } from './flash.mjs';

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel/codemirror", "name": "@strudel/codemirror",
"version": "0.9.0", "version": "0.11.0",
"description": "Codemirror Extensions for Strudel", "description": "Codemirror Extensions for Strudel",
"main": "index.mjs", "main": "index.mjs",
"publishConfig": { "publishConfig": {
@ -41,14 +41,14 @@
"@codemirror/state": "^6.4.0", "@codemirror/state": "^6.4.0",
"@codemirror/view": "^6.23.0", "@codemirror/view": "^6.23.0",
"@lezer/highlight": "^1.2.0", "@lezer/highlight": "^1.2.0",
"@nanostores/persistent": "^0.9.1",
"@replit/codemirror-emacs": "^6.0.1", "@replit/codemirror-emacs": "^6.0.1",
"@replit/codemirror-vim": "^6.1.0", "@replit/codemirror-vim": "^6.1.0",
"@replit/codemirror-vscode-keymap": "^6.0.2", "@replit/codemirror-vscode-keymap": "^6.0.2",
"@strudel.cycles/core": "workspace:*", "@strudel/core": "workspace:*",
"@uiw/codemirror-themes": "^4.21.21", "@uiw/codemirror-themes": "^4.21.21",
"@uiw/codemirror-themes-all": "^4.21.21", "@uiw/codemirror-themes-all": "^4.21.21",
"nanostores": "^0.9.5", "nanostores": "^0.9.5"
"@nanostores/persistent": "^0.9.1"
}, },
"devDependencies": { "devDependencies": {
"vite": "^5.0.10" "vite": "^5.0.10"

View file

@ -1,4 +1,4 @@
import { ref, pure } from '@strudel.cycles/core'; import { ref, pure } from '@strudel/core';
import { WidgetType, ViewPlugin, Decoration } from '@codemirror/view'; import { WidgetType, ViewPlugin, Decoration } from '@codemirror/view';
import { StateEffect, StateField } from '@codemirror/state'; import { StateEffect, StateField } from '@codemirror/state';

View file

@ -1,17 +1,17 @@
# @strudel.cycles/core # @strudel/core
This package contains the bare essence of strudel. This package contains the bare essence of strudel.
## Install ## Install
```sh ```sh
npm i @strudel.cycles/core --save npm i @strudel/core --save
``` ```
## Example ## Example
```js ```js
import { sequence } from '@strudel.cycles/core'; import { sequence } from '@strudel/core';
const pattern = sequence('a', ['b', 'c']); const pattern = sequence('a', ['b', 'c']);
@ -33,7 +33,7 @@ b: 3/2 - 7/4
c: 7/4 - 2 c: 7/4 - 2
``` ```
- [play with @strudel.cycles/core on codesandbox](https://codesandbox.io/s/strudel-core-test-forked-9ywhv7?file=/src/index.js). - [play with @strudel/core on codesandbox](https://codesandbox.io/s/strudel-core-test-forked-9ywhv7?file=/src/index.js).
- [open color pattern example](https://raw.githack.com/tidalcycles/strudel/main/packages/core/examples/canvas.html) - [open color pattern example](https://raw.githack.com/tidalcycles/strudel/main/packages/core/examples/canvas.html)
- [open minimal repl example](https://raw.githack.com/tidalcycles/strudel/main/packages/core/examples/vanilla.html) - [open minimal repl example](https://raw.githack.com/tidalcycles/strudel/main/packages/core/examples/vanilla.html)
- [open minimal vite example](./examples/vite-vanilla-repl/) - [open minimal vite example](./examples/vite-vanilla-repl/)

View file

@ -97,7 +97,7 @@ const generic_params = [
*/ */
['postgain'], ['postgain'],
/** /**
* Like {@link gain}, but linear. * Like `gain`, but linear.
* *
* @name amp * @name amp
* @param {number | Pattern} amount gain. * @param {number | Pattern} amount gain.
@ -856,7 +856,7 @@ const generic_params = [
*/ */
['detune', 'det'], ['detune', 'det'],
/** /**
* Set dryness of reverb. See {@link room} and {@link size} for more information about reverb. * Set dryness of reverb. See `room` and `size` for more information about reverb.
* *
* @name dry * @name dry
* @param {number | Pattern} dry 0 = wet, 1 = dry * @param {number | Pattern} dry 0 = wet, 1 = dry
@ -868,7 +868,7 @@ const generic_params = [
['dry'], ['dry'],
// TODO: does not seem to do anything // TODO: does not seem to do anything
/* /*
* Used when using {@link begin}/{@link end} or {@link chop}/{@link striate} and friends, to change the fade out time of the 'grain' envelope. * Used when using `begin`/`end` or `chop`/`striate` and friends, to change the fade out time of the 'grain' envelope.
* *
* @name fadeTime * @name fadeTime
* @param {number | Pattern} time between 0 and 1 * @param {number | Pattern} time between 0 and 1
@ -891,6 +891,82 @@ const generic_params = [
* *
*/ */
['freq'], ['freq'],
// pitch envelope
/**
* Attack time of pitch envelope.
*
* @name pattack
* @synonyms patt
* @param {number | Pattern} time time in seconds
* @example
* note("<c eb g bb>").pattack("<0 .1 .25 .5>")
*
*/
['pattack', 'patt'],
/**
* Decay time of pitch envelope.
*
* @name pdecay
* @synonyms pdec
* @param {number | Pattern} time time in seconds
* @example
* note("<c eb g bb>").pdecay("<0 .1 .25 .5>")
*
*/
['pdecay', 'pdec'],
// TODO: how to use psustain?!
['psustain', 'psus'],
/**
* Release time of pitch envelope
*
* @name prelease
* @synonyms prel
* @param {number | Pattern} time time in seconds
* @example
* note("<c eb g bb> ~")
* .release(.5) // to hear the pitch release
* .prelease("<0 .1 .25 .5>")
*
*/
['prelease', 'prel'],
/**
* Amount of pitch envelope. Negative values will flip the envelope.
* If you don't set other pitch envelope controls, `pattack:.2` will be the default.
*
* @name penv
* @param {number | Pattern} semitones change in semitones
* @example
* note("c")
* .penv("<12 7 1 .5 0 -1 -7 -12>")
*
*/
['penv'],
/**
* Curve of envelope. Defaults to linear. exponential is good for kicks
*
* @name pcurve
* @param {number | Pattern} type 0 = linear, 1 = exponential
* @example
* note("g1*2")
* .s("sine").pdec(.5)
* .penv(32)
* .pcurve("<0 1>")
*
*/
['pcurve'],
/**
* Sets the range anchor of the envelope:
* - anchor 0: range = [note, note + penv]
* - anchor 1: range = [note - penv, note]
* If you don't set an anchor, the value will default to the psustain value.
*
* @name panchor
* @param {number | Pattern} anchor anchor offset
* @example
* note("c").penv(12).panchor("<0 .5 1 .5>")
*
*/
['panchor'],
// TODO: https://tidalcycles.org/docs/configuration/MIDIOSC/control-voltage/#gate // TODO: https://tidalcycles.org/docs/configuration/MIDIOSC/control-voltage/#gate
['gate', 'gat'], ['gate', 'gat'],
// ['hatgrain'], // ['hatgrain'],
@ -1115,7 +1191,7 @@ const generic_params = [
*/ */
[['ir', 'i'], 'iresponse'], [['ir', 'i'], 'iresponse'],
/** /**
* Sets the room size of the reverb, see {@link room}. * Sets the room size of the reverb, see `room`.
* When this property is changed, the reverb will be recaculated, so only change this sparsely.. * When this property is changed, the reverb will be recaculated, so only change this sparsely..
* *
* @name roomsize * @name roomsize
@ -1173,7 +1249,7 @@ const generic_params = [
*/ */
['speed'], ['speed'],
/** /**
* Used in conjunction with {@link speed}, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`. * Used in conjunction with `speed`, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`.
* *
* @name unit * @name unit
* @param {number | string | Pattern} unit see description above * @param {number | string | Pattern} unit see description above
@ -1209,7 +1285,7 @@ const generic_params = [
* Formant filter to make things sound like vowels. * Formant filter to make things sound like vowels.
* *
* @name vowel * @name vowel
* @param {string | Pattern} vowel You can use a e i o u. * @param {string | Pattern} vowel You can use a e i o u ae aa oe ue y uh un en an on, corresponding to [a] [e] [i] [o] [u] [æ] [ɑ] [ø] [y] [ɯ] [ʌ] [œ̃] [ɛ̃] [ɑ̃] [ɔ̃]. Aliases: aa = å = ɑ, oe = ø = ö, y = ı, ae = æ.
* @example * @example
* note("c2 <eb2 <g2 g1>>").s('sawtooth') * note("c2 <eb2 <g2 g1>>").s('sawtooth')
* .vowel("<a e i <o u>>") * .vowel("<a e i <o u>>")

View file

@ -30,12 +30,12 @@ export { default as drawLine } from './drawLine.mjs';
// below won't work with runtime.mjs (json import fails) // below won't work with runtime.mjs (json import fails)
/* import * as p from './package.json'; /* import * as p from './package.json';
export const version = p.version; */ export const version = p.version; */
logger('🌀 @strudel.cycles/core loaded 🌀'); logger('🌀 @strudel/core loaded 🌀');
if (globalThis._strudelLoaded) { if (globalThis._strudelLoaded) {
console.warn( console.warn(
`@strudel.cycles/core was loaded more than once... `@strudel/core was loaded more than once...
This might happen when you have multiple versions of strudel installed. This might happen when you have multiple versions of strudel installed.
Please check with "npm ls @strudel.cycles/core".`, Please check with "npm ls @strudel/core".`,
); );
} }
globalThis._strudelLoaded = true; globalThis._strudelLoaded = true;

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/core", "name": "@strudel/core",
"version": "0.9.0", "version": "0.11.0",
"description": "Port of Tidal Cycles to JavaScript", "description": "Port of Tidal Cycles to JavaScript",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",

View file

@ -26,7 +26,7 @@ export class Pattern {
/** /**
* Create a pattern. As an end user, you will most likely not create a Pattern directly. * Create a pattern. As an end user, you will most likely not create a Pattern directly.
* *
* @param {function} query - The function that maps a {@link State} to an array of {@link Hap}. * @param {function} query - The function that maps a `State` to an array of `Hap`.
* @noAutocomplete * @noAutocomplete
*/ */
constructor(query) { constructor(query) {
@ -39,7 +39,7 @@ export class Pattern {
/** /**
* Returns a new pattern, with the function applied to the value of * Returns a new pattern, with the function applied to the value of
* each hap. It has the alias {@link Pattern#fmap}. * each hap. It has the alias `fmap`.
* @synonyms fmap * @synonyms fmap
* @param {Function} func to to apply to the value * @param {Function} func to to apply to the value
* @returns Pattern * @returns Pattern
@ -51,7 +51,7 @@ export class Pattern {
} }
/** /**
* see {@link Pattern#withValue} * see `withValue`
* @noAutocomplete * @noAutocomplete
*/ */
fmap(func) { fmap(func) {
@ -115,7 +115,7 @@ export class Pattern {
} }
/** /**
* As with {@link Pattern#appBoth}, but the `whole` timespan is not the intersection, * As with `appBoth`, but the `whole` timespan is not the intersection,
* but the timespan from the function of patterns that this method is called * but the timespan from the function of patterns that this method is called
* on. In practice, this means that the pattern structure, including onsets, * on. In practice, this means that the pattern structure, including onsets,
* are preserved from the pattern of functions (often referred to as the left * are preserved from the pattern of functions (often referred to as the left
@ -148,7 +148,7 @@ export class Pattern {
} }
/** /**
* As with {@link Pattern#appLeft}, but `whole` timespans are instead taken from the * As with `appLeft`, but `whole` timespans are instead taken from the
* pattern of values, i.e. structure is preserved from the right hand/outer * pattern of values, i.e. structure is preserved from the right hand/outer
* pattern. * pattern.
* @param {Pattern} pat_val * @param {Pattern} pat_val
@ -387,7 +387,7 @@ export class Pattern {
} }
/** /**
* As with {@link Pattern#withQuerySpan}, but the function is applied to both the * As with `withQuerySpan`, but the function is applied to both the
* begin and end time of the query timespan. * begin and end time of the query timespan.
* @param {Function} func the function to apply * @param {Function} func the function to apply
* @returns Pattern * @returns Pattern
@ -398,7 +398,7 @@ export class Pattern {
} }
/** /**
* Similar to {@link Pattern#withQuerySpan}, but the function is applied to the timespans * Similar to `withQuerySpan`, but the function is applied to the timespans
* of all haps returned by pattern queries (both `part` timespans, and where * of all haps returned by pattern queries (both `part` timespans, and where
* present, `whole` timespans). * present, `whole` timespans).
* @param {Function} func * @param {Function} func
@ -410,7 +410,7 @@ export class Pattern {
} }
/** /**
* As with {@link Pattern#withHapSpan}, but the function is applied to both the * As with `withHapSpan`, but the function is applied to both the
* begin and end time of the hap timespans. * begin and end time of the hap timespans.
* @param {Function} func the function to apply * @param {Function} func the function to apply
* @returns Pattern * @returns Pattern
@ -431,7 +431,7 @@ export class Pattern {
} }
/** /**
* As with {@link Pattern#withHaps}, but applies the function to every hap, rather than every list of haps. * As with `withHaps`, but applies the function to every hap, rather than every list of haps.
* @param {Function} func * @param {Function} func
* @returns Pattern * @returns Pattern
* @noAutocomplete * @noAutocomplete
@ -499,7 +499,7 @@ export class Pattern {
} }
/** /**
* As with {@link Pattern#filterHaps}, but the function is applied to values * As with `filterHaps`, but the function is applied to values
* inside haps. * inside haps.
* @param {Function} value_test * @param {Function} value_test
* @returns Pattern * @returns Pattern
@ -621,7 +621,7 @@ export class Pattern {
} }
/** /**
* More human-readable version of the {@link Pattern#firstCycleValues} accessor. * More human-readable version of the `firstCycleValues` accessor.
* @noAutocomplete * @noAutocomplete
*/ */
get showFirstCycle() { get showFirstCycle() {
@ -691,7 +691,7 @@ export class Pattern {
// Methods without corresponding toplevel functions // Methods without corresponding toplevel functions
/** /**
* Layers the result of the given function(s). Like {@link Pattern.superimpose}, but without the original pattern: * Layers the result of the given function(s). Like `superimpose`, but without the original pattern:
* @name layer * @name layer
* @memberof Pattern * @memberof Pattern
* @synonyms apply * @synonyms apply
@ -1154,8 +1154,8 @@ export function isPattern(thing) {
/* if (!thing instanceof Pattern) { /* if (!thing instanceof Pattern) {
console.warn( console.warn(
`Found Pattern that fails "instanceof Pattern" check. `Found Pattern that fails "instanceof Pattern" check.
This may happen if you are using multiple versions of @strudel.cycles/core. This may happen if you are using multiple versions of @strudel/core.
Please check by running "npm ls @strudel.cycles/core".`, Please check by running "npm ls @strudel/core".`,
); );
console.log(thing); console.log(thing);
} */ } */
@ -1189,7 +1189,7 @@ export function stack(...pats) {
/** Concatenation: combines a list of patterns, switching between them successively, one per cycle: /** Concatenation: combines a list of patterns, switching between them successively, one per cycle:
* *
* synonyms: {@link cat} * synonyms: `cat`
* *
* @return {Pattern} * @return {Pattern}
* @example * @example
@ -1244,7 +1244,7 @@ export function cat(...pats) {
return slowcat(...pats); return slowcat(...pats);
} }
/** Like {@link Pattern.seq}, but each step has a length, relative to the whole. /** Like `seq`, but each step has a length, relative to the whole.
* @return {Pattern} * @return {Pattern}
* @example * @example
* timeCat([3,"e3"],[1, "g3"]).note() // "e3@3 g3".note() * timeCat([3,"e3"],[1, "g3"]).note() // "e3@3 g3".note()
@ -1279,7 +1279,7 @@ export function fastcat(...pats) {
return slowcat(...pats)._fast(pats.length); return slowcat(...pats)._fast(pats.length);
} }
/** See {@link fastcat} */ /** See `fastcat` */
export function sequence(...pats) { export function sequence(...pats) {
return fastcat(...pats); return fastcat(...pats);
} }
@ -1636,7 +1636,7 @@ export const { fastGap, fastgap } = register(['fastGap', 'fastgap'], function (f
}); });
/** /**
* Similar to compress, but doesn't leave gaps, and the 'focus' can be bigger than a cycle * Similar to `compress`, but doesn't leave gaps, and the 'focus' can be bigger than a cycle
* @example * @example
* s("bd hh sd hh").focus(1/4, 3/4) * s("bd hh sd hh").focus(1/4, 3/4)
*/ */
@ -1753,7 +1753,7 @@ export const lastOf = register('lastOf', function (n, func, pat) {
*/ */
/** /**
* An alias for {@link firstOf} * An alias for `firstOf`
* @name every * @name every
* @memberof Pattern * @memberof Pattern
* @param {number} n how many cycles * @param {number} n how many cycles
@ -2365,7 +2365,7 @@ export const { loopAt, loopat } = register(['loopAt', 'loopat'], function (facto
// It is still here to work in cases where repl.mjs is not used // It is still here to work in cases where repl.mjs is not used
/** /**
* Makes the sample fit its event duration. Good for rhythmical loops like drum breaks. * Makes the sample fit its event duration. Good for rhythmical loops like drum breaks.
* Similar to loopAt. * Similar to `loopAt`.
* @name fit * @name fit
* @example * @example
* samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' }) * samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' })

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 { Hap } from './hap.mjs';
import { Pattern, fastcat, reify, silence, stack, register } from './pattern.mjs'; import { Pattern, fastcat, reify, silence, stack, register } from './pattern.mjs';
import Fraction from './fraction.mjs'; import Fraction from './fraction.mjs';
import { id, _mod, clamp } from './util.mjs'; import { id, _mod, clamp, objectMap } from './util.mjs';
export function steady(value) { export function steady(value) {
// A continuous value // A continuous value
@ -156,31 +156,96 @@ export const _irand = (i) => rand.fmap((x) => Math.trunc(x * i));
*/ */
export const irand = (ipat) => reify(ipat).fmap(_irand).innerJoin(); export const irand = (ipat) => reify(ipat).fmap(_irand).innerJoin();
/** const _pick = function (lookup, pat, modulo = true) {
* pick from the list of values (or patterns of values) via the index using the given const array = Array.isArray(lookup);
* pattern of integers const len = Object.keys(lookup).length;
lookup = objectMap(lookup, reify);
if (len === 0) {
return silence;
}
return pat.fmap((i) => {
let key = i;
if (array) {
key = modulo ? Math.round(key) % len : clamp(Math.round(key), 0, lookup.length - 1);
}
return lookup[key];
});
};
/** * Picks patterns (or plain values) either from a list (by index) or a lookup table (by name).
* Similar to `inhabit`, but maintains the structure of the original patterns.
* @param {Pattern} pat * @param {Pattern} pat
* @param {*} xs * @param {*} xs
* @returns {Pattern} * @returns {Pattern}
* @example * @example
* note(pick("<0 1 [2!2] 3>", ["g a", "e f", "f g f g" , "g a c d"])) * note("<0 1 2!2 3>".pick(["g a", "e f", "f g f g" , "g c d"]))
* @example
* sound("<0 1 [2,0]>".pick(["bd sd", "cp cp", "hh hh"]))
* @example
* sound("<0!2 [0,1] 1>".pick(["bd(3,8)", "sd sd"]))
* @example
* s("<a!2 [a,b] b>".pick({a: "bd(3,8)", b: "sd sd"}))
*/ */
export const pick = (pat, xs) => { export const pick = function (lookup, pat) {
xs = xs.map(reify); // backward compatibility - the args used to be flipped
if (xs.length == 0) { if (Array.isArray(pat)) {
return silence; [pat, lookup] = [lookup, pat];
} }
return pat return __pick(lookup, pat);
.fmap((i) => {
const key = clamp(Math.round(i), 0, xs.length - 1);
return xs[key];
})
.innerJoin();
}; };
const __pick = register('pick', function (lookup, pat) {
return _pick(lookup, pat, false).innerJoin();
});
/** * The same as `pick`, but if you pick a number greater than the size of the list,
* it wraps around, rather than sticking at the maximum value.
* For example, if you pick the fifth pattern of a list of three, you'll get the
* second one.
* @param {Pattern} pat
* @param {*} xs
* @returns {Pattern}
*/
export const pickmod = register('pickmod', function (lookup, pat) {
return _pick(lookup, pat, true).innerJoin();
});
/** /**
* pick from the list of values (or patterns of values) via the index using the given /** * Picks patterns (or plain values) either from a list (by index) or a lookup table (by name).
* Similar to `pick`, but cycles are squeezed into the target ('inhabited') pattern.
* @param {Pattern} pat
* @param {*} xs
* @returns {Pattern}
* @example
* "<a b [a,b]>".inhabit({a: s("bd(3,8)"),
b: s("cp sd")
})
* @example
* s("a@2 [a b] a".inhabit({a: "bd(3,8)", b: "sd sd"})).slow(4)
*/
export const inhabit = register('inhabit', function (lookup, pat) {
return _pick(lookup, pat, true).squeezeJoin();
});
/** * The same as `inhabit`, but if you pick a number greater than the size of the list,
* it wraps around, rather than sticking at the maximum value.
* For example, if you pick the fifth pattern of a list of three, you'll get the
* second one.
* @param {Pattern} pat
* @param {*} xs
* @returns {Pattern}
*/
export const inhabitmod = register('inhabit', function (lookup, pat) {
return _pick(lookup, pat, false).squeezeJoin();
});
/**
* Pick from the list of values (or patterns of values) via the index using the given
* pattern of integers. The selected pattern will be compressed to fit the duration of the selecting event * pattern of integers. The selected pattern will be compressed to fit the duration of the selecting event
* @param {Pattern} pat * @param {Pattern} pat
* @param {*} xs * @param {*} xs
@ -356,7 +421,7 @@ export const degradeBy = register('degradeBy', function (x, pat) {
export const degrade = register('degrade', (pat) => pat._degradeBy(0.5)); export const degrade = register('degrade', (pat) => pat._degradeBy(0.5));
/** /**
* Inverse of {@link Pattern#degradeBy}: Randomly removes events from the pattern by a given amount. * Inverse of `degradeBy`: Randomly removes events from the pattern by a given amount.
* 0 = 100% chance of removal * 0 = 100% chance of removal
* 1 = 0% chance of removal * 1 = 0% chance of removal
* Events that would be removed by degradeBy are let through by undegradeBy and vice versa (see second example). * Events that would be removed by degradeBy are let through by undegradeBy and vice versa (see second example).
@ -380,7 +445,7 @@ export const undegrade = register('undegrade', (pat) => pat._undegradeBy(0.5));
/** /**
* *
* Randomly applies the given function by the given probability. * Randomly applies the given function by the given probability.
* Similar to {@link Pattern#someCyclesBy} * Similar to `someCyclesBy`
* *
* @name sometimesBy * @name sometimesBy
* @memberof Pattern * @memberof Pattern
@ -415,7 +480,7 @@ export const sometimes = register('sometimes', function (func, pat) {
/** /**
* *
* Randomly applies the given function by the given probability on a cycle by cycle basis. * Randomly applies the given function by the given probability on a cycle by cycle basis.
* Similar to {@link Pattern#sometimesBy} * Similar to `sometimesBy`
* *
* @name someCyclesBy * @name someCyclesBy
* @memberof Pattern * @memberof Pattern

View file

@ -46,6 +46,7 @@ import {
rev, rev,
time, time,
run, run,
pick,
} from '../index.mjs'; } from '../index.mjs';
import { steady } from '../signal.mjs'; import { steady } from '../signal.mjs';
@ -1057,4 +1058,63 @@ describe('Pattern', () => {
expect(slowcat(0, 1).repeatCycles(2).fast(6).firstCycleValues).toStrictEqual([0, 0, 1, 1, 0, 0]); expect(slowcat(0, 1).repeatCycles(2).fast(6).firstCycleValues).toStrictEqual([0, 0, 1, 1, 0, 0]);
}); });
}); });
describe('inhabit', () => {
it('Can pattern named patterns', () => {
expect(
sameFirst(
sequence('a', 'b', stack('a', 'b')).inhabit({ a: sequence(1, 2), b: sequence(10, 20, 30) }),
sequence([1, 2], [10, 20, 30], stack([1, 2], [10, 20, 30])),
),
);
});
it('Can pattern indexed patterns', () => {
expect(
sameFirst(
sequence('0', '1', stack('0', '1')).inhabit([sequence(1, 2), sequence(10, 20, 30)]),
sequence([1, 2], [10, 20, 30], stack([1, 2], [10, 20, 30])),
),
);
});
});
describe('pick', () => {
it('Can pattern named patterns', () => {
expect(
sameFirst(
sequence('a', 'b', 'a', stack('a', 'b')).pick({ a: sequence(1, 2, 3, 4), b: sequence(10, 20, 30, 40) }),
sequence(1, 20, 3, stack(4, 40)),
),
);
});
it('Can pattern indexed patterns', () => {
expect(
sameFirst(
sequence(0, 1, 0, stack(0, 1)).pick([sequence(1, 2, 3, 4), sequence(10, 20, 30, 40)]),
sequence(1, 20, 3, stack(4, 40)),
),
);
});
it('Clamps indexes', () => {
expect(
sameFirst(sequence(0, 1, 2, 3).pick([sequence(1, 2, 3, 4), sequence(10, 20, 30, 40)]), sequence(1, 20, 30, 40)),
);
});
it('Is backwards compatible', () => {
expect(
sameFirst(
pick([sequence('a', 'b'), sequence('c', 'd')], sequence(0, 1)),
pick(sequence(0, 1), [sequence('a', 'b'), sequence('c', 'd')]),
),
);
});
});
describe('pickmod', () => {
it('Wraps indexes', () => {
expect(
sameFirst(
sequence(0, 1, 2, 3).pickmod([sequence(1, 2, 3, 4), sequence(10, 20, 30, 40)]),
sequence(1, 20, 3, 40),
),
);
});
});
}); });

View file

@ -316,3 +316,10 @@ export function hash2code(hash) {
return base64ToUnicode(decodeURIComponent(hash)); return base64ToUnicode(decodeURIComponent(hash));
//return atob(decodeURIComponent(codeParam || '')); //return atob(decodeURIComponent(codeParam || ''));
} }
export function objectMap(obj, fn) {
if (Array.isArray(obj)) {
return obj.map(fn);
}
return Object.fromEntries(Object.entries(obj).map(([k, v], i) => [k, fn(v, k, i)]));
}

View file

@ -0,0 +1 @@
# @strudel/csound

View file

@ -1,5 +1,5 @@
import { getFrequency, logger, register } from '@strudel.cycles/core'; import { getFrequency, logger, register } from '@strudel/core';
import { getAudioContext } from '@strudel.cycles/webaudio'; import { getAudioContext } from '@strudel/webaudio';
import csd from './project.csd?raw'; import csd from './project.csd?raw';
// import livecodeOrc from './livecode.orc?raw'; // import livecodeOrc from './livecode.orc?raw';
import presetsOrc from './presets.orc?raw'; import presetsOrc from './presets.orc?raw';

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/csound", "name": "@strudel/csound",
"version": "0.9.0", "version": "0.11.0",
"description": "csound bindings for strudel", "description": "csound bindings for strudel",
"main": "index.mjs", "main": "index.mjs",
"publishConfig": { "publishConfig": {
@ -33,8 +33,8 @@
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@csound/browser": "6.18.7", "@csound/browser": "6.18.7",
"@strudel.cycles/core": "workspace:*", "@strudel/core": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*" "@strudel/webaudio": "workspace:*"
}, },
"devDependencies": { "devDependencies": {
"vite": "^5.0.10" "vite": "^5.0.10"

View file

@ -1,5 +1,5 @@
import { Invoke } from './utils.mjs'; import { Invoke } from './utils.mjs';
import { Pattern, noteToMidi } from '@strudel.cycles/core'; import { Pattern, noteToMidi } from '@strudel/core';
const ON_MESSAGE = 0x90; const ON_MESSAGE = 0x90;
const OFF_MESSAGE = 0x80; const OFF_MESSAGE = 0x80;

View file

@ -1,4 +1,4 @@
import { parseNumeral, Pattern } from '@strudel.cycles/core'; import { parseNumeral, Pattern } from '@strudel/core';
import { Invoke } from './utils.mjs'; import { Invoke } from './utils.mjs';
Pattern.prototype.osc = function () { Pattern.prototype.osc = function () {

View file

@ -22,7 +22,7 @@
"url": "https://github.com/tidalcycles/strudel/issues" "url": "https://github.com/tidalcycles/strudel/issues"
}, },
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*", "@strudel/core": "workspace:*",
"@tauri-apps/api": "^1.5.3" "@tauri-apps/api": "^1.5.3"
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme" "homepage": "https://github.com/tidalcycles/strudel#readme"

View file

@ -1,13 +1,13 @@
# @strudel.cycles/embed # @strudel/embed
This package contains a embeddable web component for the Strudel REPL. This package contains a embeddable web component for the Strudel REPL.
## Usage ## Usage
Either install with `npm i @strudel.cycles/embed` or just use a cdn to import the script: Either install with `npm i @strudel/embed` or just use a cdn to import the script:
```html ```html
<script src="https://unpkg.com/@strudel.cycles/embed@latest"></script> <script src="https://unpkg.com/@strudel/embed@latest"></script>
<strudel-repl> <strudel-repl>
<!-- <!--
note(`[[e5 [b4 c5] d5 [c5 b4]] note(`[[e5 [b4 c5] d5 [c5 b4]]

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/embed", "name": "@strudel/embed",
"version": "0.2.0", "version": "0.11.0",
"description": "Embeddable Web Component to load a Strudel REPL into an iframe", "description": "Embeddable Web Component to load a Strudel REPL into an iframe",
"main": "embed.js", "main": "embed.js",
"type": "module", "type": "module",

View file

@ -27,7 +27,7 @@ npm i @strudel/hydra
Then add the import to your evalScope: Then add the import to your evalScope:
```js ```js
import { evalScope } from '@strudel.cycles/core'; import { evalScope } from '@strudel/core';
evalScope( evalScope(
import('@strudel/hydra') import('@strudel/hydra')

View file

@ -1,4 +1,4 @@
import { getDrawContext } from '@strudel.cycles/core'; import { getDrawContext } from '@strudel/core';
let latestOptions; let latestOptions;

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel/hydra", "name": "@strudel/hydra",
"version": "0.9.0", "version": "0.11.0",
"description": "Hydra integration for strudel", "description": "Hydra integration for strudel",
"main": "hydra.mjs", "main": "hydra.mjs",
"publishConfig": { "publishConfig": {
@ -33,7 +33,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*", "@strudel/core": "workspace:*",
"hydra-synth": "^1.3.29" "hydra-synth": "^1.3.29"
}, },
"devDependencies": { "devDependencies": {

View file

@ -1,9 +1,9 @@
# @strudel.cycles/midi # @strudel/midi
This package adds midi functionality to strudel Patterns. This package adds midi functionality to strudel Patterns.
## Install ## Install
```sh ```sh
npm i @strudel.cycles/midi --save npm i @strudel/midi --save
``` ```

View file

@ -5,8 +5,8 @@ This program is free software: you can redistribute it and/or modify it under th
*/ */
import * as _WebMidi from 'webmidi'; import * as _WebMidi from 'webmidi';
import { Pattern, isPattern, logger, ref } from '@strudel.cycles/core'; import { Pattern, isPattern, logger, ref } from '@strudel/core';
import { noteToMidi } from '@strudel.cycles/core'; import { noteToMidi } from '@strudel/core';
import { Note } from 'webmidi'; import { Note } from 'webmidi';
// if you use WebMidi from outside of this package, make sure to import that instance: // if you use WebMidi from outside of this package, make sure to import that instance:
export const { WebMidi } = _WebMidi; export const { WebMidi } = _WebMidi;

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/midi", "name": "@strudel/midi",
"version": "0.9.0", "version": "0.11.0",
"description": "Midi API for strudel", "description": "Midi API for strudel",
"main": "index.mjs", "main": "index.mjs",
"publishConfig": { "publishConfig": {
@ -29,8 +29,8 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*", "@strudel/core": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*", "@strudel/webaudio": "workspace:*",
"webmidi": "^3.1.8" "webmidi": "^3.1.8"
}, },
"devDependencies": { "devDependencies": {

View file

@ -1,17 +1,17 @@
# @strudel.cycles/mini # @strudel/mini
This package contains the mini notation parser and pattern generator. This package contains the mini notation parser and pattern generator.
## Install ## Install
```sh ```sh
npm i @strudel.cycles/mini --save npm i @strudel/mini --save
``` ```
## Example ## Example
```js ```js
import { mini } from '@strudel.cycles/mini'; import { mini } from '@strudel/mini';
const pattern = mini('a [b c*2]'); const pattern = mini('a [b c*2]');
@ -28,7 +28,7 @@ yields:
(7/8 -> 1/1, 7/8 -> 1/1, c) (7/8 -> 1/1, 7/8 -> 1/1, c)
``` ```
[Play with @strudel.cycles/mini codesandbox](https://codesandbox.io/s/strudel-mini-example-oe9wcu?file=/src/index.js) [Play with @strudel/mini codesandbox](https://codesandbox.io/s/strudel-mini-example-oe9wcu?file=/src/index.js)
## Mini Notation API ## Mini Notation API

View file

@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/ */
import * as krill from './krill-parser.js'; import * as krill from './krill-parser.js';
import * as strudel from '@strudel.cycles/core'; import * as strudel from '@strudel/core';
const randOffset = 0.0003; const randOffset = 0.0003;

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/mini", "name": "@strudel/mini",
"version": "0.9.0", "version": "0.11.0",
"description": "Mini notation for strudel", "description": "Mini notation for strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@ -32,7 +32,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*" "@strudel/core": "workspace:*"
}, },
"devDependencies": { "devDependencies": {
"peggy": "^3.0.2", "peggy": "^3.0.2",

View file

@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/ */
import { getLeafLocation, getLeafLocations, mini, mini2ast } from '../mini.mjs'; import { getLeafLocation, getLeafLocations, mini, mini2ast } from '../mini.mjs';
import '@strudel.cycles/core/euclid.mjs'; import '@strudel/core/euclid.mjs';
import { describe, expect, it } from 'vitest'; import { describe, expect, it } from 'vitest';
describe('mini', () => { describe('mini', () => {

View file

@ -1,4 +1,4 @@
# @strudel.cycles/osc # @strudel/osc
OSC output for strudel patterns! Currently only tested with super collider / super dirt. OSC output for strudel patterns! Currently only tested with super collider / super dirt.

View file

@ -6,7 +6,7 @@ This program is free software: you can redistribute it and/or modify it under th
import OSC from 'osc-js'; import OSC from 'osc-js';
import { logger, parseNumeral, Pattern } from '@strudel.cycles/core'; import { logger, parseNumeral, Pattern } from '@strudel/core';
let connection; // Promise<OSC> let connection; // Promise<OSC>
function connect() { function connect() {

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/osc", "name": "@strudel/osc",
"version": "0.9.0", "version": "0.11.0",
"description": "OSC messaging for strudel", "description": "OSC messaging for strudel",
"main": "osc.mjs", "main": "osc.mjs",
"publishConfig": { "publishConfig": {
@ -36,7 +36,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*", "@strudel/core": "workspace:*",
"osc-js": "^2.4.0" "osc-js": "^2.4.0"
}, },
"devDependencies": { "devDependencies": {

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel/repl", "name": "@strudel/repl",
"version": "0.9.4", "version": "0.11.0",
"description": "Strudel REPL as a Web Component", "description": "Strudel REPL as a Web Component",
"main": "index.mjs", "main": "index.mjs",
"publishConfig": { "publishConfig": {
@ -33,15 +33,15 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/midi": "workspace:*",
"@strudel.cycles/mini": "workspace:*",
"@strudel.cycles/soundfonts": "workspace:*",
"@strudel.cycles/tonal": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@strudel/codemirror": "workspace:*", "@strudel/codemirror": "workspace:*",
"@strudel/hydra": "workspace:*" "@strudel/core": "workspace:*",
"@strudel/hydra": "workspace:*",
"@strudel/midi": "workspace:*",
"@strudel/mini": "workspace:*",
"@strudel/soundfonts": "workspace:*",
"@strudel/tonal": "workspace:*",
"@strudel/transpiler": "workspace:*",
"@strudel/webaudio": "workspace:*"
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-replace": "^5.0.5", "@rollup/plugin-replace": "^5.0.5",

View file

@ -1,22 +1,22 @@
import { controls, noteToMidi, valueToMidi, Pattern, evalScope } from '@strudel.cycles/core'; import { controls, noteToMidi, valueToMidi, Pattern, evalScope } from '@strudel/core';
import { registerSynthSounds, registerZZFXSounds, samples } from '@strudel.cycles/webaudio'; import { registerSynthSounds, registerZZFXSounds, samples } from '@strudel/webaudio';
import * as core from '@strudel.cycles/core'; import * as core from '@strudel/core';
export async function prebake() { export async function prebake() {
const modulesLoading = evalScope( const modulesLoading = evalScope(
// import('@strudel.cycles/core'), // import('@strudel/core'),
core, core,
import('@strudel.cycles/mini'), import('@strudel/mini'),
import('@strudel.cycles/tonal'), import('@strudel/tonal'),
import('@strudel.cycles/webaudio'), import('@strudel/webaudio'),
import('@strudel/codemirror'), import('@strudel/codemirror'),
import('@strudel/hydra'), import('@strudel/hydra'),
import('@strudel.cycles/soundfonts'), import('@strudel/soundfonts'),
import('@strudel.cycles/midi'), import('@strudel/midi'),
// import('@strudel.cycles/xen'), // import('@strudel/xen'),
// import('@strudel.cycles/serial'), // import('@strudel/serial'),
// import('@strudel.cycles/csound'), // import('@strudel/csound'),
// import('@strudel.cycles/osc'), // import('@strudel/osc'),
controls, // sadly, this cannot be exported from core directly (yet) controls, // sadly, this cannot be exported from core directly (yet)
); );
// load samples // load samples
@ -26,10 +26,10 @@ export async function prebake() {
registerSynthSounds(), registerSynthSounds(),
registerZZFXSounds(), registerZZFXSounds(),
//registerSoundfonts(), //registerSoundfonts(),
// need dynamic import here, because importing @strudel.cycles/soundfonts fails on server: // need dynamic import here, because importing @strudel/soundfonts fails on server:
// => getting "window is not defined", as soon as "@strudel.cycles/soundfonts" is imported statically // => getting "window is not defined", as soon as "@strudel/soundfonts" is imported statically
// seems to be a problem with soundfont2 // seems to be a problem with soundfont2
import('@strudel.cycles/soundfonts').then(({ registerSoundfonts }) => registerSoundfonts()), import('@strudel/soundfonts').then(({ registerSoundfonts }) => registerSoundfonts()),
samples(`${ds}/tidal-drum-machines.json`), samples(`${ds}/tidal-drum-machines.json`),
samples(`${ds}/piano.json`), samples(`${ds}/piano.json`),
samples(`${ds}/Dirt-Samples.json`), samples(`${ds}/Dirt-Samples.json`),

View file

@ -1,6 +1,6 @@
import { getDrawContext, silence } from '@strudel.cycles/core'; import { getDrawContext, silence } from '@strudel/core';
import { transpiler } from '@strudel.cycles/transpiler'; import { transpiler } from '@strudel/transpiler';
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio'; import { getAudioContext, webaudioOutput } from '@strudel/webaudio';
import { StrudelMirror, codemirrorSettings } from '@strudel/codemirror'; import { StrudelMirror, codemirrorSettings } from '@strudel/codemirror';
import { prebake } from './prebake.mjs'; import { prebake } from './prebake.mjs';

View file

@ -1,3 +1,3 @@
# @strudel.cycles/serial # @strudel/serial
This package adds webserial functionality to strudel Patterns, for e.g. sending messages to arduino microcontrollers. This package adds webserial functionality to strudel Patterns, for e.g. sending messages to arduino microcontrollers.

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/serial", "name": "@strudel/serial",
"version": "0.9.0", "version": "0.11.0",
"description": "Webserial API for strudel", "description": "Webserial API for strudel",
"main": "serial.mjs", "main": "serial.mjs",
"publishConfig": { "publishConfig": {
@ -29,7 +29,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*" "@strudel/core": "workspace:*"
}, },
"devDependencies": { "devDependencies": {
"vite": "^5.0.10" "vite": "^5.0.10"

View file

@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Pattern, isPattern } from '@strudel.cycles/core'; import { Pattern, isPattern } from '@strudel/core';
var writeMessagers = {}; var writeMessagers = {};
var choosing = false; var choosing = false;

View file

@ -0,0 +1 @@
# @strudel/soundfonts

View file

@ -1,5 +1,12 @@
import { noteToMidi, freqToMidi, getSoundIndex } from '@strudel.cycles/core'; import { noteToMidi, freqToMidi, getSoundIndex } from '@strudel/core';
import { getAudioContext, registerSound, getParamADSR, getADSRValues } from '@strudel.cycles/webaudio'; import {
getAudioContext,
registerSound,
getParamADSR,
getADSRValues,
getPitchEnvelope,
getVibratoOscillator,
} from '@strudel/webaudio';
import gm from './gm.mjs'; import gm from './gm.mjs';
let loadCache = {}; let loadCache = {};
@ -149,10 +156,16 @@ export function registerSoundfonts() {
getParamADSR(node.gain, attack, decay, sustain, release, 0, 0.3, time, holdEnd, 'linear'); getParamADSR(node.gain, attack, decay, sustain, release, 0, 0.3, time, holdEnd, 'linear');
let envEnd = holdEnd + release + 0.01; let envEnd = holdEnd + release + 0.01;
// vibrato
let vibratoOscillator = getVibratoOscillator(bufferSource.detune, value, time);
// pitch envelope
getPitchEnvelope(bufferSource.detune, value, time, holdEnd);
bufferSource.stop(envEnd); bufferSource.stop(envEnd);
const stop = (releaseTime) => {}; const stop = (releaseTime) => {};
bufferSource.onended = () => { bufferSource.onended = () => {
bufferSource.disconnect(); bufferSource.disconnect();
vibratoOscillator?.stop();
node.disconnect(); node.disconnect();
onended(); onended();
}; };

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/soundfonts", "name": "@strudel/soundfonts",
"version": "0.9.0", "version": "0.11.0",
"description": "Soundsfont support for strudel", "description": "Soundsfont support for strudel",
"main": "index.mjs", "main": "index.mjs",
"publishConfig": { "publishConfig": {
@ -30,8 +30,8 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*", "@strudel/core": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*", "@strudel/webaudio": "workspace:*",
"sfumato": "^0.1.2", "sfumato": "^0.1.2",
"soundfont2": "^0.4.0" "soundfont2": "^0.4.0"
}, },

View file

@ -1,5 +1,5 @@
import { Pattern, getPlayableNoteValue, noteToMidi } from '@strudel.cycles/core'; import { Pattern, getPlayableNoteValue, noteToMidi } from '@strudel/core';
import { getAudioContext, registerSound } from '@strudel.cycles/webaudio'; import { getAudioContext, registerSound } from '@strudel/webaudio';
import { loadSoundfont as _loadSoundfont, startPresetNote } from 'sfumato'; import { loadSoundfont as _loadSoundfont, startPresetNote } from 'sfumato';
Pattern.prototype.soundfont = function (sf, n = 0) { Pattern.prototype.soundfont = function (sf, n = 0) {

View file

@ -31,10 +31,10 @@ export const getParamADSR = (
decay = nanFallback(decay); decay = nanFallback(decay);
sustain = nanFallback(sustain); sustain = nanFallback(sustain);
release = nanFallback(release); release = nanFallback(release);
const ramp = curve === 'exponential' ? 'exponentialRampToValueAtTime' : 'linearRampToValueAtTime'; const ramp = curve === 'exponential' ? 'exponentialRampToValueAtTime' : 'linearRampToValueAtTime';
if (curve === 'exponential') { if (curve === 'exponential') {
min = Math.max(0.0001, min); min = min === 0 ? 0.001 : min;
max = max === 0 ? 0.001 : max;
} }
const range = max - min; const range = max - min;
const peak = max; const peak = max;
@ -42,12 +42,17 @@ export const getParamADSR = (
const duration = end - begin; const duration = end - begin;
const envValAtTime = (time) => { const envValAtTime = (time) => {
let val;
if (attack > time) { if (attack > time) {
let slope = getSlope(min, peak, 0, attack); let slope = getSlope(min, peak, 0, attack);
return time * slope + (min > peak ? min : 0); val = time * slope + (min > peak ? min : 0);
} else { } else {
return (time - attack) * getSlope(peak, sustainVal, 0, decay) + peak; val = (time - attack) * getSlope(peak, sustainVal, 0, decay) + peak;
} }
if (curve === 'exponential') {
val = val || 0.001;
}
return val;
}; };
param.setValueAtTime(min, begin); param.setValueAtTime(min, begin);
@ -144,3 +149,40 @@ export function drywet(dry, wet, wetAmount = 0) {
wet_gain.connect(mix); wet_gain.connect(mix);
return mix; return mix;
} }
let curves = ['linear', 'exponential'];
export function getPitchEnvelope(param, value, t, holdEnd) {
// envelope is active when any of these values is set
const hasEnvelope = value.pattack ?? value.pdecay ?? value.psustain ?? value.prelease ?? value.penv;
if (!hasEnvelope) {
return;
}
const penv = nanFallback(value.penv, 1, true);
const curve = curves[value.pcurve ?? 0];
let [pattack, pdecay, psustain, prelease] = getADSRValues(
[value.pattack, value.pdecay, value.psustain, value.prelease],
curve,
[0.2, 0.001, 1, 0.001],
);
let panchor = value.panchor ?? psustain;
const cents = penv * 100; // penv is in semitones
const min = 0 - cents * panchor;
const max = cents - cents * panchor;
getParamADSR(param, pattack, pdecay, psustain, prelease, min, max, t, holdEnd, curve);
}
export function getVibratoOscillator(param, value, t) {
const { vibmod = 0.5, vib } = value;
let vibratoOscillator;
if (vib > 0) {
vibratoOscillator = getAudioContext().createOscillator();
vibratoOscillator.frequency.value = vib;
const gain = getAudioContext().createGain();
// Vibmod is the amount of vibrato, in semitones
gain.gain.value = vibmod * 100;
vibratoOscillator.connect(gain);
gain.connect(param);
vibratoOscillator.start(t);
return vibratoOscillator;
}
}

View file

@ -1,6 +1,6 @@
{ {
"name": "superdough", "name": "superdough",
"version": "0.9.12", "version": "0.10.0",
"description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.", "description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",

View file

@ -1,6 +1,6 @@
import { noteToMidi, valueToMidi, getSoundIndex } from './util.mjs'; import { noteToMidi, valueToMidi, getSoundIndex } from './util.mjs';
import { getAudioContext, registerSound } from './index.mjs'; import { getAudioContext, registerSound } from './index.mjs';
import { getADSRValues, getParamADSR } from './helpers.mjs'; import { getADSRValues, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs';
import { logger } from './logger.mjs'; import { logger } from './logger.mjs';
const bufferCache = {}; // string: Promise<ArrayBuffer> const bufferCache = {}; // string: Promise<ArrayBuffer>
@ -244,8 +244,6 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
loopEnd = 1, loopEnd = 1,
end = 1, end = 1,
duration, duration,
vib,
vibmod = 0.5,
} = value; } = value;
// load sample // load sample
if (speed === 0) { if (speed === 0) {
@ -263,17 +261,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
const bufferSource = await getSampleBufferSource(s, n, note, speed, freq, bank, resolveUrl); const bufferSource = await getSampleBufferSource(s, n, note, speed, freq, bank, resolveUrl);
// vibrato // vibrato
let vibratoOscillator; let vibratoOscillator = getVibratoOscillator(bufferSource.detune, value, t);
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? // asny stuff above took too long?
if (ac.currentTime > t) { if (ac.currentTime > t) {
@ -310,6 +298,9 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
getParamADSR(node.gain, attack, decay, sustain, release, 0, 1, t, holdEnd, 'linear'); getParamADSR(node.gain, attack, decay, sustain, release, 0, 1, t, holdEnd, 'linear');
// pitch envelope
getPitchEnvelope(bufferSource.detune, value, t, holdEnd);
const out = ac.createGain(); // we need a separate gain for the cutgroups because firefox... const out = ac.createGain(); // we need a separate gain for the cutgroups because firefox...
node.connect(out); node.connect(out);
bufferSource.onended = function () { bufferSource.onended = function () {

View file

@ -1,6 +1,6 @@
import { midiToFreq, noteToMidi } from './util.mjs'; import { midiToFreq, noteToMidi } from './util.mjs';
import { registerSound, getAudioContext } from './superdough.mjs'; import { registerSound, getAudioContext } from './superdough.mjs';
import { gainNode, getADSRValues, getParamADSR } from './helpers.mjs'; import { gainNode, getADSRValues, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs';
import { getNoiseMix, getNoiseOscillator } from './noise.mjs'; import { getNoiseMix, getNoiseOscillator } from './noise.mjs';
const mod = (freq, range = 1, type = 'sine') => { const mod = (freq, range = 1, type = 'sine') => {
@ -105,15 +105,11 @@ export function waveformN(partials, type) {
} }
// expects one of waveforms as s // expects one of waveforms as s
export function getOscillator( export function getOscillator(s, t, value) {
s, let {
t,
{
n: partials, n: partials,
note, note,
freq, freq,
vib = 0,
vibmod = 0.5,
noise = 0, noise = 0,
// fm // fm
fmh: fmHarmonicity = 1, fmh: fmHarmonicity = 1,
@ -126,8 +122,7 @@ export function getOscillator(
fmvelocity: fmVelocity, fmvelocity: fmVelocity,
fmwave: fmWaveform = 'sine', fmwave: fmWaveform = 'sine',
duration, duration,
}, } = value;
) {
let ac = getAudioContext(); let ac = getAudioContext();
let o; let o;
// If no partials are given, use stock waveforms // If no partials are given, use stock waveforms
@ -184,17 +179,10 @@ export function getOscillator(
} }
// Additional oscillator for vibrato effect // Additional oscillator for vibrato effect
let vibratoOscillator; let vibratoOscillator = getVibratoOscillator(o.detune, value, t);
if (vib > 0) {
vibratoOscillator = getAudioContext().createOscillator(); // pitch envelope
vibratoOscillator.frequency.value = vib; getPitchEnvelope(o.detune, value, t, t + duration);
const gain = getAudioContext().createGain();
// Vibmod is the amount of vibrato, in semitones
gain.gain.value = vibmod * 100;
vibratoOscillator.connect(gain);
gain.connect(o.detune);
vibratoOscillator.start(t);
}
let noiseMix; let noiseMix;
if (noise) { if (noise) {

View file

@ -5,6 +5,37 @@ export var vowelFormant = {
i: { freqs: [270, 1850, 2900, 3350, 3590], gains: [1, 0.0631, 0.0631, 0.0158, 0.0158], qs: [40, 90, 100, 120, 120] }, i: { freqs: [270, 1850, 2900, 3350, 3590], gains: [1, 0.0631, 0.0631, 0.0158, 0.0158], qs: [40, 90, 100, 120, 120] },
o: { freqs: [430, 820, 2700, 3000, 3300], gains: [1, 0.3162, 0.0501, 0.0794, 0.01995], qs: [40, 80, 100, 120, 120] }, o: { freqs: [430, 820, 2700, 3000, 3300], gains: [1, 0.3162, 0.0501, 0.0794, 0.01995], qs: [40, 80, 100, 120, 120] },
u: { freqs: [370, 630, 2750, 3000, 3400], gains: [1, 0.1, 0.0708, 0.0316, 0.01995], qs: [40, 60, 100, 120, 120] }, u: { freqs: [370, 630, 2750, 3000, 3400], gains: [1, 0.1, 0.0708, 0.0316, 0.01995], qs: [40, 60, 100, 120, 120] },
ae: { freqs: [650, 1515, 2400, 3000, 3350], gains: [1, 0.5, 0.1008, 0.0631, 0.0126], qs: [80, 90, 120, 130, 140] },
aa: { freqs: [560, 900, 2570, 3000, 3300], gains: [1, 0.5, 0.0708, 0.0631, 0.0126], qs: [80, 90, 120, 130, 140] },
oe: { freqs: [500, 1430, 2300, 3000, 3300], gains: [1, 0.2, 0.0708, 0.0316, 0.01995], qs: [40, 60, 100, 120, 120] },
ue: { freqs: [250, 1750, 2150, 3200, 3300], gains: [1, 0.1, 0.0708, 0.0316, 0.01995], qs: [40, 60, 100, 120, 120] },
y: { freqs: [400, 1460, 2400, 3000, 3300], gains: [1, 0.2, 0.0708, 0.0316, 0.02995], qs: [40, 60, 100, 120, 120] },
uh: { freqs: [600, 1250, 2100, 3100, 3500], gains: [1, 0.3, 0.0608, 0.0316, 0.01995], qs: [40, 70, 100, 120, 130] },
un: { freqs: [500, 1240, 2280, 3000, 3500], gains: [1, 0.1, 0.1708, 0.0216, 0.02995], qs: [40, 60, 100, 120, 120] },
en: { freqs: [600, 1480, 2450, 3200, 3300], gains: [1, 0.15, 0.0708, 0.0316, 0.02995], qs: [40, 60, 100, 120, 120] },
an: { freqs: [700, 1050, 2500, 3000, 3300], gains: [1, 0.1, 0.0708, 0.0316, 0.02995], qs: [40, 60, 100, 120, 120] },
on: { freqs: [500, 1080, 2350, 3000, 3300], gains: [1, 0.1, 0.0708, 0.0316, 0.02995], qs: [40, 60, 100, 120, 120] },
get æ() {
return this.ae;
},
get ø() {
return this.oe;
},
get ɑ() {
return this.aa;
},
get å() {
return this.aa;
},
get ö() {
return this.oe;
},
get ü() {
return this.ue;
},
get ı() {
return this.y;
},
}; };
if (typeof GainNode !== 'undefined') { if (typeof GainNode !== 'undefined') {
class VowelNode extends GainNode { class VowelNode extends GainNode {

View file

@ -1,18 +1,18 @@
# @strudel.cycles/tonal # @strudel/tonal
This package adds tonal / harmonic functions to strudel Patterns. This package adds tonal / harmonic functions to strudel Patterns.
## Install ## Install
```sh ```sh
npm i @strudel.cycles/tonal --save npm i @strudel/tonal --save
``` ```
## Example ## Example
```js ```js
import { sequence } from '@strudel.cycles/core'; import { sequence } from '@strudel/core';
import '@strudel.cycles/tonal'; import '@strudel/tonal';
const pattern = sequence(0, [1, 2]).scale('C major'); const pattern = sequence(0, [1, 2]).scale('C major');
@ -27,7 +27,7 @@ yields:
(3/4 -> 1/1, 3/4 -> 1/1, E3) (3/4 -> 1/1, 3/4 -> 1/1, E3)
``` ```
[play with @strudel.cycles/tonal codesandbox](https://codesandbox.io/s/strudel-tonal-example-rgc5if?file=/src/index.js) [play with @strudel/tonal codesandbox](https://codesandbox.io/s/strudel-tonal-example-rgc5if?file=/src/index.js)
## Tonal API ## Tonal API

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/tonal", "name": "@strudel/tonal",
"version": "0.9.0", "version": "0.11.0",
"description": "Tonal functions for strudel", "description": "Tonal functions for strudel",
"main": "index.mjs", "main": "index.mjs",
"publishConfig": { "publishConfig": {
@ -31,7 +31,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*", "@strudel/core": "workspace:*",
"@tonaljs/tonal": "^4.7.2", "@tonaljs/tonal": "^4.7.2",
"chord-voicings": "^0.0.1", "chord-voicings": "^0.0.1",
"webmidi": "^3.1.8" "webmidi": "^3.1.8"

View file

@ -7,7 +7,7 @@ This program is free software: you can redistribute it and/or modify it under th
// import { strict as assert } from 'assert'; // import { strict as assert } from 'assert';
import '../tonal.mjs'; // need to import this to add prototypes import '../tonal.mjs'; // need to import this to add prototypes
import { pure, controls, seq } from '@strudel.cycles/core'; import { pure, controls, seq } from '@strudel/core';
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { mini } from '../../mini/mini.mjs'; import { mini } from '../../mini/mini.mjs';
const { n } = controls; const { n } = controls;

View file

@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/ */
import { Note, Interval, Scale } from '@tonaljs/tonal'; import { Note, Interval, Scale } from '@tonaljs/tonal';
import { register, _mod, silence, logger, pure, isNote } from '@strudel.cycles/core'; import { register, _mod, silence, logger, pure, isNote } from '@strudel/core';
import { stepInNamedScale } from './tonleiter.mjs'; import { stepInNamedScale } from './tonleiter.mjs';
const octavesInterval = (octaves) => (octaves <= 0 ? -1 : 1) + octaves * 7 + 'P'; const octavesInterval = (octaves) => (octaves <= 0 ? -1 : 1) + octaves * 7 + 'P';

View file

@ -1,4 +1,4 @@
import { isNote, isNoteWithOctave, _mod, noteToMidi, tokenizeNote } from '@strudel.cycles/core'; import { isNote, isNoteWithOctave, _mod, noteToMidi, tokenizeNote } from '@strudel/core';
import { Interval, Scale } from '@tonaljs/tonal'; import { Interval, Scale } from '@tonaljs/tonal';
// https://codesandbox.io/s/stateless-voicings-g2tmz0?file=/src/lib.js:0-2515 // https://codesandbox.io/s/stateless-voicings-g2tmz0?file=/src/lib.js:0-2515

View file

@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { stack, register, silence, logger } from '@strudel.cycles/core'; import { stack, register, silence, logger } from '@strudel/core';
import { renderVoicing } from './tonleiter.mjs'; import { renderVoicing } from './tonleiter.mjs';
import _voicings from 'chord-voicings'; import _voicings from 'chord-voicings';
import { complex, simple } from './ireal.mjs'; import { complex, simple } from './ireal.mjs';

View file

@ -1,4 +1,4 @@
# @strudel.cycles/transpiler # @strudel/transpiler
This package contains a JS code transpiler with the following features: This package contains a JS code transpiler with the following features:
@ -9,14 +9,14 @@ This package contains a JS code transpiler with the following features:
## Install ## Install
```sh ```sh
npm i @strudel.cycles/transpiler npm i @strudel/transpiler
``` ```
## Use ## Use
```js ```js
import { transpiler } from '@strudel.cycles/core'; import { transpiler } from '@strudel/core';
import { evaluate } from '@strudel.cycles/core'; import { evaluate } from '@strudel/core';
transpiler('note("c3 [e3,g3]")', { wrapAsync: false, addReturn: false, simpleLocs: true }); transpiler('note("c3 [e3,g3]")', { wrapAsync: false, addReturn: false, simpleLocs: true });
/* mini('c3 [e3,g3]').withMiniLocation(7,17) */ /* mini('c3 [e3,g3]').withMiniLocation(7,17) */

View file

@ -1,4 +1,4 @@
import { evaluate as _evaluate } from '@strudel.cycles/core'; import { evaluate as _evaluate } from '@strudel/core';
import { transpiler } from './transpiler.mjs'; import { transpiler } from './transpiler.mjs';
export * from './transpiler.mjs'; export * from './transpiler.mjs';

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/transpiler", "name": "@strudel/transpiler",
"version": "0.9.0", "version": "0.11.0",
"description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.", "description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.",
"main": "index.mjs", "main": "index.mjs",
"publishConfig": { "publishConfig": {
@ -30,8 +30,8 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*", "@strudel/core": "workspace:*",
"@strudel.cycles/mini": "workspace:*", "@strudel/mini": "workspace:*",
"acorn": "^8.11.3", "acorn": "^8.11.3",
"escodegen": "^2.1.0", "escodegen": "^2.1.0",
"estree-walker": "^3.0.1" "estree-walker": "^3.0.1"

View file

@ -1,8 +1,8 @@
import escodegen from 'escodegen'; import escodegen from 'escodegen';
import { parse } from 'acorn'; import { parse } from 'acorn';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import { isNoteWithOctave } from '@strudel.cycles/core'; import { isNoteWithOctave } from '@strudel/core';
import { getLeafLocations } from '@strudel.cycles/mini'; import { getLeafLocations } from '@strudel/mini';
export function transpiler(input, options = {}) { export function transpiler(input, options = {}) {
const { wrapAsync = false, addReturn = true, emitMiniLocations = true, emitWidgets = true } = options; const { wrapAsync = false, addReturn = true, emitMiniLocations = true, emitWidgets = true } = options;

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel/web", "name": "@strudel/web",
"version": "0.9.0", "version": "0.11.0",
"description": "Easy to setup, opiniated bundle of Strudel for the browser.", "description": "Easy to setup, opiniated bundle of Strudel for the browser.",
"main": "web.mjs", "main": "web.mjs",
"publishConfig": { "publishConfig": {
@ -33,11 +33,11 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*", "@strudel/core": "workspace:*",
"@strudel.cycles/mini": "workspace:*", "@strudel/mini": "workspace:*",
"@strudel.cycles/tonal": "workspace:*", "@strudel/tonal": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*", "@strudel/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*" "@strudel/webaudio": "workspace:*"
}, },
"devDependencies": { "devDependencies": {
"vite": "^5.0.10" "vite": "^5.0.10"

View file

@ -1,25 +1,25 @@
export * from '@strudel.cycles/core'; export * from '@strudel/core';
export * from '@strudel.cycles/webaudio'; export * from '@strudel/webaudio';
//export * from '@strudel.cycles/soundfonts'; //export * from '@strudel/soundfonts';
export * from '@strudel.cycles/transpiler'; export * from '@strudel/transpiler';
export * from '@strudel.cycles/mini'; export * from '@strudel/mini';
export * from '@strudel.cycles/tonal'; export * from '@strudel/tonal';
export * from '@strudel.cycles/webaudio'; export * from '@strudel/webaudio';
import { Pattern, evalScope, controls } from '@strudel.cycles/core'; import { Pattern, evalScope, controls } from '@strudel/core';
import { initAudioOnFirstClick, registerSynthSounds, webaudioScheduler } from '@strudel.cycles/webaudio'; import { initAudioOnFirstClick, registerSynthSounds, webaudioScheduler } from '@strudel/webaudio';
// import { registerSoundfonts } from '@strudel.cycles/soundfonts'; // import { registerSoundfonts } from '@strudel/soundfonts';
import { evaluate as _evaluate } from '@strudel.cycles/transpiler'; import { evaluate as _evaluate } from '@strudel/transpiler';
import { miniAllStrings } from '@strudel.cycles/mini'; import { miniAllStrings } from '@strudel/mini';
// init logic // init logic
export async function defaultPrebake() { export async function defaultPrebake() {
const loadModules = evalScope( const loadModules = evalScope(
evalScope, evalScope,
controls, controls,
import('@strudel.cycles/core'), import('@strudel/core'),
import('@strudel.cycles/mini'), import('@strudel/mini'),
import('@strudel.cycles/tonal'), import('@strudel/tonal'),
import('@strudel.cycles/webaudio'), import('@strudel/webaudio'),
{ hush, evaluate }, { hush, evaluate },
); );
await Promise.all([loadModules, registerSynthSounds() /* , registerSoundfonts() */]); await Promise.all([loadModules, registerSynthSounds() /* , registerSoundfonts() */]);

View file

@ -1,4 +1,4 @@
# @strudel.cycles/webaudio # @strudel/webaudio
This package contains helpers to make music with strudel and the Web Audio API. This package contains helpers to make music with strudel and the Web Audio API.
It is a thin binding to [superdough](https://www.npmjs.com/package/superdough). It is a thin binding to [superdough](https://www.npmjs.com/package/superdough).
@ -6,14 +6,14 @@ It is a thin binding to [superdough](https://www.npmjs.com/package/superdough).
## Install ## Install
```sh ```sh
npm i @strudel.cycles/webaudio --save npm i @strudel/webaudio --save
``` ```
## Example ## Example
```js ```js
import { repl, controls } from "@strudel.cycles/core"; import { repl, controls } from "@strudel/core";
import { initAudioOnFirstClick, getAudioContext, webaudioOutput } from "@strudel.cycles/webaudio"; import { initAudioOnFirstClick, getAudioContext, webaudioOutput } from "@strudel/webaudio";
const { note } = controls; const { note } = controls;
initAudioOnFirstClick(); initAudioOnFirstClick();

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/webaudio", "name": "@strudel/webaudio",
"version": "0.9.0", "version": "0.11.0",
"description": "Web Audio helpers for Strudel", "description": "Web Audio helpers for Strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@ -34,7 +34,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*", "@strudel/core": "workspace:*",
"superdough": "workspace:*" "superdough": "workspace:*"
}, },
"devDependencies": { "devDependencies": {

View file

@ -1,4 +1,4 @@
import { Pattern, getDrawContext, clamp } from '@strudel.cycles/core'; import { Pattern, getDrawContext, clamp } from '@strudel/core';
import { analyser, getAnalyzerData } from 'superdough'; import { analyser, getAnalyzerData } from 'superdough';
export function drawTimeScope( export function drawTimeScope(

View file

@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import * as strudel from '@strudel.cycles/core'; import * as strudel from '@strudel/core';
import { superdough, getAudioContext, setLogger, doughTrigger } from 'superdough'; import { superdough, getAudioContext, setLogger, doughTrigger } from 'superdough';
const { Pattern, logger } = strudel; const { Pattern, logger } = strudel;

View file

@ -1,9 +1,9 @@
# @strudel.cycles/xen # @strudel/xen
This package adds xenharmonic / microtonal functions to strudel Patterns. Further documentation + examples will follow. This package adds xenharmonic / microtonal functions to strudel Patterns. Further documentation + examples will follow.
## Install ## Install
```sh ```sh
npm i @strudel.cycles/xen --save npm i @strudel/xen --save
``` ```

View file

@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/xen", "name": "@strudel/xen",
"version": "0.9.0", "version": "0.11.0",
"description": "Xenharmonic API for strudel", "description": "Xenharmonic API for strudel",
"main": "index.mjs", "main": "index.mjs",
"publishConfig": { "publishConfig": {
@ -30,7 +30,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*" "@strudel/core": "workspace:*"
}, },
"devDependencies": { "devDependencies": {
"vite": "^5.0.10", "vite": "^5.0.10",

View file

@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/ */
import Tune from './tunejs.js'; import Tune from './tunejs.js';
import { register } from '@strudel.cycles/core'; import { register } from '@strudel/core';
export const tune = register('tune', (scale, pat) => { export const tune = register('tune', (scale, pat) => {
const tune = new Tune(); const tune = new Tune();

View file

@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { register, _mod, parseNumeral } from '@strudel.cycles/core'; import { register, _mod, parseNumeral } from '@strudel/core';
export function edo(name) { export function edo(name) {
if (!/^[1-9]+[0-9]*edo$/.test(name)) { if (!/^[1-9]+[0-9]*edo$/.test(name)) {

231
pnpm-lock.yaml generated
View file

@ -8,22 +8,22 @@ importers:
.: .:
dependencies: dependencies:
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:packages/core version: link:packages/core
'@strudel.cycles/mini': '@strudel/mini':
specifier: workspace:* specifier: workspace:*
version: link:packages/mini version: link:packages/mini
'@strudel.cycles/tonal': '@strudel/tonal':
specifier: workspace:* specifier: workspace:*
version: link:packages/tonal version: link:packages/tonal
'@strudel.cycles/transpiler': '@strudel/transpiler':
specifier: workspace:* specifier: workspace:*
version: link:packages/transpiler version: link:packages/transpiler
'@strudel.cycles/webaudio': '@strudel/webaudio':
specifier: workspace:* specifier: workspace:*
version: link:packages/webaudio version: link:packages/webaudio
'@strudel.cycles/xen': '@strudel/xen':
specifier: workspace:* specifier: workspace:*
version: link:packages/xen version: link:packages/xen
devDependencies: devDependencies:
@ -72,27 +72,27 @@ importers:
examples/codemirror-repl: examples/codemirror-repl:
dependencies: dependencies:
'@strudel.cycles/core':
specifier: workspace:*
version: link:../../packages/core
'@strudel.cycles/mini':
specifier: workspace:*
version: link:../../packages/mini
'@strudel.cycles/soundfonts':
specifier: workspace:*
version: link:../../packages/soundfonts
'@strudel.cycles/tonal':
specifier: workspace:*
version: link:../../packages/tonal
'@strudel.cycles/transpiler':
specifier: workspace:*
version: link:../../packages/transpiler
'@strudel.cycles/webaudio':
specifier: workspace:*
version: link:../../packages/webaudio
'@strudel/codemirror': '@strudel/codemirror':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/codemirror version: link:../../packages/codemirror
'@strudel/core':
specifier: workspace:*
version: link:../../packages/core
'@strudel/mini':
specifier: workspace:*
version: link:../../packages/mini
'@strudel/soundfonts':
specifier: workspace:*
version: link:../../packages/soundfonts
'@strudel/tonal':
specifier: workspace:*
version: link:../../packages/tonal
'@strudel/transpiler':
specifier: workspace:*
version: link:../../packages/transpiler
'@strudel/webaudio':
specifier: workspace:*
version: link:../../packages/webaudio
devDependencies: devDependencies:
vite: vite:
specifier: ^5.0.10 specifier: ^5.0.10
@ -110,19 +110,19 @@ importers:
examples/minimal-repl: examples/minimal-repl:
dependencies: dependencies:
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/core version: link:../../packages/core
'@strudel.cycles/mini': '@strudel/mini':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/mini version: link:../../packages/mini
'@strudel.cycles/tonal': '@strudel/tonal':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/tonal version: link:../../packages/tonal
'@strudel.cycles/transpiler': '@strudel/transpiler':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/transpiler version: link:../../packages/transpiler
'@strudel.cycles/webaudio': '@strudel/webaudio':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/webaudio version: link:../../packages/webaudio
devDependencies: devDependencies:
@ -178,7 +178,7 @@ importers:
'@replit/codemirror-vscode-keymap': '@replit/codemirror-vscode-keymap':
specifier: ^6.0.2 specifier: ^6.0.2
version: 6.0.2(@codemirror/autocomplete@6.11.1)(@codemirror/commands@6.3.3)(@codemirror/language@6.10.0)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.5)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0) version: 6.0.2(@codemirror/autocomplete@6.11.1)(@codemirror/commands@6.3.3)(@codemirror/language@6.10.0)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.5)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0)
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:../core version: link:../core
'@uiw/codemirror-themes': '@uiw/codemirror-themes':
@ -213,10 +213,10 @@ importers:
'@csound/browser': '@csound/browser':
specifier: 6.18.7 specifier: 6.18.7
version: 6.18.7(eslint@8.56.0) version: 6.18.7(eslint@8.56.0)
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:../core version: link:../core
'@strudel.cycles/webaudio': '@strudel/webaudio':
specifier: workspace:* specifier: workspace:*
version: link:../webaudio version: link:../webaudio
devDependencies: devDependencies:
@ -226,7 +226,7 @@ importers:
packages/desktopbridge: packages/desktopbridge:
dependencies: dependencies:
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:../core version: link:../core
'@tauri-apps/api': '@tauri-apps/api':
@ -237,7 +237,7 @@ importers:
packages/hydra: packages/hydra:
dependencies: dependencies:
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:../core version: link:../core
hydra-synth: hydra-synth:
@ -253,10 +253,10 @@ importers:
packages/midi: packages/midi:
dependencies: dependencies:
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:../core version: link:../core
'@strudel.cycles/webaudio': '@strudel/webaudio':
specifier: workspace:* specifier: workspace:*
version: link:../webaudio version: link:../webaudio
webmidi: webmidi:
@ -269,7 +269,7 @@ importers:
packages/mini: packages/mini:
dependencies: dependencies:
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:../core version: link:../core
devDependencies: devDependencies:
@ -285,7 +285,7 @@ importers:
packages/osc: packages/osc:
dependencies: dependencies:
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:../core version: link:../core
osc-js: osc-js:
@ -301,33 +301,33 @@ importers:
packages/repl: packages/repl:
dependencies: dependencies:
'@strudel.cycles/core':
specifier: workspace:*
version: link:../core
'@strudel.cycles/midi':
specifier: workspace:*
version: link:../midi
'@strudel.cycles/mini':
specifier: workspace:*
version: link:../mini
'@strudel.cycles/soundfonts':
specifier: workspace:*
version: link:../soundfonts
'@strudel.cycles/tonal':
specifier: workspace:*
version: link:../tonal
'@strudel.cycles/transpiler':
specifier: workspace:*
version: link:../transpiler
'@strudel.cycles/webaudio':
specifier: workspace:*
version: link:../webaudio
'@strudel/codemirror': '@strudel/codemirror':
specifier: workspace:* specifier: workspace:*
version: link:../codemirror version: link:../codemirror
'@strudel/core':
specifier: workspace:*
version: link:../core
'@strudel/hydra': '@strudel/hydra':
specifier: workspace:* specifier: workspace:*
version: link:../hydra version: link:../hydra
'@strudel/midi':
specifier: workspace:*
version: link:../midi
'@strudel/mini':
specifier: workspace:*
version: link:../mini
'@strudel/soundfonts':
specifier: workspace:*
version: link:../soundfonts
'@strudel/tonal':
specifier: workspace:*
version: link:../tonal
'@strudel/transpiler':
specifier: workspace:*
version: link:../transpiler
'@strudel/webaudio':
specifier: workspace:*
version: link:../webaudio
devDependencies: devDependencies:
'@rollup/plugin-replace': '@rollup/plugin-replace':
specifier: ^5.0.5 specifier: ^5.0.5
@ -341,7 +341,7 @@ importers:
packages/serial: packages/serial:
dependencies: dependencies:
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:../core version: link:../core
devDependencies: devDependencies:
@ -351,10 +351,10 @@ importers:
packages/soundfonts: packages/soundfonts:
dependencies: dependencies:
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:../core version: link:../core
'@strudel.cycles/webaudio': '@strudel/webaudio':
specifier: workspace:* specifier: workspace:*
version: link:../webaudio version: link:../webaudio
sfumato: sfumato:
@ -383,7 +383,7 @@ importers:
packages/tonal: packages/tonal:
dependencies: dependencies:
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:../core version: link:../core
'@tonaljs/tonal': '@tonaljs/tonal':
@ -405,10 +405,10 @@ importers:
packages/transpiler: packages/transpiler:
dependencies: dependencies:
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:../core version: link:../core
'@strudel.cycles/mini': '@strudel/mini':
specifier: workspace:* specifier: workspace:*
version: link:../mini version: link:../mini
acorn: acorn:
@ -430,19 +430,19 @@ importers:
packages/web: packages/web:
dependencies: dependencies:
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:../core version: link:../core
'@strudel.cycles/mini': '@strudel/mini':
specifier: workspace:* specifier: workspace:*
version: link:../mini version: link:../mini
'@strudel.cycles/tonal': '@strudel/tonal':
specifier: workspace:* specifier: workspace:*
version: link:../tonal version: link:../tonal
'@strudel.cycles/transpiler': '@strudel/transpiler':
specifier: workspace:* specifier: workspace:*
version: link:../transpiler version: link:../transpiler
'@strudel.cycles/webaudio': '@strudel/webaudio':
specifier: workspace:* specifier: workspace:*
version: link:../webaudio version: link:../webaudio
devDependencies: devDependencies:
@ -452,7 +452,7 @@ importers:
packages/webaudio: packages/webaudio:
dependencies: dependencies:
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:../core version: link:../core
superdough: superdough:
@ -465,7 +465,7 @@ importers:
packages/xen: packages/xen:
dependencies: dependencies:
'@strudel.cycles/core': '@strudel/core':
specifier: workspace:* specifier: workspace:*
version: link:../core version: link:../core
devDependencies: devDependencies:
@ -490,6 +490,9 @@ importers:
'@astrojs/react': '@astrojs/react':
specifier: ^3.0.9 specifier: ^3.0.9
version: 3.0.9(@types/react-dom@18.2.18)(@types/react@18.2.46)(react-dom@18.2.0)(react@18.2.0)(vite@5.0.11) version: 3.0.9(@types/react-dom@18.2.18)(@types/react@18.2.46)(react-dom@18.2.0)(react@18.2.0)(vite@5.0.11)
'@astrojs/rss':
specifier: ^4.0.2
version: 4.0.2
'@astrojs/tailwind': '@astrojs/tailwind':
specifier: ^5.1.0 specifier: ^5.1.0
version: 5.1.0(astro@4.0.8)(tailwindcss@3.4.0) version: 5.1.0(astro@4.0.8)(tailwindcss@3.4.0)
@ -511,51 +514,51 @@ importers:
'@nanostores/react': '@nanostores/react':
specifier: ^0.7.1 specifier: ^0.7.1
version: 0.7.1(nanostores@0.9.5)(react@18.2.0) version: 0.7.1(nanostores@0.9.5)(react@18.2.0)
'@strudel.cycles/core':
specifier: workspace:*
version: link:../packages/core
'@strudel.cycles/csound':
specifier: workspace:*
version: link:../packages/csound
'@strudel.cycles/midi':
specifier: workspace:*
version: link:../packages/midi
'@strudel.cycles/mini':
specifier: workspace:*
version: link:../packages/mini
'@strudel.cycles/osc':
specifier: workspace:*
version: link:../packages/osc
'@strudel.cycles/serial':
specifier: workspace:*
version: link:../packages/serial
'@strudel.cycles/soundfonts':
specifier: workspace:*
version: link:../packages/soundfonts
'@strudel.cycles/tonal':
specifier: workspace:*
version: link:../packages/tonal
'@strudel.cycles/transpiler':
specifier: workspace:*
version: link:../packages/transpiler
'@strudel.cycles/webaudio':
specifier: workspace:*
version: link:../packages/webaudio
'@strudel.cycles/xen':
specifier: workspace:*
version: link:../packages/xen
'@strudel/codemirror': '@strudel/codemirror':
specifier: workspace:* specifier: workspace:*
version: link:../packages/codemirror version: link:../packages/codemirror
'@strudel/core':
specifier: workspace:*
version: link:../packages/core
'@strudel/csound':
specifier: workspace:*
version: link:../packages/csound
'@strudel/desktopbridge': '@strudel/desktopbridge':
specifier: workspace:* specifier: workspace:*
version: link:../packages/desktopbridge version: link:../packages/desktopbridge
'@strudel/hydra': '@strudel/hydra':
specifier: workspace:* specifier: workspace:*
version: link:../packages/hydra version: link:../packages/hydra
'@strudel/midi':
specifier: workspace:*
version: link:../packages/midi
'@strudel/mini':
specifier: workspace:*
version: link:../packages/mini
'@strudel/osc':
specifier: workspace:*
version: link:../packages/osc
'@strudel/repl': '@strudel/repl':
specifier: workspace:* specifier: workspace:*
version: link:../packages/repl version: link:../packages/repl
'@strudel/serial':
specifier: workspace:*
version: link:../packages/serial
'@strudel/soundfonts':
specifier: workspace:*
version: link:../packages/soundfonts
'@strudel/tonal':
specifier: workspace:*
version: link:../packages/tonal
'@strudel/transpiler':
specifier: workspace:*
version: link:../packages/transpiler
'@strudel/webaudio':
specifier: workspace:*
version: link:../packages/webaudio
'@strudel/xen':
specifier: workspace:*
version: link:../packages/xen
'@supabase/supabase-js': '@supabase/supabase-js':
specifier: ^2.39.1 specifier: ^2.39.1
version: 2.39.1 version: 2.39.1
@ -890,6 +893,13 @@ packages:
- vite - vite
dev: false dev: false
/@astrojs/rss@4.0.2:
resolution: {integrity: sha512-Hb9GKAyvsn5EUjZtB6SniesBScMQe7SQinEHLY5EFa74QEvgcWaXTmA0Mb0P3vqDSN3d/NTYbGivprrSAawfnA==}
dependencies:
fast-xml-parser: 4.3.3
kleur: 4.1.5
dev: false
/@astrojs/tailwind@5.1.0(astro@4.0.8)(tailwindcss@3.4.0): /@astrojs/tailwind@5.1.0(astro@4.0.8)(tailwindcss@3.4.0):
resolution: {integrity: sha512-BJoCDKuWhU9FT2qYg+fr6Nfb3qP4ShtyjXGHKA/4mHN94z7BGcmauQK23iy+YH5qWvTnhqkd6mQPQ1yTZTe9Ig==} resolution: {integrity: sha512-BJoCDKuWhU9FT2qYg+fr6Nfb3qP4ShtyjXGHKA/4mHN94z7BGcmauQK23iy+YH5qWvTnhqkd6mQPQ1yTZTe9Ig==}
peerDependencies: peerDependencies:
@ -7182,6 +7192,13 @@ packages:
/fast-levenshtein@2.0.6: /fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
/fast-xml-parser@4.3.3:
resolution: {integrity: sha512-coV/D1MhrShMvU6D0I+VAK3umz6hUaxxhL0yp/9RjfiYUfAv14rDhGQL+PLForhMdr0wq3PiV07WtkkNjJjNHg==}
hasBin: true
dependencies:
strnum: 1.0.5
dev: false
/fastq@1.15.0: /fastq@1.15.0:
resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
dependencies: dependencies:
@ -12637,6 +12654,10 @@ packages:
acorn: 8.11.3 acorn: 8.11.3
dev: true dev: true
/strnum@1.0.5:
resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==}
dev: false
/strong-log-transformer@2.1.0: /strong-log-transformer@2.1.0:
resolution: {integrity: sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==} resolution: {integrity: sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==}
engines: {node: '>=4'} engines: {node: '>=4'}

View file

@ -1,4 +1,4 @@
# @strudel.cycles/tauri # @strudel/tauri
Rust source files for building native desktop apps using Tauri Rust source files for building native desktop apps using Tauri

View file

@ -2408,6 +2408,40 @@ exports[`runs examples > example "hush" example index 0 1`] = `
] ]
`; `;
exports[`runs examples > example "inhabit" example index 0 1`] = `
[
"[ 0/1 → 1/8 | s:bd ]",
"[ 3/8 → 1/2 | s:bd ]",
"[ 3/4 → 7/8 | s:bd ]",
"[ 1/1 → 3/2 | s:cp ]",
"[ 3/2 → 2/1 | s:sd ]",
"[ 2/1 → 17/8 | s:bd ]",
"[ 2/1 → 5/2 | s:cp ]",
"[ 19/8 → 5/2 | s:bd ]",
"[ 5/2 → 3/1 | s:sd ]",
"[ 11/4 → 23/8 | s:bd ]",
"[ 3/1 → 25/8 | s:bd ]",
"[ 27/8 → 7/2 | s:bd ]",
"[ 15/4 → 31/8 | s:bd ]",
]
`;
exports[`runs examples > example "inhabit" example index 1 1`] = `
[
"[ 0/1 → 1/4 | s:bd ]",
"[ 3/4 → 1/1 | s:bd ]",
"[ 3/2 → 7/4 | s:bd ]",
"[ 2/1 → 33/16 | s:bd ]",
"[ 35/16 → 9/4 | s:bd ]",
"[ 19/8 → 39/16 | s:bd ]",
"[ 5/2 → 11/4 | s:sd ]",
"[ 11/4 → 3/1 | s:sd ]",
"[ 3/1 → 25/8 | s:bd ]",
"[ 27/8 → 7/2 | s:bd ]",
"[ 15/4 → 31/8 | s:bd ]",
]
`;
exports[`runs examples > example "inside" example index 0 1`] = ` exports[`runs examples > example "inside" example index 0 1`] = `
[ [
"[ 0/1 → 1/8 | note:D3 ]", "[ 0/1 → 1/8 | note:D3 ]",
@ -3332,6 +3366,55 @@ exports[`runs examples > example "pan" example index 0 1`] = `
] ]
`; `;
exports[`runs examples > example "panchor" example index 0 1`] = `
[
"[ 0/1 → 1/1 | note:c penv:12 panchor:0 ]",
"[ 1/1 → 2/1 | note:c penv:12 panchor:0.5 ]",
"[ 2/1 → 3/1 | note:c penv:12 panchor:1 ]",
"[ 3/1 → 4/1 | note:c penv:12 panchor:0.5 ]",
]
`;
exports[`runs examples > example "pattack" example index 0 1`] = `
[
"[ 0/1 → 1/1 | note:c pattack:0 ]",
"[ 1/1 → 2/1 | note:eb pattack:0.1 ]",
"[ 2/1 → 3/1 | note:g pattack:0.25 ]",
"[ 3/1 → 4/1 | note:bb pattack:0.5 ]",
]
`;
exports[`runs examples > example "pcurve" example index 0 1`] = `
[
"[ 0/1 → 1/2 | note:g1 s:sine pdecay:0.5 penv:32 pcurve:0 ]",
"[ 1/2 → 1/1 | note:g1 s:sine pdecay:0.5 penv:32 pcurve:0 ]",
"[ 1/1 → 3/2 | note:g1 s:sine pdecay:0.5 penv:32 pcurve:1 ]",
"[ 3/2 → 2/1 | note:g1 s:sine pdecay:0.5 penv:32 pcurve:1 ]",
"[ 2/1 → 5/2 | note:g1 s:sine pdecay:0.5 penv:32 pcurve:0 ]",
"[ 5/2 → 3/1 | note:g1 s:sine pdecay:0.5 penv:32 pcurve:0 ]",
"[ 3/1 → 7/2 | note:g1 s:sine pdecay:0.5 penv:32 pcurve:1 ]",
"[ 7/2 → 4/1 | note:g1 s:sine pdecay:0.5 penv:32 pcurve:1 ]",
]
`;
exports[`runs examples > example "pdecay" example index 0 1`] = `
[
"[ 0/1 → 1/1 | note:c pdecay:0 ]",
"[ 1/1 → 2/1 | note:eb pdecay:0.1 ]",
"[ 2/1 → 3/1 | note:g pdecay:0.25 ]",
"[ 3/1 → 4/1 | note:bb pdecay:0.5 ]",
]
`;
exports[`runs examples > example "penv" example index 0 1`] = `
[
"[ 0/1 → 1/1 | note:c penv:12 ]",
"[ 1/1 → 2/1 | note:c penv:7 ]",
"[ 2/1 → 3/1 | note:c penv:1 ]",
"[ 3/1 → 4/1 | note:c penv:0.5 ]",
]
`;
exports[`runs examples > example "perlin" example index 0 1`] = ` exports[`runs examples > example "perlin" example index 0 1`] = `
[ [
"[ 0/1 → 1/4 | s:hh cutoff:512.5097280354112 ]", "[ 0/1 → 1/4 | s:hh cutoff:512.5097280354112 ]",
@ -3552,10 +3635,61 @@ exports[`runs examples > example "pick" example index 0 1`] = `
"[ 9/4 → 5/2 | note:g ]", "[ 9/4 → 5/2 | note:g ]",
"[ 5/2 → 11/4 | note:f ]", "[ 5/2 → 11/4 | note:f ]",
"[ 11/4 → 3/1 | note:g ]", "[ 11/4 → 3/1 | note:g ]",
"[ 3/1 → 13/4 | note:g ]", "[ 3/1 → 13/4 | note:f ]",
"[ 13/4 → 7/2 | note:a ]", "[ 13/4 → 7/2 | note:g ]",
"[ 7/2 → 15/4 | note:c ]", "[ 7/2 → 15/4 | note:f ]",
"[ 15/4 → 4/1 | note:d ]", "[ 15/4 → 4/1 | note:g ]",
]
`;
exports[`runs examples > example "pick" example index 1 1`] = `
[
"[ 0/1 → 1/2 | s:bd ]",
"[ 1/2 → 1/1 | s:sd ]",
"[ 1/1 → 3/2 | s:cp ]",
"[ 3/2 → 2/1 | s:cp ]",
"[ 2/1 → 5/2 | s:hh ]",
"[ 2/1 → 5/2 | s:bd ]",
"[ 5/2 → 3/1 | s:hh ]",
"[ 5/2 → 3/1 | s:sd ]",
"[ 3/1 → 7/2 | s:bd ]",
"[ 7/2 → 4/1 | s:sd ]",
]
`;
exports[`runs examples > example "pick" example index 2 1`] = `
[
"[ 0/1 → 1/8 | s:bd ]",
"[ 3/8 → 1/2 | s:bd ]",
"[ 3/4 → 7/8 | s:bd ]",
"[ 1/1 → 9/8 | s:bd ]",
"[ 11/8 → 3/2 | s:bd ]",
"[ 7/4 → 15/8 | s:bd ]",
"[ 2/1 → 17/8 | s:bd ]",
"[ 2/1 → 5/2 | s:sd ]",
"[ 19/8 → 5/2 | s:bd ]",
"[ 5/2 → 3/1 | s:sd ]",
"[ 11/4 → 23/8 | s:bd ]",
"[ 3/1 → 7/2 | s:sd ]",
"[ 7/2 → 4/1 | s:sd ]",
]
`;
exports[`runs examples > example "pick" example index 3 1`] = `
[
"[ 0/1 → 1/8 | s:bd ]",
"[ 3/8 → 1/2 | s:bd ]",
"[ 3/4 → 7/8 | s:bd ]",
"[ 1/1 → 9/8 | s:bd ]",
"[ 11/8 → 3/2 | s:bd ]",
"[ 7/4 → 15/8 | s:bd ]",
"[ 2/1 → 17/8 | s:bd ]",
"[ 2/1 → 5/2 | s:sd ]",
"[ 19/8 → 5/2 | s:bd ]",
"[ 5/2 → 3/1 | s:sd ]",
"[ 11/4 → 23/8 | s:bd ]",
"[ 3/1 → 7/2 | s:sd ]",
"[ 7/2 → 4/1 | s:sd ]",
] ]
`; `;
@ -3660,6 +3794,15 @@ exports[`runs examples > example "postgain" example index 0 1`] = `
] ]
`; `;
exports[`runs examples > example "prelease" example index 0 1`] = `
[
"[ 0/1 → 1/2 | note:c release:0.5 prelease:0 ]",
"[ 1/1 → 3/2 | note:eb release:0.5 prelease:0.1 ]",
"[ 2/1 → 5/2 | note:g release:0.5 prelease:0.25 ]",
"[ 3/1 → 7/2 | note:bb release:0.5 prelease:0.5 ]",
]
`;
exports[`runs examples > example "press" example index 0 1`] = ` exports[`runs examples > example "press" example index 0 1`] = `
[ [
"[ 0/1 → 1/2 | s:hh ]", "[ 0/1 → 1/2 | s:hh ]",

View file

@ -3,25 +3,25 @@
// it might require mocking more stuff when tunes added that use other functions // it might require mocking more stuff when tunes added that use other functions
// import * as tunes from './tunes.mjs'; // import * as tunes from './tunes.mjs';
import { evaluate } from '@strudel.cycles/transpiler'; import { evaluate } from '@strudel/transpiler';
import { evalScope } from '@strudel.cycles/core'; import { evalScope } from '@strudel/core';
import * as strudel from '@strudel.cycles/core'; import * as strudel from '@strudel/core';
import * as webaudio from '@strudel.cycles/webaudio'; import * as webaudio from '@strudel/webaudio';
import controls from '@strudel.cycles/core/controls.mjs'; import controls from '@strudel/core/controls.mjs';
// import gist from '@strudel.cycles/core/gist.js'; // import gist from '@strudel/core/gist.js';
import { mini, m } from '@strudel.cycles/mini/mini.mjs'; import { mini, m } from '@strudel/mini/mini.mjs';
// import * as voicingHelpers from '@strudel.cycles/tonal/voicings.mjs'; // import * as voicingHelpers from '@strudel/tonal/voicings.mjs';
// import euclid from '@strudel.cycles/core/euclid.mjs'; // import euclid from '@strudel/core/euclid.mjs';
// import '@strudel.cycles/midi/midi.mjs'; // import '@strudel/midi/midi.mjs';
import * as tonalHelpers from '@strudel.cycles/tonal'; import * as tonalHelpers from '@strudel/tonal';
import '@strudel.cycles/xen/xen.mjs'; import '@strudel/xen/xen.mjs';
// import '@strudel.cycles/xen/tune.mjs'; // import '@strudel/xen/tune.mjs';
// import '@strudel.cycles/core/euclid.mjs'; // import '@strudel/core/euclid.mjs';
// import '@strudel.cycles/core/speak.mjs'; // window is not defined // import '@strudel/core/speak.mjs'; // window is not defined
// import '@strudel.cycles/osc/osc.mjs'; // import '@strudel/osc/osc.mjs';
// import '@strudel.cycles/webaudio/webaudio.mjs'; // import '@strudel/webaudio/webaudio.mjs';
// import '@strudel.cycles/serial/serial.mjs'; // import '@strudel/serial/serial.mjs';
// import controls from '@strudel.cycles/core/controls.mjs'; // import controls from '@strudel/core/controls.mjs';
import '../website/src/repl/piano'; import '../website/src/repl/piano';
class MockedNode { class MockedNode {

View file

@ -1,5 +1,5 @@
{ {
"name": "@strudel.cycles/website", "name": "@strudel/website",
"type": "module", "type": "module",
"version": "0.6.0", "version": "0.6.0",
"private": true, "private": true,
@ -16,6 +16,7 @@
"@astro-community/astro-embed-youtube": "^0.4.3", "@astro-community/astro-embed-youtube": "^0.4.3",
"@astrojs/mdx": "^2.0.3", "@astrojs/mdx": "^2.0.3",
"@astrojs/react": "^3.0.9", "@astrojs/react": "^3.0.9",
"@astrojs/rss": "^4.0.2",
"@astrojs/tailwind": "^5.1.0", "@astrojs/tailwind": "^5.1.0",
"@docsearch/css": "^3.5.2", "@docsearch/css": "^3.5.2",
"@docsearch/react": "^3.5.2", "@docsearch/react": "^3.5.2",
@ -23,21 +24,21 @@
"@heroicons/react": "^2.1.1", "@heroicons/react": "^2.1.1",
"@nanostores/persistent": "^0.9.1", "@nanostores/persistent": "^0.9.1",
"@nanostores/react": "^0.7.1", "@nanostores/react": "^0.7.1",
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/csound": "workspace:*",
"@strudel.cycles/midi": "workspace:*",
"@strudel.cycles/mini": "workspace:*",
"@strudel.cycles/osc": "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/codemirror": "workspace:*", "@strudel/codemirror": "workspace:*",
"@strudel/core": "workspace:*",
"@strudel/csound": "workspace:*",
"@strudel/desktopbridge": "workspace:*", "@strudel/desktopbridge": "workspace:*",
"@strudel/hydra": "workspace:*", "@strudel/hydra": "workspace:*",
"@strudel/midi": "workspace:*",
"@strudel/mini": "workspace:*",
"@strudel/osc": "workspace:*",
"@strudel/repl": "workspace:*", "@strudel/repl": "workspace:*",
"@strudel/serial": "workspace:*",
"@strudel/soundfonts": "workspace:*",
"@strudel/tonal": "workspace:*",
"@strudel/transpiler": "workspace:*",
"@strudel/webaudio": "workspace:*",
"@strudel/xen": "workspace:*",
"@supabase/supabase-js": "^2.39.1", "@supabase/supabase-js": "^2.39.1",
"@tailwindcss/forms": "^0.5.7", "@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.10", "@tailwindcss/typography": "^0.5.10",

View file

@ -1,7 +1,7 @@
--- ---
import type { CollectionEntry } from 'astro:content'; import type { CollectionEntry } from 'astro:content';
type Props = CollectionEntry<'blog'>['data']; type Props = { post: CollectionEntry<'blog'> };
const { post } = Astro.props; const { post } = Astro.props;
const { Content } = await post.render(); const { Content } = await post.render();
@ -9,7 +9,7 @@ import { format } from 'date-fns';
--- ---
<article <article
class="prose max-w-none prose-headings:font-sans prose-headings:font-black prose-headings:text-slate-900 dark:prose-headings:text-gray-200 dark:text-gray-400 dark:prose-strong:text-gray-400 dark:prose-code:text-slate-400 dark:prose-a:text-gray-300 prose-a:text-slate-900 prose-blockquote:text-slate-800 dark:prose-blockquote:text-slate-400" class="prose max-w-none prose-headings:font-sans prose-headings:font-black prose-headings:text-slate-900 dark:prose-headings:text-gray-200 dark:text-gray-400 dark:prose-strong:text-gray-400 dark:prose-code:text-slate-400 dark:prose-a:text-gray-300 prose-a:text-slate-900 prose-blockquote:text-slate-800 dark:prose-blockquote:text-slate-400 border-b-4 border-lineHighlight pt-4"
> >
<div class="pb-2"> <div class="pb-2">
<div class="md:flex justify-between"> <div class="md:flex justify-between">

View file

@ -10,7 +10,7 @@ const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} /> <meta name="generator" content={Astro.generator} />
<link rel="alternate" type="application/rss+xml" title={`RSS Feed for strudel.cc`} href="/rss.xml" />
<link rel="icon" type="image/svg+xml" href={`${baseNoTrailing}/favicon.ico`} /> <link rel="icon" type="image/svg+xml" href={`${baseNoTrailing}/favicon.ico`} />
<meta <meta
@ -24,7 +24,7 @@ const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL
<base href={BASE_URL} /> <base href={BASE_URL} />
<!-- Scrollable a11y code helper --> <!-- Scrollable a11y code helper -->
<script src{`${baseNoTrailing}/make-scrollable-code-focusable.js`} is:inline></script> <script {`${baseNoTrailing}/make-scrollable-code-focusable.js`} is:inline></script>
<script src="/src/pwa.ts"></script> <script src="/src/pwa.ts"></script>
<!-- this does not work for some reason: --> <!-- this does not work for some reason: -->

View file

@ -1,5 +1,5 @@
--- ---
import { SITE, OPEN_GRAPH, Frontmatter } from '../config'; import { SITE, OPEN_GRAPH, type Frontmatter } from '../config';
export interface Props { export interface Props {
frontmatter: Frontmatter; frontmatter: Frontmatter;
@ -25,11 +25,3 @@ const imageAlt = frontmatter.image?.alt ?? OPEN_GRAPH.image.alt;
<meta property="og:image:alt" content={imageAlt} /> <meta property="og:image:alt" content={imageAlt} />
<meta name="description" property="og:description" content={frontmatter.description ?? SITE.description} /> <meta name="description" property="og:description" content={frontmatter.description ?? SITE.description} />
<meta property="og:site_name" content={SITE.title} /> <meta property="og:site_name" content={SITE.title} />
<!-- Twitter Tags -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content={OPEN_GRAPH.twitter} />
<meta name="twitter:title" content={formattedContentTitle} />
<meta name="twitter:description" content={frontmatter.description ?? SITE.description} />
<meta name="twitter:image" content={canonicalImageSrc} />
<meta name="twitter:image:alt" content={imageAlt} />

View file

@ -1,7 +1,7 @@
import useEvent from '@src/useEvent.mjs'; import useEvent from '@src/useEvent.mjs';
import useFrame from '@src/useFrame.mjs'; import useFrame from '@src/useFrame.mjs';
import { getAudioContext } from '@strudel.cycles/webaudio'; import { getAudioContext } from '@strudel/webaudio';
import { midi2note } from '@strudel.cycles/core'; import { midi2note } from '@strudel/core';
import { useState, useRef, useEffect } from 'react'; import { useState, useRef, useEffect } from 'react';
import Claviature from '@components/Claviature'; import Claviature from '@components/Claviature';

View file

@ -1,4 +1,4 @@
import { colorMap } from '@strudel.cycles/core/color.mjs'; import { colorMap } from '@strudel/core/color.mjs';
import React from 'react'; import React from 'react';
const Colors = () => { const Colors = () => {

View file

@ -1,4 +1,19 @@
export function Icon({ type }) { export function Icon({ type }) {
if (type === 'skip') {
// !Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.
return (
<svg
fillRule="evenodd"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
height="16"
width="10"
viewBox="0 0 320 512"
>
<path d="M52.5 440.6c-9.5 7.9-22.8 9.7-34.1 4.4S0 428.4 0 416V96C0 83.6 7.2 72.3 18.4 67s24.5-3.6 34.1 4.4l192 160L256 241V96c0-17.7 14.3-32 32-32s32 14.3 32 32V416c0 17.7-14.3 32-32 32s-32-14.3-32-32V271l-11.5 9.6-192 160z" />
</svg>
);
}
return ( return (
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
{ {
@ -31,6 +46,13 @@ export function Icon({ type }) {
clipRule="evenodd" clipRule="evenodd"
/> />
), ),
skip: (
<path
fillRule="evenodd"
d="M52.5 440.6c-9.5 7.9-22.8 9.7-34.1 4.4S0 428.4 0 416V96C0 83.6 7.2 72.3 18.4 67s24.5-3.6 34.1 4.4l192 160L256 241V96c0-17.7 14.3-32 32-32s32 14.3 32 32V416c0 17.7-14.3 32-32 32s-32-14.3-32-32V271l-11.5 9.6-192 160z"
clipRule="evenodd"
/>
),
}[type] }[type]
} }
</svg> </svg>

View file

@ -1,8 +1,8 @@
import { useState, useRef, useCallback, useMemo, useEffect } from 'react'; import { useState, useRef, useCallback, useMemo, useEffect } from 'react';
import { Icon } from './Icon'; import { Icon } from './Icon';
import { silence, getPunchcardPainter, noteToMidi } from '@strudel.cycles/core'; import { silence, getPunchcardPainter, noteToMidi, _mod } from '@strudel/core';
import { transpiler } from '@strudel.cycles/transpiler'; import { transpiler } from '@strudel/transpiler';
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio'; import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel/webaudio';
import { StrudelMirror } from '@strudel/codemirror'; import { StrudelMirror } from '@strudel/codemirror';
// import { prebake } from '@strudel/repl'; // import { prebake } from '@strudel/repl';
import { prebake } from '../repl/prebake.mjs'; import { prebake } from '../repl/prebake.mjs';
@ -10,14 +10,16 @@ import { loadModules } from '../repl/util.mjs';
import Claviature from '@components/Claviature'; import Claviature from '@components/Claviature';
import useClient from '@src/useClient.mjs'; import useClient from '@src/useClient.mjs';
let prebaked, modulesLoading; let prebaked, modulesLoading, audioLoading;
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
prebaked = prebake(); prebaked = prebake();
modulesLoading = loadModules(); modulesLoading = loadModules();
audioLoading = initAudioOnFirstClick();
} }
export function MiniRepl({ export function MiniRepl({
tune: code, tune,
tunes,
hideHeader = false, hideHeader = false,
canvasHeight = 100, canvasHeight = 100,
onTrigger, onTrigger,
@ -26,6 +28,7 @@ export function MiniRepl({
claviature, claviature,
claviatureLabels, claviatureLabels,
}) { }) {
const code = tunes ? tunes[0] : tune;
const id = useMemo(() => s4(), []); const id = useMemo(() => s4(), []);
const canvasId = useMemo(() => `canvas-${id}`, [id]); const canvasId = useMemo(() => `canvas-${id}`, [id]);
const shouldDraw = !!punchcard || !!claviature; const shouldDraw = !!punchcard || !!claviature;
@ -75,7 +78,7 @@ export function MiniRepl({
} }
return pat; return pat;
}, },
prebake: async () => Promise.all([modulesLoading, prebaked]), prebake: async () => Promise.all([modulesLoading, prebaked, audioLoading]),
onUpdateState: (state) => { onUpdateState: (state) => {
setReplState({ ...state }); setReplState({ ...state });
}, },
@ -91,6 +94,14 @@ export function MiniRepl({
const containerRef = useRef(); const containerRef = useRef();
const client = useClient(); const client = useClient();
const [tuneIndex, setTuneIndex] = useState(0);
const changeTune = (index) => {
index = _mod(index, tunes.length);
setTuneIndex(index);
editorRef.current?.setCode(tunes[index]);
editorRef.current?.evaluate();
};
if (!client) { if (!client) {
return <pre>{code}</pre>; return <pre>{code}</pre>;
} }
@ -119,6 +130,28 @@ export function MiniRepl({
<Icon type="refresh" /> <Icon type="refresh" />
</button> </button>
</div> </div>
{tunes && (
<div className="flex">
<button
className={
'cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground bg-lineHighlight hover:bg-background'
}
onClick={() => changeTune(tuneIndex - 1)}
>
<div className="rotate-180">
<Icon type="skip" />
</div>
</button>
<button
className={
'cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground bg-lineHighlight hover:bg-background'
}
onClick={() => changeTune(tuneIndex + 1)}
>
<Icon type="skip" />
</button>
</div>
)}
</div> </div>
)} )}
<div className="overflow-auto relative p-1"> <div className="overflow-auto relative p-1">

81
website/src/examples.mjs Normal file
View file

@ -0,0 +1,81 @@
export const examples = [
`// "coastline" @by eddyflux
await samples('github:eddyflux/crate')
setcps(.75)
let chords = chord("<Bbm9 Fm9>/4").dict('ireal')
stack(
stack( // DRUMS
s("bd").struct("<[x*<1 2> [~@3 x]] x>"),
s("~ [rim, sd:<2 3>]").room("<0 .2>"),
n("[0 <1 3>]*<2!3 4>").s("hh"),
s("rd:<1!3 2>*2").mask("<0 0 1 1>/16").gain(.5)
).bank('crate')
.mask("<[0 1] 1 1 1>/16".early(.5))
, // CHORDS
chords.offset(-1).voicing().s("gm_epiano1:1")
.phaser(4).room(.5)
, // MELODY
n("<0!3 1*2>").set(chords).mode("root:g2")
.voicing().s("gm_acoustic_bass"),
chords.n("[0 <4 3 <2 5>>*2](<3 5>,8)")
.set(x).anchor("D5").voicing()
.segment(4).clip(rand.range(.4,.8))
.room(.75).shape(.3).delay(.25)
.fm(sine.range(3,8).slow(8))
.lpf(sine.range(500,1000).slow(8)).lpq(5)
.rarely(ply("2")).chunk(4, fast(2))
.gain(perlin.range(.6, .9))
.mask("<0 1 1 0>/16")
)
.late("[0 .01]*4").late("[0 .01]*2").size(4)`,
`// "broken cut 1" @by froos
await samples('github:tidalcycles/Dirt-Samples/master')
samples({
'slap': 'https://cdn.freesound.org/previews/495/495416_10350281-lq.mp3',
'whirl': 'https://cdn.freesound.org/previews/495/495313_10350281-lq.mp3',
'attack': 'https://cdn.freesound.org/previews/494/494947_10350281-lq.mp3'
})
setcps(1.25)
note("[c2 ~](3,8)*2,eb,g,bb,d").s("sawtooth")
.noise(0.3)
.lpf(perlin.range(800,2000).mul(0.6))
.lpenv(perlin.range(1,5)).lpa(.25).lpd(.1).lps(0)
.add.mix(note("<0!3 [1 <4!3 12>]>")).late(.5)
.vib("4:.2")
.room(1).roomsize(4).slow(4)
.stack(
s("bd").late("<0.01 .251>"),
s("breaks165:1/2").fit()
.chop(4).sometimesBy(.4, ply("2"))
.sometimesBy(.1, ply("4")).release(.01)
.gain(1.5).sometimes(mul(speed("1.05"))).cut(1)
,
s("<whirl attack>?").delay(".8:.1:.8").room(2).slow(8).cut(2),
).reset("<x@30 [x*[8 [8 [16 32]]]]@2>".late(2))`,
`// "acidic tooth" @by eddyflux
setcps(1)
stack(
note("[<g1 f1>/8](<3 5>,8)")
.clip(perlin.range(.15,1.5))
.release(.1)
.s("sawtooth")
.lpf(sine.range(400,800).slow(16))
.lpq(cosine.range(6,14).slow(3))
.lpenv(sine.mul(4).slow(4))
.lpd(.2).lpa(.02)
.ftype('24db')
.rarely(add(note(12)))
.room(.2).shape(.3).postgain(.5)
.superimpose(x=>x.add(note(12)).delay(.5).bpf(1000))
.gain("[.2 1@3]*2") // fake sidechain
,
stack(
s("bd*2").mask("<0@4 1@16>"),
s("hh*8").gain(saw.mul(saw.fast(2))).clip(sine)
.mask("<0@8 1@16>")
).bank('RolandTR909')
)`,
];

View file

@ -32,7 +32,18 @@ const posts = (await getCollection('blog')).sort((a, b) => compareDesc(a.data.da
<LeftSidebar currentPage={currentPage} /> <LeftSidebar currentPage={currentPage} />
</aside> </aside>
<PageContent> <PageContent>
{posts.map((post) => <BlogPost post={post} />)} <div class="border-b-4 border-lineHighlight py-4">
<h1>Strudel Blog</h1>
<p>
Welcome to the Strudel Blog, where we will keep you updated with the latest changes and things happening
in the strudelsphere. You can subscribe to this blog using <a target="_blank" href="/rss.xml"
>this rss link</a
>
</p>
</div>
<div class="space-y-8">
{posts.map((post) => <BlogPost post={post} />)}
</div>
</PageContent> </PageContent>
<aside class="fixed right-0 h-full overflow-auto pr-4 pl-0 pb-16 hidden xl:block" title="Table of Contents"> <aside class="fixed right-0 h-full overflow-auto pr-4 pl-0 pb-16 hidden xl:block" title="Table of Contents">
<RightSidebar <RightSidebar

View file

@ -4,7 +4,7 @@ layout: ../../../layouts/MainLayout.astro
--- ---
import { MiniRepl } from '@src/docs/MiniRepl'; import { MiniRepl } from '@src/docs/MiniRepl';
import { midi2note } from '@strudel.cycles/core/'; import { midi2note } from '@strudel/core/';
import Box from '@components/Box.astro'; import Box from '@components/Box.astro';
import QA from '@components/QA'; import QA from '@components/QA';

View file

@ -1,6 +1,6 @@
import { createCanvas } from 'canvas'; import { createCanvas } from 'canvas';
import { pianoroll } from '@strudel.cycles/core'; import { pianoroll } from '@strudel/core';
import { evaluate } from '@strudel.cycles/transpiler'; import { evaluate } from '@strudel/transpiler';
import '../../../../test/runtime.mjs'; import '../../../../test/runtime.mjs';
import * as tunes from '../../repl/tunes.mjs'; import * as tunes from '../../repl/tunes.mjs';

View file

@ -138,6 +138,60 @@ There is one filter envelope for each filter type and thus one set of envelope f
<JsDoc client:idle name="lpenv" h={0} /> <JsDoc client:idle name="lpenv" h={0} />
# Pitch Envelope
You can also control the pitch with envelopes!
Pitch envelopes can breathe life into static sounds:
<MiniRepl
client:idle
tune={`n("<-4,0 5 2 1>*<2!3 4>")
.scale("<C F>/8:pentatonic")
.s("gm_electric_guitar_jazz")
.penv("<.5 0 7 -2>*2").vib("4:.1")
.phaser(2).delay(.25).room(.3)
.size(4).fast(.75)`}
/>
You also create some lovely chiptune-style sounds:
<MiniRepl
client:idle
tune={`n(run("<4 8>/16")).jux(rev)
.chord("<C^7 <Db^7 Fm7>>")
.dict('ireal')
.voicing().add(note("<0 1>/8"))
.dec(.1).room(.2)
.segment("<4 [2 8]>")
.penv("<0 <2 -2>>").patt(.02)`}
/>
Let's break down all pitch envelope controls:
## pattack
<JsDoc client:idle name="pattack" h={0} />
## pdecay
<JsDoc client:idle name="pdecay" h={0} />
## prelease
<JsDoc client:idle name="prelease" h={0} />
## penv
<JsDoc client:idle name="penv" h={0} />
## pcurve
<JsDoc client:idle name="pcurve" h={0} />
## panchor
<JsDoc client:idle name="panchor" h={0} />
# Dynamics # Dynamics
## gain ## gain

View file

@ -4,8 +4,6 @@ layout: ../../layouts/MainLayout.astro
--- ---
import { MiniRepl } from '../../docs/MiniRepl'; import { MiniRepl } from '../../docs/MiniRepl';
import { JsDoc } from '../../docs/JsDoc';
import { samples } from '@strudel.cycles/webaudio';
see https://strudel.cc/?zMEo5kowGrFc see https://strudel.cc/?zMEo5kowGrFc

View file

@ -4,8 +4,6 @@ layout: ../../layouts/MainLayout.astro
--- ---
import { MiniRepl } from '../../docs/MiniRepl'; import { MiniRepl } from '../../docs/MiniRepl';
import { JsDoc } from '../../docs/JsDoc';
import { samples } from '@strudel.cycles/webaudio';
Note: Note:

View file

@ -0,0 +1,19 @@
import rss from '@astrojs/rss';
import { getCollection } from 'astro:content';
export async function GET(context) {
const posts = (await getCollection('blog')).filter((p) => !p.data.draft);
const options = {
title: 'Strudel Blog',
description:
'The Strudel Blog will keep you updated with the latest changes and things happening in the strudelsphere.',
site: context.site,
items: posts.map((post) => ({
link: `/blog/#${post.slug}`,
title: post.data.title,
pubDate: post.data.date,
description: post.data.description,
})),
};
return rss(options);
}

View file

@ -1,6 +1,6 @@
import { createCanvas } from 'canvas'; import { createCanvas } from 'canvas';
import { pianoroll } from '@strudel.cycles/core'; import { pianoroll } from '@strudel/core';
import { evaluate } from '@strudel.cycles/transpiler'; import { evaluate } from '@strudel/transpiler';
import '../../../../test/runtime.mjs'; import '../../../../test/runtime.mjs';
import { getMyPatterns } from '../../my_patterns'; import { getMyPatterns } from '../../my_patterns';
@ -14,10 +14,7 @@ export async function GET({ params, request }) {
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
pianoroll({ time: 4, haps, ctx, playhead: 1, fold: 1, background: 'transparent', playheadColor: 'transparent' }); pianoroll({ time: 4, haps, ctx, playhead: 1, fold: 1, background: 'transparent', playheadColor: 'transparent' });
const buffer = canvas.toBuffer('image/png'); const buffer = canvas.toBuffer('image/png');
return { return new Response(buffer);
body: buffer,
encoding: 'binary',
};
} }
export async function getStaticPaths() { export async function getStaticPaths() {
const patterns = await getMyPatterns(); const patterns = await getMyPatterns();

View file

@ -16,16 +16,16 @@ The purpose of the multiple packages is to
## Overview ## Overview
[See the latest published packages on npm](https://www.npmjs.com/search?q=%40strudel.cycles). [See the latest published packages on npm](https://www.npmjs.com/search?q=%40strudel).
Here is an overview of all the packages: Here is an overview of all the packages:
### Essential Packages ### Essential Packages
These package are the most essential. You might want to use all of those if you're using strudel in your project: These package are the most essential. You might want to use all of those if you're using strudel in your project:
- [core](https://github.com/tidalcycles/strudel/tree/main/packages/core#strudelcyclescore): tidal pattern engine with core primitives - [core](https://github.com/tidalcycles/strudel/tree/main/packages/core#strudelcore): tidal pattern engine with core primitives
- [mini](https://github.com/tidalcycles/strudel/tree/main/packages/mini#strudelcyclesmini): mini notation parser + core bindings - [mini](https://github.com/tidalcycles/strudel/tree/main/packages/mini#strudelmini): mini notation parser + core bindings
- [transpiler](https://github.com/tidalcycles/strudel/tree/main/packages/transpiler#strudelcyclestranspiler): user code transpiler. syntax sugar + highlighting - [transpiler](https://github.com/tidalcycles/strudel/tree/main/packages/transpiler#strudeltranspiler): user code transpiler. syntax sugar + highlighting
### Language Extensions ### Language Extensions
@ -38,23 +38,24 @@ These packages extend the pattern language by specific functions
These packages provide bindings for different ways to output strudel patterns: These packages provide bindings for different ways to output strudel patterns:
- [webaudio](https://github.com/tidalcycles/strudel/tree/main/packages/webaudio#strudelcycleswebaudio): the default webaudio output - [webaudio](https://github.com/tidalcycles/strudel/tree/main/packages/webaudio#strudelwebaudio): the default webaudio output
- [osc](https://github.com/tidalcycles/strudel/tree/main/packages/osc#strudelcyclesosc): bindings to communicate via OSC - [osc](https://github.com/tidalcycles/strudel/tree/main/packages/osc#strudelosc): bindings to communicate via OSC
- [midi](https://github.com/tidalcycles/strudel/tree/main/packages/midi#strudelcyclesmidi): webmidi bindings - [midi](https://github.com/tidalcycles/strudel/tree/main/packages/midi#strudelmidi): webmidi bindings
- [csound](https://github.com/tidalcycles/strudel/tree/main/packages/csound#strudelcyclescsound): csound bindings - [csound](https://github.com/tidalcycles/strudel/tree/main/packages/csound#strudelcsound): csound bindings
- [soundfonts](https://github.com/tidalcycles/strudel/tree/main/packages/serial#strudelcyclessoundfonts): Soundfont support - [soundfonts](https://github.com/tidalcycles/strudel/tree/main/packages/serial#strudelsoundfonts): Soundfont support
- [serial](https://github.com/tidalcycles/strudel/tree/main/packages/serial#strudelcyclesserial): webserial bindings - [serial](https://github.com/tidalcycles/strudel/tree/main/packages/serial#strudelserial): webserial bindings
### Others ### Others
- [embed](https://github.com/tidalcycles/strudel/tree/main/packages/embed#strudelcyclesembed): embeddable REPL web component - [embed](https://github.com/tidalcycles/strudel/tree/main/packages/embed#strudelembed): embeddable REPL web component
- [react](https://github.com/tidalcycles/strudel/tree/main/packages/react#strudelcyclesreact): react hooks and components for strudel
### No Longer Maintained ### No Longer Maintained
- [react](https://www.npmjs.com/package/@strudel.cycles/react): react hooks and components for strudel
- [eval](https://www.npmjs.com/package/@strudel.cycles/eval): old code transpiler - [eval](https://www.npmjs.com/package/@strudel.cycles/eval): old code transpiler
- [tone](https://www.npmjs.com/package/@strudel.cycles/tone): bindings for Tone.js instruments and effects - [tone](https://www.npmjs.com/package/@strudel.cycles/tone): bindings for Tone.js instruments and effects
- [webdirt](https://www.npmjs.com/package/@strudel.cycles/webdirt): webdirt bindings, replaced by webaudio package - [webdirt](https://www.npmjs.com/package/@strudel.cycles/webdirt): webdirt bindings, replaced by webaudio package
- any `@strudel.cycles/*` packages have been renamed to `@strudel/*` since version 0.10.0.
## Tools ## Tools

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