mirror of
https://codeberg.org/uzu/strudel
synced 2026-10-01 15:51:31 -04:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 85ee97c9ef | |||
| 479041ebb6 | |||
| ad080d02b3 | |||
| e9e7e02547 | |||
| 2ffc6c9601 | |||
| 3a9349d9f5 | |||
| bd3f2845f8 | |||
| 2d320c459b | |||
| ff9f4a8a0d | |||
| d7b94807cb | |||
| 2c16a37a68 | |||
| 717e4a2c1d | |||
| c225b4719f | |||
| 25f762c393 | |||
| b75561e3c7 | |||
| abaab89061 | |||
| e409086b34 | |||
| 532f1435d9 | |||
| 6cb61ade03 | |||
| a40219ba06 | |||
| 9c99f5ada2 | |||
| 2d72c8faa0 | |||
| b188fc6b6d | |||
| bfb22228cc | |||
| 9714d88d61 | |||
| bdddb91510 | |||
| 5bf1805534 | |||
| 202685e8c4 | |||
| aba633e16e | |||
| da45ce689c | |||
| acea7e435e | |||
| cb4810cbbc | |||
| 5ec205b383 | |||
| ebc0b293a9 | |||
| dffeac41af | |||
| 943d78f268 | |||
| 5ac59e95dd | |||
| 69a5d62027 | |||
| 226cf356d6 | |||
| 73d49f152a | |||
| cdba9efb59 |
@@ -454,9 +454,6 @@ export const { drive } = registerControl('drive');
|
||||
*/
|
||||
export const { channels, ch } = registerControl('channels', 'ch');
|
||||
|
||||
// superdirt only
|
||||
export const { phaserrate, phasr } = registerControl('phaserrate', 'phasr');
|
||||
|
||||
/**
|
||||
* Phaser audio effect that approximates popular guitar pedals.
|
||||
*
|
||||
@@ -468,7 +465,11 @@ export const { phaserrate, phasr } = registerControl('phaserrate', 'phasr');
|
||||
* .phaser("<1 2 4 8>")
|
||||
*
|
||||
*/
|
||||
export const { phaser, ph } = registerControl(['phaser', 'phaserdepth', 'phasercenter', 'phasersweep'], 'ph');
|
||||
export const { phaserrate, ph, phaser } = registerControl(
|
||||
['phaserrate', 'phaserdepth', 'phasercenter', 'phasersweep'],
|
||||
'ph',
|
||||
'phaser',
|
||||
);
|
||||
|
||||
/**
|
||||
* The frequency sweep range of the lfo for the phaser effect. Defaults to 2000
|
||||
|
||||
+107
-31
@@ -809,15 +809,6 @@ export class Pattern {
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Multi-pattern functions
|
||||
|
||||
/**
|
||||
* Stacks the given pattern(s) to the current pattern.
|
||||
* @name stack
|
||||
* @memberof Pattern
|
||||
* @example
|
||||
* s("hh*4").stack(
|
||||
* note("c4(5,8)")
|
||||
* )
|
||||
*/
|
||||
stack(...pats) {
|
||||
return stack(this, ...pats);
|
||||
}
|
||||
@@ -826,30 +817,9 @@ export class Pattern {
|
||||
return sequence(this, ...pats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given pattern(s) to the current pattern.
|
||||
* @name seq
|
||||
* @memberof Pattern
|
||||
* @synonyms sequence, fastcat
|
||||
* @example
|
||||
* s("hh*4").seq(
|
||||
* note("c4(5,8)")
|
||||
* )
|
||||
*/
|
||||
seq(...pats) {
|
||||
return sequence(this, ...pats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given pattern(s) to the next cycle.
|
||||
* @name cat
|
||||
* @memberof Pattern
|
||||
* @synonyms slowcat
|
||||
* @example
|
||||
* s("hh*4").cat(
|
||||
* note("c4(5,8)")
|
||||
* )
|
||||
*/
|
||||
cat(...pats) {
|
||||
return cat(this, ...pats);
|
||||
}
|
||||
@@ -1280,6 +1250,12 @@ export function reify(thing) {
|
||||
* @example
|
||||
* stack("g3", "b3", ["e4", "d4"]).note()
|
||||
* // "g3,b3,[e4,d4]".note()
|
||||
*
|
||||
* @example
|
||||
* // As a chained function:
|
||||
* s("hh*4").stack(
|
||||
* note("c4(5,8)")
|
||||
* )
|
||||
*/
|
||||
export function stack(...pats) {
|
||||
// Array test here is to avoid infinite recursions..
|
||||
@@ -1408,6 +1384,11 @@ export function slowcatPrime(...pats) {
|
||||
* cat("e5", "b4", ["d5", "c5"]).note()
|
||||
* // "<e5 b4 [d5 c5]>".note()
|
||||
*
|
||||
* @example
|
||||
* // As a chained function:
|
||||
* s("hh*4").cat(
|
||||
* note("c4(5,8)")
|
||||
* )
|
||||
*/
|
||||
export function cat(...pats) {
|
||||
return slowcat(...pats);
|
||||
@@ -1430,6 +1411,36 @@ export function arrange(...sections) {
|
||||
return s_cat(...sections).slow(total);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similarly to `arrange`, allows you to arrange multiple patterns together over multiple cycles.
|
||||
* Unlike `arrange`, you specify a start and stop time for each pattern rather than duration, which
|
||||
* means that patterns can overlap.
|
||||
* @return {Pattern}
|
||||
* @example
|
||||
seqPLoop([0, 2, "bd(3,8)"],
|
||||
[1, 3, "cp(3,8)"]
|
||||
)
|
||||
.sound()
|
||||
*/
|
||||
export function seqPLoop(...parts) {
|
||||
let total = Fraction(0);
|
||||
const pats = [];
|
||||
for (let part of parts) {
|
||||
if (part.length == 2) {
|
||||
part.unshift(total);
|
||||
}
|
||||
total = part[1];
|
||||
}
|
||||
|
||||
return stack(
|
||||
...parts.map(([start, stop, pat]) =>
|
||||
pure(reify(pat)).compress(Fraction(start).div(total), Fraction(stop).div(total)),
|
||||
),
|
||||
)
|
||||
.slow(total)
|
||||
.innerJoin(); // or resetJoin or restartJoin ??
|
||||
}
|
||||
|
||||
export function fastcat(...pats) {
|
||||
let result = slowcat(...pats);
|
||||
if (pats.length > 1) {
|
||||
@@ -1448,12 +1459,18 @@ export function sequence(...pats) {
|
||||
}
|
||||
|
||||
/** Like **cat**, but the items are crammed into one cycle.
|
||||
* @synonyms fastcat, sequence
|
||||
* @synonyms sequence, fastcat
|
||||
* @example
|
||||
* seq("e5", "b4", ["d5", "c5"]).note()
|
||||
* // "e5 b4 [d5 c5]".note()
|
||||
*
|
||||
* @example
|
||||
* // As a chained function:
|
||||
* s("hh*4").seq(
|
||||
* note("c4(5,8)")
|
||||
* )
|
||||
*/
|
||||
|
||||
export function seq(...pats) {
|
||||
return fastcat(...pats);
|
||||
}
|
||||
@@ -2016,6 +2033,34 @@ export const { zoomArc, zoomarc } = register(['zoomArc', 'zoomarc'], function (a
|
||||
return pat.zoom(a.begin, a.end);
|
||||
});
|
||||
|
||||
/**
|
||||
* Splits a pattern into the given number of slices, and plays them according to a pattern of slice numbers.
|
||||
* Similar to `slice`, but slices up patterns rather than sound samples.
|
||||
* @param {number} number of slices
|
||||
* @param {number} slices to play
|
||||
* @example
|
||||
* note("0 1 2 3 4 5 6 7".scale('c:mixolydian'))
|
||||
*.bite(4, "3 2 1 0")
|
||||
* @example
|
||||
* sound("bd - bd bd*2, - sd:6 - sd:5 sd:1 - [- sd:2] -, hh [- cp:7]")
|
||||
.bank("RolandTR909").speed(1.2)
|
||||
.bite(4, "0 0 [1 2] <3 2> 0 0 [2 1] 3")
|
||||
*/
|
||||
export const bite = register(
|
||||
'bite',
|
||||
(npat, ipat, pat) => {
|
||||
return ipat
|
||||
.fmap((i) => (n) => {
|
||||
const a = Fraction(i).div(n).mod(1);
|
||||
const b = a.add(Fraction(1).div(n));
|
||||
return pat.zoom(a, b);
|
||||
})
|
||||
.appLeft(npat)
|
||||
.squeezeJoin();
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
/**
|
||||
* Selects the given fraction of the pattern and repeats that part to fill the remainder of the cycle.
|
||||
* @param {number} fraction fraction to select
|
||||
@@ -2447,6 +2492,37 @@ Pattern.prototype.tag = function (tag) {
|
||||
return this.withContext((ctx) => ({ ...ctx, tags: (ctx.tags || []).concat([tag]) }));
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters haps using the given function
|
||||
* @name filter
|
||||
* @param {Function} test function to test Hap
|
||||
* @example
|
||||
* s("hh!7 oh").filter(hap => hap.value.s==='hh')
|
||||
*/
|
||||
export const filter = register('filter', (test, pat) => pat.withHaps((haps) => haps.filter(test)));
|
||||
|
||||
/**
|
||||
* Filters haps by their begin time
|
||||
* @name filterWhen
|
||||
* @noAutocomplete
|
||||
* @param {Function} test function to test Hap.whole.begin
|
||||
*/
|
||||
export const filterWhen = register('filterWhen', (test, pat) => pat.filter((h) => test(h.whole.begin)));
|
||||
|
||||
/**
|
||||
* Use within to apply a function to only a part of a pattern.
|
||||
* @name within
|
||||
* @param {number} start start within cycle (0 - 1)
|
||||
* @param {number} end end within cycle (0 - 1). Must be > start
|
||||
* @param {Function} func function to be applied to the sub-pattern
|
||||
*/
|
||||
export const within = register('within', (a, b, fn, pat) =>
|
||||
stack(
|
||||
fn(pat.filterWhen((t) => t.cyclePos() >= a && t.cyclePos() <= b)),
|
||||
pat.filterWhen((t) => t.cyclePos() < a || t.cyclePos() > b),
|
||||
),
|
||||
);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Tactus-related functions, i.e. ones that do stepwise
|
||||
// transformations
|
||||
|
||||
@@ -169,13 +169,18 @@ const refs = {};
|
||||
export async function midin(input) {
|
||||
if (isPattern(input)) {
|
||||
throw new Error(
|
||||
`.midi does not accept Pattern input. Make sure to pass device name with single quotes. Example: .midi('${
|
||||
`midin: does not accept Pattern as input. Make sure to pass device name with single quotes. Example: midin('${
|
||||
WebMidi.outputs?.[0]?.name || 'IAC Driver Bus 1'
|
||||
}')`,
|
||||
);
|
||||
}
|
||||
const initial = await enableWebMidi(); // only returns on first init
|
||||
const device = getDevice(input, WebMidi.inputs);
|
||||
if (!device) {
|
||||
throw new Error(
|
||||
`midiin: device "${input}" not found.. connected devices: ${getMidiDeviceNamesString(WebMidi.inputs)}`,
|
||||
);
|
||||
}
|
||||
if (initial) {
|
||||
const otherInputs = WebMidi.inputs.filter((o) => o.name !== device.name);
|
||||
logger(
|
||||
|
||||
+173
-223
@@ -1,253 +1,203 @@
|
||||
/*
|
||||
mini.mjs - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/mini/mini.mjs>
|
||||
Copyright (C) 2024 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/mini/mini.mjs>
|
||||
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 * as krill from './krill-parser.js';
|
||||
import * as strudel from '@strudel/core';
|
||||
import Fraction, { lcm } from '@strudel/core/fraction.mjs';
|
||||
|
||||
const randOffset = 0.0003;
|
||||
|
||||
const applyOptions = (parent, enter) => (pat, i) => {
|
||||
const ast = parent.source_[i];
|
||||
const options = ast.options_;
|
||||
const ops = options?.ops;
|
||||
const tactus_source = pat.__tactus_source;
|
||||
if (ops) {
|
||||
for (const op of ops) {
|
||||
switch (op.type_) {
|
||||
case 'stretch': {
|
||||
const legalTypes = ['fast', 'slow'];
|
||||
const { type, amount } = op.arguments_;
|
||||
if (!legalTypes.includes(type)) {
|
||||
throw new Error(`mini: stretch: type must be one of ${legalTypes.join('|')} but got ${type}`);
|
||||
}
|
||||
pat = strudel.reify(pat)[type](enter(amount));
|
||||
break;
|
||||
}
|
||||
case 'replicate': {
|
||||
const { amount } = op.arguments_;
|
||||
pat = strudel.reify(pat);
|
||||
pat = pat._repeatCycles(amount)._fast(amount);
|
||||
break;
|
||||
}
|
||||
case 'bjorklund': {
|
||||
if (op.arguments_.rotation) {
|
||||
pat = pat.euclidRot(enter(op.arguments_.pulse), enter(op.arguments_.step), enter(op.arguments_.rotation));
|
||||
} else {
|
||||
pat = pat.euclid(enter(op.arguments_.pulse), enter(op.arguments_.step));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'degradeBy': {
|
||||
pat = strudel
|
||||
.reify(pat)
|
||||
._degradeByWith(strudel.rand.early(randOffset * op.arguments_.seed), op.arguments_.amount ?? 0.5);
|
||||
break;
|
||||
}
|
||||
case 'tail': {
|
||||
const friend = enter(op.arguments_.element);
|
||||
pat = pat.fmap((a) => (b) => (Array.isArray(a) ? [...a, b] : [a, b])).appLeft(friend);
|
||||
break;
|
||||
}
|
||||
case 'range': {
|
||||
const friend = enter(op.arguments_.element);
|
||||
pat = strudel.reify(pat);
|
||||
const arrayRange = (start, stop, step = 1) =>
|
||||
Array.from({ length: Math.abs(stop - start) / step + 1 }, (value, index) =>
|
||||
start < stop ? start + index * step : start - index * step,
|
||||
);
|
||||
let range = (apat, bpat) => apat.squeezeBind((a) => bpat.bind((b) => strudel.fastcat(...arrayRange(a, b))));
|
||||
pat = range(pat, friend);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.warn(`operator "${op.type_}" not implemented`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pat.__tactus_source = pat.__tactus_source || tactus_source;
|
||||
return pat;
|
||||
// "parser"
|
||||
let token_types = {
|
||||
open_polyrhythm: /^\[/,
|
||||
close_polyrhythm: /^\]/,
|
||||
open_polymeter: /^\{/,
|
||||
close_polymeter: /^\}/,
|
||||
open_slowpolymeter: /^\</,
|
||||
close_slowpolymeter: /^\>/,
|
||||
subpat_delimiter: /^,/,
|
||||
fast: /^\*/,
|
||||
slow: /^\//,
|
||||
// is there a better way without back-tracking for whole numbers?
|
||||
// float: /^(\d*\.)?\d+/,
|
||||
plain: /^[a-zA-Z0-9\.\#]+/,
|
||||
};
|
||||
|
||||
// expects ast from mini2ast + quoted mini string + optional callback when a node is entered
|
||||
export function patternifyAST(ast, code, onEnter, offset = 0) {
|
||||
onEnter?.(ast);
|
||||
const enter = (node) => patternifyAST(node, code, onEnter, offset);
|
||||
switch (ast.type_) {
|
||||
case 'pattern': {
|
||||
// resolveReplications(ast);
|
||||
const children = ast.source_.map((child) => enter(child)).map(applyOptions(ast, enter));
|
||||
const alignment = ast.arguments_.alignment;
|
||||
const with_tactus = children.filter((child) => child.__tactus_source);
|
||||
let pat;
|
||||
switch (alignment) {
|
||||
case 'stack': {
|
||||
pat = strudel.stack(...children);
|
||||
if (with_tactus.length) {
|
||||
pat.tactus = lcm(...with_tactus.map((x) => Fraction(x.tactus)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'polymeter_slowcat': {
|
||||
pat = strudel.stack(...children.map((child) => child._slow(child.__weight)));
|
||||
if (with_tactus.length) {
|
||||
pat.tactus = lcm(...with_tactus.map((x) => Fraction(x.tactus)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'polymeter': {
|
||||
// polymeter
|
||||
const stepsPerCycle = ast.arguments_.stepsPerCycle
|
||||
? enter(ast.arguments_.stepsPerCycle).fmap((x) => strudel.Fraction(x))
|
||||
: strudel.pure(strudel.Fraction(children.length > 0 ? children[0].__weight : 1));
|
||||
let token_offset;
|
||||
function next_token(code, global_offset) {
|
||||
for (let type in token_types) {
|
||||
const match = code.match(token_types[type]);
|
||||
if (match) {
|
||||
const leftpad = match[0].length - match[0].trimLeft().length;
|
||||
const rightpad = match[0].length - match[0].trimRight().length;
|
||||
const result = {
|
||||
type,
|
||||
value: match[0],
|
||||
location: [token_offset + leftpad + global_offset, token_offset + match[0].length + global_offset - rightpad],
|
||||
};
|
||||
token_offset += match[0].length;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
throw new Error('could not match "' + code + '"');
|
||||
}
|
||||
|
||||
const aligned = children.map((child) => child.fast(stepsPerCycle.fmap((x) => x.div(child.__weight))));
|
||||
pat = strudel.stack(...aligned);
|
||||
break;
|
||||
}
|
||||
case 'rand': {
|
||||
pat = strudel.chooseInWith(strudel.rand.early(randOffset * ast.arguments_.seed).segment(1), children);
|
||||
if (with_tactus.length) {
|
||||
pat.tactus = lcm(...with_tactus.map((x) => Fraction(x.tactus)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'feet': {
|
||||
pat = strudel.fastcat(...children);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const weightedChildren = ast.source_.some((child) => !!child.options_?.weight);
|
||||
if (weightedChildren) {
|
||||
const weightSum = ast.source_.reduce(
|
||||
(sum, child) => sum.add(child.options_?.weight || strudel.Fraction(1)),
|
||||
strudel.Fraction(0),
|
||||
);
|
||||
pat = strudel.timeCat(
|
||||
...ast.source_.map((child, i) => [child.options_?.weight || strudel.Fraction(1), children[i]]),
|
||||
);
|
||||
pat.__weight = weightSum; // for polymeter
|
||||
pat.tactus = weightSum;
|
||||
if (with_tactus.length) {
|
||||
pat.tactus = pat.tactus.mul(lcm(...with_tactus.map((x) => Fraction(x.tactus))));
|
||||
}
|
||||
} else {
|
||||
pat = strudel.sequence(...children);
|
||||
pat.tactus = children.length;
|
||||
}
|
||||
if (ast.arguments_.tactus) {
|
||||
pat.__tactus_source = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (with_tactus.length) {
|
||||
pat.__tactus_source = true;
|
||||
}
|
||||
return pat;
|
||||
function tokenize(code, global_offset) {
|
||||
let tokens = [];
|
||||
token_offset = 0;
|
||||
while (code.length > 0) {
|
||||
const trimmed = code.trim();
|
||||
token_offset += code.length - trimmed.length;
|
||||
code = trimmed;
|
||||
const token = next_token(code, global_offset);
|
||||
code = code.slice(token.value.length);
|
||||
tokens.push(token);
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
class Parser {
|
||||
parse(code, global_offset) {
|
||||
this.tokens = tokenize(code, global_offset);
|
||||
const subpats = this.parse_subpats();
|
||||
return { type: 'polymeter', subpats };
|
||||
}
|
||||
consume(type) {
|
||||
const token = this.tokens.shift();
|
||||
this.offset += token.length;
|
||||
if (token.type !== type) {
|
||||
throw new Error('expected token type ' + type + ' got ' + token.type);
|
||||
}
|
||||
case 'element': {
|
||||
1;
|
||||
return enter(ast.source_);
|
||||
return token;
|
||||
}
|
||||
parse_step() {
|
||||
let next = this.tokens[0]?.type;
|
||||
if (next === 'open_polyrhythm') {
|
||||
return this.parse_polyrhythm();
|
||||
}
|
||||
case 'atom': {
|
||||
if (ast.source_ === '~' || ast.source_ === '-') {
|
||||
return strudel.silence;
|
||||
}
|
||||
if (!ast.location_) {
|
||||
console.warn('no location for', ast);
|
||||
return ast.source_;
|
||||
}
|
||||
const value = !isNaN(Number(ast.source_)) ? Number(ast.source_) : ast.source_;
|
||||
if (offset === -1) {
|
||||
// skip location handling (used when getting leaves to avoid confusion)
|
||||
return strudel.pure(value);
|
||||
}
|
||||
const [from, to] = getLeafLocation(code, ast, offset);
|
||||
return strudel.pure(value).withLoc(from, to);
|
||||
if (next === 'open_polymeter') {
|
||||
return this.parse_polymeter();
|
||||
}
|
||||
case 'stretch':
|
||||
return enter(ast.source_).slow(enter(ast.arguments_.amount));
|
||||
default:
|
||||
console.warn(`node type "${ast.type_}" not implemented -> returning silence`);
|
||||
return strudel.silence;
|
||||
if (next === 'open_slowpolymeter') {
|
||||
return this.parse_slowpolymeter();
|
||||
}
|
||||
if (next === 'plain') {
|
||||
return this.consume('plain');
|
||||
}
|
||||
throw new Error('unexpected token ' + this.tokens[0]?.value + ' of type ' + this.tokens[0]?.type);
|
||||
}
|
||||
parse_expr() {
|
||||
let result = this.parse_step();
|
||||
let looking = true;
|
||||
while (looking) {
|
||||
let next = this.tokens[0]?.type;
|
||||
if (!next) {
|
||||
looking = false;
|
||||
} else {
|
||||
switch (next) {
|
||||
case 'fast':
|
||||
case 'slow':
|
||||
this.consume(next);
|
||||
const factor = this.parse_step();
|
||||
result = { type: next, factor, step: result };
|
||||
break;
|
||||
default:
|
||||
looking = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
parse_seq(...close_types) {
|
||||
const steps = [];
|
||||
while (!close_types.includes(this.tokens[0]?.type)) {
|
||||
steps.push(this.parse_expr());
|
||||
}
|
||||
return { type: 'seq', steps };
|
||||
}
|
||||
parse_subpats(close_type) {
|
||||
const subpats = [];
|
||||
while (true) {
|
||||
subpats.push(this.parse_seq(close_type, 'subpat_delimiter'));
|
||||
const next = this.tokens[0]?.type;
|
||||
if (next) {
|
||||
this.consume(next);
|
||||
}
|
||||
if (next === close_type) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return subpats;
|
||||
}
|
||||
parse_polymeter() {
|
||||
this.consume('open_polymeter');
|
||||
const subpats = this.parse_subpats('close_polymeter');
|
||||
return { type: 'polymeter', subpats };
|
||||
}
|
||||
parse_polyrhythm() {
|
||||
this.consume('open_polyrhythm');
|
||||
const subpats = this.parse_subpats('close_polyrhythm');
|
||||
return { type: 'polyrhythm', subpats };
|
||||
}
|
||||
parse_slowpolymeter() {
|
||||
this.consume('open_slowpolymeter');
|
||||
const subpats = this.parse_subpats('close_slowpolymeter');
|
||||
return { type: 'slowpolymeter', subpats };
|
||||
}
|
||||
}
|
||||
|
||||
// takes quoted mini string + leaf node within, returns source location of node (whitespace corrected)
|
||||
export const getLeafLocation = (code, leaf, globalOffset = 0) => {
|
||||
// value is expected without quotes!
|
||||
const { start, end } = leaf.location_;
|
||||
const actual = code?.split('').slice(start.offset, end.offset).join('');
|
||||
// make sure whitespaces are not part of the highlight
|
||||
const [offsetStart = 0, offsetEnd = 0] = actual
|
||||
? actual.split(leaf.source_).map((p) => p.split('').filter((c) => c === ' ').length)
|
||||
: [];
|
||||
return [start.offset + offsetStart + globalOffset, end.offset - offsetEnd + globalOffset];
|
||||
};
|
||||
|
||||
// takes quoted mini string, returns ast
|
||||
export const mini2ast = (code, start = 0, userCode = code) => {
|
||||
try {
|
||||
return krill.parse(code);
|
||||
} catch (error) {
|
||||
const region = [error.location.start.offset + start, error.location.end.offset + start];
|
||||
const line = userCode.slice(0, region[0]).split('\n').length;
|
||||
throw new Error(`[mini] parse error at line ${line}: ${error.message}`);
|
||||
function patternifyTree(tree) {
|
||||
if (tree.type === 'polyrhythm') {
|
||||
const args = tree.subpats.map((arg) => patternifyTree(arg));
|
||||
return strudel.polyrhythm(...args);
|
||||
}
|
||||
if (tree.type === 'slowpolymeter') {
|
||||
const args = tree.subpats.map((arg) => patternifyTree(arg));
|
||||
return strudel.s_polymeterSteps(1, ...args);
|
||||
}
|
||||
if (tree.type === 'polymeter') {
|
||||
const args = tree.subpats.map((subpat) => patternifyTree(subpat));
|
||||
return strudel.s_polymeter(...args);
|
||||
}
|
||||
if (tree.type === 'fast') {
|
||||
const step = patternifyTree(tree.step);
|
||||
const factor = patternifyTree(tree.factor);
|
||||
return strudel.fast(factor, step);
|
||||
}
|
||||
if (tree.type === 'slow') {
|
||||
const step = patternifyTree(tree.step);
|
||||
const factor = patternifyTree(tree.factor);
|
||||
return strudel.slow(factor, step);
|
||||
}
|
||||
if (tree.type === 'plain') {
|
||||
return strudel.pure(tree.value).withLoc(tree.location[0], tree.location[1]);
|
||||
}
|
||||
if (tree.type === 'seq') {
|
||||
const steps = tree.steps.map((step) => patternifyTree(step));
|
||||
return strudel.fastcat(...steps);
|
||||
}
|
||||
}
|
||||
|
||||
const parser = new Parser();
|
||||
export const m = (code, global_offset) => {
|
||||
const tree = parser.parse(code, global_offset + 1);
|
||||
const pat = patternifyTree(tree);
|
||||
return strudel.reify(pat);
|
||||
};
|
||||
|
||||
// takes quoted mini string, returns all nodes that are leaves
|
||||
export const getLeaves = (code, start, userCode) => {
|
||||
const ast = mini2ast(code, start, userCode);
|
||||
let leaves = [];
|
||||
patternifyAST(
|
||||
ast,
|
||||
code,
|
||||
(node) => {
|
||||
if (node.type_ === 'atom') {
|
||||
leaves.push(node);
|
||||
}
|
||||
},
|
||||
-1,
|
||||
);
|
||||
return leaves;
|
||||
};
|
||||
|
||||
// takes quoted mini string, returns locations [fromCol,toCol] of all leaf nodes
|
||||
export const getLeafLocations = (code, start = 0, userCode) => {
|
||||
return getLeaves(code, start, userCode).map((l) => getLeafLocation(code, l, start));
|
||||
export const getLeafLocations = (code, start) => {
|
||||
code = code.replace(/^"/, '');
|
||||
code = code.replace(/"$/, '');
|
||||
const leaves = tokenize(code, start).filter((x) => x.type == 'plain');
|
||||
return leaves.map((x) => x.location.map((y) => y + 1));
|
||||
};
|
||||
|
||||
// mini notation only (wraps in "")
|
||||
export const mini = (...strings) => {
|
||||
const pats = strings.map((str) => {
|
||||
const code = `"${str}"`;
|
||||
const ast = mini2ast(code);
|
||||
return patternifyAST(ast, code);
|
||||
});
|
||||
const pats = strings.map(m);
|
||||
return strudel.sequence(...pats);
|
||||
};
|
||||
|
||||
// turns str mini string (without quotes) into pattern
|
||||
// offset is the position of the mini string in the JS code
|
||||
// each leaf node will get .withLoc added
|
||||
// this function is used by the transpiler for double quoted strings
|
||||
export const m = (str, offset) => {
|
||||
const code = `"${str}"`;
|
||||
const ast = mini2ast(code);
|
||||
return patternifyAST(ast, code, null, offset);
|
||||
};
|
||||
|
||||
// includes haskell style (raw krill parsing)
|
||||
export const h = (string) => {
|
||||
const ast = mini2ast(string);
|
||||
return patternifyAST(ast, string);
|
||||
};
|
||||
|
||||
export function minify(thing) {
|
||||
if (typeof thing === 'string') {
|
||||
return mini(thing);
|
||||
|
||||
@@ -221,19 +221,6 @@ describe('mini', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLeafLocation', () => {
|
||||
it('gets location of leaf nodes', () => {
|
||||
const code = '"bd sd"';
|
||||
const ast = mini2ast(code);
|
||||
|
||||
const bd = ast.source_[0].source_;
|
||||
expect(getLeafLocation(code, bd)).toEqual([1, 3]);
|
||||
|
||||
const sd = ast.source_[1].source_;
|
||||
expect(getLeafLocation(code, sd)).toEqual([4, 6]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLeafLocations', () => {
|
||||
it('gets locations of leaf nodes', () => {
|
||||
expect(getLeafLocations('"bd sd"')).toEqual([
|
||||
@@ -242,9 +229,9 @@ describe('getLeafLocations', () => {
|
||||
]);
|
||||
expect(getLeafLocations('"bd*2 [sd cp]"')).toEqual([
|
||||
[1, 3], // bd columns
|
||||
[4, 5], // "2" columns
|
||||
[7, 9], // sd columns
|
||||
[10, 12], // cp columns
|
||||
[4, 5], // "2" columns
|
||||
]);
|
||||
expect(getLeafLocations('"bd*<2 3>"')).toEqual([
|
||||
[1, 3], // bd columns
|
||||
|
||||
@@ -68,7 +68,7 @@ Pattern.prototype.serial = function (br = 115200, sendcrc = false, singlecharids
|
||||
if (!(name in writeMessagers)) {
|
||||
getWriter(name, br);
|
||||
}
|
||||
const onTrigger = (time, hap, currentTime) => {
|
||||
const onTrigger = (t_deprecate, hap, currentTime, cps, targetTime) => {
|
||||
var message = '';
|
||||
var chk = 0;
|
||||
if (typeof hap.value === 'object') {
|
||||
@@ -105,7 +105,7 @@ Pattern.prototype.serial = function (br = 115200, sendcrc = false, singlecharids
|
||||
} else {
|
||||
message = hap.value;
|
||||
}
|
||||
const offset = (time - currentTime + latency) * 1000;
|
||||
const offset = (targetTime - currentTime + latency) * 1000;
|
||||
|
||||
window.setTimeout(function () {
|
||||
writeMessagers[name](message, chk);
|
||||
|
||||
@@ -22,16 +22,13 @@ function humanFileSize(bytes, si) {
|
||||
return bytes.toFixed(1) + ' ' + units[u];
|
||||
}
|
||||
|
||||
export const getSampleBufferSource = async (s, n, note, speed, freq, bank, resolveUrl) => {
|
||||
let transpose = 0;
|
||||
if (freq !== undefined && note !== undefined) {
|
||||
logger('[sampler] hap has note and freq. ignoring note', 'warning');
|
||||
}
|
||||
let midi = valueToMidi({ freq, note }, 36);
|
||||
transpose = midi - 36; // C3 is middle C
|
||||
|
||||
const ac = getAudioContext();
|
||||
|
||||
// deduces relevant info for sample loading from hap.value and sample definition
|
||||
// it encapsulates the core sampler logic into a pure and synchronous function
|
||||
// hapValue: Hap.value, bank: sample bank definition for sound "s" (values in strudel.json format)
|
||||
export function getSampleInfo(hapValue, bank) {
|
||||
const { s, n = 0, speed = 1.0 } = hapValue;
|
||||
let midi = valueToMidi(hapValue, 36);
|
||||
let transpose = midi - 36; // C3 is middle C;
|
||||
let sampleUrl;
|
||||
let index = 0;
|
||||
if (Array.isArray(bank)) {
|
||||
@@ -50,19 +47,54 @@ export const getSampleBufferSource = async (s, n, note, speed, freq, bank, resol
|
||||
index = getSoundIndex(n, bank[closest].length);
|
||||
sampleUrl = bank[closest][index];
|
||||
}
|
||||
const label = `${s}:${index}`;
|
||||
let playbackRate = Math.abs(speed) * Math.pow(2, transpose / 12);
|
||||
return { transpose, sampleUrl, index, midi, label, playbackRate };
|
||||
}
|
||||
|
||||
// takes hapValue and returns buffer + playbackRate.
|
||||
export const getSampleBuffer = async (hapValue, bank, resolveUrl) => {
|
||||
let { sampleUrl, label, playbackRate } = getSampleInfo(hapValue, bank);
|
||||
if (resolveUrl) {
|
||||
sampleUrl = await resolveUrl(sampleUrl);
|
||||
}
|
||||
let buffer = await loadBuffer(sampleUrl, ac, s, index);
|
||||
if (speed < 0) {
|
||||
const ac = getAudioContext();
|
||||
const buffer = await loadBuffer(sampleUrl, ac, label);
|
||||
|
||||
if (hapValue.unit === 'c') {
|
||||
playbackRate = playbackRate * buffer.duration;
|
||||
}
|
||||
return { buffer, playbackRate };
|
||||
};
|
||||
|
||||
// creates playback ready AudioBufferSourceNode from hapValue
|
||||
export const getSampleBufferSource = async (hapValue, bank, resolveUrl) => {
|
||||
let { buffer, playbackRate } = await getSampleBuffer(hapValue, bank, resolveUrl);
|
||||
if (hapValue.speed < 0) {
|
||||
// should this be cached?
|
||||
buffer = reverseBuffer(buffer);
|
||||
}
|
||||
const ac = getAudioContext();
|
||||
const bufferSource = ac.createBufferSource();
|
||||
bufferSource.buffer = buffer;
|
||||
const playbackRate = 1.0 * Math.pow(2, transpose / 12);
|
||||
bufferSource.playbackRate.value = playbackRate;
|
||||
return bufferSource;
|
||||
|
||||
const { s, loopBegin = 0, loopEnd = 1, begin = 0, end = 1 } = hapValue;
|
||||
|
||||
// "The computation of the offset into the sound is performed using the sound buffer's natural sample rate,
|
||||
// rather than the current playback rate, so even if the sound is playing at twice its normal speed,
|
||||
// the midway point through a 10-second audio buffer is still 5."
|
||||
const offset = begin * bufferSource.buffer.duration;
|
||||
|
||||
const loop = s.startsWith('wt_') ? 1 : hapValue.loop;
|
||||
if (loop) {
|
||||
bufferSource.loop = true;
|
||||
bufferSource.loopStart = loopBegin * bufferSource.buffer.duration - offset;
|
||||
bufferSource.loopEnd = loopEnd * bufferSource.buffer.duration - offset;
|
||||
}
|
||||
const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value;
|
||||
const sliceDuration = (end - begin) * bufferDuration;
|
||||
return { bufferSource, offset, bufferDuration, sliceDuration };
|
||||
};
|
||||
|
||||
export const loadBuffer = (url, ac, s, n = 0) => {
|
||||
@@ -232,10 +264,10 @@ export const samples = async (sampleMap, baseUrl = sampleMap._base || '', option
|
||||
const { prebake, tag } = options;
|
||||
processSampleMap(
|
||||
sampleMap,
|
||||
(key, value) =>
|
||||
registerSound(key, (t, hapValue, onended) => onTriggerSample(t, hapValue, onended, value), {
|
||||
(key, bank) =>
|
||||
registerSound(key, (t, hapValue, onended) => onTriggerSample(t, hapValue, onended, bank), {
|
||||
type: 'sample',
|
||||
samples: value,
|
||||
samples: bank,
|
||||
baseUrl,
|
||||
prebake,
|
||||
tag,
|
||||
@@ -249,38 +281,26 @@ const cutGroups = [];
|
||||
export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
|
||||
let {
|
||||
s,
|
||||
freq,
|
||||
unit,
|
||||
nudge = 0, // TODO: is this in seconds?
|
||||
cut,
|
||||
loop,
|
||||
clip = undefined, // if set, samples will be cut off when the hap ends
|
||||
n = 0,
|
||||
note,
|
||||
speed = 1, // sample playback speed
|
||||
loopBegin = 0,
|
||||
begin = 0,
|
||||
loopEnd = 1,
|
||||
end = 1,
|
||||
duration,
|
||||
} = value;
|
||||
|
||||
// load sample
|
||||
if (speed === 0) {
|
||||
// no playback
|
||||
return;
|
||||
}
|
||||
loop = s.startsWith('wt_') ? 1 : value.loop;
|
||||
const ac = getAudioContext();
|
||||
|
||||
// destructure adsr here, because the default should be different for synths and samples
|
||||
|
||||
let [attack, decay, sustain, release] = getADSRValues([value.attack, value.decay, value.sustain, value.release]);
|
||||
//const soundfont = getSoundfontKey(s);
|
||||
const time = t + nudge;
|
||||
|
||||
const bufferSource = await getSampleBufferSource(s, n, note, speed, freq, bank, resolveUrl);
|
||||
|
||||
// vibrato
|
||||
let vibratoOscillator = getVibratoOscillator(bufferSource.detune, value, t);
|
||||
const { bufferSource, sliceDuration, offset } = await getSampleBufferSource(value, bank, resolveUrl);
|
||||
|
||||
// asny stuff above took too long?
|
||||
if (ac.currentTime > t) {
|
||||
@@ -292,26 +312,19 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
|
||||
logger(`[sampler] could not load "${s}:${n}"`, 'error');
|
||||
return;
|
||||
}
|
||||
bufferSource.playbackRate.value = Math.abs(speed) * bufferSource.playbackRate.value;
|
||||
if (unit === 'c') {
|
||||
// are there other units?
|
||||
bufferSource.playbackRate.value = bufferSource.playbackRate.value * bufferSource.buffer.duration * 1; //cps;
|
||||
}
|
||||
// "The computation of the offset into the sound is performed using the sound buffer's natural sample rate,
|
||||
// rather than the current playback rate, so even if the sound is playing at twice its normal speed,
|
||||
// the midway point through a 10-second audio buffer is still 5."
|
||||
const offset = begin * bufferSource.buffer.duration;
|
||||
if (loop) {
|
||||
bufferSource.loop = true;
|
||||
bufferSource.loopStart = loopBegin * bufferSource.buffer.duration - offset;
|
||||
bufferSource.loopEnd = loopEnd * bufferSource.buffer.duration - offset;
|
||||
}
|
||||
|
||||
// vibrato
|
||||
let vibratoOscillator = getVibratoOscillator(bufferSource.detune, value, t);
|
||||
|
||||
const time = t + nudge;
|
||||
bufferSource.start(time, offset);
|
||||
|
||||
const envGain = ac.createGain();
|
||||
const node = bufferSource.connect(envGain);
|
||||
|
||||
// if none of these controls is set, the duration of the sound will be set to the duration of the sample slice
|
||||
if (clip == null && loop == null && value.release == null) {
|
||||
const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value;
|
||||
duration = (end - begin) * bufferDuration;
|
||||
duration = sliceDuration;
|
||||
}
|
||||
let holdEnd = t + duration;
|
||||
|
||||
|
||||
@@ -147,14 +147,6 @@ export function initializeAudioOutput() {
|
||||
destinationGain.connect(audioContext.destination);
|
||||
}
|
||||
|
||||
export function setGlobalGain(gain) {
|
||||
if (destinationGain == null) {
|
||||
initializeAudioOutput();
|
||||
}
|
||||
|
||||
destinationGain.gain.value = gain;
|
||||
}
|
||||
|
||||
// input: AudioNode, channels: ?Array<int>
|
||||
export const connectToDestination = (input, channels = [0, 1]) => {
|
||||
const ctx = getAudioContext();
|
||||
@@ -383,7 +375,7 @@ export const superdough = async (value, t, hapDuration) => {
|
||||
bandq = getDefaultValue('bandq'),
|
||||
channels = getDefaultValue('channels'),
|
||||
//phaser
|
||||
phaser,
|
||||
phaserrate: phaser,
|
||||
phaserdepth = getDefaultValue('phaserdepth'),
|
||||
phasersweep,
|
||||
phasercenter,
|
||||
@@ -429,6 +421,7 @@ export const superdough = async (value, t, hapDuration) => {
|
||||
};
|
||||
if (bank && s) {
|
||||
s = `${bank}_${s}`;
|
||||
value.s = s;
|
||||
}
|
||||
|
||||
// get source AudioNode
|
||||
|
||||
@@ -967,6 +967,156 @@ exports[`runs examples > example "begin" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "bite" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | note:Bb3 ]",
|
||||
"[ 1/8 → 1/4 | note:C4 ]",
|
||||
"[ 1/4 → 3/8 | note:G3 ]",
|
||||
"[ 3/8 → 1/2 | note:A3 ]",
|
||||
"[ 1/2 → 5/8 | note:E3 ]",
|
||||
"[ 5/8 → 3/4 | note:F3 ]",
|
||||
"[ 3/4 → 7/8 | note:C3 ]",
|
||||
"[ 7/8 → 1/1 | note:D3 ]",
|
||||
"[ 1/1 → 9/8 | note:Bb3 ]",
|
||||
"[ 9/8 → 5/4 | note:C4 ]",
|
||||
"[ 5/4 → 11/8 | note:G3 ]",
|
||||
"[ 11/8 → 3/2 | note:A3 ]",
|
||||
"[ 3/2 → 13/8 | note:E3 ]",
|
||||
"[ 13/8 → 7/4 | note:F3 ]",
|
||||
"[ 7/4 → 15/8 | note:C3 ]",
|
||||
"[ 15/8 → 2/1 | note:D3 ]",
|
||||
"[ 2/1 → 17/8 | note:Bb3 ]",
|
||||
"[ 17/8 → 9/4 | note:C4 ]",
|
||||
"[ 9/4 → 19/8 | note:G3 ]",
|
||||
"[ 19/8 → 5/2 | note:A3 ]",
|
||||
"[ 5/2 → 21/8 | note:E3 ]",
|
||||
"[ 21/8 → 11/4 | note:F3 ]",
|
||||
"[ 11/4 → 23/8 | note:C3 ]",
|
||||
"[ 23/8 → 3/1 | note:D3 ]",
|
||||
"[ 3/1 → 25/8 | note:Bb3 ]",
|
||||
"[ 25/8 → 13/4 | note:C4 ]",
|
||||
"[ 13/4 → 27/8 | note:G3 ]",
|
||||
"[ 27/8 → 7/2 | note:A3 ]",
|
||||
"[ 7/2 → 29/8 | note:E3 ]",
|
||||
"[ 29/8 → 15/4 | note:F3 ]",
|
||||
"[ 15/4 → 31/8 | note:C3 ]",
|
||||
"[ 31/8 → 4/1 | note:D3 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "bite" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 0/1 → 1/8 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 1/16 → 1/8 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 1/8 → 1/4 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 1/8 → 1/4 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 3/16 → 1/4 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 1/4 → 5/16 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 9/32 → 5/16 | s:sd n:5 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 5/16 → 11/32 | s:sd n:1 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 5/16 → 3/8 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 3/8 → 7/16 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 3/8 → 1/2 | s:cp n:7 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 13/32 → 7/16 | s:sd n:2 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 7/16 → 1/2 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 1/2 → 5/8 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 1/2 → 5/8 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 9/16 → 5/8 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 5/8 → 3/4 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 5/8 → 3/4 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 11/16 → 3/4 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 3/4 → 25/32 | s:sd n:1 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 3/4 → 13/16 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 13/16 → 7/8 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 27/32 → 7/8 | s:sd n:5 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 7/8 → 15/16 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 7/8 → 1/1 | s:cp n:7 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 29/32 → 15/16 | s:sd n:2 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 15/16 → 1/1 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 1/1 → 9/8 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 1/1 → 9/8 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 17/16 → 9/8 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 9/8 → 5/4 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 9/8 → 5/4 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 19/16 → 5/4 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 5/4 → 21/16 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 41/32 → 21/16 | s:sd n:5 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 21/16 → 43/32 | s:sd n:1 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 21/16 → 11/8 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 11/8 → 23/16 | s:sd n:1 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 11/8 → 3/2 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 3/2 → 13/8 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 3/2 → 13/8 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 25/16 → 13/8 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 13/8 → 7/4 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 13/8 → 7/4 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 27/16 → 7/4 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 7/4 → 57/32 | s:sd n:1 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 7/4 → 29/16 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 29/16 → 15/8 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 59/32 → 15/8 | s:sd n:5 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 15/8 → 31/16 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 15/8 → 2/1 | s:cp n:7 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 61/32 → 31/16 | s:sd n:2 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 31/16 → 2/1 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 2/1 → 17/8 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 2/1 → 17/8 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 33/16 → 17/8 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 17/8 → 9/4 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 17/8 → 9/4 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 35/16 → 9/4 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 9/4 → 37/16 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 73/32 → 37/16 | s:sd n:5 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 37/16 → 75/32 | s:sd n:1 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 37/16 → 19/8 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 19/8 → 39/16 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 19/8 → 5/2 | s:cp n:7 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 77/32 → 39/16 | s:sd n:2 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 39/16 → 5/2 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 5/2 → 21/8 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 5/2 → 21/8 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 41/16 → 21/8 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 21/8 → 11/4 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 21/8 → 11/4 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 43/16 → 11/4 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 11/4 → 89/32 | s:sd n:1 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 11/4 → 45/16 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 45/16 → 23/8 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 91/32 → 23/8 | s:sd n:5 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 23/8 → 47/16 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 23/8 → 3/1 | s:cp n:7 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 93/32 → 47/16 | s:sd n:2 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 47/16 → 3/1 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 3/1 → 25/8 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 3/1 → 25/8 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 49/16 → 25/8 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 25/8 → 13/4 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 25/8 → 13/4 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 51/16 → 13/4 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 13/4 → 53/16 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 105/32 → 53/16 | s:sd n:5 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 53/16 → 107/32 | s:sd n:1 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 53/16 → 27/8 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 27/8 → 55/16 | s:sd n:1 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 27/8 → 7/2 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 7/2 → 29/8 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 7/2 → 29/8 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 57/16 → 29/8 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 29/8 → 15/4 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 29/8 → 15/4 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 59/16 → 15/4 | s:sd n:6 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 15/4 → 121/32 | s:sd n:1 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 15/4 → 61/16 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 61/16 → 31/8 | s:hh bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 123/32 → 31/8 | s:sd n:5 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 31/8 → 63/16 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 31/8 → 4/1 | s:cp n:7 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 125/32 → 63/16 | s:sd n:2 bank:RolandTR909 speed:1.2 ]",
|
||||
"[ 63/16 → 4/1 | s:bd bank:RolandTR909 speed:1.2 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "bpattack" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | note:c2 s:sawtooth bandf:500 bpattack:0.5 bpenv:4 ]",
|
||||
@@ -1229,6 +1379,16 @@ exports[`runs examples > example "brandBy" example index 0 1`] = `
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "cat" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/1 | note:e5 ]",
|
||||
"[ 1/1 → 2/1 | note:b4 ]",
|
||||
"[ 2/1 → 5/2 | note:d5 ]",
|
||||
"[ 5/2 → 3/1 | note:c5 ]",
|
||||
"[ 3/1 → 4/1 | note:e5 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "cat" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | s:hh ]",
|
||||
"[ 1/4 → 1/2 | s:hh ]",
|
||||
@@ -1251,16 +1411,6 @@ exports[`runs examples > example "cat" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "cat" example index 0 2`] = `
|
||||
[
|
||||
"[ 0/1 → 1/1 | note:e5 ]",
|
||||
"[ 1/1 → 2/1 | note:b4 ]",
|
||||
"[ 2/1 → 5/2 | note:d5 ]",
|
||||
"[ 5/2 → 3/1 | note:c5 ]",
|
||||
"[ 3/1 → 4/1 | note:e5 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "ceil" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | note:42 ]",
|
||||
@@ -2666,6 +2816,43 @@ exports[`runs examples > example "fastGap" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "filter" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | s:hh ]",
|
||||
"[ 1/8 → 1/4 | s:hh ]",
|
||||
"[ 1/4 → 3/8 | s:hh ]",
|
||||
"[ 3/8 → 1/2 | s:hh ]",
|
||||
"[ 1/2 → 5/8 | s:hh ]",
|
||||
"[ 5/8 → 3/4 | s:hh ]",
|
||||
"[ 3/4 → 7/8 | s:hh ]",
|
||||
"[ 7/8 → 1/1 | s:oh ]",
|
||||
"[ 1/1 → 9/8 | s:hh ]",
|
||||
"[ 9/8 → 5/4 | s:hh ]",
|
||||
"[ 5/4 → 11/8 | s:hh ]",
|
||||
"[ 11/8 → 3/2 | s:hh ]",
|
||||
"[ 3/2 → 13/8 | s:hh ]",
|
||||
"[ 13/8 → 7/4 | s:hh ]",
|
||||
"[ 7/4 → 15/8 | s:hh ]",
|
||||
"[ 15/8 → 2/1 | s:oh ]",
|
||||
"[ 2/1 → 17/8 | s:hh ]",
|
||||
"[ 17/8 → 9/4 | s:hh ]",
|
||||
"[ 9/4 → 19/8 | s:hh ]",
|
||||
"[ 19/8 → 5/2 | s:hh ]",
|
||||
"[ 5/2 → 21/8 | s:hh ]",
|
||||
"[ 21/8 → 11/4 | s:hh ]",
|
||||
"[ 11/4 → 23/8 | s:hh ]",
|
||||
"[ 23/8 → 3/1 | s:oh ]",
|
||||
"[ 3/1 → 25/8 | s:hh ]",
|
||||
"[ 25/8 → 13/4 | s:hh ]",
|
||||
"[ 13/4 → 27/8 | s:hh ]",
|
||||
"[ 27/8 → 7/2 | s:hh ]",
|
||||
"[ 7/2 → 29/8 | s:hh ]",
|
||||
"[ 29/8 → 15/4 | s:hh ]",
|
||||
"[ 15/4 → 31/8 | s:hh ]",
|
||||
"[ 31/8 → 4/1 | s:oh ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "firstOf" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | note:g3 ]",
|
||||
@@ -4937,149 +5124,149 @@ exports[`runs examples > example "perlin" example index 0 1`] = `
|
||||
|
||||
exports[`runs examples > example "phaser" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | note:D3 s:sawtooth release:0.5 phaser:1 ]",
|
||||
"[ 1/8 → 1/4 | note:E3 s:sawtooth release:0.5 phaser:1 ]",
|
||||
"[ 1/4 → 3/8 | note:F#3 s:sawtooth release:0.5 phaser:1 ]",
|
||||
"[ 3/8 → 1/2 | note:A3 s:sawtooth release:0.5 phaser:1 ]",
|
||||
"[ 1/2 → 5/8 | note:B3 s:sawtooth release:0.5 phaser:1 ]",
|
||||
"[ 5/8 → 3/4 | note:D4 s:sawtooth release:0.5 phaser:1 ]",
|
||||
"[ 3/4 → 7/8 | note:E4 s:sawtooth release:0.5 phaser:1 ]",
|
||||
"[ 7/8 → 1/1 | note:F#4 s:sawtooth release:0.5 phaser:1 ]",
|
||||
"[ 1/1 → 9/8 | note:D3 s:sawtooth release:0.5 phaser:2 ]",
|
||||
"[ 9/8 → 5/4 | note:E3 s:sawtooth release:0.5 phaser:2 ]",
|
||||
"[ 5/4 → 11/8 | note:F#3 s:sawtooth release:0.5 phaser:2 ]",
|
||||
"[ 11/8 → 3/2 | note:A3 s:sawtooth release:0.5 phaser:2 ]",
|
||||
"[ 3/2 → 13/8 | note:B3 s:sawtooth release:0.5 phaser:2 ]",
|
||||
"[ 13/8 → 7/4 | note:D4 s:sawtooth release:0.5 phaser:2 ]",
|
||||
"[ 7/4 → 15/8 | note:E4 s:sawtooth release:0.5 phaser:2 ]",
|
||||
"[ 15/8 → 2/1 | note:F#4 s:sawtooth release:0.5 phaser:2 ]",
|
||||
"[ 2/1 → 17/8 | note:D3 s:sawtooth release:0.5 phaser:4 ]",
|
||||
"[ 17/8 → 9/4 | note:E3 s:sawtooth release:0.5 phaser:4 ]",
|
||||
"[ 9/4 → 19/8 | note:F#3 s:sawtooth release:0.5 phaser:4 ]",
|
||||
"[ 19/8 → 5/2 | note:A3 s:sawtooth release:0.5 phaser:4 ]",
|
||||
"[ 5/2 → 21/8 | note:B3 s:sawtooth release:0.5 phaser:4 ]",
|
||||
"[ 21/8 → 11/4 | note:D4 s:sawtooth release:0.5 phaser:4 ]",
|
||||
"[ 11/4 → 23/8 | note:E4 s:sawtooth release:0.5 phaser:4 ]",
|
||||
"[ 23/8 → 3/1 | note:F#4 s:sawtooth release:0.5 phaser:4 ]",
|
||||
"[ 3/1 → 25/8 | note:D3 s:sawtooth release:0.5 phaser:8 ]",
|
||||
"[ 25/8 → 13/4 | note:E3 s:sawtooth release:0.5 phaser:8 ]",
|
||||
"[ 13/4 → 27/8 | note:F#3 s:sawtooth release:0.5 phaser:8 ]",
|
||||
"[ 27/8 → 7/2 | note:A3 s:sawtooth release:0.5 phaser:8 ]",
|
||||
"[ 7/2 → 29/8 | note:B3 s:sawtooth release:0.5 phaser:8 ]",
|
||||
"[ 29/8 → 15/4 | note:D4 s:sawtooth release:0.5 phaser:8 ]",
|
||||
"[ 15/4 → 31/8 | note:E4 s:sawtooth release:0.5 phaser:8 ]",
|
||||
"[ 31/8 → 4/1 | note:F#4 s:sawtooth release:0.5 phaser:8 ]",
|
||||
"[ 0/1 → 1/8 | note:D3 s:sawtooth release:0.5 phaserrate:1 ]",
|
||||
"[ 1/8 → 1/4 | note:E3 s:sawtooth release:0.5 phaserrate:1 ]",
|
||||
"[ 1/4 → 3/8 | note:F#3 s:sawtooth release:0.5 phaserrate:1 ]",
|
||||
"[ 3/8 → 1/2 | note:A3 s:sawtooth release:0.5 phaserrate:1 ]",
|
||||
"[ 1/2 → 5/8 | note:B3 s:sawtooth release:0.5 phaserrate:1 ]",
|
||||
"[ 5/8 → 3/4 | note:D4 s:sawtooth release:0.5 phaserrate:1 ]",
|
||||
"[ 3/4 → 7/8 | note:E4 s:sawtooth release:0.5 phaserrate:1 ]",
|
||||
"[ 7/8 → 1/1 | note:F#4 s:sawtooth release:0.5 phaserrate:1 ]",
|
||||
"[ 1/1 → 9/8 | note:D3 s:sawtooth release:0.5 phaserrate:2 ]",
|
||||
"[ 9/8 → 5/4 | note:E3 s:sawtooth release:0.5 phaserrate:2 ]",
|
||||
"[ 5/4 → 11/8 | note:F#3 s:sawtooth release:0.5 phaserrate:2 ]",
|
||||
"[ 11/8 → 3/2 | note:A3 s:sawtooth release:0.5 phaserrate:2 ]",
|
||||
"[ 3/2 → 13/8 | note:B3 s:sawtooth release:0.5 phaserrate:2 ]",
|
||||
"[ 13/8 → 7/4 | note:D4 s:sawtooth release:0.5 phaserrate:2 ]",
|
||||
"[ 7/4 → 15/8 | note:E4 s:sawtooth release:0.5 phaserrate:2 ]",
|
||||
"[ 15/8 → 2/1 | note:F#4 s:sawtooth release:0.5 phaserrate:2 ]",
|
||||
"[ 2/1 → 17/8 | note:D3 s:sawtooth release:0.5 phaserrate:4 ]",
|
||||
"[ 17/8 → 9/4 | note:E3 s:sawtooth release:0.5 phaserrate:4 ]",
|
||||
"[ 9/4 → 19/8 | note:F#3 s:sawtooth release:0.5 phaserrate:4 ]",
|
||||
"[ 19/8 → 5/2 | note:A3 s:sawtooth release:0.5 phaserrate:4 ]",
|
||||
"[ 5/2 → 21/8 | note:B3 s:sawtooth release:0.5 phaserrate:4 ]",
|
||||
"[ 21/8 → 11/4 | note:D4 s:sawtooth release:0.5 phaserrate:4 ]",
|
||||
"[ 11/4 → 23/8 | note:E4 s:sawtooth release:0.5 phaserrate:4 ]",
|
||||
"[ 23/8 → 3/1 | note:F#4 s:sawtooth release:0.5 phaserrate:4 ]",
|
||||
"[ 3/1 → 25/8 | note:D3 s:sawtooth release:0.5 phaserrate:8 ]",
|
||||
"[ 25/8 → 13/4 | note:E3 s:sawtooth release:0.5 phaserrate:8 ]",
|
||||
"[ 13/4 → 27/8 | note:F#3 s:sawtooth release:0.5 phaserrate:8 ]",
|
||||
"[ 27/8 → 7/2 | note:A3 s:sawtooth release:0.5 phaserrate:8 ]",
|
||||
"[ 7/2 → 29/8 | note:B3 s:sawtooth release:0.5 phaserrate:8 ]",
|
||||
"[ 29/8 → 15/4 | note:D4 s:sawtooth release:0.5 phaserrate:8 ]",
|
||||
"[ 15/4 → 31/8 | note:E4 s:sawtooth release:0.5 phaserrate:8 ]",
|
||||
"[ 31/8 → 4/1 | note:F#4 s:sawtooth release:0.5 phaserrate:8 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "phasercenter" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 1/8 → 1/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 1/4 → 3/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 3/8 → 1/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 1/2 → 5/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 5/8 → 3/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 3/4 → 7/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 7/8 → 1/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 1/1 → 9/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
|
||||
"[ 9/8 → 5/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
|
||||
"[ 5/4 → 11/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
|
||||
"[ 11/8 → 3/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
|
||||
"[ 3/2 → 13/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
|
||||
"[ 13/8 → 7/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
|
||||
"[ 7/4 → 15/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
|
||||
"[ 15/8 → 2/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasercenter:2000 ]",
|
||||
"[ 2/1 → 17/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
|
||||
"[ 17/8 → 9/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
|
||||
"[ 9/4 → 19/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
|
||||
"[ 19/8 → 5/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
|
||||
"[ 5/2 → 21/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
|
||||
"[ 21/8 → 11/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
|
||||
"[ 11/4 → 23/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
|
||||
"[ 23/8 → 3/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasercenter:4000 ]",
|
||||
"[ 3/1 → 25/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 25/8 → 13/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 13/4 → 27/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 27/8 → 7/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 7/2 → 29/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 29/8 → 15/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 15/4 → 31/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 31/8 → 4/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasercenter:800 ]",
|
||||
"[ 0/1 → 1/8 | note:D3 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
"[ 1/8 → 1/4 | note:E3 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
"[ 1/4 → 3/8 | note:F#3 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
"[ 3/8 → 1/2 | note:A3 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
"[ 1/2 → 5/8 | note:B3 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
"[ 5/8 → 3/4 | note:D4 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
"[ 3/4 → 7/8 | note:E4 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
"[ 7/8 → 1/1 | note:F#4 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
"[ 1/1 → 9/8 | note:D3 s:sawtooth release:0.5 phaserrate:2 phasercenter:2000 ]",
|
||||
"[ 9/8 → 5/4 | note:E3 s:sawtooth release:0.5 phaserrate:2 phasercenter:2000 ]",
|
||||
"[ 5/4 → 11/8 | note:F#3 s:sawtooth release:0.5 phaserrate:2 phasercenter:2000 ]",
|
||||
"[ 11/8 → 3/2 | note:A3 s:sawtooth release:0.5 phaserrate:2 phasercenter:2000 ]",
|
||||
"[ 3/2 → 13/8 | note:B3 s:sawtooth release:0.5 phaserrate:2 phasercenter:2000 ]",
|
||||
"[ 13/8 → 7/4 | note:D4 s:sawtooth release:0.5 phaserrate:2 phasercenter:2000 ]",
|
||||
"[ 7/4 → 15/8 | note:E4 s:sawtooth release:0.5 phaserrate:2 phasercenter:2000 ]",
|
||||
"[ 15/8 → 2/1 | note:F#4 s:sawtooth release:0.5 phaserrate:2 phasercenter:2000 ]",
|
||||
"[ 2/1 → 17/8 | note:D3 s:sawtooth release:0.5 phaserrate:2 phasercenter:4000 ]",
|
||||
"[ 17/8 → 9/4 | note:E3 s:sawtooth release:0.5 phaserrate:2 phasercenter:4000 ]",
|
||||
"[ 9/4 → 19/8 | note:F#3 s:sawtooth release:0.5 phaserrate:2 phasercenter:4000 ]",
|
||||
"[ 19/8 → 5/2 | note:A3 s:sawtooth release:0.5 phaserrate:2 phasercenter:4000 ]",
|
||||
"[ 5/2 → 21/8 | note:B3 s:sawtooth release:0.5 phaserrate:2 phasercenter:4000 ]",
|
||||
"[ 21/8 → 11/4 | note:D4 s:sawtooth release:0.5 phaserrate:2 phasercenter:4000 ]",
|
||||
"[ 11/4 → 23/8 | note:E4 s:sawtooth release:0.5 phaserrate:2 phasercenter:4000 ]",
|
||||
"[ 23/8 → 3/1 | note:F#4 s:sawtooth release:0.5 phaserrate:2 phasercenter:4000 ]",
|
||||
"[ 3/1 → 25/8 | note:D3 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
"[ 25/8 → 13/4 | note:E3 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
"[ 13/4 → 27/8 | note:F#3 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
"[ 27/8 → 7/2 | note:A3 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
"[ 7/2 → 29/8 | note:B3 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
"[ 29/8 → 15/4 | note:D4 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
"[ 15/4 → 31/8 | note:E4 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
"[ 31/8 → 4/1 | note:F#4 s:sawtooth release:0.5 phaserrate:2 phasercenter:800 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "phaserdepth" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | note:D3 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
|
||||
"[ 1/8 → 1/4 | note:E3 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
|
||||
"[ 1/4 → 3/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
|
||||
"[ 3/8 → 1/2 | note:A3 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
|
||||
"[ 1/2 → 5/8 | note:B3 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
|
||||
"[ 5/8 → 3/4 | note:D4 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
|
||||
"[ 3/4 → 7/8 | note:E4 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
|
||||
"[ 7/8 → 1/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phaserdepth:0 ]",
|
||||
"[ 1/1 → 9/8 | note:D3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
|
||||
"[ 9/8 → 5/4 | note:E3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
|
||||
"[ 5/4 → 11/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
|
||||
"[ 11/8 → 3/2 | note:A3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
|
||||
"[ 3/2 → 13/8 | note:B3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
|
||||
"[ 13/8 → 7/4 | note:D4 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
|
||||
"[ 7/4 → 15/8 | note:E4 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
|
||||
"[ 15/8 → 2/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phaserdepth:0.5 ]",
|
||||
"[ 2/1 → 17/8 | note:D3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
|
||||
"[ 17/8 → 9/4 | note:E3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
|
||||
"[ 9/4 → 19/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
|
||||
"[ 19/8 → 5/2 | note:A3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
|
||||
"[ 5/2 → 21/8 | note:B3 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
|
||||
"[ 21/8 → 11/4 | note:D4 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
|
||||
"[ 11/4 → 23/8 | note:E4 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
|
||||
"[ 23/8 → 3/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phaserdepth:0.75 ]",
|
||||
"[ 3/1 → 25/8 | note:D3 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
|
||||
"[ 25/8 → 13/4 | note:E3 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
|
||||
"[ 13/4 → 27/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
|
||||
"[ 27/8 → 7/2 | note:A3 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
|
||||
"[ 7/2 → 29/8 | note:B3 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
|
||||
"[ 29/8 → 15/4 | note:D4 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
|
||||
"[ 15/4 → 31/8 | note:E4 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
|
||||
"[ 31/8 → 4/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phaserdepth:1 ]",
|
||||
"[ 0/1 → 1/8 | note:D3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0 ]",
|
||||
"[ 1/8 → 1/4 | note:E3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0 ]",
|
||||
"[ 1/4 → 3/8 | note:F#3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0 ]",
|
||||
"[ 3/8 → 1/2 | note:A3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0 ]",
|
||||
"[ 1/2 → 5/8 | note:B3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0 ]",
|
||||
"[ 5/8 → 3/4 | note:D4 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0 ]",
|
||||
"[ 3/4 → 7/8 | note:E4 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0 ]",
|
||||
"[ 7/8 → 1/1 | note:F#4 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0 ]",
|
||||
"[ 1/1 → 9/8 | note:D3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.5 ]",
|
||||
"[ 9/8 → 5/4 | note:E3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.5 ]",
|
||||
"[ 5/4 → 11/8 | note:F#3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.5 ]",
|
||||
"[ 11/8 → 3/2 | note:A3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.5 ]",
|
||||
"[ 3/2 → 13/8 | note:B3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.5 ]",
|
||||
"[ 13/8 → 7/4 | note:D4 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.5 ]",
|
||||
"[ 7/4 → 15/8 | note:E4 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.5 ]",
|
||||
"[ 15/8 → 2/1 | note:F#4 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.5 ]",
|
||||
"[ 2/1 → 17/8 | note:D3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.75 ]",
|
||||
"[ 17/8 → 9/4 | note:E3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.75 ]",
|
||||
"[ 9/4 → 19/8 | note:F#3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.75 ]",
|
||||
"[ 19/8 → 5/2 | note:A3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.75 ]",
|
||||
"[ 5/2 → 21/8 | note:B3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.75 ]",
|
||||
"[ 21/8 → 11/4 | note:D4 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.75 ]",
|
||||
"[ 11/4 → 23/8 | note:E4 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.75 ]",
|
||||
"[ 23/8 → 3/1 | note:F#4 s:sawtooth release:0.5 phaserrate:2 phaserdepth:0.75 ]",
|
||||
"[ 3/1 → 25/8 | note:D3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:1 ]",
|
||||
"[ 25/8 → 13/4 | note:E3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:1 ]",
|
||||
"[ 13/4 → 27/8 | note:F#3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:1 ]",
|
||||
"[ 27/8 → 7/2 | note:A3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:1 ]",
|
||||
"[ 7/2 → 29/8 | note:B3 s:sawtooth release:0.5 phaserrate:2 phaserdepth:1 ]",
|
||||
"[ 29/8 → 15/4 | note:D4 s:sawtooth release:0.5 phaserrate:2 phaserdepth:1 ]",
|
||||
"[ 15/4 → 31/8 | note:E4 s:sawtooth release:0.5 phaserrate:2 phaserdepth:1 ]",
|
||||
"[ 31/8 → 4/1 | note:F#4 s:sawtooth release:0.5 phaserrate:2 phaserdepth:1 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "phasersweep" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 1/8 → 1/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 1/4 → 3/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 3/8 → 1/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 1/2 → 5/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 5/8 → 3/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 3/4 → 7/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 7/8 → 1/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 1/1 → 9/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
|
||||
"[ 9/8 → 5/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
|
||||
"[ 5/4 → 11/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
|
||||
"[ 11/8 → 3/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
|
||||
"[ 3/2 → 13/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
|
||||
"[ 13/8 → 7/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
|
||||
"[ 7/4 → 15/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
|
||||
"[ 15/8 → 2/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasersweep:2000 ]",
|
||||
"[ 2/1 → 17/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
|
||||
"[ 17/8 → 9/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
|
||||
"[ 9/4 → 19/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
|
||||
"[ 19/8 → 5/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
|
||||
"[ 5/2 → 21/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
|
||||
"[ 21/8 → 11/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
|
||||
"[ 11/4 → 23/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
|
||||
"[ 23/8 → 3/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasersweep:4000 ]",
|
||||
"[ 3/1 → 25/8 | note:D3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 25/8 → 13/4 | note:E3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 13/4 → 27/8 | note:F#3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 27/8 → 7/2 | note:A3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 7/2 → 29/8 | note:B3 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 29/8 → 15/4 | note:D4 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 15/4 → 31/8 | note:E4 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 31/8 → 4/1 | note:F#4 s:sawtooth release:0.5 phaser:2 phasersweep:800 ]",
|
||||
"[ 0/1 → 1/8 | note:D3 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
"[ 1/8 → 1/4 | note:E3 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
"[ 1/4 → 3/8 | note:F#3 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
"[ 3/8 → 1/2 | note:A3 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
"[ 1/2 → 5/8 | note:B3 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
"[ 5/8 → 3/4 | note:D4 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
"[ 3/4 → 7/8 | note:E4 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
"[ 7/8 → 1/1 | note:F#4 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
"[ 1/1 → 9/8 | note:D3 s:sawtooth release:0.5 phaserrate:2 phasersweep:2000 ]",
|
||||
"[ 9/8 → 5/4 | note:E3 s:sawtooth release:0.5 phaserrate:2 phasersweep:2000 ]",
|
||||
"[ 5/4 → 11/8 | note:F#3 s:sawtooth release:0.5 phaserrate:2 phasersweep:2000 ]",
|
||||
"[ 11/8 → 3/2 | note:A3 s:sawtooth release:0.5 phaserrate:2 phasersweep:2000 ]",
|
||||
"[ 3/2 → 13/8 | note:B3 s:sawtooth release:0.5 phaserrate:2 phasersweep:2000 ]",
|
||||
"[ 13/8 → 7/4 | note:D4 s:sawtooth release:0.5 phaserrate:2 phasersweep:2000 ]",
|
||||
"[ 7/4 → 15/8 | note:E4 s:sawtooth release:0.5 phaserrate:2 phasersweep:2000 ]",
|
||||
"[ 15/8 → 2/1 | note:F#4 s:sawtooth release:0.5 phaserrate:2 phasersweep:2000 ]",
|
||||
"[ 2/1 → 17/8 | note:D3 s:sawtooth release:0.5 phaserrate:2 phasersweep:4000 ]",
|
||||
"[ 17/8 → 9/4 | note:E3 s:sawtooth release:0.5 phaserrate:2 phasersweep:4000 ]",
|
||||
"[ 9/4 → 19/8 | note:F#3 s:sawtooth release:0.5 phaserrate:2 phasersweep:4000 ]",
|
||||
"[ 19/8 → 5/2 | note:A3 s:sawtooth release:0.5 phaserrate:2 phasersweep:4000 ]",
|
||||
"[ 5/2 → 21/8 | note:B3 s:sawtooth release:0.5 phaserrate:2 phasersweep:4000 ]",
|
||||
"[ 21/8 → 11/4 | note:D4 s:sawtooth release:0.5 phaserrate:2 phasersweep:4000 ]",
|
||||
"[ 11/4 → 23/8 | note:E4 s:sawtooth release:0.5 phaserrate:2 phasersweep:4000 ]",
|
||||
"[ 23/8 → 3/1 | note:F#4 s:sawtooth release:0.5 phaserrate:2 phasersweep:4000 ]",
|
||||
"[ 3/1 → 25/8 | note:D3 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
"[ 25/8 → 13/4 | note:E3 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
"[ 13/4 → 27/8 | note:F#3 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
"[ 27/8 → 7/2 | note:A3 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
"[ 7/2 → 29/8 | note:B3 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
"[ 29/8 → 15/4 | note:D4 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
"[ 15/4 → 31/8 | note:E4 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
"[ 31/8 → 4/1 | note:F#4 s:sawtooth release:0.5 phaserrate:2 phasersweep:800 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -6789,6 +6976,27 @@ exports[`runs examples > example "segment" example index 0 1`] = `
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "seq" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/3 | note:e5 ]",
|
||||
"[ 1/3 → 2/3 | note:b4 ]",
|
||||
"[ 2/3 → 5/6 | note:d5 ]",
|
||||
"[ 5/6 → 1/1 | note:c5 ]",
|
||||
"[ 1/1 → 4/3 | note:e5 ]",
|
||||
"[ 4/3 → 5/3 | note:b4 ]",
|
||||
"[ 5/3 → 11/6 | note:d5 ]",
|
||||
"[ 11/6 → 2/1 | note:c5 ]",
|
||||
"[ 2/1 → 7/3 | note:e5 ]",
|
||||
"[ 7/3 → 8/3 | note:b4 ]",
|
||||
"[ 8/3 → 17/6 | note:d5 ]",
|
||||
"[ 17/6 → 3/1 | note:c5 ]",
|
||||
"[ 3/1 → 10/3 | note:e5 ]",
|
||||
"[ 10/3 → 11/3 | note:b4 ]",
|
||||
"[ 11/3 → 23/6 | note:d5 ]",
|
||||
"[ 23/6 → 4/1 | note:c5 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "seq" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | s:hh ]",
|
||||
"[ 1/8 → 1/4 | s:hh ]",
|
||||
@@ -6829,24 +7037,23 @@ exports[`runs examples > example "seq" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "seq" example index 0 2`] = `
|
||||
exports[`runs examples > example "seqPLoop" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/3 | note:e5 ]",
|
||||
"[ 1/3 → 2/3 | note:b4 ]",
|
||||
"[ 2/3 → 5/6 | note:d5 ]",
|
||||
"[ 5/6 → 1/1 | note:c5 ]",
|
||||
"[ 1/1 → 4/3 | note:e5 ]",
|
||||
"[ 4/3 → 5/3 | note:b4 ]",
|
||||
"[ 5/3 → 11/6 | note:d5 ]",
|
||||
"[ 11/6 → 2/1 | note:c5 ]",
|
||||
"[ 2/1 → 7/3 | note:e5 ]",
|
||||
"[ 7/3 → 8/3 | note:b4 ]",
|
||||
"[ 8/3 → 17/6 | note:d5 ]",
|
||||
"[ 17/6 → 3/1 | note:c5 ]",
|
||||
"[ 3/1 → 10/3 | note:e5 ]",
|
||||
"[ 10/3 → 11/3 | note:b4 ]",
|
||||
"[ 11/3 → 23/6 | note:d5 ]",
|
||||
"[ 23/6 → 4/1 | note:c5 ]",
|
||||
"[ 0/1 → 1/8 | s:bd ]",
|
||||
"[ 3/8 → 1/2 | s:bd ]",
|
||||
"[ 3/4 → 7/8 | s:bd ]",
|
||||
"[ 1/1 → 9/8 | s:bd ]",
|
||||
"[ 1/1 → 9/8 | s:cp ]",
|
||||
"[ 11/8 → 3/2 | s:bd ]",
|
||||
"[ 11/8 → 3/2 | s:cp ]",
|
||||
"[ 7/4 → 15/8 | s:bd ]",
|
||||
"[ 7/4 → 15/8 | s:cp ]",
|
||||
"[ 2/1 → 17/8 | s:cp ]",
|
||||
"[ 19/8 → 5/2 | s:cp ]",
|
||||
"[ 11/4 → 23/8 | s:cp ]",
|
||||
"[ 3/1 → 25/8 | s:bd ]",
|
||||
"[ 27/8 → 7/2 | s:bd ]",
|
||||
"[ 15/4 → 31/8 | s:bd ]",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -7523,6 +7730,27 @@ exports[`runs examples > example "squiz" example index 0 1`] = `
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "stack" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | note:e4 ]",
|
||||
"[ 0/1 → 1/1 | note:g3 ]",
|
||||
"[ 0/1 → 1/1 | note:b3 ]",
|
||||
"[ 1/2 → 1/1 | note:d4 ]",
|
||||
"[ 1/1 → 3/2 | note:e4 ]",
|
||||
"[ 1/1 → 2/1 | note:g3 ]",
|
||||
"[ 1/1 → 2/1 | note:b3 ]",
|
||||
"[ 3/2 → 2/1 | note:d4 ]",
|
||||
"[ 2/1 → 5/2 | note:e4 ]",
|
||||
"[ 2/1 → 3/1 | note:g3 ]",
|
||||
"[ 2/1 → 3/1 | note:b3 ]",
|
||||
"[ 5/2 → 3/1 | note:d4 ]",
|
||||
"[ 3/1 → 7/2 | note:e4 ]",
|
||||
"[ 3/1 → 4/1 | note:g3 ]",
|
||||
"[ 3/1 → 4/1 | note:b3 ]",
|
||||
"[ 7/2 → 4/1 | note:d4 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "stack" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | note:c4 ]",
|
||||
"[ 0/1 → 1/4 | s:hh ]",
|
||||
@@ -7563,27 +7791,6 @@ exports[`runs examples > example "stack" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "stack" example index 0 2`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | note:e4 ]",
|
||||
"[ 0/1 → 1/1 | note:g3 ]",
|
||||
"[ 0/1 → 1/1 | note:b3 ]",
|
||||
"[ 1/2 → 1/1 | note:d4 ]",
|
||||
"[ 1/1 → 3/2 | note:e4 ]",
|
||||
"[ 1/1 → 2/1 | note:g3 ]",
|
||||
"[ 1/1 → 2/1 | note:b3 ]",
|
||||
"[ 3/2 → 2/1 | note:d4 ]",
|
||||
"[ 2/1 → 5/2 | note:e4 ]",
|
||||
"[ 2/1 → 3/1 | note:g3 ]",
|
||||
"[ 2/1 → 3/1 | note:b3 ]",
|
||||
"[ 5/2 → 3/1 | note:d4 ]",
|
||||
"[ 3/1 → 7/2 | note:e4 ]",
|
||||
"[ 3/1 → 4/1 | note:g3 ]",
|
||||
"[ 3/1 → 4/1 | note:b3 ]",
|
||||
"[ 7/2 → 4/1 | note:d4 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "steps" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | s:bd ]",
|
||||
|
||||
@@ -26,7 +26,7 @@ author: froos
|
||||
- fix: 💄 Enhance visualisation of the Tutorial on mobile by @puria in https://github.com/tidalcycles/strudel/pull/19
|
||||
- Stateful queries and events (WIP) by @yaxu in https://github.com/tidalcycles/strudel/pull/14
|
||||
- Fix resolveState by @yaxu in https://github.com/tidalcycles/strudel/pull/22
|
||||
- added \_asNumber + interprete numbers as midi by @felixroos in https://github.com/tidalcycles/strudel/pull/21
|
||||
- added \_asNumber + interpret numbers as midi by @felixroos in https://github.com/tidalcycles/strudel/pull/21
|
||||
- Update package.json by @ChiakiUehira in https://github.com/tidalcycles/strudel/pull/23
|
||||
- packaging by @felixroos in https://github.com/tidalcycles/strudel/pull/24
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ author: froos
|
||||
|
||||
- support freq in pianoroll by @felixroos in https://github.com/tidalcycles/strudel/pull/308
|
||||
- ICLC2023 paper WIP by @yaxu in https://github.com/tidalcycles/strudel/pull/306
|
||||
- fix: copy share link to clipboard was broken for some browers by @felixroos in https://github.com/tidalcycles/strudel/pull/311
|
||||
- fix: copy share link to clipboard was broken for some browsers by @felixroos in https://github.com/tidalcycles/strudel/pull/311
|
||||
- Jsdoc component by @felixroos in https://github.com/tidalcycles/strudel/pull/312
|
||||
- object support for .scale by @felixroos in https://github.com/tidalcycles/strudel/pull/307
|
||||
- Astro build by @felixroos in https://github.com/tidalcycles/strudel/pull/315
|
||||
|
||||
@@ -138,7 +138,7 @@ Plenty of things have been added to the docs, including a [showcase of what peop
|
||||
- Export patterns + ui tweaks by @felixroos in https://github.com/tidalcycles/strudel/pull/855
|
||||
- Pattern organization by @felixroos in https://github.com/tidalcycles/strudel/pull/858
|
||||
- Sound Import from local file system by @daslyfe in https://github.com/tidalcycles/strudel/pull/839
|
||||
- bugfix: suspend and close exisiting audio context when changing interface by @daslyfe in https://github.com/tidalcycles/strudel/pull/882
|
||||
- bugfix: suspend and close existing audio context when changing interface by @daslyfe in https://github.com/tidalcycles/strudel/pull/882
|
||||
- add root mode for voicings by @felixroos in https://github.com/tidalcycles/strudel/pull/887
|
||||
- scales can now be anchored by @felixroos in https://github.com/tidalcycles/strudel/pull/888
|
||||
- add dough function for raw dsp by @felixroos in https://github.com/tidalcycles/strudel/pull/707 (experimental)
|
||||
@@ -195,7 +195,7 @@ Plenty of things have been added to the docs, including a [showcase of what peop
|
||||
- Update tauri.yml workflow file by @vasilymilovidov in https://github.com/tidalcycles/strudel/pull/705
|
||||
- vite-vanilla-repl readme fix by @felixroos in https://github.com/tidalcycles/strudel/pull/737
|
||||
- completely revert config mess by @felixroos in https://github.com/tidalcycles/strudel/pull/745
|
||||
- hopefully fix trainling slashes bug by @felixroos in https://github.com/tidalcycles/strudel/pull/753
|
||||
- hopefully fix trailing slashes bug by @felixroos in https://github.com/tidalcycles/strudel/pull/753
|
||||
- Update vite pwa by @felixroos in https://github.com/tidalcycles/strudel/pull/772
|
||||
- Update to Astro 3 by @felixroos in https://github.com/tidalcycles/strudel/pull/775
|
||||
- support multiple named serial connections, change default baudrate by @yaxu in https://github.com/tidalcycles/strudel/pull/551
|
||||
|
||||
@@ -120,6 +120,7 @@ export function MiniRepl({
|
||||
'cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground bg-lineHighlight hover:bg-background',
|
||||
started ? 'animate-pulse' : '',
|
||||
)}
|
||||
aria-label={started ? 'stop' : 'play'}
|
||||
onClick={() => editorRef.current?.toggle()}
|
||||
>
|
||||
<Icon type={started ? 'stop' : 'play'} />
|
||||
@@ -129,6 +130,7 @@ export function MiniRepl({
|
||||
'w-16 flex items-center justify-center p-1 text-foreground border-lineHighlight bg-lineHighlight',
|
||||
isDirty ? 'text-foreground hover:bg-background cursor-pointer' : 'opacity-50 cursor-not-allowed',
|
||||
)}
|
||||
aria-label="update"
|
||||
onClick={() => editorRef.current?.evaluate()}
|
||||
>
|
||||
<Icon type="refresh" />
|
||||
@@ -140,6 +142,7 @@ export function MiniRepl({
|
||||
className={
|
||||
'cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground bg-lineHighlight hover:bg-background'
|
||||
}
|
||||
aria-label="previous example"
|
||||
onClick={() => changeTune(tuneIndex - 1)}
|
||||
>
|
||||
<div className="rotate-180">
|
||||
@@ -150,6 +153,7 @@ export function MiniRepl({
|
||||
className={
|
||||
'cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground bg-lineHighlight hover:bg-background'
|
||||
}
|
||||
aria-label="next example"
|
||||
onClick={() => changeTune(tuneIndex + 1)}
|
||||
>
|
||||
<Icon type="skip" />
|
||||
|
||||
@@ -25,26 +25,14 @@ These are the equivalents used by the Mini Notation:
|
||||
|
||||
<JsDoc client:idle name="cat" h={0} />
|
||||
|
||||
You can also use cat as a chained function like this:
|
||||
|
||||
<JsDoc client:idle name="Pattern.cat" h={0} hideDescription />
|
||||
|
||||
## seq
|
||||
|
||||
<JsDoc client:idle name="seq" h={0} />
|
||||
|
||||
Or as a chained function:
|
||||
|
||||
<JsDoc client:idle name="Pattern.seq" h={0} hideDescription />
|
||||
|
||||
## stack
|
||||
|
||||
<JsDoc client:idle name="stack" h={0} />
|
||||
|
||||
As a chained function:
|
||||
|
||||
<JsDoc client:idle name="Pattern.stack" h={0} hideDescription />
|
||||
|
||||
## s_cat
|
||||
|
||||
<JsDoc client:idle name="s_cat" h={0} />
|
||||
|
||||
@@ -43,7 +43,7 @@ export function Header({ context, embedded = false }) {
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cx('mt-[1px]', started && 'animate-spin', 'cursor-pointer')}
|
||||
className={cx('mt-[1px]', started && 'animate-spin', 'cursor-pointer', isZen && 'fixed top-2 right-4')}
|
||||
onClick={() => {
|
||||
if (!isEmbedded) {
|
||||
setIsZen(!isZen);
|
||||
|
||||
@@ -3,7 +3,7 @@ import cx from '@src/cx.mjs';
|
||||
export function ConsoleTab({ log }) {
|
||||
return (
|
||||
<div id="console-tab" className="break-all px-4 dark:text-white text-stone-900 text-sm">
|
||||
<pre>{`███████╗████████╗██████╗ ██╗ ██╗██████╗ ███████╗██╗
|
||||
<pre aria-hidden="true">{`███████╗████████╗██████╗ ██╗ ██╗██████╗ ███████╗██╗
|
||||
██╔════╝╚══██╔══╝██╔══██╗██║ ██║██╔══██╗██╔════╝██║
|
||||
███████╗ ██║ ██████╔╝██║ ██║██║ ██║█████╗ ██║
|
||||
╚════██║ ██║ ██╔══██╗██║ ██║██║ ██║██╔══╝ ██║
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
import jsdocJson from '../../../../../doc.json';
|
||||
const visibleFunctions = jsdocJson.docs
|
||||
const availableFunctions = jsdocJson.docs
|
||||
.filter(({ name, description }) => name && !name.startsWith('_') && !!description)
|
||||
.sort((a, b) => /* a.meta.filename.localeCompare(b.meta.filename) + */ a.name.localeCompare(b.name));
|
||||
|
||||
@@ -10,9 +12,29 @@ const getInnerText = (html) => {
|
||||
};
|
||||
|
||||
export function Reference() {
|
||||
const [search, setSearch] = useState('');
|
||||
|
||||
const visibleFunctions = useMemo(() => {
|
||||
return availableFunctions.filter((entry) => {
|
||||
if (!search) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return entry.name.includes(search) || (entry.synonyms?.some((s) => s.includes(search)) ?? false);
|
||||
});
|
||||
}, [search]);
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full pt-2 text-foreground overflow-hidden">
|
||||
<div className="w-42 flex-none h-full overflow-y-auto overflow-x-hidden pr-4">
|
||||
<div class="w-full ml-2 mb-2 top-0 sticky">
|
||||
<input
|
||||
className="w-full p-1 bg-background rounded-md"
|
||||
placeholder="Search"
|
||||
value={search}
|
||||
onInput={(event) => setSearch(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
{visibleFunctions.map((entry, i) => (
|
||||
<a
|
||||
key={i}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useMemo } from 'react';
|
||||
import { audioEngineTargets, defaultSettings, settingsMap, useSettings } from '../../../settings.mjs';
|
||||
import { defaultSettings, settingsMap, useSettings } from '../../../settings.mjs';
|
||||
import { themes } from '@strudel/codemirror';
|
||||
import { isUdels, setGlobalAudioVolume } from '../../util.mjs';
|
||||
import { isUdels } from '../../util.mjs';
|
||||
import { ButtonGroup } from './Forms.jsx';
|
||||
import { AudioDeviceSelector } from './AudioDeviceSelector.jsx';
|
||||
import { AudioEngineTargetSelector } from './AudioEngineTargetSelector.jsx';
|
||||
@@ -32,47 +31,23 @@ function SelectInput({ value, options, onChange }) {
|
||||
);
|
||||
}
|
||||
|
||||
function NumberSlider({ value, onChange, min, max, step = 1, ...rest }) {
|
||||
const fractionalDigits = useMemo(() => {
|
||||
const stepStr = step.toString();
|
||||
const decimalPointIdx = stepStr.indexOf('.');
|
||||
if (decimalPointIdx < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return stepStr.slice(decimalPointIdx + 1).length;
|
||||
}, [step]);
|
||||
|
||||
const textInputCharWidth = useMemo(() => {
|
||||
const maxValueWholePartLength = Math.floor(max).toString().length;
|
||||
return maxValueWholePartLength + '.'.length + fractionalDigits;
|
||||
}, [max, fractionalDigits]);
|
||||
|
||||
function NumberSlider({ value, onChange, step = 1, ...rest }) {
|
||||
return (
|
||||
<div className="flex space-x-2 gap-1">
|
||||
<input
|
||||
className="p-2 grow"
|
||||
type="range"
|
||||
value={value}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
onChange={(e) => onChange(Number(e.target.value))}
|
||||
{...rest}
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
style={{
|
||||
// approximate text size + some leeway for the default padding + some space between the browser's up/down arrows and the input's value
|
||||
width: `calc(${textInputCharWidth}ch + 2 * 0.75rem + 1rem)`,
|
||||
}}
|
||||
value={Number(value).toFixed(fractionalDigits)}
|
||||
min={min}
|
||||
max={max}
|
||||
value={value}
|
||||
step={step}
|
||||
className="bg-background rounded-md"
|
||||
className="w-16 bg-background rounded-md"
|
||||
onChange={(e) => onChange(Number(e.target.value))}
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -126,7 +101,6 @@ export function SettingsTab({ started }) {
|
||||
panelPosition,
|
||||
audioDeviceName,
|
||||
audioEngineTarget,
|
||||
audioVolume,
|
||||
} = useSettings();
|
||||
const shouldAlwaysSync = isUdels();
|
||||
const canChangeAudioDevice = AudioContext.prototype.setSinkId != null;
|
||||
@@ -161,19 +135,6 @@ export function SettingsTab({ started }) {
|
||||
}}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="Audio Volume">
|
||||
{audioEngineTarget === audioEngineTargets.osc && (
|
||||
<span class="text-sm italic">Has no effect when Audio Engine Target is OSC</span>
|
||||
)}
|
||||
<NumberSlider
|
||||
value={audioVolume}
|
||||
onChange={(audioVolume) => settingsMap.setKey('audioVolume', audioVolume)}
|
||||
min={0}
|
||||
max={100}
|
||||
step={0.1}
|
||||
disabled={audioEngineTarget === audioEngineTargets.osc}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="Theme">
|
||||
<SelectInput options={themeOptions} value={theme} onChange={(theme) => settingsMap.setKey('theme', theme)} />
|
||||
</FormItem>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import useEvent from '@src/useEvent.mjs';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { getAudioContext, soundMap, connectToDestination } from '@strudel/webaudio';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import React, { useMemo, useRef, useState } from 'react';
|
||||
import { settingsMap, useSettings } from '../../../settings.mjs';
|
||||
import { ButtonGroup } from './Forms.jsx';
|
||||
import ImportSoundsButton from './ImportSoundsButton.jsx';
|
||||
@@ -12,15 +12,20 @@ const getSamples = (samples) =>
|
||||
export function SoundsTab() {
|
||||
const sounds = useStore(soundMap);
|
||||
const { soundsFilter } = useSettings();
|
||||
const [search, setSearch] = useState('');
|
||||
|
||||
const soundEntries = useMemo(() => {
|
||||
let filtered = Object.entries(sounds)
|
||||
.filter(([key]) => !key.startsWith('_'))
|
||||
.sort((a, b) => a[0].localeCompare(b[0]));
|
||||
if (!sounds) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let filtered = Object.entries(sounds)
|
||||
.filter(([key]) => !key.startsWith('_'))
|
||||
.sort((a, b) => a[0].localeCompare(b[0]))
|
||||
.filter(([name]) => name.toLowerCase().includes(search.toLowerCase()));
|
||||
|
||||
if (soundsFilter === 'user') {
|
||||
return filtered.filter(([key, { data }]) => !data.prebake);
|
||||
return filtered.filter(([_, { data }]) => !data.prebake);
|
||||
}
|
||||
if (soundsFilter === 'drums') {
|
||||
return filtered.filter(([_, { data }]) => data.type === 'sample' && data.tag === 'drum-machines');
|
||||
@@ -32,9 +37,11 @@ export function SoundsTab() {
|
||||
return filtered.filter(([_, { data }]) => ['synth', 'soundfont'].includes(data.type));
|
||||
}
|
||||
return filtered;
|
||||
}, [sounds, soundsFilter]);
|
||||
}, [sounds, soundsFilter, search]);
|
||||
|
||||
// holds mutable ref to current triggered sound
|
||||
const trigRef = useRef();
|
||||
|
||||
// stop current sound on mouseup
|
||||
useEvent('mouseup', () => {
|
||||
const t = trigRef.current;
|
||||
@@ -43,8 +50,17 @@ export function SoundsTab() {
|
||||
ref?.stop(getAudioContext().currentTime + 0.01);
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<div id="sounds-tab" className="px-4 flex flex-col w-full h-full dark:text-white text-stone-900">
|
||||
<div className="w-full ml-2 mb-2 top-0 sticky">
|
||||
<input
|
||||
className="w-full p-1 bg-background rounded-md"
|
||||
placeholder="Search"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="pb-2 flex shrink-0 flex-wrap">
|
||||
<ButtonGroup
|
||||
value={soundsFilter}
|
||||
|
||||
@@ -23,10 +23,10 @@ async function hasStrudelJson(subpath) {
|
||||
async function loadStrudelJson(subpath) {
|
||||
const contents = await readTextFile(subpath + '/strudel.json', { dir });
|
||||
const sampleMap = JSON.parse(contents);
|
||||
processSampleMap(sampleMap, (key, value) => {
|
||||
registerSound(key, (t, hapValue, onended) => onTriggerSample(t, hapValue, onended, value, fileResolver(subpath)), {
|
||||
processSampleMap(sampleMap, (key, bank) => {
|
||||
registerSound(key, (t, hapValue, onended) => onTriggerSample(t, hapValue, onended, bank, fileResolver(subpath)), {
|
||||
type: 'sample',
|
||||
samples: value,
|
||||
samples: bank,
|
||||
fileSystem: true,
|
||||
tag: 'local',
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
resetLoadedSounds,
|
||||
initAudioOnFirstClick,
|
||||
} from '@strudel/webaudio';
|
||||
import { getAudioDevices, setAudioDevice, setGlobalAudioVolume, setVersionDefaultsFrom } from './util.mjs';
|
||||
import { getAudioDevices, setAudioDevice, setVersionDefaultsFrom } from './util.mjs';
|
||||
import { StrudelMirror, defaultSettings } from '@strudel/codemirror';
|
||||
import { clearHydra } from '@strudel/hydra';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
@@ -162,7 +162,6 @@ export function useReplContext() {
|
||||
// on first load, set stored audio device if possible
|
||||
useEffect(() => {
|
||||
const { audioDeviceName } = _settings;
|
||||
|
||||
if (audioDeviceName !== defaultAudioDeviceName) {
|
||||
getAudioDevices().then((devices) => {
|
||||
const deviceID = devices.get(audioDeviceName);
|
||||
@@ -174,9 +173,6 @@ export function useReplContext() {
|
||||
}
|
||||
}, []);
|
||||
|
||||
// set stored audio volume
|
||||
useEffect(() => setGlobalAudioVolume(_settings.audioVolume), [_settings.audioVolume]);
|
||||
|
||||
//
|
||||
// UI Actions
|
||||
//
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import { evalScope, hash2code, logger } from '@strudel/core';
|
||||
import { settingPatterns, defaultAudioDeviceName } from '../settings.mjs';
|
||||
import {
|
||||
getAudioContext,
|
||||
initializeAudioOutput,
|
||||
setDefaultAudioContext,
|
||||
setGlobalGain,
|
||||
setVersionDefaults,
|
||||
} from '@strudel/webaudio';
|
||||
import { getAudioContext, initializeAudioOutput, setDefaultAudioContext, setVersionDefaults } from '@strudel/webaudio';
|
||||
import { getMetadata } from '../metadata_parser';
|
||||
import { isTauri } from '../tauri.mjs';
|
||||
import './Repl.css';
|
||||
@@ -194,19 +188,6 @@ export const setAudioDevice = async (id) => {
|
||||
initializeAudioOutput();
|
||||
};
|
||||
|
||||
const NATURAL_LOG_10 = Math.log(1000);
|
||||
const LOW_VOLUME_CONSTANT = Math.exp(0.1 * NATURAL_LOG_10);
|
||||
export const setGlobalAudioVolume = (volume) => {
|
||||
// Gain is calculated to adjust the volume to a logarithmic scale to match how us humans perceive loudness.
|
||||
// Formula is taken from https://www.dr-lex.be/info-stuff/volumecontrols.html
|
||||
volume /= 100; // [0, 1]
|
||||
|
||||
let gain = volume >= 0.1 ? 0.001 * Math.exp(NATURAL_LOG_10 * volume) : volume * 10 * 0.001 * LOW_VOLUME_CONSTANT;
|
||||
gain = Math.max(0, Math.min(gain, 1)); // just in case
|
||||
|
||||
setGlobalGain(gain);
|
||||
};
|
||||
|
||||
export function setVersionDefaultsFrom(code) {
|
||||
try {
|
||||
const metadata = getMetadata(code);
|
||||
|
||||
@@ -34,7 +34,6 @@ export const defaultSettings = {
|
||||
userPatterns: '{}',
|
||||
audioDeviceName: defaultAudioDeviceName,
|
||||
audioEngineTarget: audioEngineTargets.webaudio,
|
||||
audioVolume: 50,
|
||||
};
|
||||
|
||||
let search = null;
|
||||
|
||||
Reference in New Issue
Block a user