Merge branch 'main' into fix-tutorial-bugs
This commit is contained in:
commit
85d0d06cab
33 changed files with 3796 additions and 2764 deletions
|
|
@ -787,6 +787,6 @@ controls.createParam = (name) => {
|
|||
};
|
||||
|
||||
controls.createParams = (...names) =>
|
||||
names.reduce((acc, name) => Object.assign(acc, { [name]: createParam(name) }), {});
|
||||
names.reduce((acc, name) => Object.assign(acc, { [name]: controls.createParam(name) }), {});
|
||||
|
||||
export default controls;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
|||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Pattern, getTime } from './index.mjs';
|
||||
import { Pattern, getTime, State, TimeSpan } from './index.mjs';
|
||||
|
||||
export const getDrawContext = (id = 'test-canvas') => {
|
||||
let canvas = document.querySelector('#' + id);
|
||||
|
|
|
|||
65
packages/core/examples/without-audio.html
Normal file
65
packages/core/examples/without-audio.html
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Buildless Vanilla Strudel REPL</title>
|
||||
</head>
|
||||
<body style="margin: 0; background: #222">
|
||||
<div style="display: grid; height: 100vh">
|
||||
<textarea
|
||||
id="text"
|
||||
style="font-size: 2em; border: 0; color: white; background: transparent; outline: none; padding: 20px"
|
||||
spellcheck="false"
|
||||
>
|
||||
// LOADING</textarea
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
id="start"
|
||||
style="
|
||||
position: absolute;
|
||||
border-radius: 10px;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
padding: 20px;
|
||||
border: 2px solid white;
|
||||
background: transparent;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
"
|
||||
>
|
||||
evaluate
|
||||
</button>
|
||||
<div id="output"></div>
|
||||
<script type="module">
|
||||
import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel.cycles/core@0.4.1';
|
||||
import { transpiler } from 'https://cdn.skypack.dev/@strudel.cycles/transpiler@0.4.1';
|
||||
|
||||
const modules = [
|
||||
import('https://cdn.skypack.dev/@strudel.cycles/core@0.4.1'),
|
||||
import('https://cdn.skypack.dev/@strudel.cycles/mini@0.4.1'),
|
||||
];
|
||||
|
||||
const input = document.getElementById('text');
|
||||
|
||||
Promise.all(modules).then(() => {
|
||||
input.innerHTML = `note("<c3 [d3 e3]>").cutoff(1000)`;
|
||||
document.getElementById('start').addEventListener('click', () => {
|
||||
evaluate(input.value);
|
||||
});
|
||||
});
|
||||
evalScope(controls, ...modules);
|
||||
|
||||
const { evaluate } = repl({
|
||||
defaultOutput: (hap, deadline, duration) => {
|
||||
console.log(deadline, duration, hap.value);
|
||||
},
|
||||
getTime: () => Date.now() / 1000,
|
||||
transpiler,
|
||||
beforeEval: (code) => console.log('evaluate', code),
|
||||
afterEval: (code) => {},
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
|||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Pattern } from './index.mjs';
|
||||
import { Pattern, toMidi, getDrawContext } from './index.mjs';
|
||||
|
||||
const scale = (normalized, min, max) => normalized * (max - min) + min;
|
||||
const getValue = (e) => {
|
||||
|
|
|
|||
|
|
@ -40,11 +40,11 @@ export function repl({
|
|||
throw new Error('no code to evaluate');
|
||||
}
|
||||
try {
|
||||
beforeEval({ code });
|
||||
beforeEval?.({ code });
|
||||
const { pattern } = await _evaluate(code, transpiler);
|
||||
logger(`[eval] code updated`);
|
||||
scheduler.setPattern(pattern, autostart);
|
||||
afterEval({ code, pattern });
|
||||
afterEval?.({ code, pattern });
|
||||
return pattern;
|
||||
} catch (err) {
|
||||
// console.warn(`[repl] eval error: ${err.message}`);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
|||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Pattern, patternify2 } from './index.mjs';
|
||||
import { Pattern, patternify2, reify } from './index.mjs';
|
||||
|
||||
let synth;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue