Compare commits

..

10 Commits

Author SHA1 Message Date
Felix Roos cd1c4632d7 build to single file 2022-11-23 14:00:31 +01:00
Felix Roos f1a850e133 forgot tonal pkg import 2022-11-22 21:43:30 +01:00
Felix Roos 53b61b8d85 add workshop seed project 2022-11-22 21:33:47 +01:00
Alex McLean e1a532500e Tidying up core (#256)
* remove _ prefixes except for functions to be patternified
* categorise pattern methods
* experimental support for `.add.squeeze` and friends as alternative to `.addSqueeze`
* `every` is now an alias for `firstOf` with additional `lastOf` (which every will become an alias for next)
2022-11-22 08:51:25 +00:00
Felix Roos 4cf412b93d Merge pull request #266 from tidalcycles/faster-fast
fix performance bottleneck
2022-11-21 22:15:48 +01:00
Felix Roos 54c9c434e0 fix examples snapshot 2022-11-21 22:09:26 +01:00
Felix Roos de19f3e5fe fix tune snapshots 2022-11-21 22:08:17 +01:00
Felix Roos 8304993481 fix: #194 2022-11-21 21:59:08 +01:00
Felix Roos 4c838aeaca hotfix: weird audion bug 2022-11-17 11:13:49 +01:00
Felix Roos 547d925065 Merge pull request #263 from tidalcycles/fix-tutorial-bugs
fix tutorial bugs
2022-11-17 11:08:37 +01:00
49 changed files with 8790 additions and 2449 deletions
+1 -1
View File
@@ -74,7 +74,7 @@ const fraction = (n) => {
-> those farey sequences turn out to make pattern querying ~20 times slower! always use strings! -> those farey sequences turn out to make pattern querying ~20 times slower! always use strings!
-> still, some optimizations could be done: .mul .div .add .sub calls still use numbers -> still, some optimizations could be done: .mul .div .add .sub calls still use numbers
*/ */
n = String(n); // n = String(n); // this is actually faster but imprecise...
} }
return Fraction(n); return Fraction(n);
}; };
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/core", "name": "@strudel.cycles/core",
"version": "0.4.1", "version": "0.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/core", "name": "@strudel.cycles/core",
"version": "0.4.1", "version": "0.4.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",
+629 -559
View File
File diff suppressed because it is too large Load Diff
+7 -7
View File
@@ -20,7 +20,7 @@ export const signal = (func) => {
}; };
export const isaw = signal((t) => 1 - (t % 1)); export const isaw = signal((t) => 1 - (t % 1));
export const isaw2 = isaw._toBipolar(); export const isaw2 = isaw.toBipolar();
/** /**
* A sawtooth signal between 0 and 1. * A sawtooth signal between 0 and 1.
@@ -33,7 +33,7 @@ export const isaw2 = isaw._toBipolar();
* *
*/ */
export const saw = signal((t) => t % 1); export const saw = signal((t) => t % 1);
export const saw2 = saw._toBipolar(); export const saw2 = saw.toBipolar();
export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t)); export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t));
@@ -45,7 +45,7 @@ export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t));
* sine.segment(16).range(0,15).slow(2).scale('C minor').note() * sine.segment(16).range(0,15).slow(2).scale('C minor').note()
* *
*/ */
export const sine = sine2._fromBipolar(); export const sine = sine2.fromBipolar();
/** /**
* A cosine signal between 0 and 1. * A cosine signal between 0 and 1.
@@ -67,7 +67,7 @@ export const cosine2 = sine2._early(Fraction(1).div(4));
* *
*/ */
export const square = signal((t) => Math.floor((t * 2) % 2)); export const square = signal((t) => Math.floor((t * 2) % 2));
export const square2 = square._toBipolar(); export const square2 = square.toBipolar();
/** /**
* A triangle signal between 0 and 1. * A triangle signal between 0 and 1.
@@ -127,7 +127,7 @@ export const rand = signal(timeToRand);
/** /**
* A continuous pattern of random numbers, between -1 and 1 * A continuous pattern of random numbers, between -1 and 1
*/ */
export const rand2 = rand._toBipolar(); export const rand2 = rand.toBipolar();
export const _brandBy = (p) => rand.fmap((x) => x < p); export const _brandBy = (p) => rand.fmap((x) => x < p);
export const brandBy = (pPat) => reify(pPat).fmap(_brandBy).innerJoin(); export const brandBy = (pPat) => reify(pPat).fmap(_brandBy).innerJoin();
@@ -201,7 +201,7 @@ Pattern.prototype.choose = function (...xs) {
* @returns {Pattern} * @returns {Pattern}
*/ */
Pattern.prototype.choose2 = function (...xs) { Pattern.prototype.choose2 = function (...xs) {
return chooseWith(this._fromBipolar(), xs); return chooseWith(this.fromBipolar(), xs);
}; };
/** /**
@@ -259,7 +259,7 @@ export const perlinWith = (pat) => {
export const perlin = perlinWith(time.fmap((v) => Number(v))); export const perlin = perlinWith(time.fmap((v) => Number(v)));
Pattern.prototype._degradeByWith = function (withPat, x) { Pattern.prototype._degradeByWith = function (withPat, x) {
return this.fmap((a) => (_) => a).appLeft(withPat._filterValues((v) => v > x)); return this.fmap((a) => (_) => a).appLeft(withPat.filterValues((v) => v > x));
}; };
/** /**
+32 -25
View File
@@ -49,7 +49,7 @@ import { steady } from '../signal.mjs';
import controls from '../controls.mjs'; import controls from '../controls.mjs';
const { n } = controls; const { n, s } = controls;
const st = (begin, end) => new State(ts(begin, end)); const st = (begin, end) => new State(ts(begin, end));
const ts = (begin, end) => new TimeSpan(Fraction(begin), Fraction(end)); const ts = (begin, end) => new TimeSpan(Fraction(begin), Fraction(end));
const hap = (whole, part, value, context = {}) => new Hap(whole, part, value, context); const hap = (whole, part, value, context = {}) => new Hap(whole, part, value, context);
@@ -58,7 +58,7 @@ const third = Fraction(1, 3);
const twothirds = Fraction(2, 3); const twothirds = Fraction(2, 3);
const sameFirst = (a, b) => { const sameFirst = (a, b) => {
return expect(a._sortHapsByPart().firstCycle()).toStrictEqual(b._sortHapsByPart().firstCycle()); return expect(a.sortHapsByPart().firstCycle()).toStrictEqual(b.sortHapsByPart().firstCycle());
}; };
describe('TimeSpan', () => { describe('TimeSpan', () => {
@@ -382,7 +382,7 @@ describe('Pattern', () => {
); );
}); });
it('copes with breaking up events across cycles', () => { it('copes with breaking up events across cycles', () => {
expect(pure('a').slow(2)._fastGap(2)._setContext({}).query(st(0, 2))).toStrictEqual([ expect(pure('a').slow(2)._fastGap(2).setContext({}).query(st(0, 2))).toStrictEqual([
hap(ts(0, 1), ts(0, 0.5), 'a'), hap(ts(0, 1), ts(0, 0.5), 'a'),
hap(ts(0.5, 1.5), ts(1, 1.5), 'a'), hap(ts(0.5, 1.5), ts(1, 1.5), 'a'),
]); ]);
@@ -446,8 +446,8 @@ describe('Pattern', () => {
}); });
describe('slow()', () => { describe('slow()', () => {
it('Supports zero-length queries', () => { it('Supports zero-length queries', () => {
expect(steady('a').slow(1)._setContext({}).queryArc(0, 0)).toStrictEqual( expect(steady('a').slow(1).setContext({}).queryArc(0, 0)).toStrictEqual(
steady('a')._setContext({}).queryArc(0, 0), steady('a').setContext({}).queryArc(0, 0),
); );
}); });
}); });
@@ -465,7 +465,7 @@ describe('Pattern', () => {
it('Filters true', () => { it('Filters true', () => {
expect( expect(
pure(true) pure(true)
._filterValues((x) => x) .filterValues((x) => x)
.firstCycle().length, .firstCycle().length,
).toBe(1); ).toBe(1);
}); });
@@ -490,7 +490,7 @@ describe('Pattern', () => {
pure(10) pure(10)
.when(slowcat(true, false), (x) => x.add(3)) .when(slowcat(true, false), (x) => x.add(3))
.fast(4) .fast(4)
._sortHapsByPart() .sortHapsByPart()
.firstCycle(), .firstCycle(),
).toStrictEqual(fastcat(13, 10, 13, 10).firstCycle()); ).toStrictEqual(fastcat(13, 10, 13, 10).firstCycle());
}); });
@@ -577,26 +577,26 @@ describe('Pattern', () => {
}); });
}); });
describe('every()', () => { describe('firstOf()', () => {
it('Can apply a function every 3rd time', () => { it('Can apply a function every 3rd time', () => {
expect( expect(
pure('a') pure('a')
.every(3, (x) => x._fast(2)) .firstOf(3, (x) => x._fast(2))
._fast(3) ._fast(3)
.firstCycle(), .firstCycle(),
).toStrictEqual(sequence(sequence('a', 'a'), 'a', 'a').firstCycle()); ).toStrictEqual(sequence(sequence('a', 'a'), 'a', 'a').firstCycle());
}); });
it('works with currying', () => { it('works with currying', () => {
expect(pure('a').every(3, fast(2))._fast(3).firstCycle()).toStrictEqual( expect(pure('a').firstOf(3, fast(2))._fast(3).firstCycle()).toStrictEqual(
sequence(sequence('a', 'a'), 'a', 'a').firstCycle(), sequence(sequence('a', 'a'), 'a', 'a').firstCycle(),
); );
expect(sequence(3, 4, 5).every(3, add(3)).fast(5).firstCycle()).toStrictEqual( expect(sequence(3, 4, 5).firstOf(3, add(3)).fast(5).firstCycle()).toStrictEqual(
sequence(6, 7, 8, 3, 4, 5, 3, 4, 5, 6, 7, 8, 3, 4, 5).firstCycle(), sequence(6, 7, 8, 3, 4, 5, 3, 4, 5, 6, 7, 8, 3, 4, 5).firstCycle(),
); );
expect(sequence(3, 4, 5).every(2, sub(1)).fast(5).firstCycle()).toStrictEqual( expect(sequence(3, 4, 5).firstOf(2, sub(1)).fast(5).firstCycle()).toStrictEqual(
sequence(2, 3, 4, 3, 4, 5, 2, 3, 4, 3, 4, 5, 2, 3, 4).firstCycle(), sequence(2, 3, 4, 3, 4, 5, 2, 3, 4, 3, 4, 5, 2, 3, 4).firstCycle(),
); );
expect(sequence(3, 4, 5).every(3, add(3)).every(2, sub(1)).fast(2).firstCycle()).toStrictEqual( expect(sequence(3, 4, 5).firstOf(3, add(3)).firstOf(2, sub(1)).fast(2).firstCycle()).toStrictEqual(
sequence(5, 6, 7, 3, 4, 5).firstCycle(), sequence(5, 6, 7, 3, 4, 5).firstCycle(),
); );
}); });
@@ -692,7 +692,7 @@ describe('Pattern', () => {
it('Can set the hap context', () => { it('Can set the hap context', () => {
expect( expect(
pure('a') pure('a')
._setContext([ .setContext([
[ [
[0, 1], [0, 1],
[1, 2], [1, 2],
@@ -713,13 +713,13 @@ describe('Pattern', () => {
it('Can update the hap context', () => { it('Can update the hap context', () => {
expect( expect(
pure('a') pure('a')
._setContext([ .setContext([
[ [
[0, 1], [0, 1],
[1, 2], [1, 2],
], ],
]) ])
._withContext((c) => [ .withContext((c) => [
...c, ...c,
[ [
[3, 4], [3, 4],
@@ -743,7 +743,7 @@ describe('Pattern', () => {
}); });
describe('apply', () => { describe('apply', () => {
it('Can apply a function', () => { it('Can apply a function', () => {
expect(sequence('a', 'b')._apply(fast(2)).firstCycle()).toStrictEqual(sequence('a', 'b').fast(2).firstCycle()); expect(sequence('a', 'b').apply(fast(2)).firstCycle()).toStrictEqual(sequence('a', 'b').fast(2).firstCycle());
}), }),
it('Can apply a pattern of functions', () => { it('Can apply a pattern of functions', () => {
expect(sequence('a', 'b').apply(fast(2)).firstCycle()).toStrictEqual(sequence('a', 'b').fast(2).firstCycle()); expect(sequence('a', 'b').apply(fast(2)).firstCycle()).toStrictEqual(sequence('a', 'b').fast(2).firstCycle());
@@ -784,18 +784,18 @@ describe('Pattern', () => {
}); });
describe('jux', () => { describe('jux', () => {
it('Can juxtapose', () => { it('Can juxtapose', () => {
expect(pure({ a: 1 }).jux(fast(2))._sortHapsByPart().firstCycle()).toStrictEqual( expect(pure({ a: 1 }).jux(fast(2)).sortHapsByPart().firstCycle()).toStrictEqual(
stack(pure({ a: 1, pan: 0 }), pure({ a: 1, pan: 1 }).fast(2)) stack(pure({ a: 1, pan: 0 }), pure({ a: 1, pan: 1 }).fast(2))
._sortHapsByPart() .sortHapsByPart()
.firstCycle(), .firstCycle(),
); );
}); });
}); });
describe('juxBy', () => { describe('juxBy', () => {
it('Can juxtapose by half', () => { it('Can juxtapose by half', () => {
expect(pure({ a: 1 }).juxBy(0.5, fast(2))._sortHapsByPart().firstCycle()).toStrictEqual( expect(pure({ a: 1 }).juxBy(0.5, fast(2)).sortHapsByPart().firstCycle()).toStrictEqual(
stack(pure({ a: 1, pan: 0.25 }), pure({ a: 1, pan: 0.75 }).fast(2)) stack(pure({ a: 1, pan: 0.25 }), pure({ a: 1, pan: 0.75 }).fast(2))
._sortHapsByPart() .sortHapsByPart()
.firstCycle(), .firstCycle(),
); );
}); });
@@ -805,7 +805,7 @@ describe('Pattern', () => {
expect( expect(
sequence('a', ['a', 'a']) sequence('a', ['a', 'a'])
.fmap((a) => fastcat('b', 'c')) .fmap((a) => fastcat('b', 'c'))
._squeezeJoin() .squeezeJoin()
.firstCycle(), .firstCycle(),
).toStrictEqual( ).toStrictEqual(
sequence( sequence(
@@ -820,7 +820,7 @@ describe('Pattern', () => {
it('Squeezes to the correct cycle', () => { it('Squeezes to the correct cycle', () => {
expect( expect(
pure(time.struct(true)) pure(time.struct(true))
._squeezeJoin() .squeezeJoin()
.queryArc(3, 4) .queryArc(3, 4)
.map((x) => x.value), .map((x) => x.value),
).toStrictEqual([Fraction(3.5)]); ).toStrictEqual([Fraction(3.5)]);
@@ -857,7 +857,7 @@ describe('Pattern', () => {
); );
}); });
it('Can chop(2,3)', () => { it('Can chop(2,3)', () => {
expect(pure({ sound: 'a' }).fast(2).chop(2, 3)._sortHapsByPart().firstCycle()).toStrictEqual( expect(pure({ sound: 'a' }).fast(2).chop(2, 3).sortHapsByPart().firstCycle()).toStrictEqual(
sequence( sequence(
[ [
{ sound: 'a', begin: 0, end: 0.5 }, { sound: 'a', begin: 0, end: 0.5 },
@@ -869,7 +869,7 @@ describe('Pattern', () => {
{ sound: 'a', begin: 2 / 3, end: 1 }, { sound: 'a', begin: 2 / 3, end: 1 },
], ],
) )
._sortHapsByPart() .sortHapsByPart()
.firstCycle(), .firstCycle(),
); );
}); });
@@ -893,4 +893,11 @@ describe('Pattern', () => {
); );
}); });
}); });
describe('alignments', () => {
it('Can squeeze arguments', () => {
expect(sequence(1, 2).add.squeeze(4, 5).firstCycle()).toStrictEqual(
sequence(5, 6, 6, 7).firstCycle()
);
});
});
}); });
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/eval", "name": "@strudel.cycles/eval",
"version": "0.4.1", "version": "0.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/eval", "name": "@strudel.cycles/eval",
"version": "0.4.1", "version": "0.4.0",
"description": "Code evaluator for strudel", "description": "Code evaluator for strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@@ -28,7 +28,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"estraverse": "^5.3.0", "estraverse": "^5.3.0",
"shift-ast": "^7.0.0", "shift-ast": "^7.0.0",
"shift-codegen": "^8.1.0", "shift-codegen": "^8.1.0",
+1 -1
View File
@@ -13,7 +13,7 @@ const { fastcat, evalScope } = strudel;
describe('evaluate', async () => { describe('evaluate', async () => {
await evalScope({ mini }, strudel); await evalScope({ mini }, strudel);
const ev = async (code) => (await evaluate(code)).pattern._firstCycleValues; const ev = async (code) => (await evaluate(code)).pattern.firstCycleValues;
it('Should evaluate strudel functions', async () => { it('Should evaluate strudel functions', async () => {
expect(await ev('pure("c3")')).toEqual(['c3']); expect(await ev('pure("c3")')).toEqual(['c3']);
expect(await ev('cat("c3")')).toEqual(['c3']); expect(await ev('cat("c3")')).toEqual(['c3']);
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/midi", "name": "@strudel.cycles/midi",
"version": "0.4.1", "version": "0.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/midi", "name": "@strudel.cycles/midi",
"version": "0.4.1", "version": "0.4.0",
"description": "Midi API for strudel", "description": "Midi API for strudel",
"main": "index.mjs", "main": "index.mjs",
"repository": { "repository": {
@@ -21,7 +21,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/tone": "^0.4.1", "@strudel.cycles/tone": "^0.4.0",
"tone": "^14.7.77", "tone": "^14.7.77",
"webmidi": "^3.0.21" "webmidi": "^3.0.21"
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/mini", "name": "@strudel.cycles/mini",
"version": "0.4.1", "version": "0.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/mini", "name": "@strudel.cycles/mini",
"version": "0.4.1", "version": "0.4.0",
"description": "Mini notation for strudel", "description": "Mini notation for strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@@ -26,9 +26,9 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/eval": "^0.4.1", "@strudel.cycles/eval": "^0.4.0",
"@strudel.cycles/tone": "^0.4.1" "@strudel.cycles/tone": "^0.4.0"
}, },
"devDependencies": { "devDependencies": {
"peggy": "^2.0.1" "peggy": "^2.0.1"
+2 -2
View File
@@ -9,8 +9,8 @@ import '@strudel.cycles/core/euclid.mjs';
import { describe, expect, it } from 'vitest'; import { describe, expect, it } from 'vitest';
describe('mini', () => { describe('mini', () => {
const minV = (v) => mini(v)._firstCycleValues; const minV = (v) => mini(v).firstCycleValues;
const minS = (v) => mini(v)._showFirstCycle; const minS = (v) => mini(v).showFirstCycle;
it('supports single elements', () => { it('supports single elements', () => {
expect(minV('a')).toEqual(['a']); expect(minV('a')).toEqual(['a']);
}); });
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/osc", "name": "@strudel.cycles/osc",
"version": "0.3.1", "version": "0.3.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/osc", "name": "@strudel.cycles/osc",
"version": "0.3.1", "version": "0.3.0",
"description": "OSC messaging for strudel", "description": "OSC messaging for strudel",
"main": "osc.mjs", "main": "osc.mjs",
"scripts": { "scripts": {
+5 -5
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/react", "name": "@strudel.cycles/react",
"version": "0.4.2", "version": "0.4.1",
"description": "React components for strudel", "description": "React components for strudel",
"main": "dist/index.cjs.js", "main": "dist/index.cjs.js",
"module": "dist/index.es.js", "module": "dist/index.es.js",
@@ -39,10 +39,10 @@
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@codemirror/lang-javascript": "^6.1.1", "@codemirror/lang-javascript": "^6.1.1",
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/tone": "^0.4.1", "@strudel.cycles/tone": "^0.4.0",
"@strudel.cycles/transpiler": "^0.4.1", "@strudel.cycles/transpiler": "^0.4.0",
"@strudel.cycles/webaudio": "^0.4.2", "@strudel.cycles/webaudio": "^0.4.1",
"@uiw/codemirror-themes": "^4.12.4", "@uiw/codemirror-themes": "^4.12.4",
"@uiw/react-codemirror": "^4.12.4", "@uiw/react-codemirror": "^4.12.4",
"react-hook-inview": "^4.5.0" "react-hook-inview": "^4.5.0"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/soundfonts", "name": "@strudel.cycles/soundfonts",
"version": "0.4.2", "version": "0.4.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/soundfonts", "name": "@strudel.cycles/soundfonts",
"version": "0.4.2", "version": "0.4.1",
"description": "Soundsfont support for strudel", "description": "Soundsfont support for strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@@ -22,8 +22,8 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/webaudio": "^0.4.2", "@strudel.cycles/webaudio": "^0.4.1",
"sfumato": "^0.1.2", "sfumato": "^0.1.2",
"soundfont2": "^0.4.0" "soundfont2": "^0.4.0"
}, },
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/tonal", "name": "@strudel.cycles/tonal",
"version": "0.4.1", "version": "0.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/tonal", "name": "@strudel.cycles/tonal",
"version": "0.4.1", "version": "0.4.0",
"description": "Tonal functions for strudel", "description": "Tonal functions for strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@@ -25,7 +25,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"@tonaljs/tonal": "^4.6.5", "@tonaljs/tonal": "^4.6.5",
"chord-voicings": "^0.0.1", "chord-voicings": "^0.0.1",
"webmidi": "^3.0.21" "webmidi": "^3.0.21"
+1 -1
View File
@@ -12,6 +12,6 @@ import { describe, it, expect } from 'vitest';
describe('tonal', () => { describe('tonal', () => {
it('Should run tonal functions ', () => { it('Should run tonal functions ', () => {
expect(pure('c3').scale('C major').scaleTranspose(1)._firstCycleValues).toEqual(['D3']); expect(pure('c3').scale('C major').scaleTranspose(1).firstCycleValues).toEqual(['D3']);
}); });
}); });
+3 -3
View File
@@ -75,7 +75,7 @@ function scaleOffset(scale, offset, note) {
*/ */
Pattern.prototype._transpose = function (intervalOrSemitones) { Pattern.prototype._transpose = function (intervalOrSemitones) {
return this._withHap((hap) => { return this.withHap((hap) => {
const interval = !isNaN(Number(intervalOrSemitones)) const interval = !isNaN(Number(intervalOrSemitones))
? Interval.fromSemitones(intervalOrSemitones /* as number */) ? Interval.fromSemitones(intervalOrSemitones /* as number */)
: String(intervalOrSemitones); : String(intervalOrSemitones);
@@ -111,7 +111,7 @@ Pattern.prototype._transpose = function (intervalOrSemitones) {
*/ */
Pattern.prototype._scaleTranspose = function (offset /* : number | string */) { Pattern.prototype._scaleTranspose = function (offset /* : number | string */) {
return this._withHap((hap) => { return this.withHap((hap) => {
if (!hap.context.scale) { if (!hap.context.scale) {
throw new Error('can only use scaleTranspose after .scale'); throw new Error('can only use scaleTranspose after .scale');
} }
@@ -142,7 +142,7 @@ Pattern.prototype._scaleTranspose = function (offset /* : number | string */) {
*/ */
Pattern.prototype._scale = function (scale /* : string */) { Pattern.prototype._scale = function (scale /* : string */) {
return this._withHap((hap) => { return this.withHap((hap) => {
let note = hap.value; let note = hap.value;
const asNumber = Number(note); const asNumber = Number(note);
if (!isNaN(asNumber)) { if (!isNaN(asNumber)) {
+1 -1
View File
@@ -51,7 +51,7 @@ Pattern.prototype.voicings = function (range) {
} }
return this.fmapNested((event) => { return this.fmapNested((event) => {
lastVoicing = getVoicing(event.value, lastVoicing, range); lastVoicing = getVoicing(event.value, lastVoicing, range);
return stack(...lastVoicing)._withContext(() => ({ return stack(...lastVoicing).withContext(() => ({
locations: event.context.locations || [], locations: event.context.locations || [],
})); }));
}); });
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/tone", "name": "@strudel.cycles/tone",
"version": "0.4.1", "version": "0.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/tone", "name": "@strudel.cycles/tone",
"version": "0.4.1", "version": "0.4.0",
"description": "Tone.js API for strudel", "description": "Tone.js API for strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@@ -22,7 +22,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"tone": "^14.7.77" "tone": "^14.7.77"
} }
} }
+1 -1
View File
@@ -13,6 +13,6 @@ describe('tone', () => {
// const s = synth().chain(out()); // TODO: mock audio context? // const s = synth().chain(out()); // TODO: mock audio context?
// assert.deepStrictEqual(s, new Tone.Synth().chain(out())); // assert.deepStrictEqual(s, new Tone.Synth().chain(out()));
const s = {}; const s = {};
expect(pure('c3').tone(s)._firstCycleValues).toEqual(['c3']); expect(pure('c3').tone(s).firstCycleValues).toEqual(['c3']);
}); });
}); });
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/transpiler", "name": "@strudel.cycles/transpiler",
"version": "0.4.1", "version": "0.4.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",
"scripts": { "scripts": {
@@ -24,7 +24,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"acorn": "^8.8.1", "acorn": "^8.8.1",
"escodegen": "^2.0.0", "escodegen": "^2.0.0",
"estree-walker": "^3.0.1" "estree-walker": "^3.0.1"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/webaudio", "name": "@strudel.cycles/webaudio",
"version": "0.4.2", "version": "0.4.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/webaudio", "name": "@strudel.cycles/webaudio",
"version": "0.4.2", "version": "0.4.1",
"description": "Web Audio helpers for Strudel", "description": "Web Audio helpers for Strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@@ -30,6 +30,6 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1" "@strudel.cycles/core": "^0.4.0"
} }
} }
+1 -1
View File
@@ -157,7 +157,7 @@ function getDelay(orbit, delaytime, delayfeedback, t) {
if (!delays[orbit]) { if (!delays[orbit]) {
const ac = getAudioContext(); const ac = getAudioContext();
const dly = ac.createFeedbackDelay(1, delaytime, delayfeedback); const dly = ac.createFeedbackDelay(1, delaytime, delayfeedback);
dly.start(t); dly.start?.(t); // for some reason, this throws when audion extension is installed..
dly.connect(getDestination()); dly.connect(getDestination());
delays[orbit] = dly; delays[orbit] = dly;
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/webdirt", "name": "@strudel.cycles/webdirt",
"version": "0.4.1", "version": "0.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/webdirt", "name": "@strudel.cycles/webdirt",
"version": "0.4.1", "version": "0.4.0",
"description": "WebDirt integration for Strudel", "description": "WebDirt integration for Strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@@ -22,7 +22,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"WebDirt": "github:dktr0/WebDirt" "WebDirt": "github:dktr0/WebDirt"
} }
} }
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/xen", "name": "@strudel.cycles/xen",
"version": "0.4.1", "version": "0.4.0",
"description": "Xenharmonic API for strudel", "description": "Xenharmonic API for strudel",
"main": "index.mjs", "main": "index.mjs",
"scripts": { "scripts": {
@@ -24,6 +24,6 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1" "@strudel.cycles/core": "^0.4.0"
} }
} }
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,22 @@
// Vitest Snapshot v1 // Vitest Snapshot v1
exports[`runs examples > example "_apply" example index 0 1`] = `
[
"0/1 -> 1/1: {\\"note\\":\\"C3\\"}",
"0/1 -> 1/1: {\\"note\\":\\"Eb3\\"}",
"0/1 -> 1/1: {\\"note\\":\\"G3\\"}",
"1/1 -> 2/1: {\\"note\\":\\"Eb3\\"}",
"1/1 -> 2/1: {\\"note\\":\\"G3\\"}",
"1/1 -> 2/1: {\\"note\\":\\"Bb3\\"}",
"2/1 -> 3/1: {\\"note\\":\\"G3\\"}",
"2/1 -> 3/1: {\\"note\\":\\"Bb3\\"}",
"2/1 -> 3/1: {\\"note\\":\\"D4\\"}",
"3/1 -> 4/1: {\\"note\\":\\"C3\\"}",
"3/1 -> 4/1: {\\"note\\":\\"Eb3\\"}",
"3/1 -> 4/1: {\\"note\\":\\"G3\\"}",
]
`;
exports[`runs examples > example "accelerate" example index 0 1`] = ` exports[`runs examples > example "accelerate" example index 0 1`] = `
[ [
"0/1 -> 2/1: {\\"s\\":\\"sax\\",\\"accelerate\\":0}", "0/1 -> 2/1: {\\"s\\":\\"sax\\",\\"accelerate\\":0}",
@@ -807,36 +824,36 @@ exports[`runs examples > example "echo" example index 0 1`] = `
[ [
"0/1 -> 1/2: {\\"s\\":\\"bd\\"}", "0/1 -> 1/2: {\\"s\\":\\"bd\\"}",
"1/2 -> 1/1: {\\"s\\":\\"sd\\"}", "1/2 -> 1/1: {\\"s\\":\\"sd\\"}",
"-4166666666666667/12500000000000000 -> 8333333333333333/50000000000000000: {\\"s\\":\\"sd\\"}", "-1/3 -> 1/6: {\\"s\\":\\"sd\\"}",
"8333333333333333/50000000000000000 -> 8333333333333333/12500000000000000: {\\"s\\":\\"bd\\"}", "1/6 -> 2/3: {\\"s\\":\\"bd\\"}",
"8333333333333333/12500000000000000 -> 7291666666666667/6250000000000000: {\\"s\\":\\"sd\\"}", "2/3 -> 7/6: {\\"s\\":\\"sd\\"}",
"-4166666666666667/25000000000000000 -> 8333333333333333/25000000000000000: {\\"s\\":\\"sd\\"}", "-1/6 -> 1/3: {\\"s\\":\\"sd\\"}",
"8333333333333333/25000000000000000 -> 5208333333333333/6250000000000000: {\\"s\\":\\"bd\\"}", "1/3 -> 5/6: {\\"s\\":\\"bd\\"}",
"5208333333333333/6250000000000000 -> 8333333333333333/6250000000000000: {\\"s\\":\\"sd\\"}", "5/6 -> 4/3: {\\"s\\":\\"sd\\"}",
"1/1 -> 3/2: {\\"s\\":\\"bd\\"}", "1/1 -> 3/2: {\\"s\\":\\"bd\\"}",
"3/2 -> 2/1: {\\"s\\":\\"sd\\"}", "3/2 -> 2/1: {\\"s\\":\\"sd\\"}",
"8333333333333333/12500000000000000 -> 7291666666666667/6250000000000000: {\\"s\\":\\"sd\\"}", "2/3 -> 7/6: {\\"s\\":\\"sd\\"}",
"7291666666666667/6250000000000000 -> 5208333333333333/3125000000000000: {\\"s\\":\\"bd\\"}", "7/6 -> 5/3: {\\"s\\":\\"bd\\"}",
"5208333333333333/3125000000000000 -> 6770833333333333/3125000000000000: {\\"s\\":\\"sd\\"}", "5/3 -> 13/6: {\\"s\\":\\"sd\\"}",
"5208333333333333/6250000000000000 -> 8333333333333333/6250000000000000: {\\"s\\":\\"sd\\"}", "5/6 -> 4/3: {\\"s\\":\\"sd\\"}",
"8333333333333333/6250000000000000 -> 5729166666666667/3125000000000000: {\\"s\\":\\"bd\\"}", "4/3 -> 11/6: {\\"s\\":\\"bd\\"}",
"5729166666666667/3125000000000000 -> 7291666666666667/3125000000000000: {\\"s\\":\\"sd\\"}", "11/6 -> 7/3: {\\"s\\":\\"sd\\"}",
"2/1 -> 5/2: {\\"s\\":\\"bd\\"}", "2/1 -> 5/2: {\\"s\\":\\"bd\\"}",
"5/2 -> 3/1: {\\"s\\":\\"sd\\"}", "5/2 -> 3/1: {\\"s\\":\\"sd\\"}",
"5208333333333333/3125000000000000 -> 6770833333333333/3125000000000000: {\\"s\\":\\"sd\\"}", "5/3 -> 13/6: {\\"s\\":\\"sd\\"}",
"6770833333333333/3125000000000000 -> 8333333333333333/3125000000000000: {\\"s\\":\\"bd\\"}", "13/6 -> 8/3: {\\"s\\":\\"bd\\"}",
"8333333333333333/3125000000000000 -> 4947916666666667/1562500000000000: {\\"s\\":\\"sd\\"}", "8/3 -> 19/6: {\\"s\\":\\"sd\\"}",
"5729166666666667/3125000000000000 -> 7291666666666667/3125000000000000: {\\"s\\":\\"sd\\"}", "11/6 -> 7/3: {\\"s\\":\\"sd\\"}",
"7291666666666667/3125000000000000 -> 8854166666666667/3125000000000000: {\\"s\\":\\"bd\\"}", "7/3 -> 17/6: {\\"s\\":\\"bd\\"}",
"8854166666666667/3125000000000000 -> 5208333333333333/1562500000000000: {\\"s\\":\\"sd\\"}", "17/6 -> 10/3: {\\"s\\":\\"sd\\"}",
"3/1 -> 7/2: {\\"s\\":\\"bd\\"}", "3/1 -> 7/2: {\\"s\\":\\"bd\\"}",
"7/2 -> 4/1: {\\"s\\":\\"sd\\"}", "7/2 -> 4/1: {\\"s\\":\\"sd\\"}",
"8333333333333333/3125000000000000 -> 4947916666666667/1562500000000000: {\\"s\\":\\"sd\\"}", "8/3 -> 19/6: {\\"s\\":\\"sd\\"}",
"4947916666666667/1562500000000000 -> 5729166666666667/1562500000000000: {\\"s\\":\\"bd\\"}", "19/6 -> 11/3: {\\"s\\":\\"bd\\"}",
"5729166666666667/1562500000000000 -> 6510416666666667/1562500000000000: {\\"s\\":\\"sd\\"}", "11/3 -> 25/6: {\\"s\\":\\"sd\\"}",
"8854166666666667/3125000000000000 -> 5208333333333333/1562500000000000: {\\"s\\":\\"sd\\"}", "17/6 -> 10/3: {\\"s\\":\\"sd\\"}",
"5208333333333333/1562500000000000 -> 5989583333333333/1562500000000000: {\\"s\\":\\"bd\\"}", "10/3 -> 23/6: {\\"s\\":\\"bd\\"}",
"5989583333333333/1562500000000000 -> 6770833333333333/1562500000000000: {\\"s\\":\\"sd\\"}", "23/6 -> 13/3: {\\"s\\":\\"sd\\"}",
] ]
`; `;
@@ -1593,6 +1610,27 @@ exports[`runs examples > example "fastcat" example index 0 1`] = `
] ]
`; `;
exports[`runs examples > example "firstOf" example index 0 1`] = `
[
"3/4 -> 1/1: {\\"note\\":\\"c3\\"}",
"1/2 -> 3/4: {\\"note\\":\\"d3\\"}",
"1/4 -> 1/2: {\\"note\\":\\"e3\\"}",
"0/1 -> 1/4: {\\"note\\":\\"g3\\"}",
"1/1 -> 5/4: {\\"note\\":\\"c3\\"}",
"5/4 -> 3/2: {\\"note\\":\\"d3\\"}",
"3/2 -> 7/4: {\\"note\\":\\"e3\\"}",
"7/4 -> 2/1: {\\"note\\":\\"g3\\"}",
"2/1 -> 9/4: {\\"note\\":\\"c3\\"}",
"9/4 -> 5/2: {\\"note\\":\\"d3\\"}",
"5/2 -> 11/4: {\\"note\\":\\"e3\\"}",
"11/4 -> 3/1: {\\"note\\":\\"g3\\"}",
"3/1 -> 13/4: {\\"note\\":\\"c3\\"}",
"13/4 -> 7/2: {\\"note\\":\\"d3\\"}",
"7/2 -> 15/4: {\\"note\\":\\"e3\\"}",
"15/4 -> 4/1: {\\"note\\":\\"g3\\"}",
]
`;
exports[`runs examples > example "freq" example index 0 1`] = ` exports[`runs examples > example "freq" example index 0 1`] = `
[ [
"0/1 -> 1/4: {\\"freq\\":220,\\"s\\":\\"superzow\\"}", "0/1 -> 1/4: {\\"freq\\":220,\\"s\\":\\"superzow\\"}",
@@ -1793,6 +1831,27 @@ exports[`runs examples > example "iterBack" example index 0 1`] = `
] ]
`; `;
exports[`runs examples > example "lastOf" example index 0 1`] = `
[
"0/1 -> 1/4: {\\"note\\":\\"c3\\"}",
"1/4 -> 1/2: {\\"note\\":\\"d3\\"}",
"1/2 -> 3/4: {\\"note\\":\\"e3\\"}",
"3/4 -> 1/1: {\\"note\\":\\"g3\\"}",
"1/1 -> 5/4: {\\"note\\":\\"c3\\"}",
"5/4 -> 3/2: {\\"note\\":\\"d3\\"}",
"3/2 -> 7/4: {\\"note\\":\\"e3\\"}",
"7/4 -> 2/1: {\\"note\\":\\"g3\\"}",
"2/1 -> 9/4: {\\"note\\":\\"c3\\"}",
"9/4 -> 5/2: {\\"note\\":\\"d3\\"}",
"5/2 -> 11/4: {\\"note\\":\\"e3\\"}",
"11/4 -> 3/1: {\\"note\\":\\"g3\\"}",
"15/4 -> 4/1: {\\"note\\":\\"c3\\"}",
"7/2 -> 15/4: {\\"note\\":\\"d3\\"}",
"13/4 -> 7/2: {\\"note\\":\\"e3\\"}",
"3/1 -> 13/4: {\\"note\\":\\"g3\\"}",
]
`;
exports[`runs examples > example "late" example index 0 1`] = ` exports[`runs examples > example "late" example index 0 1`] = `
[ [
"0/1 -> 1/2: {\\"s\\":\\"bd\\"}", "0/1 -> 1/2: {\\"s\\":\\"bd\\"}",
+24
View File
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+33
View File
@@ -0,0 +1,33 @@
# Strudel Workshop
This may become a resource to teach strudel in workshop format.
## Install
From the root of the strudel repo:
```sh
npm i # install strudel project dependencies
cd workshop && npm i # install workshop dependencies
npm run dev # run dev server
```
This assumes you have node 16+ installed.
## How to write
While running the dev server, go to `workshop.mdx` to write!
When the file is saved, the browser will hot reload the file and reveal the changes instantly.
You can use normal markdown + JSX, which enables using the MiniRepl component.
You could also add any react component and import it to the mdx file.
## Build
Running
```sh
npm run build
```
..will output a single `index.html` file to `dist`. This file can either be opened directly in the browser (locally) or uploaded to some server.
+12
View File
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Strudel Workshop</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
+5982
View File
File diff suppressed because it is too large Load Diff
+29
View File
@@ -0,0 +1,29 @@
{
"name": "workshop",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --open",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@mdx-js/mdx": "^1.6.22",
"@mdx-js/react": "^1.6.22",
"@tailwindcss/typography": "^0.5.8",
"@types/react": "^18.0.24",
"@types/react-dom": "^18.0.8",
"@vitejs/plugin-react": "^2.2.0",
"autoprefixer": "^10.4.13",
"postcss": "^8.4.19",
"tailwindcss": "^3.2.4",
"vite": "^3.2.3",
"vite-plugin-mdx": "^3.5.11",
"vite-plugin-singlefile": "^0.13.1"
}
}
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
+16
View File
@@ -0,0 +1,16 @@
import { initAudioOnFirstClick } from '@strudel.cycles/webaudio';
import Workshop from './workshop.mdx';
initAudioOnFirstClick();
function App() {
return (
<div className="bg-slate-900 w-screen h-screen overflow-auto">
<main className="p-4 pl-6 max-w-3xl prose prose-invert">
<Workshop />
</main>
</div>
);
}
export default App;
+22
View File
@@ -0,0 +1,22 @@
import { evalScope, controls } from '@strudel.cycles/core';
import { MiniRepl as _MiniRepl } from '@strudel.cycles/react';
import { samples } from '@strudel.cycles/webaudio';
import '@strudel.cycles/react/dist/style.css';
samples('github:tidalcycles/Dirt-Samples/master')
evalScope(
controls,
import('@strudel.cycles/core'),
// import('@strudel.cycles/tone'),
import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/midi'),
// import('@strudel.cycles/xen'),
import('@strudel.cycles/webaudio'),
import('@strudel.cycles/osc'),
);
export function MiniRepl({ tune }) {
return <_MiniRepl tune={tune} hideOutsideView={true} />;
}
+3
View File
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
+11
View File
@@ -0,0 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
);
+49
View File
@@ -0,0 +1,49 @@
import { MiniRepl } from './MiniRepl';
# Workshop
> This is a starter project for any type of teaching material using strudel!
## What is Strudel?
1. A pastry made with fruit or cheese rolled up in layers of thin sheets of dough and then baked.
2. a pastry made from multiple, thin layers of dough rolled up and filled with fruit, etc.
3. thin sheet of filled dough rolled and baked
## Some REPLs:
Small REPL:
<MiniRepl tune={`s("bd sd,hh*8")`} />
Big REPL:
<MiniRepl
tune={`await samples({
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'],
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
}, 'github:tidalcycles/Dirt-Samples/master/');
stack(
s("bd,[~ <sd!3 sd(3,4,2)>],hh(3,4)") // drums
.speed(perlin.range(.7,.9)) // random sample speed variation
,"<a1 b1\*2 a1(3,8) e2>" // bassline
.off(1/8,x=>x.add(12).degradeBy(.5)) // random octave jumps
.add(perlin.range(0,.5)) // random pitch variation
.superimpose(add(.05)) // add second, slightly detuned voice
.n() // wrap in "n"
.decay(.15).sustain(0) // make each note of equal length
.s('sawtooth') // waveform
.gain(.4) // turn down
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings() // chords
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
.add(perlin.range(0,.5)) // random pitch variation
.n() // wrap in "n"
.s('sawtooth') // waveform
.gain(.16) // turn down
.cutoff(500) // fixed cutoff
.attack(1) // slowly fade in
)
.slow(3/2)`}
/>
+8
View File
@@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx,mdx}'],
theme: {
extend: {},
},
plugins: [require('@tailwindcss/typography')],
};
+25
View File
@@ -0,0 +1,25 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import _mdx from 'vite-plugin-mdx';
import { viteSingleFile } from 'vite-plugin-singlefile';
// import remarkToc from 'remark-toc';
// import rehypeSlug from 'rehype-slug';
// import rehypeAutolinkHeadings from 'rehype-autolink-headings';
const mdx = _mdx.default || _mdx;
// `options` are passed to `@mdx-js/mdx`
const options = {
// See https://mdxjs.com/advanced/plugins
remarkPlugins: [
// remarkToc,
// E.g. `remark-frontmatter`
],
rehypePlugins: [
// rehypeSlug, rehypeAutolinkHeadings
],
};
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), mdx(options), viteSingleFile()],
});