Merge pull request #805 from tidalcycles/pq-functions

tidal style d1 ... d9 functions + more
This commit is contained in:
Felix Roos 2023-11-17 14:37:51 +01:00 committed by GitHub
commit 47a6af2b25
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 56 additions and 31 deletions

View file

@ -47,10 +47,5 @@ export const evaluate = async (code, transpiler) => {
// if no transpiler is given, we expect a single instruction (!wrapExpression) // if no transpiler is given, we expect a single instruction (!wrapExpression)
const options = { wrapExpression: !!transpiler }; const options = { wrapExpression: !!transpiler };
let evaluated = await safeEval(code, options); let evaluated = await safeEval(code, options);
if (!isPattern(evaluated)) {
console.log('evaluated', evaluated);
const message = `got "${typeof evaluated}" instead of pattern`;
throw new Error(message + (typeof evaluated === 'function' ? ', did you forget to call a function?' : '.'));
}
return { mode: 'javascript', pattern: evaluated, meta }; return { mode: 'javascript', pattern: evaluated, meta };
}; };

View file

@ -1178,7 +1178,7 @@ export function reify(thing) {
* @return {Pattern} * @return {Pattern}
* @synonyms polyrhythm, pr * @synonyms polyrhythm, pr
* @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) { export function stack(...pats) {
// Array test here is to avoid infinite recursions.. // Array test here is to avoid infinite recursions..
@ -1193,7 +1193,7 @@ export function stack(...pats) {
* *
* @return {Pattern} * @return {Pattern}
* @example * @example
* slowcat(e5, b4, [d5, c5]) * slowcat("e5", "b4", ["d5", "c5"])
* *
*/ */
export function slowcat(...pats) { export function slowcat(...pats) {
@ -1237,7 +1237,7 @@ export function slowcatPrime(...pats) {
* @synonyms slowcat * @synonyms slowcat
* @return {Pattern} * @return {Pattern}
* @example * @example
* 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) { export function cat(...pats) {
@ -1247,7 +1247,7 @@ export function cat(...pats) {
/** Like {@link Pattern.seq}, but each step has a length, relative to the whole. /** Like {@link Pattern.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) { export 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));
@ -1287,7 +1287,7 @@ export function sequence(...pats) {
/** Like **cat**, but the items are crammed into one cycle. /** Like **cat**, but the items are crammed into one cycle.
* @synonyms fastcat, sequence * @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) { export function seq(...pats) {
@ -1975,9 +1975,9 @@ export const press = register('press', function (pat) {
* s("hh*3") * s("hh*3")
* ) * )
*/ */
export const hush = register('hush', function (pat) { Pattern.prototype.hush = function () {
return silence; return silence;
}); };
/** /**
* Applies `rev` to a pattern every other cycle, so that the pattern alternates between forwards and backwards. * Applies `rev` to a pattern every other cycle, so that the pattern alternates between forwards and backwards.

View file

@ -3,7 +3,7 @@ import { evaluate as _evaluate } from './evaluate.mjs';
import { logger } from './logger.mjs'; import { logger } from './logger.mjs';
import { setTime } from './time.mjs'; import { setTime } from './time.mjs';
import { evalScope } from './evaluate.mjs'; import { evalScope } from './evaluate.mjs';
import { register } from './pattern.mjs'; import { register, Pattern, isPattern, silence, stack } from './pattern.mjs';
export function repl({ export function repl({
interval, interval,
@ -24,22 +24,37 @@ export function repl({
getTime, getTime,
onToggle, onToggle,
}); });
let playPatterns = []; let pPatterns = {};
let allTransform;
const hush = function () {
pPatterns = {};
allTransform = undefined;
return silence;
};
const setPattern = (pattern, autostart = true) => { const setPattern = (pattern, autostart = true) => {
pattern = editPattern?.(pattern) || pattern; pattern = editPattern?.(pattern) || pattern;
scheduler.setPattern(pattern, autostart); scheduler.setPattern(pattern, autostart);
}; };
setTime(() => scheduler.now()); // TODO: refactor? setTime(() => scheduler.now()); // TODO: refactor?
const evaluate = async (code, autostart = true) => { const evaluate = async (code, autostart = true, shouldHush = true) => {
if (!code) { if (!code) {
throw new Error('no code to evaluate'); throw new Error('no code to evaluate');
} }
try { try {
await beforeEval?.({ code }); await beforeEval?.({ code });
playPatterns = []; shouldHush && hush();
let { pattern, meta } = await _evaluate(code, transpiler); let { pattern, meta } = await _evaluate(code, transpiler);
if (playPatterns.length) { if (Object.keys(pPatterns).length) {
pattern = pattern.stack(...playPatterns); pattern = stack(...Object.values(pPatterns));
}
if (allTransform) {
pattern = allTransform(pattern);
}
if (!isPattern(pattern)) {
const message = `got "${typeof evaluated}" instead of pattern`;
throw new Error(message + (typeof evaluated === 'function' ? ', did you forget to call a function?' : '.'));
} }
logger(`[eval] code updated`); logger(`[eval] code updated`);
setPattern(pattern, autostart); setPattern(pattern, autostart);
@ -62,10 +77,32 @@ export function repl({
return pat.loopAtCps(cycles, scheduler.cps); return pat.loopAtCps(cycles, scheduler.cps);
}); });
const play = register('play', (pat) => { Pattern.prototype.p = function (id) {
playPatterns.push(pat); pPatterns[id] = this;
return pat; return this;
};
Pattern.prototype.q = function (id) {
return silence;
};
const all = function (transform) {
allTransform = transform;
return silence;
};
for (let i = 1; i < 10; ++i) {
Object.defineProperty(Pattern.prototype, `d${i}`, {
get() {
return this.p(i);
},
}); });
Object.defineProperty(Pattern.prototype, `p${i}`, {
get() {
return this.p(i);
},
});
Pattern.prototype[`q${i}`] = silence;
}
const fit = register('fit', (pat) => const fit = register('fit', (pat) =>
pat.withHap((hap) => pat.withHap((hap) =>
@ -80,7 +117,8 @@ export function repl({
evalScope({ evalScope({
loopAt, loopAt,
fit, fit,
play, all,
hush,
setCps, setCps,
setcps: setCps, setcps: setCps,
setCpm, setCpm,

View file

@ -17,9 +17,6 @@ describe('transpiler', () => {
it('wraps backtick string with mini and adds location', () => { it('wraps backtick string with mini and adds location', () => {
expect(transpiler('`c3`', simple).output).toEqual("m('c3', 0);"); expect(transpiler('`c3`', simple).output).toEqual("m('c3', 0);");
}); });
it('replaces note variables with note strings', () => {
expect(transpiler('seq(c3, d3)', simple).output).toEqual("seq('c3', 'd3');");
});
it('keeps tagged template literal as is', () => { it('keeps tagged template literal as is', () => {
expect(transpiler('xxx`c3`', simple).output).toEqual('xxx`c3`;'); expect(transpiler('xxx`c3`', simple).output).toEqual('xxx`c3`;');
}); });

View file

@ -47,11 +47,6 @@ export function transpiler(input, options = {}) {
}); });
return this.replace(widgetWithLocation(node)); return this.replace(widgetWithLocation(node));
} }
// TODO: remove pseudo note variables?
if (node.type === 'Identifier' && isNoteWithOctave(node.name)) {
this.skip();
return this.replace({ type: 'Literal', value: node.name });
}
}, },
leave(node, parent, prop, index) {}, leave(node, parent, prop, index) {},
}); });