Beat-oriented functionality (#976)
* annotate pure values with their value, allowing single mininotation values to maintain their labels as pure * Don't use any 'patternified' arguments if they're all 'pure' * allow pattern weights (roughly, beats-per-cycle) to be inferred where possible, including from mininotation and across many transformations (e.g. `fast` with a 'pure' factor) * Add `beatCat`, similar to `timeCat` but funkier * `silence` has a weight of 1, add alternative `nothing` with a weight of 0, and `gap` function with weight argument * preserve weight across applicative operations (weight comes with the structure) * add `stack` alternatives that take advantage of pattern weights to align patterns differently - `stackLeft`, `stackRight`, `stackCentre`, `stackExpand`, with `stackBy` with an argument for patterning the alignment.
This commit is contained in:
parent
d102e1dbbc
commit
398533877c
8 changed files with 1391 additions and 1381 deletions
|
|
@ -4,9 +4,10 @@ Copyright (C) 2023 Strudel contributors - see <https://github.com/tidalcycles/st
|
|||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { s } from '../controls.mjs';
|
||||
import { s, pan } from '../controls.mjs';
|
||||
import { mini } from '../../mini/mini.mjs';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import Fraction from '../fraction.mjs';
|
||||
|
||||
describe('controls', () => {
|
||||
it('should support controls', () => {
|
||||
|
|
@ -29,4 +30,13 @@ describe('controls', () => {
|
|||
expect(s(mini('bd').pan(1)).firstCycleValues).toEqual([{ s: 'bd', pan: 1 }]);
|
||||
expect(s(mini('bd:1').pan(1)).firstCycleValues).toEqual([{ s: 'bd', n: 1, pan: 1 }]);
|
||||
});
|
||||
it('preserves weight of the left pattern', () => {
|
||||
expect(s(mini('bd cp mt').pan(mini('1 2 3 4'))).weight).toEqual(Fraction(3));
|
||||
});
|
||||
it('preserves weight of the right pattern for .out', () => {
|
||||
expect(s(mini('bd cp mt').set.out(pan(mini('1 2 3 4')))).weight).toEqual(Fraction(4));
|
||||
});
|
||||
it('combines weight of the pattern for .mix as lcm', () => {
|
||||
expect(s(mini('bd cp mt').set.mix(pan(mini('1 2 3 4')))).weight).toEqual(Fraction(12));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -604,7 +604,7 @@ describe('Pattern', () => {
|
|||
});
|
||||
});
|
||||
describe('polymeter()', () => {
|
||||
it('Can layer up cycles, stepwise', () => {
|
||||
it('Can layer up cycles, stepwise, with lists', () => {
|
||||
expect(polymeterSteps(3, ['d', 'e']).firstCycle()).toStrictEqual(
|
||||
fastcat(pure('d'), pure('e'), pure('d')).firstCycle(),
|
||||
);
|
||||
|
|
@ -613,6 +613,9 @@ describe('Pattern', () => {
|
|||
stack(sequence('a', 'b', 'c', 'a', 'b', 'c'), sequence('d', 'e', 'd', 'e', 'd', 'e')).firstCycle(),
|
||||
);
|
||||
});
|
||||
it('Can layer up cycles, stepwise, with weighted patterns', () => {
|
||||
sameFirst(polymeterSteps(3, sequence('a', 'b')).fast(2), sequence('a', 'b', 'a', 'b', 'a', 'b'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('firstOf()', () => {
|
||||
|
|
@ -1116,4 +1119,23 @@ describe('Pattern', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
describe('weight', () => {
|
||||
it('Is correctly preserved/calculated through transformations', () => {
|
||||
expect(sequence(0, 1, 2, 3).linger(4).weight).toStrictEqual(Fraction(4));
|
||||
expect(sequence(0, 1, 2, 3).iter(4).weight).toStrictEqual(Fraction(4));
|
||||
expect(sequence(0, 1, 2, 3).fast(4).weight).toStrictEqual(Fraction(16));
|
||||
expect(sequence(0, 1, 2, 3).hurry(4).weight).toStrictEqual(Fraction(16));
|
||||
expect(sequence(0, 1, 2, 3).rev().weight).toStrictEqual(Fraction(4));
|
||||
expect(sequence(1).segment(10).weight).toStrictEqual(Fraction(10));
|
||||
expect(sequence(1, 0, 1).invert().weight).toStrictEqual(Fraction(3));
|
||||
expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).chop(4).weight).toStrictEqual(Fraction(8));
|
||||
expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).striate(4).weight).toStrictEqual(Fraction(8));
|
||||
expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).slice(4, sequence(0, 1, 2, 3)).weight).toStrictEqual(
|
||||
Fraction(4),
|
||||
);
|
||||
expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).splice(4, sequence(0, 1, 2, 3)).weight).toStrictEqual(
|
||||
Fraction(4),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue