Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fiddles with cat/stack #90

Merged
merged 20 commits into from
Apr 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
131 changes: 0 additions & 131 deletions featurelist.md

This file was deleted.

39 changes: 39 additions & 0 deletions packages/core/drawLine.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import Fraction, { gcd } from './fraction.mjs';

function drawLine(pat, chars = 60) {
let cycle = 0;
let pos = Fraction(0);
let lines = [''];
let emptyLine = ''; // this will be the "reference" empty line, which will be copied into extra lines
while (lines[0].length < chars) {
const haps = pat.queryArc(cycle, cycle + 1);
const durations = haps.filter((hap) => hap.hasOnset()).map((hap) => hap.duration);
const charFraction = gcd(...durations);
const totalSlots = charFraction.inverse(); // number of character slots for the current cycle
lines = lines.map((line) => line + '|'); // add pipe character before each cycle
emptyLine += '|';
for (let i = 0; i < totalSlots; i++) {
const [begin, end] = [pos, pos.add(charFraction)];
const matches = haps.filter((hap) => hap.whole.begin.lte(begin) && hap.whole.end.gte(end));
const missingLines = matches.length - lines.length;
if (missingLines > 0) {
lines = lines.concat(Array(missingLines).fill(emptyLine));
}
lines = lines.map((line, i) => {
const hap = matches[i];
if (hap) {
const isOnset = hap.whole.begin.eq(begin);
const char = isOnset ? '' + hap.value : '-';
return line + char;
}
return line + '.';
});
emptyLine += '.';
pos = pos.add(charFraction);
}
cycle++;
}
return lines.join('\n');
}

export default drawLine;
2 changes: 1 addition & 1 deletion packages/core/examples/canvas.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
const events = pattern.firstCycle(); // query first cycle
events.forEach((event) => {
ctx.fillStyle = event.value;
ctx.fillRect(event.whole.begin * canvas.width, 0, event.duration * canvas.width, canvas.height);
ctx.fillRect(event.whole.begin * canvas.width, 0, event.duration.valueOf() * canvas.width, canvas.height);
});
}
</script>
Expand Down
6 changes: 6 additions & 0 deletions packages/core/fraction.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ const fraction = (n) => {
return Fraction(n);
};

export const gcd = (...fractions) => {
return fractions.reduce((gcd, fraction) => gcd.gcd(fraction), fraction(1));
};

fraction._original = Fraction;

export default fraction;

// "If you concern performance, cache Fraction.js objects and pass arrays/objects.“
Expand Down
2 changes: 1 addition & 1 deletion packages/core/hap.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class Hap {
}

get duration() {
return this.whole.end.sub(this.whole.begin).valueOf();
return this.whole.end.sub(this.whole.begin);
}

wholeOrPart() {
Expand Down
77 changes: 62 additions & 15 deletions packages/core/pattern.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Hap from './hap.mjs';
import State from './state.mjs';

import { isNote, toMidi, compose, removeUndefineds, flatten, id, listRange, curry, mod } from './util.mjs';
import drawLine from './drawLine.mjs';

export class Pattern {
// the following functions will get patternFactories as nested functions:
Expand Down Expand Up @@ -563,6 +564,17 @@ export class Pattern {
return this._withContext((context) => ({ ...context, color }));
}

log() {
return this._withEvent((e) => {
return e.setContext({ ...e.context, logs: (e.context?.logs || []).concat([e.show()]) });
});
}

drawLine() {
console.log(drawLine(this));
return this;
}

_segment(rate) {
return this.struct(pure(true)._fast(rate));
}
Expand Down Expand Up @@ -597,10 +609,6 @@ export class Pattern {
return slowcatPrime(...pats);
}

append(other) {
return fastcat(...[this, other]);
}

rev() {
const pat = this;
const query = function (state) {
Expand Down Expand Up @@ -643,14 +651,31 @@ export class Pattern {
return this.juxBy(1, func);
}

// is there a different name for those in tidal?
stack(...pats) {
return stack(this, ...pats);
}

sequence(...pats) {
return sequence(this, ...pats);
}

// shorthand for sequence
seq(...pats) {
return sequence(this, ...pats);
}

cat(...pats) {
return cat(this, ...pats);
}

fastcat(...pats) {
return fastcat(this, ...pats);
}

slowcat(...pats) {
return slowcat(this, ...pats);
}

superimpose(...funcs) {
return this.stack(...funcs.map((func) => func(this)));
}
Expand Down Expand Up @@ -786,7 +811,20 @@ Pattern.prototype.patternified = [
'velocity',
];
// methods that create patterns, which are added to patternified Pattern methods
Pattern.prototype.factories = { pure, stack, slowcat, fastcat, cat, timeCat, sequence, polymeter, pm, polyrhythm, pr };
Pattern.prototype.factories = {
pure,
stack,
slowcat,
fastcat,
cat,
timeCat,
sequence,
seq,
polymeter,
pm,
polyrhythm,
pr,
};
// the magic happens in Pattern constructor. Keeping this in prototype enables adding methods from the outside (e.g. see tonal.ts)

// Elemental patterns
Expand Down Expand Up @@ -814,18 +852,23 @@ export function reify(thing) {
}
return pure(thing);
}

// Basic functions for combining patterns

export function stack(...pats) {
const reified = pats.map((pat) => reify(pat));
const query = (state) => flatten(reified.map((pat) => pat.query(state)));
// Array test here is to avoid infinite recursions..
pats = pats.map((pat) => (Array.isArray(pat) ? sequence(...pat) : reify(pat)));
const query = (state) => flatten(pats.map((pat) => pat.query(state)));
return new Pattern(query);
}

export function slowcat(...pats) {
// Concatenation: combines a list of patterns, switching between them
// successively, one per cycle.
pats = pats.map(reify);

// Array test here is to avoid infinite recursions..
pats = pats.map((pat) => (Array.isArray(pat) ? sequence(...pat) : reify(pat)));

const query = function (state) {
const span = state.span;
const pat_n = mod(span.begin.sam(), pats.length);
Expand Down Expand Up @@ -862,7 +905,7 @@ export function fastcat(...pats) {
}

export function cat(...pats) {
return fastcat(...pats);
return slowcat(...pats);
}

export function timeCat(...timepats) {
Expand All @@ -878,6 +921,15 @@ export function timeCat(...timepats) {
return stack(...pats);
}

export function sequence(...pats) {
return fastcat(...pats);
}

// shorthand for sequence
export function seq(...pats) {
return fastcat(...pats);
}

function _sequenceCount(x) {
if (Array.isArray(x)) {
if (x.length == 0) {
Expand All @@ -891,10 +943,6 @@ function _sequenceCount(x) {
return [reify(x), 1];
}

export function sequence(...xs) {
return _sequenceCount(xs)[0];
}

export function polymeterSteps(steps, ...args) {
const seqs = args.map((a) => _sequenceCount(a));
if (seqs.length == 0) {
Expand Down Expand Up @@ -941,7 +989,6 @@ export function pr(args) {
}

export const add = curry((a, pat) => pat.add(a));
export const append = curry((a, pat) => pat.append(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));
Expand Down