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", "description": "Port of tidalcycles to javascript",
"scripts": { "scripts": {
"pretest": "cd tutorial && npm run jsdoc-json", "pretest": "cd tutorial && npm run jsdoc-json",
"test": "npm run lint && vitest run --version", "test": "vitest run --version",
"test-ui": "vitest --ui", "test-ui": "vitest --ui",
"test-coverage": "vitest --coverage", "test-coverage": "vitest --coverage",
"bootstrap": "lerna bootstrap", "bootstrap": "lerna bootstrap",
@@ -17,8 +17,7 @@
"preview": "npx serve ./out", "preview": "npx serve ./out",
"deploy": "NODE_DEBUG=gh-pages gh-pages -d out", "deploy": "NODE_DEBUG=gh-pages gh-pages -d out",
"jsdoc": "jsdoc packages/ -c jsdoc.config.json", "jsdoc": "jsdoc packages/ -c jsdoc.config.json",
"jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -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"
}, },
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
@@ -43,7 +42,6 @@
"devDependencies": { "devDependencies": {
"@vitest/ui": "^0.21.1", "@vitest/ui": "^0.21.1",
"c8": "^7.12.0", "c8": "^7.12.0",
"eslint": "^8.28.0",
"events": "^3.3.0", "events": "^3.3.0",
"gh-pages": "^4.0.0", "gh-pages": "^4.0.0",
"jsdoc": "^3.6.10", "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/>. 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'; import { Pattern, sequence } from './pattern.mjs';
const { Pattern, sequence } = pattern;
const controls = {}; const controls = {};
const generic_params = [ const generic_params = [
@@ -401,7 +400,7 @@ const generic_params = [
* @example * @example
* freq("220 110 440 110").s("superzow").osc() * freq("220 110 440 110").s("superzow").osc()
* @example * @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', ''], ['f', 'freq', ''],
@@ -788,6 +787,6 @@ controls.createParam = (name) => {
}; };
controls.createParams = (...names) => 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; 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/>. 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'; import { Pattern, getTime } from './index.mjs';
const Pattern = pattern.Pattern;
import { getTime } from './time.mjs';
import { State } from './state.mjs';
import { TimeSpan } from './timespan.mjs';
export const getDrawContext = (id = 'test-canvas') => { export const getDrawContext = (id = 'test-canvas') => {
let canvas = document.querySelector('#' + id); 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/>. 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'; import { Pattern, timeCat } from './pattern.mjs';
const { Pattern, timeCat } = pattern;
import bjork from 'bjork'; import bjork from 'bjork';
import { rotate } from './util.mjs'; import { rotate } from './util.mjs';
import Fraction from './fraction.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/>. 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'; import { isPattern, Pattern } from './index.mjs';
const { isPattern, Pattern } = pattern;
let scoped = false; let scoped = false;
export const evalScope = async (...args) => { 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/>. 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 controls from './controls.mjs';
import pattern from './pattern.mjs'; export * from './euclid.mjs';
import Fraction from './fraction.mjs';
import * as draw from './draw.mjs'; import { logger } from './logger.mjs';
import * as euclid from './euclid.mjs'; export { Fraction, controls };
import * as evaluate from './evaluate.mjs'; export * from './hap.mjs';
import * as hap from './hap.mjs'; export * from './pattern.mjs';
import * as repl from './repl.mjs'; export * from './signal.mjs';
import * as pianoroll from './pianoroll.mjs'; export * from './state.mjs';
import * as signal from './signal.mjs'; export * from './timespan.mjs';
import * as speak from './speak.mjs'; export * from './util.mjs';
import * as state from './state.mjs'; export * from './speak.mjs';
import * as time from './time.mjs'; export * from './evaluate.mjs';
import * as timespan from './timespan.mjs'; export * from './repl.mjs';
import * as ui from './ui.mjs'; export * from './logger.mjs';
import * as util from './util.mjs'; export * from './time.mjs';
export * from './draw.mjs';
const core = { drawLine, gist, logger, Fraction, ...controls, ...pattern, ...draw, ...euclid, export * from './pianoroll.mjs';
...evaluate, ...hap, ...repl, ...pianoroll, ...signal, ...speak, export * from './ui.mjs';
...state, ...time, ...timespan, ...ui, ...util export { default as drawLine } from './drawLine.mjs';
}; export { default as gist } from './gist.js';
export default core;
// below won't work with runtime.mjs (json import fails) // below won't work with runtime.mjs (json import fails)
/* import * as p from './package.json'; /* import * as p from './package.json';
export const version = p.version; */ export const version = p.version; */
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/core", "name": "@strudel.cycles/core",
"version": "0.4.1", "version": "0.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/core", "name": "@strudel.cycles/core",
"version": "0.4.1", "version": "0.4.0",
"description": "Port of Tidal Cycles to JavaScript", "description": "Port of Tidal Cycles to JavaScript",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "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 drawLine from './drawLine.mjs';
import { logger } from './logger.mjs'; import { logger } from './logger.mjs';
// monster object collecting everything to be exported
const pattern = {};
let stringParser; let stringParser;
// parser is expected to turn a string into a pattern // parser is expected to turn a string into a pattern
// if set, the reify function will parse all strings with it // if set, the reify function will parse all strings with it
// intended to use with mini to automatically interpret all strings as mini notation // 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 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 _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. * 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; this.query = query;
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Haskell-style functor, applicative and monadic operations // Haskell-style functor, applicative and monadic operations
/** /**
* Returns a new pattern, with the function applied to the value of * Returns a new pattern, with the function applied to the value of
* each hap. It has the alias {@link Pattern#fmap}. * each hap. It has the alias {@link Pattern#fmap}.
@@ -167,6 +164,7 @@ class Pattern {
return new Pattern(query); return new Pattern(query);
} }
bindWhole(choose_whole, func) { bindWhole(choose_whole, func) {
const pat_val = this; const pat_val = this;
const query = function (state) { const query = function (state) {
@@ -207,7 +205,7 @@ class Pattern {
} }
outerBind(func) { outerBind(func) {
return this.bindWhole((a) => a, func); return this.bindWhole((a, _) => a, func);
} }
outerJoin() { outerJoin() {
@@ -315,7 +313,7 @@ class Pattern {
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Utility methods mainly for internal use // Utility methods mainly for internal use
/** /**
* Query haps inside the given time span. * Query haps inside the given time span.
* *
@@ -536,7 +534,7 @@ class Pattern {
// removes continuous haps that don't have a 'whole' timespan // removes continuous haps that don't have a 'whole' timespan
return this.filterHaps((hap) => hap.whole); return this.filterHaps((hap) => hap.whole);
} }
/** /**
* Queries the pattern for the first cycle, returning Haps. Mainly of use when * Queries the pattern for the first cycle, returning Haps. Mainly of use when
* debugging a pattern. * debugging a pattern.
@@ -942,8 +940,8 @@ class Pattern {
//binary_pat = sequence(binary_pat) //binary_pat = sequence(binary_pat)
const true_pat = binary_pat.filterValues(id); const true_pat = binary_pat.filterValues(id);
const false_pat = binary_pat.filterValues((val) => !val); const false_pat = binary_pat.filterValues((val) => !val);
const with_pat = true_pat.fmap(() => (y) => y).appRight(func(this)); const with_pat = true_pat.fmap((_) => (y) => y).appRight(func(this));
const without_pat = false_pat.fmap(() => (y) => y).appRight(this); const without_pat = false_pat.fmap((_) => (y) => y).appRight(this);
return stack(with_pat, without_pat); return stack(with_pat, without_pat);
} }
@@ -1009,7 +1007,7 @@ class Pattern {
every(n, func) { every(n, func) {
return this.firstOf(n, func); return this.firstOf(n, func);
} }
/** /**
* Returns a new pattern where every other cycle is played once, twice as * 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 * fast, and offset in time by one quarter of a cycle. Creates a kind of
@@ -1029,7 +1027,6 @@ class Pattern {
* @example * @example
* note("c3 d3 e3 g3").rev() * note("c3 d3 e3 g3").rev()
*/ */
rev() { rev() {
const pat = this; const pat = this;
const query = function (state) { const query = function (state) {
@@ -1051,9 +1048,10 @@ class Pattern {
} }
palindrome() { palindrome() {
return this.every(2, pattern.rev); return this.every(2, rev);
} }
juxBy(by, func) { juxBy(by, func) {
by /= 2; by /= 2;
const elem_or = function (dict, key, dflt) { const elem_or = function (dict, key, dflt) {
@@ -1186,12 +1184,8 @@ class Pattern {
* note("0 1 2 3".scale('A minor')).iter(4) * note("0 1 2 3".scale('A minor')).iter(4)
*/ */
_iter(times, back = false) { _iter(times, back = false) {
times = Fraction(times); times = Fraction(times)
return slowcat( return slowcat(...listRange(0, times.sub(1)).map((i) => (back ? this.late(Fraction(i).div(times)) : this.early(Fraction(i).div(times)))));
...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)); on = Boolean(parseInt(on));
return on ? silence : this; return on ? silence : this;
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Control-related methods, which manipulate patterns of objects // 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'. * 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. * 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 // Context methods - ones that deal with metadata
_color(color) { _color(color) {
return this.withContext((context) => ({ ...context, 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. * Sets the velocity from 0 to 1. Is multiplied together with gain.
@@ -1363,53 +1363,9 @@ class Pattern {
_legato(value) { _legato(value) {
return this.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(span.end.sub(span.begin).mul(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.. // TODO - adopt value.mjs fully..
function _composeOp(a, b, func) { function _composeOp(a, b, func) {
function _nonFunctionObject(x) { function _nonFunctionObject(x) {
@@ -1432,7 +1388,7 @@ function _composeOp(a, b, func) {
// pattern composers // pattern composers
const composers = { const composers = {
set: [(a, b) => b], set: [(a, b) => b],
keep: [(a) => a], keep: [(a, b) => a],
keepif: [(a, b) => (b ? a : undefined)], keepif: [(a, b) => (b ? a : undefined)],
// numerical functions // numerical functions
@@ -1507,48 +1463,53 @@ function _composeOp(a, b, func) {
// generate methods to do what and how // generate methods to do what and how
for (const [what, [op, preprocess]] of Object.entries(composers)) { 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) { for (const how of hows) {
Pattern.prototype[how.toLowerCase()] = function (...args) { return this.set[how.toLowerCase()](args) }; Pattern.prototype[what + how] = function (...other) {
curry_toplevel(how.toLowerCase(), 2); 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 // binary composers
@@ -1563,22 +1524,14 @@ function _composeOp(a, b, func) {
* .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~") * .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~")
* .slow(4) * .slow(4)
*/ */
Pattern.prototype.struct = function(...args) { return this.keepif.out(...args) } Pattern.prototype.struct = Pattern.prototype.keepifOut;
curry_toplevel('struct', 2); Pattern.prototype.structAll = Pattern.prototype.keepOut;
Pattern.prototype.structAll = function(...args) { return this.keep.out(...args) } Pattern.prototype.mask = Pattern.prototype.keepifIn;
curry_toplevel('structAll', 2); Pattern.prototype.maskAll = Pattern.prototype.keepIn;
Pattern.prototype.mask = function(...args) { return this.keepif.in(...args) } Pattern.prototype.reset = Pattern.prototype.keepifTrig;
curry_toplevel('mask', 2); Pattern.prototype.resetAll = Pattern.prototype.keepTrig;
Pattern.prototype.maskAll = function(...args) { return this.keep.in(...args) } Pattern.prototype.restart = Pattern.prototype.keepifTrigzero;
curry_toplevel('maskAll', 2); Pattern.prototype.restartAll = Pattern.prototype.keepTrigzero;
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);
})(); })();
// methods of Pattern that get callable factories // methods of Pattern that get callable factories
@@ -1603,6 +1556,10 @@ Pattern.prototype.patternified = [
'velocity', 'velocity',
]; ];
// aliases
export const polyrhythm = stack;
export const pr = stack;
// methods that create patterns, which are added to patternified Pattern methods // methods that create patterns, which are added to patternified Pattern methods
Pattern.prototype.factories = { Pattern.prototype.factories = {
pure, 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) // 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 // Elemental patterns
// Nothing // Nothing
const silence = new Pattern(() => []); export const silence = new Pattern((_) => []);
pattern['silence'] = silence;
/** A discrete value that repeats once per cycle. /** A discrete value that repeats once per cycle.
* *
@@ -1660,32 +1588,27 @@ pattern['silence'] = silence;
* @example * @example
* pure('e4') // "e4" * pure('e4') // "e4"
*/ */
function pure(value) { export function pure(value) {
function query(state) { function query(state) {
return state.span.spanCycles.map((subspan) => new Hap(Fraction(subspan.begin).wholeCycle(), subspan, value)); return state.span.spanCycles.map((subspan) => new Hap(Fraction(subspan.begin).wholeCycle(), subspan, value));
} }
return new Pattern(query); 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 // thing?.constructor?.name !== 'Pattern' // <- this will fail when code is mangled
const is = thing instanceof Pattern || thing?._Pattern; const is = thing instanceof Pattern || thing?._Pattern;
// TODO: find out how to check wrong core dependency. below will never work !thing === 'undefined' if (!thing instanceof Pattern) {
// wrapping it in (..) will result other checks to log that warning (e.g. isPattern('kalimba'))
/* if (!thing instanceof Pattern) {
console.warn( console.warn(
`Found Pattern that fails "instanceof Pattern" check. `Found Pattern that fails "instanceof Pattern" check.
This may happen if you are using multiple versions of @strudel.cycles/core. This may happen if you are using multiple versions of @strudel.cycles/core.
Please check by running "npm ls @strudel.cycles/core".`, Please check by running "npm ls @strudel.cycles/core".`,
); );
console.log(thing); }
} */
return is; return is;
} }
pattern['isPattern'] = isPattern;
function reify(thing) { export function reify(thing) {
// Turns something into a pattern, unless it's already a pattern // Turns something into a pattern, unless it's already a pattern
if (isPattern(thing)) { if (isPattern(thing)) {
return thing; return thing;
@@ -1695,7 +1618,6 @@ function reify(thing) {
} }
return pure(thing); return pure(thing);
} }
pattern['reify'] = reify;
/** The given items are played at the same time at the same length. /** The given items are played at the same time at the same length.
* *
@@ -1703,20 +1625,12 @@ pattern['reify'] = reify;
* @example * @example
* stack(g3, b3, [e4, d4]).note() // "g3,b3,[e4,d4]".note() * 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.. // Array test here is to avoid infinite recursions..
pats = pats.map((pat) => (Array.isArray(pat) ? sequence(...pat) : reify(pat))); pats = pats.map((pat) => (Array.isArray(pat) ? sequence(...pat) : reify(pat)));
const query = (state) => flatten(pats.map((pat) => pat.query(state))); const query = (state) => flatten(pats.map((pat) => pat.query(state)));
return new Pattern(query); 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: /** 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]) * slowcat(e5, b4, [d5, c5])
* *
*/ */
function slowcat(...pats) { export function slowcat(...pats) {
// Array test here is to avoid infinite recursions.. // Array test here is to avoid infinite recursions..
pats = pats.map((pat) => (Array.isArray(pat) ? sequence(...pat) : reify(pat))); pats = pats.map((pat) => (Array.isArray(pat) ? sequence(...pat) : reify(pat)));
@@ -1747,13 +1661,12 @@ function slowcat(...pats) {
}; };
return new Pattern(query).splitQueries(); 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. /** 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 * @param {...any} items - The items to concatenate
* @return {Pattern} * @return {Pattern}
*/ */
function slowcatPrime(...pats) { export function slowcatPrime(...pats) {
pats = pats.map(reify); pats = pats.map(reify);
const query = function (state) { const query = function (state) {
const pat_n = Math.floor(state.span.begin) % pats.length; const pat_n = Math.floor(state.span.begin) % pats.length;
@@ -1762,7 +1675,6 @@ function slowcatPrime(...pats) {
}; };
return new Pattern(query).splitQueries(); return new Pattern(query).splitQueries();
} }
pattern['slowcatPrime'] = slowcatPrime;
/** Concatenation: as with {@link slowcat}, but squashes a cycle from each pattern into one cycle /** 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]) * // sequence(e5, b4, [d5, c5])
* // seq(e5, b4, [d5, c5]) * // seq(e5, b4, [d5, c5])
*/ */
function fastcat(...pats) { export function fastcat(...pats) {
return slowcat(...pats)._fast(pats.length); return slowcat(...pats)._fast(pats.length);
} }
pattern['fastcat'] = fastcat;
/** The given items are con**cat**enated, where each one takes one cycle. Synonym: slowcat /** 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() * cat(e5, b4, [d5, c5]).note() // "<e5 b4 [d5 c5]>".note()
* *
*/ */
function cat(...pats) { export function cat(...pats) {
return slowcat(...pats); return slowcat(...pats);
} }
pattern['cat'] = cat;
/** Like {@link seq}, but each step has a length, relative to the whole. /** Like {@link seq}, but each step has a length, relative to the whole.
* @return {Pattern} * @return {Pattern}
* @example * @example
* timeCat([3,e3],[1, g3]).note() // "e3@3 g3".note() * 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)); const total = timepats.map((a) => a[0]).reduce((a, b) => a.add(b), Fraction(0));
let begin = Fraction(0); let begin = Fraction(0);
const pats = []; const pats = [];
@@ -1809,23 +1719,20 @@ function timeCat(...timepats) {
} }
return stack(...pats); return stack(...pats);
} }
pattern['timeCat'] = timeCat;
/** See {@link fastcat} */ /** See {@link fastcat} */
function sequence(...pats) { export function sequence(...pats) {
return fastcat(...pats); return fastcat(...pats);
} }
pattern['sequence'] = sequence;
/** Like **cat**, but the items are crammed into one cycle. Synonyms: fastcat, sequence /** Like **cat**, but the items are crammed into one cycle. Synonyms: fastcat, sequence
* @example * @example
* seq(e5, b4, [d5, c5]).note() // "e5 b4 [d5 c5]".note() * seq(e5, b4, [d5, c5]).note() // "e5 b4 [d5 c5]".note()
* *
*/ */
function seq(...pats) { export function seq(...pats) {
return fastcat(...pats); return fastcat(...pats);
} }
pattern['seq'] = seq;
function _sequenceCount(x) { function _sequenceCount(x) {
if (Array.isArray(x)) { if (Array.isArray(x)) {
@@ -1840,7 +1747,7 @@ function _sequenceCount(x) {
return [reify(x), 1]; return [reify(x), 1];
} }
function polymeterSteps(steps, ...args) { export function polymeterSteps(steps, ...args) {
const seqs = args.map((a) => _sequenceCount(a)); const seqs = args.map((a) => _sequenceCount(a));
if (seqs.length == 0) { if (seqs.length == 0) {
return silence; return silence;
@@ -1851,7 +1758,7 @@ function polymeterSteps(steps, ...args) {
const pats = []; const pats = [];
for (const seq of seqs) { for (const seq of seqs) {
if (seq[1] == 0) { if (seq[1] == 0) {
continue; next;
} }
if (steps == seq[1]) { if (steps == seq[1]) {
pats.push(seq[0]); pats.push(seq[0]);
@@ -1861,27 +1768,57 @@ function polymeterSteps(steps, ...args) {
} }
return stack(...pats); return stack(...pats);
} }
pattern['polymeterSteps'] = polymeterSteps;
function polymeter(...args) { export function polymeter(...args) {
return polymeterSteps(0, ...args); return polymeterSteps(0, ...args);
} }
pattern['polymeter'] = polymeter;
function pm(...args) { // alias
return polymeter(...args); 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 // 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 // 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 // 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 // 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) // 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]) => { Object.entries(Pattern.prototype.composable).forEach(([functionName, composable]) => {
// compose with dot // compose with dot
func[functionName] = (...args) => { func[functionName] = (...args) => {
@@ -1894,31 +1831,25 @@ function makeComposable(func) {
}); });
return func; return func;
} }
pattern['makeComposable'] = makeComposable;
const patternify2 = (f) => (pata, patb, pat) => export const patternify2 = (f) => (pata, patb, pat) =>
pata pata
.fmap((a) => (b) => f.call(pat, a, b)) .fmap((a) => (b) => f.call(pat, a, b))
.appLeft(patb) .appLeft(patb)
.innerJoin(); .innerJoin();
pattern['patternify2'] = patternify2; export const patternify3 = (f) => (pata, patb, patc, pat) =>
const patternify3 = (f) => (pata, patb, patc, pat) =>
pata pata
.fmap((a) => (b) => (c) => f.call(pat, a, b, c)) .fmap((a) => (b) => (c) => f.call(pat, a, b, c))
.appLeft(patb) .appLeft(patb)
.appLeft(patc) .appLeft(patc)
.innerJoin(); .innerJoin();
pattern['patternify3'] = patternify3; export const patternify4 = (f) => (pata, patb, patc, patd, pat) =>
const patternify4 = (f) => (pata, patb, patc, patd, pat) =>
pata pata
.fmap((a) => (b) => (c) => (d) => f.call(pat, a, b, c, d)) .fmap((a) => (b) => (c) => (d) => f.call(pat, a, b, c, d))
.appLeft(patb) .appLeft(patb)
.appLeft(patc) .appLeft(patc)
.appLeft(patd) .appLeft(patd)
.innerJoin(); .innerJoin();
pattern['patternify4'] = patternify4;
Pattern.prototype.echo = function (...args) { Pattern.prototype.echo = function (...args) {
args = args.map(reify); 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) // 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 () { Pattern.prototype.bootstrap = function () {
// makeComposable(Pattern.prototype); // makeComposable(Pattern.prototype);
const bootstrapped = Object.fromEntries( const bootstrapped = Object.fromEntries(
@@ -1986,9 +1916,10 @@ Pattern.prototype.bootstrap = function () {
this.patternified.forEach((prop) => { this.patternified.forEach((prop) => {
// the following will patternify all functions in Pattern.prototype.patternified // the following will patternify all functions in Pattern.prototype.patternified
Pattern.prototype[prop] = function (...args) { 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]; const func = Pattern.prototype['_' + prop];
Pattern.prototype[prop] = function (...args) { Pattern.prototype[prop] = function (...args) {
@@ -1999,22 +1930,22 @@ Pattern.prototype.bootstrap = function () {
// a getter that returns a function, so 'pat' can be // a getter that returns a function, so 'pat' can be
// accessed by closures that are methods of that function.. // accessed by closures that are methods of that function..
get: function() { get: function() {
const pat = this; const pat = this;
// wrap the default behaviour // wrap the default behaviour
const wrapper = pat.patternify(x => x.innerJoin(), func); const wrapper = pat.patternify(x => x.innerJoin(), func);
// add the variants // add the variants
wrapper['in'] = pat.patternify(x => x.innerJoin(), func); wrapper['in'] = pat.patternify(x => x.innerJoin(), func);
wrapper['out'] = pat.patternify(x => x.outerJoin(), func); wrapper['out'] = pat.patternify(x => x.outerJoin(), func);
wrapper['trig'] = pat.patternify(x => x.trigJoin(), func); wrapper['trig'] = pat.patternify(x => x.trigJoin(), func);
wrapper['trigzero'] = pat.patternify(x => x.trigzeroJoin(), func); wrapper['trigzero'] = pat.patternify(x => x.trigzeroJoin(), func);
wrapper['squeeze'] = pat.patternify(x => x.squeezeJoin(), 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))` // 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) // TODO: find a way to implement below outside of constructor (code only worked there)
/* Object.assign( /* 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('early', (a, pat) => pat.early(a), { patternified: true, composable: true });
Pattern.prototype.define('hush', (pat) => pat.hush(), { patternified: false, composable: true }); Pattern.prototype.define('hush', (pat) => pat.hush(), { patternified: false, composable: true });
Pattern.prototype.define('bypass', (pat) => pat.bypass(on), { patternified: true, composable: true });
export default pattern;
+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/>. 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'; import { Pattern } from './index.mjs';
const { Pattern } = pattern;
import { toMidi } from './util.mjs';
import { getDrawContext } from './draw.mjs';
const scale = (normalized, min, max) => normalized * (max - min) + min; const scale = (normalized, min, max) => normalized * (max - min) + min;
const getValue = (e) => { const getValue = (e) => {
+2 -2
View File
@@ -40,11 +40,11 @@ export function repl({
throw new Error('no code to evaluate'); throw new Error('no code to evaluate');
} }
try { try {
beforeEval?.({ code }); beforeEval({ code });
const { pattern } = await _evaluate(code, transpiler); const { pattern } = await _evaluate(code, transpiler);
logger(`[eval] code updated`); logger(`[eval] code updated`);
scheduler.setPattern(pattern, autostart); scheduler.setPattern(pattern, autostart);
afterEval?.({ code, pattern }); afterEval({ code, pattern });
return pattern; return pattern;
} catch (err) { } catch (err) {
// console.warn(`[repl] eval error: ${err.message}`); // 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 { Hap } from './hap.mjs';
import pattern from './pattern.mjs'; import { Pattern, fastcat, reify, silence, stack, isPattern } from './pattern.mjs';
const { Pattern, fastcat, reify, silence, stack, isPattern } = pattern;
import Fraction from './fraction.mjs'; import Fraction from './fraction.mjs';
import { id } from './util.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/>. 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'; import { Pattern, patternify2 } from './index.mjs';
const {Pattern, patternify2, reify} = pattern;
let synth; let synth;
try { 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/>. 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'; import { fastcat, stack, slowcat, silence, pure } from '../pattern.mjs';
const { fastcat, stack, slowcat, silence, pure } = pattern;
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import drawLine from '../drawLine.mjs'; 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 { describe, it, expect } from 'vitest';
import core from '../index.mjs'; import {
TimeSpan,
const { Hap,
State,
Pattern, Pattern,
pure, pure,
stack, stack,
@@ -28,12 +29,6 @@ const {
sub, sub,
mul, mul,
div, div,
id,
ply,
rev,
TimeSpan,
Hap,
State,
saw, saw,
saw2, saw2,
isaw, isaw,
@@ -44,7 +39,11 @@ const {
square2, square2,
tri, tri,
tri2, tri2,
time} = core; id,
ply,
rev,
time,
} from '../index.mjs';
import { steady } from '../signal.mjs'; import { steady } from '../signal.mjs';
@@ -157,32 +156,32 @@ describe('Pattern', () => {
describe('add()', () => { describe('add()', () => {
it('can structure In()', () => { it('can structure In()', () => {
expect(pure(3).add(pure(4)).query(st(0, 1))[0].value).toBe(7); 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()', () => { 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', () => { 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 / 3, 1 / 2), ts(1 / 3, 1 / 2), 6),
new Hap(ts(1 / 2, 2 / 3), ts(1 / 2, 2 / 3), 7), new Hap(ts(1 / 2, 2 / 3), ts(1 / 2, 2 / 3), 7),
]); ]);
}); });
it('can Trig() structure', () => { it('can Trig() structure', () => {
sameFirst( 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), sequence(26, 27, 36, 37),
); );
}); });
it('can Trigzero() structure', () => { it('can Trigzero() structure', () => {
sameFirst( 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), sequence(21, 22, 31, 32),
); );
}); });
it('can Squeeze() structure', () => { it('can Squeeze() structure', () => {
sameFirst( sameFirst(
sequence(1, [2, 3]).add.squeeze(sequence(10, 20, 30)), sequence(1, [2, 3]).addSqueeze(sequence(10, 20, 30)),
sequence( sequence(
[11, 21, 31], [11, 21, 31],
[ [
@@ -194,7 +193,7 @@ describe('Pattern', () => {
}); });
it('can SqueezeOut() structure', () => { it('can SqueezeOut() structure', () => {
sameFirst( 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]]), sequence([11, [12, 13]], [21, [22, 23]], [31, [32, 33]]),
); );
}); });
@@ -205,32 +204,32 @@ describe('Pattern', () => {
describe('keep()', () => { describe('keep()', () => {
it('can structure In()', () => { it('can structure In()', () => {
expect(pure(3).keep(pure(4)).query(st(0, 1))[0].value).toBe(3); 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()', () => { 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', () => { 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 / 3, 1 / 2), ts(1 / 3, 1 / 2), 1),
new Hap(ts(1 / 2, 2 / 3), ts(1 / 2, 2 / 3), 2), new Hap(ts(1 / 2, 2 / 3), ts(1 / 2, 2 / 3), 2),
]); ]);
}); });
it('can Trig() structure', () => { it('can Trig() structure', () => {
sameFirst( 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), sequence(6, 7, 6, 7),
); );
}); });
it('can Trigzero() structure', () => { it('can Trigzero() structure', () => {
sameFirst( 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), sequence(1, 2, 1, 2),
); );
}); });
it('can Squeeze() structure', () => { it('can Squeeze() structure', () => {
sameFirst( sameFirst(
sequence(1, [2, 3]).keep.squeeze(sequence(10, 20, 30)), sequence(1, [2, 3]).keepSqueeze(sequence(10, 20, 30)),
sequence( sequence(
[1, 1, 1], [1, 1, 1],
[ [
@@ -240,39 +239,39 @@ describe('Pattern', () => {
), ),
); );
}); });
it('can squeezeOut() structure', () => { it('can SqueezeOut() structure', () => {
sameFirst(sequence(1, [2, 3]).keep.squeezeout(10, 20, 30), sequence([1, [2, 3]], [1, [2, 3]], [1, [2, 3]])); sameFirst(sequence(1, [2, 3]).keepSqueezeOut(10, 20, 30), sequence([1, [2, 3]], [1, [2, 3]], [1, [2, 3]]));
}); });
}); });
describe('keepif()', () => { describe('keepif()', () => {
it('can structure In()', () => { it('can structure In()', () => {
sameFirst(sequence(3, 4).keepif(true, false), sequence(3, silence)); 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()', () => { 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', () => { 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 / 3, 1 / 2), ts(1 / 3, 1 / 2), 1),
new Hap(ts(1 / 2, 2 / 3), ts(1 / 2, 2 / 3), 2), new Hap(ts(1 / 2, 2 / 3), ts(1 / 2, 2 / 3), 2),
]); ]);
}); });
it('can Trig() structure', () => { it('can Trig() structure', () => {
sameFirst( 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), sequence(silence, silence, 6, 7),
); );
}); });
it('can Trigzero() structure', () => { it('can Trigzero() structure', () => {
sameFirst( 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), sequence(silence, silence, 1, 2),
); );
}); });
it('can Squeeze() structure', () => { it('can Squeeze() structure', () => {
sameFirst( sameFirst(
sequence(1, [2, 3]).keepif.squeeze(sequence(true, true, false)), sequence(1, [2, 3]).keepifSqueeze(sequence(true, true, false)),
sequence( sequence(
[1, 1, silence], [1, 1, silence],
[ [
@@ -283,7 +282,7 @@ describe('Pattern', () => {
); );
}); });
it('can SqueezeOut() structure', () => { 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()', () => { describe('sub()', () => {
@@ -321,15 +320,15 @@ describe('Pattern', () => {
it('Can set things with plain values', () => { it('Can set things with plain values', () => {
sameFirst(sequence(1, 2, 3).set(4), sequence(4).fast(3)); 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', () => { 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', () => { it('Can squeeze one pattern inside the haps of another', () => {
sameFirst( sameFirst(
sequence(1, [2, 3]).set.squeeze(sequence('a', 'b', 'c')), sequence(1, [2, 3]).setSqueeze(sequence('a', 'b', 'c')),
sequence( sequence(
['a', 'b', 'c'], ['a', 'b', 'c'],
[ [
@@ -339,7 +338,7 @@ describe('Pattern', () => {
), ),
); );
sameFirst( sameFirst(
sequence(1, [2, 3]).set.squeeze('a', 'b', 'c'), sequence(1, [2, 3]).setSqueeze('a', 'b', 'c'),
sequence( sequence(
['a', 'b', 'c'], ['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/>. 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'; import { pure } from '../pattern.mjs';
const { pure } = pattern;
import { import {
isNote, isNote,
tokenizeNote, 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/>. 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'; import shapeshifter from './shapeshifter.mjs';
export const evaluate = async (code) => { 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", "name": "@strudel.cycles/eval",
"version": "0.4.1", "version": "0.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/eval", "name": "@strudel.cycles/eval",
"version": "0.4.1", "version": "0.4.0",
"description": "Code evaluator for strudel", "description": "Code evaluator for strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@@ -28,7 +28,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"estraverse": "^5.3.0", "estraverse": "^5.3.0",
"shift-ast": "^7.0.0", "shift-ast": "^7.0.0",
"shift-codegen": "^8.1.0", "shift-codegen": "^8.1.0",
+1 -1
View File
@@ -21,7 +21,7 @@ import {
import shiftCodegen from 'shift-codegen'; import shiftCodegen from 'shift-codegen';
const codegen = shiftCodegen.default || shiftCodegen; // parcel module resolution fuckup 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; const { Pattern } = strudel;
+1 -1
View File
@@ -8,7 +8,7 @@ import { expect, describe, it } from 'vitest';
import { evaluate } from '../evaluate.mjs'; import { evaluate } from '../evaluate.mjs';
import { mini } from '@strudel.cycles/mini'; import { mini } from '@strudel.cycles/mini';
import strudel from '@strudel.cycles/core'; import * as strudel from '@strudel.cycles/core';
const { fastcat, evalScope } = strudel; const { fastcat, evalScope } = strudel;
describe('evaluate', async () => { 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 * as _WebMidi from 'webmidi';
import core from '@strudel.cycles/core'; import { Pattern, isPattern, isNote, getPlayableNoteValue, logger } from '@strudel.cycles/core';
const { Pattern, isPattern, isNote, getPlayableNoteValue, logger } = core;
import { getAudioContext } from '@strudel.cycles/webaudio'; import { getAudioContext } from '@strudel.cycles/webaudio';
// if you use WebMidi from outside of this package, make sure to import that instance: // 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", "name": "@strudel.cycles/midi",
"version": "0.4.1", "version": "0.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/midi", "name": "@strudel.cycles/midi",
"version": "0.4.1", "version": "0.4.0",
"description": "Midi API for strudel", "description": "Midi API for strudel",
"main": "index.mjs", "main": "index.mjs",
"repository": { "repository": {
@@ -21,7 +21,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/tone": "^0.4.1", "@strudel.cycles/tone": "^0.4.0",
"tone": "^14.7.77", "tone": "^14.7.77",
"webmidi": "^3.0.21" "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 * 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'; // 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; var _seedState = 0;
const randOffset = 0.0002; const randOffset = 0.0002;
@@ -23,15 +23,14 @@ const applyOptions = (parent) => (pat, i) => {
const operator = options?.operator; const operator = options?.operator;
if (operator) { if (operator) {
switch (operator.type_) { switch (operator.type_) {
case 'stretch': { case 'stretch':
const speed = Fraction(operator.arguments_.amount).inverse(); const speed = Fraction(operator.arguments_.amount).inverse();
return reify(pat).fast(speed); return reify(pat).fast(speed);
}
case 'bjorklund': case 'bjorklund':
return pat.euclid(operator.arguments_.pulse, operator.arguments_.step, operator.arguments_.rotation); return pat.euclid(operator.arguments_.pulse, operator.arguments_.step, operator.arguments_.rotation);
case 'degradeBy': case 'degradeBy':
return reify(pat)._degradeByWith( return reify(pat)._degradeByWith(
rand.early(randOffset * _nextSeed()).segment(1), strudel.rand.early(randOffset * _nextSeed()).segment(1),
operator.arguments_.amount, operator.arguments_.amount,
); );
// TODO: case 'fixed-step': "%" // TODO: case 'fixed-step': "%"
@@ -88,7 +87,7 @@ function resolveReplications(ast) {
export function patternifyAST(ast) { export function patternifyAST(ast) {
switch (ast.type_) { switch (ast.type_) {
case 'pattern': { case 'pattern':
resolveReplications(ast); resolveReplications(ast);
const children = ast.source_.map(patternifyAST).map(applyOptions(ast)); const children = ast.source_.map(patternifyAST).map(applyOptions(ast));
const alignment = ast.arguments_.alignment; const alignment = ast.arguments_.alignment;
@@ -96,7 +95,7 @@ export function patternifyAST(ast) {
return stack(...children); return stack(...children);
} }
if (alignment === 'r') { 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); const weightedChildren = ast.source_.some((child) => !!child.options_?.weight);
if (!weightedChildren && alignment === 't') { if (!weightedChildren && alignment === 't') {
@@ -111,8 +110,7 @@ export function patternifyAST(ast) {
return pat; return pat;
} }
return sequence(...children); return sequence(...children);
} case 'element':
case 'element': {
if (ast.source_ === '~') { if (ast.source_ === '~') {
return silence; 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 pure(value).withLocation([start.line, start.column, start.offset], [end.line, end.column, end.offset]);
} }
return patternifyAST(ast.source_); return patternifyAST(ast.source_);
}
case 'stretch': case 'stretch':
return patternifyAST(ast.source_).slow(ast.arguments_.amount); return patternifyAST(ast.source_).slow(ast.arguments_.amount);
/* case 'scale': /* case 'scale':
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/mini", "name": "@strudel.cycles/mini",
"version": "0.4.1", "version": "0.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/mini", "name": "@strudel.cycles/mini",
"version": "0.4.1", "version": "0.4.0",
"description": "Mini notation for strudel", "description": "Mini notation for strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@@ -26,9 +26,9 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/eval": "^0.4.1", "@strudel.cycles/eval": "^0.4.0",
"@strudel.cycles/tone": "^0.4.1" "@strudel.cycles/tone": "^0.4.0"
}, },
"devDependencies": { "devDependencies": {
"peggy": "^2.0.1" "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 OSC from 'osc-js';
import core from '@strudel.cycles/core'; import { logger, parseNumeral, Pattern } from '@strudel.cycles/core';
const { Pattern, logger, parseNumeral } = core;
let connection; // Promise<OSC> let connection; // Promise<OSC>
function connect() { function connect() {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/osc", "name": "@strudel.cycles/osc",
"version": "0.3.1", "version": "0.3.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/osc", "name": "@strudel.cycles/osc",
"version": "0.3.1", "version": "0.3.0",
"description": "OSC messaging for strudel", "description": "OSC messaging for strudel",
"main": "osc.mjs", "main": "osc.mjs",
"scripts": { "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: Here is a minimal example of how to set up a MiniRepl:
```jsx ```jsx
import core from '@strudel.cycles/core'; import { evalScope, controls } from '@strudel.cycles/core';
const {evalScope} = core;
import { MiniRepl } from '@strudel.cycles/react'; import { MiniRepl } from '@strudel.cycles/react';
import { prebake } from '../repl/src/prebake.mjs'; import { prebake } from '../repl/src/prebake.mjs';
evalScope( evalScope(
core, controls,
import('@strudel.cycles/core'), import('@strudel.cycles/core'),
import('@strudel.cycles/tonal'), import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'), 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 { createTheme as Z } from "@uiw/codemirror-themes";
import { useInView as ee } from "react-hook-inview"; import { useInView as ee } from "react-hook-inview";
import { webaudioOutput as te, getAudioContext as re } from "@strudel.cycles/webaudio"; 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"; import { transpiler as ne } from "@strudel.cycles/transpiler";
const ae = Z({ const ae = Z({
theme: "dark", theme: "dark",
@@ -74,11 +74,11 @@ const B = $.define(), se = G.define({
for (let o of t.effects) for (let o of t.effects)
if (o.is(z)) { if (o.is(z)) {
const a = o.value.map( const a = o.value.map(
(s) => (s.context.locations || []).map(({ start: u, end: d }) => { (s) => (s.context.locations || []).map(({ start: f, end: d }) => {
const f = s.context.color || "#FFCA28"; const u = s.context.color || "#FFCA28";
let c = t.newDoc.line(u.line).from + u.column, i = t.newDoc.line(d.line).from + d.column; let c = t.newDoc.line(f.line).from + f.column, i = t.newDoc.line(d.line).from + d.column;
const m = t.newDoc.length; 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) || []; ).flat().filter(Boolean) || [];
e = E.set(a, !0); e = E.set(a, !0);
@@ -90,13 +90,13 @@ const B = $.define(), se = G.define({
}, },
provide: (e) => U.decorations.from(e) provide: (e) => U.decorations.from(e)
}), le = [Y(), ae, ie, se]; }), 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 = _( const d = _(
(i) => { (i) => {
t?.(i); t?.(i);
}, },
[t] [t]
), f = _( ), u = _(
(i) => { (i) => {
o?.(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, { return /* @__PURE__ */ n.createElement(n.Fragment, null, /* @__PURE__ */ n.createElement(X, {
value: e, value: e,
onChange: d, onChange: d,
onCreateEditor: f, onCreateEditor: u,
onUpdate: c, onUpdate: c,
extensions: le extensions: le
})); }));
@@ -119,21 +119,21 @@ function K(...e) {
return e.filter(Boolean).join(" "); return e.filter(Boolean).join(" ");
} }
function ue({ view: e, pattern: t, active: o, getTime: a }) { function ue({ view: e, pattern: t, active: o, getTime: a }) {
const s = H([]), u = H(); const s = H([]), f = H();
L(() => { L(() => {
if (e) if (e)
if (t && o) { if (t && o) {
let d = requestAnimationFrame(function f() { let u = function() {
try { try {
const c = a(), m = [Math.max(u.current || c, c - 1 / 10, 0), c + 1 / 60]; const c = a(), m = [Math.max(f.current || c, c - 1 / 10, 0), c + 1 / 60];
u.current = m[1], s.current = s.current.filter((g) => g.whole.end > c); f.current = m[1], s.current = s.current.filter((g) => g.whole.end > c);
const h = t.queryArc(...m).filter((g) => g.hasOnset()); const h = t.queryArc(...m).filter((g) => g.hasOnset());
s.current = s.current.concat(h), e.dispatch({ effects: z.of(s.current) }); s.current = s.current.concat(h), e.dispatch({ effects: z.of(s.current) });
} catch { } catch {
e.dispatch({ effects: z.of([]) }); e.dispatch({ effects: z.of([]) });
} }
d = requestAnimationFrame(f); d = requestAnimationFrame(u);
}); }, d = requestAnimationFrame(u);
return () => { return () => {
cancelAnimationFrame(d); cancelAnimationFrame(d);
}; };
@@ -177,21 +177,20 @@ function O({ type: e }) {
function Ee(e) { function Ee(e) {
return L(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), _((t) => window.postMessage(t, "*"), []); return L(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), _((t) => window.postMessage(t, "*"), []);
} }
const we = oe.repl; function we({
function ye({
defaultOutput: e, defaultOutput: e,
interval: t, interval: t,
getTime: o, getTime: o,
evalOnMount: a = !1, evalOnMount: a = !1,
initialCode: s = "", initialCode: s = "",
autolink: u = !1, autolink: f = !1,
beforeEval: d, beforeEval: d,
afterEval: f, afterEval: u,
onEvalError: c, onEvalError: c,
onToggle: i 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( 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(
() => we({ () => oe({
interval: t, interval: t,
defaultOutput: e, defaultOutput: e,
onSchedulerError: g, onSchedulerError: g,
@@ -204,7 +203,7 @@ function ye({
y(l), d?.(); y(l), d?.();
}, },
afterEval: ({ pattern: l, code: P }) => { 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) => { onToggle: (l) => {
x(l), i?.(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); return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
} }
const _e = () => re().currentTime; const ke = () => re().currentTime;
function Te({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) { function Se({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) {
const { const {
code: s, code: s,
setCode: u, setCode: f,
evaluate: d, evaluate: d,
activateCode: f, activateCode: u,
error: c, error: c,
isDirty: i, isDirty: i,
activeCode: m, activeCode: m,
@@ -262,10 +261,10 @@ function Te({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) {
scheduler: C, scheduler: C,
togglePlay: N, togglePlay: N,
stop: p stop: p
} = ye({ } = we({
initialCode: e, initialCode: e,
defaultOutput: te, defaultOutput: te,
getTime: _e getTime: ke
}), [y, M] = w(), [S, k] = ee({ }), [y, M] = w(), [S, k] = ee({
threshold: 0.01 threshold: 0.01
}), D = H(), F = V(() => ((k || !t) && (D.current = !0), k || D.current), [k, t]); }), 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(() => { }), j(() => {
if (a) { if (a) {
const x = async (b) => { 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); 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" type: g ? "pause" : "play"
})), /* @__PURE__ */ n.createElement("button", { })), /* @__PURE__ */ n.createElement("button", {
className: K(i ? v.button : v.buttonDisabled), className: K(i ? v.button : v.buttonDisabled),
onClick: () => f() onClick: () => u()
}, /* @__PURE__ */ n.createElement(O, { }, /* @__PURE__ */ n.createElement(O, {
type: "refresh" type: "refresh"
}))), c && /* @__PURE__ */ n.createElement("div", { }))), c && /* @__PURE__ */ n.createElement("div", {
@@ -304,18 +303,18 @@ function Te({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) {
className: v.body className: v.body
}, F && /* @__PURE__ */ n.createElement(de, { }, F && /* @__PURE__ */ n.createElement(de, {
value: s, value: s,
onChange: u, onChange: f,
onViewChanged: M 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 { export {
de as CodeMirror, de as CodeMirror,
Te as MiniRepl, Se as MiniRepl,
K as cx, K as cx,
ce as flash, ce as flash,
ue as useHighlighting, ue as useHighlighting,
Ve as useKeydown, Te as useKeydown,
Ee as usePostMessage, Ee as usePostMessage,
ye as useStrudel we as useStrudel
}; };
@@ -1,5 +1,4 @@
import core from '@strudel.cycles/core'; import { evalScope, controls } from '@strudel.cycles/core';
const { evalScope } = core;
import { getAudioContext, panic, webaudioOutput } from '@strudel.cycles/webaudio'; import { getAudioContext, panic, webaudioOutput } from '@strudel.cycles/webaudio';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import CodeMirror, { flash } from '../../../src/components/CodeMirror6'; import CodeMirror, { flash } from '../../../src/components/CodeMirror6';
@@ -11,8 +10,8 @@ import './style.css';
// TODO: only import stuff when play is pressed? // TODO: only import stuff when play is pressed?
evalScope( evalScope(
core, controls,
// import('@strudel.cycles/core'), import('@strudel.cycles/core'),
// import('@strudel.cycles/tone'), // import('@strudel.cycles/tone'),
// import('@strudel.cycles/midi'), // TODO: find out why midi loads tone.js // import('@strudel.cycles/midi'), // TODO: find out why midi loads tone.js
import('@strudel.cycles/tonal'), import('@strudel.cycles/tonal'),
+5 -5
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/react", "name": "@strudel.cycles/react",
"version": "0.4.2", "version": "0.4.1",
"description": "React components for strudel", "description": "React components for strudel",
"main": "dist/index.cjs.js", "main": "dist/index.cjs.js",
"module": "dist/index.es.js", "module": "dist/index.es.js",
@@ -39,10 +39,10 @@
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@codemirror/lang-javascript": "^6.1.1", "@codemirror/lang-javascript": "^6.1.1",
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/tone": "^0.4.1", "@strudel.cycles/tone": "^0.4.0",
"@strudel.cycles/transpiler": "^0.4.1", "@strudel.cycles/transpiler": "^0.4.0",
"@strudel.cycles/webaudio": "^0.4.2", "@strudel.cycles/webaudio": "^0.4.1",
"@uiw/codemirror-themes": "^4.12.4", "@uiw/codemirror-themes": "^4.12.4",
"@uiw/react-codemirror": "^4.12.4", "@uiw/react-codemirror": "^4.12.4",
"react-hook-inview": "^4.5.0" "react-hook-inview": "^4.5.0"
+1 -2
View File
@@ -1,8 +1,7 @@
import React from 'react'; import React from 'react';
import { MiniRepl } from './components/MiniRepl'; import { MiniRepl } from './components/MiniRepl';
import 'tailwindcss/tailwind.css'; import 'tailwindcss/tailwind.css';
import core from '@strudel.cycles/core'; import { controls, evalScope } from '@strudel.cycles/core';
const {controls, evalScope} = core;
evalScope( evalScope(
controls, controls,
+5 -2
View File
@@ -7,7 +7,9 @@ function useHighlighting({ view, pattern, active, getTime }) {
useEffect(() => { useEffect(() => {
if (view) { if (view) {
if (pattern && active) { if (pattern && active) {
let frame = requestAnimationFrame(function updateHighlights() { let frame = requestAnimationFrame(updateHighlights);
function updateHighlights() {
try { try {
const audioTime = getTime(); const audioTime = getTime();
// force min framerate of 10 fps => fixes crash on tab refocus, where lastEnd could be far away // 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([]) }); view.dispatch({ effects: setHighlights.of([]) });
} }
frame = requestAnimationFrame(updateHighlights); frame = requestAnimationFrame(updateHighlights);
}); }
return () => { return () => {
cancelAnimationFrame(frame); cancelAnimationFrame(frame);
}; };
+1 -2
View File
@@ -1,6 +1,5 @@
import { useRef, useCallback, useEffect, useMemo, useState } from 'react'; import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
import core from '@strudel.cycles/core'; import { repl } from '@strudel.cycles/core';
const repl = core.repl
import { transpiler } from '@strudel.cycles/transpiler'; import { transpiler } from '@strudel.cycles/transpiler';
import usePostMessage from './usePostMessage.mjs'; 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/>. 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 { Pattern, isPattern } from '@strudel.cycles/core';
const { Pattern, isPattern } = core;
var writeMessage; var serialWriter;
var choosing = false; var choosing = false;
export async function getWriter(br = 38400) { export async function getWriter(br = 38400) {
@@ -15,30 +14,17 @@ export async function getWriter(br = 38400) {
return; return;
} }
choosing = true; choosing = true;
if (writeMessage) { if (serialWriter) {
return writeMessage; return serialWriter;
} }
if ('serial' in navigator) { if ('serial' in navigator) {
const port = await navigator.serial.requestPort(); const port = await navigator.serial.requestPort();
await port.open({ baudRate: br }); await port.open({ baudRate: br });
const encoder = new TextEncoder(); const textEncoder = new TextEncoderStream();
const writer = port.writable.getWriter(); const writableStreamClosed = textEncoder.readable.pipeTo(port.writable);
writeMessage = function (message, chk) { const writer = textEncoder.writable.getWriter();
const encoded = encoder.encode(message) serialWriter = function (message) {
if (!chk) { writer.write(message);
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);
}
}; };
} else { } else {
throw 'Webserial is not available in this browser.'; throw 'Webserial is not available in this browser.';
@@ -47,41 +33,18 @@ export async function getWriter(br = 38400) {
const latency = 0.1; const latency = 0.1;
// crc16 (CCITT-FALSE) https://gist.github.com/tijnkooijmans/10981093 Pattern.prototype.serial = function (...args) {
function crc16(data) { return this._withHap((hap) => {
const length = data.length if (!serialWriter) {
if (length == 0) { getWriter(...args);
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);
} }
const onTrigger = (time, hap, currentTime) => { const onTrigger = (time, hap, currentTime) => {
var message = ''; var message = '';
var chk = 0;
if (typeof hap.value === 'object') { if (typeof hap.value === 'object') {
if ('action' in hap.value) { if ('action' in hap.value) {
var action = hap.value['action']; message += hap.value['action'] + '(';
if (singlecharids) {
action = action.charAt(0);
}
message += action + '(';
var first = true; var first = true;
for (var [key, val] of Object.entries(hap.value)) { for (const [key, val] of Object.entries(hap.value)) {
if (key === 'action') { if (key === 'action') {
continue; continue;
} }
@@ -90,26 +53,19 @@ Pattern.prototype.serial = function (br=38400,sendcrc=false,singlecharids=false)
} else { } else {
message += ','; message += ',';
} }
if (singlecharids) { message += `${key}:${val}`;
key = key.charAt(0);
}
message += key + ':' + val;
} }
message += ')'; message += ')';
if (sendcrc) {
chk = crc16(message)
}
} else { } else {
for (const [key, val] of Object.entries(hap.value)) { for (const [key, val] of Object.entries(hap.value)) {
message += `${key}:${val}`; message += `${key}:${val};`;
} }
} }
} else { } else {
message = hap.value; message = hap.value;
} }
const offset = (time - currentTime + latency) * 1000; const offset = (time - currentTime + latency) * 1000;
window.setTimeout(serialWriter, offset, message);
window.setTimeout(function () {writeMessage(message, chk)}, offset);
}; };
return hap.setContext({ ...hap.context, onTrigger, dominantTrigger: true }); 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 = {}; let loadCache = {};
async function loadFont(name) { async function loadFont(name) {
if (loadCache[name]) { if (loadCache[name]) {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/soundfonts", "name": "@strudel.cycles/soundfonts",
"version": "0.4.2", "version": "0.4.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/soundfonts", "name": "@strudel.cycles/soundfonts",
"version": "0.4.2", "version": "0.4.1",
"description": "Soundsfont support for strudel", "description": "Soundsfont support for strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@@ -22,8 +22,8 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/webaudio": "^0.4.2", "@strudel.cycles/webaudio": "^0.4.1",
"sfumato": "^0.1.2", "sfumato": "^0.1.2",
"soundfont2": "^0.4.0" "soundfont2": "^0.4.0"
}, },
+1 -3
View File
@@ -1,6 +1,4 @@
import strudel from '@strudel.cycles/core'; import { Pattern } from '@strudel.cycles/core';
const { Pattern, getPlayableNoteValue, toMidi } = strudel;
import { getAudioContext } from '@strudel.cycles/webaudio';
import { loadSoundfont as _loadSoundfont, startPresetNote } from 'sfumato'; import { loadSoundfont as _loadSoundfont, startPresetNote } from 'sfumato';
Pattern.prototype.soundfont = function (sf, n = 0) { Pattern.prototype.soundfont = function (sf, n = 0) {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/tonal", "name": "@strudel.cycles/tonal",
"version": "0.4.1", "version": "0.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/tonal", "name": "@strudel.cycles/tonal",
"version": "0.4.1", "version": "0.4.0",
"description": "Tonal functions for strudel", "description": "Tonal functions for strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@@ -25,8 +25,8 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"@tonaljs/tonal": "^4.7.2", "@tonaljs/tonal": "^4.6.5",
"chord-voicings": "^0.0.1", "chord-voicings": "^0.0.1",
"webmidi": "^3.0.21" "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 { strict as assert } from 'assert';
import '../tonal.mjs'; // need to import this to add prototypes import '../tonal.mjs'; // need to import this to add prototypes
import core from '@strudel.cycles/core'; import { pure } from '@strudel.cycles/core';
const { pure } = core;
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
describe('tonal', () => { 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 { Note, Interval, Scale } from '@tonaljs/tonal';
import core from '@strudel.cycles/core'; import { Pattern, mod } from '@strudel.cycles/core';
const { Pattern, pattern, mod } = core;
// transpose note inside scale by offset steps // transpose note inside scale by offset steps
// function scaleOffset(scale: string, offset: number, note: string) { // function scaleOffset(scale: string, offset: number, note: string) {
+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/>. 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 { Pattern as _Pattern, stack, Hap, reify } from '@strudel.cycles/core';
const { Pattern, stack, reify, Hap } = core
import _voicings from 'chord-voicings'; import _voicings from 'chord-voicings';
const { dictionaryVoicing, minTopNoteDiff, lefthand } = _voicings.default || _voicings; // parcel module resolution fuckup const { dictionaryVoicing, minTopNoteDiff, lefthand } = _voicings.default || _voicings; // parcel module resolution fuckup
@@ -19,7 +17,7 @@ const getVoicing = (chord, lastVoicing, range = ['F3', 'A4']) =>
lastVoicing, lastVoicing,
}); });
// const Pattern = _Pattern; const Pattern = _Pattern;
Pattern.prototype.fmapNested = function (func) { Pattern.prototype.fmapNested = function (func) {
return new Pattern((span) => return new Pattern((span) =>
@@ -61,7 +59,7 @@ Pattern.prototype.voicings = function (range) {
Pattern.prototype._rootNotes = function (octave = 2) { Pattern.prototype._rootNotes = function (octave = 2) {
return this.fmap((value) => { return this.fmap((value) => {
const root = value.match(/^([a-gA-G][b#]?).*$/)[1]; const [_, root] = value.match(/^([a-gA-G][b#]?).*$/);
return root + octave; return root + octave;
}); });
}; };
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/tone", "name": "@strudel.cycles/tone",
"version": "0.4.1", "version": "0.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/tone", "name": "@strudel.cycles/tone",
"version": "0.4.1", "version": "0.4.0",
"description": "Tone.js API for strudel", "description": "Tone.js API for strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@@ -22,7 +22,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"tone": "^14.7.77" "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 '../tone.mjs';
import strudel from '@strudel.cycles/core'; import { pure } from '@strudel.cycles/core';
const { pure } = strudel;
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
describe('tone', () => { 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/>. 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 { Pattern } from '@strudel.cycles/core';
const { Pattern } = core;
import * as _Tone from 'tone'; import * as _Tone from 'tone';
// import Tone from here, to make sure to get the same AudioContext // import Tone from here, to make sure to get the same AudioContext
+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'; import { transpiler } from './transpiler.mjs';
export * 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", "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.", "description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.",
"main": "index.mjs", "main": "index.mjs",
"scripts": { "scripts": {
@@ -24,7 +24,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"acorn": "^8.8.1", "acorn": "^8.8.1",
"escodegen": "^2.0.0", "escodegen": "^2.0.0",
"estree-walker": "^3.0.1" "estree-walker": "^3.0.1"
+1 -2
View File
@@ -1,8 +1,7 @@
import escodegen from 'escodegen'; import escodegen from 'escodegen';
import { parse } from 'acorn'; import { parse } from 'acorn';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import core from '@strudel.cycles/core'; import { isNote } from '@strudel.cycles/core';
const { isNote } = core;
export function transpiler(input, options = {}) { export function transpiler(input, options = {}) {
const { wrapAsync = false, addReturn = true, simpleLocs = false } = options; const { wrapAsync = false, addReturn = true, simpleLocs = false } = options;
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/webaudio", "name": "@strudel.cycles/webaudio",
"version": "0.4.2", "version": "0.4.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/webaudio", "name": "@strudel.cycles/webaudio",
"version": "0.4.2", "version": "0.4.1",
"description": "Web Audio helpers for Strudel", "description": "Web Audio helpers for Strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@@ -30,6 +30,6 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "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'; import { logger } from '@strudel.cycles/core';
const { logger, toMidi } = core;
import { getAudioContext } from './index.mjs';
const bufferCache = {}; // string: Promise<ArrayBuffer> const bufferCache = {}; // string: Promise<ArrayBuffer>
const loadCache = {}; // 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/>. 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'; // import { Pattern, getFrequency, patternify2 } from '@strudel.cycles/core';
const { fromMidi, logger, toMidi } = strudel; import * as strudel from '@strudel.cycles/core';
import { fromMidi, logger, toMidi } from '@strudel.cycles/core';
import './feedbackdelay.mjs'; import './feedbackdelay.mjs';
import './reverb.mjs'; import './reverb.mjs';
import { getSampleBufferSource } from './sampler.mjs'; import { getSampleBufferSource } from './sampler.mjs';
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/webdirt", "name": "@strudel.cycles/webdirt",
"version": "0.4.1", "version": "0.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/webdirt", "name": "@strudel.cycles/webdirt",
"version": "0.4.1", "version": "0.4.0",
"description": "WebDirt integration for Strudel", "description": "WebDirt integration for Strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@@ -22,7 +22,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "^0.4.1", "@strudel.cycles/core": "^0.4.0",
"WebDirt": "github:dktr0/WebDirt" "WebDirt": "github:dktr0/WebDirt"
} }
} }
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@strudel.cycles/xen", "name": "@strudel.cycles/xen",
"version": "0.4.1", "version": "0.4.0",
"description": "Xenharmonic API for strudel", "description": "Xenharmonic API for strudel",
"main": "index.mjs", "main": "index.mjs",
"scripts": { "scripts": {
@@ -24,6 +24,6 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "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 Tune from './tunejs.js';
import core from '@strudel.cycles/core'; import { Pattern } from '@strudel.cycles/core';
const { Pattern } = core;
Pattern.prototype._tune = function (scale, tonic = 220) { Pattern.prototype._tune = function (scale, tonic = 220) {
const tune = new Tune(); 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/>. 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 { Pattern, mod } from '@strudel.cycles/core';
const { Pattern, mod } = core;
export function edo(name) { export function edo(name) {
if (!/^[1-9]+[0-9]*edo$/.test(name)) { if (!/^[1-9]+[0-9]*edo$/.test(name)) {
+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/>. 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 { cleanupDraw, cleanupUi, controls, evalScope, logger } from '@strudel.cycles/core';
const { cleanupDraw, cleanupUi, evalScope, logger } = core;
import { CodeMirror, cx, flash, useHighlighting, useStrudel } from '@strudel.cycles/react'; import { CodeMirror, cx, flash, useHighlighting, useStrudel } from '@strudel.cycles/react';
import { import {
getAudioContext, getAudioContext,
@@ -43,12 +42,11 @@ const modules = [
import('@strudel.cycles/osc'), import('@strudel.cycles/osc'),
import('@strudel.cycles/serial'), import('@strudel.cycles/serial'),
import('@strudel.cycles/soundfonts'), import('@strudel.cycles/soundfonts'),
import('@strudel.cycles/csound'),
]; ];
evalScope( evalScope(
// Tone, // Tone,
core, controls, // sadly, this cannot be exported from core direclty
{ WebDirt }, { WebDirt },
...modules, ...modules,
); );
+1 -2
View File
@@ -1,6 +1,5 @@
import XMarkIcon from '@heroicons/react/20/solid/XMarkIcon'; import XMarkIcon from '@heroicons/react/20/solid/XMarkIcon';
import core from '@strudel.cycles/core'; import { logger } from '@strudel.cycles/core';
const { logger } = core;
import { cx } from '@strudel.cycles/react'; import { cx } from '@strudel.cycles/react';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import React, { useContext, useCallback, useLayoutEffect, useRef, useState } from 'react'; import React, { useContext, useCallback, useLayoutEffect, useRef, useState } from 'react';
+1 -2
View File
@@ -1,5 +1,4 @@
import core from '@strudel.cycles/core'; import { Pattern, toMidi } from '@strudel.cycles/core';
const { Pattern, toMidi } = core;
import { samples } from '@strudel.cycles/webaudio'; import { samples } from '@strudel.cycles/webaudio';
export async function prebake({ isMock = false, baseDir = '.' } = {}) { export async function prebake({ isMock = false, baseDir = '.' } = {}) {
+15 -20
View File
@@ -5,8 +5,8 @@
// import * as tunes from './tunes.mjs'; // import * as tunes from './tunes.mjs';
// import { evaluate } from '@strudel.cycles/eval'; // import { evaluate } from '@strudel.cycles/eval';
import { evaluate } from '@strudel.cycles/transpiler'; import { evaluate } from '@strudel.cycles/transpiler';
import strudel from '@strudel.cycles/core'; import { evalScope } from '@strudel.cycles/core';
const { Pattern, evalScope } = strudel; import * as strudel from '@strudel.cycles/core';
import * as webaudio from '@strudel.cycles/webaudio'; import * as webaudio from '@strudel.cycles/webaudio';
import controls from '@strudel.cycles/core/controls.mjs'; import controls from '@strudel.cycles/core/controls.mjs';
// import gist from '@strudel.cycles/core/gist.js'; // import gist from '@strudel.cycles/core/gist.js';
@@ -85,51 +85,48 @@ const toneHelpersMocked = {
highpass: mockNode, highpass: mockNode,
}; };
Pattern.prototype.osc = function () { strudel.Pattern.prototype.osc = function () {
return this; return this;
}; };
Pattern.prototype.csound = function () { strudel.Pattern.prototype.tone = function () {
return this; return this;
}; };
Pattern.prototype.tone = function () { strudel.Pattern.prototype.webdirt = function () {
return this;
};
Pattern.prototype.webdirt = function () {
return this; return this;
}; };
// draw mock // draw mock
Pattern.prototype.pianoroll = function () { strudel.Pattern.prototype.pianoroll = function () {
return this; return this;
}; };
// speak mock // speak mock
Pattern.prototype.speak = function () { strudel.Pattern.prototype.speak = function () {
return this; return this;
}; };
// webaudio mock // webaudio mock
Pattern.prototype.wave = function () { strudel.Pattern.prototype.wave = function () {
return this; return this;
}; };
Pattern.prototype.filter = function () { strudel.Pattern.prototype.filter = function () {
return this; return this;
}; };
Pattern.prototype.adsr = function () { strudel.Pattern.prototype.adsr = function () {
return this; return this;
}; };
Pattern.prototype.out = function () { strudel.Pattern.prototype.out = function () {
return this; return this;
}; };
Pattern.prototype.soundfont = function () { strudel.Pattern.prototype.soundfont = function () {
return this; return this;
}; };
// tune mock // tune mock
Pattern.prototype.tune = function () { strudel.Pattern.prototype.tune = function () {
return this; return this;
}; };
Pattern.prototype.midi = function () { strudel.Pattern.prototype.midi = function () {
return this; return this;
}; };
@@ -159,7 +156,7 @@ const loadSoundfont = () => {};
evalScope( evalScope(
// Tone, // Tone,
strudel, strudel,
Pattern.prototype.bootstrap(), strudel.Pattern.prototype.bootstrap(),
toneHelpersMocked, toneHelpersMocked,
uiHelpersMocked, uiHelpersMocked,
controls, controls,
@@ -173,8 +170,6 @@ evalScope(
{ {
// gist, // gist,
// euclid, // euclid,
csound: id,
loadOrc: id,
mini, mini,
getDrawContext, getDrawContext,
getAudioContext, 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/8 -> 1/2: {\\"n\\":1,\\"s\\":\\"amencutup\\",\\"room\\":0.5}",
"3/4 -> 7/8: {\\"n\\":3,\\"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}", "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\\":\\"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}", "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}", "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`] = ` exports[`renders tunes > tune: barryHarris 1`] = `
[ [
"0/1 -> 2/1: {\\"note\\":\\"C3\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.4722222222222222}", "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`] = ` exports[`renders tunes > tune: bridgeIsOver 1`] = `
[ [
"0/1 -> 5/26: {\\"note\\":\\"B2\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.46759259259259256}", "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`] = ` exports[`renders tunes > tune: delay 1`] = `
[ [
"0/1 -> 1/2: {\\"s\\":\\"bd\\",\\"delay\\":0,\\"delaytime\\":0.16,\\"delayfeedback\\":0.8,\\"speed\\":-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`] = ` exports[`renders tunes > tune: goodTimes 1`] = `
[ [
"0/1 -> 1/2: {\\"note\\":\\"F3\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.49537037037037035}", "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`] = ` exports[`renders tunes > tune: meltingsubmarine 1`] = `
[ [
"0/1 -> 3/2: {\\"s\\":\\"bd\\",\\"speed\\":0.7519542165100574}", "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`] = ` exports[`renders tunes > tune: undergroundPlumber 1`] = `
[ [
"0/1 -> 3/16: {\\"s\\":\\"bd\\",\\"gain\\":0.7}", "0/1 -> 3/16: {\\"s\\":\\"bd\\",\\"gain\\":0.7}",
-287
View File
@@ -162,290 +162,3 @@ stack(
drums.fast(2), drums.fast(2),
synths synths
).slow(2)`; */ ).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/>. 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) export const tetrisMini = `note(\`[[e5 [b4 c5] d5 [c5 b4]]
stack( [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( seq(
"~", "~",
"~", "~",
@@ -66,8 +83,7 @@ stack(
).note().slow(51); ).note().slow(51);
`; `;
export const giantSteps = `// John Coltrane - Giant Steps export const giantSteps = `stack(
stack(
// melody // melody
seq( seq(
"[F#5 D5] [B4 G4] Bb4 [B4 A4]", "[F#5 D5] [B4 G4] Bb4 [B4 A4]",
@@ -91,8 +107,34 @@ stack(
) )
).slow(20).note()`; ).slow(20).note()`;
export const zeldasRescue = `// Koji Kondo - Princess Zelda's Rescue export const giantStepsReggae = `stack(
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 // melody
\`[B3@2 D4] [A3@2 [G3 A3]] [B3@2 D4] [A3] \`[B3@2 D4] [A3@2 [G3 A3]] [B3@2 D4] [A3]
[B3@2 D4] [A4@2 G4] [D4@2 [C4 B3]] [A3] [B3@2 D4] [A4@2 G4] [D4@2 [C4 B3]] [A3]
@@ -115,9 +157,7 @@ stack(
.room(1) .room(1)
`; `;
export const caverave = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const caverave = `const keys = x => x.s('sawtooth').cutoff(1200).gain(.5).attack(0).decay(.16).sustain(.3).release(.1);
// by Felix Roos
const keys = x => x.s('sawtooth').cutoff(1200).gain(.5).attack(0).decay(.16).sustain(.3).release(.1);
const drums = stack( const drums = stack(
s("bd*2").mask("<x@7 ~>/8").gain(.8), s("bd*2").mask("<x@7 ~>/8").gain(.8),
@@ -161,7 +201,90 @@ stack(
).s() ).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]" "0,2,[7 6]"
.add("<0 1 2 3 4 5 7 8>") .add("<0 1 2 3 4 5 7 8>")
.scale('C bebop major') .scale('C bebop major')
@@ -170,9 +293,7 @@ export const barryHarris = `// adapted from a Barry Harris excercise
.note().piano() .note().piano()
`; `;
export const blippyRhodes = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const blippyRhodes = `samples({
// by Felix Roos
samples({
bd: 'samples/tidal/bd/BT0A0D0.wav', bd: 'samples/tidal/bd/BT0A0D0.wav',
sn: 'samples/tidal/sn/ST0T0S3.wav', sn: 'samples/tidal/sn/ST0T0S3.wav',
hh: 'samples/tidal/hh/000_hh3closedhh.wav', hh: 'samples/tidal/hh/000_hh3closedhh.wav',
@@ -211,9 +332,7 @@ stack(
.s('sawtooth').cutoff(600), .s('sawtooth').cutoff(600),
).fast(3/2)`; ).fast(3/2)`;
export const wavyKalimba = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const wavyKalimba = `samples({
// by Felix Roos
samples({
'kalimba': { c5:'https://freesound.org/data/previews/536/536549_11935698-lq.mp3' } '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']) const scales = cat('C major', 'C mixolydian', 'F lydian', ['F minor', 'Db major'])
@@ -241,9 +360,37 @@ stack(
.delay(.2) .delay(.2)
`; `;
export const festivalOfFingers = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // TODO: rework tune to use freq
// by Felix Roos /*
const chords = "<Cm7 Fm7 G7 F#7>"; 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( stack(
chords.voicings().struct("x(3,8,-1)").velocity(.5).off(1/7,x=>x.transpose(12).velocity(.2)), 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)"), chords.rootNotes(2).struct("x(4,8,-2)"),
@@ -256,8 +403,7 @@ stack(
.note().piano()`; .note().piano()`;
// iter, echo, echoWith // iter, echo, echoWith
export const undergroundPlumber = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ 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' })
// by Felix Roos, inspired by Friendship - Let's not talk about it (1979) :)
samples({ bd: 'bd/BT0A0D0.wav', sn: 'sn/ST0T0S3.wav', hh: 'hh/000_hh3closedhh.wav', cp: 'cp/HANDCLP0.wav', 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/') }, 'https://loophole-letters.vercel.app/samples/tidal/')
@@ -282,9 +428,7 @@ stack(
.fast(2/3) .fast(2/3)
.pianoroll({})`; .pianoroll({})`;
export const bridgeIsOver = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const bridgeIsOver = `samples({mad:'https://freesound.org/data/previews/22/22274_109943-lq.mp3'})
// by Felix Roos, bassline by BDP - The Bridge Is Over
samples({mad:'https://freesound.org/data/previews/22/22274_109943-lq.mp3'})
stack( stack(
stack( stack(
"c3*2 [[c3@1.4 bb2] ab2] gb2*2 <[[gb2@1.4 ab2] bb2] gb2>".legato(".5 1".fast(2)).velocity(.8), "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() .pianoroll()
`; `;
export const goodTimes = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const goodTimes = `const scale = cat('C3 dorian','Bb2 major').slow(4);
// by Felix Roos
const scale = cat('C3 dorian','Bb2 major').slow(4);
stack( stack(
"2*4".add(12).scale(scale) "2*4".add(12).scale(scale)
.off(1/8,x=>x.scaleTranspose("2")).fast(2) .off(1/8,x=>x.scaleTranspose("2")).fast(2)
@@ -325,9 +467,7 @@ stack(
.slow(2) .slow(2)
.pianoroll({maxMidi:100,minMidi:20})`; .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/ export const echoPiano = `"<0 2 [4 6](3,4,1) 3*2>"
// by Felix Roos
"<0 2 [4 6](3,4,1) 3*2>"
.scale('D minor') .scale('D minor')
.color('salmon') .color('salmon')
.off(1/4, x=>x.scaleTranspose(2).color('green')) .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() .note().piano()
.pianoroll()`; .pianoroll()`;
export const sml1 = `// Hirokazu Tanaka - World 1-1 export const sml1 = `
stack( stack(
// melody // melody
\`< \`<
@@ -372,9 +512,30 @@ stack(
.legato(.5) .legato(.5)
).fast(2).note()`; ).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 // TODO: does not work on linux (at least for me..)
samples({ 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' }, bell: { c6: 'https://freesound.org/data/previews/411/411089_5121236-lq.mp3' },
bass: { d2: 'https://freesound.org/data/previews/608/608286_13074022-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'}) .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/ export const waa2 = `n(
// by Felix Roos
n(
"a4 [a3 c3] a3 c3" "a4 [a3 c3] a3 c3"
.sub("<7 12 5 12>".slow(2)) .sub("<7 12 5 12>".slow(2))
.off(1/4,x=>x.add(7)) .off(1/4,x=>x.add(7))
@@ -411,9 +570,7 @@ n(
.room(.5) .room(.5)
`; `;
export const hyperpop = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const hyperpop = `const lfo = cosine.slow(15);
// by Felix Roos
const lfo = cosine.slow(15);
const lfo2 = sine.slow(16); const lfo2 = sine.slow(16);
const filter1 = x=>x.cutoff(lfo2.range(300,3000)); const filter1 = x=>x.cutoff(lfo2.range(300,3000));
const filter2 = x=>x.hcutoff(lfo.range(1000,6000)).cutoff(4000) const filter2 = x=>x.hcutoff(lfo.range(1000,6000)).cutoff(4000)
@@ -462,9 +619,7 @@ stack(
).slow(2) ).slow(2)
// strudel disable-highlighting`; // strudel disable-highlighting`;
export const festivalOfFingers3 = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const festivalOfFingers3 = `"[-7*3],0,2,6,[8 7]"
// by Felix Roos
"[-7*3],0,2,6,[8 7]"
.echoWith(4,1/4, (x,n)=>x .echoWith(4,1/4, (x,n)=>x
.add(n*7) .add(n*7)
.velocity(1/(n+1)) .velocity(1/(n+1))
@@ -480,9 +635,33 @@ export const festivalOfFingers3 = `// licensed with CC BY-NC-SA 4.0 https://crea
.note().piano() .note().piano()
//.pianoroll({maxMidi:160})`; //.pianoroll({maxMidi:160})`;
export const meltingsubmarine = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const bossa = `
// by Felix Roos const scales = sequence('C minor', ['D locrian', 'G phrygian'], 'Bb2 minor', ['C locrian','F phrygian']).slow(4)
samples({ 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'], 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'], sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'], hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
@@ -522,9 +701,68 @@ stack(
) )
.slow(3/2)`; .slow(3/2)`;
export const outroMusic = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const swimmingWithSoundfonts = `stack(
// by Felix Roos n(
samples({ "~",
"~",
"~",
"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'], 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'], sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'], hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
@@ -551,9 +789,7 @@ samples({
//.pianoroll({autorange:1,vertical:1,fold:0}) //.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/ 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'] },
// 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'] },
'github:cleary/samples-flbass/main/') 'github:cleary/samples-flbass/main/')
samples({ samples({
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'], 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")) .stack(s("bd:1*2,~ sd:0,[~ hh:0]*2"))
.pianoroll({vertical:1})`; .pianoroll({vertical:1})`;
export const chop = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const bossaRandom = `const chords = "<Am7 Am7 Dm7 E7>"
// by Felix Roos const roots = chords.rootNotes(2)
samples({ p: 'https://cdn.freesound.org/previews/648/648433_11943129-lq.mp3' })
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") s("p")
.loopAt(32,1) .loopAt(32,1)
@@ -590,18 +835,14 @@ s("p")
.sustain(.6) .sustain(.6)
`; `;
export const delay = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const delay = `stack(
// by Felix Roos
stack(
s("bd <sd cp>") s("bd <sd cp>")
.delay("<0 .5>") .delay("<0 .5>")
.delaytime(".16 | .33") .delaytime(".16 | .33")
.delayfeedback(".6 | .8") .delayfeedback(".6 | .8")
).sometimes(x=>x.speed("-1"))`; ).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/ export const orbit = `stack(
// by Felix Roos
stack(
s("bd <sd cp>") s("bd <sd cp>")
.delay(.5) .delay(.5)
.delaytime(.33) .delaytime(.33)
@@ -613,9 +854,7 @@ stack(
.orbit(2) .orbit(2)
).sometimes(x=>x.speed("-1"))`; ).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/ export const belldub = `samples({ bell: {b4:'https://cdn.freesound.org/previews/339/339809_5121236-lq.mp3'}})
// by Felix Roos
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 // "Hand Bells, B, Single.wav" by InspectorJ (www.jshaw.co.uk) of Freesound.org
stack( stack(
// bass // bass
@@ -646,9 +885,7 @@ stack(
.mask("<1 0>/8") .mask("<1 0>/8")
).slow(5)`; ).slow(5)`;
export const dinofunk = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const dinofunk = `samples({bass:'https://cdn.freesound.org/previews/614/614637_2434927-hq.mp3',
// by Felix Roos
samples({bass:'https://cdn.freesound.org/previews/614/614637_2434927-hq.mp3',
dino:{b4:'https://cdn.freesound.org/previews/316/316403_5123851-hq.mp3'}}) dino:{b4:'https://cdn.freesound.org/previews/316/316403_5123851-hq.mp3'}})
stack( 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) 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/ export const sampleDemo = `stack(
// by Felix Roos
stack(
// percussion // percussion
s("[woodblock:1 woodblock:2*2] snare_rim:0,gong/8,brakedrum:1(3,8),~@3 cowbell:3") s("[woodblock:1 woodblock:2*2] snare_rim:0,gong/8,brakedrum:1(3,8),~@3 cowbell:3")
.sometimes(x=>x.speed(2)), .sometimes(x=>x.speed(2)),
@@ -682,9 +917,7 @@ stack(
.release(.1).room(.5) .release(.1).room(.5)
)`; )`;
export const holyflute = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const holyflute = `"c3 eb3(3,8) c4/2 g3*2"
// by Felix Roos
"c3 eb3(3,8) c4/2 g3*2"
.superimpose( .superimpose(
x=>x.slow(2).add(12), x=>x.slow(2).add(12),
x=>x.slow(4).sub(5) 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}) .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/ export const flatrave = `stack(
// by Felix Roos
stack(
s("bd*2,~ [cp,sd]").bank('RolandTR909'), s("bd*2,~ [cp,sd]").bank('RolandTR909'),
s("hh:1*4").sometimes(fast("2")) s("hh:1*4").sometimes(fast("2"))
@@ -718,9 +949,7 @@ stack(
.decay(.05).sustain(0).delay(.2).degradeBy(.5).mask("<0 1>/16") .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/ export const amensister = `await samples('github:tidalcycles/Dirt-Samples/master')
// by Felix Roos
await samples('github:tidalcycles/Dirt-Samples/master')
stack( stack(
// amen // amen
@@ -752,9 +981,7 @@ stack(
n("0 1").s("east").delay(.5).degradeBy(.8).speed(rand.range(.5,1.5)) n("0 1").s("east").delay(.5).degradeBy(.8).speed(rand.range(.5,1.5))
).reset("<x@7 x(5,8)>")`; ).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/ export const juxUndTollerei = `note("c3 eb3 g3 bb3").palindrome()
// by Felix Roos
note("c3 eb3 g3 bb3").palindrome()
.s('sawtooth') .s('sawtooth')
.jux(x=>x.rev().color('green').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')) .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) .room(.6)
.delay(.5).delaytime(.1).delayfeedback(.4) .delay(.5).delaytime(.1).delayfeedback(.4)
.pianoroll()`; .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 { MiniRepl as _MiniRepl } from '@strudel.cycles/react';
import { samples } from '@strudel.cycles/webaudio'; import { samples } from '@strudel.cycles/webaudio';
import { prebake } from '../repl/src/prebake.mjs'; import { prebake } from '../repl/src/prebake.mjs';
@@ -7,9 +7,9 @@ fetch('https://strudel.tidalcycles.org/EmuSP12.json')
.then((res) => res.json()) .then((res) => res.json())
.then((json) => samples(json, 'https://strudel.tidalcycles.org/EmuSP12/')); .then((json) => samples(json, 'https://strudel.tidalcycles.org/EmuSP12/'));
core.evalScope( evalScope(
core, controls,
// import('@strudel.cycles/core'), import('@strudel.cycles/core'),
// import('@strudel.cycles/tone'), // import('@strudel.cycles/tone'),
import('@strudel.cycles/tonal'), import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'), import('@strudel.cycles/mini'),
+1 -1
View File
@@ -18,7 +18,7 @@ function renderAsMDX(name) {
} }
return `### ${item.longname} 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); // console.log(_, 'a', a, 'b', b);
return `<a href="#${a}${b ? `-${b}` : ''}">${a}${b ? `#${b}` : ''}</a>`; 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`] = ` exports[`runs examples > example "bandf" example index 0 1`] = `
[ [
"0/1 -> 1/2: {\\"s\\":\\"bd\\",\\"bandf\\":1000}", "0/1 -> 1/2: {\\"s\\":\\"bd\\",\\"bandf\\":1000}",
@@ -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`] = ` exports[`runs examples > example "early" example index 0 1`] = `
[ [
"0/1 -> 1/2: {\\"s\\":\\"bd\\"}", "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`] = ` exports[`runs examples > example "fast" example index 0 1`] = `
[ [
"0/1 -> 1/4: {\\"s\\":\\"bd\\"}", "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()],
});