Reinstate slice and splice (#500)

Reinstate slice/splice, fixing up "unit" param using new global cps
This commit is contained in:
Alex McLean 2023-03-02 14:44:41 +00:00 committed by GitHub
parent fdc4819e39
commit 2d5064bd5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 37 additions and 27 deletions

View File

@ -1415,25 +1415,32 @@ export function register(name, func, patternify = true) {
const arity = func.length; const arity = func.length;
var pfunc; // the patternified function var pfunc; // the patternified function
pfunc = function (...args) { if (patternify) {
args = args.map(reify); pfunc = function (...args) {
const pat = args[args.length - 1]; args = args.map(reify);
if (arity === 1) { const pat = args[args.length - 1];
return func(pat); if (arity === 1) {
} return func(pat);
const [left, ...right] = args.slice(0, -1); }
let mapFn = (...args) => { const [left, ...right] = args.slice(0, -1);
// make sure to call func with the correct argument count let mapFn = (...args) => {
// args.length is expected to be <= arity-1 // make sure to call func with the correct argument count
// so we set undefined args explicitly undefined // args.length is expected to be <= arity-1
Array(arity - 1) // so we set undefined args explicitly undefined
.fill() Array(arity - 1)
.map((_, i) => args[i] ?? undefined); .fill()
return func(...args, pat); .map((_, i) => args[i] ?? undefined);
return func(...args, pat);
};
mapFn = curry(mapFn, null, arity - 1);
return right.reduce((acc, p) => acc.appLeft(p), left.fmap(mapFn)).innerJoin();
}; };
mapFn = curry(mapFn, null, arity - 1); } else {
return right.reduce((acc, p) => acc.appLeft(p), left.fmap(mapFn)).innerJoin(); pfunc = function (...args) {
}; args = args.map(reify);
return func(...args);
};
}
Pattern.prototype[name] = function (...args) { Pattern.prototype[name] = function (...args) {
// For methods that take a single argument (plus 'this'), allow // For methods that take a single argument (plus 'this'), allow
@ -2231,11 +2238,13 @@ const _loopAt = function (factor, pat, cps = 1) {
* @example * @example
* await samples('github:tidalcycles/Dirt-Samples/master') * await samples('github:tidalcycles/Dirt-Samples/master')
* s("breaks165").slice(8, "0 1 <2 2*2> 3 [4 0] 5 6 7".every(3, rev)).slow(1.5) * s("breaks165").slice(8, "0 1 <2 2*2> 3 [4 0] 5 6 7".every(3, rev)).slow(1.5)
*/
const slice = register( const slice = register(
'slice', 'slice',
function (npat, ipat, opat) { function (npat, ipat, opat) {
return npat.innerBind((n) => return npat.innerBind((n) =>
ipat.outerBind((i) => ipat.outerBind((i) =>
opat.outerBind((o) => { opat.outerBind((o) => {
// If it's not an object, assume it's a string and make it a 's' control parameter // If it's not an object, assume it's a string and make it a 's' control parameter
o = o instanceof Object ? o : { s: o }; o = o instanceof Object ? o : { s: o };
@ -2248,7 +2257,6 @@ const slice = register(
}, },
false, // turns off auto-patternification false, // turns off auto-patternification
); );
*/
/* /*
* Works the same as slice, but changes the playback speed of each slice to match the duration of its step. * Works the same as slice, but changes the playback speed of each slice to match the duration of its step.
@ -2258,6 +2266,8 @@ const slice = register(
* @example * @example
* await samples('github:tidalcycles/Dirt-Samples/master') * await samples('github:tidalcycles/Dirt-Samples/master')
* s("breaks165").splice(8, "0 1 [2 3 0]@2 3 0@2 7").hurry(0.65) * s("breaks165").splice(8, "0 1 [2 3 0]@2 3 0@2 7").hurry(0.65)
*/
const splice = register( const splice = register(
'splice', 'splice',
function (npat, ipat, opat) { function (npat, ipat, opat) {
@ -2273,8 +2283,7 @@ const splice = register(
}); });
}, },
false, // turns off auto-patternification false, // turns off auto-patternification
); );
*/
const { loopAt, loopat } = register(['loopAt', 'loopat'], function (factor, pat) { const { loopAt, loopat } = register(['loopAt', 'loopat'], function (factor, pat) {
return _loopAt(factor, pat, 1); return _loopAt(factor, pat, 1);

View File

@ -21,7 +21,7 @@ export function repl({
onTrigger: async (hap, deadline, duration, cps) => { onTrigger: async (hap, deadline, duration, cps) => {
try { try {
if (!hap.context.onTrigger || !hap.context.dominantTrigger) { if (!hap.context.onTrigger || !hap.context.dominantTrigger) {
await defaultOutput(hap, deadline, duration); await defaultOutput(hap, deadline, duration, cps);
} }
if (hap.context.onTrigger) { if (hap.context.onTrigger) {
// call signature of output / onTrigger is different... // call signature of output / onTrigger is different...

View File

@ -976,6 +976,7 @@ describe('Pattern', () => {
sameFirst(n(0, 1).weave(2, s('bd', silence), s(silence, 'sd')), sequence(s('bd').n(0), s('sd').n(1))); sameFirst(n(0, 1).weave(2, s('bd', silence), s(silence, 'sd')), sequence(s('bd').n(0), s('sd').n(1)));
}); });
}); });
*/
describe('slice', () => { describe('slice', () => {
it('Can slice a sample', () => { it('Can slice a sample', () => {
sameFirst( sameFirst(
@ -1001,5 +1002,5 @@ describe('Pattern', () => {
), ),
); );
}); });
});*/ });
}); });

View File

@ -191,7 +191,7 @@ function effectSend(input, effect, wet) {
} }
// export const webaudioOutput = async (t, hap, ct, cps) => { // export const webaudioOutput = async (t, hap, ct, cps) => {
export const webaudioOutput = async (hap, deadline, hapDuration) => { export const webaudioOutput = async (hap, deadline, hapDuration, cps) => {
const ac = getAudioContext(); const ac = getAudioContext();
hap.ensureObjectValue(); hap.ensureObjectValue();
@ -305,7 +305,7 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
bufferSource.playbackRate.value = Math.abs(speed) * bufferSource.playbackRate.value; bufferSource.playbackRate.value = Math.abs(speed) * bufferSource.playbackRate.value;
if (unit === 'c') { if (unit === 'c') {
// are there other units? // are there other units?
bufferSource.playbackRate.value = bufferSource.playbackRate.value * bufferSource.buffer.duration; bufferSource.playbackRate.value = bufferSource.playbackRate.value * bufferSource.buffer.duration * cps;
} }
let duration = soundfont || clip ? hapDuration : bufferSource.buffer.duration / bufferSource.playbackRate.value; let duration = soundfont || clip ? hapDuration : bufferSource.buffer.duration / bufferSource.playbackRate.value;
// "The computation of the offset into the sound is performed using the sound buffer's natural sample rate, // "The computation of the offset into the sound is performed using the sound buffer's natural sample rate,
@ -378,7 +378,7 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
chain[0].onended = () => chain.concat([delaySend, reverbSend]).forEach((n) => n?.disconnect()); chain[0].onended = () => chain.concat([delaySend, reverbSend]).forEach((n) => n?.disconnect());
}; };
export const webaudioOutputTrigger = (t, hap, ct, cps) => webaudioOutput(hap, t - ct, hap.duration / cps); export const webaudioOutputTrigger = (t, hap, ct, cps) => webaudioOutput(hap, t - ct, hap.duration / cps, cps);
Pattern.prototype.webaudio = function () { Pattern.prototype.webaudio = function () {
// TODO: refactor (t, hap, ct, cps) to (hap, deadline, duration) ? // TODO: refactor (t, hap, ct, cps) to (hap, deadline, duration) ?