Compare commits

..

3 Commits

Author SHA1 Message Date
Felix Roos cd1c4632d7 build to single file 2022-11-23 14:00:31 +01:00
Felix Roos f1a850e133 forgot tonal pkg import 2022-11-22 21:43:30 +01:00
Felix Roos 53b61b8d85 add workshop seed project 2022-11-22 21:33:47 +01:00
96 changed files with 12730 additions and 6193 deletions
-14
View File
@@ -1,14 +0,0 @@
krill-parser.js
krill.pegjs
.eslintrc.json
server.js
tidal-sniffer.js
*.jsx
tunejs.js
out/**
postcss.config.js
postcss.config.cjs
tailwind.config.js
vite.config.js
/**/dist/**/*
!**/*.mjs
-16
View File
@@ -1,16 +0,0 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": ["eslint:recommended"],
"overrides": [],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [],
"rules": {
"no-unused-vars": ["warn", { "destructuredArrayIgnorePattern": ".", "ignoreRestSiblings": false }]
}
}
+5360 -2543
View File
File diff suppressed because it is too large Load Diff
+2 -4
View File
@@ -5,7 +5,7 @@
"description": "Port of tidalcycles to javascript",
"scripts": {
"pretest": "cd tutorial && npm run jsdoc-json",
"test": "npm run lint && vitest run --version",
"test": "vitest run --version",
"test-ui": "vitest --ui",
"test-coverage": "vitest --coverage",
"bootstrap": "lerna bootstrap",
@@ -17,8 +17,7 @@
"preview": "npx serve ./out",
"deploy": "NODE_DEBUG=gh-pages gh-pages -d out",
"jsdoc": "jsdoc packages/ -c jsdoc.config.json",
"jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc.config.json",
"lint": "npx eslint . --ext mjs,js --quiet"
"jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc.config.json"
},
"workspaces": [
"packages/*"
@@ -43,7 +42,6 @@
"devDependencies": {
"@vitest/ui": "^0.21.1",
"c8": "^7.12.0",
"eslint": "^8.28.0",
"events": "^3.3.0",
"gh-pages": "^4.0.0",
"jsdoc": "^3.6.10",
+3 -4
View File
@@ -4,8 +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/>.
*/
import pattern from './pattern.mjs';
const { Pattern, sequence } = pattern;
import { Pattern, sequence } from './pattern.mjs';
const controls = {};
const generic_params = [
@@ -401,7 +400,7 @@ const generic_params = [
* @example
* freq("220 110 440 110").s("superzow").osc()
* @example
* freq("110".mul.out(".5 1.5 .6 [2 3]")).s("superzow").osc()
* freq("110".mulOut(".5 1.5 .6 [2 3]")).s("superzow").osc()
*
*/
['f', 'freq', ''],
@@ -788,6 +787,6 @@ controls.createParam = (name) => {
};
controls.createParams = (...names) =>
names.reduce((acc, name) => Object.assign(acc, { [name]: controls.createParam(name) }), {});
names.reduce((acc, name) => Object.assign(acc, { [name]: createParam(name) }), {});
export default controls;
+1 -5
View File
@@ -4,11 +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/>.
*/
import pattern from './pattern.mjs';
const Pattern = pattern.Pattern;
import { getTime } from './time.mjs';
import { State } from './state.mjs';
import { TimeSpan } from './timespan.mjs';
import { Pattern, getTime } from './index.mjs';
export const getDrawContext = (id = 'test-canvas') => {
let canvas = document.querySelector('#' + id);
+1 -2
View File
@@ -4,8 +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/>.
*/
import pattern from './pattern.mjs';
const { Pattern, timeCat } = pattern;
import { Pattern, timeCat } from './pattern.mjs';
import bjork from 'bjork';
import { rotate } from './util.mjs';
import Fraction from './fraction.mjs';
+1 -2
View File
@@ -4,8 +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/>.
*/
import pattern from './pattern.mjs';
const { isPattern, Pattern } = pattern;
import { isPattern, Pattern } from './index.mjs';
let scoped = false;
export const evalScope = async (...args) => {
-65
View File
@@ -1,65 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Buildless Vanilla Strudel REPL</title>
</head>
<body style="margin: 0; background: #222">
<div style="display: grid; height: 100vh">
<textarea
id="text"
style="font-size: 2em; border: 0; color: white; background: transparent; outline: none; padding: 20px"
spellcheck="false"
>
// LOADING</textarea
>
</div>
<button
id="start"
style="
position: absolute;
border-radius: 10px;
top: 20px;
right: 20px;
padding: 20px;
border: 2px solid white;
background: transparent;
color: white;
cursor: pointer;
"
>
evaluate
</button>
<div id="output"></div>
<script type="module">
import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel.cycles/core@0.4.1';
import { transpiler } from 'https://cdn.skypack.dev/@strudel.cycles/transpiler@0.4.1';
const modules = [
import('https://cdn.skypack.dev/@strudel.cycles/core@0.4.1'),
import('https://cdn.skypack.dev/@strudel.cycles/mini@0.4.1'),
];
const input = document.getElementById('text');
Promise.all(modules).then(() => {
input.innerHTML = `note("<c3 [d3 e3]>").cutoff(1000)`;
document.getElementById('start').addEventListener('click', () => {
evaluate(input.value);
});
});
evalScope(controls, ...modules);
const { evaluate } = repl({
defaultOutput: (hap, deadline, duration) => {
console.log(deadline, duration, hap.value);
},
getTime: () => Date.now() / 1000,
transpiler,
beforeEval: (code) => console.log('evaluate', code),
afterEval: (code) => {},
});
</script>
</body>
</html>
+20 -29
View File
@@ -4,36 +4,27 @@ 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/>.
*/
import Fraction from './fraction.mjs';
import drawLine from './drawLine.mjs';
import gist from './gist.js';
import { logger } from './logger.mjs';
// Fraction: Fraction, drawLine: drawline, gist: gist, logger: logger,
import controls from './controls.mjs';
import pattern from './pattern.mjs';
import * as draw from './draw.mjs';
import * as euclid from './euclid.mjs';
import * as evaluate from './evaluate.mjs';
import * as hap from './hap.mjs';
import * as repl from './repl.mjs';
import * as pianoroll from './pianoroll.mjs';
import * as signal from './signal.mjs';
import * as speak from './speak.mjs';
import * as state from './state.mjs';
import * as time from './time.mjs';
import * as timespan from './timespan.mjs';
import * as ui from './ui.mjs';
import * as util from './util.mjs';
const core = { drawLine, gist, logger, Fraction, ...controls, ...pattern, ...draw, ...euclid,
...evaluate, ...hap, ...repl, ...pianoroll, ...signal, ...speak,
...state, ...time, ...timespan, ...ui, ...util
};
export default core;
export * from './euclid.mjs';
import Fraction from './fraction.mjs';
import { logger } from './logger.mjs';
export { Fraction, controls };
export * from './hap.mjs';
export * from './pattern.mjs';
export * from './signal.mjs';
export * from './state.mjs';
export * from './timespan.mjs';
export * from './util.mjs';
export * from './speak.mjs';
export * from './evaluate.mjs';
export * from './repl.mjs';
export * from './logger.mjs';
export * from './time.mjs';
export * from './draw.mjs';
export * from './pianoroll.mjs';
export * from './ui.mjs';
export { default as drawLine } from './drawLine.mjs';
export { default as gist } from './gist.js';
// below won't work with runtime.mjs (json import fails)
/* import * as p from './package.json';
export const version = p.version; */
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/core",
"version": "0.4.1",
"version": "0.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/core",
"version": "0.4.1",
"version": "0.4.0",
"description": "Port of Tidal Cycles to JavaScript",
"main": "index.mjs",
"type": "module",
+150 -220
View File
@@ -14,18 +14,14 @@ import { compose, removeUndefineds, flatten, id, listRange, curry, mod, numeralA
import drawLine from './drawLine.mjs';
import { logger } from './logger.mjs';
// monster object collecting everything to be exported
const pattern = {};
let stringParser;
// parser is expected to turn a string into a pattern
// if set, the reify function will parse all strings with it
// intended to use with mini to automatically interpret all strings as mini notation
pattern['setStringParser'] = (parser) => (stringParser = parser);
export const setStringParser = (parser) => (stringParser = parser);
/** @class Class representing a pattern. */
class Pattern {
export class Pattern {
_Pattern = true; // this property is used to detect if a pattern that fails instanceof Pattern is an instance of another Pattern
/**
* Create a pattern. As an end user, you will most likely not create a Pattern directly.
@@ -36,9 +32,10 @@ class Pattern {
this.query = query;
}
//////////////////////////////////////////////////////////////////////
// Haskell-style functor, applicative and monadic operations
/**
* Returns a new pattern, with the function applied to the value of
* each hap. It has the alias {@link Pattern#fmap}.
@@ -167,6 +164,7 @@ class Pattern {
return new Pattern(query);
}
bindWhole(choose_whole, func) {
const pat_val = this;
const query = function (state) {
@@ -207,7 +205,7 @@ class Pattern {
}
outerBind(func) {
return this.bindWhole((a) => a, func);
return this.bindWhole((a, _) => a, func);
}
outerJoin() {
@@ -315,7 +313,7 @@ class Pattern {
//////////////////////////////////////////////////////////////////////
// Utility methods mainly for internal use
/**
* Query haps inside the given time span.
*
@@ -536,7 +534,7 @@ class Pattern {
// removes continuous haps that don't have a 'whole' timespan
return this.filterHaps((hap) => hap.whole);
}
/**
* Queries the pattern for the first cycle, returning Haps. Mainly of use when
* debugging a pattern.
@@ -942,8 +940,8 @@ class Pattern {
//binary_pat = sequence(binary_pat)
const true_pat = binary_pat.filterValues(id);
const false_pat = binary_pat.filterValues((val) => !val);
const with_pat = true_pat.fmap(() => (y) => y).appRight(func(this));
const without_pat = false_pat.fmap(() => (y) => y).appRight(this);
const with_pat = true_pat.fmap((_) => (y) => y).appRight(func(this));
const without_pat = false_pat.fmap((_) => (y) => y).appRight(this);
return stack(with_pat, without_pat);
}
@@ -1009,7 +1007,7 @@ class Pattern {
every(n, func) {
return this.firstOf(n, func);
}
/**
* Returns a new pattern where every other cycle is played once, twice as
* fast, and offset in time by one quarter of a cycle. Creates a kind of
@@ -1029,7 +1027,6 @@ class Pattern {
* @example
* note("c3 d3 e3 g3").rev()
*/
rev() {
const pat = this;
const query = function (state) {
@@ -1051,9 +1048,10 @@ class Pattern {
}
palindrome() {
return this.every(2, pattern.rev);
return this.every(2, rev);
}
juxBy(by, func) {
by /= 2;
const elem_or = function (dict, key, dflt) {
@@ -1186,12 +1184,8 @@ class Pattern {
* note("0 1 2 3".scale('A minor')).iter(4)
*/
_iter(times, back = false) {
times = Fraction(times);
return slowcat(
...listRange(0, times.sub(1)).map((i) =>
back ? this.late(Fraction(i).div(times)) : this.early(Fraction(i).div(times)),
),
);
times = Fraction(times)
return slowcat(...listRange(0, times.sub(1)).map((i) => (back ? this.late(Fraction(i).div(times)) : this.early(Fraction(i).div(times)))));
}
/**
@@ -1237,10 +1231,10 @@ class Pattern {
on = Boolean(parseInt(on));
return on ? silence : this;
}
//////////////////////////////////////////////////////////////////////
// Control-related methods, which manipulate patterns of objects
/**
* Cuts each sample into the given number of parts, allowing you to explore a technique known as 'granular synthesis'.
* It turns a pattern of samples into a pattern of parts of samples.
@@ -1288,11 +1282,17 @@ class Pattern {
//////////////////////////////////////////////////////////////////////
// Context methods - ones that deal with metadata
_color(color) {
return this.withContext((context) => ({ ...context, color }));
}
log() {
return this.withHap((e) => {
return e.setContext({ ...e.context, logs: (e.context?.logs || []).concat([e.show()]) });
});
}
/**
*
* Sets the velocity from 0 to 1. Is multiplied together with gain.
@@ -1363,53 +1363,9 @@ class Pattern {
_legato(value) {
return this.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(span.end.sub(span.begin).mul(value))));
}
}
//////////////////////////////////////////////////////////////////////
// functions relating to chords/patterns of lists
// returns Array<Hap[]> where each list of haps satisfies eq
function groupHapsBy(eq, haps) {
let groups = [];
haps.forEach((hap) => {
const match = groups.findIndex(([other]) => eq(hap, other));
if (match === -1) {
groups.push([hap]);
} else {
groups[match].push(hap);
}
});
return groups;
}
pattern['Pattern'] = Pattern;
// congruent haps = haps with equal spans
const congruent = (a, b) => a.spanEquals(b);
// Pattern<Hap<T>> -> Pattern<Hap<T[]>>
// returned pattern contains arrays of congruent haps
Pattern.prototype.collect = function () {
return this.withHaps((haps) =>
groupHapsBy(congruent, haps).map((_haps) => new Hap(_haps[0].whole, _haps[0].part, _haps, {})),
);
};
// applies func to each array of congruent haps
Pattern.prototype.arpWith = function (func) {
return this.collect()
.fmap((v) => reify(func(v)))
.squeezeJoin()
.withHap((h) => new Hap(h.whole, h.part, h.value.value, h.combineContext(h.value)));
};
// applies pattern of indices to each array of congruent haps
Pattern.prototype.arp = function (pat) {
return this.arpWith((haps) => pat.fmap((i) => haps[i % haps.length]));
};
//////////////////////////////////////////////////////////////////////
// compose matrix functions
// TODO - adopt value.mjs fully..
function _composeOp(a, b, func) {
function _nonFunctionObject(x) {
@@ -1432,7 +1388,7 @@ function _composeOp(a, b, func) {
// pattern composers
const composers = {
set: [(a, b) => b],
keep: [(a) => a],
keep: [(a, b) => a],
keepif: [(a, b) => (b ? a : undefined)],
// numerical functions
@@ -1507,48 +1463,53 @@ function _composeOp(a, b, func) {
// generate methods to do what and how
for (const [what, [op, preprocess]] of Object.entries(composers)) {
Object.defineProperty(Pattern.prototype, what, {
// a getter that returns a function, so 'pat' can be
// accessed by closures that are methods of that function..
get: function() {
const pat = this;
// wrap the 'in' function as default behaviour
const wrapper = (...other) => pat[what]['in'](...other);
// add methods to that function for each behaviour
for (const how of hows) {
wrapper[how.toLowerCase()] = function (...other) {
var howpat = pat;
other = sequence(other);
if (preprocess) {
howpat = preprocess(howpat);
other = preprocess(other);
}
var result;
// hack to remove undefs when doing 'keepif'
if (what === 'keepif') {
// avoid union, as we want to throw away the value of 'b' completely
result = howpat['_op' + how](other, (a) => (b) => op(a, b));
result = result.removeUndefineds();
} else {
result = howpat['_op' + how](other, (a) => (b) => _composeOp(a, b, op));
}
return result;
}
}
wrapper.squeezein = wrapper.squeeze
return wrapper;
}
});
// Default op to 'set', e.g. pat.squeeze(pat2) = pat.set.squeeze(pat2)
for (const how of hows) {
Pattern.prototype[how.toLowerCase()] = function (...args) { return this.set[how.toLowerCase()](args) };
curry_toplevel(how.toLowerCase(), 2);
Pattern.prototype[what + how] = function (...other) {
var pat = this;
other = sequence(other);
if (preprocess) {
pat = preprocess(pat);
other = preprocess(other);
}
var result;
// hack to remove undefs when doing 'keepif'
if (what === 'keepif') {
// avoid union, as we want to throw away the value of 'b' completely
result = pat['_op' + how](other, (a) => (b) => op(a, b));
result = result.removeUndefineds();
} else {
result = pat['_op' + how](other, (a) => (b) => _composeOp(a, b, op));
}
return result;
};
if (how === 'Squeeze') {
// support 'squeezeIn' longhand
Pattern.prototype[what + 'SqueezeIn'] = Pattern.prototype[what + how];
}
if (how === 'In') {
// set 'in' to default, but with magic properties to pick a different 'how'
Object.defineProperty(Pattern.prototype, what, {
// a getter that returns a function, so 'pat' can be
// accessed by closures that are methods of that function..
get: function() {
const pat = this;
// wrap the 'in' function as default behaviour
const wrapper = (...other) => pat[what + "In"](...other);
// add methods to that function to pick alternative behaviours
for (const wraphow of hows) {
wrapper[wraphow.toLowerCase()] = (...other) => pat[what + wraphow](...other);
}
return wrapper;
}
});
} else {
// default what to 'set', e.g. squeeze = setSqueeze
if (what === 'set') {
Pattern.prototype[how.toLowerCase()] = Pattern.prototype[what + how];
}
}
}
curry_toplevel(what, 2);
}
// binary composers
@@ -1563,22 +1524,14 @@ function _composeOp(a, b, func) {
* .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~")
* .slow(4)
*/
Pattern.prototype.struct = function(...args) { return this.keepif.out(...args) }
curry_toplevel('struct', 2);
Pattern.prototype.structAll = function(...args) { return this.keep.out(...args) }
curry_toplevel('structAll', 2);
Pattern.prototype.mask = function(...args) { return this.keepif.in(...args) }
curry_toplevel('mask', 2);
Pattern.prototype.maskAll = function(...args) { return this.keep.in(...args) }
curry_toplevel('maskAll', 2);
Pattern.prototype.reset = function(...args) { return this.keepif.trig(...args) }
curry_toplevel('reset', 2);
Pattern.prototype.resetAll = function(...args) { return this.keep.trig(...args) }
curry_toplevel('resetAll', 2);
Pattern.prototype.restart = function(...args) { return this.keepif.trigzero(...args) }
curry_toplevel('restart', 2);
Pattern.prototype.restartAll = function(...args) { return this.keep.trigzero(...args) }
curry_toplevel('restartAll', 2);
Pattern.prototype.struct = Pattern.prototype.keepifOut;
Pattern.prototype.structAll = Pattern.prototype.keepOut;
Pattern.prototype.mask = Pattern.prototype.keepifIn;
Pattern.prototype.maskAll = Pattern.prototype.keepIn;
Pattern.prototype.reset = Pattern.prototype.keepifTrig;
Pattern.prototype.resetAll = Pattern.prototype.keepTrig;
Pattern.prototype.restart = Pattern.prototype.keepifTrigzero;
Pattern.prototype.restartAll = Pattern.prototype.keepTrigzero;
})();
// methods of Pattern that get callable factories
@@ -1603,6 +1556,10 @@ Pattern.prototype.patternified = [
'velocity',
];
// aliases
export const polyrhythm = stack;
export const pr = stack;
// methods that create patterns, which are added to patternified Pattern methods
Pattern.prototype.factories = {
pure,
@@ -1620,39 +1577,10 @@ Pattern.prototype.factories = {
};
// the magic happens in Pattern constructor. Keeping this in prototype enables adding methods from the outside (e.g. see tonal.ts)
function curry_toplevel(name, arity=1) {
//if (name in pattern) {
// throw(`${name} already exists`);
//}
if (arity == 1) {
pattern[name] = (pat) => pat[name]();
}
else if (arity == 2) {
pattern[name] = curry((a, pat) => pat[name](a));
}
else if (arity == 3) {
pattern[name] = curry((a, b, pat) => pat[name](a, b));
}
else if (arity == 4) {
pattern[name] = curry((a, b, c, pat) => pat[name](a, b, c));
}
}
for (const x of [[1, ['inv', 'invert', 'rev']],
[2, ['chop','chunk','chunkBack', 'mask','struct','superimpose'].concat(Pattern.prototype.patternified)],
[3, ['every','juxBy','off','range','rangex','range2','when']],
[4, ['echo']]
]) {
for (const name of x[1]) {
curry_toplevel(name, x[0]);
}
}
// Elemental patterns
// Nothing
const silence = new Pattern(() => []);
pattern['silence'] = silence;
export const silence = new Pattern((_) => []);
/** A discrete value that repeats once per cycle.
*
@@ -1660,32 +1588,27 @@ pattern['silence'] = silence;
* @example
* pure('e4') // "e4"
*/
function pure(value) {
export function pure(value) {
function query(state) {
return state.span.spanCycles.map((subspan) => new Hap(Fraction(subspan.begin).wholeCycle(), subspan, value));
}
return new Pattern(query);
}
pattern['pure'] = pure;
function isPattern(thing) {
export function isPattern(thing) {
// thing?.constructor?.name !== 'Pattern' // <- this will fail when code is mangled
const is = thing instanceof Pattern || thing?._Pattern;
// TODO: find out how to check wrong core dependency. below will never work !thing === 'undefined'
// wrapping it in (..) will result other checks to log that warning (e.g. isPattern('kalimba'))
/* if (!thing instanceof Pattern) {
if (!thing instanceof Pattern) {
console.warn(
`Found Pattern that fails "instanceof Pattern" check.
This may happen if you are using multiple versions of @strudel.cycles/core.
Please check by running "npm ls @strudel.cycles/core".`,
);
console.log(thing);
} */
}
return is;
}
pattern['isPattern'] = isPattern;
function reify(thing) {
export function reify(thing) {
// Turns something into a pattern, unless it's already a pattern
if (isPattern(thing)) {
return thing;
@@ -1695,7 +1618,6 @@ function reify(thing) {
}
return pure(thing);
}
pattern['reify'] = reify;
/** The given items are played at the same time at the same length.
*
@@ -1703,20 +1625,12 @@ pattern['reify'] = reify;
* @example
* stack(g3, b3, [e4, d4]).note() // "g3,b3,[e4,d4]".note()
*/
function stack(...pats) {
export function stack(...pats) {
// 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);
}
pattern['stack'] = stack;
function polyrhythm(...pats) {
return stack(...pats);
}
function pr(...pats) {
return stack(...pats);
}
pattern['pr'] = pattern['polyrhythm'] = pattern['stack'];
/** Concatenation: combines a list of patterns, switching between them successively, one per cycle:
*
@@ -1727,7 +1641,7 @@ pattern['pr'] = pattern['polyrhythm'] = pattern['stack'];
* slowcat(e5, b4, [d5, c5])
*
*/
function slowcat(...pats) {
export function slowcat(...pats) {
// Array test here is to avoid infinite recursions..
pats = pats.map((pat) => (Array.isArray(pat) ? sequence(...pat) : reify(pat)));
@@ -1747,13 +1661,12 @@ function slowcat(...pats) {
};
return new Pattern(query).splitQueries();
}
pattern['slowcat'] = slowcat;
/** Concatenation: combines a list of patterns, switching between them successively, one per cycle. Unlike slowcat, this version will skip cycles.
* @param {...any} items - The items to concatenate
* @return {Pattern}
*/
function slowcatPrime(...pats) {
export function slowcatPrime(...pats) {
pats = pats.map(reify);
const query = function (state) {
const pat_n = Math.floor(state.span.begin) % pats.length;
@@ -1762,7 +1675,6 @@ function slowcatPrime(...pats) {
};
return new Pattern(query).splitQueries();
}
pattern['slowcatPrime'] = slowcatPrime;
/** Concatenation: as with {@link slowcat}, but squashes a cycle from each pattern into one cycle
*
@@ -1775,10 +1687,9 @@ pattern['slowcatPrime'] = slowcatPrime;
* // sequence(e5, b4, [d5, c5])
* // seq(e5, b4, [d5, c5])
*/
function fastcat(...pats) {
export function fastcat(...pats) {
return slowcat(...pats)._fast(pats.length);
}
pattern['fastcat'] = fastcat;
/** The given items are con**cat**enated, where each one takes one cycle. Synonym: slowcat
*
@@ -1788,17 +1699,16 @@ pattern['fastcat'] = fastcat;
* cat(e5, b4, [d5, c5]).note() // "<e5 b4 [d5 c5]>".note()
*
*/
function cat(...pats) {
export function cat(...pats) {
return slowcat(...pats);
}
pattern['cat'] = cat;
/** Like {@link seq}, but each step has a length, relative to the whole.
* @return {Pattern}
* @example
* timeCat([3,e3],[1, g3]).note() // "e3@3 g3".note()
*/
function timeCat(...timepats) {
export function timeCat(...timepats) {
const total = timepats.map((a) => a[0]).reduce((a, b) => a.add(b), Fraction(0));
let begin = Fraction(0);
const pats = [];
@@ -1809,23 +1719,20 @@ function timeCat(...timepats) {
}
return stack(...pats);
}
pattern['timeCat'] = timeCat;
/** See {@link fastcat} */
function sequence(...pats) {
export function sequence(...pats) {
return fastcat(...pats);
}
pattern['sequence'] = sequence;
/** Like **cat**, but the items are crammed into one cycle. Synonyms: fastcat, sequence
* @example
* seq(e5, b4, [d5, c5]).note() // "e5 b4 [d5 c5]".note()
*
*/
function seq(...pats) {
export function seq(...pats) {
return fastcat(...pats);
}
pattern['seq'] = seq;
function _sequenceCount(x) {
if (Array.isArray(x)) {
@@ -1840,7 +1747,7 @@ function _sequenceCount(x) {
return [reify(x), 1];
}
function polymeterSteps(steps, ...args) {
export function polymeterSteps(steps, ...args) {
const seqs = args.map((a) => _sequenceCount(a));
if (seqs.length == 0) {
return silence;
@@ -1851,7 +1758,7 @@ function polymeterSteps(steps, ...args) {
const pats = [];
for (const seq of seqs) {
if (seq[1] == 0) {
continue;
next;
}
if (steps == seq[1]) {
pats.push(seq[0]);
@@ -1861,27 +1768,57 @@ function polymeterSteps(steps, ...args) {
}
return stack(...pats);
}
pattern['polymeterSteps'] = polymeterSteps;
function polymeter(...args) {
export function polymeter(...args) {
return polymeterSteps(0, ...args);
}
pattern['polymeter'] = polymeter;
function pm(...args) {
return polymeter(...args);
// alias
export function pm(...args) {
polymeter(...args);
}
pattern['pm'] = polymeter;
export const add = curry((a, pat) => pat.add(a));
export const chop = curry((a, pat) => pat.chop(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));
export const early = curry((a, pat) => pat.early(a));
export const echo = curry((a, b, c, pat) => pat.echo(a, b, c));
export const every = curry((i, f, pat) => pat.every(i, f));
export const fast = curry((a, pat) => pat.fast(a));
export const inv = (pat) => pat.inv();
export const invert = (pat) => pat.invert();
export const iter = curry((a, pat) => pat.iter(a));
export const iterBack = curry((a, pat) => pat.iterBack(a));
export const jux = curry((f, pat) => pat.jux(f));
export const juxBy = curry((by, f, pat) => pat.juxBy(by, f));
export const late = curry((a, pat) => pat.late(a));
export const linger = curry((a, pat) => pat.linger(a));
export const mask = curry((a, pat) => pat.mask(a));
export const mul = curry((a, pat) => pat.mul(a));
export const off = curry((t, f, pat) => pat.off(t, f));
export const ply = curry((a, pat) => pat.ply(a));
export const range = curry((a, b, pat) => pat.range(a, b));
export const rangex = curry((a, b, pat) => pat.rangex(a, b));
export const range2 = curry((a, b, pat) => pat.range2(a, b));
export const rev = (pat) => pat.rev();
export const slow = curry((a, pat) => pat.slow(a));
export const struct = curry((a, pat) => pat.struct(a));
export const sub = curry((a, pat) => pat.sub(a));
export const superimpose = curry((array, pat) => pat.superimpose(...array));
export const set = curry((a, pat) => pat.set(a));
export const when = curry((binary, f, pat) => pat.when(binary, f));
// 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 = {fast: pattern.fast, slow: pattern.slow, early: pattern.early, late: pattern.late, superimpose: pattern.superimpose };
Pattern.prototype.composable = { fast, slow, 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)
function makeComposable(func) {
export function makeComposable(func) {
Object.entries(Pattern.prototype.composable).forEach(([functionName, composable]) => {
// compose with dot
func[functionName] = (...args) => {
@@ -1894,31 +1831,25 @@ function makeComposable(func) {
});
return func;
}
pattern['makeComposable'] = makeComposable;
const patternify2 = (f) => (pata, patb, pat) =>
export const patternify2 = (f) => (pata, patb, pat) =>
pata
.fmap((a) => (b) => f.call(pat, a, b))
.appLeft(patb)
.innerJoin();
pattern['patternify2'] = patternify2;
const patternify3 = (f) => (pata, patb, patc, pat) =>
export const patternify3 = (f) => (pata, patb, patc, pat) =>
pata
.fmap((a) => (b) => (c) => f.call(pat, a, b, c))
.appLeft(patb)
.appLeft(patc)
.innerJoin();
pattern['patternify3'] = patternify3;
const patternify4 = (f) => (pata, patb, patc, patd, pat) =>
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['patternify4'] = patternify4;
Pattern.prototype.echo = function (...args) {
args = args.map(reify);
@@ -1970,7 +1901,6 @@ Pattern.prototype.range2 = function (...args) {
};
// call this after all Pattern.prototype.define calls have been executed! (right before evaluate)
// (but isn't this called for every define call anyway?)
Pattern.prototype.bootstrap = function () {
// makeComposable(Pattern.prototype);
const bootstrapped = Object.fromEntries(
@@ -1986,9 +1916,10 @@ Pattern.prototype.bootstrap = function () {
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 this.patternify(x => x.innerJoin(), Pattern.prototype['_' + prop])(...args);
};
/*
const func = Pattern.prototype['_' + prop];
Pattern.prototype[prop] = function (...args) {
@@ -1999,22 +1930,22 @@ Pattern.prototype.bootstrap = function () {
// a getter that returns a function, so 'pat' can be
// accessed by closures that are methods of that function..
get: function() {
const pat = this;
// wrap the default behaviour
const pat = this;
// wrap the default behaviour
const wrapper = pat.patternify(x => x.innerJoin(), func);
// add the variants
// add the variants
wrapper['in'] = pat.patternify(x => x.innerJoin(), func);
wrapper['out'] = pat.patternify(x => x.outerJoin(), func);
wrapper['trig'] = pat.patternify(x => x.trigJoin(), func);
wrapper['trigzero'] = pat.patternify(x => x.trigzeroJoin(), func);
wrapper['squeeze'] = pat.patternify(x => x.squeezeJoin(), func);
return wrapper;
return wrapper;
}
});
*/
// with the following, you can do, e.g. `stack(c3).fast.slowcat(1, 2, 4, 8)` instead of `stack(c3).fast(slowcat(1, 2, 4, 8))`
// TODO: find a way to implement below outside of constructor (code only worked there)
/* Object.assign(
@@ -2047,5 +1978,4 @@ Pattern.prototype.define = (name, func, options = {}) => {
// Pattern.prototype.define('early', (a, pat) => pat.early(a), { patternified: true, composable: true });
Pattern.prototype.define('hush', (pat) => pat.hush(), { patternified: false, composable: true });
export default pattern;
Pattern.prototype.define('bypass', (pat) => pat.bypass(on), { patternified: true, composable: true });
+1 -5
View File
@@ -4,11 +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/>.
*/
import pattern from './pattern.mjs';
const { Pattern } = pattern;
import { toMidi } from './util.mjs';
import { getDrawContext } from './draw.mjs';
import { Pattern } from './index.mjs';
const scale = (normalized, min, max) => normalized * (max - min) + min;
const getValue = (e) => {
+2 -2
View File
@@ -40,11 +40,11 @@ export function repl({
throw new Error('no code to evaluate');
}
try {
beforeEval?.({ code });
beforeEval({ code });
const { pattern } = await _evaluate(code, transpiler);
logger(`[eval] code updated`);
scheduler.setPattern(pattern, autostart);
afterEval?.({ code, pattern });
afterEval({ code, pattern });
return pattern;
} catch (err) {
// console.warn(`[repl] eval error: ${err.message}`);
+1 -2
View File
@@ -5,8 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import { Hap } from './hap.mjs';
import pattern from './pattern.mjs';
const { Pattern, fastcat, reify, silence, stack, isPattern } = pattern;
import { Pattern, fastcat, reify, silence, stack, isPattern } from './pattern.mjs';
import Fraction from './fraction.mjs';
import { id } from './util.mjs';
+1 -2
View File
@@ -4,8 +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/>.
*/
import pattern from './pattern.mjs';
const {Pattern, patternify2, reify} = pattern;
import { Pattern, patternify2 } from './index.mjs';
let synth;
try {
+1 -2
View File
@@ -4,8 +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/>.
*/
import pattern from '../pattern.mjs';
const { fastcat, stack, slowcat, silence, pure } = pattern;
import { fastcat, stack, slowcat, silence, pure } from '../pattern.mjs';
import { describe, it, expect } from 'vitest';
import drawLine from '../drawLine.mjs';
+36 -37
View File
@@ -8,9 +8,10 @@ import Fraction from 'fraction.js';
import { describe, it, expect } from 'vitest';
import core from '../index.mjs';
const {
import {
TimeSpan,
Hap,
State,
Pattern,
pure,
stack,
@@ -28,12 +29,6 @@ const {
sub,
mul,
div,
id,
ply,
rev,
TimeSpan,
Hap,
State,
saw,
saw2,
isaw,
@@ -44,7 +39,11 @@ const {
square2,
tri,
tri2,
time} = core;
id,
ply,
rev,
time,
} from '../index.mjs';
import { steady } from '../signal.mjs';
@@ -157,32 +156,32 @@ describe('Pattern', () => {
describe('add()', () => {
it('can structure In()', () => {
expect(pure(3).add(pure(4)).query(st(0, 1))[0].value).toBe(7);
expect(pure(3).add.in(pure(4)).query(st(0, 1))[0].value).toBe(7);
expect(pure(3).addIn(pure(4)).query(st(0, 1))[0].value).toBe(7);
});
it('can structure Out()', () => {
sameFirst(sequence(1, 2).add.out(4), sequence(5, 6).struct(true));
sameFirst(sequence(1, 2).addOut(4), sequence(5, 6).struct(true));
});
it('can Mix() structure', () => {
expect(sequence(1, 2).add.mix(silence, 5, silence).firstCycle()).toStrictEqual([
expect(sequence(1, 2).addMix(silence, 5, silence).firstCycle()).toStrictEqual([
new Hap(ts(1 / 3, 1 / 2), ts(1 / 3, 1 / 2), 6),
new Hap(ts(1 / 2, 2 / 3), ts(1 / 2, 2 / 3), 7),
]);
});
it('can Trig() structure', () => {
sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).add.trig(20, 30).early(2),
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).addTrig(20, 30).early(2),
sequence(26, 27, 36, 37),
);
});
it('can Trigzero() structure', () => {
sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).add.trigzero(20, 30).early(2),
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).addTrigzero(20, 30).early(2),
sequence(21, 22, 31, 32),
);
});
it('can Squeeze() structure', () => {
sameFirst(
sequence(1, [2, 3]).add.squeeze(sequence(10, 20, 30)),
sequence(1, [2, 3]).addSqueeze(sequence(10, 20, 30)),
sequence(
[11, 21, 31],
[
@@ -194,7 +193,7 @@ describe('Pattern', () => {
});
it('can SqueezeOut() structure', () => {
sameFirst(
sequence(1, [2, 3]).add.squeezeout(10, 20, 30),
sequence(1, [2, 3]).addSqueezeOut(10, 20, 30),
sequence([11, [12, 13]], [21, [22, 23]], [31, [32, 33]]),
);
});
@@ -205,32 +204,32 @@ describe('Pattern', () => {
describe('keep()', () => {
it('can structure In()', () => {
expect(pure(3).keep(pure(4)).query(st(0, 1))[0].value).toBe(3);
expect(pure(3).keep.in(pure(4)).query(st(0, 1))[0].value).toBe(3);
expect(pure(3).keepIn(pure(4)).query(st(0, 1))[0].value).toBe(3);
});
it('can structure Out()', () => {
sameFirst(sequence(1, 2).keep.out(4), sequence(1, 2).struct(true));
sameFirst(sequence(1, 2).keepOut(4), sequence(1, 2).struct(true));
});
it('can Mix() structure', () => {
expect(sequence(1, 2).keep.mix(silence, 5, silence).firstCycle()).toStrictEqual([
expect(sequence(1, 2).keepMix(silence, 5, silence).firstCycle()).toStrictEqual([
new Hap(ts(1 / 3, 1 / 2), ts(1 / 3, 1 / 2), 1),
new Hap(ts(1 / 2, 2 / 3), ts(1 / 2, 2 / 3), 2),
]);
});
it('can Trig() structure', () => {
sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keep.trig(20, 30).early(2),
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keepTrig(20, 30).early(2),
sequence(6, 7, 6, 7),
);
});
it('can Trigzero() structure', () => {
sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keep.trigzero(20, 30).early(2),
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keepTrigzero(20, 30).early(2),
sequence(1, 2, 1, 2),
);
});
it('can Squeeze() structure', () => {
sameFirst(
sequence(1, [2, 3]).keep.squeeze(sequence(10, 20, 30)),
sequence(1, [2, 3]).keepSqueeze(sequence(10, 20, 30)),
sequence(
[1, 1, 1],
[
@@ -240,39 +239,39 @@ describe('Pattern', () => {
),
);
});
it('can squeezeOut() structure', () => {
sameFirst(sequence(1, [2, 3]).keep.squeezeout(10, 20, 30), sequence([1, [2, 3]], [1, [2, 3]], [1, [2, 3]]));
it('can SqueezeOut() structure', () => {
sameFirst(sequence(1, [2, 3]).keepSqueezeOut(10, 20, 30), sequence([1, [2, 3]], [1, [2, 3]], [1, [2, 3]]));
});
});
describe('keepif()', () => {
it('can structure In()', () => {
sameFirst(sequence(3, 4).keepif(true, false), sequence(3, silence));
sameFirst(sequence(3, 4).keepif.in(true, false), sequence(3, silence));
sameFirst(sequence(3, 4).keepifIn(true, false), sequence(3, silence));
});
it('can structure Out()', () => {
sameFirst(pure(1).keepif.out(true, false), sequence(1, silence));
sameFirst(pure(1).keepifOut(true, false), sequence(1, silence));
});
it('can Mix() structure', () => {
expect(sequence(1, 2).keepif.mix(false, true, false).firstCycle()).toStrictEqual([
expect(sequence(1, 2).keepifMix(false, true, false).firstCycle()).toStrictEqual([
new Hap(ts(1 / 3, 1 / 2), ts(1 / 3, 1 / 2), 1),
new Hap(ts(1 / 2, 2 / 3), ts(1 / 2, 2 / 3), 2),
]);
});
it('can Trig() structure', () => {
sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keepif.trig(false, true).early(2),
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keepifTrig(false, true).early(2),
sequence(silence, silence, 6, 7),
);
});
it('can Trigzero() structure', () => {
sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keepif.trigzero(false, true).early(2),
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keepifTrigzero(false, true).early(2),
sequence(silence, silence, 1, 2),
);
});
it('can Squeeze() structure', () => {
sameFirst(
sequence(1, [2, 3]).keepif.squeeze(sequence(true, true, false)),
sequence(1, [2, 3]).keepifSqueeze(sequence(true, true, false)),
sequence(
[1, 1, silence],
[
@@ -283,7 +282,7 @@ describe('Pattern', () => {
);
});
it('can SqueezeOut() structure', () => {
sameFirst(sequence(1, [2, 3]).keepif.squeezeout(true, true, false), sequence([1, [2, 3]], [1, [2, 3]], silence));
sameFirst(sequence(1, [2, 3]).keepifSqueezeOut(true, true, false), sequence([1, [2, 3]], [1, [2, 3]], silence));
});
});
describe('sub()', () => {
@@ -321,15 +320,15 @@ describe('Pattern', () => {
it('Can set things with plain values', () => {
sameFirst(sequence(1, 2, 3).set(4), sequence(4).fast(3));
});
describe('set.out()', () => {
describe('setOut()', () => {
it('Can set things with structure from second pattern', () => {
sameFirst(sequence(1, 2).set.out(4), pure(4).mask(true, true));
sameFirst(sequence(1, 2).setOut(4), pure(4).mask(true, true));
});
});
describe('set.squeeze()', () => {
describe('setSqueeze()', () => {
it('Can squeeze one pattern inside the haps of another', () => {
sameFirst(
sequence(1, [2, 3]).set.squeeze(sequence('a', 'b', 'c')),
sequence(1, [2, 3]).setSqueeze(sequence('a', 'b', 'c')),
sequence(
['a', 'b', 'c'],
[
@@ -339,7 +338,7 @@ describe('Pattern', () => {
),
);
sameFirst(
sequence(1, [2, 3]).set.squeeze('a', 'b', 'c'),
sequence(1, [2, 3]).setSqueeze('a', 'b', 'c'),
sequence(
['a', 'b', 'c'],
[
+1 -2
View File
@@ -4,8 +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/>.
*/
import pattern from '../pattern.mjs';
const { pure } = pattern;
import { pure } from '../pattern.mjs';
import {
isNote,
tokenizeNote,
-122
View File
@@ -1,122 +0,0 @@
import strudel from '@strudel.cycles/core';
const { getFrequency, logger, Pattern } = strudel;
import { getAudioContext } from '@strudel.cycles/webaudio';
import csd from './project.csd?raw';
// import livecodeOrc from './livecode.orc?raw';
import presetsOrc from './presets.orc?raw';
let csoundLoader, _csound;
// triggers given instrument name using csound.
Pattern.prototype._csound = function (instrument) {
instrument = instrument || 'triangle';
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)
return this.onTrigger((time, hap) => {
if (!_csound) {
logger('[csound] not loaded yet', 'warning');
return;
}
if (typeof hap.value !== 'object') {
throw new Error('csound only support objects as hap values');
}
let { gain = 0.8 } = hap.value;
gain *= 0.2;
const freq = Math.round(getFrequency(hap));
const controls = Object.entries({ ...hap.value, freq })
.flat()
.join('/');
// TODO: find out how to send a precise ctx based time
// http://www.csounds.com/manual/html/i.html
const params = [
`"${instrument}"`, // p1: instrument name
time - getAudioContext().currentTime, //.toFixed(precision), // p2: starting time in arbitrary unit called beats
hap.duration + 0, // p3: duration in beats
// instrument specific params:
freq, //.toFixed(precision), // p4: frequency
gain, // p5: gain
`"${controls}"`, // p6 controls as string (like superdirt osc message)
];
const msg = `i ${params.join(' ')}`;
_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) {
const [msg] = args;
if (
type === 'message' &&
(['[commit: HEAD]'].includes(msg) ||
msg.startsWith('--Csound version') ||
msg.startsWith('libsndfile') ||
msg.startsWith('sr =') ||
msg.startsWith('0dBFS') ||
msg.startsWith('audio buffered') ||
msg.startsWith('writing') ||
msg.startsWith('SECTION 1:'))
) {
// ignore
return;
}
let logType = 'info';
if (msg.startsWith('error:')) {
logType = 'error';
}
logger(`[csound] ${msg || ''}`, logType);
}
async function load() {
if (window.__csound__) {
// allows using some other csound instance
_csound = window.__csound__;
} else {
const { Csound } = await import('@csound/browser');
_csound = await Csound({ audioContext: getAudioContext() });
}
_csound.removeAllListeners('message');
['message'].forEach((k) => _csound.on(k, (...args) => eventLogger(k, args)));
await _csound.setOption('-m0d'); // see -m flag https://csound.com/docs/manual/CommandFlags.html
await _csound.setOption('--sample-accurate');
await _csound.compileCsdText(csd);
// await _csound.compileOrc(livecodeOrc);
await _csound.compileOrc(presetsOrc);
await _csound.start();
return _csound;
}
async function init() {
csoundLoader = csoundLoader || load();
return csoundLoader;
}
let orcCache = {};
export async function loadOrc(url) {
await init();
if (typeof url !== 'string') {
throw new Error('loadOrc: expected url string');
}
if (url.startsWith('github:')) {
const [_, path] = url.split('github:');
url = `https://raw.githubusercontent.com/${path}`;
}
if (!orcCache[url]) {
orcCache[url] = fetch(url)
.then((res) => res.text())
.then((code) => _csound.compileOrc(code));
}
await orcCache[url];
}
File diff suppressed because it is too large Load Diff
-32
View File
@@ -1,32 +0,0 @@
{
"name": "@strudel.cycles/csound",
"version": "0.3.0",
"description": "csound bindings for strudel",
"main": "csound.mjs",
"scripts": {
"test": "echo \"No tests present.\" && exit 0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tidalcycles/strudel.git"
},
"keywords": [
"tidalcycles",
"strudel",
"pattern",
"livecoding",
"algorave"
],
"author": "Felix Roos <flix91@gmail.com>",
"contributors": [
"Alex McLean <alex@slab.org>"
],
"license": "AGPL-3.0-or-later",
"bugs": {
"url": "https://github.com/tidalcycles/strudel/issues"
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@csound/browser": "^6.18.3"
}
}
-96
View File
@@ -1,96 +0,0 @@
; returns value of given key in given "string map"
; keymap("freq", "note/c3/freq/220/gain/0.5")
; yields "220"
opcode keymap, S, SS
Skey, Smap xin
idelimiter = strindex(Smap, strcat(Skey, "/"))
ifrom = idelimiter + strlen(Skey) + 1
Svalue = strsub(Smap, ifrom, strlen(Smap))
Svalue = strsub(Svalue, 0, strindex(Svalue, "/"))
xout Svalue
endop
; TODO add incredibly dope synths
instr organ
iduration = p3
ifreq = p4
igain = p5
ioct = octcps(ifreq)
asig = vco2(igain, ifreq, 12, .5) ; my edit
kpwm = oscili(.1, 5)
asig = vco2(igain, ifreq, 4, .5 + kpwm)
asig += vco2(igain/4, ifreq * 2)
; filter
; idepth = 2
; acut = transegr:a(0, .005, 0, idepth, .06, -4.2, 0.001, .01, -4.2, 0) ; filter envelope
; asig = zdf_2pole(asig, cpsoct(ioct + acut), 0.5)
; amp envelope
iattack = .001
irelease = .05
asig *= linsegr:a(0, iattack, 1, iduration, 1, irelease, 0)
out(asig, asig)
endin
instr triangle
iduration = p3
ifreq = p4
igain = p5
ioct = octcps(ifreq)
asig = vco2(igain, ifreq, 12, .5)
; amp envelope
iattack = .001
irelease = .05
asig *= linsegr:a(0, iattack, 1, iduration, 1, irelease, 0)
out(asig, asig)
endin
instr pad
iduration = p3
ifreq = p4
igain = p5
ioct = octcps(ifreq)
asig = vco2(igain, ifreq, 0)
; amp envelope
iattack = .5
irelease = .1
asig *= linsegr:a(0, iattack, 1, iduration, 1, irelease, 0)
idepth = 2
acut = transegr:a(0, .005, 0, idepth, .06, -4.2, 0.001, .01, -4.2, 0)
asig = zdf_2pole(asig, 1000, 2)
out(asig, asig)
endin
gisine ftgen 0, 0, 4096, 10, 1
instr bow
kpres = 2
krat = 0.16
kvibf = 6.12723
kvib linseg 0, 0.5, 0, 1, 1, p3-0.5, 1
kvamp = kvib * 0.01
asig wgbow .7, p4, kpres, krat, kvibf, kvamp, gisine
asig = asig*p5
outs asig, asig
endin
instr Meta
Smap = strget(p6)
Sinstrument = keymap("s", Smap)
schedule(Sinstrument, 0, p3, p4, p5)
; TODO find a way to pipe Sinstrument through effects
endin
-10
View File
@@ -1,10 +0,0 @@
<CsoundSynthesizer>
<CsInstruments>
sr=48000
ksmps=64
nchnls=2
0dbfs=1
</CsInstruments>
</CsoundSynthesizer>
+2 -2
View File
@@ -4,9 +4,9 @@ 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/>.
*/
import core from '@strudel.cycles/core';
import { evaluate as _evaluate } from '@strudel.cycles/core';
import shapeshifter from './shapeshifter.mjs';
export const evaluate = async (code) => {
return core.evaluate(code, shapeshifter);
return _evaluate(code, shapeshifter);
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/eval",
"version": "0.4.1",
"version": "0.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/eval",
"version": "0.4.1",
"version": "0.4.0",
"description": "Code evaluator for strudel",
"main": "index.mjs",
"type": "module",
@@ -28,7 +28,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.1",
"@strudel.cycles/core": "^0.4.0",
"estraverse": "^5.3.0",
"shift-ast": "^7.0.0",
"shift-codegen": "^8.1.0",
+1 -1
View File
@@ -21,7 +21,7 @@ import {
import shiftCodegen from 'shift-codegen';
const codegen = shiftCodegen.default || shiftCodegen; // parcel module resolution fuckup
import strudel from '@strudel.cycles/core';
import * as strudel from '@strudel.cycles/core';
const { Pattern } = strudel;
+1 -1
View File
@@ -8,7 +8,7 @@ import { expect, describe, it } from 'vitest';
import { evaluate } from '../evaluate.mjs';
import { mini } from '@strudel.cycles/mini';
import strudel from '@strudel.cycles/core';
import * as strudel from '@strudel.cycles/core';
const { fastcat, evalScope } = strudel;
describe('evaluate', async () => {
+1 -2
View File
@@ -5,8 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import * as _WebMidi from 'webmidi';
import core from '@strudel.cycles/core';
const { Pattern, isPattern, isNote, getPlayableNoteValue, logger } = core;
import { Pattern, isPattern, isNote, getPlayableNoteValue, logger } from '@strudel.cycles/core';
import { getAudioContext } from '@strudel.cycles/webaudio';
// if you use WebMidi from outside of this package, make sure to import that instance:
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/midi",
"version": "0.4.1",
"version": "0.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/midi",
"version": "0.4.1",
"version": "0.4.0",
"description": "Midi API for strudel",
"main": "index.mjs",
"repository": {
@@ -21,7 +21,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/tone": "^0.4.1",
"@strudel.cycles/tone": "^0.4.0",
"tone": "^14.7.77",
"webmidi": "^3.0.21"
}
+7 -10
View File
@@ -5,10 +5,10 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import * as krill from './krill-parser.js';
import core from '@strudel.cycles/core';
import * as strudel from '@strudel.cycles/core';
// import { addMiniLocations } from '@strudel.cycles/eval/shapeshifter.mjs';
const { pure, Pattern, stack, slowcat, sequence, timeCat, silence, reify, rand, chooseInWith, Fraction } = core;
const { pure, Pattern, Fraction, stack, slowcat, sequence, timeCat, silence, reify } = strudel;
var _seedState = 0;
const randOffset = 0.0002;
@@ -23,15 +23,14 @@ const applyOptions = (parent) => (pat, i) => {
const operator = options?.operator;
if (operator) {
switch (operator.type_) {
case 'stretch': {
case 'stretch':
const speed = Fraction(operator.arguments_.amount).inverse();
return reify(pat).fast(speed);
}
case 'bjorklund':
return pat.euclid(operator.arguments_.pulse, operator.arguments_.step, operator.arguments_.rotation);
case 'degradeBy':
return reify(pat)._degradeByWith(
rand.early(randOffset * _nextSeed()).segment(1),
strudel.rand.early(randOffset * _nextSeed()).segment(1),
operator.arguments_.amount,
);
// TODO: case 'fixed-step': "%"
@@ -88,7 +87,7 @@ function resolveReplications(ast) {
export function patternifyAST(ast) {
switch (ast.type_) {
case 'pattern': {
case 'pattern':
resolveReplications(ast);
const children = ast.source_.map(patternifyAST).map(applyOptions(ast));
const alignment = ast.arguments_.alignment;
@@ -96,7 +95,7 @@ export function patternifyAST(ast) {
return stack(...children);
}
if (alignment === 'r') {
return chooseInWith(rand.early(randOffset * _nextSeed()).segment(1), children);
return strudel.chooseInWith(strudel.rand.early(randOffset * _nextSeed()).segment(1), children);
}
const weightedChildren = ast.source_.some((child) => !!child.options_?.weight);
if (!weightedChildren && alignment === 't') {
@@ -111,8 +110,7 @@ export function patternifyAST(ast) {
return pat;
}
return sequence(...children);
}
case 'element': {
case 'element':
if (ast.source_ === '~') {
return silence;
}
@@ -131,7 +129,6 @@ export function patternifyAST(ast) {
return pure(value).withLocation([start.line, start.column, start.offset], [end.line, end.column, end.offset]);
}
return patternifyAST(ast.source_);
}
case 'stretch':
return patternifyAST(ast.source_).slow(ast.arguments_.amount);
/* case 'scale':
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/mini",
"version": "0.4.1",
"version": "0.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/mini",
"version": "0.4.1",
"version": "0.4.0",
"description": "Mini notation for strudel",
"main": "index.mjs",
"type": "module",
@@ -26,9 +26,9 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.1",
"@strudel.cycles/eval": "^0.4.1",
"@strudel.cycles/tone": "^0.4.1"
"@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/eval": "^0.4.0",
"@strudel.cycles/tone": "^0.4.0"
},
"devDependencies": {
"peggy": "^2.0.1"
+1 -2
View File
@@ -5,8 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import OSC from 'osc-js';
import core from '@strudel.cycles/core';
const { Pattern, logger, parseNumeral } = core;
import { logger, parseNumeral, Pattern } from '@strudel.cycles/core';
let connection; // Promise<OSC>
function connect() {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/osc",
"version": "0.3.1",
"version": "0.3.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/osc",
"version": "0.3.1",
"version": "0.3.0",
"description": "OSC messaging for strudel",
"main": "osc.mjs",
"scripts": {
+2 -3
View File
@@ -13,13 +13,12 @@ npm i @strudel.cycles/react
Here is a minimal example of how to set up a MiniRepl:
```jsx
import core from '@strudel.cycles/core';
const {evalScope} = core;
import { evalScope, controls } from '@strudel.cycles/core';
import { MiniRepl } from '@strudel.cycles/react';
import { prebake } from '../repl/src/prebake.mjs';
evalScope(
core,
controls,
import('@strudel.cycles/core'),
import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'),
+1 -1
View File
File diff suppressed because one or more lines are too long
+34 -35
View File
@@ -7,7 +7,7 @@ import { tags as r } from "@lezer/highlight";
import { createTheme as Z } from "@uiw/codemirror-themes";
import { useInView as ee } from "react-hook-inview";
import { webaudioOutput as te, getAudioContext as re } from "@strudel.cycles/webaudio";
import oe from "@strudel.cycles/core";
import { repl as oe } from "@strudel.cycles/core";
import { transpiler as ne } from "@strudel.cycles/transpiler";
const ae = Z({
theme: "dark",
@@ -74,11 +74,11 @@ const B = $.define(), se = G.define({
for (let o of t.effects)
if (o.is(z)) {
const a = o.value.map(
(s) => (s.context.locations || []).map(({ start: u, end: d }) => {
const f = s.context.color || "#FFCA28";
let c = t.newDoc.line(u.line).from + u.column, i = t.newDoc.line(d.line).from + d.column;
(s) => (s.context.locations || []).map(({ start: f, end: d }) => {
const u = s.context.color || "#FFCA28";
let c = t.newDoc.line(f.line).from + f.column, i = t.newDoc.line(d.line).from + d.column;
const m = t.newDoc.length;
return c > m || i > m ? void 0 : E.mark({ attributes: { style: `outline: 1.5px solid ${f};` } }).range(c, i);
return c > m || i > m ? void 0 : E.mark({ attributes: { style: `outline: 1.5px solid ${u};` } }).range(c, i);
})
).flat().filter(Boolean) || [];
e = E.set(a, !0);
@@ -90,13 +90,13 @@ const B = $.define(), se = G.define({
},
provide: (e) => U.decorations.from(e)
}), le = [Y(), ae, ie, se];
function de({ value: e, onChange: t, onViewChanged: o, onSelectionChange: a, options: s, editorDidMount: u }) {
function de({ value: e, onChange: t, onViewChanged: o, onSelectionChange: a, options: s, editorDidMount: f }) {
const d = _(
(i) => {
t?.(i);
},
[t]
), f = _(
), u = _(
(i) => {
o?.(i);
},
@@ -110,7 +110,7 @@ function de({ value: e, onChange: t, onViewChanged: o, onSelectionChange: a, opt
return /* @__PURE__ */ n.createElement(n.Fragment, null, /* @__PURE__ */ n.createElement(X, {
value: e,
onChange: d,
onCreateEditor: f,
onCreateEditor: u,
onUpdate: c,
extensions: le
}));
@@ -119,21 +119,21 @@ function K(...e) {
return e.filter(Boolean).join(" ");
}
function ue({ view: e, pattern: t, active: o, getTime: a }) {
const s = H([]), u = H();
const s = H([]), f = H();
L(() => {
if (e)
if (t && o) {
let d = requestAnimationFrame(function f() {
let u = function() {
try {
const c = a(), m = [Math.max(u.current || c, c - 1 / 10, 0), c + 1 / 60];
u.current = m[1], s.current = s.current.filter((g) => g.whole.end > c);
const c = a(), m = [Math.max(f.current || c, c - 1 / 10, 0), c + 1 / 60];
f.current = m[1], s.current = s.current.filter((g) => g.whole.end > c);
const h = t.queryArc(...m).filter((g) => g.hasOnset());
s.current = s.current.concat(h), e.dispatch({ effects: z.of(s.current) });
} catch {
e.dispatch({ effects: z.of([]) });
}
d = requestAnimationFrame(f);
});
d = requestAnimationFrame(u);
}, d = requestAnimationFrame(u);
return () => {
cancelAnimationFrame(d);
};
@@ -177,21 +177,20 @@ function O({ type: e }) {
function Ee(e) {
return L(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), _((t) => window.postMessage(t, "*"), []);
}
const we = oe.repl;
function ye({
function we({
defaultOutput: e,
interval: t,
getTime: o,
evalOnMount: a = !1,
initialCode: s = "",
autolink: u = !1,
autolink: f = !1,
beforeEval: d,
afterEval: f,
afterEval: u,
onEvalError: c,
onToggle: i
}) {
const m = V(() => ke(), []), [h, g] = w(), [C, N] = w(), [p, y] = w(s), [M, S] = w(), [k, D] = w(), [F, x] = w(!1), b = p !== M, { scheduler: A, evaluate: T, start: J, stop: q, pause: Q } = V(
() => we({
const m = V(() => ye(), []), [h, g] = w(), [C, N] = w(), [p, y] = w(s), [M, S] = w(), [k, D] = w(), [F, x] = w(!1), b = p !== M, { scheduler: A, evaluate: T, start: J, stop: q, pause: Q } = V(
() => oe({
interval: t,
defaultOutput: e,
onSchedulerError: g,
@@ -204,7 +203,7 @@ function ye({
y(l), d?.();
},
afterEval: ({ pattern: l, code: P }) => {
S(P), D(l), N(), g(), u && (window.location.hash = "#" + encodeURIComponent(btoa(P))), f?.();
S(P), D(l), N(), g(), f && (window.location.hash = "#" + encodeURIComponent(btoa(P))), u?.();
},
onToggle: (l) => {
x(l), i?.(l);
@@ -244,16 +243,16 @@ function ye({
}
};
}
function ke() {
function ye() {
return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
}
const _e = () => re().currentTime;
function Te({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) {
const ke = () => re().currentTime;
function Se({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) {
const {
code: s,
setCode: u,
setCode: f,
evaluate: d,
activateCode: f,
activateCode: u,
error: c,
isDirty: i,
activeCode: m,
@@ -262,10 +261,10 @@ function Te({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) {
scheduler: C,
togglePlay: N,
stop: p
} = ye({
} = we({
initialCode: e,
defaultOutput: te,
getTime: _e
getTime: ke
}), [y, M] = w(), [S, k] = ee({
threshold: 0.01
}), D = H(), F = V(() => ((k || !t) && (D.current = !0), k || D.current), [k, t]);
@@ -277,7 +276,7 @@ function Te({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) {
}), j(() => {
if (a) {
const x = async (b) => {
(b.ctrlKey || b.altKey) && (b.code === "Enter" ? (b.preventDefault(), ce(y), await f()) : b.code === "Period" && (p(), b.preventDefault()));
(b.ctrlKey || b.altKey) && (b.code === "Enter" ? (b.preventDefault(), ce(y), await u()) : b.code === "Period" && (p(), b.preventDefault()));
};
return window.addEventListener("keydown", x, !0), () => window.removeEventListener("keydown", x, !0);
}
@@ -295,7 +294,7 @@ function Te({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) {
type: g ? "pause" : "play"
})), /* @__PURE__ */ n.createElement("button", {
className: K(i ? v.button : v.buttonDisabled),
onClick: () => f()
onClick: () => u()
}, /* @__PURE__ */ n.createElement(O, {
type: "refresh"
}))), c && /* @__PURE__ */ n.createElement("div", {
@@ -304,18 +303,18 @@ function Te({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) {
className: v.body
}, F && /* @__PURE__ */ n.createElement(de, {
value: s,
onChange: u,
onChange: f,
onViewChanged: M
})));
}
const Ve = (e) => j(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]);
const Te = (e) => j(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]);
export {
de as CodeMirror,
Te as MiniRepl,
Se as MiniRepl,
K as cx,
ce as flash,
ue as useHighlighting,
Ve as useKeydown,
Te as useKeydown,
Ee as usePostMessage,
ye as useStrudel
we as useStrudel
};
@@ -1,5 +1,4 @@
import core from '@strudel.cycles/core';
const { evalScope } = core;
import { evalScope, controls } from '@strudel.cycles/core';
import { getAudioContext, panic, webaudioOutput } from '@strudel.cycles/webaudio';
import { useCallback, useState } from 'react';
import CodeMirror, { flash } from '../../../src/components/CodeMirror6';
@@ -11,8 +10,8 @@ import './style.css';
// TODO: only import stuff when play is pressed?
evalScope(
core,
// import('@strudel.cycles/core'),
controls,
import('@strudel.cycles/core'),
// import('@strudel.cycles/tone'),
// import('@strudel.cycles/midi'), // TODO: find out why midi loads tone.js
import('@strudel.cycles/tonal'),
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/react",
"version": "0.4.2",
"version": "0.4.1",
"description": "React components for strudel",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
@@ -39,10 +39,10 @@
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@codemirror/lang-javascript": "^6.1.1",
"@strudel.cycles/core": "^0.4.1",
"@strudel.cycles/tone": "^0.4.1",
"@strudel.cycles/transpiler": "^0.4.1",
"@strudel.cycles/webaudio": "^0.4.2",
"@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/tone": "^0.4.0",
"@strudel.cycles/transpiler": "^0.4.0",
"@strudel.cycles/webaudio": "^0.4.1",
"@uiw/codemirror-themes": "^4.12.4",
"@uiw/react-codemirror": "^4.12.4",
"react-hook-inview": "^4.5.0"
+1 -2
View File
@@ -1,8 +1,7 @@
import React from 'react';
import { MiniRepl } from './components/MiniRepl';
import 'tailwindcss/tailwind.css';
import core from '@strudel.cycles/core';
const {controls, evalScope} = core;
import { controls, evalScope } from '@strudel.cycles/core';
evalScope(
controls,
+5 -2
View File
@@ -7,7 +7,9 @@ function useHighlighting({ view, pattern, active, getTime }) {
useEffect(() => {
if (view) {
if (pattern && active) {
let frame = requestAnimationFrame(function updateHighlights() {
let frame = requestAnimationFrame(updateHighlights);
function updateHighlights() {
try {
const audioTime = getTime();
// force min framerate of 10 fps => fixes crash on tab refocus, where lastEnd could be far away
@@ -23,7 +25,8 @@ function useHighlighting({ view, pattern, active, getTime }) {
view.dispatch({ effects: setHighlights.of([]) });
}
frame = requestAnimationFrame(updateHighlights);
});
}
return () => {
cancelAnimationFrame(frame);
};
+1 -2
View File
@@ -1,6 +1,5 @@
import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
import core from '@strudel.cycles/core';
const repl = core.repl
import { repl } from '@strudel.cycles/core';
import { transpiler } from '@strudel.cycles/transpiler';
import usePostMessage from './usePostMessage.mjs';
+18 -62
View File
@@ -4,10 +4,9 @@ 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/>.
*/
import core from '@strudel.cycles/core';
const { Pattern, isPattern } = core;
import { Pattern, isPattern } from '@strudel.cycles/core';
var writeMessage;
var serialWriter;
var choosing = false;
export async function getWriter(br = 38400) {
@@ -15,30 +14,17 @@ export async function getWriter(br = 38400) {
return;
}
choosing = true;
if (writeMessage) {
return writeMessage;
if (serialWriter) {
return serialWriter;
}
if ('serial' in navigator) {
const port = await navigator.serial.requestPort();
await port.open({ baudRate: br });
const encoder = new TextEncoder();
const writer = port.writable.getWriter();
writeMessage = function (message, chk) {
const encoded = encoder.encode(message)
if (!chk) {
writer.write(encoded);
}
else {
const bytes = new Uint8Array(4);
bytes[0] = 124; // | symbol
bytes[1] = (chk >> 8) & 0xFF;
bytes[2] = chk & 0xFF;
bytes[3] = 59; // semicolon
const withchk = new Uint8Array(encoded.length+4)
withchk.set(encoded);
withchk.set(bytes, encoded.length);
writer.write(withchk);
}
const textEncoder = new TextEncoderStream();
const writableStreamClosed = textEncoder.readable.pipeTo(port.writable);
const writer = textEncoder.writable.getWriter();
serialWriter = function (message) {
writer.write(message);
};
} else {
throw 'Webserial is not available in this browser.';
@@ -47,41 +33,18 @@ export async function getWriter(br = 38400) {
const latency = 0.1;
// crc16 (CCITT-FALSE) https://gist.github.com/tijnkooijmans/10981093
function crc16(data) {
const length = data.length
if (length == 0) {
return 0;
}
var crc = 0xFFFF;
for (var i = 0; i < length; ++i) {
crc ^= data.charCodeAt(i) << 8;
for (var j = 0; j < 8; ++j) {
crc = (crc & 0x8000) > 0 ? (crc << 1) ^ 0x1021 : crc << 1;
}
}
return crc & 0xffff;
}
Pattern.prototype.serial = function (br=38400,sendcrc=false,singlecharids=false) {
return this.withHap((hap) => {
if (!writeMessage) {
getWriter(br);
Pattern.prototype.serial = function (...args) {
return this._withHap((hap) => {
if (!serialWriter) {
getWriter(...args);
}
const onTrigger = (time, hap, currentTime) => {
var message = '';
var chk = 0;
if (typeof hap.value === 'object') {
if ('action' in hap.value) {
var action = hap.value['action'];
if (singlecharids) {
action = action.charAt(0);
}
message += action + '(';
message += hap.value['action'] + '(';
var first = true;
for (var [key, val] of Object.entries(hap.value)) {
for (const [key, val] of Object.entries(hap.value)) {
if (key === 'action') {
continue;
}
@@ -90,26 +53,19 @@ Pattern.prototype.serial = function (br=38400,sendcrc=false,singlecharids=false)
} else {
message += ',';
}
if (singlecharids) {
key = key.charAt(0);
}
message += key + ':' + val;
message += `${key}:${val}`;
}
message += ')';
if (sendcrc) {
chk = crc16(message)
}
} else {
for (const [key, val] of Object.entries(hap.value)) {
message += `${key}:${val}`;
message += `${key}:${val};`;
}
}
} else {
message = hap.value;
}
const offset = (time - currentTime + latency) * 1000;
window.setTimeout(function () {writeMessage(message, chk)}, offset);
window.setTimeout(serialWriter, offset, message);
};
return hap.setContext({ ...hap.context, onTrigger, dominantTrigger: true });
});
-3
View File
@@ -1,6 +1,3 @@
import core from '@strudel.cycles/core';
const toMidi = core.toMidi;
let loadCache = {};
async function loadFont(name) {
if (loadCache[name]) {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/soundfonts",
"version": "0.4.2",
"version": "0.4.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/soundfonts",
"version": "0.4.2",
"version": "0.4.1",
"description": "Soundsfont support for strudel",
"main": "index.mjs",
"type": "module",
@@ -22,8 +22,8 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.1",
"@strudel.cycles/webaudio": "^0.4.2",
"@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/webaudio": "^0.4.1",
"sfumato": "^0.1.2",
"soundfont2": "^0.4.0"
},
+1 -3
View File
@@ -1,6 +1,4 @@
import strudel from '@strudel.cycles/core';
const { Pattern, getPlayableNoteValue, toMidi } = strudel;
import { getAudioContext } from '@strudel.cycles/webaudio';
import { Pattern } from '@strudel.cycles/core';
import { loadSoundfont as _loadSoundfont, startPresetNote } from 'sfumato';
Pattern.prototype.soundfont = function (sf, n = 0) {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/tonal",
"version": "0.4.1",
"version": "0.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/tonal",
"version": "0.4.1",
"version": "0.4.0",
"description": "Tonal functions for strudel",
"main": "index.mjs",
"type": "module",
@@ -25,8 +25,8 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.1",
"@tonaljs/tonal": "^4.7.2",
"@strudel.cycles/core": "^0.4.0",
"@tonaljs/tonal": "^4.6.5",
"chord-voicings": "^0.0.1",
"webmidi": "^3.0.21"
}
+1 -2
View File
@@ -7,8 +7,7 @@ This program is free software: you can redistribute it and/or modify it under th
// import { strict as assert } from 'assert';
import '../tonal.mjs'; // need to import this to add prototypes
import core from '@strudel.cycles/core';
const { pure } = core;
import { pure } from '@strudel.cycles/core';
import { describe, it, expect } from 'vitest';
describe('tonal', () => {
+1 -2
View File
@@ -5,8 +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 core from '@strudel.cycles/core';
const { Pattern, pattern, mod } = core;
import { Pattern, mod } from '@strudel.cycles/core';
// transpose note inside scale by offset steps
// function scaleOffset(scale: string, offset: number, note: string) {
+3 -5
View File
@@ -4,9 +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/>.
*/
import core from '@strudel.cycles/core';
const { Pattern, stack, reify, Hap } = core
import { Pattern as _Pattern, stack, Hap, reify } from '@strudel.cycles/core';
import _voicings from 'chord-voicings';
const { dictionaryVoicing, minTopNoteDiff, lefthand } = _voicings.default || _voicings; // parcel module resolution fuckup
@@ -19,7 +17,7 @@ const getVoicing = (chord, lastVoicing, range = ['F3', 'A4']) =>
lastVoicing,
});
// const Pattern = _Pattern;
const Pattern = _Pattern;
Pattern.prototype.fmapNested = function (func) {
return new Pattern((span) =>
@@ -61,7 +59,7 @@ Pattern.prototype.voicings = function (range) {
Pattern.prototype._rootNotes = function (octave = 2) {
return this.fmap((value) => {
const root = value.match(/^([a-gA-G][b#]?).*$/)[1];
const [_, root] = value.match(/^([a-gA-G][b#]?).*$/);
return root + octave;
});
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/tone",
"version": "0.4.1",
"version": "0.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/tone",
"version": "0.4.1",
"version": "0.4.0",
"description": "Tone.js API for strudel",
"main": "index.mjs",
"type": "module",
@@ -22,7 +22,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.1",
"@strudel.cycles/core": "^0.4.0",
"tone": "^14.7.77"
}
}
+1 -2
View File
@@ -5,8 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import '../tone.mjs';
import strudel from '@strudel.cycles/core';
const { pure } = strudel;
import { pure } from '@strudel.cycles/core';
import { describe, it, expect } from 'vitest';
describe('tone', () => {
+1 -3
View File
@@ -4,9 +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/>.
*/
import core from '@strudel.cycles/core';
const { Pattern } = core;
import { Pattern } from '@strudel.cycles/core';
import * as _Tone from 'tone';
// import Tone from here, to make sure to get the same AudioContext
+2 -2
View File
@@ -1,5 +1,5 @@
import core from '@strudel.cycles/core';
import { evaluate as _evaluate } from '@strudel.cycles/core';
import { transpiler } from './transpiler.mjs';
export * from './transpiler.mjs';
export const evaluate = (code) => core.evaluate(code, transpiler);
export const evaluate = (code) => _evaluate(code, transpiler);
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/transpiler",
"version": "0.4.1",
"version": "0.4.0",
"description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.",
"main": "index.mjs",
"scripts": {
@@ -24,7 +24,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.1",
"@strudel.cycles/core": "^0.4.0",
"acorn": "^8.8.1",
"escodegen": "^2.0.0",
"estree-walker": "^3.0.1"
+1 -2
View File
@@ -1,8 +1,7 @@
import escodegen from 'escodegen';
import { parse } from 'acorn';
import { walk } from 'estree-walker';
import core from '@strudel.cycles/core';
const { isNote } = core;
import { isNote } from '@strudel.cycles/core';
export function transpiler(input, options = {}) {
const { wrapAsync = false, addReturn = true, simpleLocs = false } = options;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/webaudio",
"version": "0.4.2",
"version": "0.4.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/webaudio",
"version": "0.4.2",
"version": "0.4.1",
"description": "Web Audio helpers for Strudel",
"main": "index.mjs",
"type": "module",
@@ -30,6 +30,6 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.1"
"@strudel.cycles/core": "^0.4.0"
}
}
+1 -3
View File
@@ -1,6 +1,4 @@
import core from '@strudel.cycles/core';
const { logger, toMidi } = core;
import { getAudioContext } from './index.mjs';
import { logger } from '@strudel.cycles/core';
const bufferCache = {}; // string: Promise<ArrayBuffer>
const loadCache = {}; // string: Promise<ArrayBuffer>
+3 -2
View File
@@ -4,8 +4,9 @@ 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/>.
*/
import strudel from '@strudel.cycles/core';
const { fromMidi, logger, toMidi } = strudel;
// import { Pattern, getFrequency, patternify2 } from '@strudel.cycles/core';
import * as strudel from '@strudel.cycles/core';
import { fromMidi, logger, toMidi } from '@strudel.cycles/core';
import './feedbackdelay.mjs';
import './reverb.mjs';
import { getSampleBufferSource } from './sampler.mjs';
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/webdirt",
"version": "0.4.1",
"version": "0.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/webdirt",
"version": "0.4.1",
"version": "0.4.0",
"description": "WebDirt integration for Strudel",
"main": "index.mjs",
"type": "module",
@@ -22,7 +22,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.1",
"@strudel.cycles/core": "^0.4.0",
"WebDirt": "github:dktr0/WebDirt"
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/xen",
"version": "0.4.1",
"version": "0.4.0",
"description": "Xenharmonic API for strudel",
"main": "index.mjs",
"scripts": {
@@ -24,6 +24,6 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.1"
"@strudel.cycles/core": "^0.4.0"
}
}
+1 -2
View File
@@ -5,8 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import Tune from './tunejs.js';
import core from '@strudel.cycles/core';
const { Pattern } = core;
import { Pattern } from '@strudel.cycles/core';
Pattern.prototype._tune = function (scale, tonic = 220) {
const tune = new Tune();
+1 -2
View File
@@ -4,8 +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/>.
*/
import core from '@strudel.cycles/core';
const { Pattern, mod } = core;
import { Pattern, mod } from '@strudel.cycles/core';
export function edo(name) {
if (!/^[1-9]+[0-9]*edo$/.test(name)) {
+2 -4
View File
@@ -4,8 +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/>.
*/
import core from '@strudel.cycles/core';
const { cleanupDraw, cleanupUi, evalScope, logger } = core;
import { cleanupDraw, cleanupUi, controls, evalScope, logger } from '@strudel.cycles/core';
import { CodeMirror, cx, flash, useHighlighting, useStrudel } from '@strudel.cycles/react';
import {
getAudioContext,
@@ -43,12 +42,11 @@ const modules = [
import('@strudel.cycles/osc'),
import('@strudel.cycles/serial'),
import('@strudel.cycles/soundfonts'),
import('@strudel.cycles/csound'),
];
evalScope(
// Tone,
core,
controls, // sadly, this cannot be exported from core direclty
{ WebDirt },
...modules,
);
+1 -2
View File
@@ -1,6 +1,5 @@
import XMarkIcon from '@heroicons/react/20/solid/XMarkIcon';
import core from '@strudel.cycles/core';
const { logger } = core;
import { logger } from '@strudel.cycles/core';
import { cx } from '@strudel.cycles/react';
import { nanoid } from 'nanoid';
import React, { useContext, useCallback, useLayoutEffect, useRef, useState } from 'react';
+1 -2
View File
@@ -1,5 +1,4 @@
import core from '@strudel.cycles/core';
const { Pattern, toMidi } = core;
import { Pattern, toMidi } from '@strudel.cycles/core';
import { samples } from '@strudel.cycles/webaudio';
export async function prebake({ isMock = false, baseDir = '.' } = {}) {
+15 -20
View File
@@ -5,8 +5,8 @@
// import * as tunes from './tunes.mjs';
// import { evaluate } from '@strudel.cycles/eval';
import { evaluate } from '@strudel.cycles/transpiler';
import strudel from '@strudel.cycles/core';
const { Pattern, evalScope } = strudel;
import { evalScope } from '@strudel.cycles/core';
import * as strudel from '@strudel.cycles/core';
import * as webaudio from '@strudel.cycles/webaudio';
import controls from '@strudel.cycles/core/controls.mjs';
// import gist from '@strudel.cycles/core/gist.js';
@@ -85,51 +85,48 @@ const toneHelpersMocked = {
highpass: mockNode,
};
Pattern.prototype.osc = function () {
strudel.Pattern.prototype.osc = function () {
return this;
};
Pattern.prototype.csound = function () {
strudel.Pattern.prototype.tone = function () {
return this;
};
Pattern.prototype.tone = function () {
return this;
};
Pattern.prototype.webdirt = function () {
strudel.Pattern.prototype.webdirt = function () {
return this;
};
// draw mock
Pattern.prototype.pianoroll = function () {
strudel.Pattern.prototype.pianoroll = function () {
return this;
};
// speak mock
Pattern.prototype.speak = function () {
strudel.Pattern.prototype.speak = function () {
return this;
};
// webaudio mock
Pattern.prototype.wave = function () {
strudel.Pattern.prototype.wave = function () {
return this;
};
Pattern.prototype.filter = function () {
strudel.Pattern.prototype.filter = function () {
return this;
};
Pattern.prototype.adsr = function () {
strudel.Pattern.prototype.adsr = function () {
return this;
};
Pattern.prototype.out = function () {
strudel.Pattern.prototype.out = function () {
return this;
};
Pattern.prototype.soundfont = function () {
strudel.Pattern.prototype.soundfont = function () {
return this;
};
// tune mock
Pattern.prototype.tune = function () {
strudel.Pattern.prototype.tune = function () {
return this;
};
Pattern.prototype.midi = function () {
strudel.Pattern.prototype.midi = function () {
return this;
};
@@ -159,7 +156,7 @@ const loadSoundfont = () => {};
evalScope(
// Tone,
strudel,
Pattern.prototype.bootstrap(),
strudel.Pattern.prototype.bootstrap(),
toneHelpersMocked,
uiHelpersMocked,
controls,
@@ -173,8 +170,6 @@ evalScope(
{
// gist,
// euclid,
csound: id,
loadOrc: id,
mini,
getDrawContext,
getAudioContext,
+401 -74
View File
@@ -7,7 +7,7 @@ exports[`renders tunes > tune: amensister 1`] = `
"3/8 -> 1/2: {\\"n\\":1,\\"s\\":\\"amencutup\\",\\"room\\":0.5}",
"3/4 -> 7/8: {\\"n\\":3,\\"s\\":\\"amencutup\\",\\"room\\":0.5}",
"7/8 -> 1/1: {\\"n\\":3,\\"s\\":\\"amencutup\\",\\"room\\":0.5}",
"1/4 -> 3/8: {\\"n\\":1,\\"speed\\":2,\\"delay\\":0.5,\\"s\\":\\"amencutup\\",\\"room\\":0.5}",
"1/4 -> 3/8: {\\"n\\":1,\\"speed\\":-2,\\"delay\\":0.5,\\"s\\":\\"amencutup\\",\\"room\\":0.5}",
"0/1 -> 1/8: {\\"note\\":\\"Eb1\\",\\"s\\":\\"sawtooth\\",\\"decay\\":0.1,\\"sustain\\":0,\\"gain\\":0.4,\\"cutoff\\":300.0066107586751,\\"resonance\\":10}",
"0/1 -> 1/8: {\\"note\\":\\"F1\\",\\"s\\":\\"sawtooth\\",\\"decay\\":0.1,\\"sustain\\":0,\\"gain\\":0.4,\\"cutoff\\":300.0066107586751,\\"resonance\\":10}",
"1/4 -> 3/8: {\\"note\\":\\"A1\\",\\"s\\":\\"sawtooth\\",\\"decay\\":0.1,\\"sustain\\":0,\\"gain\\":0.4,\\"cutoff\\":300.7878869297153,\\"resonance\\":10}",
@@ -39,35 +39,6 @@ exports[`renders tunes > tune: amensister 1`] = `
]
`;
exports[`renders tunes > tune: arpoon 1`] = `
[
"0/1 -> 2/3: {\\"note\\":55.000528662214684,\\"cutoff\\":509.2515887569149,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5249999999999999,\\"s\\":\\"piano\\",\\"clip\\":1}",
"0/1 -> 2/3: {\\"note\\":55.000528662214684,\\"cutoff\\":509.2515887569149,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5249999999999999,\\"s\\":\\"piano\\",\\"clip\\":1}",
"0/1 -> 2/3: {\\"note\\":55.000528662214684,\\"cutoff\\":509.2515887569149,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5249999999999999,\\"s\\":\\"piano\\",\\"clip\\":1}",
"0/1 -> 2/3: {\\"note\\":64.00052866221468,\\"cutoff\\":509.2515887569149,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5249999999999999,\\"s\\":\\"piano\\",\\"clip\\":1}",
"0/1 -> 2/3: {\\"note\\":64.00052866221468,\\"cutoff\\":509.2515887569149,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5249999999999999,\\"s\\":\\"piano\\",\\"clip\\":1}",
"0/1 -> 2/3: {\\"note\\":64.00052866221468,\\"cutoff\\":509.2515887569149,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5249999999999999,\\"s\\":\\"piano\\",\\"clip\\":1}",
"1/3 -> 1/1: {\\"note\\":60.003688107962134,\\"cutoff\\":564.5418893373399,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5799038105676657,\\"s\\":\\"piano\\",\\"clip\\":1}",
"1/3 -> 1/1: {\\"note\\":60.003688107962134,\\"cutoff\\":564.5418893373399,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5799038105676657,\\"s\\":\\"piano\\",\\"clip\\":1}",
"1/3 -> 1/1: {\\"note\\":60.003688107962134,\\"cutoff\\":564.5418893373399,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5799038105676657,\\"s\\":\\"piano\\",\\"clip\\":1}",
"2/3 -> 4/3: {\\"note\\":59.010756146386846,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}",
"2/3 -> 4/3: {\\"note\\":59.010756146386846,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}",
"2/3 -> 4/3: {\\"note\\":59.010756146386846,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}",
"2/3 -> 4/3: {\\"note\\":59.010756146386846,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}",
"2/3 -> 4/3: {\\"note\\":64.01075614638685,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}",
"2/3 -> 4/3: {\\"note\\":64.01075614638685,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}",
"2/3 -> 4/3: {\\"note\\":64.01075614638685,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}",
"2/3 -> 4/3: {\\"note\\":64.01075614638685,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}",
"0/1 -> 1/1: {\\"note\\":33,\\"s\\":\\"sawtooth\\",\\"clip\\":1,\\"cutoff\\":300}",
"0/1 -> 1/1: {\\"note\\":33.12,\\"s\\":\\"sawtooth\\",\\"clip\\":1,\\"cutoff\\":300}",
"0/1 -> 1/2: {\\"s\\":\\"bd\\",\\"bank\\":\\"RolandTR909\\",\\"gain\\":0.5}",
"1/2 -> 1/1: {\\"s\\":\\"bd\\",\\"bank\\":\\"RolandTR909\\",\\"gain\\":0.5}",
"1/2 -> 2/3: {\\"s\\":\\"hh\\",\\"bank\\":\\"RolandTR909\\",\\"gain\\":0.5}",
"2/3 -> 5/6: {\\"s\\":\\"hh\\",\\"bank\\":\\"RolandTR909\\",\\"gain\\":0.5}",
"5/6 -> 1/1: {\\"s\\":\\"hh\\",\\"bank\\":\\"RolandTR909\\",\\"gain\\":0.5}",
]
`;
exports[`renders tunes > tune: barryHarris 1`] = `
[
"0/1 -> 2/1: {\\"note\\":\\"C3\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.4722222222222222}",
@@ -347,6 +318,36 @@ exports[`renders tunes > tune: blippyRhodes 1`] = `
]
`;
exports[`renders tunes > tune: bossa 1`] = `
[
"0/1 -> 1/4: {\\"note\\":\\"Bb3\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.5185185185185186}",
"0/1 -> 1/4: {\\"note\\":\\"D4\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.537037037037037}",
"0/1 -> 1/4: {\\"note\\":\\"Eb4\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.5416666666666667}",
"0/1 -> 1/4: {\\"note\\":\\"G4\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.5601851851851851}",
"1/2 -> 5/4: {\\"note\\":\\"Bb3\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.5185185185185186}",
"1/2 -> 5/4: {\\"note\\":\\"D4\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.537037037037037}",
"1/2 -> 5/4: {\\"note\\":\\"Eb4\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.5416666666666667}",
"1/2 -> 5/4: {\\"note\\":\\"G4\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.5601851851851851}",
"0/1 -> 1/2: {\\"note\\":\\"C2\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.41666666666666663}",
"3/4 -> 7/8: {\\"note\\":\\"G2\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.44907407407407407}",
]
`;
exports[`renders tunes > tune: bossaRandom 1`] = `
[
"0/1 -> 1/4: {\\"note\\":\\"G4\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.5601851851851851}",
"0/1 -> 1/4: {\\"note\\":\\"B4\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.5787037037037037}",
"0/1 -> 1/4: {\\"note\\":\\"C5\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.5833333333333333}",
"0/1 -> 1/4: {\\"note\\":\\"E5\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.6018518518518519}",
"3/4 -> 1/1: {\\"note\\":\\"G4\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.5601851851851851}",
"3/4 -> 1/1: {\\"note\\":\\"B4\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.5787037037037037}",
"3/4 -> 1/1: {\\"note\\":\\"C5\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.5833333333333333}",
"3/4 -> 1/1: {\\"note\\":\\"E5\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.6018518518518519}",
"0/1 -> 1/2: {\\"note\\":\\"A2\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.45833333333333337}",
"1/2 -> 1/1: {\\"note\\":\\"A2\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.45833333333333337}",
]
`;
exports[`renders tunes > tune: bridgeIsOver 1`] = `
[
"0/1 -> 5/26: {\\"note\\":\\"B2\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.46759259259259256}",
@@ -1408,20 +1409,6 @@ exports[`renders tunes > tune: chop 1`] = `
]
`;
exports[`renders tunes > tune: csoundDemo 1`] = `
[
"0/1 -> 1/1: {\\"note\\":\\"D3\\"}",
"-1/4 -> 1/4: {\\"note\\":\\"Bb3\\"}",
"1/4 -> 5/4: {\\"note\\":\\"F3\\"}",
"0/1 -> 1/2: {\\"note\\":\\"F4\\"}",
"1/2 -> 3/2: {\\"note\\":\\"C4\\"}",
"-1/4 -> 1/4: {\\"note\\":\\"A4\\"}",
"1/4 -> 3/4: {\\"note\\":\\"A4\\"}",
"1/4 -> 3/4: {\\"note\\":\\"A4\\"}",
"3/4 -> 7/4: {\\"note\\":\\"E4\\"}",
]
`;
exports[`renders tunes > tune: delay 1`] = `
[
"0/1 -> 1/2: {\\"s\\":\\"bd\\",\\"delay\\":0,\\"delaytime\\":0.16,\\"delayfeedback\\":0.8,\\"speed\\":-1}",
@@ -7257,6 +7244,257 @@ exports[`renders tunes > tune: giantSteps 1`] = `
]
`;
exports[`renders tunes > tune: giantStepsReggae 1`] = `
[
"0/1 -> 25/32: {\\"note\\":\\"F#5\\"}",
"25/32 -> 25/16: {\\"note\\":\\"D5\\"}",
"25/64 -> 75/128: {\\"note\\":\\"A#3\\"}",
"25/64 -> 75/128: {\\"note\\":\\"C#4\\"}",
"25/64 -> 75/128: {\\"note\\":\\"D#4\\"}",
"25/64 -> 75/128: {\\"note\\":\\"F#4\\"}",
"0/1 -> 25/64: {\\"note\\":\\"B2\\"}",
"25/32 -> 75/64: {\\"note\\":\\"D2\\"}",
"25/32 -> 25/16: {\\"note\\":\\"D5\\"}",
"25/16 -> 75/32: {\\"note\\":\\"B4\\"}",
"75/64 -> 175/128: {\\"note\\":\\"F#3\\"}",
"75/64 -> 175/128: {\\"note\\":\\"B3\\"}",
"75/64 -> 175/128: {\\"note\\":\\"C4\\"}",
"75/64 -> 175/128: {\\"note\\":\\"E4\\"}",
"125/64 -> 275/128: {\\"note\\":\\"F#3\\"}",
"125/64 -> 275/128: {\\"note\\":\\"A3\\"}",
"125/64 -> 275/128: {\\"note\\":\\"B3\\"}",
"125/64 -> 275/128: {\\"note\\":\\"D4\\"}",
"25/32 -> 75/64: {\\"note\\":\\"D2\\"}",
"25/16 -> 125/64: {\\"note\\":\\"G2\\"}",
"25/16 -> 75/32: {\\"note\\":\\"B4\\"}",
"75/32 -> 25/8: {\\"note\\":\\"G4\\"}",
"125/64 -> 275/128: {\\"note\\":\\"F#3\\"}",
"125/64 -> 275/128: {\\"note\\":\\"A3\\"}",
"125/64 -> 275/128: {\\"note\\":\\"B3\\"}",
"125/64 -> 275/128: {\\"note\\":\\"D4\\"}",
"175/64 -> 375/128: {\\"note\\":\\"Ab3\\"}",
"175/64 -> 375/128: {\\"note\\":\\"C4\\"}",
"175/64 -> 375/128: {\\"note\\":\\"D4\\"}",
"175/64 -> 375/128: {\\"note\\":\\"G4\\"}",
"75/32 -> 175/64: {\\"note\\":\\"D2\\"}",
"75/32 -> 25/8: {\\"note\\":\\"G4\\"}",
"25/8 -> 75/16: {\\"note\\":\\"Bb4\\"}",
"225/64 -> 475/128: {\\"note\\":\\"G3\\"}",
"225/64 -> 475/128: {\\"note\\":\\"Bb3\\"}",
"225/64 -> 475/128: {\\"note\\":\\"D4\\"}",
"225/64 -> 475/128: {\\"note\\":\\"F4\\"}",
"25/8 -> 225/64: {\\"note\\":\\"Eb2\\"}",
"125/32 -> 275/64: {\\"note\\":\\"Bb2\\"}",
"25/8 -> 75/16: {\\"note\\":\\"Bb4\\"}",
"75/16 -> 175/32: {\\"note\\":\\"B4\\"}",
"275/64 -> 575/128: {\\"note\\":\\"G3\\"}",
"275/64 -> 575/128: {\\"note\\":\\"Bb3\\"}",
"275/64 -> 575/128: {\\"note\\":\\"D4\\"}",
"275/64 -> 575/128: {\\"note\\":\\"F4\\"}",
"125/32 -> 275/64: {\\"note\\":\\"Bb2\\"}",
"75/16 -> 325/64: {\\"note\\":\\"A2\\"}",
"75/16 -> 175/32: {\\"note\\":\\"B4\\"}",
"175/32 -> 25/4: {\\"note\\":\\"A4\\"}",
"325/64 -> 675/128: {\\"note\\":\\"G3\\"}",
"325/64 -> 675/128: {\\"note\\":\\"B3\\"}",
"325/64 -> 675/128: {\\"note\\":\\"C4\\"}",
"325/64 -> 675/128: {\\"note\\":\\"E4\\"}",
"375/64 -> 775/128: {\\"note\\":\\"F#3\\"}",
"375/64 -> 775/128: {\\"note\\":\\"B3\\"}",
"375/64 -> 775/128: {\\"note\\":\\"C4\\"}",
"375/64 -> 775/128: {\\"note\\":\\"E4\\"}",
"75/16 -> 325/64: {\\"note\\":\\"A2\\"}",
"175/32 -> 375/64: {\\"note\\":\\"D2\\"}",
"175/32 -> 25/4: {\\"note\\":\\"A4\\"}",
"25/4 -> 225/32: {\\"note\\":\\"D5\\"}",
"375/64 -> 775/128: {\\"note\\":\\"F#3\\"}",
"375/64 -> 775/128: {\\"note\\":\\"B3\\"}",
"375/64 -> 775/128: {\\"note\\":\\"C4\\"}",
"375/64 -> 775/128: {\\"note\\":\\"E4\\"}",
"425/64 -> 875/128: {\\"note\\":\\"F#3\\"}",
"425/64 -> 875/128: {\\"note\\":\\"A3\\"}",
"425/64 -> 875/128: {\\"note\\":\\"B3\\"}",
"425/64 -> 875/128: {\\"note\\":\\"D4\\"}",
"25/4 -> 425/64: {\\"note\\":\\"G2\\"}",
"25/4 -> 225/32: {\\"note\\":\\"D5\\"}",
"225/32 -> 125/16: {\\"note\\":\\"Bb4\\"}",
"125/16 -> 275/32: {\\"note\\":\\"G4\\"}",
"475/64 -> 975/128: {\\"note\\":\\"Ab3\\"}",
"475/64 -> 975/128: {\\"note\\":\\"C4\\"}",
"475/64 -> 975/128: {\\"note\\":\\"D4\\"}",
"475/64 -> 975/128: {\\"note\\":\\"G4\\"}",
"225/32 -> 475/64: {\\"note\\":\\"Bb2\\"}",
"125/16 -> 525/64: {\\"note\\":\\"Eb2\\"}",
"125/16 -> 275/32: {\\"note\\":\\"G4\\"}",
"275/32 -> 75/8: {\\"note\\":\\"Eb4\\"}",
"525/64 -> 1075/128: {\\"note\\":\\"G3\\"}",
"525/64 -> 1075/128: {\\"note\\":\\"Bb3\\"}",
"525/64 -> 1075/128: {\\"note\\":\\"D4\\"}",
"525/64 -> 1075/128: {\\"note\\":\\"F4\\"}",
"575/64 -> 1175/128: {\\"note\\":\\"E3\\"}",
"575/64 -> 1175/128: {\\"note\\":\\"G#3\\"}",
"575/64 -> 1175/128: {\\"note\\":\\"A#3\\"}",
"575/64 -> 1175/128: {\\"note\\":\\"D#4\\"}",
"125/16 -> 525/64: {\\"note\\":\\"Eb2\\"}",
"275/32 -> 575/64: {\\"note\\":\\"F#2\\"}",
"275/32 -> 75/8: {\\"note\\":\\"Eb4\\"}",
"75/8 -> 175/16: {\\"note\\":\\"F#4\\"}",
"575/64 -> 1175/128: {\\"note\\":\\"E3\\"}",
"575/64 -> 1175/128: {\\"note\\":\\"G#3\\"}",
"575/64 -> 1175/128: {\\"note\\":\\"A#3\\"}",
"575/64 -> 1175/128: {\\"note\\":\\"D#4\\"}",
"625/64 -> 1275/128: {\\"note\\":\\"A#3\\"}",
"625/64 -> 1275/128: {\\"note\\":\\"C#4\\"}",
"625/64 -> 1275/128: {\\"note\\":\\"D#4\\"}",
"625/64 -> 1275/128: {\\"note\\":\\"F#4\\"}",
"75/8 -> 625/64: {\\"note\\":\\"B2\\"}",
"75/8 -> 175/16: {\\"note\\":\\"F#4\\"}",
"175/16 -> 375/32: {\\"note\\":\\"G4\\"}",
"675/64 -> 1375/128: {\\"note\\":\\"A#3\\"}",
"675/64 -> 1375/128: {\\"note\\":\\"C#4\\"}",
"675/64 -> 1375/128: {\\"note\\":\\"D#4\\"}",
"675/64 -> 1375/128: {\\"note\\":\\"F#4\\"}",
"325/32 -> 675/64: {\\"note\\":\\"F#2\\"}",
"175/16 -> 725/64: {\\"note\\":\\"F2\\"}",
"175/16 -> 375/32: {\\"note\\":\\"G4\\"}",
"375/32 -> 25/2: {\\"note\\":\\"F4\\"}",
"725/64 -> 1475/128: {\\"note\\":\\"Ab3\\"}",
"725/64 -> 1475/128: {\\"note\\":\\"C4\\"}",
"725/64 -> 1475/128: {\\"note\\":\\"Eb4\\"}",
"725/64 -> 1475/128: {\\"note\\":\\"G4\\"}",
"175/16 -> 725/64: {\\"note\\":\\"F2\\"}",
"375/32 -> 775/64: {\\"note\\":\\"Bb2\\"}",
"375/32 -> 25/2: {\\"note\\":\\"F4\\"}",
"25/2 -> 225/16: {\\"note\\":\\"Bb4\\"}",
"775/64 -> 1575/128: {\\"note\\":\\"Ab3\\"}",
"775/64 -> 1575/128: {\\"note\\":\\"C4\\"}",
"775/64 -> 1575/128: {\\"note\\":\\"D4\\"}",
"775/64 -> 1575/128: {\\"note\\":\\"G4\\"}",
"825/64 -> 1675/128: {\\"note\\":\\"G3\\"}",
"825/64 -> 1675/128: {\\"note\\":\\"Bb3\\"}",
"825/64 -> 1675/128: {\\"note\\":\\"D4\\"}",
"825/64 -> 1675/128: {\\"note\\":\\"F4\\"}",
"375/32 -> 775/64: {\\"note\\":\\"Bb2\\"}",
"25/2 -> 825/64: {\\"note\\":\\"Eb2\\"}",
"25/2 -> 225/16: {\\"note\\":\\"Bb4\\"}",
"825/64 -> 1675/128: {\\"note\\":\\"G3\\"}",
"825/64 -> 1675/128: {\\"note\\":\\"Bb3\\"}",
"825/64 -> 1675/128: {\\"note\\":\\"D4\\"}",
"825/64 -> 1675/128: {\\"note\\":\\"F4\\"}",
"875/64 -> 1775/128: {\\"note\\":\\"G3\\"}",
"875/64 -> 1775/128: {\\"note\\":\\"Bb3\\"}",
"875/64 -> 1775/128: {\\"note\\":\\"D4\\"}",
"875/64 -> 1775/128: {\\"note\\":\\"F4\\"}",
"425/32 -> 875/64: {\\"note\\":\\"Bb2\\"}",
"25/2 -> 225/16: {\\"note\\":\\"Bb4\\"}",
"225/16 -> 475/32: {\\"note\\":\\"B4\\"}",
"475/32 -> 125/8: {\\"note\\":\\"A4\\"}",
"925/64 -> 1875/128: {\\"note\\":\\"G3\\"}",
"925/64 -> 1875/128: {\\"note\\":\\"B3\\"}",
"925/64 -> 1875/128: {\\"note\\":\\"C4\\"}",
"925/64 -> 1875/128: {\\"note\\":\\"E4\\"}",
"225/16 -> 925/64: {\\"note\\":\\"A2\\"}",
"475/32 -> 975/64: {\\"note\\":\\"D2\\"}",
"475/32 -> 125/8: {\\"note\\":\\"A4\\"}",
"125/8 -> 275/16: {\\"note\\":\\"D5\\"}",
"975/64 -> 1975/128: {\\"note\\":\\"F#3\\"}",
"975/64 -> 1975/128: {\\"note\\":\\"B3\\"}",
"975/64 -> 1975/128: {\\"note\\":\\"C4\\"}",
"975/64 -> 1975/128: {\\"note\\":\\"E4\\"}",
"475/32 -> 975/64: {\\"note\\":\\"D2\\"}",
"125/8 -> 1025/64: {\\"note\\":\\"G2\\"}",
"125/8 -> 275/16: {\\"note\\":\\"D5\\"}",
"1025/64 -> 2075/128: {\\"note\\":\\"F#3\\"}",
"1025/64 -> 2075/128: {\\"note\\":\\"A3\\"}",
"1025/64 -> 2075/128: {\\"note\\":\\"B3\\"}",
"1025/64 -> 2075/128: {\\"note\\":\\"D4\\"}",
"1075/64 -> 2175/128: {\\"note\\":\\"F#3\\"}",
"1075/64 -> 2175/128: {\\"note\\":\\"A3\\"}",
"1075/64 -> 2175/128: {\\"note\\":\\"B3\\"}",
"1075/64 -> 2175/128: {\\"note\\":\\"D4\\"}",
"125/8 -> 1025/64: {\\"note\\":\\"G2\\"}",
"525/32 -> 1075/64: {\\"note\\":\\"D2\\"}",
"125/8 -> 275/16: {\\"note\\":\\"D5\\"}",
"275/16 -> 575/32: {\\"note\\":\\"D#5\\"}",
"575/32 -> 75/4: {\\"note\\":\\"C#5\\"}",
"1125/64 -> 2275/128: {\\"note\\":\\"E3\\"}",
"1125/64 -> 2275/128: {\\"note\\":\\"G#3\\"}",
"1125/64 -> 2275/128: {\\"note\\":\\"B3\\"}",
"1125/64 -> 2275/128: {\\"note\\":\\"D#4\\"}",
"275/16 -> 1125/64: {\\"note\\":\\"C#2\\"}",
"575/32 -> 1175/64: {\\"note\\":\\"F#2\\"}",
"575/32 -> 75/4: {\\"note\\":\\"C#5\\"}",
"75/4 -> 325/16: {\\"note\\":\\"F#5\\"}",
"1175/64 -> 2375/128: {\\"note\\":\\"E3\\"}",
"1175/64 -> 2375/128: {\\"note\\":\\"G#3\\"}",
"1175/64 -> 2375/128: {\\"note\\":\\"A#3\\"}",
"1175/64 -> 2375/128: {\\"note\\":\\"D#4\\"}",
"575/32 -> 1175/64: {\\"note\\":\\"F#2\\"}",
"75/4 -> 1225/64: {\\"note\\":\\"B2\\"}",
"75/4 -> 325/16: {\\"note\\":\\"F#5\\"}",
"1225/64 -> 2475/128: {\\"note\\":\\"A#3\\"}",
"1225/64 -> 2475/128: {\\"note\\":\\"C#4\\"}",
"1225/64 -> 2475/128: {\\"note\\":\\"D#4\\"}",
"1225/64 -> 2475/128: {\\"note\\":\\"F#4\\"}",
"1275/64 -> 2575/128: {\\"note\\":\\"A#3\\"}",
"1275/64 -> 2575/128: {\\"note\\":\\"C#4\\"}",
"1275/64 -> 2575/128: {\\"note\\":\\"D#4\\"}",
"1275/64 -> 2575/128: {\\"note\\":\\"F#4\\"}",
"75/4 -> 1225/64: {\\"note\\":\\"B2\\"}",
"625/32 -> 1275/64: {\\"note\\":\\"F#2\\"}",
"75/4 -> 325/16: {\\"note\\":\\"F#5\\"}",
"325/16 -> 675/32: {\\"note\\":\\"G5\\"}",
"1275/64 -> 2575/128: {\\"note\\":\\"A#3\\"}",
"1275/64 -> 2575/128: {\\"note\\":\\"C#4\\"}",
"1275/64 -> 2575/128: {\\"note\\":\\"D#4\\"}",
"1275/64 -> 2575/128: {\\"note\\":\\"F#4\\"}",
"1325/64 -> 2675/128: {\\"note\\":\\"Ab3\\"}",
"1325/64 -> 2675/128: {\\"note\\":\\"C4\\"}",
"1325/64 -> 2675/128: {\\"note\\":\\"Eb4\\"}",
"1325/64 -> 2675/128: {\\"note\\":\\"G4\\"}",
"325/16 -> 1325/64: {\\"note\\":\\"F2\\"}",
"325/16 -> 675/32: {\\"note\\":\\"G5\\"}",
"675/32 -> 175/8: {\\"note\\":\\"F5\\"}",
"175/8 -> 375/16: {\\"note\\":\\"Bb5\\"}",
"1375/64 -> 2775/128: {\\"note\\":\\"Ab3\\"}",
"1375/64 -> 2775/128: {\\"note\\":\\"C4\\"}",
"1375/64 -> 2775/128: {\\"note\\":\\"D4\\"}",
"1375/64 -> 2775/128: {\\"note\\":\\"G4\\"}",
"675/32 -> 1375/64: {\\"note\\":\\"Bb2\\"}",
"175/8 -> 1425/64: {\\"note\\":\\"Eb2\\"}",
"175/8 -> 375/16: {\\"note\\":\\"Bb5\\"}",
"1425/64 -> 2875/128: {\\"note\\":\\"G3\\"}",
"1425/64 -> 2875/128: {\\"note\\":\\"Bb3\\"}",
"1425/64 -> 2875/128: {\\"note\\":\\"D4\\"}",
"1425/64 -> 2875/128: {\\"note\\":\\"F4\\"}",
"175/8 -> 1425/64: {\\"note\\":\\"Eb2\\"}",
"725/32 -> 1475/64: {\\"note\\":\\"Bb2\\"}",
"175/8 -> 375/16: {\\"note\\":\\"Bb5\\"}",
"375/16 -> 775/32: {\\"note\\":\\"F#5\\"}",
"1475/64 -> 2975/128: {\\"note\\":\\"G3\\"}",
"1475/64 -> 2975/128: {\\"note\\":\\"Bb3\\"}",
"1475/64 -> 2975/128: {\\"note\\":\\"D4\\"}",
"1475/64 -> 2975/128: {\\"note\\":\\"F4\\"}",
"1525/64 -> 3075/128: {\\"note\\":\\"E3\\"}",
"1525/64 -> 3075/128: {\\"note\\":\\"G#3\\"}",
"1525/64 -> 3075/128: {\\"note\\":\\"B3\\"}",
"1525/64 -> 3075/128: {\\"note\\":\\"D#4\\"}",
"725/32 -> 1475/64: {\\"note\\":\\"Bb2\\"}",
"375/16 -> 1525/64: {\\"note\\":\\"C#2\\"}",
"375/16 -> 775/32: {\\"note\\":\\"F#5\\"}",
"775/32 -> 3125/128: {\\"note\\":\\"F#5\\"}",
"1525/64 -> 3075/128: {\\"note\\":\\"E3\\"}",
"1525/64 -> 3075/128: {\\"note\\":\\"G#3\\"}",
"1525/64 -> 3075/128: {\\"note\\":\\"B3\\"}",
"1525/64 -> 3075/128: {\\"note\\":\\"D#4\\"}",
"1575/64 -> 3175/128: {\\"note\\":\\"E3\\"}",
"1575/64 -> 3175/128: {\\"note\\":\\"G#3\\"}",
"1575/64 -> 3175/128: {\\"note\\":\\"A#3\\"}",
"1575/64 -> 3175/128: {\\"note\\":\\"D#4\\"}",
"775/32 -> 1575/64: {\\"note\\":\\"F#2\\"}",
]
`;
exports[`renders tunes > tune: goodTimes 1`] = `
[
"0/1 -> 1/2: {\\"note\\":\\"F3\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.49537037037037035}",
@@ -8097,36 +8335,6 @@ exports[`renders tunes > tune: juxUndTollerei 1`] = `
]
`;
exports[`renders tunes > tune: loungeSponge 1`] = `
[
"0/1 -> 3/8: {\\"note\\":\\"B3\\",\\"cutoff\\":1396}",
"0/1 -> 3/8: {\\"note\\":\\"D4\\",\\"cutoff\\":1396}",
"0/1 -> 3/8: {\\"note\\":\\"E4\\",\\"cutoff\\":1396}",
"0/1 -> 3/8: {\\"note\\":\\"G4\\",\\"cutoff\\":1396}",
"3/8 -> 3/4: {\\"note\\":\\"B3\\",\\"cutoff\\":1396}",
"3/8 -> 3/4: {\\"note\\":\\"D4\\",\\"cutoff\\":1396}",
"3/8 -> 3/4: {\\"note\\":\\"E4\\",\\"cutoff\\":1396}",
"3/8 -> 3/4: {\\"note\\":\\"G4\\",\\"cutoff\\":1396}",
"3/4 -> 1/1: {\\"note\\":\\"B3\\",\\"cutoff\\":1396}",
"3/4 -> 1/1: {\\"note\\":\\"D4\\",\\"cutoff\\":1396}",
"3/4 -> 1/1: {\\"note\\":\\"E4\\",\\"cutoff\\":1396}",
"3/4 -> 1/1: {\\"note\\":\\"G4\\",\\"cutoff\\":1396}",
"0/1 -> 1/4: {\\"note\\":\\"C2\\",\\"gain\\":1}",
"1/4 -> 1/2: {\\"note\\":\\"C2\\",\\"gain\\":4}",
"1/2 -> 3/4: {\\"note\\":\\"C2\\",\\"gain\\":1}",
"3/4 -> 1/1: {\\"note\\":\\"C2\\",\\"gain\\":4}",
"0/1 -> 3/16: {\\"note\\":\\"A4\\"}",
"3/4 -> 15/16: {\\"note\\":\\"A5\\"}",
"-1/4 -> -1/16: {\\"note\\":\\"E5\\"}",
"1/2 -> 11/16: {\\"note\\":\\"C5\\"}",
"0/1 -> 1/2: {\\"s\\":\\"bd\\",\\"bank\\":\\"RolandTR909\\"}",
"1/2 -> 1/1: {\\"s\\":\\"bd\\",\\"bank\\":\\"RolandTR909\\"}",
"1/4 -> 1/2: {\\"s\\":\\"hh\\",\\"bank\\":\\"RolandTR909\\"}",
"3/4 -> 1/1: {\\"s\\":\\"hh\\",\\"bank\\":\\"RolandTR909\\"}",
"1/2 -> 1/1: {\\"s\\":\\"cp\\",\\"bank\\":\\"RolandTR909\\"}",
]
`;
exports[`renders tunes > tune: meltingsubmarine 1`] = `
[
"0/1 -> 3/2: {\\"s\\":\\"bd\\",\\"speed\\":0.7519542165100574}",
@@ -9362,6 +9570,125 @@ exports[`renders tunes > tune: swimming 1`] = `
]
`;
exports[`renders tunes > tune: swimmingWithSoundfonts 1`] = `
[
"0/1 -> 3/4: {\\"n\\":\\"F4\\",\\"s\\":\\"Kalimba: Ethnic\\"}",
"0/1 -> 3/4: {\\"n\\":\\"Bb4\\",\\"s\\":\\"Kalimba: Ethnic\\"}",
"0/1 -> 3/4: {\\"n\\":\\"D5\\",\\"s\\":\\"Kalimba: Ethnic\\"}",
"3/4 -> 5/4: {\\"n\\":\\"D4\\",\\"s\\":\\"Kalimba: Ethnic\\"}",
"3/4 -> 5/4: {\\"n\\":\\"G4\\",\\"s\\":\\"Kalimba: Ethnic\\"}",
"3/4 -> 5/4: {\\"n\\":\\"Bb4\\",\\"s\\":\\"Kalimba: Ethnic\\"}",
"0/1 -> 3/4: {\\"n\\":\\"G3\\",\\"s\\":\\"Acoustic Bass: Bass\\"}",
"3/4 -> 3/2: {\\"n\\":\\"G3\\",\\"s\\":\\"Acoustic Bass: Bass\\"}",
]
`;
exports[`renders tunes > tune: tetrisMini 1`] = `
[
"0/1 -> 1/2: {\\"note\\":\\"e5\\"}",
"1/2 -> 3/4: {\\"note\\":\\"b4\\"}",
"3/4 -> 1/1: {\\"note\\":\\"c5\\"}",
"0/1 -> 1/4: {\\"note\\":\\"e2\\"}",
"1/4 -> 1/2: {\\"note\\":\\"e3\\"}",
"1/2 -> 3/4: {\\"note\\":\\"e2\\"}",
"3/4 -> 1/1: {\\"note\\":\\"e3\\"}",
"1/1 -> 3/2: {\\"note\\":\\"d5\\"}",
"3/2 -> 7/4: {\\"note\\":\\"c5\\"}",
"7/4 -> 2/1: {\\"note\\":\\"b4\\"}",
"1/1 -> 5/4: {\\"note\\":\\"e2\\"}",
"5/4 -> 3/2: {\\"note\\":\\"e3\\"}",
"3/2 -> 7/4: {\\"note\\":\\"e2\\"}",
"7/4 -> 2/1: {\\"note\\":\\"e3\\"}",
"2/1 -> 5/2: {\\"note\\":\\"a4\\"}",
"5/2 -> 11/4: {\\"note\\":\\"a4\\"}",
"11/4 -> 3/1: {\\"note\\":\\"c5\\"}",
"2/1 -> 9/4: {\\"note\\":\\"a2\\"}",
"9/4 -> 5/2: {\\"note\\":\\"a3\\"}",
"5/2 -> 11/4: {\\"note\\":\\"a2\\"}",
"11/4 -> 3/1: {\\"note\\":\\"a3\\"}",
"3/1 -> 7/2: {\\"note\\":\\"e5\\"}",
"7/2 -> 15/4: {\\"note\\":\\"d5\\"}",
"15/4 -> 4/1: {\\"note\\":\\"c5\\"}",
"3/1 -> 13/4: {\\"note\\":\\"a2\\"}",
"13/4 -> 7/2: {\\"note\\":\\"a3\\"}",
"7/2 -> 15/4: {\\"note\\":\\"a2\\"}",
"15/4 -> 4/1: {\\"note\\":\\"a3\\"}",
"4/1 -> 9/2: {\\"note\\":\\"b4\\"}",
"19/4 -> 5/1: {\\"note\\":\\"c5\\"}",
"4/1 -> 17/4: {\\"note\\":\\"g#2\\"}",
"17/4 -> 9/2: {\\"note\\":\\"g#3\\"}",
"9/2 -> 19/4: {\\"note\\":\\"g#2\\"}",
"19/4 -> 5/1: {\\"note\\":\\"g#3\\"}",
"5/1 -> 11/2: {\\"note\\":\\"d5\\"}",
"11/2 -> 6/1: {\\"note\\":\\"e5\\"}",
"5/1 -> 21/4: {\\"note\\":\\"e2\\"}",
"21/4 -> 11/2: {\\"note\\":\\"e3\\"}",
"11/2 -> 23/4: {\\"note\\":\\"e2\\"}",
"23/4 -> 6/1: {\\"note\\":\\"e3\\"}",
"6/1 -> 13/2: {\\"note\\":\\"c5\\"}",
"13/2 -> 7/1: {\\"note\\":\\"a4\\"}",
"6/1 -> 25/4: {\\"note\\":\\"a2\\"}",
"25/4 -> 13/2: {\\"note\\":\\"a3\\"}",
"13/2 -> 27/4: {\\"note\\":\\"a2\\"}",
"27/4 -> 7/1: {\\"note\\":\\"a3\\"}",
"7/1 -> 15/2: {\\"note\\":\\"a4\\"}",
"7/1 -> 29/4: {\\"note\\":\\"a2\\"}",
"29/4 -> 15/2: {\\"note\\":\\"a3\\"}",
"15/2 -> 31/4: {\\"note\\":\\"b1\\"}",
"31/4 -> 8/1: {\\"note\\":\\"c2\\"}",
"33/4 -> 17/2: {\\"note\\":\\"d5\\"}",
"35/4 -> 9/1: {\\"note\\":\\"f5\\"}",
"8/1 -> 33/4: {\\"note\\":\\"d2\\"}",
"33/4 -> 17/2: {\\"note\\":\\"d3\\"}",
"17/2 -> 35/4: {\\"note\\":\\"d2\\"}",
"35/4 -> 9/1: {\\"note\\":\\"d3\\"}",
"9/1 -> 19/2: {\\"note\\":\\"a5\\"}",
"19/2 -> 39/4: {\\"note\\":\\"g5\\"}",
"39/4 -> 10/1: {\\"note\\":\\"f5\\"}",
"9/1 -> 37/4: {\\"note\\":\\"d2\\"}",
"37/4 -> 19/2: {\\"note\\":\\"d3\\"}",
"19/2 -> 39/4: {\\"note\\":\\"d2\\"}",
"39/4 -> 10/1: {\\"note\\":\\"d3\\"}",
"10/1 -> 21/2: {\\"note\\":\\"e5\\"}",
"43/4 -> 11/1: {\\"note\\":\\"c5\\"}",
"10/1 -> 41/4: {\\"note\\":\\"c2\\"}",
"41/4 -> 21/2: {\\"note\\":\\"c3\\"}",
"21/2 -> 43/4: {\\"note\\":\\"c2\\"}",
"43/4 -> 11/1: {\\"note\\":\\"c3\\"}",
"11/1 -> 23/2: {\\"note\\":\\"e5\\"}",
"23/2 -> 47/4: {\\"note\\":\\"d5\\"}",
"47/4 -> 12/1: {\\"note\\":\\"c5\\"}",
"11/1 -> 45/4: {\\"note\\":\\"c2\\"}",
"45/4 -> 23/2: {\\"note\\":\\"c3\\"}",
"23/2 -> 47/4: {\\"note\\":\\"c2\\"}",
"47/4 -> 12/1: {\\"note\\":\\"c3\\"}",
"12/1 -> 25/2: {\\"note\\":\\"b4\\"}",
"25/2 -> 51/4: {\\"note\\":\\"b4\\"}",
"51/4 -> 13/1: {\\"note\\":\\"c5\\"}",
"12/1 -> 49/4: {\\"note\\":\\"b1\\"}",
"49/4 -> 25/2: {\\"note\\":\\"b2\\"}",
"25/2 -> 51/4: {\\"note\\":\\"b1\\"}",
"51/4 -> 13/1: {\\"note\\":\\"b2\\"}",
"13/1 -> 27/2: {\\"note\\":\\"d5\\"}",
"27/2 -> 14/1: {\\"note\\":\\"e5\\"}",
"13/1 -> 53/4: {\\"note\\":\\"e2\\"}",
"53/4 -> 27/2: {\\"note\\":\\"e3\\"}",
"27/2 -> 55/4: {\\"note\\":\\"e2\\"}",
"55/4 -> 14/1: {\\"note\\":\\"e3\\"}",
"14/1 -> 29/2: {\\"note\\":\\"c5\\"}",
"29/2 -> 15/1: {\\"note\\":\\"a4\\"}",
"14/1 -> 57/4: {\\"note\\":\\"a1\\"}",
"57/4 -> 29/2: {\\"note\\":\\"a2\\"}",
"29/2 -> 59/4: {\\"note\\":\\"a1\\"}",
"59/4 -> 15/1: {\\"note\\":\\"a2\\"}",
"15/1 -> 31/2: {\\"note\\":\\"a4\\"}",
"15/1 -> 61/4: {\\"note\\":\\"a1\\"}",
"61/4 -> 31/2: {\\"note\\":\\"a2\\"}",
"31/2 -> 63/4: {\\"note\\":\\"a1\\"}",
"63/4 -> 16/1: {\\"note\\":\\"a2\\"}",
]
`;
exports[`renders tunes > tune: undergroundPlumber 1`] = `
[
"0/1 -> 3/16: {\\"s\\":\\"bd\\",\\"gain\\":0.7}",
-287
View File
@@ -162,290 +162,3 @@ stack(
drums.fast(2),
synths
).slow(2)`; */
export const tetrisMini = `note(\`[[e5 [b4 c5] d5 [c5 b4]]
[a4 [a4 c5] e5 [d5 c5]]
[b4 [~ c5] d5 e5]
[c5 a4 a4 ~]
[[~ d5] [~ f5] a5 [g5 f5]]
[e5 [~ c5] e5 [d5 c5]]
[b4 [b4 c5] d5 e5]
[c5 a4 a4 ~]],
[[e2 e3]*4]
[[a2 a3]*4]
[[g#2 g#3]*2 [e2 e3]*2]
[a2 a3 a2 a3 a2 a3 b1 c2]
[[d2 d3]*4]
[[c2 c3]*4]
[[b1 b2]*2 [e2 e3]*2]
[[a1 a2]*4]\`).slow(16)
`;
export const giantStepsReggae = `stack(
// melody
seq(
"[F#5 D5] [B4 G4] Bb4 [B4 A4]",
"[D5 Bb4] [G4 Eb4] F#4 [G4 F4]",
"Bb4 [B4 A4] D5 [D#5 C#5]",
"F#5 [G5 F5] Bb5 [F#5 [F#5 ~@3]]",
),
// chords
seq(
"[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]",
"[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]",
"Eb^7 [Am7 D7] G^7 [C#m7 F#7]",
"B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]"
)
.struct("~ [x ~]".fast(4*8))
.voicings(['E3', 'G4']),
// bass
seq(
"[B2 D2] [G2 D2] [Eb2 Bb2] [A2 D2]",
"[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]",
"[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]",
"[B2 F#2] [F2 Bb2] [Eb2 Bb2] [C#2 F#2]"
)
.struct("x ~".fast(4*8))
).slow(25).note()`;
// TODO:
/*
export const xylophoneCalling = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
const t = x => x.scaleTranspose("<0 2 4 3>/4").transpose(-2)
const s = x => x.scale(cat('C3 minor pentatonic','G3 minor pentatonic').slow(4))
const delay = new FeedbackDelay(1/8, .6).chain(vol(0.1), out());
const chorus = new Chorus(1,2.5,0.5).start();
stack(
// melody
"<<10 7> <8 3>>/4".struct("x*3").apply(s)
.scaleTranspose("<0 3 2> <1 4 3>")
.superimpose(scaleTranspose(2).early(1/8))
.apply(t).tone(polysynth().set({
...osc('triangle4'),
...adsr(0,.08,0)
}).chain(vol(0.2).connect(delay),chorus,out())).mask("<~@3 x>/16".early(1/8)),
// pad
"[1,3]/4".scale('G3 minor pentatonic').apply(t).tone(polysynth().set({
...osc('square2'),
...adsr(0.1,.4,0.8)
}).chain(vol(0.2),chorus,out())).mask("<~ x>/32"),
// xylophone
"c3,g3,c4".struct("<x*2 x>").fast("<1 <2!3 [4 8]>>").apply(s).scaleTranspose("<0 <1 [2 [3 <4 5>]]>>").apply(t).tone(polysynth().set({
...osc('sawtooth4'),
...adsr(0,.1,0)
}).chain(vol(0.4).connect(delay),out())).mask("<x@3 ~>/16".early(1/8)),
// bass
"c2 [c2 ~]*2".scale('C hirajoshi').apply(t).tone(synth({
...osc('sawtooth6'),
...adsr(0,.03,.4,.1)
}).chain(vol(0.4),out())),
// kick
"<c1!3 [c1 ~]*2>*2".tone(membrane().chain(vol(0.8),out())),
// snare
"~ <c3!7 [c3 c3*2]>".tone(noise().chain(vol(0.8),out())),
// hihat
"c3*4".transpose("[-24 0]*2").tone(metal(adsr(0,.02)).chain(vol(0.5).connect(delay),out()))
).slow(1)
// strudel disable-highlighting`;
*/
// TODO:
/*
export const sowhatelse = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
// mixer
const mix = (key) => vol({
chords: .2,
lead: 0.8,
bass: .4,
snare: .95,
kick: .9,
hihat: .35,
}[key]||0);
const delay = new FeedbackDelay(1/6, .3).chain(vol(.7), out());
const delay2 = new FeedbackDelay(1/6, .2).chain(vol(.15), out());
const chorus = new Chorus(1,2.5,0.5).start();
// instruments
const instr = (instrument) => ({
organ: polysynth().set({...osc('sawtooth4'), ...adsr(.01,.2,0)}).chain(mix('chords').connect(delay),out()),
lead: polysynth().set({...osc('triangle4'),...adsr(0.01,.05,0)}).chain(mix('lead').connect(delay2), out()),
bass: polysynth().set({...osc('sawtooth8'),...adsr(.02,.05,.3,.2)}).chain(mix('bass'),lowpass(3000), out()),
pad: polysynth().set({...osc('square2'),...adsr(0.1,.4,0.8)}).chain(vol(0.15),chorus,out()),
hihat: metal(adsr(0, .02, 0)).chain(mix('hihat'), out()),
snare: noise(adsr(0, .15, 0.01)).chain(mix('snare'), lowpass(5000), out()),
kick: membrane().chain(mix('kick'), out())
}[instrument]);
// harmony
const t = transpose("<0 0 1 0>/8");
const sowhat = scaleTranspose("0,3,6,9,11");
// track
stack(
"[<0 4 [3 [2 1]]>]/4".struct("[x]*3").mask("[~ x ~]").scale('D5 dorian').off(1/6, scaleTranspose(-7)).off(1/3, scaleTranspose(-5)).apply(t).tone(instr('lead')).mask("<~ ~ x x>/8"),
"<<e3 [~@2 a3]> <[d3 ~] [c3 f3] g3>>".scale('D dorian').apply(sowhat).apply(t).tone(instr('organ')).mask("<x x x ~>/8"),
"<[d2 [d2 ~]*3]!3 <a1*2 c2*3 [a1 e2]>>".apply(t).tone(instr('bass')),
"c1*6".tone(instr('hihat')),
"~ c3".tone(instr('snare')),
"<[c1@5 c1] <c1 [[c1@2 c1] ~] [c1 ~ c1] [c1!2 ~ c1!3]>>".tone(instr('kick')),
"[2,4]/4".scale('D dorian').apply(t).tone(instr('pad')).mask("<x x x ~>/8")
).fast(6/8)
// strudel disable-highlighting`;
*/
// TODO: rework tune to use freq
/*
export const jemblung = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
const delay = new FeedbackDelay(1/8, .6).chain(vol(0.15), out());
const snare = noise({type:'white',...adsr(0,0.2,0)}).chain(lowpass(5000),vol(1.8),out());
const s = polysynth().set({...osc('sawtooth4'),...adsr(0.01,.2,.6,0.2)}).chain(vol(.23).connect(delay),out());
stack(
stack(
"0 1 4 [3!2 5]".layer(
// chords
x=>x.add("0,3").duration("0.05!3 0.02"),
// bass
x=>x.add("-8").struct("x*8").duration(0.1)
),
// melody
"12 11*3 12 ~".duration(0.005)
)
.add("<0 1>")
.tune("jemblung2")
//.mul(22/5).round().xen("22edo")
//.mul(12/5).round().xen("12edo")
.tone(s),
// kick
"[c2 ~]*2".duration(0.05).tone(membrane().chain(out())),
// snare
"[~ c1]*2".early(0.001).tone(snare),
// hihat
"c2*8".tone(noise().chain(highpass(6000),vol(0.5).connect(delay),out())),
).slow(3)`;
*/
/*
// TODO: does not work on linux (at least for me..)
export const speakerman = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
backgroundImage('https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fi.ytimg.com%2Fvi%2FXR0rKqW3VwY%2Fmaxresdefault.jpg&f=1&nofb=1',
{ className:'darken', style:'background-size:cover'})
stack(
"[g3,bb3,d4] [f3,a3,c4] [c3,e3,g3]@2".slow(2).late(.1),
cat(
'Baker man',
'is baking bread',
'Baker man',
'is baking bread',
'Sagabona',
'kunjani wena',
'Sagabona',
'kunjani wena',
'The night train, is coming',
'got to keep on running',
'The night train, is coming',
'got to keep on running',
).speak("en zu en".slow(12), "<0 2 3 4 5 6>".slow(2)),
).slow(4)`;
*/
export const bossa = `const scales = sequence('C minor', ['D locrian', 'G phrygian'], 'Bb2 minor', ['C locrian','F phrygian']).slow(4)
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(),
"[~ [0 ~]] 0 [~ [4 ~]] 4".sub(7).restart(scales).scale(scales).early(.25)
).note().piano().slow(2)`;
/*
export const customTrigger = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
stack(
freq("55 [110,165] 110 [220,275]".mul("<1 <3/4 2/3>>").struct("x(3,8)").layer(x=>x.mul("1.006,.995"))),
freq("440(5,8)".legato(.18).mul("<1 3/4 2 2/3>")).gain(perlin.range(.2,.8))
).s("<sawtooth square>/2")
.onTrigger((t,hap,ct)=>{
const ac = Tone.getContext().rawContext;
t = ac.currentTime + t - ct;
const { freq, s, gain = 1 } = hap.value;
const master = ac.createGain();
master.gain.value = 0.1 * gain;
master.connect(ac.destination);
const o = ac.createOscillator();
o.type = s || 'triangle';
o.frequency.value = Number(freq);
o.connect(master);
o.start(t);
o.stop(t + hap.duration);
}).stack(s("bd(3,8),hh*4,~ sd").webdirt())`; */
export const swimmingWithSoundfonts = `// Koji Kondo - Swimming (Super Mario World)
stack(
n(
"~",
"~",
"~",
"A5 [F5@2 C5] [D5@2 F5] F5",
"[C5@2 F5] [F5@2 C6] A5 G5",
"A5 [F5@2 C5] [D5@2 F5] F5",
"[C5@2 F5] [Bb5 A5 G5] F5@2",
"A5 [F5@2 C5] [D5@2 F5] F5",
"[C5@2 F5] [F5@2 C6] A5 G5",
"A5 [F5@2 C5] [D5@2 F5] F5",
"[C5@2 F5] [Bb5 A5 G5] F5@2",
"A5 [F5@2 C5] A5 F5",
"Ab5 [F5@2 Ab5] G5@2",
"A5 [F5@2 C5] A5 F5",
"Ab5 [F5@2 C5] C6@2",
"A5 [F5@2 C5] [D5@2 F5] F5",
"[C5@2 F5] [Bb5 A5 G5] F5@2"
).s('Sitar: Ethnic'),
n(
"[F4,Bb4,D5] [[D4,G4,Bb4]@2 [Bb3,D4,F4]] [[G3,C4,E4]@2 [[Ab3,F4] [A3,Gb4]]] [Bb3,E4,G4]",
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, Bb3, Db3] [F3, Bb3, Db3]]",
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
"[~ [A3, C4, E4] [A3, C4, E4]] [~ [Ab3, C4, Eb4] [Ab3, C4, Eb4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [G3, C4, E4] [G3, C4, E4]]",
"[~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
"[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]",
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
"[~ [A3, C4, E4] [A3, C4, E4]] [~ [Ab3, C4, Eb4] [Ab3, C4, Eb4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [G3, C4, E4] [G3, C4, E4]]",
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
"[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]",
"[~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [A3, C4, F4] [A3, C4, F4]] [~ [A3, C4, F4] [A3, C4, F4]]",
"[~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [G3, Bb3, F4] [G3, Bb3, F4]] [~ [G3, Bb3, E4] [G3, Bb3, E4]]",
"[~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [A3, C4, F4] [A3, C4, F4]] [~ [A3, C4, F4] [A3, C4, F4]]",
"[~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [G3, Bb3, F4] [G3, Bb3, F4]] [~ [G3, Bb3, E4] [G3, Bb3, E4]]",
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
"[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]"
).s('Kalimba: Ethnic'),
n(
"[G3 G3 C3 E3]",
"[F2 D2 G2 C2]",
"[F2 D2 G2 C2]",
"[F2 A2 Bb2 B2]",
"[A2 Ab2 G2 C2]",
"[F2 A2 Bb2 B2]",
"[G2 C2 F2 F2]",
"[F2 A2 Bb2 B2]",
"[A2 Ab2 G2 C2]",
"[F2 A2 Bb2 B2]",
"[G2 C2 F2 F2]",
"[Bb2 Bb2 A2 A2]",
"[Ab2 Ab2 G2 [C2 D2 E2]]",
"[Bb2 Bb2 A2 A2]",
"[Ab2 Ab2 G2 [C2 D2 E2]]",
"[F2 A2 Bb2 B2]",
"[G2 C2 F2 F2]"
).s('Acoustic Bass: Bass')
).slow(51)
`;
export const bossaRandom = `const chords = "<Am7 Am7 Dm7 E7>"
const roots = chords.rootNotes(2)
stack(
chords.voicings(['F4', 'A5']).struct(
\` x@2 ~ x ~ ~ ~ x |
x? ~ ~ x@3 ~ x |
x? ~ ~ x ~ x@3\`),
roots.struct("x [~ x?0.2] x [~ x?] | x!4 | x@2 ~ ~ ~ x x x").transpose("0 7")
).slow(2).pianoroll().note().piano()`;
+309 -154
View File
@@ -4,8 +4,25 @@ 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/>.
*/
export const swimming = `// Koji Kondo - Swimming (Super Mario World)
stack(
export const tetrisMini = `note(\`[[e5 [b4 c5] d5 [c5 b4]]
[a4 [a4 c5] e5 [d5 c5]]
[b4 [~ c5] d5 e5]
[c5 a4 a4 ~]
[[~ d5] [~ f5] a5 [g5 f5]]
[e5 [~ c5] e5 [d5 c5]]
[b4 [b4 c5] d5 e5]
[c5 a4 a4 ~]],
[[e2 e3]*4]
[[a2 a3]*4]
[[g#2 g#3]*2 [e2 e3]*2]
[a2 a3 a2 a3 a2 a3 b1 c2]
[[d2 d3]*4]
[[c2 c3]*4]
[[b1 b2]*2 [e2 e3]*2]
[[a1 a2]*4]\`).slow(16)
`;
export const swimming = `stack(
seq(
"~",
"~",
@@ -66,8 +83,7 @@ stack(
).note().slow(51);
`;
export const giantSteps = `// John Coltrane - Giant Steps
stack(
export const giantSteps = `stack(
// melody
seq(
"[F#5 D5] [B4 G4] Bb4 [B4 A4]",
@@ -91,8 +107,34 @@ stack(
)
).slow(20).note()`;
export const zeldasRescue = `// Koji Kondo - Princess Zelda's Rescue
stack(
export const giantStepsReggae = `stack(
// melody
seq(
"[F#5 D5] [B4 G4] Bb4 [B4 A4]",
"[D5 Bb4] [G4 Eb4] F#4 [G4 F4]",
"Bb4 [B4 A4] D5 [D#5 C#5]",
"F#5 [G5 F5] Bb5 [F#5 [F#5 ~@3]]",
),
// chords
seq(
"[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]",
"[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]",
"Eb^7 [Am7 D7] G^7 [C#m7 F#7]",
"B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]"
)
.struct("~ [x ~]".fast(4*8))
.voicings(['E3', 'G4']),
// bass
seq(
"[B2 D2] [G2 D2] [Eb2 Bb2] [A2 D2]",
"[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]",
"[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]",
"[B2 F#2] [F2 Bb2] [Eb2 Bb2] [C#2 F#2]"
)
.struct("x ~".fast(4*8))
).slow(25).note()`;
export const zeldasRescue = `stack(
// melody
\`[B3@2 D4] [A3@2 [G3 A3]] [B3@2 D4] [A3]
[B3@2 D4] [A4@2 G4] [D4@2 [C4 B3]] [A3]
@@ -115,9 +157,7 @@ stack(
.room(1)
`;
export const caverave = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
const keys = x => x.s('sawtooth').cutoff(1200).gain(.5).attack(0).decay(.16).sustain(.3).release(.1);
export const caverave = `const keys = x => x.s('sawtooth').cutoff(1200).gain(.5).attack(0).decay(.16).sustain(.3).release(.1);
const drums = stack(
s("bd*2").mask("<x@7 ~>/8").gain(.8),
@@ -161,7 +201,90 @@ stack(
).s()
`;
export const barryHarris = `// adapted from a Barry Harris excercise
// TODO:
/*
export const xylophoneCalling = `const t = x => x.scaleTranspose("<0 2 4 3>/4").transpose(-2)
const s = x => x.scale(cat('C3 minor pentatonic','G3 minor pentatonic').slow(4))
const delay = new FeedbackDelay(1/8, .6).chain(vol(0.1), out());
const chorus = new Chorus(1,2.5,0.5).start();
stack(
// melody
"<<10 7> <8 3>>/4".struct("x*3").apply(s)
.scaleTranspose("<0 3 2> <1 4 3>")
.superimpose(scaleTranspose(2).early(1/8))
.apply(t).tone(polysynth().set({
...osc('triangle4'),
...adsr(0,.08,0)
}).chain(vol(0.2).connect(delay),chorus,out())).mask("<~@3 x>/16".early(1/8)),
// pad
"[1,3]/4".scale('G3 minor pentatonic').apply(t).tone(polysynth().set({
...osc('square2'),
...adsr(0.1,.4,0.8)
}).chain(vol(0.2),chorus,out())).mask("<~ x>/32"),
// xylophone
"c3,g3,c4".struct("<x*2 x>").fast("<1 <2!3 [4 8]>>").apply(s).scaleTranspose("<0 <1 [2 [3 <4 5>]]>>").apply(t).tone(polysynth().set({
...osc('sawtooth4'),
...adsr(0,.1,0)
}).chain(vol(0.4).connect(delay),out())).mask("<x@3 ~>/16".early(1/8)),
// bass
"c2 [c2 ~]*2".scale('C hirajoshi').apply(t).tone(synth({
...osc('sawtooth6'),
...adsr(0,.03,.4,.1)
}).chain(vol(0.4),out())),
// kick
"<c1!3 [c1 ~]*2>*2".tone(membrane().chain(vol(0.8),out())),
// snare
"~ <c3!7 [c3 c3*2]>".tone(noise().chain(vol(0.8),out())),
// hihat
"c3*4".transpose("[-24 0]*2").tone(metal(adsr(0,.02)).chain(vol(0.5).connect(delay),out()))
).slow(1)
// strudel disable-highlighting`;
*/
// TODO:
/*
export const sowhatelse = `// mixer
const mix = (key) => vol({
chords: .2,
lead: 0.8,
bass: .4,
snare: .95,
kick: .9,
hihat: .35,
}[key]||0);
const delay = new FeedbackDelay(1/6, .3).chain(vol(.7), out());
const delay2 = new FeedbackDelay(1/6, .2).chain(vol(.15), out());
const chorus = new Chorus(1,2.5,0.5).start();
// instruments
const instr = (instrument) => ({
organ: polysynth().set({...osc('sawtooth4'), ...adsr(.01,.2,0)}).chain(mix('chords').connect(delay),out()),
lead: polysynth().set({...osc('triangle4'),...adsr(0.01,.05,0)}).chain(mix('lead').connect(delay2), out()),
bass: polysynth().set({...osc('sawtooth8'),...adsr(.02,.05,.3,.2)}).chain(mix('bass'),lowpass(3000), out()),
pad: polysynth().set({...osc('square2'),...adsr(0.1,.4,0.8)}).chain(vol(0.15),chorus,out()),
hihat: metal(adsr(0, .02, 0)).chain(mix('hihat'), out()),
snare: noise(adsr(0, .15, 0.01)).chain(mix('snare'), lowpass(5000), out()),
kick: membrane().chain(mix('kick'), out())
}[instrument]);
// harmony
const t = transpose("<0 0 1 0>/8");
const sowhat = scaleTranspose("0,3,6,9,11");
// track
stack(
"[<0 4 [3 [2 1]]>]/4".struct("[x]*3").mask("[~ x ~]").scale('D5 dorian').off(1/6, scaleTranspose(-7)).off(1/3, scaleTranspose(-5)).apply(t).tone(instr('lead')).mask("<~ ~ x x>/8"),
"<<e3 [~@2 a3]> <[d3 ~] [c3 f3] g3>>".scale('D dorian').apply(sowhat).apply(t).tone(instr('organ')).mask("<x x x ~>/8"),
"<[d2 [d2 ~]*3]!3 <a1*2 c2*3 [a1 e2]>>".apply(t).tone(instr('bass')),
"c1*6".tone(instr('hihat')),
"~ c3".tone(instr('snare')),
"<[c1@5 c1] <c1 [[c1@2 c1] ~] [c1 ~ c1] [c1!2 ~ c1!3]>>".tone(instr('kick')),
"[2,4]/4".scale('D dorian').apply(t).tone(instr('pad')).mask("<x x x ~>/8")
).fast(6/8)
// strudel disable-highlighting`;
*/
export const barryHarris = `backgroundImage(
'https://media.npr.org/assets/img/2017/02/03/barryharris_600dpi_wide-7eb49998aa1af377d62bb098041624c0a0d1a454.jpg',
{style:'background-size:cover'})
"0,2,[7 6]"
.add("<0 1 2 3 4 5 7 8>")
.scale('C bebop major')
@@ -170,9 +293,7 @@ export const barryHarris = `// adapted from a Barry Harris excercise
.note().piano()
`;
export const blippyRhodes = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
samples({
export const blippyRhodes = `samples({
bd: 'samples/tidal/bd/BT0A0D0.wav',
sn: 'samples/tidal/sn/ST0T0S3.wav',
hh: 'samples/tidal/hh/000_hh3closedhh.wav',
@@ -211,9 +332,7 @@ stack(
.s('sawtooth').cutoff(600),
).fast(3/2)`;
export const wavyKalimba = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
samples({
export const wavyKalimba = `samples({
'kalimba': { c5:'https://freesound.org/data/previews/536/536549_11935698-lq.mp3' }
})
const scales = cat('C major', 'C mixolydian', 'F lydian', ['F minor', 'Db major'])
@@ -241,9 +360,37 @@ stack(
.delay(.2)
`;
export const festivalOfFingers = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
const chords = "<Cm7 Fm7 G7 F#7>";
// TODO: rework tune to use freq
/*
export const jemblung = `const delay = new FeedbackDelay(1/8, .6).chain(vol(0.15), out());
const snare = noise({type:'white',...adsr(0,0.2,0)}).chain(lowpass(5000),vol(1.8),out());
const s = polysynth().set({...osc('sawtooth4'),...adsr(0.01,.2,.6,0.2)}).chain(vol(.23).connect(delay),out());
stack(
stack(
"0 1 4 [3!2 5]".layer(
// chords
x=>x.add("0,3").duration("0.05!3 0.02"),
// bass
x=>x.add("-8").struct("x*8").duration(0.1)
),
// melody
"12 11*3 12 ~".duration(0.005)
)
.add("<0 1>")
.tune("jemblung2")
//.mul(22/5).round().xen("22edo")
//.mul(12/5).round().xen("12edo")
.tone(s),
// kick
"[c2 ~]*2".duration(0.05).tone(membrane().chain(out())),
// snare
"[~ c1]*2".early(0.001).tone(snare),
// hihat
"c2*8".tone(noise().chain(highpass(6000),vol(0.5).connect(delay),out())),
).slow(3)`;
*/
export const festivalOfFingers = `const chords = "<Cm7 Fm7 G7 F#7>";
stack(
chords.voicings().struct("x(3,8,-1)").velocity(.5).off(1/7,x=>x.transpose(12).velocity(.2)),
chords.rootNotes(2).struct("x(4,8,-2)"),
@@ -256,8 +403,7 @@ stack(
.note().piano()`;
// iter, echo, echoWith
export const undergroundPlumber = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos, inspired by Friendship - Let's not talk about it (1979) :)
export const undergroundPlumber = `backgroundImage('https://images.nintendolife.com/news/2016/08/video_exploring_the_funky_inspiration_for_the_super_mario_bros_underground_theme/large.jpg',{ className:'darken' })
samples({ bd: 'bd/BT0A0D0.wav', sn: 'sn/ST0T0S3.wav', hh: 'hh/000_hh3closedhh.wav', cp: 'cp/HANDCLP0.wav',
}, 'https://loophole-letters.vercel.app/samples/tidal/')
@@ -282,9 +428,7 @@ stack(
.fast(2/3)
.pianoroll({})`;
export const bridgeIsOver = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos, bassline by BDP - The Bridge Is Over
samples({mad:'https://freesound.org/data/previews/22/22274_109943-lq.mp3'})
export const bridgeIsOver = `samples({mad:'https://freesound.org/data/previews/22/22274_109943-lq.mp3'})
stack(
stack(
"c3*2 [[c3@1.4 bb2] ab2] gb2*2 <[[gb2@1.4 ab2] bb2] gb2>".legato(".5 1".fast(2)).velocity(.8),
@@ -303,9 +447,7 @@ stack(
.pianoroll()
`;
export const goodTimes = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
const scale = cat('C3 dorian','Bb2 major').slow(4);
export const goodTimes = `const scale = cat('C3 dorian','Bb2 major').slow(4);
stack(
"2*4".add(12).scale(scale)
.off(1/8,x=>x.scaleTranspose("2")).fast(2)
@@ -325,9 +467,7 @@ stack(
.slow(2)
.pianoroll({maxMidi:100,minMidi:20})`;
export const echoPiano = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
"<0 2 [4 6](3,4,1) 3*2>"
export const echoPiano = `"<0 2 [4 6](3,4,1) 3*2>"
.scale('D minor')
.color('salmon')
.off(1/4, x=>x.scaleTranspose(2).color('green'))
@@ -337,7 +477,7 @@ export const echoPiano = `// licensed with CC BY-NC-SA 4.0 https://creativecommo
.note().piano()
.pianoroll()`;
export const sml1 = `// Hirokazu Tanaka - World 1-1
export const sml1 = `
stack(
// melody
\`<
@@ -372,9 +512,30 @@ stack(
.legato(.5)
).fast(2).note()`;
export const randomBells = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
samples({
/*
// TODO: does not work on linux (at least for me..)
export const speakerman = `backgroundImage('https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fi.ytimg.com%2Fvi%2FXR0rKqW3VwY%2Fmaxresdefault.jpg&f=1&nofb=1',
{ className:'darken', style:'background-size:cover'})
stack(
"[g3,bb3,d4] [f3,a3,c4] [c3,e3,g3]@2".slow(2).late(.1),
cat(
'Baker man',
'is baking bread',
'Baker man',
'is baking bread',
'Sagabona',
'kunjani wena',
'Sagabona',
'kunjani wena',
'The night train, is coming',
'got to keep on running',
'The night train, is coming',
'got to keep on running',
).speak("en zu en".slow(12), "<0 2 3 4 5 6>".slow(2)),
).slow(4)`;
*/
export const randomBells = `samples({
bell: { c6: 'https://freesound.org/data/previews/411/411089_5121236-lq.mp3' },
bass: { d2: 'https://freesound.org/data/previews/608/608286_13074022-lq.mp3' }
})
@@ -395,9 +556,7 @@ stack(
.pianoroll({minMidi:20,maxMidi:120,background:'transparent'})
`;
export const waa2 = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
n(
export const waa2 = `n(
"a4 [a3 c3] a3 c3"
.sub("<7 12 5 12>".slow(2))
.off(1/4,x=>x.add(7))
@@ -411,9 +570,7 @@ n(
.room(.5)
`;
export const hyperpop = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
const lfo = cosine.slow(15);
export const hyperpop = `const lfo = cosine.slow(15);
const lfo2 = sine.slow(16);
const filter1 = x=>x.cutoff(lfo2.range(300,3000));
const filter2 = x=>x.hcutoff(lfo.range(1000,6000)).cutoff(4000)
@@ -462,9 +619,7 @@ stack(
).slow(2)
// strudel disable-highlighting`;
export const festivalOfFingers3 = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
"[-7*3],0,2,6,[8 7]"
export const festivalOfFingers3 = `"[-7*3],0,2,6,[8 7]"
.echoWith(4,1/4, (x,n)=>x
.add(n*7)
.velocity(1/(n+1))
@@ -480,9 +635,33 @@ export const festivalOfFingers3 = `// licensed with CC BY-NC-SA 4.0 https://crea
.note().piano()
//.pianoroll({maxMidi:160})`;
export const meltingsubmarine = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
samples({
export const bossa = `
const scales = sequence('C minor', ['D locrian', 'G phrygian'], 'Bb2 minor', ['C locrian','F phrygian']).slow(4)
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(),
"[~ [0 ~]] 0 [~ [4 ~]] 4".sub(7).restart(scales).scale(scales).early(.25)
).note().piano().slow(2)`;
/*
export const customTrigger = `stack(
freq("55 [110,165] 110 [220,275]".mul("<1 <3/4 2/3>>").struct("x(3,8)").layer(x=>x.mul("1.006,.995"))),
freq("440(5,8)".legato(.18).mul("<1 3/4 2 2/3>")).gain(perlin.range(.2,.8))
).s("<sawtooth square>/2")
.onTrigger((t,hap,ct)=>{
const ac = Tone.getContext().rawContext;
t = ac.currentTime + t - ct;
const { freq, s, gain = 1 } = hap.value;
const master = ac.createGain();
master.gain.value = 0.1 * gain;
master.connect(ac.destination);
const o = ac.createOscillator();
o.type = s || 'triangle';
o.frequency.value = Number(freq);
o.connect(master);
o.start(t);
o.stop(t + hap.duration);
}).stack(s("bd(3,8),hh*4,~ sd").webdirt())`; */
export const meltingsubmarine = `samples({
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'],
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
@@ -522,9 +701,68 @@ stack(
)
.slow(3/2)`;
export const outroMusic = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
samples({
export const swimmingWithSoundfonts = `stack(
n(
"~",
"~",
"~",
"A5 [F5@2 C5] [D5@2 F5] F5",
"[C5@2 F5] [F5@2 C6] A5 G5",
"A5 [F5@2 C5] [D5@2 F5] F5",
"[C5@2 F5] [Bb5 A5 G5] F5@2",
"A5 [F5@2 C5] [D5@2 F5] F5",
"[C5@2 F5] [F5@2 C6] A5 G5",
"A5 [F5@2 C5] [D5@2 F5] F5",
"[C5@2 F5] [Bb5 A5 G5] F5@2",
"A5 [F5@2 C5] A5 F5",
"Ab5 [F5@2 Ab5] G5@2",
"A5 [F5@2 C5] A5 F5",
"Ab5 [F5@2 C5] C6@2",
"A5 [F5@2 C5] [D5@2 F5] F5",
"[C5@2 F5] [Bb5 A5 G5] F5@2"
).s('Sitar: Ethnic'),
n(
"[F4,Bb4,D5] [[D4,G4,Bb4]@2 [Bb3,D4,F4]] [[G3,C4,E4]@2 [[Ab3,F4] [A3,Gb4]]] [Bb3,E4,G4]",
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, Bb3, Db3] [F3, Bb3, Db3]]",
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
"[~ [A3, C4, E4] [A3, C4, E4]] [~ [Ab3, C4, Eb4] [Ab3, C4, Eb4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [G3, C4, E4] [G3, C4, E4]]",
"[~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
"[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]",
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
"[~ [A3, C4, E4] [A3, C4, E4]] [~ [Ab3, C4, Eb4] [Ab3, C4, Eb4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [G3, C4, E4] [G3, C4, E4]]",
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
"[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]",
"[~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [A3, C4, F4] [A3, C4, F4]] [~ [A3, C4, F4] [A3, C4, F4]]",
"[~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [G3, Bb3, F4] [G3, Bb3, F4]] [~ [G3, Bb3, E4] [G3, Bb3, E4]]",
"[~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [A3, C4, F4] [A3, C4, F4]] [~ [A3, C4, F4] [A3, C4, F4]]",
"[~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [G3, Bb3, F4] [G3, Bb3, F4]] [~ [G3, Bb3, E4] [G3, Bb3, E4]]",
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
"[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]"
).s('Kalimba: Ethnic'),
n(
"[G3 G3 C3 E3]",
"[F2 D2 G2 C2]",
"[F2 D2 G2 C2]",
"[F2 A2 Bb2 B2]",
"[A2 Ab2 G2 C2]",
"[F2 A2 Bb2 B2]",
"[G2 C2 F2 F2]",
"[F2 A2 Bb2 B2]",
"[A2 Ab2 G2 C2]",
"[F2 A2 Bb2 B2]",
"[G2 C2 F2 F2]",
"[Bb2 Bb2 A2 A2]",
"[Ab2 Ab2 G2 [C2 D2 E2]]",
"[Bb2 Bb2 A2 A2]",
"[Ab2 Ab2 G2 [C2 D2 E2]]",
"[F2 A2 Bb2 B2]",
"[G2 C2 F2 F2]"
).s('Acoustic Bass: Bass')
).slow(51)
`;
export const outroMusic = `samples({
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'],
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
@@ -551,9 +789,7 @@ samples({
//.pianoroll({autorange:1,vertical:1,fold:0})
`;
export const bassFuge = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
samples({ flbass: ['00_c2_finger_long_neck.wav','01_c2_finger_short_neck.wav','02_c2_finger_long_bridge.wav','03_c2_finger_short_bridge.wav','04_c2_pick_long.wav','05_c2_pick_short.wav','06_c2_palm_mute.wav'] },
export const bassFuge = `samples({ flbass: ['00_c2_finger_long_neck.wav','01_c2_finger_short_neck.wav','02_c2_finger_long_bridge.wav','03_c2_finger_short_bridge.wav','04_c2_pick_long.wav','05_c2_pick_short.wav','06_c2_palm_mute.wav'] },
'github:cleary/samples-flbass/main/')
samples({
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'],
@@ -577,9 +813,18 @@ x=>x.add(7).color('steelblue')
.stack(s("bd:1*2,~ sd:0,[~ hh:0]*2"))
.pianoroll({vertical:1})`;
export const chop = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
samples({ p: 'https://cdn.freesound.org/previews/648/648433_11943129-lq.mp3' })
export const bossaRandom = `const chords = "<Am7 Am7 Dm7 E7>"
const roots = chords.rootNotes(2)
stack(
chords.voicings(['F4', 'A5']).struct(
\` x@2 ~ x ~ ~ ~ x |
x? ~ ~ x@3 ~ x |
x? ~ ~ x ~ x@3\`),
roots.struct("x [~ x?0.2] x [~ x?] | x!4 | x@2 ~ ~ ~ x x x").transpose("0 7")
).slow(2).pianoroll().note().piano()`;
export const chop = `samples({ p: 'https://cdn.freesound.org/previews/648/648433_11943129-lq.mp3' })
s("p")
.loopAt(32,1)
@@ -590,18 +835,14 @@ s("p")
.sustain(.6)
`;
export const delay = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
stack(
export const delay = `stack(
s("bd <sd cp>")
.delay("<0 .5>")
.delaytime(".16 | .33")
.delayfeedback(".6 | .8")
).sometimes(x=>x.speed("-1"))`;
export const orbit = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
stack(
export const orbit = `stack(
s("bd <sd cp>")
.delay(.5)
.delaytime(.33)
@@ -613,9 +854,7 @@ stack(
.orbit(2)
).sometimes(x=>x.speed("-1"))`;
export const belldub = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
samples({ bell: {b4:'https://cdn.freesound.org/previews/339/339809_5121236-lq.mp3'}})
export const belldub = `samples({ bell: {b4:'https://cdn.freesound.org/previews/339/339809_5121236-lq.mp3'}})
// "Hand Bells, B, Single.wav" by InspectorJ (www.jshaw.co.uk) of Freesound.org
stack(
// bass
@@ -646,9 +885,7 @@ stack(
.mask("<1 0>/8")
).slow(5)`;
export const dinofunk = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
samples({bass:'https://cdn.freesound.org/previews/614/614637_2434927-hq.mp3',
export const dinofunk = `samples({bass:'https://cdn.freesound.org/previews/614/614637_2434927-hq.mp3',
dino:{b4:'https://cdn.freesound.org/previews/316/316403_5123851-hq.mp3'}})
stack(
@@ -666,9 +903,7 @@ note("Abm7".voicings(['c3','a4']).struct("x(3,8,1)".slow(2))),
note("<b4 eb4>").s('dino').delay(.8).slow(8).room(.5)
)`;
export const sampleDemo = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
stack(
export const sampleDemo = `stack(
// percussion
s("[woodblock:1 woodblock:2*2] snare_rim:0,gong/8,brakedrum:1(3,8),~@3 cowbell:3")
.sometimes(x=>x.speed(2)),
@@ -682,9 +917,7 @@ stack(
.release(.1).room(.5)
)`;
export const holyflute = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
"c3 eb3(3,8) c4/2 g3*2"
export const holyflute = `"c3 eb3(3,8) c4/2 g3*2"
.superimpose(
x=>x.slow(2).add(12),
x=>x.slow(4).sub(5)
@@ -695,9 +928,7 @@ export const holyflute = `// licensed with CC BY-NC-SA 4.0 https://creativecommo
.pianoroll({fold:0,autorange:0,vertical:0,cycles:12,smear:0,minMidi:40})
`;
export const flatrave = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
stack(
export const flatrave = `stack(
s("bd*2,~ [cp,sd]").bank('RolandTR909'),
s("hh:1*4").sometimes(fast("2"))
@@ -718,9 +949,7 @@ stack(
.decay(.05).sustain(0).delay(.2).degradeBy(.5).mask("<0 1>/16")
)`;
export const amensister = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
await samples('github:tidalcycles/Dirt-Samples/master')
export const amensister = `await samples('github:tidalcycles/Dirt-Samples/master')
stack(
// amen
@@ -752,9 +981,7 @@ stack(
n("0 1").s("east").delay(.5).degradeBy(.8).speed(rand.range(.5,1.5))
).reset("<x@7 x(5,8)>")`;
export const juxUndTollerei = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
note("c3 eb3 g3 bb3").palindrome()
export const juxUndTollerei = `note("c3 eb3 g3 bb3").palindrome()
.s('sawtooth')
.jux(x=>x.rev().color('green').s('sawtooth'))
.off(1/4, x=>x.add(note("<7 12>/2")).slow(2).late(.005).s('triangle'))
@@ -764,75 +991,3 @@ note("c3 eb3 g3 bb3").palindrome()
.room(.6)
.delay(.5).delaytime(.1).delayfeedback(.4)
.pianoroll()`;
export const csoundDemo = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
await csound\`
instr CoolSynth
iduration = p3
ifreq = p4
igain = p5
ioct = octcps(ifreq)
kpwm = oscili(.05, 8)
asig = vco2(igain, ifreq, 4, .5 + kpwm)
asig += vco2(igain, ifreq * 2)
idepth = 2
acut = transegr:a(0, .005, 0, idepth, .06, -4.2, 0.001, .01, -4.2, 0) ; filter envelope
asig = zdf_2pole(asig, cpsoct(ioct + acut + 2), 0.5)
iattack = .01
isustain = .5
idecay = .1
irelease = .1
asig *= linsegr:a(0, iattack, 1, idecay, isustain, iduration, isustain, irelease, 0)
out(asig, asig)
endin\`
"<0 2 [4 6](3,4,1) 3*2>"
.off(1/4, add(2))
.off(1/2, add(6))
.scale('D minor')
.note()
//.pianoroll()
.csound('CoolSynth')`;
export const loungeSponge = `
// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos
// livecode.orc by Steven Yi
await loadOrc('github:kunstmusik/csound-live-code/master/livecode.orc')
stack(
note("<C^7 A7 Dm7 Fm7>/2".voicings())
.cutoff(sine.range(500,2000).round().slow(16))
.euclidLegato(3,8).csound('FM1')
,
note("<C2 A1 D2 F2>/2").ply(8).csound('Bass').gain("1 4 1 4")
,
note("0 7 [4 3] 2".fast(2/3).off(".25 .125",add("<2 4 -3 -1>"))
.slow(2).scale('A4 minor'))
.legato(.25).csound('SynHarp')
,
s("bd*2,[~ hh]*2,~ cp").bank('RolandTR909')
)`;
export const arpoon = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// "Arpoon" by Felix Roos
await samples('github:tidalcycles/Dirt-Samples/master')
"<<Am7 C^7> C7 F^7 [Fm7 E7b9]>".voicings()
.arp("[0,3] 2 [1,3] 2".fast(3)).legato(2)
.add(perlin.range(0,0.2)).sub("<0 -12>/8")
.note().cutoff(perlin.range(500,4000)).resonance(12)
.gain("<.5 .8>*16")
.decay(.16).sustain(0.5)
.delay(.2)
.room(.5).pan(sine.range(.3,.6))
.s('piano').clip(1)
.stack("<<A1 C2>!2 F2 [F2 E2]>".add.out("0 -5".fast(2)).add("0,.12").note().s('sawtooth').clip(1).cutoff(300))
.slow(4)
.stack(s("bd*4, [~ [hh hh? hh?]]*2,~ [sd ~ [sd:2? bd?]]").arp("0|1").bank('RolandTR909').gain(.5).slow(2))
`;
+4 -4
View File
@@ -1,4 +1,4 @@
import core from '@strudel.cycles/core';
import { evalScope, controls } from '@strudel.cycles/core';
import { MiniRepl as _MiniRepl } from '@strudel.cycles/react';
import { samples } from '@strudel.cycles/webaudio';
import { prebake } from '../repl/src/prebake.mjs';
@@ -7,9 +7,9 @@ fetch('https://strudel.tidalcycles.org/EmuSP12.json')
.then((res) => res.json())
.then((json) => samples(json, 'https://strudel.tidalcycles.org/EmuSP12/'));
core.evalScope(
core,
// import('@strudel.cycles/core'),
evalScope(
controls,
import('@strudel.cycles/core'),
// import('@strudel.cycles/tone'),
import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'),
+1 -1
View File
@@ -18,7 +18,7 @@ function renderAsMDX(name) {
}
return `### ${item.longname}
${item.description.replaceAll(/\{@link ([a-zA-Z]+)?#?([a-zA-Z]*)\}/g, (_, a, b) => {
${item.description.replaceAll(/\{\@link ([a-zA-Z]+)?\#?([a-zA-Z]*)\}/g, (_, a, b) => {
// console.log(_, 'a', a, 'b', b);
return `<a href="#${a}${b ? `-${b}` : ''}">${a}${b ? `#${b}` : ''}</a>`;
})}
@@ -232,6 +232,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`] = `
[
"0/1 -> 1/2: {\\"s\\":\\"bd\\",\\"bandf\\":1000}",
@@ -765,6 +782,27 @@ exports[`runs examples > example "dry" example index 0 1`] = `
]
`;
exports[`runs examples > example "each" example index 0 1`] = `
[
"0/1 -> 1/4: {\\"note\\":\\"c3\\"}",
"1/4 -> 1/2: {\\"note\\":\\"d3\\"}",
"1/2 -> 3/4: {\\"note\\":\\"e3\\"}",
"3/4 -> 1/1: {\\"note\\":\\"g3\\"}",
"1/1 -> 5/4: {\\"note\\":\\"c3\\"}",
"5/4 -> 3/2: {\\"note\\":\\"d3\\"}",
"3/2 -> 7/4: {\\"note\\":\\"e3\\"}",
"7/4 -> 2/1: {\\"note\\":\\"g3\\"}",
"2/1 -> 9/4: {\\"note\\":\\"c3\\"}",
"9/4 -> 5/2: {\\"note\\":\\"d3\\"}",
"5/2 -> 11/4: {\\"note\\":\\"e3\\"}",
"11/4 -> 3/1: {\\"note\\":\\"g3\\"}",
"15/4 -> 4/1: {\\"note\\":\\"c3\\"}",
"7/2 -> 15/4: {\\"note\\":\\"d3\\"}",
"13/4 -> 7/2: {\\"note\\":\\"e3\\"}",
"3/1 -> 13/4: {\\"note\\":\\"g3\\"}",
]
`;
exports[`runs examples > example "early" example index 0 1`] = `
[
"0/1 -> 1/2: {\\"s\\":\\"bd\\"}",
@@ -1509,6 +1547,27 @@ exports[`runs examples > example "every" example index 0 1`] = `
]
`;
exports[`runs examples > example "every" example index 0 2`] = `
[
"3/4 -> 1/1: {\\"note\\":\\"c3\\"}",
"1/2 -> 3/4: {\\"note\\":\\"d3\\"}",
"1/4 -> 1/2: {\\"note\\":\\"e3\\"}",
"0/1 -> 1/4: {\\"note\\":\\"g3\\"}",
"1/1 -> 5/4: {\\"note\\":\\"c3\\"}",
"5/4 -> 3/2: {\\"note\\":\\"d3\\"}",
"3/2 -> 7/4: {\\"note\\":\\"e3\\"}",
"7/4 -> 2/1: {\\"note\\":\\"g3\\"}",
"2/1 -> 9/4: {\\"note\\":\\"c3\\"}",
"9/4 -> 5/2: {\\"note\\":\\"d3\\"}",
"5/2 -> 11/4: {\\"note\\":\\"e3\\"}",
"11/4 -> 3/1: {\\"note\\":\\"g3\\"}",
"3/1 -> 13/4: {\\"note\\":\\"c3\\"}",
"13/4 -> 7/2: {\\"note\\":\\"d3\\"}",
"7/2 -> 15/4: {\\"note\\":\\"e3\\"}",
"15/4 -> 4/1: {\\"note\\":\\"g3\\"}",
]
`;
exports[`runs examples > example "fast" example index 0 1`] = `
[
"0/1 -> 1/4: {\\"s\\":\\"bd\\"}",
+24
View File
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+33
View File
@@ -0,0 +1,33 @@
# Strudel Workshop
This may become a resource to teach strudel in workshop format.
## Install
From the root of the strudel repo:
```sh
npm i # install strudel project dependencies
cd workshop && npm i # install workshop dependencies
npm run dev # run dev server
```
This assumes you have node 16+ installed.
## How to write
While running the dev server, go to `workshop.mdx` to write!
When the file is saved, the browser will hot reload the file and reveal the changes instantly.
You can use normal markdown + JSX, which enables using the MiniRepl component.
You could also add any react component and import it to the mdx file.
## Build
Running
```sh
npm run build
```
..will output a single `index.html` file to `dist`. This file can either be opened directly in the browser (locally) or uploaded to some server.
+12
View File
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Strudel Workshop</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
+5982
View File
File diff suppressed because it is too large Load Diff
+29
View File
@@ -0,0 +1,29 @@
{
"name": "workshop",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --open",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@mdx-js/mdx": "^1.6.22",
"@mdx-js/react": "^1.6.22",
"@tailwindcss/typography": "^0.5.8",
"@types/react": "^18.0.24",
"@types/react-dom": "^18.0.8",
"@vitejs/plugin-react": "^2.2.0",
"autoprefixer": "^10.4.13",
"postcss": "^8.4.19",
"tailwindcss": "^3.2.4",
"vite": "^3.2.3",
"vite-plugin-mdx": "^3.5.11",
"vite-plugin-singlefile": "^0.13.1"
}
}
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
+16
View File
@@ -0,0 +1,16 @@
import { initAudioOnFirstClick } from '@strudel.cycles/webaudio';
import Workshop from './workshop.mdx';
initAudioOnFirstClick();
function App() {
return (
<div className="bg-slate-900 w-screen h-screen overflow-auto">
<main className="p-4 pl-6 max-w-3xl prose prose-invert">
<Workshop />
</main>
</div>
);
}
export default App;
+22
View File
@@ -0,0 +1,22 @@
import { evalScope, controls } from '@strudel.cycles/core';
import { MiniRepl as _MiniRepl } from '@strudel.cycles/react';
import { samples } from '@strudel.cycles/webaudio';
import '@strudel.cycles/react/dist/style.css';
samples('github:tidalcycles/Dirt-Samples/master')
evalScope(
controls,
import('@strudel.cycles/core'),
// import('@strudel.cycles/tone'),
import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/midi'),
// import('@strudel.cycles/xen'),
import('@strudel.cycles/webaudio'),
import('@strudel.cycles/osc'),
);
export function MiniRepl({ tune }) {
return <_MiniRepl tune={tune} hideOutsideView={true} />;
}
+3
View File
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
+11
View File
@@ -0,0 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
);
+49
View File
@@ -0,0 +1,49 @@
import { MiniRepl } from './MiniRepl';
# Workshop
> This is a starter project for any type of teaching material using strudel!
## What is Strudel?
1. A pastry made with fruit or cheese rolled up in layers of thin sheets of dough and then baked.
2. a pastry made from multiple, thin layers of dough rolled up and filled with fruit, etc.
3. thin sheet of filled dough rolled and baked
## Some REPLs:
Small REPL:
<MiniRepl tune={`s("bd sd,hh*8")`} />
Big REPL:
<MiniRepl
tune={`await samples({
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'],
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
}, 'github:tidalcycles/Dirt-Samples/master/');
stack(
s("bd,[~ <sd!3 sd(3,4,2)>],hh(3,4)") // drums
.speed(perlin.range(.7,.9)) // random sample speed variation
,"<a1 b1\*2 a1(3,8) e2>" // bassline
.off(1/8,x=>x.add(12).degradeBy(.5)) // random octave jumps
.add(perlin.range(0,.5)) // random pitch variation
.superimpose(add(.05)) // add second, slightly detuned voice
.n() // wrap in "n"
.decay(.15).sustain(0) // make each note of equal length
.s('sawtooth') // waveform
.gain(.4) // turn down
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings() // chords
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
.add(perlin.range(0,.5)) // random pitch variation
.n() // wrap in "n"
.s('sawtooth') // waveform
.gain(.16) // turn down
.cutoff(500) // fixed cutoff
.attack(1) // slowly fade in
)
.slow(3/2)`}
/>
+8
View File
@@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx,mdx}'],
theme: {
extend: {},
},
plugins: [require('@tailwindcss/typography')],
};
+25
View File
@@ -0,0 +1,25 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import _mdx from 'vite-plugin-mdx';
import { viteSingleFile } from 'vite-plugin-singlefile';
// import remarkToc from 'remark-toc';
// import rehypeSlug from 'rehype-slug';
// import rehypeAutolinkHeadings from 'rehype-autolink-headings';
const mdx = _mdx.default || _mdx;
// `options` are passed to `@mdx-js/mdx`
const options = {
// See https://mdxjs.com/advanced/plugins
remarkPlugins: [
// remarkToc,
// E.g. `remark-frontmatter`
],
rehypePlugins: [
// rehypeSlug, rehypeAutolinkHeadings
],
};
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), mdx(options), viteSingleFile()],
});