Compare commits

..

39 Commits

Author SHA1 Message Date
Felix Roos 3af2210c5e remove pesky ticks and do it right 2022-04-01 17:42:52 +02:00
Felix Roos d6aa73fd28 more lookahead 2022-04-01 16:46:18 +02:00
Felix Roos f0083cfe45 just increment those ticks 2022-04-01 16:34:43 +02:00
Felix Roos 66f32dd678 loosen 2022-04-01 16:25:14 +02:00
Felix Roos 7faf1e1366 remove lastEnd 2022-04-01 16:15:58 +02:00
Felix Roos e6ae16ca51 use ticks 2022-04-01 16:15:13 +02:00
Felix Roos 6af5250501 loosen tolerances at the right spot... 2022-04-01 15:45:12 +02:00
Felix Roos 7c0dd9a6cc loosen tolerances 2022-04-01 15:26:02 +02:00
Felix Roos 5f6b2223e1 fix scheduling issues 2022-04-01 15:11:38 +02:00
Felix Roos 864157ac84 add more web audio stuff 2022-03-31 22:53:56 +02:00
Felix Roos c89ee5ddb3 improve oscillators 2022-03-30 18:21:19 +02:00
Felix Roos 5a255350b4 add scope 2022-03-30 11:56:13 +02:00
Felix Roos 8919524432 clear canvas on update 2022-03-30 11:56:05 +02:00
Felix Roos f79a64de60 add stripped down scheduler example 2022-03-30 00:46:16 +02:00
Felix Roos 3fdeccdb23 add core examples 2022-03-29 22:20:12 +02:00
alex fb52227a92 prettify 2022-03-29 15:29:19 +01:00
Alex McLean e376f25d92 Merge pull request #25 from tidalcycles/Add-chunk-and-chunkBack
Add chunk, chunkBack and iterBack
2022-03-28 16:27:21 +01:00
alex d2a715af0f Add chunk, chunkBack and iterBack 2022-03-28 14:51:13 +01:00
alex 3c230986e7 Expain style and community a little bit. 2022-03-28 13:54:38 +01:00
alex f529d09227 prettify strudel.mjs, plus add top level echo function 2022-03-28 13:37:13 +01:00
Alex McLean b7988f2158 Merge pull request #24 from tidalcycles/packages
packaging
2022-03-28 10:48:21 +01:00
Felix Roos 2a0d8c3f77 remove codesandbox specific line 2022-03-28 00:31:56 +02:00
Felix Roos 429fcaf05a Publish
- @strudel.cycles/midi@0.0.3
 - @strudel.cycles/mini@0.0.3
 - @strudel.cycles/tone@0.0.3
2022-03-28 00:04:23 +02:00
Felix Roos 4603d16162 add midi note 2022-03-27 23:53:31 +02:00
Felix Roos 6d4a7f96eb add tone example 2022-03-27 23:49:56 +02:00
Felix Roos 4c277afea9 add publishing info 2022-03-27 23:45:46 +02:00
Felix Roos 11be5db628 Publish
- @strudel.cycles/core@0.0.2
 - @strudel.cycles/eval@0.0.2
 - @strudel.cycles/midi@0.0.2
 - @strudel.cycles/mini@0.0.2
 - @strudel.cycles/tonal@0.0.2
 - @strudel.cycles/tone@0.0.2
 - @strudel.cycles/xen@0.0.2
2022-03-27 23:44:26 +02:00
Felix Roos 779d303572 simplify import 2022-03-27 23:43:30 +02:00
Felix Roos 788f344012 simplify onEvent call 2022-03-27 23:43:20 +02:00
Felix Roos 33c9326427 add missing dependency 2022-03-27 23:43:06 +02:00
Felix Roos 9ef57152e4 add duration getter 2022-03-27 23:42:47 +02:00
Felix Roos f8f2fb608d add tonal example 2022-03-27 22:49:06 +02:00
Felix Roos 1cf6b37ee4 add mini example 2022-03-27 22:41:37 +02:00
Felix Roos 76822717f4 add eval example 2022-03-27 22:35:55 +02:00
Felix Roos 4142b113e9 add core example 2022-03-27 22:25:48 +02:00
Felix Roos 3c123538e9 repl readme 2022-03-27 22:21:51 +02:00
Felix Roos 73996997e5 add install commands 2022-03-27 22:17:07 +02:00
Felix Roos c4d14594ce remove node 14 test 2022-03-27 22:12:05 +02:00
Felix Roos 657960024d independent mode 2022-03-27 21:52:26 +02:00
31 changed files with 4258 additions and 888 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14, 16, 17]
node-version: [16, 17]
steps:
- uses: actions/checkout@v2
+16
View File
@@ -0,0 +1,16 @@
{
"printWidth": 120,
"useTabs": false,
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"jsxSingleQuote": false,
"trailingComma": "all",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "always",
"proseWrap": "preserve",
"htmlWhitespaceSensitivity": "css",
"endOfLine": "lf"
}
+19 -5
View File
@@ -20,10 +20,24 @@ npm install
npm run start
```
## Build
## Publish Packages
```bash
cd repl
npm run build
npm run static # <- test static build
To publish, just run:
```sh
npx lerna version
```
This will publish all packages that changed since the last version.
## Style
For now, please try to copy the style of surrounding code. VS Code users can install the 'prettier' add-on which will use the .prettierrc configuration file for automatic formatting.
## Community
There is a #strudel channel on the TidalCycles discord: https://discord.com/invite/HGEdXmRkzT
You can also ask questions and find related discussions on the tidal club forum: https://club.tidalcycles.org/
The discord and forum is shared with the haskell (tidal) and python (vortex) siblings of this project.
+1 -1
View File
@@ -2,5 +2,5 @@
"packages": [
"packages/*"
],
"version": "0.0.1"
"version": "independent"
}
+33
View File
@@ -1,3 +1,36 @@
# @strudel.cycles/core
This package contains the bare essence of strudel.
## Install
```sh
npm i @strudel.cycles/core --save
```
## Example
```js
import { sequence, State, TimeSpan } from '@strudel.cycles/core';
const pattern = sequence('a', ['b', 'c']);
const events = pattern.query(new State(new TimeSpan(0, 2)));
const spans = events.map(
(event) => `${event.value}: ${event.whole.begin.toFraction()} - ${event.whole.end.toFraction()} `,
);
```
spans:
```log
a: 0 - 1/2
b: 1/2 - 3/4
c: 3/4 - 1
a: 1 - 3/2
b: 3/2 - 7/4
c: 7/4 - 2
```
[play with @strudel.cycles/core on codesandbox](https://codesandbox.io/s/strudel-core-test-qmz6qr?file=/src/index.js).
+26
View File
@@ -0,0 +1,26 @@
<input
type="text"
id="text"
value="cat('a', 'b')"
style="width: 100%; font-size: 2em; outline: none; margin-bottom: 10px"
spellcheck="false"
/>
<div id="output"></div>
<script type="module">
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.0.2');
Object.assign(window, strudel); // assign all strudel functions to global scope to use with eval
const input = document.getElementById('text');
const getEvents = () => {
const code = document.getElementById('text').value;
const pattern = eval(code);
const events = pattern.firstCycle();
console.log(code, '->', events);
document.getElementById('output').innerHTML = events.map((e) => e.show()).join('<br/>');
};
getEvents();
input.addEventListener('input', () => getEvents());
</script>
<p>
This page shows how skypack can be used to import strudel core directly into a simple html page. <br />
No server, no bundler and no build setup is needed to run this!
</p>
+35
View File
@@ -0,0 +1,35 @@
<body style="margin: 0">
<input
type="text"
id="text"
value="cat('lime', slowcat('steelblue','darkblue'),'yellow','salmon','black').every(2,rev).fast(16)"
style="width: 100%; font-size: 1.5em; background: black; color: white; outline: none; position: absolute; top: 0"
spellcheck="false"
/>
<canvas id="canvas"></canvas>
<script type="module">
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.0.2');
// this adds all strudel functions to the global scope, to be used by eval
Object.assign(window, strudel);
// setup elements
const input = document.getElementById('text');
const canvas = document.getElementById('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const ctx = canvas.getContext('2d');
input.focus(); // autofocus
input.setSelectionRange(input.value.length, input.value.length); // move cursor to end
paint(input.value); // initial paint
input.addEventListener('input', (e) => paint(e.target.value)); // repaint on input
function paint(code) {
const pattern = eval(code); // run code
const events = pattern.firstCycle(); // query first cycle
ctx.clearRect(0, 0, canvas.width, canvas.height); // clear canvas
events.forEach((event) => {
ctx.fillStyle = event.value;
ctx.fillRect(event.whole.begin * canvas.width, 0, event.duration * canvas.width, canvas.height);
});
}
</script>
</body>
+237
View File
@@ -0,0 +1,237 @@
<div style="position: absolute; top: 0; right: 0; padding: 4px">
<button id="start" style="margin-bottom: 4px; font-size: 2em">start</button><br />
<button id="stop" style="font-size: 2em">stop</button>
</div>
<textarea
style="font-size: 2em; background: #e8d565; color: #323230; height: 100%; width: 100%; outline: none; border: 0"
id="text"
spellcheck="false"
>
Loading...</textarea
>
<script type="module">
document.body.style = 'margin: 0';
import * as strudel from 'https://cdn.skypack.dev/@strudel.cycles/core@0.0.2';
import 'https://cdn.skypack.dev/@strudel.cycles/core@0.0.2/euclid.mjs';
const { cat, State, TimeSpan } = strudel;
let pattern;
Object.assign(window, strudel); // add strudel to eval scope
const input = document.getElementById('text');
let initialCode = `sequence(880, [440, 660], 440, 660)
.div(slowcat(3,2).slow(2))
.off(1/8,mul(2))
.off(1/4,mul(3))
.legato(2)
.slow(2)`;
try {
initialCode = atob(decodeURIComponent(window.location.href.split('#')[1]));
} catch (err) {
console.warn('failed to decode', err);
}
input.value = initialCode;
const evaluate = () => {
try {
pattern = eval(input.value);
window.location.hash = '#' + encodeURIComponent(btoa(input.value)); // update url hash
} catch (err) {
console.warn(err);
}
};
input.addEventListener('input', () => evaluate());
// helpers to create a worker dynamically without needing a server / extra file
const stringifyFunction = (func) => '(' + func + ')();';
const urlifyFunction = (func) =>
URL.createObjectURL(new Blob([stringifyFunction(func)], { type: 'text/javascript' }));
const createWorker = (func) => new Worker(urlifyFunction(func));
const interval = 0.1;
const lookahead = 0.5;
// this class is basically the tale of two clocks
class Metro {
worker;
audioContext;
interval = 0.2; // query span
lastEnd = 0;
constructor(audioContext, callback, interval = this.interval) {
this.audioContext = audioContext;
this.interval = interval;
this.worker = createWorker(() => {
// we cannot use closures here!
let interval;
let timerID = null; // this is clock #1 (the sloppy js clock)
const clear = () => {
if (timerID) {
clearInterval(timerID);
timerID = null;
}
};
const start = () => {
clear();
if (!interval) {
throw new Error('no interval set! call worker.postMessage({interval}) before starting.');
}
timerID = setInterval(() => postMessage('tick'), interval * 1000);
};
self.onmessage = function (e) {
if (e.data == 'start') {
start();
} else if (e.data.interval) {
interval = e.data.interval;
if (timerID) {
start();
}
} else if (e.data == 'stop') {
clear();
}
};
});
this.worker.postMessage({ interval });
const round = (n, d) => Math.round(n * d) / d;
const precision = 100;
this.worker.onmessage = (e) => {
if (e.data === 'tick') {
const begin = this.lastEnd || this.audioContext.currentTime;
const end = this.audioContext.currentTime + this.interval; // DONT reference begin here!
this.lastEnd = end;
// callback with query span, using clock #2 (the audio clock)
callback(begin, end);
}
};
}
start() {
console.log('start...');
this.audioContext.resume();
this.worker.postMessage('start');
}
stop() {
console.log('stop...');
this.worker.postMessage('stop');
}
}
const audioContext = new AudioContext();
const metro = new Metro(
audioContext,
(begin, end) => {
pattern.query(new State(new TimeSpan(begin, end))).forEach((e) => {
if (!e.part.begin.equals(e.whole.begin)) {
return;
}
if (e.context.createAudioNode) {
e.context.createAudioNode(e);
} else {
// fallback sine wave
const attack = 0.01;
const decay = 0.05;
const sustain = 0.5;
const release = 0.01;
const velocity = (e.context?.velocity || 1) * 0.1;
const osc = audioContext.createOscillator();
osc.type = 'sine';
osc.frequency.value = e.value;
osc.start(e.whole.begin);
const envelope = adsr(attack, decay, sustain, release, velocity, e.whole.begin, e.whole.end);
osc.stop(e.whole.end + release);
osc.connect(envelope);
envelope.connect(audioContext.destination);
}
});
},
interval,
lookahead,
);
const adsr = (attack, decay, sustain, release, velocity, begin, end) => {
const gainNode = audioContext.createGain();
gainNode.gain.setValueAtTime(0, begin);
gainNode.gain.linearRampToValueAtTime(velocity, begin + attack); // attack
gainNode.gain.linearRampToValueAtTime(sustain * velocity, begin + attack + decay); // sustain start
gainNode.gain.setValueAtTime(sustain * velocity, end); // sustain end
gainNode.gain.linearRampToValueAtTime(0, end + release); // release
// for some reason, using exponential ramping creates little cracklings
return gainNode;
};
strudel.Pattern.prototype.withAudioNode = function (createAudioNode) {
return this._withEvent((event) => {
return event.setContext({
...event.context,
createAudioNode: (e) => createAudioNode(e, event.context.createAudioNode?.(event)),
});
});
};
strudel.Pattern.prototype._osc = function (type) {
return this.withAudioNode((e) => {
const osc = audioContext.createOscillator();
osc.type = type;
osc.frequency.value = e.value; // expects frequency..
osc.start(e.whole.begin.valueOf() + lookahead);
osc.stop(e.whole.end.valueOf() + lookahead); // release?
// osc.connect(audioContext.destination);
return osc;
});
};
strudel.Pattern.prototype.define('osc', (type, pat) => pat.osc(type), { patternified: true });
strudel.Pattern.prototype.adsr = function (a = 0.01, d = 0.05, s = 1, r = 0.01) {
return this.withAudioNode((e, node) => {
const velocity = e.context?.velocity || 1;
const envelope = adsr(
a,
d,
s,
r,
velocity,
e.whole.begin.valueOf() + lookahead,
e.whole.end.valueOf() + lookahead,
);
node?.connect(envelope);
return envelope;
});
};
strudel.Pattern.prototype.filter = function (type = 'lowshelf', frequency = 1000, gain = 25) {
return this.withAudioNode((e, node) => {
const filter = audioContext.createBiquadFilter();
filter.type = type;
filter.frequency.value = frequency;
filter.gain.value = gain;
node?.connect(filter);
return filter;
});
};
strudel.Pattern.prototype.out = function () {
const master = audioContext.createGain();
master.gain.value = 0.1;
master.connect(audioContext.destination);
return this.withAudioNode((e, node) => {
if (!node) {
console.warn('out: no source! call .osc() first');
}
node?.connect(master);
});
};
document.getElementById('start').addEventListener('click', () => metro.start());
document.getElementById('stop').addEventListener('click', () => metro.stop());
evaluate(); // evaluate initial code
/*
sequence(1,3/2,1,2).stack(
stack(5,3,4).velocity(.8).slow(2).legato(.2)
//.echo(3, 1/4, .5)
).mul(110) // frequencies
.div(slowcat(slowcat(2,5/4, 3/2),slowcat(3,4/3)).slow(2))
.echoWith(
16, // n of partials / cutoff
tri2.slow(128).div(2), // time between partials
(x,n)=>x // n = partial index 0..n
.mul((n+1)*3/2)
.legato(2)
.velocity(1/((n+1)**1.6)) // amplitude falloff
).legato(2) // note length
*/
</script>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/core",
"version": "0.0.1",
"version": "0.0.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/core",
"version": "0.0.1",
"version": "0.0.2",
"description": "Port of Tidal Cycles to JavaScript",
"main": "strudel.mjs",
"type": "module",
+938 -817
View File
File diff suppressed because it is too large Load Diff
+33
View File
@@ -3,6 +3,39 @@
This package contains the strudel code transformer and evaluator.
It allows creating strudel patterns from input code that is optimized for minimal keystrokes and human readability.
## Install
```sh
npm i @strudel.cycles/eval --save
```
## Example
```js
import { evaluate, extend } from '@strudel.cycles/eval';
import * as strudel from '@strudel.cycles/core';
extend(strudel); // add strudel to eval scope
async function run(code) {
const { pattern } = await evaluate(code);
const events = pattern.firstCycle();
console.log(events.map((e) => e.show()).join('\n'));
}
run('sequence([a3, [b3, c4]])');
```
yields:
```js
(0/1 -> 1/2, 0/1 -> 1/2, a3)
(1/2 -> 3/4, 1/2 -> 3/4, b3)
(3/4 -> 1/1, 3/4 -> 1/1, c4)
```
[play with @strudel.cycles/eval on codesandbox](https://codesandbox.io/s/strudel-eval-example-ndz1d8?file=/src/index.js)
## Dev Notes
shift-traverser is currently monkey patched because its package.json uses estraverse@^4.2.0,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/eval",
"version": "0.0.1",
"version": "0.0.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/eval",
"version": "0.0.1",
"version": "0.0.2",
"description": "Code evaluator for strudel",
"main": "evaluate.mjs",
"directories": {
@@ -28,7 +28,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.0.1",
"@strudel.cycles/core": "^0.0.2",
"estraverse": "^5.3.0",
"shift-ast": "^6.1.0",
"shift-codegen": "^7.0.3",
+10
View File
@@ -1,3 +1,13 @@
# @strudel.cycles/midi
This package adds midi functionality to strudel Patterns.
## Install
```sh
npm i @strudel.cycles/midi --save
```
## Dev Notes
- is this package really necessary? currently, /tone also depends on webmidi through @tonejs/piano. Either move piano out of /tone or merge /midi into /tone...
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/midi",
"version": "0.0.1",
"version": "0.0.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/midi",
"version": "0.0.1",
"version": "0.0.3",
"description": "Midi API for strudel",
"main": "midi.mjs",
"repository": {
@@ -21,7 +21,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/tone": "^0.0.1",
"@strudel.cycles/tone": "^0.0.3",
"tone": "^14.7.77",
"webmidi": "^2.5.2"
}
+32
View File
@@ -1,3 +1,35 @@
# @strudel.cycles/mini
This package contains the mini notation parser and pattern generator.
## Install
```sh
npm i @strudel.cycles/mini --save
```
## Example
```js
import { mini } from '@strudel.cycles/mini';
const pattern = mini('a [b c*2]');
const events = pattern.firstCycle().map((e) => e.show());
console.log(events);
```
yields:
```log
(0/1 -> 1/2, 0/1 -> 1/2, a)
(1/2 -> 3/4, 1/2 -> 3/4, b)
(3/4 -> 7/8, 3/4 -> 7/8, 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)
## Mini Notation API
See "Mini Notation" in the [Strudel Tutorial](https://strudel.tidalcycles.org/tutorial/)
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/mini",
"version": "0.0.1",
"version": "0.0.3",
"description": "Mini notation for strudel",
"main": "mini.mjs",
"type": "module",
@@ -25,7 +25,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/eval": "^0.0.1",
"@strudel.cycles/tone": "^0.0.1"
"@strudel.cycles/eval": "^0.0.2",
"@strudel.cycles/tone": "^0.0.3"
}
}
+31
View File
@@ -1,3 +1,34 @@
# @strudel.cycles/tonal
This package adds tonal / harmonic functions to strudel Patterns.
## Install
```sh
npm i @strudel.cycles/tonal --save
```
## Example
```js
import { sequence } from '@strudel.cycles/core';
import '@strudel.cycles/tonal';
const pattern = sequence(0, [1, 2]).scale('C major');
const events = pattern.firstCycle().map((e) => e.show());
```
yields:
```js
(0/1 -> 1/2, 0/1 -> 1/2, C3)
(1/2 -> 3/4, 1/2 -> 3/4, D3)
(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)
## Tonal API
See "Tonal API" in the [Strudel Tutorial](https://strudel.tidalcycles.org/tutorial/)
+2756
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/tonal",
"version": "0.0.1",
"version": "0.0.2",
"description": "Tonal functions for strudel",
"main": "tonal.mjs",
"type": "module",
@@ -25,6 +25,8 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.0.1"
"@strudel.cycles/core": "^0.0.2",
"@tonaljs/tonal": "^4.6.5",
"webmidi": "^3.0.15"
}
}
+1 -3
View File
@@ -1,9 +1,7 @@
import { Note, Interval, Scale } from '@tonaljs/tonal';
import { Pattern as _Pattern } from '@strudel.cycles/core/strudel.mjs';
import { Pattern } from '@strudel.cycles/core';
import { mod } from '@strudel.cycles/core/util.mjs';
const Pattern = _Pattern; // as any;
// transpose note inside scale by offset steps
// function scaleTranspose(scale: string, offset: number, note: string) {
export function scaleTranspose(scale, offset, note) {
+33
View File
@@ -2,6 +2,39 @@
This package adds Tone.js functions to strudel Patterns.
## Install
```sh
npm i @strudel.cycles/tone --save
```
## Example
The following example will create a pattern and play it back with tone.js:
```js
import { sequence, stack, State, TimeSpan } from '@strudel.cycles/core';
import { Tone, polysynth, osc, out } from '@strudel.cycles/tone';
const pattern = sequence('c3', ['eb3', stack('g3', 'bb3')]).tone(polysynth().set(osc('sawtooth4')).chain(out()));
document.getElementById('play').addEventListener('click', async () => {
await Tone.start();
Tone.getTransport().stop();
const events = pattern.query(new State(new TimeSpan(0, 4))).filter((e) => e.whole.begin.equals(e.part.begin));
events.forEach((event) =>
Tone.getTransport().schedule((time) => event.context.onTrigger(time, event), event.whole.begin.valueOf()),
);
Tone.getTransport().start('+0.1');
});
```
[open in codesandbox](https://codesandbox.io/s/strudel-tone-example-5ph2te?file=/src/index.js:0-708)
## API
See "Tone API" in the [Strudel Tutorial](https://strudel.tidalcycles.org/tutorial/)
## Dev Notes
- `@tonejs/piano` has peer dependency on `webmidi@^2.5.1`! A newer version of webmidi will break.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/tone",
"version": "0.0.1",
"version": "0.0.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/tone",
"version": "0.0.1",
"version": "0.0.3",
"description": "Tone.js API for strudel",
"main": "tone.mjs",
"type": "module",
@@ -22,7 +22,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.0.1",
"@strudel.cycles/core": "^0.0.2",
"@tonejs/piano": "^0.2.1",
"chord-voicings": "^0.0.1",
"tone": "^14.7.77"
+1 -34
View File
@@ -1,4 +1,4 @@
import { Pattern as _Pattern } from '@strudel.cycles/core';
import { Pattern } from '@strudel.cycles/core';
import * as _Tone from 'tone';
// import Tone from here, to make sure to get the same AudioContext
@@ -41,11 +41,8 @@ defaultSynth.set({
// what about
// https://www.charlie-roberts.com/gibberish/playground/
const Pattern = _Pattern;
// with this function, you can play the pattern with any tone synth
Pattern.prototype.tone = function (instrument) {
// instrument.toDestination();
return this._withEvent((event) => {
const onTrigger = (time, event) => {
let note;
@@ -74,36 +71,6 @@ Pattern.prototype.tone = function (instrument) {
note = getPlayableNoteValue(event);
instrument.triggerAttackRelease(note, event.duration, time, velocity);
}
/* switch (instrument.constructor.name) {
case 'PluckSynth':
note = getPlayableNoteValue(event);
instrument.triggerAttack(note, time);
break;
case 'NoiseSynth':
instrument.triggerAttackRelease(event.duration, time); // noise has no value
break;
case 'Piano':
note = getPlayableNoteValue(event);
instrument.keyDown({ note, time, velocity });
instrument.keyUp({ note, time: time + event.duration, velocity });
break;
case 'Sampler':
note = getPlayableNoteValue(event);
instrument.triggerAttackRelease(note, event.duration, time, velocity);
break;
case 'Players':
if (!instrument.has(event.value)) {
throw new Error(`name "${event.value}" not defined for players`);
}
const player = instrument.player(event.value);
// velocity ?
player.start(time);
player.stop(time + event.duration);
break;
default:
note = getPlayableNoteValue(event);
instrument.triggerAttackRelease(note, event.duration, time, velocity);
} */
};
return event.setContext({ ...event.context, instrument, onTrigger });
});
+6
View File
@@ -1,3 +1,9 @@
# @strudel.cycles/xen
This package adds xenharmonic / microtonal functions to strudel Patterns.
## Install
```sh
npm i @strudel.cycles/xen --save
```
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/xen",
"version": "0.0.1",
"version": "0.0.2",
"description": "Xenharmonic API for strudel",
"main": "xen.mjs",
"scripts": {
@@ -24,6 +24,6 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.0.1"
"@strudel.cycles/core": "^0.0.2"
}
}
+27 -1
View File
@@ -1,6 +1,32 @@
# Strudel REPL
TBD
This is the REPL for Strudel. REPL stands for
- Read
- Evaluate
- Play!
- Loop
The REPL is deployed at [strudel.tidalcycles.org](https://strudel.tidalcycles.org/).
## Run REPL locally
```bash
# from project root
npm install
npx lerna bootstrap
cd repl
npm install
npm run start
```
## Build REPL
```bash
cd repl
npm run build # <- builds repl + tutorial to ../docs
npm run static # <- test static build
```
## Dev Notes
+2 -8
View File
@@ -42,16 +42,10 @@ function useCycle(props) {
?.filter((event) => event.part.begin.equals(event.whole.begin))
.forEach((event) => {
Tone.getTransport().schedule((time) => {
const toneEvent = {
time: event.whole.begin.valueOf(),
duration: event.whole.end.sub(event.whole.begin).valueOf(),
value: event.value,
context: event.context,
};
onEvent(time, toneEvent);
onEvent(time, event);
Tone.Draw.schedule(() => {
// do drawing or DOM manipulation here
onDraw?.(time, toneEvent);
onDraw?.(time, event);
}, time);
}, event.part.begin.valueOf());
});