Merge branch 'tidalcycles:main' into gainmod

This commit is contained in:
Jade (Rose) Rowland 2024-05-09 23:55:13 -04:00 committed by GitHub
commit 1509ff1793
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 180 additions and 45 deletions

View file

@ -0,0 +1,46 @@
import { describe, bench } from 'vitest';
import { calculateTactus, sequence, stack } from '../index.mjs';
const pat64 = sequence(...Array(64).keys());
describe('tactus', () => {
calculateTactus(true);
bench(
'+tactus',
() => {
pat64.iter(64).fast(64).firstCycle();
},
{ time: 1000 },
);
calculateTactus(false);
bench(
'-tactus',
() => {
pat64.iter(64).fast(64).firstCycle();
},
{ time: 1000 },
);
});
describe('stack', () => {
calculateTactus(true);
bench(
'+tactus',
() => {
stack(pat64, pat64, pat64, pat64, pat64, pat64, pat64, pat64).fast(64).firstCycle();
},
{ time: 1000 },
);
calculateTactus(false);
bench(
'-tactus',
() => {
stack(pat64, pat64, pat64, pat64, pat64, pat64, pat64, pat64).fast(64).firstCycle();
},
{ time: 1000 },
);
});
calculateTactus(true);

View file

@ -9,6 +9,7 @@
},
"scripts": {
"test": "vitest run",
"bench": "vitest bench",
"build": "vite build",
"prepublishOnly": "pnpm build"
},

View file

@ -27,6 +27,12 @@ import { logger } from './logger.mjs';
let stringParser;
let __tactus = true;
export const calculateTactus = function (x) {
__tactus = x ? true : false;
};
// parser is expected to turn a string into a pattern
// if set, the reify function will parse all strings with it
// intended to use with mini to automatically interpret all strings as mini notation
@ -60,6 +66,9 @@ export class Pattern {
}
withTactus(f) {
if (!__tactus) {
return this;
}
return new Pattern(this.query, this.tactus === undefined ? undefined : f(this.tactus));
}
@ -149,7 +158,9 @@ export class Pattern {
return span_a.intersection_e(span_b);
};
const result = pat_func.appWhole(whole_func, pat_val);
result.tactus = lcm(pat_val.tactus, pat_func.tactus);
if (__tactus) {
result.tactus = lcm(pat_val.tactus, pat_func.tactus);
}
return result;
}
@ -1254,7 +1265,9 @@ export function stack(...pats) {
pats = pats.map((pat) => (Array.isArray(pat) ? sequence(...pat) : reify(pat)));
const query = (state) => flatten(pats.map((pat) => pat.query(state)));
const result = new Pattern(query);
result.tactus = lcm(...pats.map((pat) => pat.tactus));
if (__tactus) {
result.tactus = lcm(...pats.map((pat) => pat.tactus));
}
return result;
}
@ -1267,7 +1280,7 @@ function _stackWith(func, pats) {
return pats[0];
}
const [left, ...right] = pats.map((pat) => pat.tactus);
const tactus = left.maximum(...right);
const tactus = __tactus ? left.maximum(...right) : undefined;
return stack(...func(tactus, pats));
}
@ -1347,7 +1360,7 @@ export function slowcat(...pats) {
const offset = span.begin.floor().sub(span.begin.div(pats.length).floor());
return pat.withHapTime((t) => t.add(offset)).query(state.setSpan(span.withTime((t) => t.sub(offset))));
};
const tactus = lcm(...pats.map((x) => x.tactus));
const tactus = __tactus ? lcm(...pats.map((x) => x.tactus)) : undefined;
return new Pattern(query).splitQueries().setTactus(tactus);
}
@ -1766,7 +1779,9 @@ export const { focusSpan, focusspan } = register(['focusSpan', 'focusspan'], fun
*/
export const ply = register('ply', function (factor, pat) {
const result = pat.fmap((x) => pure(x)._fast(factor)).squeezeJoin();
result.tactus = Fraction(factor).mulmaybe(pat.tactus);
if (__tactus) {
result.tactus = Fraction(factor).mulmaybe(pat.tactus);
}
return result;
});
@ -1781,16 +1796,19 @@ export const ply = register('ply', function (factor, pat) {
* @example
* s("bd hh sd hh").fast(2) // s("[bd hh sd hh]*2")
*/
export const { fast, density } = register(['fast', 'density'], function (factor, pat) {
if (factor === 0) {
return silence;
}
factor = Fraction(factor);
const fastQuery = pat.withQueryTime((t) => t.mul(factor));
const result = fastQuery.withHapTime((t) => t.div(factor));
result.tactus = factor.mulmaybe(pat.tactus);
return result;
});
export const { fast, density } = register(
['fast', 'density'],
function (factor, pat) {
if (factor === 0) {
return silence;
}
factor = Fraction(factor);
const fastQuery = pat.withQueryTime((t) => t.mul(factor));
return fastQuery.withHapTime((t) => t.div(factor)).setTactus(pat.tactus);
},
true,
true,
);
/**
* Both speeds up the pattern (like 'fast') and the sample playback (like 'speed').
@ -1958,7 +1976,7 @@ export const zoom = register('zoom', function (s, e, pat) {
return nothing;
}
const d = e.sub(s);
const tactus = pat.tactus.mulmaybe(d);
const tactus = __tactus ? pat.tactus.mulmaybe(d) : undefined;
return pat
.withQuerySpan((span) => span.withCycle((t) => t.mul(d).add(s)))
.withHapSpan((span) => span.withCycle((t) => t.sub(s).div(d)))
@ -2173,7 +2191,7 @@ export const { juxBy, juxby } = register(['juxBy', 'juxby'], function (by, func,
const left = pat.withValue((val) => Object.assign({}, val, { pan: elem_or(val, 'pan', 0.5) - by }));
const right = func(pat.withValue((val) => Object.assign({}, val, { pan: elem_or(val, 'pan', 0.5) + by })));
return stack(left, right).setTactus(lcm(left.tactus, right.tactus));
return stack(left, right).setTactus(__tactus ? lcm(left.tactus, right.tactus) : undefined);
});
/**
@ -2292,7 +2310,13 @@ export const { iterBack, iterback } = register(
export const { repeatCycles } = register(
'repeatCycles',
function (n, pat) {
return slowcat(...Array(n).fill(pat));
return new Pattern(function (state) {
const cycle = state.span.begin.sam();
const source_cycle = cycle.div(n).sam();
const delta = cycle.sub(source_cycle);
state = state.withSpan((span) => span.withTime((spant) => spant.sub(delta)));
return pat.query(state).map((hap) => hap.withSpan((span) => span.withTime((spant) => spant.add(delta))));
}).splitQueries();
},
true,
true,
@ -2773,7 +2797,7 @@ export const chop = register('chop', function (n, pat) {
const func = function (o) {
return sequence(slice_objects.map((slice_o) => Object.assign({}, o, slice_o)));
};
return pat.squeezeBind(func).setTactus(Fraction(n).mulmaybe(pat.tactus));
return pat.squeezeBind(func).setTactus(__tactus ? Fraction(n).mulmaybe(pat.tactus) : undefined);
});
/**
@ -2788,7 +2812,10 @@ export const striate = register('striate', function (n, pat) {
const slices = Array.from({ length: n }, (x, i) => i);
const slice_objects = slices.map((i) => ({ begin: i / n, end: (i + 1) / n }));
const slicePat = slowcat(...slice_objects);
return pat.set(slicePat)._fast(n);
return pat
.set(slicePat)
._fast(n)
.setTactus(__tactus ? Fraction(n).mulmaybe(pat.tactus) : undefined);
});
/**

View file

@ -51,6 +51,7 @@ import {
stackRight,
stackCentre,
s_cat,
calculateTactus,
} from '../index.mjs';
import { steady } from '../signal.mjs';
@ -1127,8 +1128,8 @@ describe('Pattern', () => {
it('Is correctly preserved/calculated through transformations', () => {
expect(sequence(0, 1, 2, 3).linger(4).tactus).toStrictEqual(Fraction(4));
expect(sequence(0, 1, 2, 3).iter(4).tactus).toStrictEqual(Fraction(4));
expect(sequence(0, 1, 2, 3).fast(4).tactus).toStrictEqual(Fraction(16));
expect(sequence(0, 1, 2, 3).hurry(4).tactus).toStrictEqual(Fraction(16));
expect(sequence(0, 1, 2, 3).fast(4).tactus).toStrictEqual(Fraction(4));
expect(sequence(0, 1, 2, 3).hurry(4).tactus).toStrictEqual(Fraction(4));
expect(sequence(0, 1, 2, 3).rev().tactus).toStrictEqual(Fraction(4));
expect(sequence(1).segment(10).tactus).toStrictEqual(Fraction(10));
expect(sequence(1, 0, 1).invert().tactus).toStrictEqual(Fraction(3));
@ -1164,7 +1165,7 @@ describe('Pattern', () => {
expect(stackCentre(fastcat(0, 1, 2), fastcat(3, 4)).tactus).toStrictEqual(Fraction(3));
expect(fastcat(0, 1).ply(3).tactus).toStrictEqual(Fraction(6));
expect(fastcat(0, 1).setTactus(undefined).ply(3).tactus).toStrictEqual(undefined);
expect(fastcat(0, 1).fast(3).tactus).toStrictEqual(Fraction(6));
expect(fastcat(0, 1).fast(3).tactus).toStrictEqual(Fraction(2));
expect(fastcat(0, 1).setTactus(undefined).fast(3).tactus).toStrictEqual(undefined);
});
});