Compare commits

...

10 Commits

Author SHA1 Message Date
alex 97a38a6f59 more fiddling with making module level curried functions 2022-12-05 20:45:25 +00:00
alex 986de6a73b add more toplevel functions 2022-12-05 14:51:41 +00:00
alex 1750db2f1c more strudel import fixes 2022-12-05 13:34:37 +00:00
Alex McLean eadcf25775 Merge branch 'main' into separate-alignment-methods 2022-12-05 13:22:43 +00:00
alex 1d2c4fd771 untabify 2022-12-05 13:14:19 +00:00
alex 92bf43e3a6 fix up imports from core, and remove circular deps that were breaking everything 2022-12-05 13:07:56 +00:00
alex 9dd4385bbf failing attempt at bundling everything into a single core object 2022-12-05 12:23:51 +00:00
alex 274378df42 export pattern.mjs as single object 2022-12-05 11:33:40 +00:00
alex 5ec217ea21 default alignment op to set 2022-12-05 10:10:51 +00:00
alex d2375f89ee experiment simplifying how/what matrix into separate methods 2022-11-30 08:45:34 +00:00
44 changed files with 336 additions and 257 deletions
+3 -2
View File
@@ -4,7 +4,8 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Pattern, sequence } from './pattern.mjs'; import pattern from './pattern.mjs';
const { Pattern, sequence } = pattern;
const controls = {}; const controls = {};
const generic_params = [ const generic_params = [
@@ -400,7 +401,7 @@ const generic_params = [
* @example * @example
* freq("220 110 440 110").s("superzow").osc() * freq("220 110 440 110").s("superzow").osc()
* @example * @example
* freq("110".mulOut(".5 1.5 .6 [2 3]")).s("superzow").osc() * freq("110".mul.out(".5 1.5 .6 [2 3]")).s("superzow").osc()
* *
*/ */
['f', 'freq', ''], ['f', 'freq', ''],
+5 -1
View File
@@ -4,7 +4,11 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Pattern, getTime, State, TimeSpan } from './index.mjs'; import pattern from './pattern.mjs';
const Pattern = pattern.Pattern;
import { getTime } from './time.mjs';
import { State } from './state.mjs';
import { TimeSpan } from './timespan.mjs';
export const getDrawContext = (id = 'test-canvas') => { export const getDrawContext = (id = 'test-canvas') => {
let canvas = document.querySelector('#' + id); let canvas = document.querySelector('#' + id);
+2 -1
View File
@@ -4,7 +4,8 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Pattern, timeCat } from './pattern.mjs'; import pattern from './pattern.mjs';
const { Pattern, timeCat } = pattern;
import bjork from 'bjork'; import bjork from 'bjork';
import { rotate } from './util.mjs'; import { rotate } from './util.mjs';
import Fraction from './fraction.mjs'; import Fraction from './fraction.mjs';
+2 -1
View File
@@ -4,7 +4,8 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { isPattern, Pattern } from './index.mjs'; import pattern from './pattern.mjs';
const { isPattern, Pattern } = pattern;
let scoped = false; let scoped = false;
export const evalScope = async (...args) => { export const evalScope = async (...args) => {
+28 -19
View File
@@ -4,27 +4,36 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import controls from './controls.mjs';
export * from './euclid.mjs';
import Fraction from './fraction.mjs'; import Fraction from './fraction.mjs';
import drawLine from './drawLine.mjs';
import gist from './gist.js';
import { logger } from './logger.mjs'; import { logger } from './logger.mjs';
export { Fraction, controls }; // Fraction: Fraction, drawLine: drawline, gist: gist, logger: logger,
export * from './hap.mjs';
export * from './pattern.mjs'; import controls from './controls.mjs';
export * from './signal.mjs'; import pattern from './pattern.mjs';
export * from './state.mjs';
export * from './timespan.mjs'; import * as draw from './draw.mjs';
export * from './util.mjs'; import * as euclid from './euclid.mjs';
export * from './speak.mjs'; import * as evaluate from './evaluate.mjs';
export * from './evaluate.mjs'; import * as hap from './hap.mjs';
export * from './repl.mjs'; import * as repl from './repl.mjs';
export * from './logger.mjs'; import * as pianoroll from './pianoroll.mjs';
export * from './time.mjs'; import * as signal from './signal.mjs';
export * from './draw.mjs'; import * as speak from './speak.mjs';
export * from './pianoroll.mjs'; import * as state from './state.mjs';
export * from './ui.mjs'; import * as time from './time.mjs';
export { default as drawLine } from './drawLine.mjs'; import * as timespan from './timespan.mjs';
export { default as gist } from './gist.js'; import * as ui from './ui.mjs';
import * as util from './util.mjs';
const core = { drawLine, gist, logger, Fraction, ...controls, ...pattern, ...draw, ...euclid,
...evaluate, ...hap, ...repl, ...pianoroll, ...signal, ...speak,
...state, ...time, ...timespan, ...ui, ...util
};
export default core;
// below won't work with runtime.mjs (json import fails) // below won't work with runtime.mjs (json import fails)
/* import * as p from './package.json'; /* import * as p from './package.json';
export const version = p.version; */ export const version = p.version; */
+148 -118
View File
@@ -14,15 +14,18 @@ import { compose, removeUndefineds, flatten, id, listRange, curry, mod, numeralA
import drawLine from './drawLine.mjs'; import drawLine from './drawLine.mjs';
import { logger } from './logger.mjs'; import { logger } from './logger.mjs';
// monster object collecting everything to be exported
const pattern = {};
let stringParser; let stringParser;
// parser is expected to turn a string into a pattern // parser is expected to turn a string into a pattern
// if set, the reify function will parse all strings with it // if set, the reify function will parse all strings with it
// intended to use with mini to automatically interpret all strings as mini notation // intended to use with mini to automatically interpret all strings as mini notation
export const setStringParser = (parser) => (stringParser = parser); pattern['setStringParser'] = (parser) => (stringParser = parser);
/** @class Class representing a pattern. */ /** @class Class representing a pattern. */
export class Pattern { class Pattern {
_Pattern = true; // this property is used to detect if a pattern that fails instanceof Pattern is an instance of another Pattern _Pattern = true; // this property is used to detect if a pattern that fails instanceof Pattern is an instance of another Pattern
/** /**
* Create a pattern. As an end user, you will most likely not create a Pattern directly. * Create a pattern. As an end user, you will most likely not create a Pattern directly.
@@ -1026,6 +1029,7 @@ export class Pattern {
* @example * @example
* note("c3 d3 e3 g3").rev() * note("c3 d3 e3 g3").rev()
*/ */
rev() { rev() {
const pat = this; const pat = this;
const query = function (state) { const query = function (state) {
@@ -1047,7 +1051,7 @@ export class Pattern {
} }
palindrome() { palindrome() {
return this.every(2, rev); return this.every(2, pattern.rev);
} }
juxBy(by, func) { juxBy(by, func) {
@@ -1378,6 +1382,8 @@ function groupHapsBy(eq, haps) {
return groups; return groups;
} }
pattern['Pattern'] = Pattern;
// congruent haps = haps with equal spans // congruent haps = haps with equal spans
const congruent = (a, b) => a.spanEquals(b); const congruent = (a, b) => a.spanEquals(b);
// Pattern<Hap<T>> -> Pattern<Hap<T[]>> // Pattern<Hap<T>> -> Pattern<Hap<T[]>>
@@ -1501,53 +1507,48 @@ function _composeOp(a, b, func) {
// generate methods to do what and how // generate methods to do what and how
for (const [what, [op, preprocess]] of Object.entries(composers)) { for (const [what, [op, preprocess]] of Object.entries(composers)) {
for (const how of hows) { Object.defineProperty(Pattern.prototype, what, {
Pattern.prototype[what + how] = function (...other) { // a getter that returns a function, so 'pat' can be
var pat = this; // accessed by closures that are methods of that function..
other = sequence(other); get: function() {
if (preprocess) { const pat = this;
pat = preprocess(pat);
other = preprocess(other); // wrap the 'in' function as default behaviour
} const wrapper = (...other) => pat[what]['in'](...other);
var result;
// hack to remove undefs when doing 'keepif' // add methods to that function for each behaviour
if (what === 'keepif') { for (const how of hows) {
// avoid union, as we want to throw away the value of 'b' completely wrapper[how.toLowerCase()] = function (...other) {
result = pat['_op' + how](other, (a) => (b) => op(a, b)); var howpat = pat;
result = result.removeUndefineds(); other = sequence(other);
} else { if (preprocess) {
result = pat['_op' + how](other, (a) => (b) => _composeOp(a, b, op)); howpat = preprocess(howpat);
} other = preprocess(other);
return result;
};
if (how === 'Squeeze') {
// support 'squeezeIn' longhand
Pattern.prototype[what + 'SqueezeIn'] = Pattern.prototype[what + how];
}
if (how === 'In') {
// set 'in' to default, but with magic properties to pick a different 'how'
Object.defineProperty(Pattern.prototype, what, {
// a getter that returns a function, so 'pat' can be
// accessed by closures that are methods of that function..
get: function () {
const pat = this;
// wrap the 'in' function as default behaviour
const wrapper = (...other) => pat[what + 'In'](...other);
// add methods to that function to pick alternative behaviours
for (const wraphow of hows) {
wrapper[wraphow.toLowerCase()] = (...other) => pat[what + wraphow](...other);
} }
var result;
return wrapper; // hack to remove undefs when doing 'keepif'
}, if (what === 'keepif') {
}); // avoid union, as we want to throw away the value of 'b' completely
} else { result = howpat['_op' + how](other, (a) => (b) => op(a, b));
// default what to 'set', e.g. squeeze = setSqueeze result = result.removeUndefineds();
if (what === 'set') { } else {
Pattern.prototype[how.toLowerCase()] = Pattern.prototype[what + how]; result = howpat['_op' + how](other, (a) => (b) => _composeOp(a, b, op));
}
return result;
}
} }
wrapper.squeezein = wrapper.squeeze
return wrapper;
} }
});
// Default op to 'set', e.g. pat.squeeze(pat2) = pat.set.squeeze(pat2)
for (const how of hows) {
Pattern.prototype[how.toLowerCase()] = function (...args) { return this.set[how.toLowerCase()](args) };
curry_toplevel(how.toLowerCase(), 2);
} }
curry_toplevel(what, 2);
} }
// binary composers // binary composers
@@ -1562,14 +1563,22 @@ function _composeOp(a, b, func) {
* .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~") * .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~")
* .slow(4) * .slow(4)
*/ */
Pattern.prototype.struct = Pattern.prototype.keepifOut; Pattern.prototype.struct = function(...args) { return this.keepif.out(...args) }
Pattern.prototype.structAll = Pattern.prototype.keepOut; curry_toplevel('struct', 2);
Pattern.prototype.mask = Pattern.prototype.keepifIn; Pattern.prototype.structAll = function(...args) { return this.keep.out(...args) }
Pattern.prototype.maskAll = Pattern.prototype.keepIn; curry_toplevel('structAll', 2);
Pattern.prototype.reset = Pattern.prototype.keepifTrig; Pattern.prototype.mask = function(...args) { return this.keepif.in(...args) }
Pattern.prototype.resetAll = Pattern.prototype.keepTrig; curry_toplevel('mask', 2);
Pattern.prototype.restart = Pattern.prototype.keepifTrigzero; Pattern.prototype.maskAll = function(...args) { return this.keep.in(...args) }
Pattern.prototype.restartAll = Pattern.prototype.keepTrigzero; curry_toplevel('maskAll', 2);
Pattern.prototype.reset = function(...args) { return this.keepif.trig(...args) }
curry_toplevel('reset', 2);
Pattern.prototype.resetAll = function(...args) { return this.keep.trig(...args) }
curry_toplevel('resetAll', 2);
Pattern.prototype.restart = function(...args) { return this.keepif.trigzero(...args) }
curry_toplevel('restart', 2);
Pattern.prototype.restartAll = function(...args) { return this.keep.trigzero(...args) }
curry_toplevel('restartAll', 2);
})(); })();
// methods of Pattern that get callable factories // methods of Pattern that get callable factories
@@ -1594,10 +1603,6 @@ Pattern.prototype.patternified = [
'velocity', 'velocity',
]; ];
// aliases
export const polyrhythm = stack;
export const pr = stack;
// methods that create patterns, which are added to patternified Pattern methods // methods that create patterns, which are added to patternified Pattern methods
Pattern.prototype.factories = { Pattern.prototype.factories = {
pure, pure,
@@ -1615,10 +1620,39 @@ Pattern.prototype.factories = {
}; };
// the magic happens in Pattern constructor. Keeping this in prototype enables adding methods from the outside (e.g. see tonal.ts) // the magic happens in Pattern constructor. Keeping this in prototype enables adding methods from the outside (e.g. see tonal.ts)
function curry_toplevel(name, arity=1) {
//if (name in pattern) {
// throw(`${name} already exists`);
//}
if (arity == 1) {
pattern[name] = (pat) => pat[name]();
}
else if (arity == 2) {
pattern[name] = curry((a, pat) => pat[name](a));
}
else if (arity == 3) {
pattern[name] = curry((a, b, pat) => pat[name](a, b));
}
else if (arity == 4) {
pattern[name] = curry((a, b, c, pat) => pat[name](a, b, c));
}
}
for (const x of [[1, ['inv', 'invert', 'rev']],
[2, ['chop','chunk','chunkBack', 'mask','struct','superimpose'].concat(Pattern.prototype.patternified)],
[3, ['every','juxBy','off','range','rangex','range2','when']],
[4, ['echo']]
]) {
for (const name of x[1]) {
curry_toplevel(name, x[0]);
}
}
// Elemental patterns // Elemental patterns
// Nothing // Nothing
export const silence = new Pattern(() => []); const silence = new Pattern(() => []);
pattern['silence'] = silence;
/** A discrete value that repeats once per cycle. /** A discrete value that repeats once per cycle.
* *
@@ -1626,14 +1660,15 @@ export const silence = new Pattern(() => []);
* @example * @example
* pure('e4') // "e4" * pure('e4') // "e4"
*/ */
export function pure(value) { function pure(value) {
function query(state) { function query(state) {
return state.span.spanCycles.map((subspan) => new Hap(Fraction(subspan.begin).wholeCycle(), subspan, value)); return state.span.spanCycles.map((subspan) => new Hap(Fraction(subspan.begin).wholeCycle(), subspan, value));
} }
return new Pattern(query); return new Pattern(query);
} }
pattern['pure'] = pure;
export function isPattern(thing) { function isPattern(thing) {
// thing?.constructor?.name !== 'Pattern' // <- this will fail when code is mangled // thing?.constructor?.name !== 'Pattern' // <- this will fail when code is mangled
const is = thing instanceof Pattern || thing?._Pattern; const is = thing instanceof Pattern || thing?._Pattern;
// TODO: find out how to check wrong core dependency. below will never work !thing === 'undefined' // TODO: find out how to check wrong core dependency. below will never work !thing === 'undefined'
@@ -1648,8 +1683,9 @@ export function isPattern(thing) {
} */ } */
return is; return is;
} }
pattern['isPattern'] = isPattern;
export function reify(thing) { function reify(thing) {
// Turns something into a pattern, unless it's already a pattern // Turns something into a pattern, unless it's already a pattern
if (isPattern(thing)) { if (isPattern(thing)) {
return thing; return thing;
@@ -1659,6 +1695,7 @@ export function reify(thing) {
} }
return pure(thing); return pure(thing);
} }
pattern['reify'] = reify;
/** The given items are played at the same time at the same length. /** The given items are played at the same time at the same length.
* *
@@ -1666,12 +1703,20 @@ export function reify(thing) {
* @example * @example
* stack(g3, b3, [e4, d4]).note() // "g3,b3,[e4,d4]".note() * stack(g3, b3, [e4, d4]).note() // "g3,b3,[e4,d4]".note()
*/ */
export function stack(...pats) { function stack(...pats) {
// Array test here is to avoid infinite recursions.. // Array test here is to avoid infinite recursions..
pats = pats.map((pat) => (Array.isArray(pat) ? sequence(...pat) : reify(pat))); pats = pats.map((pat) => (Array.isArray(pat) ? sequence(...pat) : reify(pat)));
const query = (state) => flatten(pats.map((pat) => pat.query(state))); const query = (state) => flatten(pats.map((pat) => pat.query(state)));
return new Pattern(query); return new Pattern(query);
} }
pattern['stack'] = stack;
function polyrhythm(...pats) {
return stack(...pats);
}
function pr(...pats) {
return stack(...pats);
}
pattern['pr'] = pattern['polyrhythm'] = pattern['stack'];
/** Concatenation: combines a list of patterns, switching between them successively, one per cycle: /** Concatenation: combines a list of patterns, switching between them successively, one per cycle:
* *
@@ -1682,7 +1727,7 @@ export function stack(...pats) {
* slowcat(e5, b4, [d5, c5]) * slowcat(e5, b4, [d5, c5])
* *
*/ */
export function slowcat(...pats) { function slowcat(...pats) {
// Array test here is to avoid infinite recursions.. // Array test here is to avoid infinite recursions..
pats = pats.map((pat) => (Array.isArray(pat) ? sequence(...pat) : reify(pat))); pats = pats.map((pat) => (Array.isArray(pat) ? sequence(...pat) : reify(pat)));
@@ -1702,12 +1747,13 @@ export function slowcat(...pats) {
}; };
return new Pattern(query).splitQueries(); return new Pattern(query).splitQueries();
} }
pattern['slowcat'] = slowcat;
/** Concatenation: combines a list of patterns, switching between them successively, one per cycle. Unlike slowcat, this version will skip cycles. /** Concatenation: combines a list of patterns, switching between them successively, one per cycle. Unlike slowcat, this version will skip cycles.
* @param {...any} items - The items to concatenate * @param {...any} items - The items to concatenate
* @return {Pattern} * @return {Pattern}
*/ */
export function slowcatPrime(...pats) { function slowcatPrime(...pats) {
pats = pats.map(reify); pats = pats.map(reify);
const query = function (state) { const query = function (state) {
const pat_n = Math.floor(state.span.begin) % pats.length; const pat_n = Math.floor(state.span.begin) % pats.length;
@@ -1716,6 +1762,7 @@ export function slowcatPrime(...pats) {
}; };
return new Pattern(query).splitQueries(); return new Pattern(query).splitQueries();
} }
pattern['slowcatPrime'] = slowcatPrime;
/** Concatenation: as with {@link slowcat}, but squashes a cycle from each pattern into one cycle /** Concatenation: as with {@link slowcat}, but squashes a cycle from each pattern into one cycle
* *
@@ -1728,9 +1775,10 @@ export function slowcatPrime(...pats) {
* // sequence(e5, b4, [d5, c5]) * // sequence(e5, b4, [d5, c5])
* // seq(e5, b4, [d5, c5]) * // seq(e5, b4, [d5, c5])
*/ */
export function fastcat(...pats) { function fastcat(...pats) {
return slowcat(...pats)._fast(pats.length); return slowcat(...pats)._fast(pats.length);
} }
pattern['fastcat'] = fastcat;
/** The given items are con**cat**enated, where each one takes one cycle. Synonym: slowcat /** The given items are con**cat**enated, where each one takes one cycle. Synonym: slowcat
* *
@@ -1740,16 +1788,17 @@ export function fastcat(...pats) {
* cat(e5, b4, [d5, c5]).note() // "<e5 b4 [d5 c5]>".note() * cat(e5, b4, [d5, c5]).note() // "<e5 b4 [d5 c5]>".note()
* *
*/ */
export function cat(...pats) { function cat(...pats) {
return slowcat(...pats); return slowcat(...pats);
} }
pattern['cat'] = cat;
/** Like {@link seq}, but each step has a length, relative to the whole. /** Like {@link seq}, but each step has a length, relative to the whole.
* @return {Pattern} * @return {Pattern}
* @example * @example
* timeCat([3,e3],[1, g3]).note() // "e3@3 g3".note() * timeCat([3,e3],[1, g3]).note() // "e3@3 g3".note()
*/ */
export function timeCat(...timepats) { function timeCat(...timepats) {
const total = timepats.map((a) => a[0]).reduce((a, b) => a.add(b), Fraction(0)); const total = timepats.map((a) => a[0]).reduce((a, b) => a.add(b), Fraction(0));
let begin = Fraction(0); let begin = Fraction(0);
const pats = []; const pats = [];
@@ -1760,20 +1809,23 @@ export function timeCat(...timepats) {
} }
return stack(...pats); return stack(...pats);
} }
pattern['timeCat'] = timeCat;
/** See {@link fastcat} */ /** See {@link fastcat} */
export function sequence(...pats) { function sequence(...pats) {
return fastcat(...pats); return fastcat(...pats);
} }
pattern['sequence'] = sequence;
/** Like **cat**, but the items are crammed into one cycle. Synonyms: fastcat, sequence /** Like **cat**, but the items are crammed into one cycle. Synonyms: fastcat, sequence
* @example * @example
* seq(e5, b4, [d5, c5]).note() // "e5 b4 [d5 c5]".note() * seq(e5, b4, [d5, c5]).note() // "e5 b4 [d5 c5]".note()
* *
*/ */
export function seq(...pats) { function seq(...pats) {
return fastcat(...pats); return fastcat(...pats);
} }
pattern['seq'] = seq;
function _sequenceCount(x) { function _sequenceCount(x) {
if (Array.isArray(x)) { if (Array.isArray(x)) {
@@ -1788,7 +1840,7 @@ function _sequenceCount(x) {
return [reify(x), 1]; return [reify(x), 1];
} }
export function polymeterSteps(steps, ...args) { function polymeterSteps(steps, ...args) {
const seqs = args.map((a) => _sequenceCount(a)); const seqs = args.map((a) => _sequenceCount(a));
if (seqs.length == 0) { if (seqs.length == 0) {
return silence; return silence;
@@ -1809,57 +1861,27 @@ export function polymeterSteps(steps, ...args) {
} }
return stack(...pats); return stack(...pats);
} }
pattern['polymeterSteps'] = polymeterSteps;
export function polymeter(...args) { function polymeter(...args) {
return polymeterSteps(0, ...args); return polymeterSteps(0, ...args);
} }
pattern['polymeter'] = polymeter;
// alias function pm(...args) {
export function pm(...args) { return polymeter(...args);
polymeter(...args);
} }
pattern['pm'] = polymeter;
export const add = curry((a, pat) => pat.add(a));
export const chop = curry((a, pat) => pat.chop(a));
export const chunk = curry((a, pat) => pat.chunk(a));
export const chunkBack = curry((a, pat) => pat.chunkBack(a));
export const div = curry((a, pat) => pat.div(a));
export const early = curry((a, pat) => pat.early(a));
export const echo = curry((a, b, c, pat) => pat.echo(a, b, c));
export const every = curry((i, f, pat) => pat.every(i, f));
export const fast = curry((a, pat) => pat.fast(a));
export const inv = (pat) => pat.inv();
export const invert = (pat) => pat.invert();
export const iter = curry((a, pat) => pat.iter(a));
export const iterBack = curry((a, pat) => pat.iterBack(a));
export const jux = curry((f, pat) => pat.jux(f));
export const juxBy = curry((by, f, pat) => pat.juxBy(by, f));
export const late = curry((a, pat) => pat.late(a));
export const linger = curry((a, pat) => pat.linger(a));
export const mask = curry((a, pat) => pat.mask(a));
export const mul = curry((a, pat) => pat.mul(a));
export const off = curry((t, f, pat) => pat.off(t, f));
export const ply = curry((a, pat) => pat.ply(a));
export const range = curry((a, b, pat) => pat.range(a, b));
export const rangex = curry((a, b, pat) => pat.rangex(a, b));
export const range2 = curry((a, b, pat) => pat.range2(a, b));
export const rev = (pat) => pat.rev();
export const slow = curry((a, pat) => pat.slow(a));
export const struct = curry((a, pat) => pat.struct(a));
export const sub = curry((a, pat) => pat.sub(a));
export const superimpose = curry((array, pat) => pat.superimpose(...array));
export const set = curry((a, pat) => pat.set(a));
export const when = curry((binary, f, pat) => pat.when(binary, f));
// problem: curried functions with spread arguments must have pat at the beginning // problem: curried functions with spread arguments must have pat at the beginning
// with this, we cannot keep the pattern open at the end.. solution for now: use array to keep using pat as last arg // with this, we cannot keep the pattern open at the end.. solution for now: use array to keep using pat as last arg
// these are the core composable functions. they are extended with Pattern.prototype.define below // these are the core composable functions. they are extended with Pattern.prototype.define below
Pattern.prototype.composable = { fast, slow, early, late, superimpose }; Pattern.prototype.composable = {fast: pattern.fast, slow: pattern.slow, early: pattern.early, late: pattern.late, superimpose: pattern.superimpose };
// adds Pattern.prototype.composable to given function as child functions // adds Pattern.prototype.composable to given function as child functions
// then you can do transpose(2).late(0.2) instead of x => x.transpose(2).late(0.2) // then you can do transpose(2).late(0.2) instead of x => x.transpose(2).late(0.2)
export function makeComposable(func) { function makeComposable(func) {
Object.entries(Pattern.prototype.composable).forEach(([functionName, composable]) => { Object.entries(Pattern.prototype.composable).forEach(([functionName, composable]) => {
// compose with dot // compose with dot
func[functionName] = (...args) => { func[functionName] = (...args) => {
@@ -1872,25 +1894,31 @@ export function makeComposable(func) {
}); });
return func; return func;
} }
pattern['makeComposable'] = makeComposable;
export const patternify2 = (f) => (pata, patb, pat) => const patternify2 = (f) => (pata, patb, pat) =>
pata pata
.fmap((a) => (b) => f.call(pat, a, b)) .fmap((a) => (b) => f.call(pat, a, b))
.appLeft(patb) .appLeft(patb)
.innerJoin(); .innerJoin();
export const patternify3 = (f) => (pata, patb, patc, pat) => pattern['patternify2'] = patternify2;
const patternify3 = (f) => (pata, patb, patc, pat) =>
pata pata
.fmap((a) => (b) => (c) => f.call(pat, a, b, c)) .fmap((a) => (b) => (c) => f.call(pat, a, b, c))
.appLeft(patb) .appLeft(patb)
.appLeft(patc) .appLeft(patc)
.innerJoin(); .innerJoin();
export const patternify4 = (f) => (pata, patb, patc, patd, pat) => pattern['patternify3'] = patternify3;
const patternify4 = (f) => (pata, patb, patc, patd, pat) =>
pata pata
.fmap((a) => (b) => (c) => (d) => f.call(pat, a, b, c, d)) .fmap((a) => (b) => (c) => (d) => f.call(pat, a, b, c, d))
.appLeft(patb) .appLeft(patb)
.appLeft(patc) .appLeft(patc)
.appLeft(patd) .appLeft(patd)
.innerJoin(); .innerJoin();
pattern['patternify4'] = patternify4;
Pattern.prototype.echo = function (...args) { Pattern.prototype.echo = function (...args) {
args = args.map(reify); args = args.map(reify);
@@ -1942,6 +1970,7 @@ Pattern.prototype.range2 = function (...args) {
}; };
// call this after all Pattern.prototype.define calls have been executed! (right before evaluate) // call this after all Pattern.prototype.define calls have been executed! (right before evaluate)
// (but isn't this called for every define call anyway?)
Pattern.prototype.bootstrap = function () { Pattern.prototype.bootstrap = function () {
// makeComposable(Pattern.prototype); // makeComposable(Pattern.prototype);
const bootstrapped = Object.fromEntries( const bootstrapped = Object.fromEntries(
@@ -1970,18 +1999,18 @@ Pattern.prototype.bootstrap = function () {
// a getter that returns a function, so 'pat' can be // a getter that returns a function, so 'pat' can be
// accessed by closures that are methods of that function.. // accessed by closures that are methods of that function..
get: function() { get: function() {
const pat = this; const pat = this;
// wrap the default behaviour // wrap the default behaviour
const wrapper = pat.patternify(x => x.innerJoin(), func); const wrapper = pat.patternify(x => x.innerJoin(), func);
// add the variants // add the variants
wrapper['in'] = pat.patternify(x => x.innerJoin(), func); wrapper['in'] = pat.patternify(x => x.innerJoin(), func);
wrapper['out'] = pat.patternify(x => x.outerJoin(), func); wrapper['out'] = pat.patternify(x => x.outerJoin(), func);
wrapper['trig'] = pat.patternify(x => x.trigJoin(), func); wrapper['trig'] = pat.patternify(x => x.trigJoin(), func);
wrapper['trigzero'] = pat.patternify(x => x.trigzeroJoin(), func); wrapper['trigzero'] = pat.patternify(x => x.trigzeroJoin(), func);
wrapper['squeeze'] = pat.patternify(x => x.squeezeJoin(), func); wrapper['squeeze'] = pat.patternify(x => x.squeezeJoin(), func);
return wrapper; return wrapper;
} }
}); });
*/ */
@@ -2018,4 +2047,5 @@ Pattern.prototype.define = (name, func, options = {}) => {
// Pattern.prototype.define('early', (a, pat) => pat.early(a), { patternified: true, composable: true }); // Pattern.prototype.define('early', (a, pat) => pat.early(a), { patternified: true, composable: true });
Pattern.prototype.define('hush', (pat) => pat.hush(), { patternified: false, composable: true }); Pattern.prototype.define('hush', (pat) => pat.hush(), { patternified: false, composable: true });
Pattern.prototype.define('bypass', (pat) => pat.bypass(1), { patternified: true, composable: true });
export default pattern;
+5 -1
View File
@@ -4,7 +4,11 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Pattern, toMidi, getDrawContext } from './index.mjs'; import pattern from './pattern.mjs';
const { Pattern } = pattern;
import { toMidi } from './util.mjs';
import { getDrawContext } from './draw.mjs';
const scale = (normalized, min, max) => normalized * (max - min) + min; const scale = (normalized, min, max) => normalized * (max - min) + min;
const getValue = (e) => { const getValue = (e) => {
+2 -1
View File
@@ -5,7 +5,8 @@ This program is free software: you can redistribute it and/or modify it under th
*/ */
import { Hap } from './hap.mjs'; import { Hap } from './hap.mjs';
import { Pattern, fastcat, reify, silence, stack, isPattern } from './pattern.mjs'; import pattern from './pattern.mjs';
const { Pattern, fastcat, reify, silence, stack, isPattern } = pattern;
import Fraction from './fraction.mjs'; import Fraction from './fraction.mjs';
import { id } from './util.mjs'; import { id } from './util.mjs';
+2 -1
View File
@@ -4,7 +4,8 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Pattern, patternify2, reify } from './index.mjs'; import pattern from './pattern.mjs';
const {Pattern, patternify2, reify} = pattern;
let synth; let synth;
try { try {
+2 -1
View File
@@ -4,7 +4,8 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { fastcat, stack, slowcat, silence, pure } from '../pattern.mjs'; import pattern from '../pattern.mjs';
const { fastcat, stack, slowcat, silence, pure } = pattern;
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import drawLine from '../drawLine.mjs'; import drawLine from '../drawLine.mjs';
+37 -36
View File
@@ -8,10 +8,9 @@ import Fraction from 'fraction.js';
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { import core from '../index.mjs';
TimeSpan,
Hap, const {
State,
Pattern, Pattern,
pure, pure,
stack, stack,
@@ -29,6 +28,12 @@ import {
sub, sub,
mul, mul,
div, div,
id,
ply,
rev,
TimeSpan,
Hap,
State,
saw, saw,
saw2, saw2,
isaw, isaw,
@@ -39,11 +44,7 @@ import {
square2, square2,
tri, tri,
tri2, tri2,
id, time} = core;
ply,
rev,
time,
} from '../index.mjs';
import { steady } from '../signal.mjs'; import { steady } from '../signal.mjs';
@@ -156,32 +157,32 @@ describe('Pattern', () => {
describe('add()', () => { describe('add()', () => {
it('can structure In()', () => { it('can structure In()', () => {
expect(pure(3).add(pure(4)).query(st(0, 1))[0].value).toBe(7); expect(pure(3).add(pure(4)).query(st(0, 1))[0].value).toBe(7);
expect(pure(3).addIn(pure(4)).query(st(0, 1))[0].value).toBe(7); expect(pure(3).add.in(pure(4)).query(st(0, 1))[0].value).toBe(7);
}); });
it('can structure Out()', () => { it('can structure Out()', () => {
sameFirst(sequence(1, 2).addOut(4), sequence(5, 6).struct(true)); sameFirst(sequence(1, 2).add.out(4), sequence(5, 6).struct(true));
}); });
it('can Mix() structure', () => { it('can Mix() structure', () => {
expect(sequence(1, 2).addMix(silence, 5, silence).firstCycle()).toStrictEqual([ expect(sequence(1, 2).add.mix(silence, 5, silence).firstCycle()).toStrictEqual([
new Hap(ts(1 / 3, 1 / 2), ts(1 / 3, 1 / 2), 6), new Hap(ts(1 / 3, 1 / 2), ts(1 / 3, 1 / 2), 6),
new Hap(ts(1 / 2, 2 / 3), ts(1 / 2, 2 / 3), 7), new Hap(ts(1 / 2, 2 / 3), ts(1 / 2, 2 / 3), 7),
]); ]);
}); });
it('can Trig() structure', () => { it('can Trig() structure', () => {
sameFirst( sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).addTrig(20, 30).early(2), slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).add.trig(20, 30).early(2),
sequence(26, 27, 36, 37), sequence(26, 27, 36, 37),
); );
}); });
it('can Trigzero() structure', () => { it('can Trigzero() structure', () => {
sameFirst( sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).addTrigzero(20, 30).early(2), slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).add.trigzero(20, 30).early(2),
sequence(21, 22, 31, 32), sequence(21, 22, 31, 32),
); );
}); });
it('can Squeeze() structure', () => { it('can Squeeze() structure', () => {
sameFirst( sameFirst(
sequence(1, [2, 3]).addSqueeze(sequence(10, 20, 30)), sequence(1, [2, 3]).add.squeeze(sequence(10, 20, 30)),
sequence( sequence(
[11, 21, 31], [11, 21, 31],
[ [
@@ -193,7 +194,7 @@ describe('Pattern', () => {
}); });
it('can SqueezeOut() structure', () => { it('can SqueezeOut() structure', () => {
sameFirst( sameFirst(
sequence(1, [2, 3]).addSqueezeOut(10, 20, 30), sequence(1, [2, 3]).add.squeezeout(10, 20, 30),
sequence([11, [12, 13]], [21, [22, 23]], [31, [32, 33]]), sequence([11, [12, 13]], [21, [22, 23]], [31, [32, 33]]),
); );
}); });
@@ -204,32 +205,32 @@ describe('Pattern', () => {
describe('keep()', () => { describe('keep()', () => {
it('can structure In()', () => { it('can structure In()', () => {
expect(pure(3).keep(pure(4)).query(st(0, 1))[0].value).toBe(3); expect(pure(3).keep(pure(4)).query(st(0, 1))[0].value).toBe(3);
expect(pure(3).keepIn(pure(4)).query(st(0, 1))[0].value).toBe(3); expect(pure(3).keep.in(pure(4)).query(st(0, 1))[0].value).toBe(3);
}); });
it('can structure Out()', () => { it('can structure Out()', () => {
sameFirst(sequence(1, 2).keepOut(4), sequence(1, 2).struct(true)); sameFirst(sequence(1, 2).keep.out(4), sequence(1, 2).struct(true));
}); });
it('can Mix() structure', () => { it('can Mix() structure', () => {
expect(sequence(1, 2).keepMix(silence, 5, silence).firstCycle()).toStrictEqual([ expect(sequence(1, 2).keep.mix(silence, 5, silence).firstCycle()).toStrictEqual([
new Hap(ts(1 / 3, 1 / 2), ts(1 / 3, 1 / 2), 1), new Hap(ts(1 / 3, 1 / 2), ts(1 / 3, 1 / 2), 1),
new Hap(ts(1 / 2, 2 / 3), ts(1 / 2, 2 / 3), 2), new Hap(ts(1 / 2, 2 / 3), ts(1 / 2, 2 / 3), 2),
]); ]);
}); });
it('can Trig() structure', () => { it('can Trig() structure', () => {
sameFirst( sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keepTrig(20, 30).early(2), slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keep.trig(20, 30).early(2),
sequence(6, 7, 6, 7), sequence(6, 7, 6, 7),
); );
}); });
it('can Trigzero() structure', () => { it('can Trigzero() structure', () => {
sameFirst( sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keepTrigzero(20, 30).early(2), slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keep.trigzero(20, 30).early(2),
sequence(1, 2, 1, 2), sequence(1, 2, 1, 2),
); );
}); });
it('can Squeeze() structure', () => { it('can Squeeze() structure', () => {
sameFirst( sameFirst(
sequence(1, [2, 3]).keepSqueeze(sequence(10, 20, 30)), sequence(1, [2, 3]).keep.squeeze(sequence(10, 20, 30)),
sequence( sequence(
[1, 1, 1], [1, 1, 1],
[ [
@@ -239,39 +240,39 @@ describe('Pattern', () => {
), ),
); );
}); });
it('can SqueezeOut() structure', () => { it('can squeezeOut() structure', () => {
sameFirst(sequence(1, [2, 3]).keepSqueezeOut(10, 20, 30), sequence([1, [2, 3]], [1, [2, 3]], [1, [2, 3]])); sameFirst(sequence(1, [2, 3]).keep.squeezeout(10, 20, 30), sequence([1, [2, 3]], [1, [2, 3]], [1, [2, 3]]));
}); });
}); });
describe('keepif()', () => { describe('keepif()', () => {
it('can structure In()', () => { it('can structure In()', () => {
sameFirst(sequence(3, 4).keepif(true, false), sequence(3, silence)); sameFirst(sequence(3, 4).keepif(true, false), sequence(3, silence));
sameFirst(sequence(3, 4).keepifIn(true, false), sequence(3, silence)); sameFirst(sequence(3, 4).keepif.in(true, false), sequence(3, silence));
}); });
it('can structure Out()', () => { it('can structure Out()', () => {
sameFirst(pure(1).keepifOut(true, false), sequence(1, silence)); sameFirst(pure(1).keepif.out(true, false), sequence(1, silence));
}); });
it('can Mix() structure', () => { it('can Mix() structure', () => {
expect(sequence(1, 2).keepifMix(false, true, false).firstCycle()).toStrictEqual([ expect(sequence(1, 2).keepif.mix(false, true, false).firstCycle()).toStrictEqual([
new Hap(ts(1 / 3, 1 / 2), ts(1 / 3, 1 / 2), 1), new Hap(ts(1 / 3, 1 / 2), ts(1 / 3, 1 / 2), 1),
new Hap(ts(1 / 2, 2 / 3), ts(1 / 2, 2 / 3), 2), new Hap(ts(1 / 2, 2 / 3), ts(1 / 2, 2 / 3), 2),
]); ]);
}); });
it('can Trig() structure', () => { it('can Trig() structure', () => {
sameFirst( sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keepifTrig(false, true).early(2), slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keepif.trig(false, true).early(2),
sequence(silence, silence, 6, 7), sequence(silence, silence, 6, 7),
); );
}); });
it('can Trigzero() structure', () => { it('can Trigzero() structure', () => {
sameFirst( sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keepifTrigzero(false, true).early(2), slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keepif.trigzero(false, true).early(2),
sequence(silence, silence, 1, 2), sequence(silence, silence, 1, 2),
); );
}); });
it('can Squeeze() structure', () => { it('can Squeeze() structure', () => {
sameFirst( sameFirst(
sequence(1, [2, 3]).keepifSqueeze(sequence(true, true, false)), sequence(1, [2, 3]).keepif.squeeze(sequence(true, true, false)),
sequence( sequence(
[1, 1, silence], [1, 1, silence],
[ [
@@ -282,7 +283,7 @@ describe('Pattern', () => {
); );
}); });
it('can SqueezeOut() structure', () => { it('can SqueezeOut() structure', () => {
sameFirst(sequence(1, [2, 3]).keepifSqueezeOut(true, true, false), sequence([1, [2, 3]], [1, [2, 3]], silence)); sameFirst(sequence(1, [2, 3]).keepif.squeezeout(true, true, false), sequence([1, [2, 3]], [1, [2, 3]], silence));
}); });
}); });
describe('sub()', () => { describe('sub()', () => {
@@ -320,15 +321,15 @@ describe('Pattern', () => {
it('Can set things with plain values', () => { it('Can set things with plain values', () => {
sameFirst(sequence(1, 2, 3).set(4), sequence(4).fast(3)); sameFirst(sequence(1, 2, 3).set(4), sequence(4).fast(3));
}); });
describe('setOut()', () => { describe('set.out()', () => {
it('Can set things with structure from second pattern', () => { it('Can set things with structure from second pattern', () => {
sameFirst(sequence(1, 2).setOut(4), pure(4).mask(true, true)); sameFirst(sequence(1, 2).set.out(4), pure(4).mask(true, true));
}); });
}); });
describe('setSqueeze()', () => { describe('set.squeeze()', () => {
it('Can squeeze one pattern inside the haps of another', () => { it('Can squeeze one pattern inside the haps of another', () => {
sameFirst( sameFirst(
sequence(1, [2, 3]).setSqueeze(sequence('a', 'b', 'c')), sequence(1, [2, 3]).set.squeeze(sequence('a', 'b', 'c')),
sequence( sequence(
['a', 'b', 'c'], ['a', 'b', 'c'],
[ [
@@ -338,7 +339,7 @@ describe('Pattern', () => {
), ),
); );
sameFirst( sameFirst(
sequence(1, [2, 3]).setSqueeze('a', 'b', 'c'), sequence(1, [2, 3]).set.squeeze('a', 'b', 'c'),
sequence( sequence(
['a', 'b', 'c'], ['a', 'b', 'c'],
[ [
+2 -1
View File
@@ -4,7 +4,8 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { pure } from '../pattern.mjs'; import pattern from '../pattern.mjs';
const { pure } = pattern;
import { import {
isNote, isNote,
tokenizeNote, tokenizeNote,
+2 -1
View File
@@ -1,4 +1,5 @@
import { getFrequency, logger, Pattern } from '@strudel.cycles/core'; import strudel from '@strudel.cycles/core';
const { getFrequency, logger, Pattern } = strudel;
import { getAudioContext } from '@strudel.cycles/webaudio'; import { getAudioContext } from '@strudel.cycles/webaudio';
import csd from './project.csd?raw'; import csd from './project.csd?raw';
// import livecodeOrc from './livecode.orc?raw'; // import livecodeOrc from './livecode.orc?raw';
+2 -2
View File
@@ -4,9 +4,9 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { evaluate as _evaluate } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
import shapeshifter from './shapeshifter.mjs'; import shapeshifter from './shapeshifter.mjs';
export const evaluate = async (code) => { export const evaluate = async (code) => {
return _evaluate(code, shapeshifter); return core.evaluate(code, shapeshifter);
}; };
+1 -1
View File
@@ -21,7 +21,7 @@ import {
import shiftCodegen from 'shift-codegen'; import shiftCodegen from 'shift-codegen';
const codegen = shiftCodegen.default || shiftCodegen; // parcel module resolution fuckup const codegen = shiftCodegen.default || shiftCodegen; // parcel module resolution fuckup
import * as strudel from '@strudel.cycles/core'; import strudel from '@strudel.cycles/core';
const { Pattern } = strudel; const { Pattern } = strudel;
+1 -1
View File
@@ -8,7 +8,7 @@ import { expect, describe, it } from 'vitest';
import { evaluate } from '../evaluate.mjs'; import { evaluate } from '../evaluate.mjs';
import { mini } from '@strudel.cycles/mini'; import { mini } from '@strudel.cycles/mini';
import * as strudel from '@strudel.cycles/core'; import strudel from '@strudel.cycles/core';
const { fastcat, evalScope } = strudel; const { fastcat, evalScope } = strudel;
describe('evaluate', async () => { describe('evaluate', async () => {
+2 -1
View File
@@ -5,7 +5,8 @@ This program is free software: you can redistribute it and/or modify it under th
*/ */
import * as _WebMidi from 'webmidi'; import * as _WebMidi from 'webmidi';
import { Pattern, isPattern, isNote, getPlayableNoteValue, logger } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const { Pattern, isPattern, isNote, getPlayableNoteValue, logger } = core;
import { getAudioContext } from '@strudel.cycles/webaudio'; import { getAudioContext } from '@strudel.cycles/webaudio';
// if you use WebMidi from outside of this package, make sure to import that instance: // if you use WebMidi from outside of this package, make sure to import that instance:
+4 -4
View File
@@ -5,10 +5,10 @@ This program is free software: you can redistribute it and/or modify it under th
*/ */
import * as krill from './krill-parser.js'; import * as krill from './krill-parser.js';
import * as strudel from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
// import { addMiniLocations } from '@strudel.cycles/eval/shapeshifter.mjs'; // import { addMiniLocations } from '@strudel.cycles/eval/shapeshifter.mjs';
const { pure, Pattern, Fraction, stack, slowcat, sequence, timeCat, silence, reify } = strudel; const { pure, Pattern, stack, slowcat, sequence, timeCat, silence, reify, rand, chooseInWith, Fraction } = core;
var _seedState = 0; var _seedState = 0;
const randOffset = 0.0002; const randOffset = 0.0002;
@@ -31,7 +31,7 @@ const applyOptions = (parent) => (pat, i) => {
return pat.euclid(operator.arguments_.pulse, operator.arguments_.step, operator.arguments_.rotation); return pat.euclid(operator.arguments_.pulse, operator.arguments_.step, operator.arguments_.rotation);
case 'degradeBy': case 'degradeBy':
return reify(pat)._degradeByWith( return reify(pat)._degradeByWith(
strudel.rand.early(randOffset * _nextSeed()).segment(1), rand.early(randOffset * _nextSeed()).segment(1),
operator.arguments_.amount, operator.arguments_.amount,
); );
// TODO: case 'fixed-step': "%" // TODO: case 'fixed-step': "%"
@@ -96,7 +96,7 @@ export function patternifyAST(ast) {
return stack(...children); return stack(...children);
} }
if (alignment === 'r') { if (alignment === 'r') {
return strudel.chooseInWith(strudel.rand.early(randOffset * _nextSeed()).segment(1), children); return chooseInWith(rand.early(randOffset * _nextSeed()).segment(1), children);
} }
const weightedChildren = ast.source_.some((child) => !!child.options_?.weight); const weightedChildren = ast.source_.some((child) => !!child.options_?.weight);
if (!weightedChildren && alignment === 't') { if (!weightedChildren && alignment === 't') {
+2 -1
View File
@@ -5,7 +5,8 @@ This program is free software: you can redistribute it and/or modify it under th
*/ */
import OSC from 'osc-js'; import OSC from 'osc-js';
import { logger, parseNumeral, Pattern } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const { Pattern, logger, parseNumeral } = core;
let connection; // Promise<OSC> let connection; // Promise<OSC>
function connect() { function connect() {
+3 -2
View File
@@ -13,12 +13,13 @@ npm i @strudel.cycles/react
Here is a minimal example of how to set up a MiniRepl: Here is a minimal example of how to set up a MiniRepl:
```jsx ```jsx
import { evalScope, controls } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const {evalScope} = core;
import { MiniRepl } from '@strudel.cycles/react'; import { MiniRepl } from '@strudel.cycles/react';
import { prebake } from '../repl/src/prebake.mjs'; import { prebake } from '../repl/src/prebake.mjs';
evalScope( evalScope(
controls, core,
import('@strudel.cycles/core'), import('@strudel.cycles/core'),
import('@strudel.cycles/tonal'), import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'), import('@strudel.cycles/mini'),
+1 -1
View File
File diff suppressed because one or more lines are too long
+14 -13
View File
@@ -7,7 +7,7 @@ import { tags as r } from "@lezer/highlight";
import { createTheme as Z } from "@uiw/codemirror-themes"; import { createTheme as Z } from "@uiw/codemirror-themes";
import { useInView as ee } from "react-hook-inview"; import { useInView as ee } from "react-hook-inview";
import { webaudioOutput as te, getAudioContext as re } from "@strudel.cycles/webaudio"; import { webaudioOutput as te, getAudioContext as re } from "@strudel.cycles/webaudio";
import { repl as oe } from "@strudel.cycles/core"; import oe from "@strudel.cycles/core";
import { transpiler as ne } from "@strudel.cycles/transpiler"; import { transpiler as ne } from "@strudel.cycles/transpiler";
const ae = Z({ const ae = Z({
theme: "dark", theme: "dark",
@@ -177,7 +177,8 @@ function O({ type: e }) {
function Ee(e) { function Ee(e) {
return L(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), _((t) => window.postMessage(t, "*"), []); return L(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), _((t) => window.postMessage(t, "*"), []);
} }
function we({ const we = oe.repl;
function ye({
defaultOutput: e, defaultOutput: e,
interval: t, interval: t,
getTime: o, getTime: o,
@@ -189,8 +190,8 @@ function we({
onEvalError: c, onEvalError: c,
onToggle: i onToggle: i
}) { }) {
const m = V(() => ye(), []), [h, g] = w(), [C, N] = w(), [p, y] = w(s), [M, S] = w(), [k, D] = w(), [F, x] = w(!1), b = p !== M, { scheduler: A, evaluate: T, start: J, stop: q, pause: Q } = V( const m = V(() => ke(), []), [h, g] = w(), [C, N] = w(), [p, y] = w(s), [M, S] = w(), [k, D] = w(), [F, x] = w(!1), b = p !== M, { scheduler: A, evaluate: T, start: J, stop: q, pause: Q } = V(
() => oe({ () => we({
interval: t, interval: t,
defaultOutput: e, defaultOutput: e,
onSchedulerError: g, onSchedulerError: g,
@@ -243,11 +244,11 @@ function we({
} }
}; };
} }
function ye() { function ke() {
return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1); return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
} }
const ke = () => re().currentTime; const _e = () => re().currentTime;
function Se({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) { function Te({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) {
const { const {
code: s, code: s,
setCode: u, setCode: u,
@@ -261,10 +262,10 @@ function Se({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) {
scheduler: C, scheduler: C,
togglePlay: N, togglePlay: N,
stop: p stop: p
} = we({ } = ye({
initialCode: e, initialCode: e,
defaultOutput: te, defaultOutput: te,
getTime: ke getTime: _e
}), [y, M] = w(), [S, k] = ee({ }), [y, M] = w(), [S, k] = ee({
threshold: 0.01 threshold: 0.01
}), D = H(), F = V(() => ((k || !t) && (D.current = !0), k || D.current), [k, t]); }), D = H(), F = V(() => ((k || !t) && (D.current = !0), k || D.current), [k, t]);
@@ -307,14 +308,14 @@ function Se({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) {
onViewChanged: M onViewChanged: M
}))); })));
} }
const Te = (e) => j(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]); const Ve = (e) => j(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]);
export { export {
de as CodeMirror, de as CodeMirror,
Se as MiniRepl, Te as MiniRepl,
K as cx, K as cx,
ce as flash, ce as flash,
ue as useHighlighting, ue as useHighlighting,
Te as useKeydown, Ve as useKeydown,
Ee as usePostMessage, Ee as usePostMessage,
we as useStrudel ye as useStrudel
}; };
@@ -1,4 +1,5 @@
import { evalScope, controls } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const { evalScope } = core;
import { getAudioContext, panic, webaudioOutput } from '@strudel.cycles/webaudio'; import { getAudioContext, panic, webaudioOutput } from '@strudel.cycles/webaudio';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import CodeMirror, { flash } from '../../../src/components/CodeMirror6'; import CodeMirror, { flash } from '../../../src/components/CodeMirror6';
@@ -10,8 +11,8 @@ import './style.css';
// TODO: only import stuff when play is pressed? // TODO: only import stuff when play is pressed?
evalScope( evalScope(
controls, core,
import('@strudel.cycles/core'), // import('@strudel.cycles/core'),
// import('@strudel.cycles/tone'), // import('@strudel.cycles/tone'),
// import('@strudel.cycles/midi'), // TODO: find out why midi loads tone.js // import('@strudel.cycles/midi'), // TODO: find out why midi loads tone.js
import('@strudel.cycles/tonal'), import('@strudel.cycles/tonal'),
+2 -1
View File
@@ -1,7 +1,8 @@
import React from 'react'; import React from 'react';
import { MiniRepl } from './components/MiniRepl'; import { MiniRepl } from './components/MiniRepl';
import 'tailwindcss/tailwind.css'; import 'tailwindcss/tailwind.css';
import { controls, evalScope } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const {controls, evalScope} = core;
evalScope( evalScope(
controls, controls,
+2 -1
View File
@@ -1,5 +1,6 @@
import { useRef, useCallback, useEffect, useMemo, useState } from 'react'; import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
import { repl } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const repl = core.repl
import { transpiler } from '@strudel.cycles/transpiler'; import { transpiler } from '@strudel.cycles/transpiler';
import usePostMessage from './usePostMessage.mjs'; import usePostMessage from './usePostMessage.mjs';
+2 -1
View File
@@ -4,7 +4,8 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Pattern, isPattern } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const { Pattern, isPattern } = core;
var writeMessage; var writeMessage;
var choosing = false; var choosing = false;
+2 -1
View File
@@ -1,4 +1,5 @@
import { toMidi } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const toMidi = core.toMidi;
let loadCache = {}; let loadCache = {};
async function loadFont(name) { async function loadFont(name) {
+2 -1
View File
@@ -1,4 +1,5 @@
import { Pattern, getPlayableNoteValue, toMidi } from '@strudel.cycles/core'; import strudel from '@strudel.cycles/core';
const { Pattern, getPlayableNoteValue, toMidi } = strudel;
import { getAudioContext } from '@strudel.cycles/webaudio'; import { getAudioContext } from '@strudel.cycles/webaudio';
import { loadSoundfont as _loadSoundfont, startPresetNote } from 'sfumato'; import { loadSoundfont as _loadSoundfont, startPresetNote } from 'sfumato';
+2 -1
View File
@@ -7,7 +7,8 @@ This program is free software: you can redistribute it and/or modify it under th
// import { strict as assert } from 'assert'; // import { strict as assert } from 'assert';
import '../tonal.mjs'; // need to import this to add prototypes import '../tonal.mjs'; // need to import this to add prototypes
import { pure } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const { pure } = core;
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
describe('tonal', () => { describe('tonal', () => {
+2 -1
View File
@@ -5,7 +5,8 @@ This program is free software: you can redistribute it and/or modify it under th
*/ */
import { Note, Interval, Scale } from '@tonaljs/tonal'; import { Note, Interval, Scale } from '@tonaljs/tonal';
import { Pattern, mod } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const { Pattern, pattern, mod } = core;
// transpose note inside scale by offset steps // transpose note inside scale by offset steps
// function scaleOffset(scale: string, offset: number, note: string) { // function scaleOffset(scale: string, offset: number, note: string) {
+4 -2
View File
@@ -4,7 +4,9 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Pattern as _Pattern, stack, Hap, reify } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const { Pattern, stack, reify, Hap } = core
import _voicings from 'chord-voicings'; import _voicings from 'chord-voicings';
const { dictionaryVoicing, minTopNoteDiff, lefthand } = _voicings.default || _voicings; // parcel module resolution fuckup const { dictionaryVoicing, minTopNoteDiff, lefthand } = _voicings.default || _voicings; // parcel module resolution fuckup
@@ -17,7 +19,7 @@ const getVoicing = (chord, lastVoicing, range = ['F3', 'A4']) =>
lastVoicing, lastVoicing,
}); });
const Pattern = _Pattern; // const Pattern = _Pattern;
Pattern.prototype.fmapNested = function (func) { Pattern.prototype.fmapNested = function (func) {
return new Pattern((span) => return new Pattern((span) =>
+2 -1
View File
@@ -5,7 +5,8 @@ This program is free software: you can redistribute it and/or modify it under th
*/ */
import '../tone.mjs'; import '../tone.mjs';
import { pure } from '@strudel.cycles/core'; import strudel from '@strudel.cycles/core';
const { pure } = strudel;
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
describe('tone', () => { describe('tone', () => {
+3 -1
View File
@@ -4,7 +4,9 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Pattern } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const { Pattern } = core;
import * as _Tone from 'tone'; import * as _Tone from 'tone';
// import Tone from here, to make sure to get the same AudioContext // import Tone from here, to make sure to get the same AudioContext
+2 -2
View File
@@ -1,5 +1,5 @@
import { evaluate as _evaluate } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
import { transpiler } from './transpiler.mjs'; import { transpiler } from './transpiler.mjs';
export * from './transpiler.mjs'; export * from './transpiler.mjs';
export const evaluate = (code) => _evaluate(code, transpiler); export const evaluate = (code) => core.evaluate(code, transpiler);
+2 -1
View File
@@ -1,7 +1,8 @@
import escodegen from 'escodegen'; import escodegen from 'escodegen';
import { parse } from 'acorn'; import { parse } from 'acorn';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import { isNote } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const { isNote } = core;
export function transpiler(input, options = {}) { export function transpiler(input, options = {}) {
const { wrapAsync = false, addReturn = true, simpleLocs = false } = options; const { wrapAsync = false, addReturn = true, simpleLocs = false } = options;
+2 -1
View File
@@ -1,4 +1,5 @@
import { logger, toMidi } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const { logger, toMidi } = core;
import { getAudioContext } from './index.mjs'; import { getAudioContext } from './index.mjs';
const bufferCache = {}; // string: Promise<ArrayBuffer> const bufferCache = {}; // string: Promise<ArrayBuffer>
+2 -3
View File
@@ -4,9 +4,8 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
// import { Pattern, getFrequency, patternify2 } from '@strudel.cycles/core'; import strudel from '@strudel.cycles/core';
import * as strudel from '@strudel.cycles/core'; const { fromMidi, logger, toMidi } = strudel;
import { fromMidi, logger, toMidi } from '@strudel.cycles/core';
import './feedbackdelay.mjs'; import './feedbackdelay.mjs';
import './reverb.mjs'; import './reverb.mjs';
import { getSampleBufferSource } from './sampler.mjs'; import { getSampleBufferSource } from './sampler.mjs';
+2 -1
View File
@@ -5,7 +5,8 @@ This program is free software: you can redistribute it and/or modify it under th
*/ */
import Tune from './tunejs.js'; import Tune from './tunejs.js';
import { Pattern } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const { Pattern } = core;
Pattern.prototype._tune = function (scale, tonic = 220) { Pattern.prototype._tune = function (scale, tonic = 220) {
const tune = new Tune(); const tune = new Tune();
+2 -1
View File
@@ -4,7 +4,8 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Pattern, mod } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const { Pattern, mod } = core;
export function edo(name) { export function edo(name) {
if (!/^[1-9]+[0-9]*edo$/.test(name)) { if (!/^[1-9]+[0-9]*edo$/.test(name)) {
+3 -2
View File
@@ -4,7 +4,8 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { cleanupDraw, cleanupUi, controls, evalScope, logger } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const { cleanupDraw, cleanupUi, evalScope, logger } = core;
import { CodeMirror, cx, flash, useHighlighting, useStrudel } from '@strudel.cycles/react'; import { CodeMirror, cx, flash, useHighlighting, useStrudel } from '@strudel.cycles/react';
import { import {
getAudioContext, getAudioContext,
@@ -47,7 +48,7 @@ const modules = [
evalScope( evalScope(
// Tone, // Tone,
controls, // sadly, this cannot be exported from core direclty core,
{ WebDirt }, { WebDirt },
...modules, ...modules,
); );
+2 -1
View File
@@ -1,5 +1,6 @@
import XMarkIcon from '@heroicons/react/20/solid/XMarkIcon'; import XMarkIcon from '@heroicons/react/20/solid/XMarkIcon';
import { logger } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const { logger } = core;
import { cx } from '@strudel.cycles/react'; import { cx } from '@strudel.cycles/react';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import React, { useContext, useCallback, useLayoutEffect, useRef, useState } from 'react'; import React, { useContext, useCallback, useLayoutEffect, useRef, useState } from 'react';
+2 -1
View File
@@ -1,4 +1,5 @@
import { Pattern, toMidi } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
const { Pattern, toMidi } = core;
import { samples } from '@strudel.cycles/webaudio'; import { samples } from '@strudel.cycles/webaudio';
export async function prebake({ isMock = false, baseDir = '.' } = {}) { export async function prebake({ isMock = false, baseDir = '.' } = {}) {
+16 -16
View File
@@ -5,8 +5,8 @@
// import * as tunes from './tunes.mjs'; // import * as tunes from './tunes.mjs';
// import { evaluate } from '@strudel.cycles/eval'; // import { evaluate } from '@strudel.cycles/eval';
import { evaluate } from '@strudel.cycles/transpiler'; import { evaluate } from '@strudel.cycles/transpiler';
import { evalScope } from '@strudel.cycles/core'; import strudel from '@strudel.cycles/core';
import * as strudel from '@strudel.cycles/core'; const { Pattern, evalScope } = strudel;
import * as webaudio from '@strudel.cycles/webaudio'; import * as webaudio from '@strudel.cycles/webaudio';
import controls from '@strudel.cycles/core/controls.mjs'; import controls from '@strudel.cycles/core/controls.mjs';
// import gist from '@strudel.cycles/core/gist.js'; // import gist from '@strudel.cycles/core/gist.js';
@@ -85,51 +85,51 @@ const toneHelpersMocked = {
highpass: mockNode, highpass: mockNode,
}; };
strudel.Pattern.prototype.osc = function () { Pattern.prototype.osc = function () {
return this; return this;
}; };
strudel.Pattern.prototype.csound = function () { Pattern.prototype.csound = function () {
return this; return this;
}; };
strudel.Pattern.prototype.tone = function () { Pattern.prototype.tone = function () {
return this; return this;
}; };
strudel.Pattern.prototype.webdirt = function () { Pattern.prototype.webdirt = function () {
return this; return this;
}; };
// draw mock // draw mock
strudel.Pattern.prototype.pianoroll = function () { Pattern.prototype.pianoroll = function () {
return this; return this;
}; };
// speak mock // speak mock
strudel.Pattern.prototype.speak = function () { Pattern.prototype.speak = function () {
return this; return this;
}; };
// webaudio mock // webaudio mock
strudel.Pattern.prototype.wave = function () { Pattern.prototype.wave = function () {
return this; return this;
}; };
strudel.Pattern.prototype.filter = function () { Pattern.prototype.filter = function () {
return this; return this;
}; };
strudel.Pattern.prototype.adsr = function () { Pattern.prototype.adsr = function () {
return this; return this;
}; };
strudel.Pattern.prototype.out = function () { Pattern.prototype.out = function () {
return this; return this;
}; };
strudel.Pattern.prototype.soundfont = function () { Pattern.prototype.soundfont = function () {
return this; return this;
}; };
// tune mock // tune mock
strudel.Pattern.prototype.tune = function () { Pattern.prototype.tune = function () {
return this; return this;
}; };
strudel.Pattern.prototype.midi = function () { Pattern.prototype.midi = function () {
return this; return this;
}; };
@@ -159,7 +159,7 @@ const loadSoundfont = () => {};
evalScope( evalScope(
// Tone, // Tone,
strudel, strudel,
strudel.Pattern.prototype.bootstrap(), Pattern.prototype.bootstrap(),
toneHelpersMocked, toneHelpersMocked,
uiHelpersMocked, uiHelpersMocked,
controls, controls,
+4 -4
View File
@@ -1,4 +1,4 @@
import { evalScope, controls } from '@strudel.cycles/core'; import core from '@strudel.cycles/core';
import { MiniRepl as _MiniRepl } from '@strudel.cycles/react'; import { MiniRepl as _MiniRepl } from '@strudel.cycles/react';
import { samples } from '@strudel.cycles/webaudio'; import { samples } from '@strudel.cycles/webaudio';
import { prebake } from '../repl/src/prebake.mjs'; import { prebake } from '../repl/src/prebake.mjs';
@@ -7,9 +7,9 @@ fetch('https://strudel.tidalcycles.org/EmuSP12.json')
.then((res) => res.json()) .then((res) => res.json())
.then((json) => samples(json, 'https://strudel.tidalcycles.org/EmuSP12/')); .then((json) => samples(json, 'https://strudel.tidalcycles.org/EmuSP12/'));
evalScope( core.evalScope(
controls, core,
import('@strudel.cycles/core'), // import('@strudel.cycles/core'),
// import('@strudel.cycles/tone'), // import('@strudel.cycles/tone'),
import('@strudel.cycles/tonal'), import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'), import('@strudel.cycles/mini'),