Merge pull request #295 from tidalcycles/refactor-bootstrap
Move stuff to new register function
This commit is contained in:
commit
5f37a4a21c
23 changed files with 1158 additions and 1266 deletions
|
|
@ -4,7 +4,7 @@ Copyright (C) 2022 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/>.
|
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 { isPattern, Pattern } from './index.mjs';
|
import { isPattern } from './index.mjs';
|
||||||
|
|
||||||
let scoped = false;
|
let scoped = false;
|
||||||
export const evalScope = async (...args) => {
|
export const evalScope = async (...args) => {
|
||||||
|
|
@ -19,7 +19,7 @@ export const evalScope = async (...args) => {
|
||||||
console.warn(`evalScope: module with index ${i} could not be loaded:`, result.reason);
|
console.warn(`evalScope: module with index ${i} could not be loaded:`, result.reason);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Object.assign(globalThis, ...modules, Pattern.prototype.bootstrap());
|
Object.assign(globalThis, ...modules);
|
||||||
};
|
};
|
||||||
|
|
||||||
function safeEval(str, options = {}) {
|
function safeEval(str, options = {}) {
|
||||||
|
|
|
||||||
|
|
@ -581,21 +581,6 @@ export class Pattern {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
patternify(join, func) {
|
|
||||||
const pat = this;
|
|
||||||
return function (...args) {
|
|
||||||
// the problem here: args could be a pattern that has been
|
|
||||||
// turned into an object to add location to avoid object
|
|
||||||
// checking for every pattern method, we can remove it here...
|
|
||||||
// in the future, patternified args should be marked as well +
|
|
||||||
// some better object handling
|
|
||||||
args = args.map((arg) => (isPattern(arg) ? arg.fmap((value) => value.value || value) : arg));
|
|
||||||
const pat_arg = sequence(...args);
|
|
||||||
// arg.locations has to go somewhere..
|
|
||||||
return join(pat_arg.fmap((arg) => func.call(pat, arg)));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
asNumber() {
|
asNumber() {
|
||||||
return this.fmap(parseNumeral);
|
return this.fmap(parseNumeral);
|
||||||
}
|
}
|
||||||
|
|
@ -1277,9 +1262,9 @@ export function register(name, func) {
|
||||||
args = args.map(reify);
|
args = args.map(reify);
|
||||||
// For methods that take a single argument (plus 'this'), allow
|
// For methods that take a single argument (plus 'this'), allow
|
||||||
// multiple arguments but sequence them
|
// multiple arguments but sequence them
|
||||||
if (arity == 2 && args.length != 1) {
|
if (arity === 2 && args.length !== 1) {
|
||||||
args = [sequence(...args)];
|
args = [sequence(...args)];
|
||||||
} else if (arity != args.length + 1) {
|
} else if (arity !== args.length + 1) {
|
||||||
throw new Error(`.${name}() expects ${arity - 1} inputs but got ${args.length}.`);
|
throw new Error(`.${name}() expects ${arity - 1} inputs but got ${args.length}.`);
|
||||||
}
|
}
|
||||||
return pfunc(...args, this);
|
return pfunc(...args, this);
|
||||||
|
|
@ -1533,7 +1518,7 @@ export const { firstOf, every } = register(['firstOf', 'every'], function (n, fu
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like layer, but with a single function:
|
* Like layer, but with a single function:
|
||||||
* @name _apply
|
* @name apply
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @example
|
* @example
|
||||||
* "<c3 eb3 g3>".scale('C minor').apply(scaleTranspose("0,2,4")).note()
|
* "<c3 eb3 g3>".scale('C minor').apply(scaleTranspose("0,2,4")).note()
|
||||||
|
|
@ -1921,83 +1906,3 @@ const { loopAt, loopat } = register(['loopAt', 'loopat'], function (factor, pat)
|
||||||
const { loopAtCps, loopatcps } = register(['loopAtCps', 'loopatcps'], function (factor, cps, pat) {
|
const { loopAtCps, loopatcps } = register(['loopAtCps', 'loopatcps'], function (factor, cps, pat) {
|
||||||
return _loopAt(factor, pat, cps);
|
return _loopAt(factor, pat, cps);
|
||||||
});
|
});
|
||||||
|
|
||||||
// problem: curried functions with spread arguments must have pat at the beginning
|
|
||||||
// with this, we cannot keep the pattern open at the end.. solution for now: use array to keep using pat as last arg
|
|
||||||
|
|
||||||
// these are the core composable functions. they are extended with Pattern.prototype.define below
|
|
||||||
Pattern.prototype.composable = { early, late, superimpose };
|
|
||||||
|
|
||||||
// adds Pattern.prototype.composable to given function as child functions
|
|
||||||
// then you can do transpose(2).late(0.2) instead of x => x.transpose(2).late(0.2)
|
|
||||||
export function makeComposable(func) {
|
|
||||||
Object.entries(Pattern.prototype.composable).forEach(([functionName, composable]) => {
|
|
||||||
// compose with dot
|
|
||||||
func[functionName] = (...args) => {
|
|
||||||
// console.log(`called ${functionName}(${args.join(',')})`);
|
|
||||||
const composition = compose(func, composable(...args));
|
|
||||||
// the composition itself must be composable too :)
|
|
||||||
// then you can do endless chaining transpose(2).late(0.2).fast(2) ...
|
|
||||||
return makeComposable(composition);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return func;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const patternify = (f) => (pata, pat) => pata.fmap((a) => f.call(pat, a)).innerJoin();
|
|
||||||
export const patternify2 = (f) =>
|
|
||||||
function (pata, patb, pat) {
|
|
||||||
return pata
|
|
||||||
.fmap((a) => (b) => f.call(pat, a, b))
|
|
||||||
.appLeft(patb)
|
|
||||||
.innerJoin();
|
|
||||||
};
|
|
||||||
export const patternify3 = (f) => (pata, patb, patc, pat) =>
|
|
||||||
pata
|
|
||||||
.fmap((a) => (b) => (c) => f.call(pat, a, b, c))
|
|
||||||
.appLeft(patb)
|
|
||||||
.appLeft(patc)
|
|
||||||
.innerJoin();
|
|
||||||
export const patternify4 = (f) => (pata, patb, patc, patd, pat) =>
|
|
||||||
pata
|
|
||||||
.fmap((a) => (b) => (c) => (d) => f.call(pat, a, b, c, d))
|
|
||||||
.appLeft(patb)
|
|
||||||
.appLeft(patc)
|
|
||||||
.appLeft(patd)
|
|
||||||
.innerJoin();
|
|
||||||
|
|
||||||
Pattern.prototype.patternified = [];
|
|
||||||
|
|
||||||
// call this after all Pattern.prototype.define calls have been executed! (right before evaluate)
|
|
||||||
Pattern.prototype.bootstrap = function () {
|
|
||||||
const bootstrapped = Object.fromEntries(
|
|
||||||
Object.entries(Pattern.prototype.composable).map(([functionName, composable]) => {
|
|
||||||
if (Pattern.prototype[functionName]) {
|
|
||||||
// without this, 'C^7'.m.chordBass.transpose(2) will throw "C^7".m.chordBass.transpose is not a function
|
|
||||||
// Pattern.prototype[functionName] = makeComposable(Pattern.prototype[functionName]); // is this needed?
|
|
||||||
}
|
|
||||||
return [functionName, curry(composable, makeComposable)];
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.patternified.forEach((prop) => {
|
|
||||||
// the following will patternify all functions in Pattern.prototype.patternified
|
|
||||||
Pattern.prototype[prop] = function (...args) {
|
|
||||||
return this.patternify((x) => x.innerJoin(), Pattern.prototype['_' + prop])(...args);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return bootstrapped;
|
|
||||||
};
|
|
||||||
|
|
||||||
// this will add func as name to list of composable / patternified functions.
|
|
||||||
// those lists will be used in bootstrap to curry and compose everything, to support various call patterns
|
|
||||||
Pattern.prototype.define = (name, func, options = {}) => {
|
|
||||||
if (options.composable) {
|
|
||||||
Pattern.prototype.composable[name] = func;
|
|
||||||
}
|
|
||||||
if (options.patternified) {
|
|
||||||
Pattern.prototype.patternified = Pattern.prototype.patternified.concat([name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Pattern.prototype.bootstrap(); // automatically bootstrap after new definition
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Hap } from './hap.mjs';
|
import { Hap } from './hap.mjs';
|
||||||
import { Pattern, fastcat, reify, silence, stack, isPattern } from './pattern.mjs';
|
import { Pattern, fastcat, reify, silence, stack, register } from './pattern.mjs';
|
||||||
import Fraction from './fraction.mjs';
|
import Fraction from './fraction.mjs';
|
||||||
import { id } from './util.mjs';
|
import { id } from './util.mjs';
|
||||||
|
|
||||||
|
|
@ -258,9 +258,9 @@ export const perlinWith = (pat) => {
|
||||||
*/
|
*/
|
||||||
export const perlin = perlinWith(time.fmap((v) => Number(v)));
|
export const perlin = perlinWith(time.fmap((v) => Number(v)));
|
||||||
|
|
||||||
Pattern.prototype._degradeByWith = function (withPat, x) {
|
export const degradeByWith = register('degradeByWith', (withPat, x, pat) =>
|
||||||
return this.fmap((a) => (_) => a).appLeft(withPat.filterValues((v) => v > x));
|
pat.fmap((a) => (_) => a).appLeft(withPat.filterValues((v) => v > x)),
|
||||||
};
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Randomly removes events from the pattern by a given amount.
|
* Randomly removes events from the pattern by a given amount.
|
||||||
|
|
@ -276,9 +276,9 @@ Pattern.prototype._degradeByWith = function (withPat, x) {
|
||||||
* @example
|
* @example
|
||||||
* s("[hh?0.2]*8")
|
* s("[hh?0.2]*8")
|
||||||
*/
|
*/
|
||||||
Pattern.prototype._degradeBy = function (x) {
|
export const degradeBy = register('degradeBy', function (x, pat) {
|
||||||
return this._degradeByWith(rand, x);
|
return pat._degradeByWith(rand, x);
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -292,9 +292,7 @@ Pattern.prototype._degradeBy = function (x) {
|
||||||
* @example
|
* @example
|
||||||
* s("[hh?]*8")
|
* s("[hh?]*8")
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.degrade = function () {
|
export const degrade = register('degrade', (pat) => pat._degradeBy(0.5));
|
||||||
return this._degradeBy(0.5);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inverse of {@link Pattern#degradeBy}: Randomly removes events from the pattern by a given amount.
|
* Inverse of {@link Pattern#degradeBy}: Randomly removes events from the pattern by a given amount.
|
||||||
|
|
@ -309,26 +307,14 @@ Pattern.prototype.degrade = function () {
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").undegradeBy(0.2)
|
* s("hh*8").undegradeBy(0.2)
|
||||||
*/
|
*/
|
||||||
Pattern.prototype._undegradeBy = function (x) {
|
export const undegradeBy = register('undegradeBy', function (x, pat) {
|
||||||
return this._degradeByWith(
|
return pat._degradeByWith(
|
||||||
rand.fmap((r) => 1 - r),
|
rand.fmap((r) => 1 - r),
|
||||||
x,
|
x,
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
Pattern.prototype.undegrade = function () {
|
export const undegrade = register('undegrade', (pat) => pat._undegradeBy(0.5));
|
||||||
return this._undegradeBy(0.5);
|
|
||||||
};
|
|
||||||
|
|
||||||
Pattern.prototype._sometimesBy = function (x, func) {
|
|
||||||
return stack(this._degradeBy(x), func(this._undegradeBy(1 - x)));
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://github.com/tidalcycles/strudel/discussions/198
|
|
||||||
/* Pattern.prototype._sometimesBy = function (x, other) {
|
|
||||||
other = typeof other === 'function' ? other(this._undegradeBy(1 - x)) : reify(other)._undegradeBy(1 - x);
|
|
||||||
return stack(this._degradeBy(x), other);
|
|
||||||
}; */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -343,24 +329,12 @@ Pattern.prototype._sometimesBy = function (x, func) {
|
||||||
* @example
|
* @example
|
||||||
* s("hh(3,8)").sometimesBy(.4, x=>x.speed("0.5"))
|
* s("hh(3,8)").sometimesBy(.4, x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.sometimesBy = function (patx, func) {
|
|
||||||
const pat = this;
|
|
||||||
return reify(patx)
|
|
||||||
.fmap((x) => pat._sometimesBy(x, func))
|
|
||||||
.innerJoin();
|
|
||||||
};
|
|
||||||
|
|
||||||
// why does this exist? it is identical to sometimesBy
|
export const sometimesBy = register('sometimesBy', function (patx, func, pat) {
|
||||||
Pattern.prototype._sometimesByPre = function (x, func) {
|
|
||||||
return stack(this._degradeBy(x), func(this).undegradeBy(1 - x));
|
|
||||||
};
|
|
||||||
|
|
||||||
Pattern.prototype.sometimesByPre = function (patx, func) {
|
|
||||||
const pat = this;
|
|
||||||
return reify(patx)
|
return reify(patx)
|
||||||
.fmap((x) => pat._sometimesByPre(x, func))
|
.fmap((x) => stack(pat._degradeBy(x), func(pat._undegradeBy(1 - x))))
|
||||||
.innerJoin();
|
.innerJoin();
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -373,20 +347,9 @@ Pattern.prototype.sometimesByPre = function (patx, func) {
|
||||||
* @example
|
* @example
|
||||||
* s("hh*4").sometimes(x=>x.speed("0.5"))
|
* s("hh*4").sometimes(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.sometimes = function (func) {
|
export const sometimes = register('sometimes', function (func, pat) {
|
||||||
return this._sometimesBy(0.5, func);
|
return pat._sometimesBy(0.5, func);
|
||||||
};
|
});
|
||||||
|
|
||||||
Pattern.prototype.sometimesPre = function (func) {
|
|
||||||
return this._sometimesByPre(0.5, func);
|
|
||||||
};
|
|
||||||
|
|
||||||
Pattern.prototype._someCyclesBy = function (x, func) {
|
|
||||||
return stack(
|
|
||||||
this._degradeByWith(rand._segment(1), x),
|
|
||||||
func(this._degradeByWith(rand.fmap((r) => 1 - r)._segment(1), 1 - x)),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -401,12 +364,17 @@ Pattern.prototype._someCyclesBy = function (x, func) {
|
||||||
* @example
|
* @example
|
||||||
* s("hh(3,8)").someCyclesBy(.3, x=>x.speed("0.5"))
|
* s("hh(3,8)").someCyclesBy(.3, x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.someCyclesBy = function (patx, func) {
|
|
||||||
const pat = this;
|
export const someCyclesBy = register('someCyclesBy', function (patx, func, pat) {
|
||||||
return reify(patx)
|
return reify(patx)
|
||||||
.fmap((x) => pat._someCyclesBy(x, func))
|
.fmap((x) =>
|
||||||
|
stack(
|
||||||
|
pat._degradeByWith(rand._segment(1), x),
|
||||||
|
func(pat._degradeByWith(rand.fmap((r) => 1 - r)._segment(1), 1 - x)),
|
||||||
|
),
|
||||||
|
)
|
||||||
.innerJoin();
|
.innerJoin();
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -418,9 +386,9 @@ Pattern.prototype.someCyclesBy = function (patx, func) {
|
||||||
* @example
|
* @example
|
||||||
* s("hh(3,8)").someCycles(x=>x.speed("0.5"))
|
* s("hh(3,8)").someCycles(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.someCycles = function (func) {
|
export const someCycles = register('someCycles', function (func, pat) {
|
||||||
return this._someCyclesBy(0.5, func);
|
return pat._someCyclesBy(0.5, func);
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -432,9 +400,9 @@ Pattern.prototype.someCycles = function (func) {
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").often(x=>x.speed("0.5"))
|
* s("hh*8").often(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.often = function (func) {
|
export const often = register('often', function (func, pat) {
|
||||||
return this.sometimesBy(0.75, func);
|
return pat.sometimesBy(0.75, func);
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -446,9 +414,9 @@ Pattern.prototype.often = function (func) {
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").rarely(x=>x.speed("0.5"))
|
* s("hh*8").rarely(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.rarely = function (func) {
|
export const rarely = register('rarely', function (func, pat) {
|
||||||
return this.sometimesBy(0.25, func);
|
return pat.sometimesBy(0.25, func);
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -460,9 +428,9 @@ Pattern.prototype.rarely = function (func) {
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").almostNever(x=>x.speed("0.5"))
|
* s("hh*8").almostNever(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.almostNever = function (func) {
|
export const almostNever = register('almostNever', function (func, pat) {
|
||||||
return this.sometimesBy(0.1, func);
|
return pat.sometimesBy(0.1, func);
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -474,9 +442,9 @@ Pattern.prototype.almostNever = function (func) {
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").almostAlways(x=>x.speed("0.5"))
|
* s("hh*8").almostAlways(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.almostAlways = function (func) {
|
export const almostAlways = register('almostAlways', function (func, pat) {
|
||||||
return this.sometimesBy(0.9, func);
|
return pat.sometimesBy(0.9, func);
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -488,9 +456,9 @@ Pattern.prototype.almostAlways = function (func) {
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").never(x=>x.speed("0.5"))
|
* s("hh*8").never(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.never = function (func) {
|
export const never = register('never', function (_, pat) {
|
||||||
return this;
|
return pat;
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -502,8 +470,6 @@ Pattern.prototype.never = function (func) {
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").always(x=>x.speed("0.5"))
|
* s("hh*8").always(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.always = function (func) {
|
export const always = register('always', function (func, pat) {
|
||||||
return func(this);
|
return func(pat);
|
||||||
};
|
});
|
||||||
|
|
||||||
Pattern.prototype.patternified.push('degradeBy', 'undegradeBy');
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ Copyright (C) 2022 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/>.
|
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 { Pattern, patternify2, reify } from './index.mjs';
|
import { register } from './index.mjs';
|
||||||
|
|
||||||
let synth;
|
let synth;
|
||||||
try {
|
try {
|
||||||
|
|
@ -16,7 +16,7 @@ try {
|
||||||
let allVoices = synth?.getVoices();
|
let allVoices = synth?.getVoices();
|
||||||
// console.log('voices', allVoices);
|
// console.log('voices', allVoices);
|
||||||
|
|
||||||
function speak(words, lang, voice) {
|
function triggerSpeech(words, lang, voice) {
|
||||||
synth.cancel();
|
synth.cancel();
|
||||||
const utterance = new SpeechSynthesisUtterance(words);
|
const utterance = new SpeechSynthesisUtterance(words);
|
||||||
utterance.lang = lang;
|
utterance.lang = lang;
|
||||||
|
|
@ -31,12 +31,8 @@ function speak(words, lang, voice) {
|
||||||
speechSynthesis.speak(utterance);
|
speechSynthesis.speak(utterance);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern.prototype._speak = function (lang, voice) {
|
export const speak = register('speak', function (lang, voice, pat) {
|
||||||
return this.onTrigger((_, hap) => {
|
return pat.onTrigger((_, hap) => {
|
||||||
speak(hap.value, lang, voice);
|
triggerSpeech(hap.value, lang, voice);
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
Pattern.prototype.speak = function (lang, voice) {
|
|
||||||
return patternify2(Pattern.prototype._speak)(reify(lang), reify(voice), this);
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { getFrequency, logger, Pattern } from '@strudel.cycles/core';
|
import { getFrequency, logger, register } from '@strudel.cycles/core';
|
||||||
import { getAudioContext } from '@strudel.cycles/webaudio';
|
import { getAudioContext } from '@strudel.cycles/webaudio';
|
||||||
import csd from './project.csd?raw';
|
import csd from './project.csd?raw';
|
||||||
// import livecodeOrc from './livecode.orc?raw';
|
// import livecodeOrc from './livecode.orc?raw';
|
||||||
|
|
@ -6,12 +6,24 @@ import presetsOrc from './presets.orc?raw';
|
||||||
|
|
||||||
let csoundLoader, _csound;
|
let csoundLoader, _csound;
|
||||||
|
|
||||||
// triggers given instrument name using csound.
|
// initializes csound + can be used to reevaluate given instrument code
|
||||||
Pattern.prototype._csound = function (instrument) {
|
export async function loadCSound(code = '') {
|
||||||
|
await init();
|
||||||
|
if (code) {
|
||||||
|
code = `${code}`;
|
||||||
|
// ^ ^
|
||||||
|
// wrapping in backticks makes sure it works when calling as templated function
|
||||||
|
await _csound?.evalCode(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const loadcsound = loadCSound;
|
||||||
|
export const loadCsound = loadCSound;
|
||||||
|
|
||||||
|
export const csound = register('csound', (instrument, pat) => {
|
||||||
instrument = instrument || 'triangle';
|
instrument = instrument || 'triangle';
|
||||||
init(); // not async to support csound inside other patterns + to be able to call pattern methods after it
|
init(); // not async to support csound inside other patterns + to be able to call pattern methods after it
|
||||||
// TODO: find a alternative way to wait for csound to load (to wait with first time playback)
|
// TODO: find a alternative way to wait for csound to load (to wait with first time playback)
|
||||||
return this.onTrigger((time, hap) => {
|
return pat.onTrigger((time, hap) => {
|
||||||
if (!_csound) {
|
if (!_csound) {
|
||||||
logger('[csound] not loaded yet', 'warning');
|
logger('[csound] not loaded yet', 'warning');
|
||||||
return;
|
return;
|
||||||
|
|
@ -40,20 +52,7 @@ Pattern.prototype._csound = function (instrument) {
|
||||||
const msg = `i ${params.join(' ')}`;
|
const msg = `i ${params.join(' ')}`;
|
||||||
_csound.inputMessage(msg);
|
_csound.inputMessage(msg);
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
// initializes csound + can be used to reevaluate given instrument code
|
|
||||||
export async function csound(code = '') {
|
|
||||||
await init();
|
|
||||||
if (code) {
|
|
||||||
code = `${code}`;
|
|
||||||
// ^ ^
|
|
||||||
// wrapping in backticks makes sure it works when calling as templated function
|
|
||||||
await _csound?.evalCode(code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Pattern.prototype.define('csound', (a, pat) => pat.csound(a), { composable: false, patternified: true });
|
|
||||||
|
|
||||||
function eventLogger(type, args) {
|
function eventLogger(type, args) {
|
||||||
const [msg] = args;
|
const [msg] = args;
|
||||||
|
|
|
||||||
|
|
@ -225,9 +225,8 @@ function isPatternArg(parents) {
|
||||||
function hasModifierCall(parent) {
|
function hasModifierCall(parent) {
|
||||||
// TODO: modifiers are more than composables, for example every is not composable but should be seen as modifier..
|
// TODO: modifiers are more than composables, for example every is not composable but should be seen as modifier..
|
||||||
// need all prototypes of Pattern
|
// need all prototypes of Pattern
|
||||||
return (
|
return parent?.type === 'StaticMemberExpression';
|
||||||
parent?.type === 'StaticMemberExpression' && Object.keys(Pattern.prototype.composable).includes(parent.property)
|
// && Object.keys(Pattern.prototype.composable).includes(parent.property)
|
||||||
);
|
|
||||||
}
|
}
|
||||||
const factories = Object.keys(Pattern.prototype.factories).concat(['mini']);
|
const factories = Object.keys(Pattern.prototype.factories).concat(['mini']);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,14 @@ import * as krill from './krill-parser.js';
|
||||||
import * as strudel from '@strudel.cycles/core';
|
import * as strudel from '@strudel.cycles/core';
|
||||||
// import { addMiniLocations } from '@strudel.cycles/eval/shapeshifter.mjs';
|
// import { addMiniLocations } from '@strudel.cycles/eval/shapeshifter.mjs';
|
||||||
|
|
||||||
const { pure, Pattern, Fraction, stack, slowcat, sequence, timeCat, silence, reify } = strudel;
|
const { pure, Fraction, stack, slowcat, sequence, timeCat, silence, reify } = strudel;
|
||||||
|
|
||||||
var _seedState = 0;
|
/* var _seedState = 0;
|
||||||
const randOffset = 0.0002;
|
const randOffset = 0.0002;
|
||||||
|
|
||||||
function _nextSeed() {
|
function _nextSeed() {
|
||||||
return _seedState++;
|
return _seedState++;
|
||||||
}
|
} */
|
||||||
|
|
||||||
const applyOptions = (parent) => (pat, i) => {
|
const applyOptions = (parent) => (pat, i) => {
|
||||||
const ast = parent.source_[i];
|
const ast = parent.source_[i];
|
||||||
|
|
@ -30,10 +30,28 @@ const applyOptions = (parent) => (pat, i) => {
|
||||||
case 'bjorklund':
|
case 'bjorklund':
|
||||||
return pat.euclid(operator.arguments_.pulse, operator.arguments_.step, operator.arguments_.rotation);
|
return pat.euclid(operator.arguments_.pulse, operator.arguments_.step, operator.arguments_.rotation);
|
||||||
case 'degradeBy':
|
case 'degradeBy':
|
||||||
|
// TODO: find out what is right here
|
||||||
|
// example:
|
||||||
|
/*
|
||||||
|
stack(
|
||||||
|
s("hh*8").degrade(),
|
||||||
|
s("[ht*8]?")
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
// above example will only be in sync when _degradeBy is used...
|
||||||
|
// it also seems that the nextSeed will create undeterministic behaviour
|
||||||
|
// as it uses a global _seedState. This is probably the reason for
|
||||||
|
// https://github.com/tidalcycles/strudel/issues/245
|
||||||
|
|
||||||
|
// this is how it was:
|
||||||
|
/*
|
||||||
return reify(pat)._degradeByWith(
|
return reify(pat)._degradeByWith(
|
||||||
strudel.rand.early(randOffset * _nextSeed()).segment(1),
|
strudel.rand.early(randOffset * _nextSeed()).segment(1),
|
||||||
operator.arguments_.amount,
|
operator.arguments_.amount ?? 0.5,
|
||||||
);
|
);
|
||||||
|
*/
|
||||||
|
return reify(pat)._degradeBy(operator.arguments_.amount ?? 0.5);
|
||||||
|
|
||||||
// TODO: case 'fixed-step': "%"
|
// TODO: case 'fixed-step': "%"
|
||||||
}
|
}
|
||||||
console.warn(`operator "${operator.type_}" not implemented`);
|
console.warn(`operator "${operator.type_}" not implemented`);
|
||||||
|
|
@ -96,7 +114,9 @@ export function patternifyAST(ast) {
|
||||||
return stack(...children);
|
return stack(...children);
|
||||||
}
|
}
|
||||||
if (alignment === 'r') {
|
if (alignment === 'r') {
|
||||||
return strudel.chooseInWith(strudel.rand.early(randOffset * _nextSeed()).segment(1), children);
|
// https://github.com/tidalcycles/strudel/issues/245#issuecomment-1345406422
|
||||||
|
// return strudel.chooseInWith(strudel.rand.early(randOffset * _nextSeed()).segment(1), children);
|
||||||
|
return strudel.chooseCycles(...children);
|
||||||
}
|
}
|
||||||
const weightedChildren = ast.source_.some((child) => !!child.options_?.weight);
|
const weightedChildren = ast.source_.some((child) => !!child.options_?.weight);
|
||||||
if (!weightedChildren && alignment === 't') {
|
if (!weightedChildren && alignment === 't') {
|
||||||
|
|
@ -178,11 +198,6 @@ export const h = (string) => {
|
||||||
return patternifyAST(ast);
|
return patternifyAST(ast);
|
||||||
};
|
};
|
||||||
|
|
||||||
// shorthand for mini
|
|
||||||
Pattern.prototype.define('mini', mini, { composable: true });
|
|
||||||
Pattern.prototype.define('m', mini, { composable: true });
|
|
||||||
Pattern.prototype.define('h', h, { composable: true });
|
|
||||||
|
|
||||||
export function minify(thing) {
|
export function minify(thing) {
|
||||||
if (typeof thing === 'string') {
|
if (typeof thing === 'string') {
|
||||||
return mini(thing);
|
return mini(thing);
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ describe('mini', () => {
|
||||||
expect(haps.length < 230).toBe(true);
|
expect(haps.length < 230).toBe(true);
|
||||||
// 'Had too many cycles remaining after degradeBy 0.8');
|
// 'Had too many cycles remaining after degradeBy 0.8');
|
||||||
});
|
});
|
||||||
it('supports the random choice operator ("|") with nesting', () => {
|
/*it('supports the random choice operator ("|") with nesting', () => {
|
||||||
const numCycles = 900;
|
const numCycles = 900;
|
||||||
const haps = mini('a | [b | c] | [d | e | f]').queryArc(0, numCycles);
|
const haps = mini('a | [b | c] | [d | e | f]').queryArc(0, numCycles);
|
||||||
// Should have about 1/3 a, 1/6 each of b | c, and 1/9 each of d | e | f.
|
// Should have about 1/3 a, 1/6 each of b | c, and 1/9 each of d | e | f.
|
||||||
|
|
@ -103,5 +103,5 @@ describe('mini', () => {
|
||||||
// PRNG, this test should succeed
|
// PRNG, this test should succeed
|
||||||
expect(chisq <= 15.086).toBe(true);
|
expect(chisq <= 15.086).toBe(true);
|
||||||
// assert(chisq <= 15.086, chisq + ' was expected to be less than 15.086 under chi-squared test');
|
// assert(chisq <= 15.086, chisq + ' was expected to be less than 15.086 under chi-squared test');
|
||||||
});
|
});*/
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,5 @@
|
||||||
import './tonal.mjs';
|
import './tonal.mjs';
|
||||||
import './voicings.mjs';
|
import './voicings.mjs';
|
||||||
|
|
||||||
|
export * from './tonal.mjs';
|
||||||
|
export * from './voicings.mjs';
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Note, Interval, Scale } from '@tonaljs/tonal';
|
import { Note, Interval, Scale } from '@tonaljs/tonal';
|
||||||
import { Pattern, _mod } from '@strudel.cycles/core';
|
import { register, _mod } from '@strudel.cycles/core';
|
||||||
|
|
||||||
// transpose note inside scale by offset steps
|
// transpose note inside scale by offset steps
|
||||||
// function scaleOffset(scale: string, offset: number, note: string) {
|
// function scaleOffset(scale: string, offset: number, note: string) {
|
||||||
|
|
@ -74,8 +74,8 @@ function scaleOffset(scale, offset, note) {
|
||||||
* "c2 c3".fast(2).transpose("<1P -2M 4P 3m>".slow(2)).note()
|
* "c2 c3".fast(2).transpose("<1P -2M 4P 3m>".slow(2)).note()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pattern.prototype._transpose = function (intervalOrSemitones) {
|
export const transpose = register('transpose', function (intervalOrSemitones, pat) {
|
||||||
return this.withHap((hap) => {
|
return pat.withHap((hap) => {
|
||||||
const interval = !isNaN(Number(intervalOrSemitones))
|
const interval = !isNaN(Number(intervalOrSemitones))
|
||||||
? Interval.fromSemitones(intervalOrSemitones /* as number */)
|
? Interval.fromSemitones(intervalOrSemitones /* as number */)
|
||||||
: String(intervalOrSemitones);
|
: String(intervalOrSemitones);
|
||||||
|
|
@ -87,11 +87,9 @@ Pattern.prototype._transpose = function (intervalOrSemitones) {
|
||||||
// tone.js doesn't understand multiple sharps flats e.g. F##3 has to be turned into G3
|
// tone.js doesn't understand multiple sharps flats e.g. F##3 has to be turned into G3
|
||||||
return hap.withValue(() => Note.simplify(Note.transpose(hap.value, interval)));
|
return hap.withValue(() => Note.simplify(Note.transpose(hap.value, interval)));
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
// example: transpose(3).late(0.2) will be equivalent to compose(transpose(3), late(0.2))
|
// example: transpose(3).late(0.2) will be equivalent to compose(transpose(3), late(0.2))
|
||||||
// TODO: add Pattern.define(name, function, options) that handles all the meta programming stuff
|
|
||||||
// TODO: find out how to patternify this function when it's standalone
|
|
||||||
// e.g. `stack(c3).superimpose(transpose(slowcat(7, 5)))` or
|
// e.g. `stack(c3).superimpose(transpose(slowcat(7, 5)))` or
|
||||||
// or even `stack(c3).superimpose(transpose.slowcat(7, 5))` or
|
// or even `stack(c3).superimpose(transpose.slowcat(7, 5))` or
|
||||||
|
|
||||||
|
|
@ -110,8 +108,8 @@ Pattern.prototype._transpose = function (intervalOrSemitones) {
|
||||||
* .note()
|
* .note()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pattern.prototype._scaleTranspose = function (offset /* : number | string */) {
|
export const scaleTranspose = register('scaleTranspose', function (offset /* : number | string */, pat) {
|
||||||
return this.withHap((hap) => {
|
return pat.withHap((hap) => {
|
||||||
if (!hap.context.scale) {
|
if (!hap.context.scale) {
|
||||||
throw new Error('can only use scaleTranspose after .scale');
|
throw new Error('can only use scaleTranspose after .scale');
|
||||||
}
|
}
|
||||||
|
|
@ -120,7 +118,7 @@ Pattern.prototype._scaleTranspose = function (offset /* : number | string */) {
|
||||||
}
|
}
|
||||||
return hap.withValue(() => scaleOffset(hap.context.scale, Number(offset), hap.value));
|
return hap.withValue(() => scaleOffset(hap.context.scale, Number(offset), hap.value));
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like {@link Pattern#scaleTranspose}.
|
* Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like {@link Pattern#scaleTranspose}.
|
||||||
|
|
@ -141,8 +139,8 @@ Pattern.prototype._scaleTranspose = function (offset /* : number | string */) {
|
||||||
* .note()
|
* .note()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pattern.prototype._scale = function (scale /* : string */) {
|
export const scale = register('scale', function (scale /* : string */, pat) {
|
||||||
return this.withHap((hap) => {
|
return pat.withHap((hap) => {
|
||||||
let note = hap.value;
|
let note = hap.value;
|
||||||
const asNumber = Number(note);
|
const asNumber = Number(note);
|
||||||
if (!isNaN(asNumber)) {
|
if (!isNaN(asNumber)) {
|
||||||
|
|
@ -152,8 +150,4 @@ Pattern.prototype._scale = function (scale /* : string */) {
|
||||||
}
|
}
|
||||||
return hap.withValue(() => note).setContext({ ...hap.context, scale });
|
return hap.withValue(() => note).setContext({ ...hap.context, scale });
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
Pattern.prototype.define('transpose', (a, pat) => pat.transpose(a), { composable: true, patternified: true });
|
|
||||||
Pattern.prototype.define('scale', (a, pat) => pat.scale(a), { composable: true, patternified: true });
|
|
||||||
Pattern.prototype.define('scaleTranspose', (a, pat) => pat.scaleTranspose(a), { composable: true, patternified: true });
|
|
||||||
|
|
|
||||||
|
|
@ -4,31 +4,49 @@ Copyright (C) 2022 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/>.
|
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 { Pattern as _Pattern, stack, Hap, reify } from '@strudel.cycles/core';
|
import { stack, register } from '@strudel.cycles/core';
|
||||||
import _voicings from 'chord-voicings';
|
import _voicings from 'chord-voicings';
|
||||||
const { dictionaryVoicing, minTopNoteDiff, lefthand } = _voicings.default || _voicings; // parcel module resolution fuckup
|
const { dictionaryVoicing, minTopNoteDiff, lefthand } = _voicings.default || _voicings; // parcel module resolution fuckup
|
||||||
|
|
||||||
const getVoicing = (chord, lastVoicing, range = ['F3', 'A4']) =>
|
export const voicingRegistry = {
|
||||||
dictionaryVoicing({
|
lefthand: { dictionary: lefthand, range: ['F3', 'A4'] },
|
||||||
|
};
|
||||||
|
export const setVoicingRange = (name, range) => addVoicings(name, voicingRegistry[name].dictionary, range);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new custom voicing dictionary.
|
||||||
|
*
|
||||||
|
* @name addVoicings
|
||||||
|
* @memberof Pattern
|
||||||
|
* @param {string} name identifier for the voicing dictionary
|
||||||
|
* @param {Object} dictionary maps chord symbol to possible voicings
|
||||||
|
* @param {Array} range min, max note
|
||||||
|
* @returns Pattern
|
||||||
|
* @example
|
||||||
|
* addVoicings('cookie', {
|
||||||
|
* 7: ['3M 7m 9M 12P 15P', '7m 10M 13M 16M 19P'],
|
||||||
|
* '^7': ['3M 6M 9M 12P 14M', '7M 10M 13M 16M 19P'],
|
||||||
|
* m7: ['8P 11P 14m 17m 19P', '5P 8P 11P 14m 17m'],
|
||||||
|
* m7b5: ['3m 5d 8P 11P 14m', '5d 8P 11P 14m 17m'],
|
||||||
|
* o7: ['3m 6M 9M 11A 15P'],
|
||||||
|
* '7alt': ['3M 7m 10m 13m 15P'],
|
||||||
|
* '7#11': ['7m 10m 13m 15P 17m'],
|
||||||
|
* }, ['C3', 'C6'])
|
||||||
|
* "<C^7 A7 Dm7 G7>".voicings('cookie').note()
|
||||||
|
*/
|
||||||
|
export const addVoicings = (name, dictionary, range = ['F3', 'A4']) => {
|
||||||
|
Object.assign(voicingRegistry, { [name]: { dictionary, range } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const getVoicing = (chord, dictionaryName, lastVoicing) => {
|
||||||
|
const { dictionary, range } = voicingRegistry[dictionaryName];
|
||||||
|
return dictionaryVoicing({
|
||||||
chord,
|
chord,
|
||||||
dictionary: lefthand,
|
dictionary,
|
||||||
range,
|
range,
|
||||||
picker: minTopNoteDiff,
|
picker: minTopNoteDiff,
|
||||||
lastVoicing,
|
lastVoicing,
|
||||||
});
|
});
|
||||||
|
|
||||||
const Pattern = _Pattern;
|
|
||||||
|
|
||||||
Pattern.prototype.fmapNested = function (func) {
|
|
||||||
return new Pattern((span) =>
|
|
||||||
this.query(span)
|
|
||||||
.map((event) =>
|
|
||||||
reify(func(event))
|
|
||||||
.query(span)
|
|
||||||
.map((hap) => new Hap(event.whole, event.part, hap.value, hap.context)),
|
|
||||||
)
|
|
||||||
.flat(),
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -37,32 +55,38 @@ Pattern.prototype.fmapNested = function (func) {
|
||||||
*
|
*
|
||||||
* @name voicings
|
* @name voicings
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @param {range} range note range for possible voicings (optional, defaults to `['F3', 'A4']`)
|
* @param {string} dictionary which voicing dictionary to use.
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* stack("<C^7 A7 Dm7 G7>".voicings(), "<C3 A2 D3 G2>").note()
|
* stack("<C^7 A7 Dm7 G7>".voicings('lefthand'), "<C3 A2 D3 G2>").note()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pattern.prototype.voicings = function (range) {
|
let lastVoicing; // this now has to be global until another solution is found :-/
|
||||||
let lastVoicing;
|
// it used to be local to the voicings function at evaluation time
|
||||||
if (!range?.length) {
|
// but since register will patternify by default, means that
|
||||||
// allows to pass empty array, if too lazy to specify range
|
// the function is called over and over again, resetting the lastVoicing variables
|
||||||
range = ['F3', 'A4'];
|
export const voicings = register('voicings', function (dictionary, pat) {
|
||||||
}
|
return pat
|
||||||
return this.fmapNested((event) => {
|
.fmap((value) => {
|
||||||
lastVoicing = getVoicing(event.value, lastVoicing, range);
|
lastVoicing = getVoicing(value, dictionary, lastVoicing);
|
||||||
return stack(...lastVoicing).withContext(() => ({
|
return stack(...lastVoicing);
|
||||||
locations: event.context.locations || [],
|
})
|
||||||
}));
|
.outerJoin();
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
Pattern.prototype._rootNotes = function (octave = 2) {
|
/**
|
||||||
return this.fmap((value) => {
|
* Maps the chords of the incoming pattern to root notes in the given octave.
|
||||||
|
*
|
||||||
|
* @name rootNotes
|
||||||
|
* @memberof Pattern
|
||||||
|
* @param {octave} octave octave to use
|
||||||
|
* @returns Pattern
|
||||||
|
* @example
|
||||||
|
* "<C^7 A7 Dm7 G7>".rootNotes(2).note()
|
||||||
|
*/
|
||||||
|
export const rootNotes = register('rootNotes', function (octave, pat) {
|
||||||
|
return pat.fmap((value) => {
|
||||||
const root = value.match(/^([a-gA-G][b#]?).*$/)[1];
|
const root = value.match(/^([a-gA-G][b#]?).*$/)[1];
|
||||||
return root + octave;
|
return root + octave;
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
Pattern.prototype.define('voicings', (range, pat) => pat.voicings(range), { composable: true });
|
|
||||||
Pattern.prototype.define('rootNotes', (oct, pat) => pat.rootNotes(oct), { composable: true, patternified: true });
|
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,15 @@ Copyright (C) 2022 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/>.
|
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 { Pattern } from '@strudel.cycles/core';
|
import { register } from '@strudel.cycles/core';
|
||||||
import * as _Tone from 'tone';
|
import * as _Tone from 'tone';
|
||||||
|
|
||||||
// import Tone from here, to make sure to get the same AudioContext
|
// import Tone from here, to make sure to get the same AudioContext
|
||||||
export const Tone = _Tone;
|
export const Tone = _Tone;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
AutoFilter,
|
|
||||||
Destination,
|
|
||||||
Filter,
|
Filter,
|
||||||
Gain,
|
Gain,
|
||||||
isNote,
|
|
||||||
Synth,
|
Synth,
|
||||||
PolySynth,
|
PolySynth,
|
||||||
MembraneSynth,
|
MembraneSynth,
|
||||||
|
|
@ -49,8 +46,8 @@ export const getDefaultSynth = () => {
|
||||||
// https://www.charlie-roberts.com/gibberish/playground/
|
// https://www.charlie-roberts.com/gibberish/playground/
|
||||||
|
|
||||||
// with this function, you can play the pattern with any tone synth
|
// with this function, you can play the pattern with any tone synth
|
||||||
Pattern.prototype.tone = function (instrument) {
|
export const tone = register('tone', function (instrument, pat) {
|
||||||
return this.onTrigger((time, hap) => {
|
return pat.onTrigger((time, hap) => {
|
||||||
let note;
|
let note;
|
||||||
let velocity = hap.context?.velocity ?? 0.75;
|
let velocity = hap.context?.velocity ?? 0.75;
|
||||||
if (instrument instanceof PluckSynth) {
|
if (instrument instanceof PluckSynth) {
|
||||||
|
|
@ -74,9 +71,7 @@ Pattern.prototype.tone = function (instrument) {
|
||||||
instrument.triggerAttackRelease(note, hap.duration.valueOf(), time, velocity);
|
instrument.triggerAttackRelease(note, hap.duration.valueOf(), time, velocity);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
Pattern.prototype.define('tone', (type, pat) => pat.tone(type), { composable: true, patternified: false });
|
|
||||||
|
|
||||||
// synth helpers
|
// synth helpers
|
||||||
export const amsynth = (options) => new AMSynth(options);
|
export const amsynth = (options) => new AMSynth(options);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ Copyright (C) 2022 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/>.
|
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 { Pattern, getFrequency, patternify2 } from '@strudel.cycles/core';
|
|
||||||
import * as strudel from '@strudel.cycles/core';
|
import * as strudel from '@strudel.cycles/core';
|
||||||
import { fromMidi, logger, toMidi } from '@strudel.cycles/core';
|
import { fromMidi, logger, toMidi } from '@strudel.cycles/core';
|
||||||
import './feedbackdelay.mjs';
|
import './feedbackdelay.mjs';
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,4 @@
|
||||||
import './xen.mjs';
|
import './xen.mjs';
|
||||||
import './tune.mjs';
|
import './tune.mjs';
|
||||||
|
|
||||||
|
export * from './xen.mjs';
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,16 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Tune from './tunejs.js';
|
import Tune from './tunejs.js';
|
||||||
import { Pattern } from '@strudel.cycles/core';
|
import { register } from '@strudel.cycles/core';
|
||||||
|
|
||||||
Pattern.prototype._tune = function (scale, tonic = 220) {
|
export const tune = register('tune', (scale, pat) => {
|
||||||
const tune = new Tune();
|
const tune = new Tune();
|
||||||
if (!tune.isValidScale(scale)) {
|
if (!tune.isValidScale(scale)) {
|
||||||
throw new Error('not a valid tune.js scale name: "' + scale + '". See http://abbernie.github.io/tune/scales.html');
|
throw new Error('not a valid tune.js scale name: "' + scale + '". See http://abbernie.github.io/tune/scales.html');
|
||||||
}
|
}
|
||||||
tune.loadScale(scale);
|
tune.loadScale(scale);
|
||||||
tune.tonicize(tonic);
|
tune.tonicize(1);
|
||||||
return this._asNumber()._withHap((hap) => {
|
return pat.withHap((hap) => {
|
||||||
return hap.withValue(() => tune.note(hap.value)).setContext({ ...hap.context, type: 'frequency' });
|
return hap.withValue(() => tune.note(hap.value));
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
Pattern.prototype.define('tune', (scale, pat) => pat.tune(scale), { composable: true, patternified: true });
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ Copyright (C) 2022 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/>.
|
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 { Pattern, _mod } from '@strudel.cycles/core';
|
import { register, _mod, parseNumeral } from '@strudel.cycles/core';
|
||||||
|
|
||||||
export function edo(name) {
|
export function edo(name) {
|
||||||
if (!/^[1-9]+[0-9]*edo$/.test(name)) {
|
if (!/^[1-9]+[0-9]*edo$/.test(name)) {
|
||||||
|
|
@ -48,20 +48,17 @@ function xenOffset(xenScale, offset, index = 0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// scaleNameOrRatios: string || number[], steps?: number
|
// scaleNameOrRatios: string || number[], steps?: number
|
||||||
Pattern.prototype._xen = function (scaleNameOrRatios, steps) {
|
export const xen = register('xen', function (scaleNameOrRatios, pat) {
|
||||||
return this._asNumber()._withHap((hap) => {
|
return pat.withHap((hap) => {
|
||||||
const scale = getXenScale(scaleNameOrRatios);
|
const scale = getXenScale(scaleNameOrRatios);
|
||||||
steps = steps || scale.length;
|
const frequency = xenOffset(scale, parseNumeral(hap.value));
|
||||||
const frequency = xenOffset(scale, hap.value);
|
return hap.withValue(() => frequency);
|
||||||
return hap.withValue(() => frequency).setContext({ ...hap.context, type: 'frequency' });
|
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
Pattern.prototype.tuning = function (steps) {
|
export const tuning = register('tuning', function (ratios, pat) {
|
||||||
return this._asNumber()._withHap((hap) => {
|
return pat.withHap((hap) => {
|
||||||
const frequency = xenOffset(steps, hap.value);
|
const frequency = xenOffset(ratios, parseNumeral(hap.value));
|
||||||
return hap.withValue(() => frequency).setContext({ ...hap.context, type: 'frequency' });
|
return hap.withValue(() => frequency);
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
Pattern.prototype.define('xen', (scale, pat) => pat.xen(scale), { composable: true, patternified: true });
|
|
||||||
// Pattern.prototype.define('tuning', (scale, pat) => pat.xen(scale), { composable: true, patternified: false });
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ const supabase = createClient(
|
||||||
|
|
||||||
const modules = [
|
const modules = [
|
||||||
import('@strudel.cycles/core'),
|
import('@strudel.cycles/core'),
|
||||||
// import('@strudel.cycles/tone'),
|
|
||||||
import('@strudel.cycles/tonal'),
|
import('@strudel.cycles/tonal'),
|
||||||
import('@strudel.cycles/mini'),
|
import('@strudel.cycles/mini'),
|
||||||
import('@strudel.cycles/midi'),
|
import('@strudel.cycles/midi'),
|
||||||
|
|
@ -46,7 +45,6 @@ const modules = [
|
||||||
];
|
];
|
||||||
|
|
||||||
evalScope(
|
evalScope(
|
||||||
// Tone,
|
|
||||||
controls, // sadly, this cannot be exported from core direclty
|
controls, // sadly, this cannot be exported from core direclty
|
||||||
{ WebDirt },
|
{ WebDirt },
|
||||||
...modules,
|
...modules,
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,7 @@ import { mini } from '@strudel.cycles/mini/mini.mjs';
|
||||||
// import euclid from '@strudel.cycles/core/euclid.mjs';
|
// import euclid from '@strudel.cycles/core/euclid.mjs';
|
||||||
// import '@strudel.cycles/tone/tone.mjs';
|
// import '@strudel.cycles/tone/tone.mjs';
|
||||||
// import '@strudel.cycles/midi/midi.mjs';
|
// import '@strudel.cycles/midi/midi.mjs';
|
||||||
import '@strudel.cycles/tonal/voicings.mjs';
|
import * as tonalHelpers from '@strudel.cycles/tonal';
|
||||||
import '@strudel.cycles/tonal/tonal.mjs';
|
|
||||||
import '@strudel.cycles/xen/xen.mjs';
|
import '@strudel.cycles/xen/xen.mjs';
|
||||||
// import '@strudel.cycles/xen/tune.mjs';
|
// import '@strudel.cycles/xen/tune.mjs';
|
||||||
// import '@strudel.cycles/core/euclid.mjs';
|
// import '@strudel.cycles/core/euclid.mjs';
|
||||||
|
|
@ -154,16 +153,19 @@ const audioCtx = {
|
||||||
const getDrawContext = () => canvasCtx;
|
const getDrawContext = () => canvasCtx;
|
||||||
const getAudioContext = () => audioCtx;
|
const getAudioContext = () => audioCtx;
|
||||||
const loadSoundfont = () => {};
|
const loadSoundfont = () => {};
|
||||||
|
const loadCsound = () => {};
|
||||||
|
const loadCSound = () => {};
|
||||||
|
const loadcsound = () => {};
|
||||||
|
|
||||||
// TODO: refactor to evalScope
|
// TODO: refactor to evalScope
|
||||||
evalScope(
|
evalScope(
|
||||||
// Tone,
|
// Tone,
|
||||||
strudel,
|
strudel,
|
||||||
strudel.Pattern.prototype.bootstrap(),
|
|
||||||
toneHelpersMocked,
|
toneHelpersMocked,
|
||||||
uiHelpersMocked,
|
uiHelpersMocked,
|
||||||
controls,
|
controls,
|
||||||
webaudio,
|
webaudio,
|
||||||
|
tonalHelpers,
|
||||||
/* controls,
|
/* controls,
|
||||||
toneHelpers,
|
toneHelpers,
|
||||||
voicingHelpers,
|
voicingHelpers,
|
||||||
|
|
@ -179,6 +181,9 @@ evalScope(
|
||||||
getDrawContext,
|
getDrawContext,
|
||||||
getAudioContext,
|
getAudioContext,
|
||||||
loadSoundfont,
|
loadSoundfont,
|
||||||
|
loadCSound,
|
||||||
|
loadCsound,
|
||||||
|
loadcsound,
|
||||||
Clock: {}, // whatever
|
Clock: {}, // whatever
|
||||||
// Tone,
|
// Tone,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -89,10 +89,11 @@ export const whirlyStrudel = `seq(e4, [b2, b3], c4)
|
||||||
|
|
||||||
export const transposedChordsHacked = `stack(
|
export const transposedChordsHacked = `stack(
|
||||||
"c2 eb2 g2",
|
"c2 eb2 g2",
|
||||||
"Cm7".voicings(['g2','c4']).slow(2)
|
"Cm7".voicings('lefthand').slow(2)
|
||||||
).transpose(
|
).transpose(
|
||||||
"<1 2 3 2>".slow(2)
|
"<1 2 3 2>".slow(2)
|
||||||
).transpose(5)`;
|
).transpose(5)`;
|
||||||
|
// range ['g2','c4']
|
||||||
|
|
||||||
export const scaleTranspose = `"f2,f3,c4,ab4"
|
export const scaleTranspose = `"f2,f3,c4,ab4"
|
||||||
.scale(seq('F minor', 'F harmonic minor').slow(4))
|
.scale(seq('F minor', 'F harmonic minor').slow(4))
|
||||||
|
|
@ -102,13 +103,14 @@ export const scaleTranspose = `"f2,f3,c4,ab4"
|
||||||
export const struct = `stack(
|
export const struct = `stack(
|
||||||
"c2 g2 a2 [e2@2 eb2] d2 a2 g2 [d2 ~ db2]",
|
"c2 g2 a2 [e2@2 eb2] d2 a2 g2 [d2 ~ db2]",
|
||||||
"[C^7 A7] [Dm7 G7]".struct("[x@2 x] [~@2 x] [~ x@2]@2 [x ~@2] ~ [~@2 x@4]@2")
|
"[C^7 A7] [Dm7 G7]".struct("[x@2 x] [~@2 x] [~ x@2]@2 [x ~@2] ~ [~@2 x@4]@2")
|
||||||
.voicings(['G3','A4'])
|
.voicings('lefthand')
|
||||||
).slow(4)`;
|
).slow(4)`;
|
||||||
|
// range ['G3','A4']
|
||||||
|
|
||||||
export const magicSofa = `stack(
|
export const magicSofa = `stack(
|
||||||
"<C^7 F^7 ~> <Dm7 G7 A7 ~>"
|
"<C^7 F^7 ~> <Dm7 G7 A7 ~>"
|
||||||
.every(2, fast(2))
|
.every(2, fast(2))
|
||||||
.voicings(),
|
.voicings('lefthand'),
|
||||||
"<c2 f2 g2> <d2 g2 a2 e2>"
|
"<c2 f2 g2> <d2 g2 a2 e2>"
|
||||||
).transpose("<0 2 3 4>")`;
|
).transpose("<0 2 3 4>")`;
|
||||||
// below doesn't work anymore due to constructor cleanup
|
// below doesn't work anymore due to constructor cleanup
|
||||||
|
|
@ -156,7 +158,7 @@ const synths = stack(
|
||||||
scaleTranspose(8).early(3/8)
|
scaleTranspose(8).early(3/8)
|
||||||
).apply(thru).tone(keys).mask("<~ x>/16"),
|
).apply(thru).tone(keys).mask("<~ x>/16"),
|
||||||
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2)).apply(thru).tone(bass),
|
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2)).apply(thru).tone(bass),
|
||||||
"<Cm7 Bb7 Fm7 G7b13>/2".struct("~ [x@0.1 ~]".fast(2)).voicings().apply(thru).every(2, early(1/8)).tone(keys).mask("<x@7 ~>/8".early(1/4))
|
"<Cm7 Bb7 Fm7 G7b13>/2".struct("~ [x@0.1 ~]".fast(2)).voicings('lefthand').apply(thru).every(2, early(1/8)).tone(keys).mask("<x@7 ~>/8".early(1/4))
|
||||||
)
|
)
|
||||||
stack(
|
stack(
|
||||||
drums.fast(2),
|
drums.fast(2),
|
||||||
|
|
@ -197,7 +199,7 @@ export const giantStepsReggae = `stack(
|
||||||
"B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]"
|
"B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]"
|
||||||
)
|
)
|
||||||
.struct("~ [x ~]".fast(4*8))
|
.struct("~ [x ~]".fast(4*8))
|
||||||
.voicings(['E3', 'G4']),
|
.voicings('lefthand'),
|
||||||
// bass
|
// bass
|
||||||
seq(
|
seq(
|
||||||
"[B2 D2] [G2 D2] [Eb2 Bb2] [A2 D2]",
|
"[B2 D2] [G2 D2] [Eb2 Bb2] [A2 D2]",
|
||||||
|
|
@ -208,6 +210,8 @@ export const giantStepsReggae = `stack(
|
||||||
.struct("x ~".fast(4*8))
|
.struct("x ~".fast(4*8))
|
||||||
).slow(25).note()`;
|
).slow(25).note()`;
|
||||||
|
|
||||||
|
// range ['E3', 'G4']
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
/*
|
/*
|
||||||
export const xylophoneCalling = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
|
export const xylophoneCalling = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
|
@ -351,7 +355,7 @@ stack(
|
||||||
|
|
||||||
export const bossa = `const scales = sequence('C minor', ['D locrian', 'G phrygian'], 'Bb2 minor', ['C locrian','F phrygian']).slow(4)
|
export const bossa = `const scales = sequence('C minor', ['D locrian', 'G phrygian'], 'Bb2 minor', ['C locrian','F phrygian']).slow(4)
|
||||||
stack(
|
stack(
|
||||||
"<Cm7 [Dm7b5 G7b9] Bbm7 [Cm7b5 F7b9]>".fast(2).struct("x ~ x@3 x ~ x ~ ~ ~ x ~ x@3".late(1/8)).early(1/8).slow(2).voicings(),
|
"<Cm7 [Dm7b5 G7b9] Bbm7 [Cm7b5 F7b9]>".fast(2).struct("x ~ x@3 x ~ x ~ ~ ~ x ~ x@3".late(1/8)).early(1/8).slow(2).voicings('lefthand'),
|
||||||
"[~ [0 ~]] 0 [~ [4 ~]] 4".sub(7).restart(scales).scale(scales).early(.25)
|
"[~ [0 ~]] 0 [~ [4 ~]] 4".sub(7).restart(scales).scale(scales).early(.25)
|
||||||
).note().piano().slow(2)`;
|
).note().piano().slow(2)`;
|
||||||
|
|
||||||
|
|
@ -443,9 +447,11 @@ export const bossaRandom = `const chords = "<Am7 Am7 Dm7 E7>"
|
||||||
const roots = chords.rootNotes(2)
|
const roots = chords.rootNotes(2)
|
||||||
|
|
||||||
stack(
|
stack(
|
||||||
chords.voicings(['F4', 'A5']).struct(
|
chords.voicings('lefthand').struct(
|
||||||
\` x@2 ~ x ~ ~ ~ x |
|
\` x@2 ~ x ~ ~ ~ x |
|
||||||
x? ~ ~ x@3 ~ x |
|
x? ~ ~ x@3 ~ x |
|
||||||
x? ~ ~ x ~ x@3\`),
|
x? ~ ~ x ~ x@3\`),
|
||||||
roots.struct("x [~ x?0.2] x [~ x?] | x!4 | x@2 ~ ~ ~ x x x").transpose("0 7")
|
roots.struct("x [~ x?0.2] x [~ x?] | x!4 | x@2 ~ ~ ~ x x x").transpose("0 7")
|
||||||
).slow(2).pianoroll().note().piano()`;
|
).slow(2).pianoroll().note().piano()`;
|
||||||
|
|
||||||
|
// range ['F4', 'A5']
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,8 @@ stack(
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const giantSteps = `// John Coltrane - Giant Steps
|
export const giantSteps = `// John Coltrane - Giant Steps
|
||||||
|
setVoicingRange('lefthand', ['E3', 'G4']);
|
||||||
|
|
||||||
stack(
|
stack(
|
||||||
// melody
|
// melody
|
||||||
seq(
|
seq(
|
||||||
|
|
@ -81,7 +83,7 @@ stack(
|
||||||
"[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]",
|
"[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]",
|
||||||
"Eb^7 [Am7 D7] G^7 [C#m7 F#7]",
|
"Eb^7 [Am7 D7] G^7 [C#m7 F#7]",
|
||||||
"B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]"
|
"B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]"
|
||||||
).voicings(['E3', 'G4']),
|
).voicings('lefthand'),
|
||||||
// bass
|
// bass
|
||||||
seq(
|
seq(
|
||||||
"[B2 D2] [G2 Bb2] [Eb2 Bb3] [A2 D2]",
|
"[B2 D2] [G2 Bb2] [Eb2 Bb3] [A2 D2]",
|
||||||
|
|
@ -130,15 +132,15 @@ const synths = stack(
|
||||||
"<eb4 d4 c4 b3>/2".scale(timeCat([3,'C minor'],[1,'C melodic minor'])
|
"<eb4 d4 c4 b3>/2".scale(timeCat([3,'C minor'],[1,'C melodic minor'])
|
||||||
.slow(8)).struct("[~ x]*2")
|
.slow(8)).struct("[~ x]*2")
|
||||||
.layer(
|
.layer(
|
||||||
scaleTranspose(0).early(0),
|
x=>x.scaleTranspose(0).early(0),
|
||||||
scaleTranspose(2).early(1/8),
|
x=>x.scaleTranspose(2).early(1/8),
|
||||||
scaleTranspose(7).early(1/4),
|
x=>x.scaleTranspose(7).early(1/4),
|
||||||
scaleTranspose(8).early(3/8)
|
x=>x.scaleTranspose(8).early(3/8)
|
||||||
).apply(thru).note().apply(keys).mask("<~ x>/16"),
|
).apply(thru).note().apply(keys).mask("<~ x>/16"),
|
||||||
note("<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".apply(thru))
|
note("<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".apply(thru))
|
||||||
.struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2))
|
.struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2))
|
||||||
.s('sawtooth').attack(0.001).decay(0.2).sustain(1).cutoff(500),
|
.s('sawtooth').attack(0.001).decay(0.2).sustain(1).cutoff(500),
|
||||||
"<Cm7 Bb7 Fm7 G7b13>/2".struct("~ [x@0.2 ~]".fast(2)).voicings()
|
"<Cm7 Bb7 Fm7 G7b13>/2".struct("~ [x@0.2 ~]".fast(2)).voicings('lefthand')
|
||||||
.apply(thru).every(2, early(1/8)).note().apply(keys).sustain(0)
|
.apply(thru).every(2, early(1/8)).note().apply(keys).sustain(0)
|
||||||
.delay(.4).delaytime(.12)
|
.delay(.4).delaytime(.12)
|
||||||
.mask("<x@7 ~>/8".early(1/4))
|
.mask("<x@7 ~>/8".early(1/4))
|
||||||
|
|
@ -245,7 +247,7 @@ export const festivalOfFingers = `// licensed with CC BY-NC-SA 4.0 https://creat
|
||||||
// by Felix Roos
|
// by Felix Roos
|
||||||
const chords = "<Cm7 Fm7 G7 F#7>";
|
const chords = "<Cm7 Fm7 G7 F#7>";
|
||||||
stack(
|
stack(
|
||||||
chords.voicings().struct("x(3,8,-1)").velocity(.5).off(1/7,x=>x.transpose(12).velocity(.2)),
|
chords.voicings('lefthand').struct("x(3,8,-1)").velocity(.5).off(1/7,x=>x.transpose(12).velocity(.2)),
|
||||||
chords.rootNotes(2).struct("x(4,8,-2)"),
|
chords.rootNotes(2).struct("x(4,8,-2)"),
|
||||||
chords.rootNotes(4)
|
chords.rootNotes(4)
|
||||||
.scale(cat('C minor','F dorian','G dorian','F# mixolydian'))
|
.scale(cat('C minor','F dorian','G dorian','F# mixolydian'))
|
||||||
|
|
@ -501,7 +503,7 @@ stack(
|
||||||
.gain(.4) // turn down
|
.gain(.4) // turn down
|
||||||
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
|
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
|
||||||
//.hush()
|
//.hush()
|
||||||
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings() // chords
|
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings('lefthand') // chords
|
||||||
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
|
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
|
||||||
.add(perlin.range(0,.5)) // random pitch variation
|
.add(perlin.range(0,.5)) // random pitch variation
|
||||||
.n() // wrap in "n"
|
.n() // wrap in "n"
|
||||||
|
|
@ -531,7 +533,7 @@ samples({
|
||||||
perc: ['perc/002_perc2.wav'],
|
perc: ['perc/002_perc2.wav'],
|
||||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||||
|
|
||||||
"C^7 Am7 Dm7 G7".slow(2).voicings()
|
"C^7 Am7 Dm7 G7".slow(2).voicings('lefthand')
|
||||||
.stack("0@6 [<1 2> <2 0> 1]@2".scale('C5 major'))
|
.stack("0@6 [<1 2> <2 0> 1]@2".scale('C5 major'))
|
||||||
.n().slow(4)
|
.n().slow(4)
|
||||||
.s('0040_FluidR3_GM_sf2_file')
|
.s('0040_FluidR3_GM_sf2_file')
|
||||||
|
|
@ -626,7 +628,7 @@ stack(
|
||||||
s("mt lt ht").struct("x(3,8)").fast(2).gain(.5).room(.5).sometimes(x=>x.speed(".5")),
|
s("mt lt ht").struct("x(3,8)").fast(2).gain(.5).room(.5).sometimes(x=>x.speed(".5")),
|
||||||
s("misc:2").speed(1).delay(.5).delaytime(1/3).gain(.4),
|
s("misc:2").speed(1).delay(.5).delaytime(1/3).gain(.4),
|
||||||
// chords
|
// chords
|
||||||
note("[~ Gm7] ~ [~ Dm7] ~".voicings().superimpose(x=>x.add(.1)))
|
note("[~ Gm7] ~ [~ Dm7] ~".voicings('lefthand').superimpose(x=>x.add(.1)))
|
||||||
.s('sawtooth').gain(.5)
|
.s('sawtooth').gain(.5)
|
||||||
.cutoff(perlin.range(400,3000).slow(8))
|
.cutoff(perlin.range(400,3000).slow(8))
|
||||||
.decay(perlin.range(0.05,.2)).sustain(0)
|
.decay(perlin.range(0.05,.2)).sustain(0)
|
||||||
|
|
@ -650,11 +652,12 @@ export const dinofunk = `// licensed with CC BY-NC-SA 4.0 https://creativecommon
|
||||||
// by Felix Roos
|
// by Felix Roos
|
||||||
samples({bass:'https://cdn.freesound.org/previews/614/614637_2434927-hq.mp3',
|
samples({bass:'https://cdn.freesound.org/previews/614/614637_2434927-hq.mp3',
|
||||||
dino:{b4:'https://cdn.freesound.org/previews/316/316403_5123851-hq.mp3'}})
|
dino:{b4:'https://cdn.freesound.org/previews/316/316403_5123851-hq.mp3'}})
|
||||||
|
setVoicingRange('lefthand', ['c3','a4'])
|
||||||
|
|
||||||
stack(
|
stack(
|
||||||
s('bass').loopAt(8).clip(1),
|
s('bass').loopAt(8).clip(1),
|
||||||
s("bd*2, ~ sd,hh*4"),
|
s("bd*2, ~ sd,hh*4"),
|
||||||
note("Abm7".voicings(['c3','a4']).struct("x(3,8,1)".slow(2))),
|
note("Abm7".voicings('lefthand').struct("x(3,8,1)".slow(2))),
|
||||||
"0 1 2 3".scale('ab4 minor pentatonic')
|
"0 1 2 3".scale('ab4 minor pentatonic')
|
||||||
.superimpose(x=>x.add(.1))
|
.superimpose(x=>x.add(.1))
|
||||||
.sometimes(x=>x.add(12))
|
.sometimes(x=>x.add(12))
|
||||||
|
|
@ -767,7 +770,7 @@ note("c3 eb3 g3 bb3").palindrome()
|
||||||
|
|
||||||
export const csoundDemo = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
|
export const csoundDemo = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
// by Felix Roos
|
// by Felix Roos
|
||||||
await csound\`
|
await loadCsound\`
|
||||||
instr CoolSynth
|
instr CoolSynth
|
||||||
iduration = p3
|
iduration = p3
|
||||||
ifreq = p4
|
ifreq = p4
|
||||||
|
|
@ -806,7 +809,7 @@ export const loungeSponge = `
|
||||||
await loadOrc('github:kunstmusik/csound-live-code/master/livecode.orc')
|
await loadOrc('github:kunstmusik/csound-live-code/master/livecode.orc')
|
||||||
|
|
||||||
stack(
|
stack(
|
||||||
note("<C^7 A7 Dm7 Fm7>/2".voicings())
|
note("<C^7 A7 Dm7 Fm7>/2".voicings('lefthand'))
|
||||||
.cutoff(sine.range(500,2000).round().slow(16))
|
.cutoff(sine.range(500,2000).round().slow(16))
|
||||||
.euclidLegato(3,8).csound('FM1')
|
.euclidLegato(3,8).csound('FM1')
|
||||||
,
|
,
|
||||||
|
|
@ -823,7 +826,7 @@ export const arpoon = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.
|
||||||
// "Arpoon" by Felix Roos
|
// "Arpoon" by Felix Roos
|
||||||
await samples('github:tidalcycles/Dirt-Samples/master')
|
await samples('github:tidalcycles/Dirt-Samples/master')
|
||||||
|
|
||||||
"<<Am7 C^7> C7 F^7 [Fm7 E7b9]>".voicings()
|
"<<Am7 C^7> C7 F^7 [Fm7 E7b9]>".voicings('lefthand')
|
||||||
.arp("[0,3] 2 [1,3] 2".fast(3)).legato(2)
|
.arp("[0,3] 2 [1,3] 2".fast(3)).legato(2)
|
||||||
.add(perlin.range(0,0.2)).sub("<0 -12>/8")
|
.add(perlin.range(0,0.2)).sub("<0 -12>/8")
|
||||||
.note().cutoff(perlin.range(500,4000)).resonance(12)
|
.note().cutoff(perlin.range(500,4000)).resonance(12)
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,5 @@
|
||||||
// Vitest Snapshot v1
|
// Vitest Snapshot v1
|
||||||
|
|
||||||
exports[`runs examples > example "_apply" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/1 | note:C3 ]",
|
|
||||||
"[ 0/1 → 1/1 | note:Eb3 ]",
|
|
||||||
"[ 0/1 → 1/1 | note:G3 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:Eb3 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:G3 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:Bb3 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:G3 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:Bb3 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:D4 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:C3 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:Eb3 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:G3 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "accelerate" example index 0 1`] = `
|
exports[`runs examples > example "accelerate" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ (0/1 → 1/1) ⇝ 2/1 | s:sax accelerate:0 ]",
|
"[ (0/1 → 1/1) ⇝ 2/1 | s:sax accelerate:0 ]",
|
||||||
|
|
@ -60,6 +43,31 @@ exports[`runs examples > example "add" example index 1 1`] = `
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`runs examples > example "addVoicings" example index 0 1`] = `
|
||||||
|
[
|
||||||
|
"[ 0/1 → 1/1 | note:E3 ]",
|
||||||
|
"[ 0/1 → 1/1 | note:A3 ]",
|
||||||
|
"[ 0/1 → 1/1 | note:D4 ]",
|
||||||
|
"[ 0/1 → 1/1 | note:G4 ]",
|
||||||
|
"[ 0/1 → 1/1 | note:B4 ]",
|
||||||
|
"[ 1/1 → 2/1 | note:C#3 ]",
|
||||||
|
"[ 1/1 → 2/1 | note:G3 ]",
|
||||||
|
"[ 1/1 → 2/1 | note:B3 ]",
|
||||||
|
"[ 1/1 → 2/1 | note:E4 ]",
|
||||||
|
"[ 1/1 → 2/1 | note:A4 ]",
|
||||||
|
"[ 2/1 → 3/1 | note:D3 ]",
|
||||||
|
"[ 2/1 → 3/1 | note:G3 ]",
|
||||||
|
"[ 2/1 → 3/1 | note:C4 ]",
|
||||||
|
"[ 2/1 → 3/1 | note:F4 ]",
|
||||||
|
"[ 2/1 → 3/1 | note:A4 ]",
|
||||||
|
"[ 3/1 → 4/1 | note:F3 ]",
|
||||||
|
"[ 3/1 → 4/1 | note:B3 ]",
|
||||||
|
"[ 3/1 → 4/1 | note:E4 ]",
|
||||||
|
"[ 3/1 → 4/1 | note:A4 ]",
|
||||||
|
"[ 3/1 → 4/1 | note:D5 ]",
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "almostAlways" example index 0 1`] = `
|
exports[`runs examples > example "almostAlways" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/8 | s:hh speed:0.5 ]",
|
"[ 0/1 → 1/8 | s:hh speed:0.5 ]",
|
||||||
|
|
@ -232,6 +240,23 @@ exports[`runs examples > example "amp" example index 0 1`] = `
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`runs examples > example "apply" example index 0 1`] = `
|
||||||
|
[
|
||||||
|
"[ 0/1 → 1/1 | note:C3 ]",
|
||||||
|
"[ 0/1 → 1/1 | note:Eb3 ]",
|
||||||
|
"[ 0/1 → 1/1 | note:G3 ]",
|
||||||
|
"[ 1/1 → 2/1 | note:Eb3 ]",
|
||||||
|
"[ 1/1 → 2/1 | note:G3 ]",
|
||||||
|
"[ 1/1 → 2/1 | note:Bb3 ]",
|
||||||
|
"[ 2/1 → 3/1 | note:G3 ]",
|
||||||
|
"[ 2/1 → 3/1 | note:Bb3 ]",
|
||||||
|
"[ 2/1 → 3/1 | note:D4 ]",
|
||||||
|
"[ 3/1 → 4/1 | note:C3 ]",
|
||||||
|
"[ 3/1 → 4/1 | note:Eb3 ]",
|
||||||
|
"[ 3/1 → 4/1 | note:G3 ]",
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "bandf" example index 0 1`] = `
|
exports[`runs examples > example "bandf" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/2 | s:bd bandf:1000 ]",
|
"[ 0/1 → 1/2 | s:bd bandf:1000 ]",
|
||||||
|
|
@ -605,19 +630,21 @@ exports[`runs examples > example "degrade" example index 0 1`] = `
|
||||||
|
|
||||||
exports[`runs examples > example "degrade" example index 1 1`] = `
|
exports[`runs examples > example "degrade" example index 1 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/8 | s:hh ]",
|
"[ 1/8 → 1/4 | s:hh ]",
|
||||||
"[ 3/8 → 1/2 | s:hh ]",
|
"[ 3/4 → 7/8 | s:hh ]",
|
||||||
"[ 1/2 → 5/8 | s:hh ]",
|
"[ 1/1 → 9/8 | s:hh ]",
|
||||||
"[ 5/8 → 3/4 | s:hh ]",
|
"[ 9/8 → 5/4 | s:hh ]",
|
||||||
"[ 5/4 → 11/8 | s:hh ]",
|
"[ 5/4 → 11/8 | s:hh ]",
|
||||||
"[ 3/2 → 13/8 | s:hh ]",
|
"[ 11/8 → 3/2 | s:hh ]",
|
||||||
"[ 13/8 → 7/4 | s:hh ]",
|
"[ 7/4 → 15/8 | s:hh ]",
|
||||||
|
"[ 2/1 → 17/8 | s:hh ]",
|
||||||
"[ 9/4 → 19/8 | s:hh ]",
|
"[ 9/4 → 19/8 | s:hh ]",
|
||||||
"[ 19/8 → 5/2 | s:hh ]",
|
"[ 21/8 → 11/4 | s:hh ]",
|
||||||
"[ 5/2 → 21/8 | s:hh ]",
|
"[ 11/4 → 23/8 | s:hh ]",
|
||||||
"[ 3/1 → 25/8 | s:hh ]",
|
"[ 25/8 → 13/4 | s:hh ]",
|
||||||
"[ 13/4 → 27/8 | s:hh ]",
|
"[ 13/4 → 27/8 | s:hh ]",
|
||||||
"[ 29/8 → 15/4 | s:hh ]",
|
"[ 27/8 → 7/2 | s:hh ]",
|
||||||
|
"[ 15/4 → 31/8 | s:hh ]",
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
@ -656,6 +683,7 @@ exports[`runs examples > example "degradeBy" example index 1 1`] = `
|
||||||
"[ 1/8 → 1/4 | s:hh ]",
|
"[ 1/8 → 1/4 | s:hh ]",
|
||||||
"[ 1/4 → 3/8 | s:hh ]",
|
"[ 1/4 → 3/8 | s:hh ]",
|
||||||
"[ 3/8 → 1/2 | s:hh ]",
|
"[ 3/8 → 1/2 | s:hh ]",
|
||||||
|
"[ 1/2 → 5/8 | s:hh ]",
|
||||||
"[ 5/8 → 3/4 | s:hh ]",
|
"[ 5/8 → 3/4 | s:hh ]",
|
||||||
"[ 3/4 → 7/8 | s:hh ]",
|
"[ 3/4 → 7/8 | s:hh ]",
|
||||||
"[ 7/8 → 1/1 | s:hh ]",
|
"[ 7/8 → 1/1 | s:hh ]",
|
||||||
|
|
@ -663,20 +691,22 @@ exports[`runs examples > example "degradeBy" example index 1 1`] = `
|
||||||
"[ 9/8 → 5/4 | s:hh ]",
|
"[ 9/8 → 5/4 | s:hh ]",
|
||||||
"[ 5/4 → 11/8 | s:hh ]",
|
"[ 5/4 → 11/8 | s:hh ]",
|
||||||
"[ 11/8 → 3/2 | s:hh ]",
|
"[ 11/8 → 3/2 | s:hh ]",
|
||||||
|
"[ 3/2 → 13/8 | s:hh ]",
|
||||||
"[ 13/8 → 7/4 | s:hh ]",
|
"[ 13/8 → 7/4 | s:hh ]",
|
||||||
"[ 7/4 → 15/8 | s:hh ]",
|
"[ 7/4 → 15/8 | s:hh ]",
|
||||||
"[ 15/8 → 2/1 | s:hh ]",
|
|
||||||
"[ 2/1 → 17/8 | s:hh ]",
|
"[ 2/1 → 17/8 | s:hh ]",
|
||||||
|
"[ 17/8 → 9/4 | s:hh ]",
|
||||||
"[ 9/4 → 19/8 | s:hh ]",
|
"[ 9/4 → 19/8 | s:hh ]",
|
||||||
"[ 19/8 → 5/2 | s:hh ]",
|
|
||||||
"[ 5/2 → 21/8 | s:hh ]",
|
|
||||||
"[ 21/8 → 11/4 | s:hh ]",
|
"[ 21/8 → 11/4 | s:hh ]",
|
||||||
"[ 11/4 → 23/8 | s:hh ]",
|
"[ 11/4 → 23/8 | s:hh ]",
|
||||||
"[ 3/1 → 25/8 | s:hh ]",
|
"[ 23/8 → 3/1 | s:hh ]",
|
||||||
"[ 25/8 → 13/4 | s:hh ]",
|
"[ 25/8 → 13/4 | s:hh ]",
|
||||||
"[ 13/4 → 27/8 | s:hh ]",
|
"[ 13/4 → 27/8 | s:hh ]",
|
||||||
|
"[ 27/8 → 7/2 | s:hh ]",
|
||||||
|
"[ 7/2 → 29/8 | s:hh ]",
|
||||||
"[ 29/8 → 15/4 | s:hh ]",
|
"[ 29/8 → 15/4 | s:hh ]",
|
||||||
"[ 15/4 → 31/8 | s:hh ]",
|
"[ 15/4 → 31/8 | s:hh ]",
|
||||||
|
"[ 31/8 → 4/1 | s:hh ]",
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
@ -2352,6 +2382,15 @@ exports[`runs examples > example "room" example index 0 1`] = `
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`runs examples > example "rootNotes" example index 0 1`] = `
|
||||||
|
[
|
||||||
|
"[ 0/1 → 1/1 | note:C2 ]",
|
||||||
|
"[ 1/1 → 2/1 | note:A2 ]",
|
||||||
|
"[ 2/1 → 3/1 | note:D2 ]",
|
||||||
|
"[ 3/1 → 4/1 | note:G2 ]",
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "round" example index 0 1`] = `
|
exports[`runs examples > example "round" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/3 | note:D3 ]",
|
"[ 0/1 → 1/3 | note:D3 ]",
|
||||||
|
|
@ -2645,12 +2684,12 @@ exports[`runs examples > example "slowcat" example index 0 1`] = `
|
||||||
|
|
||||||
exports[`runs examples > example "someCycles" example index 0 1`] = `
|
exports[`runs examples > example "someCycles" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 1/1 → 9/8 | s:hh ]",
|
|
||||||
"[ 11/8 → 3/2 | s:hh ]",
|
|
||||||
"[ 7/4 → 15/8 | s:hh ]",
|
|
||||||
"[ 0/1 → 1/8 | s:hh speed:0.5 ]",
|
"[ 0/1 → 1/8 | s:hh speed:0.5 ]",
|
||||||
"[ 3/8 → 1/2 | s:hh speed:0.5 ]",
|
"[ 3/8 → 1/2 | s:hh speed:0.5 ]",
|
||||||
"[ 3/4 → 7/8 | s:hh speed:0.5 ]",
|
"[ 3/4 → 7/8 | s:hh speed:0.5 ]",
|
||||||
|
"[ 1/1 → 9/8 | s:hh ]",
|
||||||
|
"[ 11/8 → 3/2 | s:hh ]",
|
||||||
|
"[ 7/4 → 15/8 | s:hh ]",
|
||||||
"[ 2/1 → 17/8 | s:hh speed:0.5 ]",
|
"[ 2/1 → 17/8 | s:hh speed:0.5 ]",
|
||||||
"[ 19/8 → 5/2 | s:hh speed:0.5 ]",
|
"[ 19/8 → 5/2 | s:hh speed:0.5 ]",
|
||||||
"[ 11/4 → 23/8 | s:hh speed:0.5 ]",
|
"[ 11/4 → 23/8 | s:hh speed:0.5 ]",
|
||||||
|
|
@ -2680,19 +2719,19 @@ exports[`runs examples > example "someCyclesBy" example index 0 1`] = `
|
||||||
exports[`runs examples > example "sometimes" example index 0 1`] = `
|
exports[`runs examples > example "sometimes" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/4 | s:hh ]",
|
"[ 0/1 → 1/4 | s:hh ]",
|
||||||
"[ 1/1 → 5/4 | s:hh ]",
|
|
||||||
"[ 7/4 → 2/1 | s:hh ]",
|
|
||||||
"[ 2/1 → 9/4 | s:hh ]",
|
|
||||||
"[ 9/4 → 5/2 | s:hh ]",
|
|
||||||
"[ 7/2 → 15/4 | s:hh ]",
|
|
||||||
"[ 15/4 → 4/1 | s:hh ]",
|
|
||||||
"[ 1/4 → 1/2 | s:hh speed:0.5 ]",
|
"[ 1/4 → 1/2 | s:hh speed:0.5 ]",
|
||||||
"[ 1/2 → 3/4 | s:hh speed:0.5 ]",
|
"[ 1/2 → 3/4 | s:hh speed:0.5 ]",
|
||||||
"[ 3/4 → 1/1 | s:hh speed:0.5 ]",
|
"[ 3/4 → 1/1 | s:hh speed:0.5 ]",
|
||||||
|
"[ 1/1 → 5/4 | s:hh ]",
|
||||||
|
"[ 7/4 → 2/1 | s:hh ]",
|
||||||
"[ 5/4 → 3/2 | s:hh speed:0.5 ]",
|
"[ 5/4 → 3/2 | s:hh speed:0.5 ]",
|
||||||
"[ 3/2 → 7/4 | s:hh speed:0.5 ]",
|
"[ 3/2 → 7/4 | s:hh speed:0.5 ]",
|
||||||
|
"[ 2/1 → 9/4 | s:hh ]",
|
||||||
|
"[ 9/4 → 5/2 | s:hh ]",
|
||||||
"[ 5/2 → 11/4 | s:hh speed:0.5 ]",
|
"[ 5/2 → 11/4 | s:hh speed:0.5 ]",
|
||||||
"[ 11/4 → 3/1 | s:hh speed:0.5 ]",
|
"[ 11/4 → 3/1 | s:hh speed:0.5 ]",
|
||||||
|
"[ 7/2 → 15/4 | s:hh ]",
|
||||||
|
"[ 15/4 → 4/1 | s:hh ]",
|
||||||
"[ 3/1 → 13/4 | s:hh speed:0.5 ]",
|
"[ 3/1 → 13/4 | s:hh speed:0.5 ]",
|
||||||
"[ 13/4 → 7/2 | s:hh speed:0.5 ]",
|
"[ 13/4 → 7/2 | s:hh speed:0.5 ]",
|
||||||
]
|
]
|
||||||
|
|
@ -3080,68 +3119,20 @@ exports[`runs examples > example "velocity" example index 0 1`] = `
|
||||||
exports[`runs examples > example "voicings" example index 0 1`] = `
|
exports[`runs examples > example "voicings" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/1 | note:B3 ]",
|
"[ 0/1 → 1/1 | note:B3 ]",
|
||||||
"[ 0/1 → 1/1 | note:B3 ]",
|
|
||||||
"[ 0/1 → 1/1 | note:B3 ]",
|
|
||||||
"[ 0/1 → 1/1 | note:B3 ]",
|
|
||||||
"[ 0/1 → 1/1 | note:D4 ]",
|
|
||||||
"[ 0/1 → 1/1 | note:D4 ]",
|
|
||||||
"[ 0/1 → 1/1 | note:D4 ]",
|
|
||||||
"[ 0/1 → 1/1 | note:D4 ]",
|
"[ 0/1 → 1/1 | note:D4 ]",
|
||||||
"[ 0/1 → 1/1 | note:E4 ]",
|
"[ 0/1 → 1/1 | note:E4 ]",
|
||||||
"[ 0/1 → 1/1 | note:E4 ]",
|
|
||||||
"[ 0/1 → 1/1 | note:E4 ]",
|
|
||||||
"[ 0/1 → 1/1 | note:E4 ]",
|
|
||||||
"[ 0/1 → 1/1 | note:G4 ]",
|
|
||||||
"[ 0/1 → 1/1 | note:G4 ]",
|
|
||||||
"[ 0/1 → 1/1 | note:G4 ]",
|
|
||||||
"[ 0/1 → 1/1 | note:G4 ]",
|
"[ 0/1 → 1/1 | note:G4 ]",
|
||||||
"[ 1/1 → 2/1 | note:G3 ]",
|
"[ 1/1 → 2/1 | note:G3 ]",
|
||||||
"[ 1/1 → 2/1 | note:G3 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:G3 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:G3 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:B3 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:B3 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:B3 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:B3 ]",
|
"[ 1/1 → 2/1 | note:B3 ]",
|
||||||
"[ 1/1 → 2/1 | note:C#4 ]",
|
"[ 1/1 → 2/1 | note:C#4 ]",
|
||||||
"[ 1/1 → 2/1 | note:C#4 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:C#4 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:C#4 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:F#4 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:F#4 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:F#4 ]",
|
|
||||||
"[ 1/1 → 2/1 | note:F#4 ]",
|
"[ 1/1 → 2/1 | note:F#4 ]",
|
||||||
"[ 2/1 → 3/1 | note:F3 ]",
|
"[ 2/1 → 3/1 | note:F3 ]",
|
||||||
"[ 2/1 → 3/1 | note:F3 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:F3 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:F3 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:A3 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:A3 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:A3 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:A3 ]",
|
"[ 2/1 → 3/1 | note:A3 ]",
|
||||||
"[ 2/1 → 3/1 | note:C4 ]",
|
"[ 2/1 → 3/1 | note:C4 ]",
|
||||||
"[ 2/1 → 3/1 | note:C4 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:C4 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:C4 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:E4 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:E4 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:E4 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:E4 ]",
|
"[ 2/1 → 3/1 | note:E4 ]",
|
||||||
"[ 3/1 → 4/1 | note:F3 ]",
|
"[ 3/1 → 4/1 | note:F3 ]",
|
||||||
"[ 3/1 → 4/1 | note:F3 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:F3 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:F3 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:A3 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:A3 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:A3 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:A3 ]",
|
"[ 3/1 → 4/1 | note:A3 ]",
|
||||||
"[ 3/1 → 4/1 | note:B3 ]",
|
"[ 3/1 → 4/1 | note:B3 ]",
|
||||||
"[ 3/1 → 4/1 | note:B3 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:B3 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:B3 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:E4 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:E4 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:E4 ]",
|
|
||||||
"[ 3/1 → 4/1 | note:E4 ]",
|
"[ 3/1 → 4/1 | note:E4 ]",
|
||||||
"[ 0/1 → 1/1 | note:C3 ]",
|
"[ 0/1 → 1/1 | note:C3 ]",
|
||||||
"[ 1/1 → 2/1 | note:A2 ]",
|
"[ 1/1 → 2/1 | note:A2 ]",
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ s("bd,[~ <sd!3 sd(3,4,2)>],hh(3,4)") // drums
|
||||||
.s('sawtooth') // waveform
|
.s('sawtooth') // waveform
|
||||||
.gain(.4) // turn down
|
.gain(.4) // turn down
|
||||||
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
|
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
|
||||||
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings() // chords
|
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings('lefthand') // chords
|
||||||
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
|
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
|
||||||
.add(perlin.range(0,.5)) // random pitch variation
|
.add(perlin.range(0,.5)) // random pitch variation
|
||||||
.n() // wrap in "n"
|
.n() // wrap in "n"
|
||||||
|
|
@ -755,9 +755,7 @@ Transposes notes inside the scale by the number of steps:
|
||||||
|
|
||||||
Turns chord symbols into voicings, using the smoothest voice leading possible:
|
Turns chord symbols into voicings, using the smoothest voice leading possible:
|
||||||
|
|
||||||
<MiniRepl tune={`stack("<C^7 A7 Dm7 G7>".voicings(), "<C3 A2 D3 G2>").note()`} />
|
<MiniRepl tune={`stack("<C^7 A7 Dm7 G7>".voicings('lefthand'), "<C3 A2 D3 G2>").note()`} />
|
||||||
|
|
||||||
<!-- TODO: use voicing collection as first param + patternify. -->
|
|
||||||
|
|
||||||
### rootNotes(octave = 2)
|
### rootNotes(octave = 2)
|
||||||
|
|
||||||
|
|
@ -769,7 +767,7 @@ Together with layer, struct and voicings, this can be used to create a basic bac
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"<C^7 A7b13 Dm7 G7>".layer(
|
tune={`"<C^7 A7b13 Dm7 G7>".layer(
|
||||||
x => x.voicings(['d3','g4']).struct("~ x").note(),
|
x => x.voicings('lefthand').struct("~ x").note(),
|
||||||
x => x.rootNotes(2).note().s('sawtooth').cutoff(800)
|
x => x.rootNotes(2).note().s('sawtooth').cutoff(800)
|
||||||
)`}
|
)`}
|
||||||
/>
|
/>
|
||||||
|
|
@ -790,7 +788,7 @@ Either connect a midi device or use the IAC Driver (Mac) or Midi Through Port (L
|
||||||
If no outputName is given, it uses the first midi output it finds.
|
If no outputName is given, it uses the first midi output it finds.
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`stack("<C^7 A7 Dm7 G7>".voicings(), "<C3 A2 D3 G2>")
|
tune={`stack("<C^7 A7 Dm7 G7>".voicings('lefthand'), "<C3 A2 D3 G2>")
|
||||||
.midi()`}
|
.midi()`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue