mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-13 22:35:15 -04:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cd1c4632d7 | |||
| f1a850e133 | |||
| 53b61b8d85 | |||
| e1a532500e | |||
| 4cf412b93d | |||
| 54c9c434e0 | |||
| de19f3e5fe | |||
| 8304993481 | |||
| 4c838aeaca | |||
| 547d925065 |
@@ -74,7 +74,7 @@ const fraction = (n) => {
|
||||
-> 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
|
||||
*/
|
||||
n = String(n);
|
||||
// n = String(n); // this is actually faster but imprecise...
|
||||
}
|
||||
return Fraction(n);
|
||||
};
|
||||
|
||||
+632
-562
File diff suppressed because it is too large
Load Diff
@@ -20,7 +20,7 @@ export const signal = (func) => {
|
||||
};
|
||||
|
||||
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.
|
||||
@@ -33,7 +33,7 @@ export const isaw2 = isaw._toBipolar();
|
||||
*
|
||||
*/
|
||||
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));
|
||||
|
||||
@@ -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()
|
||||
*
|
||||
*/
|
||||
export const sine = sine2._fromBipolar();
|
||||
export const sine = sine2.fromBipolar();
|
||||
|
||||
/**
|
||||
* 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 square2 = square._toBipolar();
|
||||
export const square2 = square.toBipolar();
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
export const rand2 = rand._toBipolar();
|
||||
export const rand2 = rand.toBipolar();
|
||||
|
||||
export const _brandBy = (p) => rand.fmap((x) => x < p);
|
||||
export const brandBy = (pPat) => reify(pPat).fmap(_brandBy).innerJoin();
|
||||
@@ -201,7 +201,7 @@ Pattern.prototype.choose = function (...xs) {
|
||||
* @returns {Pattern}
|
||||
*/
|
||||
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)));
|
||||
|
||||
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));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,7 +49,7 @@ import { steady } from '../signal.mjs';
|
||||
|
||||
import controls from '../controls.mjs';
|
||||
|
||||
const { n } = controls;
|
||||
const { n, s } = controls;
|
||||
const st = (begin, end) => new State(ts(begin, end));
|
||||
const ts = (begin, end) => new TimeSpan(Fraction(begin), Fraction(end));
|
||||
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 sameFirst = (a, b) => {
|
||||
return expect(a._sortHapsByPart().firstCycle()).toStrictEqual(b._sortHapsByPart().firstCycle());
|
||||
return expect(a.sortHapsByPart().firstCycle()).toStrictEqual(b.sortHapsByPart().firstCycle());
|
||||
};
|
||||
|
||||
describe('TimeSpan', () => {
|
||||
@@ -382,7 +382,7 @@ describe('Pattern', () => {
|
||||
);
|
||||
});
|
||||
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.5, 1.5), ts(1, 1.5), 'a'),
|
||||
]);
|
||||
@@ -446,8 +446,8 @@ describe('Pattern', () => {
|
||||
});
|
||||
describe('slow()', () => {
|
||||
it('Supports zero-length queries', () => {
|
||||
expect(steady('a').slow(1)._setContext({}).queryArc(0, 0)).toStrictEqual(
|
||||
steady('a')._setContext({}).queryArc(0, 0),
|
||||
expect(steady('a').slow(1).setContext({}).queryArc(0, 0)).toStrictEqual(
|
||||
steady('a').setContext({}).queryArc(0, 0),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -465,7 +465,7 @@ describe('Pattern', () => {
|
||||
it('Filters true', () => {
|
||||
expect(
|
||||
pure(true)
|
||||
._filterValues((x) => x)
|
||||
.filterValues((x) => x)
|
||||
.firstCycle().length,
|
||||
).toBe(1);
|
||||
});
|
||||
@@ -490,7 +490,7 @@ describe('Pattern', () => {
|
||||
pure(10)
|
||||
.when(slowcat(true, false), (x) => x.add(3))
|
||||
.fast(4)
|
||||
._sortHapsByPart()
|
||||
.sortHapsByPart()
|
||||
.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', () => {
|
||||
expect(
|
||||
pure('a')
|
||||
.every(3, (x) => x._fast(2))
|
||||
.firstOf(3, (x) => x._fast(2))
|
||||
._fast(3)
|
||||
.firstCycle(),
|
||||
).toStrictEqual(sequence(sequence('a', 'a'), 'a', 'a').firstCycle());
|
||||
});
|
||||
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(),
|
||||
);
|
||||
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(),
|
||||
);
|
||||
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(),
|
||||
);
|
||||
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(),
|
||||
);
|
||||
});
|
||||
@@ -692,7 +692,7 @@ describe('Pattern', () => {
|
||||
it('Can set the hap context', () => {
|
||||
expect(
|
||||
pure('a')
|
||||
._setContext([
|
||||
.setContext([
|
||||
[
|
||||
[0, 1],
|
||||
[1, 2],
|
||||
@@ -713,13 +713,13 @@ describe('Pattern', () => {
|
||||
it('Can update the hap context', () => {
|
||||
expect(
|
||||
pure('a')
|
||||
._setContext([
|
||||
.setContext([
|
||||
[
|
||||
[0, 1],
|
||||
[1, 2],
|
||||
],
|
||||
])
|
||||
._withContext((c) => [
|
||||
.withContext((c) => [
|
||||
...c,
|
||||
[
|
||||
[3, 4],
|
||||
@@ -743,7 +743,7 @@ describe('Pattern', () => {
|
||||
});
|
||||
describe('apply', () => {
|
||||
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', () => {
|
||||
expect(sequence('a', 'b').apply(fast(2)).firstCycle()).toStrictEqual(sequence('a', 'b').fast(2).firstCycle());
|
||||
@@ -784,18 +784,18 @@ describe('Pattern', () => {
|
||||
});
|
||||
describe('jux', () => {
|
||||
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))
|
||||
._sortHapsByPart()
|
||||
.sortHapsByPart()
|
||||
.firstCycle(),
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('juxBy', () => {
|
||||
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))
|
||||
._sortHapsByPart()
|
||||
.sortHapsByPart()
|
||||
.firstCycle(),
|
||||
);
|
||||
});
|
||||
@@ -805,7 +805,7 @@ describe('Pattern', () => {
|
||||
expect(
|
||||
sequence('a', ['a', 'a'])
|
||||
.fmap((a) => fastcat('b', 'c'))
|
||||
._squeezeJoin()
|
||||
.squeezeJoin()
|
||||
.firstCycle(),
|
||||
).toStrictEqual(
|
||||
sequence(
|
||||
@@ -820,7 +820,7 @@ describe('Pattern', () => {
|
||||
it('Squeezes to the correct cycle', () => {
|
||||
expect(
|
||||
pure(time.struct(true))
|
||||
._squeezeJoin()
|
||||
.squeezeJoin()
|
||||
.queryArc(3, 4)
|
||||
.map((x) => x.value),
|
||||
).toStrictEqual([Fraction(3.5)]);
|
||||
@@ -857,7 +857,7 @@ describe('Pattern', () => {
|
||||
);
|
||||
});
|
||||
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(
|
||||
[
|
||||
{ sound: 'a', begin: 0, end: 0.5 },
|
||||
@@ -869,7 +869,7 @@ describe('Pattern', () => {
|
||||
{ sound: 'a', begin: 2 / 3, end: 1 },
|
||||
],
|
||||
)
|
||||
._sortHapsByPart()
|
||||
.sortHapsByPart()
|
||||
.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()
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ const { fastcat, evalScope } = strudel;
|
||||
|
||||
describe('evaluate', async () => {
|
||||
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 () => {
|
||||
expect(await ev('pure("c3")')).toEqual(['c3']);
|
||||
expect(await ev('cat("c3")')).toEqual(['c3']);
|
||||
|
||||
@@ -9,8 +9,8 @@ import '@strudel.cycles/core/euclid.mjs';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
describe('mini', () => {
|
||||
const minV = (v) => mini(v)._firstCycleValues;
|
||||
const minS = (v) => mini(v)._showFirstCycle;
|
||||
const minV = (v) => mini(v).firstCycleValues;
|
||||
const minS = (v) => mini(v).showFirstCycle;
|
||||
it('supports single elements', () => {
|
||||
expect(minV('a')).toEqual(['a']);
|
||||
});
|
||||
|
||||
@@ -12,6 +12,6 @@ import { describe, it, expect } from 'vitest';
|
||||
|
||||
describe('tonal', () => {
|
||||
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']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -75,7 +75,7 @@ function scaleOffset(scale, offset, note) {
|
||||
*/
|
||||
|
||||
Pattern.prototype._transpose = function (intervalOrSemitones) {
|
||||
return this._withHap((hap) => {
|
||||
return this.withHap((hap) => {
|
||||
const interval = !isNaN(Number(intervalOrSemitones))
|
||||
? Interval.fromSemitones(intervalOrSemitones /* as number */)
|
||||
: String(intervalOrSemitones);
|
||||
@@ -111,7 +111,7 @@ Pattern.prototype._transpose = function (intervalOrSemitones) {
|
||||
*/
|
||||
|
||||
Pattern.prototype._scaleTranspose = function (offset /* : number | string */) {
|
||||
return this._withHap((hap) => {
|
||||
return this.withHap((hap) => {
|
||||
if (!hap.context.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 */) {
|
||||
return this._withHap((hap) => {
|
||||
return this.withHap((hap) => {
|
||||
let note = hap.value;
|
||||
const asNumber = Number(note);
|
||||
if (!isNaN(asNumber)) {
|
||||
|
||||
@@ -51,7 +51,7 @@ Pattern.prototype.voicings = function (range) {
|
||||
}
|
||||
return this.fmapNested((event) => {
|
||||
lastVoicing = getVoicing(event.value, lastVoicing, range);
|
||||
return stack(...lastVoicing)._withContext(() => ({
|
||||
return stack(...lastVoicing).withContext(() => ({
|
||||
locations: event.context.locations || [],
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -13,6 +13,6 @@ describe('tone', () => {
|
||||
// const s = synth().chain(out()); // TODO: mock audio context?
|
||||
// assert.deepStrictEqual(s, new Tone.Synth().chain(out()));
|
||||
const s = {};
|
||||
expect(pure('c3').tone(s)._firstCycleValues).toEqual(['c3']);
|
||||
expect(pure('c3').tone(s).firstCycleValues).toEqual(['c3']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -157,7 +157,7 @@ function getDelay(orbit, delaytime, delayfeedback, t) {
|
||||
if (!delays[orbit]) {
|
||||
const ac = getAudioContext();
|
||||
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());
|
||||
delays[orbit] = dly;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,22 @@
|
||||
// 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`] = `
|
||||
[
|
||||
"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\\"}",
|
||||
"1/2 -> 1/1: {\\"s\\":\\"sd\\"}",
|
||||
"-4166666666666667/12500000000000000 -> 8333333333333333/50000000000000000: {\\"s\\":\\"sd\\"}",
|
||||
"8333333333333333/50000000000000000 -> 8333333333333333/12500000000000000: {\\"s\\":\\"bd\\"}",
|
||||
"8333333333333333/12500000000000000 -> 7291666666666667/6250000000000000: {\\"s\\":\\"sd\\"}",
|
||||
"-4166666666666667/25000000000000000 -> 8333333333333333/25000000000000000: {\\"s\\":\\"sd\\"}",
|
||||
"8333333333333333/25000000000000000 -> 5208333333333333/6250000000000000: {\\"s\\":\\"bd\\"}",
|
||||
"5208333333333333/6250000000000000 -> 8333333333333333/6250000000000000: {\\"s\\":\\"sd\\"}",
|
||||
"-1/3 -> 1/6: {\\"s\\":\\"sd\\"}",
|
||||
"1/6 -> 2/3: {\\"s\\":\\"bd\\"}",
|
||||
"2/3 -> 7/6: {\\"s\\":\\"sd\\"}",
|
||||
"-1/6 -> 1/3: {\\"s\\":\\"sd\\"}",
|
||||
"1/3 -> 5/6: {\\"s\\":\\"bd\\"}",
|
||||
"5/6 -> 4/3: {\\"s\\":\\"sd\\"}",
|
||||
"1/1 -> 3/2: {\\"s\\":\\"bd\\"}",
|
||||
"3/2 -> 2/1: {\\"s\\":\\"sd\\"}",
|
||||
"8333333333333333/12500000000000000 -> 7291666666666667/6250000000000000: {\\"s\\":\\"sd\\"}",
|
||||
"7291666666666667/6250000000000000 -> 5208333333333333/3125000000000000: {\\"s\\":\\"bd\\"}",
|
||||
"5208333333333333/3125000000000000 -> 6770833333333333/3125000000000000: {\\"s\\":\\"sd\\"}",
|
||||
"5208333333333333/6250000000000000 -> 8333333333333333/6250000000000000: {\\"s\\":\\"sd\\"}",
|
||||
"8333333333333333/6250000000000000 -> 5729166666666667/3125000000000000: {\\"s\\":\\"bd\\"}",
|
||||
"5729166666666667/3125000000000000 -> 7291666666666667/3125000000000000: {\\"s\\":\\"sd\\"}",
|
||||
"2/3 -> 7/6: {\\"s\\":\\"sd\\"}",
|
||||
"7/6 -> 5/3: {\\"s\\":\\"bd\\"}",
|
||||
"5/3 -> 13/6: {\\"s\\":\\"sd\\"}",
|
||||
"5/6 -> 4/3: {\\"s\\":\\"sd\\"}",
|
||||
"4/3 -> 11/6: {\\"s\\":\\"bd\\"}",
|
||||
"11/6 -> 7/3: {\\"s\\":\\"sd\\"}",
|
||||
"2/1 -> 5/2: {\\"s\\":\\"bd\\"}",
|
||||
"5/2 -> 3/1: {\\"s\\":\\"sd\\"}",
|
||||
"5208333333333333/3125000000000000 -> 6770833333333333/3125000000000000: {\\"s\\":\\"sd\\"}",
|
||||
"6770833333333333/3125000000000000 -> 8333333333333333/3125000000000000: {\\"s\\":\\"bd\\"}",
|
||||
"8333333333333333/3125000000000000 -> 4947916666666667/1562500000000000: {\\"s\\":\\"sd\\"}",
|
||||
"5729166666666667/3125000000000000 -> 7291666666666667/3125000000000000: {\\"s\\":\\"sd\\"}",
|
||||
"7291666666666667/3125000000000000 -> 8854166666666667/3125000000000000: {\\"s\\":\\"bd\\"}",
|
||||
"8854166666666667/3125000000000000 -> 5208333333333333/1562500000000000: {\\"s\\":\\"sd\\"}",
|
||||
"5/3 -> 13/6: {\\"s\\":\\"sd\\"}",
|
||||
"13/6 -> 8/3: {\\"s\\":\\"bd\\"}",
|
||||
"8/3 -> 19/6: {\\"s\\":\\"sd\\"}",
|
||||
"11/6 -> 7/3: {\\"s\\":\\"sd\\"}",
|
||||
"7/3 -> 17/6: {\\"s\\":\\"bd\\"}",
|
||||
"17/6 -> 10/3: {\\"s\\":\\"sd\\"}",
|
||||
"3/1 -> 7/2: {\\"s\\":\\"bd\\"}",
|
||||
"7/2 -> 4/1: {\\"s\\":\\"sd\\"}",
|
||||
"8333333333333333/3125000000000000 -> 4947916666666667/1562500000000000: {\\"s\\":\\"sd\\"}",
|
||||
"4947916666666667/1562500000000000 -> 5729166666666667/1562500000000000: {\\"s\\":\\"bd\\"}",
|
||||
"5729166666666667/1562500000000000 -> 6510416666666667/1562500000000000: {\\"s\\":\\"sd\\"}",
|
||||
"8854166666666667/3125000000000000 -> 5208333333333333/1562500000000000: {\\"s\\":\\"sd\\"}",
|
||||
"5208333333333333/1562500000000000 -> 5989583333333333/1562500000000000: {\\"s\\":\\"bd\\"}",
|
||||
"5989583333333333/1562500000000000 -> 6770833333333333/1562500000000000: {\\"s\\":\\"sd\\"}",
|
||||
"8/3 -> 19/6: {\\"s\\":\\"sd\\"}",
|
||||
"19/6 -> 11/3: {\\"s\\":\\"bd\\"}",
|
||||
"11/3 -> 25/6: {\\"s\\":\\"sd\\"}",
|
||||
"17/6 -> 10/3: {\\"s\\":\\"sd\\"}",
|
||||
"10/3 -> 23/6: {\\"s\\":\\"bd\\"}",
|
||||
"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`] = `
|
||||
[
|
||||
"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`] = `
|
||||
[
|
||||
"0/1 -> 1/2: {\\"s\\":\\"bd\\"}",
|
||||
|
||||
@@ -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?
|
||||
@@ -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.
|
||||
@@ -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>
|
||||
Generated
+5982
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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} />;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -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'),
|
||||
);
|
||||
@@ -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)`}
|
||||
/>
|
||||
@@ -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')],
|
||||
};
|
||||
@@ -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()],
|
||||
});
|
||||
Reference in New Issue
Block a user