mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-14 14:53:45 -04:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b765085e0 | |||
| 28b978f95f | |||
| cdf5456b75 |
+1
-1
@@ -67,6 +67,6 @@
|
||||
"lerna": "^6.6.1",
|
||||
"prettier": "^2.8.8",
|
||||
"rollup-plugin-visualizer": "^5.8.1",
|
||||
"vitest": "^0.33.0"
|
||||
"vitest": "^0.28.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,7 +509,7 @@ const generic_params = [
|
||||
* @superDirtOnly
|
||||
*/
|
||||
['octave'],
|
||||
|
||||
['offset'], // TODO: what is this? not found in tidal doc
|
||||
// ['ophatdecay'],
|
||||
// TODO: example
|
||||
/**
|
||||
@@ -573,15 +573,6 @@ const generic_params = [
|
||||
// TODO: dedup with synth param, see https://tidalcycles.org/docs/reference/synthesizers/#superpiano
|
||||
// ['velocity'],
|
||||
['voice'], // TODO: synth param
|
||||
|
||||
// voicings // https://github.com/tidalcycles/strudel/issues/506
|
||||
['chord'], // chord to voice, like C Eb Fm7 G7. the symbols can be defined via addVoicings
|
||||
['dictionary', 'dict'], // which dictionary to use for the voicings
|
||||
['anchor'], // the top note to align the voicing to, defaults to c5
|
||||
['offset'], // how the voicing is offset from the anchored position
|
||||
['octaves'], // how many octaves are voicing steps spread apart, defaults to 1
|
||||
[['mode', 'anchor']], // below = anchor note will be removed from the voicing, useful for melody harmonization
|
||||
|
||||
/**
|
||||
* Sets the level of reverb.
|
||||
*
|
||||
|
||||
@@ -36,6 +36,6 @@
|
||||
"gitHead": "0e26d4e741500f5bae35b023608f062a794905c2",
|
||||
"devDependencies": {
|
||||
"vite": "^4.3.3",
|
||||
"vitest": "^0.33.0"
|
||||
"vitest": "^0.28.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,14 +783,13 @@ export class Pattern {
|
||||
hap.setContext({
|
||||
...hap.context,
|
||||
onTrigger: (...args) => {
|
||||
// run previously set trigger, if it exists
|
||||
hap.context.onTrigger?.(...args);
|
||||
if (!dominant && hap.context.onTrigger) {
|
||||
hap.context.onTrigger(...args);
|
||||
}
|
||||
onTrigger(...args);
|
||||
},
|
||||
// if dominantTrigger is set to true, the default output (webaudio) will be disabled
|
||||
// when using multiple triggers, you cannot flip this flag to false again!
|
||||
// example: x.csound('CooLSynth').log() as well as x.log().csound('CooLSynth') should work the same
|
||||
dominantTrigger: hap.context.dominantTrigger || dominant,
|
||||
// we need this to know later if the default trigger should still fire
|
||||
dominantTrigger: dominant,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -2241,18 +2240,14 @@ const _loopAt = function (factor, pat, cps = 1) {
|
||||
.slow(factor);
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* Chops samples into the given number of slices, triggering those slices with a given pattern of slice numbers.
|
||||
* Instead of a number, it also accepts a list of numbers from 0 to 1 to slice at specific points.
|
||||
* @name slice
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
* s("breaks165").slice(8, "0 1 <2 2*2> 3 [4 0] 5 6 7".every(3, rev)).slow(1.5)
|
||||
* @example
|
||||
* await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
* s("breaks125/2").fit().slice([0,.25,.5,.75], "0 1 1 <2 3>")
|
||||
*/
|
||||
|
||||
export const slice = register(
|
||||
@@ -2263,9 +2258,9 @@ export const slice = register(
|
||||
opat.outerBind((o) => {
|
||||
// If it's not an object, assume it's a string and make it a 's' control parameter
|
||||
o = o instanceof Object ? o : { s: o };
|
||||
const begin = Array.isArray(n) ? n[i] : i / n;
|
||||
const end = Array.isArray(n) ? n[i + 1] : (i + 1) / n;
|
||||
return pure({ begin, end, _slices: n, ...o });
|
||||
// Remember we must stay pure and avoid editing the object directly
|
||||
const toAdd = { begin: i / n, end: (i + 1) / n, _slices: n };
|
||||
return pure({ ...toAdd, ...o });
|
||||
}),
|
||||
),
|
||||
);
|
||||
@@ -2307,19 +2302,6 @@ export const { loopAt, loopat } = register(['loopAt', 'loopat'], function (facto
|
||||
return _loopAt(factor, pat, 1);
|
||||
});
|
||||
|
||||
// this function will be redefined in repl.mjs to use the correct cps value.
|
||||
// It is still here to work in cases where repl.mjs is not used
|
||||
|
||||
export const fit = register('fit', (pat) =>
|
||||
pat.withHap((hap) =>
|
||||
hap.withValue((v) => ({
|
||||
...v,
|
||||
speed: 1 / hap.whole.duration,
|
||||
unit: 'c',
|
||||
})),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Makes the sample fit the given number of cycles and cps value, by
|
||||
* changing the speed. Please note that at some point cps will be
|
||||
|
||||
@@ -198,6 +198,8 @@ export function pianoroll({
|
||||
let from = -cycles * playhead;
|
||||
let to = cycles * (1 - playhead);
|
||||
|
||||
ctx.lineWidth = 1;
|
||||
|
||||
if (timeframeProp) {
|
||||
console.warn('timeframe is deprecated! use from/to instead');
|
||||
from = 0;
|
||||
@@ -246,7 +248,7 @@ export function pianoroll({
|
||||
haps
|
||||
// .filter(inFrame)
|
||||
.forEach((event) => {
|
||||
const isActive = event.whole.begin <= time && event.endClipped > time;
|
||||
const isActive = event.whole.begin <= time && event.whole.end > time;
|
||||
const color = event.value?.color || event.context?.color;
|
||||
ctx.fillStyle = color || inactive;
|
||||
ctx.strokeStyle = color || active;
|
||||
|
||||
@@ -191,6 +191,13 @@ export const chooseInWith = (pat, xs) => {
|
||||
*/
|
||||
export const choose = (...xs) => chooseWith(rand, xs);
|
||||
|
||||
/**
|
||||
* Chooses from the given list of values (or patterns of values), according
|
||||
* to the pattern that the method is called on. The pattern should be in
|
||||
* the range 0 .. 1.
|
||||
* @param {...any} xs
|
||||
* @returns {Pattern}
|
||||
*/
|
||||
Pattern.prototype.choose = function (...xs) {
|
||||
return chooseWith(this, xs);
|
||||
};
|
||||
|
||||
@@ -11,24 +11,21 @@ export const tokenizeNote = (note) => {
|
||||
if (typeof note !== 'string') {
|
||||
return [];
|
||||
}
|
||||
const [pc, acc = '', oct] = note.match(/^([a-gA-G])([#bsf]*)([0-9]*)$/)?.slice(1) || [];
|
||||
const [pc, acc = '', oct] = note.match(/^([a-gA-G])([#bsf]*)([0-9])?$/)?.slice(1) || [];
|
||||
if (!pc) {
|
||||
return [];
|
||||
}
|
||||
return [pc, acc, oct ? Number(oct) : undefined];
|
||||
};
|
||||
|
||||
const chromas = { c: 0, d: 2, e: 4, f: 5, g: 7, a: 9, b: 11 };
|
||||
const accs = { '#': 1, b: -1, s: 1, f: -1 };
|
||||
|
||||
// turns the given note into its midi number representation
|
||||
export const noteToMidi = (note, defaultOctave = 3) => {
|
||||
const [pc, acc, oct = defaultOctave] = tokenizeNote(note);
|
||||
export const noteToMidi = (note) => {
|
||||
const [pc, acc, oct = 3] = tokenizeNote(note);
|
||||
if (!pc) {
|
||||
throw new Error('not a note: "' + note + '"');
|
||||
}
|
||||
const chroma = chromas[pc.toLowerCase()];
|
||||
const offset = acc?.split('').reduce((o, char) => o + accs[char], 0) || 0;
|
||||
const chroma = { c: 0, d: 2, e: 4, f: 5, g: 7, a: 9, b: 11 }[pc.toLowerCase()];
|
||||
const offset = acc?.split('').reduce((o, char) => o + { '#': 1, b: -1, s: 1, f: -1 }[char], 0) || 0;
|
||||
return (Number(oct) + 1) * 12 + chroma + offset;
|
||||
};
|
||||
export const midiToFreq = (n) => {
|
||||
|
||||
@@ -113,8 +113,8 @@ Pattern.prototype.midi = function (output) {
|
||||
const velocity = hap.context?.velocity ?? 0.9; // TODO: refactor velocity
|
||||
const duration = hap.duration.valueOf() * 1000 - 5;
|
||||
|
||||
if (note != null) {
|
||||
const midiNumber = typeof note === 'number' ? note : noteToMidi(note);
|
||||
if (note) {
|
||||
const midiNumber = noteToMidi(note);
|
||||
device.playNote(midiNumber, midichan, {
|
||||
time,
|
||||
duration,
|
||||
|
||||
@@ -37,6 +37,6 @@
|
||||
"devDependencies": {
|
||||
"peggy": "^3.0.2",
|
||||
"vite": "^4.3.3",
|
||||
"vitest": "^0.33.0"
|
||||
"vitest": "^0.28.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,5 +3,3 @@ import './voicings.mjs';
|
||||
|
||||
export * from './tonal.mjs';
|
||||
export * from './voicings.mjs';
|
||||
|
||||
import './ireal.mjs';
|
||||
|
||||
@@ -1,523 +0,0 @@
|
||||
// explore them here: https://codesandbox.io/s/voicing-explorer-ireal-47tkx5?file=/src/ireal.js:0-16036
|
||||
// scraped via: https://codesandbox.io/s/ireal-midi-scraper-2-gjz2mr?file=/src/index.js
|
||||
|
||||
export const simple = {
|
||||
2: ['1P 5P 8P 9M', '1P 5P 8P 9M 12P', '5P 8P 9M 12P'],
|
||||
5: ['1P 5P 8P 12P', '5P 8P 12P 15P'],
|
||||
6: ['1P 5P 6M 8P 10M', '1P 5P 8P 10M 13M', '3M 5P 8P 10M 13M', '5P 8P 10M 12P 13M'],
|
||||
7: [
|
||||
'1P 5P 7m 8P 10M',
|
||||
'1P 7m 8P 10M 12P',
|
||||
'3M 7m 8P 10M 12P',
|
||||
'3M 7m 8P 10M 14m',
|
||||
'3M 7m 10M 12P 15P',
|
||||
'7m 10M 12P 14m 15P',
|
||||
'7m 10M 12P 15P 17M',
|
||||
],
|
||||
9: [
|
||||
'1P 5P 7m 9M 10M',
|
||||
'1P 7m 9M 10M 12P',
|
||||
'3M 7m 8P 9M 12P',
|
||||
'7m 9M 10M 14m 15P',
|
||||
'3M 7m 8P 12P 16M',
|
||||
'7m 10M 12P 15P 16M',
|
||||
],
|
||||
11: ['1P 5P 7m 9M 11P', '5P 7m 8P 9M 11P', '7m 8P 9M 11P 12P', '7m 8P 11P 12P 16M'],
|
||||
13: ['1P 6M 7m 9M 10M', '1P 7m 9M 10M 13M', '3M 7m 8P 9M 13M', '7m 8P 9M 10M 13M', '7m 9M 10M 13M 15P'],
|
||||
69: ['1P 5P 6M 9M 10M', '1P 5P 9M 10M 13M', '3M 5P 8P 9M 13M', '5P 8P 9M 10M 13M'],
|
||||
add9: ['1P 5P 8P 9M 10M', '1P 5P 9M 10M 12P', '3M 8P 9M 10M 12P', '3M 8P 9M 12P 15P', '5P 8P 9M 12P 17M'],
|
||||
'+': [
|
||||
'1P 3M 6m 8P 10M',
|
||||
'1P 6m 8P 10M 13m',
|
||||
'3M 6m 8P 10M 13m',
|
||||
'3M 8P 10M 13m 15P',
|
||||
'6m 8P 10M 13m 15P',
|
||||
'6m 10M 13m 15P 17M',
|
||||
],
|
||||
o: ['1P 5d 8P 10m 12d', '3m 8P 10m 12d 15P', '5d 8P 10m 12d 15P'],
|
||||
h: [
|
||||
'3m 5d 7m 8P 10m',
|
||||
'1P 5d 7m 10m 12d',
|
||||
'3m 7m 8P 10m 12d',
|
||||
'3m 7m 8P 12d 14m',
|
||||
'5d 7m 8P 10m 14m',
|
||||
'5d 8P 10m 12d 14m',
|
||||
'7m 10m 12d 14m 15P',
|
||||
'5d 8P 10m 14m 17m',
|
||||
],
|
||||
sus: ['1P 4P 5P 8P', '1P 4P 5P 8P 11P', '5P 8P 11P 12P', '5P 8P 11P 12P 15P'],
|
||||
'^': ['1P 5P 8P 10M', '1P 5P 8P 10M 12P', '3M 5P 8P 10M 12P', '3M 8P 10M 12P 15P', '5P 8P 10M 12P 15P'],
|
||||
'-': ['1P 3m 5P 8P 10m', '1P 5P 8P 10m 12P', '3m 5P 8P 10m 12P', '5P 8P 10m 12P 15P'],
|
||||
'^7': ['1P 5P 7M 10M 12P', '1P 10M 12P 14M', '3M 8P 10M 12P 14M', '5P 8P 10M 12P 14M', '5P 8P 10M 14M 17M'],
|
||||
'-7': [
|
||||
'1P 3m 5P 7m 10m',
|
||||
'1P 5P 7m 10m 12P',
|
||||
'3m 7m 8P 10m 12P',
|
||||
'3m 7m 8P 10m 14m',
|
||||
'5P 7m 8P 10m 14m',
|
||||
'7m 10m 12P 14m 15P',
|
||||
'5P 8P 10m 14m 17m',
|
||||
'7m 10m 12P 15P 17m',
|
||||
],
|
||||
'7sus': ['1P 5P 7m 8P 11P', '5P 8P 11P 12P 14m', '7m 8P 11P 12P 14m', '7m 11P 12P 14m 18P'],
|
||||
h7: [
|
||||
'3m 5d 7m 8P 10m',
|
||||
'1P 5d 7m 10m 12d',
|
||||
'1P 7m 10m 12d',
|
||||
'3m 7m 8P 10m 12d',
|
||||
'3m 7m 8P 12d 14m',
|
||||
'5d 7m 8P 10m 14m',
|
||||
'5d 8P 10m 12d 14m',
|
||||
'7m 10m 12d 14m 15P',
|
||||
'5d 8P 10m 14m 17m',
|
||||
],
|
||||
o7: [
|
||||
'1P 6M 8P 10m 12d',
|
||||
'1P 6M 10m 12d 13M',
|
||||
'3m 8P 10m 12d 13M',
|
||||
'3m 8P 12d 13M 15P',
|
||||
'5d 10m 12d 13M 15P',
|
||||
'5d 10m 13M 15P 17m',
|
||||
'6M 12d 13M 15P 17m',
|
||||
'6M 12d 15P 17m 19d',
|
||||
],
|
||||
'^9': [
|
||||
'1P 5P 7M 9M 10M',
|
||||
'1P 7M 9M 10M 12P',
|
||||
'3M 7M 8P 9M 12P',
|
||||
'3M 7M 8P 12P 16M',
|
||||
'5P 8P 10M 14M 16M',
|
||||
'7M 8P 10M 12P 16M',
|
||||
],
|
||||
'^13': ['1P 6M 7M 9M 10M', '1P 7M 9M 10M 13M', '3M 7M 8P 9M 13M', '3M 7M 8P 13M 16M', '7M 8P 10M 13M 16M'],
|
||||
'^7#11': ['1P 5P 7M 10M 12d', '3M 7M 8P 10M 12d', '1P 7M 10M 12d 14M', '3M 7M 8P 12d 14M', '5P 8P 10M 12d 14M'],
|
||||
'^9#11': ['1P 3M 5d 7M 9M', '1P 7M 9M 10M 12d', '3M 7M 8P 9M 12d', '3M 8P 9M 12d 14M'],
|
||||
'^7#5': ['1P 6m 7M 10M 13m', '3M 7M 8P 10M 13m', '6m 7M 8P 10M 13m'],
|
||||
'-6': [
|
||||
'1P 3m 5P 6M 8P',
|
||||
'1P 5P 6M 8P 10m',
|
||||
'3m 5P 6M 8P 10m',
|
||||
'1P 5P 8P 10m 13M',
|
||||
'3m 5P 8P 10m 13M',
|
||||
'5P 8P 10m 12P 13M',
|
||||
'5P 8P 10m 13M 15P',
|
||||
],
|
||||
'-69': [
|
||||
'1P 3m 5P 6M 9M',
|
||||
'3m 5P 6M 8P 9M',
|
||||
'3m 6M 9M 10m 12P',
|
||||
'1P 5P 9M 10m 13M',
|
||||
'3m 5P 8P 9M 13M',
|
||||
'5P 8P 9M 10m 13M',
|
||||
'5P 8P 10m 13M 16M',
|
||||
],
|
||||
'-^7': ['1P 3m 5P 7M 10m', '1P 5P 7M 10m 12P', '3m 7M 8P 10m 12P', '5P 7M 8P 10m 14M', '5P 8P 10m 14M 17m'],
|
||||
'-^9': ['1P 3m 5P 7M 9M', '1P 7M 9M 10m 12P', '3m 7M 8P 9M 12P', '5P 8P 9M 10m 14M'],
|
||||
'-9': [
|
||||
'1P 3m 5P 7m 9M',
|
||||
'3m 5P 7m 8P 9M',
|
||||
'3m 7m 8P 9M 12P',
|
||||
'5P 8P 9M 10m 14m',
|
||||
'3m 7m 9M 12P 15P',
|
||||
'7m 10m 12P 15P 16M',
|
||||
],
|
||||
'-add9': ['1P 2M 3m 5P 8P', '1P 3m 5P 9M', '3m 5P 8P 9M 12P', '5P 8P 9M 10m 12P'],
|
||||
'-11': [
|
||||
'1P 3m 7m 9M 11P',
|
||||
'3m 7m 8P 9M 11P',
|
||||
'1P 4P 7m 10m 12P',
|
||||
'5P 8P 11P 14m',
|
||||
'3m 7m 9M 11P 15P',
|
||||
'5P 8P 11P 14m 16M',
|
||||
'7m 10m 12P 15P 18P',
|
||||
],
|
||||
'-7b5': [
|
||||
'3m 5d 7m 8P 10m',
|
||||
'1P 7m 10m 12d',
|
||||
'1P 5d 7m 10m 12d',
|
||||
'3m 7m 8P 10m 12d',
|
||||
'3m 7m 8P 12d 14m',
|
||||
'5d 7m 8P 10m 14m',
|
||||
'5d 8P 10m 12d 14m',
|
||||
'7m 10m 12d 14m 15P',
|
||||
'5d 8P 10m 14m 17m',
|
||||
],
|
||||
h9: ['1P 7m 9M 10m 12d', '3m 7m 8P 9M 12d', '5d 8P 9M 10m 14m', '7m 10m 12d 15P 16M'],
|
||||
'-b6': ['1P 5P 6m 8P 10m', '1P 5P 8P 10m 13m', '3m 5P 8P 10m 13m', '5P 8P 10m 13m', '5P 8P 10m 13m 15P'],
|
||||
'-#5': ['1P 6m 8P 10m 13m', '3m 6m 8P 10m 13m', '6m 8P 10m 13m 15P'],
|
||||
'7b9': ['1P 3M 7m 9m 10M', '3M 7m 8P 9m 10M', '3M 7m 8P 9m 14m', '7m 9m 10M 14m 15P'],
|
||||
'7#9': ['1P 3M 7m 10m', '3M 7m 8P 10m 14m', '7m 10m 10M 14m 15P'],
|
||||
'7#11': ['1P 3M 7m 10M 12d', '3M 7m 8P 10M 12d', '7m 10M 12d 14m 15P'],
|
||||
'7b5': ['1P 3M 7m 10M 12d', '3M 7m 8P 10M 12d', '7m 10M 12d 14m 15P'],
|
||||
'7#5': ['1P 3M 7m 10M 13m', '3M 7m 8P 10M 13m', '3M 7m 8P 13m 14m', '7m 10M 13m 14m 15P'],
|
||||
'9#11': ['1P 7m 9M 10M 12d', '3M 7m 8P 9M 12d', '7m 10M 12d 15P 16M'],
|
||||
'9b5': ['1P 7m 9M 10M 12d', '3M 7m 8P 9M 12d', '7m 10M 12d 15P 16M'],
|
||||
'9#5': ['1P 7m 9M 10M 13m', '3M 7m 9M 10M 13m', '3M 7m 9M 13m 14m', '7m 10M 13m 14m 16M', '7m 10M 13m 16M 17M'],
|
||||
'7b13': ['1P 3M 7m 10M 13m', '3M 7m 8P 10M 13m', '3M 7m 8P 13m 14m', '7m 10M 13m 14m 15P'],
|
||||
'7#9#5': ['1P 3M 7m 10m 13m', '3M 7m 10m 13m 15P', '7m 10M 13m 15P 17m'],
|
||||
'7#9b5': ['1P 3M 7m 10m 12d', '3M 7m 10m 12d 15P', '7m 10M 12d 15P 17m'],
|
||||
'7#9#11': ['1P 3M 7m 10m 12d', '3M 7m 10m 12d 15P', '7m 10M 12d 15P 17m'],
|
||||
'7b9#11': ['1P 7m 9m 10M 12d', '3M 7m 8P 9m 12d', '7m 8P 10M 12d 16m'],
|
||||
'7b9b5': ['1P 7m 9m 10M 12d', '3M 7m 8P 9m 12d', '7m 8P 10M 12d 16m'],
|
||||
'7b9#5': ['1P 7m 9m 10M 13m', '3M 7m 8P 9m 13m', '7m 9m 10M 13m 15P'],
|
||||
'7b9#9': ['1P 3M 7m 9m 10m', '3M 7m 8P 9m 10m', '7m 8P 10M 16m 17m'],
|
||||
'7b9b13': ['1P 7m 9m 10M 13m', '3M 7m 8P 9m 13m', '7m 9m 10M 13m 15P'],
|
||||
'7alt': [
|
||||
'3M 7m 8P 9m 12d',
|
||||
'1P 7m 10m 10M 13m',
|
||||
'3M 7m 8P 10m 13m',
|
||||
'3M 7m 9m 12d 15P',
|
||||
'3M 7m 10m 13m 15P',
|
||||
'7m 10M 12d 15P 17m',
|
||||
'7m 10M 13m 15P 17m',
|
||||
],
|
||||
'13#11': ['1P 6M 7m 10M 12d', '3M 7m 9M 12d 13M', '7m 10M 12d 13M 16M'],
|
||||
'13b9': ['1P 3M 6M 7m 9m', '1P 6M 7m 9m 10M', '3M 7m 9m 10M 13M', '3M 7m 10M 13M 16m', '7m 10M 13M 16m 17M'],
|
||||
'13#9': ['1P 3M 6M 7m 10m', '3M 7m 8P 10m 13M', '7m 10M 13M 14m 17m'],
|
||||
'7b9sus': ['1P 5P 7m 9m 11P', '5P 7m 8P 9m 11P', '7m 8P 11P 14m 16m'],
|
||||
'7susadd3': ['1P 4P 5P 7m 10M', '5P 8P 10M 11P 14m', '7m 11P 12P 15P 17M'],
|
||||
'9sus': ['1P 5P 7m 9M 11P', '5P 7m 8P 9M 11P', '7m 8P 9M 11P 12P', '7m 8P 11P 12P 16M'],
|
||||
'13sus': ['1P 4P 6M 7m 9M', '1P 7m 9M 11P 13M', '5P 7m 9M 11P 13M', '7m 9M 11P 13M 15P'],
|
||||
'7b13sus': ['1P 5P 7m 11P 13m', '5P 7m 8P 11P 13m', '7m 11P 13m 14m 15P'],
|
||||
};
|
||||
|
||||
export const complex = {
|
||||
2: ['1P 5P 6M 8P 9M', '1P 5P 8P 9M 12P', '5P 8P 9M 12P 13M', '5P 8P 9M 12P 15P'],
|
||||
5: ['1P 5P 8P 12P', '1P 5P 8P 9M 12P', '5P 8P 12P 15P', '5P 8P 12P 15P 16M'],
|
||||
6: ['1P 5P 6M 9M 10M', '1P 5P 9M 10M 13M', '3M 5P 9M 10M 13M', '5P 8P 9M 10M 13M', '3M 6M 9M 12P 15P'],
|
||||
7: [
|
||||
'1P 5P 7m 8P 10M',
|
||||
'1P 7m 8P 10M 12P',
|
||||
'3M 7m 8P 10M 12P',
|
||||
'3M 7m 8P 10M 14m',
|
||||
'3M 7m 10M 12P 15P',
|
||||
'7m 10M 12P 14m 15P',
|
||||
'7m 10M 12P 15P 17M',
|
||||
'7m 10M 14m 17M 19P',
|
||||
],
|
||||
9: [
|
||||
'1P 6M 7m 9M 10M',
|
||||
'3M 7m 9M 10M 12P',
|
||||
'1P 7m 9M 10M 13M',
|
||||
'3M 7m 9M 10M 13M',
|
||||
'3M 7m 9M 12P 15P',
|
||||
'7m 10M 12P 13M 16M',
|
||||
'7m 10M 13M 16M 17M',
|
||||
'7m 10M 13M 16M 19P',
|
||||
],
|
||||
11: [
|
||||
'1P 4P 6M 7m 9M',
|
||||
'1P 5P 7m 9M 11P',
|
||||
'4P 6M 7m 9M 11P',
|
||||
'5P 8P 9M 11P 14m',
|
||||
'7m 9M 11P 13M 15P',
|
||||
'7m 11P 12P 14m 18P',
|
||||
],
|
||||
13: [
|
||||
'3M 7m 9M 10M 13M',
|
||||
'3M 7m 9M 13M 15P',
|
||||
'3M 7m 10M 13M 16M',
|
||||
'7m 10M 12P 13M 16M',
|
||||
'7m 10M 13M 16M 17M',
|
||||
'7m 10M 13M 16M 19P',
|
||||
],
|
||||
69: ['1P 5P 6M 9M 10M', '1P 5P 9M 10M 13M', '3M 5P 9M 10M 13M', '5P 8P 9M 10M 13M', '3M 6M 9M 12P 15P'],
|
||||
add9: [
|
||||
'1P 5P 8P 9M 10M',
|
||||
'1P 5P 9M 10M 12P',
|
||||
'3M 8P 9M 10M 12P',
|
||||
'3M 8P 9M 12P 15P',
|
||||
'5P 8P 9M 10M 15P',
|
||||
'5P 8P 9M 12P 17M',
|
||||
],
|
||||
'+': [
|
||||
'1P 6m 8P 9M 10M',
|
||||
'1P 6m 8P 10M 13m',
|
||||
'3M 8P 9M 10M 13m',
|
||||
'3M 8P 10M 13m 15P',
|
||||
'6m 10M 13m 15P 16M',
|
||||
'6m 10M 13m 15P 17M',
|
||||
],
|
||||
o: [
|
||||
'1P 6M 8P 10m 12d',
|
||||
'1P 6M 10m 12d 13M',
|
||||
'3m 8P 10m 12d 13M',
|
||||
'3m 8P 12d 13M 15P',
|
||||
'5d 10m 12d 13M 15P',
|
||||
'5d 10m 13M 15P 17m',
|
||||
'6M 12d 13M 15P 17m',
|
||||
'6M 12d 15P 17m 19d',
|
||||
],
|
||||
h: [
|
||||
'1P 5d 7m 10m 11P',
|
||||
'3m 5d 7m 8P 11P',
|
||||
'5d 7m 8P 10m 11P',
|
||||
'1P 7m 10m 12d',
|
||||
'3m 7m 8P 12d 14m',
|
||||
'5d 8P 10m 11P 14m',
|
||||
'7m 10m 11P 12d 14m',
|
||||
'7m 10m 12d 14m 15P',
|
||||
'5d 8P 10m 14m 17m',
|
||||
],
|
||||
sus: [
|
||||
'1P 4P 5P 8P 9M',
|
||||
'1P 4P 5P 8P 11P',
|
||||
'1P 5P 8P 9M 11P',
|
||||
'5P 8P 9M 11P 12P',
|
||||
'5P 8P 11P 12P 13M',
|
||||
'5P 8P 11P 13M 15P',
|
||||
],
|
||||
'^': [
|
||||
'1P 3M 5P 6M 9M',
|
||||
'1P 5P 8P 10M 12P',
|
||||
'3M 5P 9M 10M 12P',
|
||||
'1P 5P 8P 10M 13M',
|
||||
'3M 8P 10M 13M 15P',
|
||||
'5P 9M 10M 12P 15P',
|
||||
],
|
||||
'-': [
|
||||
'1P 3m 5P 8P 10m',
|
||||
'1P 3m 5P 9M 11P',
|
||||
'3m 5P 8P 9M 11P',
|
||||
'5P 8P 9M 10m 11P',
|
||||
'1P 5P 9M 10m 12P',
|
||||
'3m 5P 8P 10m 12P',
|
||||
'5P 8P 10m 12P 15P',
|
||||
],
|
||||
'^7': [
|
||||
'1P 6M 7M 9M 10M',
|
||||
'3M 7M 9M 10M 12P',
|
||||
'1P 7M 9M 10M 13M',
|
||||
'3M 7M 9M 10M 13M',
|
||||
'3M 7M 9M 12P 13M',
|
||||
'3M 7M 9M 13M 14M',
|
||||
'3M 7M 10M 13M 16M',
|
||||
'7M 10M 13M 14M 16M',
|
||||
'7M 10M 13M 16M 17M',
|
||||
'7M 10M 13M 16M 19P',
|
||||
],
|
||||
'-7': [
|
||||
'1P 3m 5P 7m 9M',
|
||||
'1P 3m 5P 7m 10m',
|
||||
'1P 5P 7m 10m 11P',
|
||||
'3m 7m 8P 10m 11P',
|
||||
'1P 5P 7m 10m 12P',
|
||||
'3m 7m 9M 10m 12P',
|
||||
'3m 7m 8P 10m 14m',
|
||||
'5P 7m 9M 10m 14m',
|
||||
'7m 10m 11P 14m 15P',
|
||||
'7m 10m 12P 15P 16M',
|
||||
'5P 8P 11P 14m 17m',
|
||||
'7m 10m 12P 15P 17m',
|
||||
],
|
||||
'7sus': [
|
||||
'1P 4P 6M 7m 9M',
|
||||
'1P 5P 7m 9M 11P',
|
||||
'4P 6M 7m 9M 11P',
|
||||
'5P 8P 9M 11P 14m',
|
||||
'7m 9M 11P 13M 15P',
|
||||
'7m 11P 12P 14m 18P',
|
||||
],
|
||||
h7: [
|
||||
'1P 5d 7m 10m 11P',
|
||||
'3m 5d 7m 8P 11P',
|
||||
'5d 7m 8P 10m 11P',
|
||||
'1P 7m 10m 12d',
|
||||
'3m 7m 8P 10m 12d',
|
||||
'3m 7m 8P 12d 14m',
|
||||
'5d 8P 10m 11P 14m',
|
||||
'7m 10m 11P 12d 14m',
|
||||
'7m 10m 12d 14m 15P',
|
||||
'5d 8P 10m 14m 17m',
|
||||
],
|
||||
o7: [
|
||||
'1P 6M 8P 10m 12d',
|
||||
'1P 6M 10m 12d 13M',
|
||||
'3m 8P 10m 12d 13M',
|
||||
'3m 8P 12d 13M 15P',
|
||||
'5d 10m 12d 13M 15P',
|
||||
'5d 10m 13M 15P 17m',
|
||||
'6M 12d 13M 15P 17m',
|
||||
'6M 12d 15P 17m 19d',
|
||||
],
|
||||
'^9': [
|
||||
'1P 6M 7M 9M 10M',
|
||||
'1P 7M 9M 10M 13M',
|
||||
'3M 7M 9M 10M 13M',
|
||||
'3M 7M 9M 12P 13M',
|
||||
'3M 7M 8P 9M 13M',
|
||||
'3M 7M 9M 13M 14M',
|
||||
'3M 7M 10M 13M 16M',
|
||||
'7M 10M 13M 14M 16M',
|
||||
'7M 10M 13M 16M 17M',
|
||||
'7M 10M 13M 16M 19P',
|
||||
],
|
||||
'^13': [
|
||||
'1P 6M 7M 9M 10M',
|
||||
'1P 7M 9M 10M 13M',
|
||||
'3M 7M 9M 12P 13M',
|
||||
'3M 7M 9M 10M 13M',
|
||||
'3M 7M 8P 9M 13M',
|
||||
'3M 7M 9M 13M 14M',
|
||||
'3M 7M 10M 13M 16M',
|
||||
'7M 10M 13M 14M 16M',
|
||||
'7M 10M 13M 16M 17M',
|
||||
'7M 10M 13M 16M 19P',
|
||||
],
|
||||
'^7#11': [
|
||||
'1P 3M 5d 7M 9M',
|
||||
'1P 7M 9M 10M 12d',
|
||||
'3M 7M 9M 10M 12d',
|
||||
'3M 7M 9M 12d 13M',
|
||||
'3M 7M 10M 12d 14M',
|
||||
'7M 10M 12d 13M 14M',
|
||||
'7M 10M 12d 13M 16M',
|
||||
'7M 10M 12d 14M 17M',
|
||||
],
|
||||
'^9#11': [
|
||||
'1P 3M 5d 7M 9M',
|
||||
'1P 7M 9M 10M 12d',
|
||||
'3M 7M 9M 10M 12d',
|
||||
'3M 7M 9M 12d 13M',
|
||||
'3M 7M 9M 12d 14M',
|
||||
'7M 10M 12d 14M 16M',
|
||||
'7M 10M 12d 13M 16M',
|
||||
],
|
||||
'^7#5': ['1P 6m 7M 10M 13m', '3M 7M 9M 10M 13m', '3M 7M 10M 13m 14M', '7M 10M 13m 14M 16M', '7M 10M 13m 14M 17M'],
|
||||
'-6': [
|
||||
'1P 3m 5P 6M 9M',
|
||||
'3m 5P 6M 8P 9M',
|
||||
'1P 5P 6M 10m 11P',
|
||||
'3m 5P 6M 8P 11P',
|
||||
'1P 5P 9M 10m 13M',
|
||||
'3m 5P 8P 9M 13M',
|
||||
'5P 8P 10m 11P 13M',
|
||||
'5P 8P 10m 13M 16M',
|
||||
],
|
||||
'-69': [
|
||||
'1P 3m 5P 6M 9M',
|
||||
'3m 5P 6M 8P 9M',
|
||||
'3m 6M 9M 10m 12P',
|
||||
'1P 5P 9M 10m 13M',
|
||||
'3m 5P 8P 9M 13M',
|
||||
'5P 8P 9M 10m 13M',
|
||||
'5P 8P 10m 13M 16M',
|
||||
],
|
||||
'-^7': [
|
||||
'1P 3m 5P 7M 9M',
|
||||
'1P 5P 7M 10m 11P',
|
||||
'3m 7M 9M 10m 11P',
|
||||
'3m 7M 9M 10m 12P',
|
||||
'3m 7M 9M 12P 14M',
|
||||
'7M 10m 11P 12P 14M',
|
||||
'7M 10m 12P 14M 16M',
|
||||
],
|
||||
'-^9': [
|
||||
'1P 3m 5P 7M 9M',
|
||||
'1P 5P 7M 10m 11P',
|
||||
'3m 7M 9M 10m 11P',
|
||||
'3m 7M 9M 10m 12P',
|
||||
'3m 7M 9M 12P 14M',
|
||||
'7M 10m 11P 12P 14M',
|
||||
'7M 10m 12P 14M 16M',
|
||||
],
|
||||
'-9': [
|
||||
'1P 3m 5P 7m 9M',
|
||||
'1P 3m 7m 9M 11P',
|
||||
'3m 7m 9M 10m 11P',
|
||||
'3m 7m 9M 10m 12P',
|
||||
'3m 7m 9M 10m 14m',
|
||||
'3m 7m 9M 12P 15P',
|
||||
'7m 10m 11P 14m 16M',
|
||||
'7m 10m 12P 16M 18P',
|
||||
],
|
||||
'-add9': ['1P 2M 3m 5P 8P', '1P 3m 5P 9M', '3m 5P 8P 9M 12P', '5P 8P 9M 10m 12P'],
|
||||
'-11': [
|
||||
'3m 5P 7m 9M 11P',
|
||||
'7m 9M 10m 11P',
|
||||
'1P 4P 7m 10m 12P',
|
||||
'3m 7m 9M 11P 12P',
|
||||
'7m 9M 10m 11P 12P',
|
||||
'3m 7m 9M 11P 14m',
|
||||
'4P 10m 12P 14m',
|
||||
'5P 8P 11P 14m',
|
||||
'5P 8P 11P 14m 16M',
|
||||
'7m 10m 12P 16M 18P',
|
||||
'7m 10m 11P 16M 21m',
|
||||
],
|
||||
'-7b5': [
|
||||
'1P 5d 7m 10m 11P',
|
||||
'3m 5d 7m 8P 11P',
|
||||
'5d 7m 8P 10m 11P',
|
||||
'1P 7m 10m 12d',
|
||||
'3m 7m 8P 10m 12d',
|
||||
'3m 7m 8P 12d 14m',
|
||||
'5d 8P 10m 11P 14m',
|
||||
'7m 10m 11P 12d 14m',
|
||||
'7m 10m 12d 14m 15P',
|
||||
'5d 8P 10m 14m 17m',
|
||||
],
|
||||
h9: [
|
||||
'3m 5d 7m 9M 11P',
|
||||
'1P 7m 9M 10m 12d',
|
||||
'3m 7m 9M 12d 14m',
|
||||
'5d 8P 9M 10m 14m',
|
||||
'7m 10m 11P 12d 14m',
|
||||
'7m 10m 12d 14m 16M',
|
||||
],
|
||||
'-b6': ['1P 3m 5P 6m 8P', '3m 5P 8P 11P 13m', '5P 8P 10m 11P 13m'],
|
||||
'-#5': ['1P 6m 8P 10m 13m', '3m 6m 8P 11P 13m', '6m 8P 10m 13m 15P'],
|
||||
'7b9': ['1P 3M 7m 9m 10M', '3M 7m 8P 9m 10M', '3M 7m 8P 9m 14m', '7m 9m 10M 14m 15P'],
|
||||
'7#9': ['1P 3M 7m 10m', '3M 7m 10m 10M 12P', '3M 7m 10m 12P 14m', '7m 10M 12P 14m 17m'],
|
||||
'7#11': ['1P 3M 7m 9M 12d', '3M 7m 9M 12d 13M', '7m 10M 12d 13M 16M'],
|
||||
'7b5': ['1P 3M 7m 9M 12d', '3M 7m 9M 12d 13M', '7m 10M 12d 13M 16M'],
|
||||
'7#5': ['1P 3M 7m 10M 13m', '3M 7m 8P 10M 13m', '3M 7m 8P 13m 14m', '7m 10M 13m 14m 15P', '7m 10M 13m 14m 17M'],
|
||||
'9#11': ['1P 7m 9M 10M 12d', '3M 7m 8P 9M 12d', '7m 10M 12d 15P 16M'],
|
||||
'9b5': ['1P 7m 9M 10M 12d', '3M 7m 8P 9M 12d', '7m 10M 12d 15P 16M'],
|
||||
'9#5': ['1P 7m 9M 10M 13m', '3M 7m 9M 10M 13m', '3M 7m 9M 13m 14m', '7m 10M 13m 14m 16M', '7m 10M 13m 16M 17M'],
|
||||
'7b13': ['1P 3M 7m 10M 13m', '3M 7m 8P 10M 13m', '3M 7m 8P 13m 14m', '7m 10M 13m 14m 15P', '7m 10M 13m 14m 17M'],
|
||||
'7#9#5': ['3M 7m 10m 10M 13m', '3M 7m 10m 13m 14m', '7m 10M 13m 14m 17m'],
|
||||
'7#9b5': ['3M 7m 10m 10M 12d', '3M 7m 10m 12d 14m', '7m 10M 12d 14m 17m'],
|
||||
'7#9#11': ['3M 7m 10m 10M 12d', '3M 7m 10m 12d 14m', '7m 10M 12d 14m 17m'],
|
||||
'7b9#11': ['3M 7m 9m 10M 12d', '3M 7m 9m 12d 14m', '7m 8P 10M 12d 16m', '7m 10M 12d 14m 16m'],
|
||||
'7b9b5': ['3M 7m 9m 10M 12d', '3M 7m 9m 12d 14m', '7m 8P 10M 12d 16m', '7m 10M 12d 14m 16m'],
|
||||
'7b9#5': ['1P 7m 9m 10M 13m', '3M 7m 9m 10M 13m', '3M 7m 10M 13m 16m', '7m 10M 13m 14m 16m', '7m 10M 13m 16m 17M'],
|
||||
'7b9#9': ['1P 3M 7m 9m 10m', '3M 7m 10m 13m 16m', '7m 10M 13m 16m 17m'],
|
||||
'7b9b13': ['1P 7m 9m 10M 13m', '3M 7m 9m 10M 13m', '3M 7m 10M 13m 16m', '7m 10M 13m 14m 16m', '7m 10M 13m 16m 17M'],
|
||||
'7alt': [
|
||||
'3M 7m 8P 10m 13m',
|
||||
'3M 7m 9m 12d 13m',
|
||||
'3M 7m 9m 10m 13m',
|
||||
'3M 7m 10m 13m 14m',
|
||||
'3M 7m 9m 12d 14m',
|
||||
'3M 7m 10m 13m 15P',
|
||||
'3M 7m 10m 13m 16m',
|
||||
'7m 10M 12d 14m 16m',
|
||||
'7m 10M 12d 13m 16m',
|
||||
'7m 10M 13m 15P 17m',
|
||||
'7m 10M 13m 16m 17m',
|
||||
'7m 10M 13m 16m 19d',
|
||||
],
|
||||
'13#11': ['3M 7m 9M 12d 13M', '7m 10M 12d 13M 16M'],
|
||||
'13b9': ['3M 7m 9m 10M 13M', '3M 7m 10M 13M 16m', '7m 10M 13M 16m 17M'],
|
||||
'13#9': ['3M 7m 10m 10M 13M', '7m 10M 13M 14m 17m'],
|
||||
'7b9sus': ['1P 5P 7m 9m 11P', '5P 7m 8P 9m 11P', '7m 8P 11P 14m 16m'],
|
||||
'7susadd3': ['1P 4P 5P 7m 10M', '5P 8P 10M 11P 14m', '7m 11P 12P 15P 17M'],
|
||||
'9sus': [
|
||||
'1P 4P 6M 7m 9M',
|
||||
'1P 5P 7m 9M 11P',
|
||||
'4P 6M 7m 9M 11P',
|
||||
'5P 8P 9M 11P 14m',
|
||||
'7m 9M 11P 13M 15P',
|
||||
'7m 11P 12P 14m 18P',
|
||||
],
|
||||
'13sus': [
|
||||
'1P 4P 6M 7m 9M',
|
||||
'1P 7m 9M 11P 13M',
|
||||
'4P 7m 9M 11P 13M',
|
||||
'7m 9M 11P 13M 15P',
|
||||
'7m 11P 13M 14m 16M',
|
||||
'7m 11P 13M 16M 18P',
|
||||
],
|
||||
'7b13sus': ['1P 5P 7m 11P 13m', '5P 7m 8P 11P 13m', '7m 11P 13m 14m 15P'],
|
||||
};
|
||||
@@ -38,6 +38,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^4.3.3",
|
||||
"vitest": "^0.33.0"
|
||||
"vitest": "^0.28.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
tonleiter.test.mjs - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/tonal/test/tonleiter.test.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 { describe, test, expect } from 'vitest';
|
||||
import {
|
||||
Step,
|
||||
Note,
|
||||
transpose,
|
||||
pc2chroma,
|
||||
rotateChroma,
|
||||
chroma2pc,
|
||||
tokenizeChord,
|
||||
note2pc,
|
||||
note2oct,
|
||||
midi2note,
|
||||
renderVoicing,
|
||||
scaleStep,
|
||||
} from '../tonleiter.mjs';
|
||||
|
||||
describe('tonleiter', () => {
|
||||
test('Step ', () => {
|
||||
expect(Step.tokenize('#11')).toEqual(['#', 11]);
|
||||
expect(Step.tokenize('b13')).toEqual(['b', 13]);
|
||||
expect(Step.tokenize('bb6')).toEqual(['bb', 6]);
|
||||
expect(Step.tokenize('b3')).toEqual(['b', 3]);
|
||||
expect(Step.tokenize('3')).toEqual(['', 3]);
|
||||
expect(Step.tokenize('10')).toEqual(['', 10]);
|
||||
// expect(Step.tokenize('asdasd')).toThrow();
|
||||
expect(Step.accidentals('b3')).toEqual(-1);
|
||||
expect(Step.accidentals('#11')).toEqual(1);
|
||||
});
|
||||
test('Note', () => {
|
||||
expect(Note.tokenize('C##')).toEqual(['C', '##']);
|
||||
expect(Note.tokenize('Bb')).toEqual(['B', 'b']);
|
||||
expect(Note.accidentals('C#')).toEqual(1);
|
||||
expect(Note.accidentals('C##')).toEqual(2);
|
||||
expect(Note.accidentals('Eb')).toEqual(-1);
|
||||
expect(Note.accidentals('Bbb')).toEqual(-2);
|
||||
});
|
||||
test('transpose', () => {
|
||||
expect(transpose('F#', '3')).toEqual('A#');
|
||||
expect(transpose('C', '3')).toEqual('E');
|
||||
expect(transpose('D', '3')).toEqual('F#');
|
||||
expect(transpose('E', '3')).toEqual('G#');
|
||||
expect(transpose('Eb', '3')).toEqual('G');
|
||||
expect(transpose('Ebb', '3')).toEqual('Gb');
|
||||
});
|
||||
test('pc2chroma', () => {
|
||||
expect(pc2chroma('C')).toBe(0);
|
||||
expect(pc2chroma('C#')).toBe(1);
|
||||
expect(pc2chroma('C##')).toBe(2);
|
||||
expect(pc2chroma('D')).toBe(2);
|
||||
expect(pc2chroma('Db')).toBe(1);
|
||||
expect(pc2chroma('Dbb')).toBe(0);
|
||||
expect(pc2chroma('bb')).toBe(10);
|
||||
expect(pc2chroma('f')).toBe(5);
|
||||
expect(pc2chroma('c')).toBe(0);
|
||||
});
|
||||
test('rotateChroma', () => {
|
||||
expect(rotateChroma(0, 1)).toBe(1);
|
||||
expect(rotateChroma(0, -1)).toBe(11);
|
||||
expect(rotateChroma(11, 1)).toBe(0);
|
||||
expect(rotateChroma(11, 13)).toBe(0);
|
||||
});
|
||||
test('chroma2pc', () => {
|
||||
expect(chroma2pc(0)).toBe('C');
|
||||
expect(chroma2pc(1)).toBe('Db');
|
||||
expect(chroma2pc(1, true)).toBe('C#');
|
||||
expect(chroma2pc(2)).toBe('D');
|
||||
expect(chroma2pc(3)).toBe('Eb');
|
||||
});
|
||||
test('tokenizeChord', () => {
|
||||
expect(tokenizeChord('Cm7')).toEqual(['C', 'm7', undefined]);
|
||||
expect(tokenizeChord('C#m7')).toEqual(['C#', 'm7', undefined]);
|
||||
expect(tokenizeChord('Bb^7')).toEqual(['Bb', '^7', undefined]);
|
||||
expect(tokenizeChord('Bb^7/F')).toEqual(['Bb', '^7', 'F']);
|
||||
});
|
||||
test('note2pc', () => {
|
||||
expect(note2pc('C5')).toBe('C');
|
||||
expect(note2pc('C52')).toBe('C');
|
||||
expect(note2pc('Bb3')).toBe('Bb');
|
||||
expect(note2pc('F')).toBe('F');
|
||||
});
|
||||
test('note2oct', () => {
|
||||
expect(note2oct('C5')).toBe(5);
|
||||
expect(note2oct('Bb3')).toBe(3);
|
||||
expect(note2oct('C7')).toBe(7);
|
||||
expect(note2oct('C10')).toBe(10);
|
||||
});
|
||||
test('midi2note', () => {
|
||||
expect(midi2note(60)).toBe('C4');
|
||||
expect(midi2note(61)).toBe('Db4');
|
||||
expect(midi2note(61, true)).toBe('C#4');
|
||||
});
|
||||
test('scaleStep', () => {
|
||||
expect(scaleStep([60, 63, 67], 0)).toBe(60);
|
||||
expect(scaleStep([60, 63, 67], 1)).toBe(63);
|
||||
expect(scaleStep([60, 63, 67], 2)).toBe(67);
|
||||
expect(scaleStep([60, 63, 67], 3)).toBe(72);
|
||||
expect(scaleStep([60, 63, 67], 4)).toBe(75);
|
||||
expect(scaleStep([60, 63, 67], -1)).toBe(55);
|
||||
expect(scaleStep([60, 63, 67], -2)).toBe(51);
|
||||
expect(scaleStep([60, 63, 67], -3)).toBe(48);
|
||||
expect(scaleStep([60, 63, 67], -4)).toBe(43);
|
||||
});
|
||||
test('renderVoicing', () => {
|
||||
const dictionary = {
|
||||
m7: [
|
||||
'3 7 10 14', // b3 5 b7 9
|
||||
'10 14 15 19', // b7 9 b3 5
|
||||
],
|
||||
};
|
||||
expect(renderVoicing({ chord: 'Em7', anchor: 'Bb4', dictionary, mode: 'below' })).toEqual([
|
||||
'G3',
|
||||
'B3',
|
||||
'D4',
|
||||
'Gb4',
|
||||
]);
|
||||
expect(renderVoicing({ chord: 'Cm7', anchor: 'D5', dictionary, mode: 'below' })).toEqual([
|
||||
'Eb4',
|
||||
'G4',
|
||||
'Bb4',
|
||||
'D5',
|
||||
]);
|
||||
expect(renderVoicing({ chord: 'Cm7', anchor: 'G5', dictionary, mode: 'below' })).toEqual([
|
||||
'Bb4',
|
||||
'D5',
|
||||
'Eb5',
|
||||
'G5',
|
||||
]);
|
||||
expect(renderVoicing({ chord: 'Cm7', anchor: 'g5', dictionary, mode: 'below' })).toEqual([
|
||||
'Bb4',
|
||||
'D5',
|
||||
'Eb5',
|
||||
'G5',
|
||||
]);
|
||||
expect(renderVoicing({ chord: 'Cm7', anchor: 'g5', dictionary, mode: 'below', n: 0 })).toEqual([70]); // Bb4
|
||||
expect(renderVoicing({ chord: 'Cm7', anchor: 'g5', dictionary, mode: 'below', n: 1 })).toEqual([74]); // D5
|
||||
expect(renderVoicing({ chord: 'Cm7', anchor: 'g5', dictionary, mode: 'below', n: 4 })).toEqual([82]); // Bb5
|
||||
expect(renderVoicing({ chord: 'Cm7', anchor: 'g5', dictionary, mode: 'below', offset: 1 })).toEqual([
|
||||
'Eb5',
|
||||
'G5',
|
||||
'Bb5',
|
||||
'D6',
|
||||
]);
|
||||
// expect(voiceBelow('G4', 'Cm7', voicingDictionary)).toEqual(['Bb3', 'D4', 'Eb4', 'G4']);
|
||||
// TODO: test with offset
|
||||
});
|
||||
});
|
||||
@@ -127,18 +127,18 @@ export const scaleTranspose = register('scaleTranspose', function (offset /* : n
|
||||
*
|
||||
* The root note defaults to octave 3, if no octave number is given.
|
||||
*
|
||||
* @memberof Pattern
|
||||
* @name scale
|
||||
* @param {string} scale Name of scale
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* n("0 2 4 6 4 2").scale("C:major")
|
||||
* "0 2 4 6 4 2".scale("C2:major").note()
|
||||
* @example
|
||||
* n("[0,7] 4 [2,7] 4")
|
||||
* .scale("C:<major minor>/2")
|
||||
* .s("piano")
|
||||
* "0 2 4 6 4 2"
|
||||
* .scale("C2:<major minor>")
|
||||
* .note()
|
||||
* @example
|
||||
* n(rand.range(0,12).segment(8).round())
|
||||
* .scale("C:ritusen")
|
||||
* "0 1 2 3 4 5 6 7".rev().scale("C2:<major minor>").note()
|
||||
* .s("folkharp")
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
import { isNote, isNoteWithOctave, _mod, noteToMidi, tokenizeNote } from '@strudel.cycles/core';
|
||||
import { Interval } from '@tonaljs/tonal';
|
||||
|
||||
// https://codesandbox.io/s/stateless-voicings-g2tmz0?file=/src/lib.js:0-2515
|
||||
|
||||
const flats = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'];
|
||||
const pcs = ['c', 'db', 'd', 'eb', 'e', 'f', 'gb', 'g', 'ab', 'a', 'bb', 'b'];
|
||||
const sharps = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
|
||||
const accs = { b: -1, '#': 1 };
|
||||
|
||||
export const pc2chroma = (pc) => {
|
||||
const [letter, ...rest] = pc.split('');
|
||||
return pcs.indexOf(letter.toLowerCase()) + rest.reduce((sum, sign) => sum + accs[sign], 0);
|
||||
};
|
||||
|
||||
export const rotateChroma = (chroma, steps) => (chroma + (steps % 12) + 12) % 12;
|
||||
|
||||
export const chroma2pc = (chroma, sharp = false) => {
|
||||
return (sharp ? sharps : flats)[chroma];
|
||||
};
|
||||
|
||||
export function tokenizeChord(chord) {
|
||||
const match = (chord || '').match(/^([A-G][b#]*)([^/]*)[/]?([A-G][b#]*)?$/);
|
||||
if (!match) {
|
||||
// console.warn('could not tokenize chord', chord);
|
||||
return [];
|
||||
}
|
||||
return match.slice(1);
|
||||
}
|
||||
export const note2pc = (note) => note.match(/^[A-G][#b]?/i)[0];
|
||||
export const note2oct = (note) => tokenizeNote(note)[2];
|
||||
|
||||
export const note2chroma = (note) => {
|
||||
return pc2chroma(note2pc(note));
|
||||
};
|
||||
|
||||
// TODO: test
|
||||
export const midi2chroma = (midi) => midi % 12;
|
||||
|
||||
// TODO: test and use in voicing function
|
||||
export const pitch2chroma = (x, defaultOctave) => {
|
||||
if (isNoteWithOctave(x)) {
|
||||
return note2chroma(x);
|
||||
}
|
||||
if (isNote(x)) {
|
||||
//pc
|
||||
return pc2chroma(x, defaultOctave);
|
||||
}
|
||||
if (typeof x === 'number') {
|
||||
// expect midi
|
||||
return midi2chroma(x);
|
||||
}
|
||||
};
|
||||
|
||||
export const step2semitones = (x) => {
|
||||
let num = Number(x);
|
||||
if (!isNaN(num)) {
|
||||
return num;
|
||||
}
|
||||
return Interval.semitones(x);
|
||||
};
|
||||
|
||||
export const x2midi = (x) => {
|
||||
if (typeof x === 'number') {
|
||||
return x;
|
||||
}
|
||||
if (typeof x === 'string') {
|
||||
return noteToMidi(x);
|
||||
}
|
||||
};
|
||||
|
||||
// duplicate: util.mjs (does not support sharp flag)
|
||||
export const midi2note = (midi, sharp = false) => {
|
||||
const oct = Math.floor(midi / 12) - 1;
|
||||
const pc = (sharp ? sharps : flats)[midi % 12];
|
||||
return pc + oct;
|
||||
};
|
||||
|
||||
export function scaleStep(notes, offset, octaves = 1) {
|
||||
notes = notes.map((note) => (typeof note === 'string' ? noteToMidi(note) : note));
|
||||
const octOffset = Math.floor(offset / notes.length) * octaves * 12;
|
||||
offset = _mod(offset, notes.length);
|
||||
return notes[offset] + octOffset;
|
||||
}
|
||||
|
||||
// different ways to resolve the note to compare the anchor to (see renderVoicing)
|
||||
let modeTarget = {
|
||||
below: (v) => v.slice(-1)[0],
|
||||
duck: (v) => v.slice(-1)[0],
|
||||
above: (v) => v[0],
|
||||
};
|
||||
|
||||
export function renderVoicing({ chord, dictionary, offset = 0, n, mode = 'below', anchor = 'c5', octaves = 1 }) {
|
||||
const [root, symbol] = tokenizeChord(chord);
|
||||
const rootChroma = pc2chroma(root);
|
||||
anchor = anchor?.note || anchor;
|
||||
const anchorChroma = pitch2chroma(anchor);
|
||||
const voicings = dictionary[symbol].map((voicing) =>
|
||||
(typeof voicing === 'string' ? voicing.split(' ') : voicing).map(step2semitones),
|
||||
);
|
||||
|
||||
let minDistance, bestIndex;
|
||||
// calculate distances up from voicing top notes
|
||||
let chromaDiffs = voicings.map((v, i) => {
|
||||
const targetStep = modeTarget[mode](v);
|
||||
const diff = _mod(anchorChroma - targetStep - rootChroma, 12);
|
||||
if (minDistance === undefined || diff < minDistance) {
|
||||
minDistance = diff;
|
||||
bestIndex = i;
|
||||
}
|
||||
return diff;
|
||||
});
|
||||
|
||||
const octDiff = Math.ceil(offset / voicings.length) * 12;
|
||||
const indexWithOffset = _mod(bestIndex + offset, voicings.length);
|
||||
const voicing = voicings[indexWithOffset];
|
||||
const targetStep = modeTarget[mode](voicing);
|
||||
const anchorMidi = noteToMidi(anchor, 4) - chromaDiffs[indexWithOffset] + octDiff;
|
||||
|
||||
const voicingMidi = voicing.map((v) => anchorMidi - targetStep + v);
|
||||
let notes = voicingMidi.map((n) => midi2note(n));
|
||||
|
||||
if (mode === 'duck') {
|
||||
notes = notes.filter((_, i) => voicingMidi[i] !== noteToMidi(anchor));
|
||||
}
|
||||
if (n !== undefined) {
|
||||
return [scaleStep(notes, n, octaves)];
|
||||
}
|
||||
return notes;
|
||||
}
|
||||
|
||||
// https://github.com/tidalcycles/strudel/blob/14184993d0ee7d69c47df57ac864a1a0f99a893f/packages/tonal/tonleiter.mjs
|
||||
const steps = [1, 0, 2, 0, 3, 4, 0, 5, 0, 6, 0, 7];
|
||||
const notes = ['C', '', 'D', '', 'E', 'F', '', 'G', '', 'A', '', 'B'];
|
||||
const noteLetters = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];
|
||||
|
||||
export const accidentalOffset = (accidentals) => {
|
||||
return accidentals.split('#').length - accidentals.split('b').length;
|
||||
};
|
||||
|
||||
const accidentalString = (offset) => {
|
||||
if (offset < 0) {
|
||||
return 'b'.repeat(-offset);
|
||||
}
|
||||
if (offset > 0) {
|
||||
return '#'.repeat(offset);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
export const Step = {
|
||||
tokenize(step) {
|
||||
const matches = step.match(/^([#b]*)([1-9][0-9]*)$/);
|
||||
if (!matches) {
|
||||
throw new Error(`Step.tokenize: not a valid step: ${step}`);
|
||||
}
|
||||
const [accidentals, stepNumber] = matches.slice(1);
|
||||
return [accidentals, parseInt(stepNumber)];
|
||||
},
|
||||
accidentals(step) {
|
||||
return accidentalOffset(Step.tokenize(step)[0]);
|
||||
},
|
||||
};
|
||||
|
||||
export const Note = {
|
||||
// TODO: support octave numbers
|
||||
tokenize(note) {
|
||||
return [note[0], note.slice(1)];
|
||||
},
|
||||
accidentals(note) {
|
||||
return accidentalOffset(this.tokenize(note)[1]);
|
||||
},
|
||||
};
|
||||
|
||||
// TODO: support octave numbers
|
||||
export function transpose(note, step) {
|
||||
// example: E, 3
|
||||
const stepNumber = Step.tokenize(step)[1]; // 3
|
||||
const noteLetter = Note.tokenize(note)[0]; // E
|
||||
const noteIndex = noteLetters.indexOf(noteLetter); // 2 "E is C+2"
|
||||
const targetNote = noteLetters[(noteIndex + stepNumber - 1) % 8]; // G "G is a third above E"
|
||||
const rootIndex = notes.indexOf(noteLetter); // 4 "E is 4 semitones above C"
|
||||
const targetIndex = notes.indexOf(targetNote); // 7 "G is 7 semitones above C"
|
||||
const indexOffset = targetIndex - rootIndex; // 3 (E to G is normally a 3 semitones)
|
||||
const stepIndex = steps.indexOf(stepNumber); // 4 ("3" is normally 4 semitones)
|
||||
const offsetAccidentals = accidentalString(Step.accidentals(step) + Note.accidentals(note) + stepIndex - indexOffset); // "we need to add a # to to the G to make it a major third from E"
|
||||
return [targetNote, offsetAccidentals].join('');
|
||||
}
|
||||
|
||||
//Note("Bb3").transpose("c3")
|
||||
+5
-105
@@ -5,9 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
*/
|
||||
|
||||
import { stack, register } from '@strudel.cycles/core';
|
||||
import { renderVoicing } from './tonleiter.mjs';
|
||||
import _voicings from 'chord-voicings';
|
||||
import { complex, simple } from './ireal.mjs';
|
||||
const { dictionaryVoicing, minTopNoteDiff } = _voicings.default || _voicings; // parcel module resolution fuckup
|
||||
|
||||
const lefthand = {
|
||||
@@ -51,33 +49,10 @@ const triads = {
|
||||
aug: ['1P 3m 5A', '3m 5A 8P', '5A 8P 10m'],
|
||||
};
|
||||
|
||||
const defaultDictionary = {
|
||||
// triads
|
||||
'': ['1P 3M 5P', '3M 5P 8P', '5P 8P 10M'],
|
||||
M: ['1P 3M 5P', '3M 5P 8P', '5P 8P 10M'],
|
||||
m: ['1P 3m 5P', '3m 5P 8P', '5P 8P 10m'],
|
||||
o: ['1P 3m 5d', '3m 5d 8P', '5d 8P 10m'],
|
||||
aug: ['1P 3m 5A', '3m 5A 8P', '5A 8P 10m'],
|
||||
// sevenths chords
|
||||
m7: ['3m 5P 7m 9M', '7m 9M 10m 12P'],
|
||||
7: ['3M 6M 7m 9M', '7m 9M 10M 13M'],
|
||||
'^7': ['3M 5P 7M 9M', '7M 9M 10M 12P'],
|
||||
69: ['3M 5P 6A 9M'],
|
||||
m7b5: ['3m 5d 7m 8P', '7m 8P 10m 12d'],
|
||||
'7b9': ['3M 6m 7m 9m', '7m 9m 10M 13m'],
|
||||
'7b13': ['3M 6m 7m 9m', '7m 9m 10M 13m'],
|
||||
o7: ['1P 3m 5d 6M', '5d 6M 8P 10m'],
|
||||
'7#11': ['7m 9M 11A 13A'],
|
||||
'7#9': ['3M 7m 9A'],
|
||||
mM7: ['3m 5P 7M 9M', '7M 9M 10m 12P'],
|
||||
m6: ['3m 5P 6M 9M', '6M 9M 10m 12P'],
|
||||
};
|
||||
|
||||
export const voicingRegistry = {
|
||||
lefthand: { dictionary: lefthand, range: ['F3', 'A4'], mode: 'below', anchor: 'a4' },
|
||||
triads: { dictionary: triads, mode: 'below', anchor: 'a4' },
|
||||
guidetones: { dictionary: guidetones, mode: 'above', anchor: 'a4' },
|
||||
default: { dictionary: defaultDictionary, mode: 'below', anchor: 'a4' },
|
||||
lefthand: { dictionary: lefthand, range: ['F3', 'A4'] },
|
||||
triads: { dictionary: triads },
|
||||
guidetones: { dictionary: guidetones },
|
||||
};
|
||||
export const setVoicingRange = (name, range) => addVoicings(name, voicingRegistry[name].dictionary, range);
|
||||
|
||||
@@ -106,11 +81,6 @@ export const addVoicings = (name, dictionary, range = ['F3', 'A4']) => {
|
||||
Object.assign(voicingRegistry, { [name]: { dictionary, range } });
|
||||
};
|
||||
|
||||
// new call signature
|
||||
export const registerVoicings = (name, dictionary, options = {}) => {
|
||||
Object.assign(voicingRegistry, { [name]: { dictionary, ...options } });
|
||||
};
|
||||
|
||||
const getVoicing = (chord, dictionaryName, lastVoicing) => {
|
||||
const { dictionary, range } = voicingRegistry[dictionaryName];
|
||||
return dictionaryVoicing({
|
||||
@@ -123,7 +93,6 @@ const getVoicing = (chord, dictionaryName, lastVoicing) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* DEPRECATED: still works, but it is recommended you use .voicing instead (without s).
|
||||
* Turns chord symbols into voicings, using the smoothest voice leading possible.
|
||||
* Uses [chord-voicings package](https://github.com/felixroos/chord-voicings#chord-voicings).
|
||||
*
|
||||
@@ -160,76 +129,7 @@ export const voicings = register('voicings', function (dictionary, pat) {
|
||||
*/
|
||||
export const rootNotes = register('rootNotes', function (octave, pat) {
|
||||
return pat.fmap((value) => {
|
||||
const chord = value.chord || value;
|
||||
const root = chord.match(/^([a-gA-G][b#]?).*$/)[1];
|
||||
const note = root + octave;
|
||||
return value.chord ? { note } : note;
|
||||
const root = value.match(/^([a-gA-G][b#]?).*$/)[1];
|
||||
return root + octave;
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Turns chord symbols into voicings. You can use the following control params:
|
||||
*
|
||||
* - `chord`: Note, followed by chord symbol, e.g. C Am G7 Bb^7
|
||||
* - `dict`: voicing dictionary to use, falls back to default dictionary
|
||||
* - `anchor`: the note that is used to align the chord
|
||||
* - `mode`: how the voicing is aligned to the anchor
|
||||
* - `below`: top note <= anchor
|
||||
* - `duck`: top note <= anchor, anchor excluded
|
||||
* - `above`: bottom note >= anchor
|
||||
* - `offset`: whole number that shifts the voicing up or down to the next voicing
|
||||
* - `n`: if set, the voicing is played like a scale. Overshooting numbers will be octaved
|
||||
*
|
||||
* All of the above controls are optional, except `chord`.
|
||||
* If you pass a pattern of strings to voicing, they will be interpreted as chords.
|
||||
*
|
||||
* @name voicing
|
||||
* @param {string} dictionary which voicing dictionary to use.
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* voicing("<C Am F G>")
|
||||
* @example
|
||||
* n("0 1 2 3 4 5 6 7").chord("<C Am F G>").voicing()
|
||||
*/
|
||||
export const voicing = register('voicing', function (pat) {
|
||||
return pat
|
||||
.fmap((value) => {
|
||||
// destructure voicing controls out
|
||||
value = typeof value === 'string' ? { chord: value } : value;
|
||||
let { dictionary = 'default', chord, anchor, offset, mode, n, octaves, ...rest } = value;
|
||||
dictionary =
|
||||
typeof dictionary === 'string' ? voicingRegistry[dictionary] : { dictionary, mode: 'below', anchor: 'c5' };
|
||||
let notes = renderVoicing({ ...dictionary, chord, anchor, offset, mode, n, octaves });
|
||||
|
||||
return stack(...notes)
|
||||
.note()
|
||||
.set(rest); // rest does not include voicing controls anymore!
|
||||
})
|
||||
.outerJoin();
|
||||
});
|
||||
|
||||
export function voicingAlias(symbol, alias, setOrSets) {
|
||||
setOrSets = !Array.isArray(setOrSets) ? [setOrSets] : setOrSets;
|
||||
setOrSets.forEach((set) => {
|
||||
set[alias] = set[symbol];
|
||||
});
|
||||
}
|
||||
|
||||
// no symbol = major chord
|
||||
voicingAlias('^', '', [simple, complex]);
|
||||
|
||||
Object.keys(simple).forEach((symbol) => {
|
||||
// add aliases for "-" === "m"
|
||||
if (symbol.includes('-')) {
|
||||
let alias = symbol.replace('-', 'm');
|
||||
voicingAlias(symbol, alias, [complex, simple]);
|
||||
}
|
||||
// add aliases for "^" === "M"
|
||||
if (symbol.includes('^')) {
|
||||
let alias = symbol.replace('^', 'M');
|
||||
voicingAlias(symbol, alias, [complex, simple]);
|
||||
}
|
||||
});
|
||||
|
||||
registerVoicings('ireal', simple);
|
||||
registerVoicings('ireal-ext', complex);
|
||||
|
||||
@@ -38,6 +38,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^4.3.3",
|
||||
"vitest": "^0.33.0"
|
||||
"vitest": "^0.28.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="https://strudel.tidalcycles.org/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@strudel/web REPL Example</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<canvas style="background: #000" width="320" height="320" id="canvas"></canvas><br />
|
||||
<button id="a">A</button>
|
||||
<button id="b">B</button>
|
||||
<button id="c">C</button>
|
||||
<button id="stop">stop</button>
|
||||
<script type="module">
|
||||
import { initStrudel } from '@strudel/web';
|
||||
const canvas = document.getElementById('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
let drawer;
|
||||
let init = initStrudel({
|
||||
prebake: () => samples('github:tidalcycles/Dirt-Samples/master'),
|
||||
onToggle: async (started) => {
|
||||
const { scheduler } = await init;
|
||||
if (started) {
|
||||
drawer.start(scheduler);
|
||||
} else {
|
||||
drawer.stop();
|
||||
}
|
||||
},
|
||||
}).then(({ scheduler }) => {
|
||||
drawer = new Drawer(
|
||||
(haps, time, { drawTime }) => {
|
||||
scheduler.pattern.context?.onPaint?.(ctx, time, haps, drawTime);
|
||||
},
|
||||
[-2, 2],
|
||||
);
|
||||
return { scheduler };
|
||||
});
|
||||
|
||||
const click = (id, action) => document.getElementById(id).addEventListener('click', action);
|
||||
click('a', () => evaluate(`s('bd,jvbass(3,8)').jux(rev).spiral({size:20})`));
|
||||
click('b', () => s('bd*2,hh(3,4),jvbass(5,8,1)').jux(rev).spiral({ size: 20, steady: 0.9 }).play());
|
||||
click('c', () => s('bd*2,hh(3,4),jvbass:[0 4](5,8,1)').jux(rev).stack(s('~ sd')).punchcard().play());
|
||||
click('stop', () => hush());
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -29,7 +29,7 @@ export async function defaultPrebake() {
|
||||
let initDone;
|
||||
|
||||
let scheduler;
|
||||
export function initStrudel(options = {}) {
|
||||
export async function initStrudel(options = {}) {
|
||||
initAudioOnFirstClick();
|
||||
miniAllStrings();
|
||||
const { prebake, ...schedulerOptions } = options;
|
||||
@@ -39,6 +39,8 @@ export function initStrudel(options = {}) {
|
||||
await prebake?.();
|
||||
})();
|
||||
scheduler = webaudioScheduler(schedulerOptions);
|
||||
await initDone;
|
||||
return { scheduler };
|
||||
}
|
||||
|
||||
window.initStrudel = initStrudel;
|
||||
|
||||
@@ -34,6 +34,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^4.3.3",
|
||||
"vitest": "^0.33.0"
|
||||
"vitest": "^0.28.0"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+147
-135
@@ -66,8 +66,8 @@ importers:
|
||||
specifier: ^5.8.1
|
||||
version: 5.9.0
|
||||
vitest:
|
||||
specifier: ^0.33.0
|
||||
version: 0.33.0(@vitest/ui@0.28.0)
|
||||
specifier: ^0.28.0
|
||||
version: 0.28.0(@vitest/ui@0.28.0)
|
||||
|
||||
packages/codemirror:
|
||||
dependencies:
|
||||
@@ -107,8 +107,8 @@ importers:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
vitest:
|
||||
specifier: ^0.33.0
|
||||
version: 0.33.0(@vitest/ui@0.28.0)
|
||||
specifier: ^0.28.0
|
||||
version: 0.28.0(@vitest/ui@0.28.0)
|
||||
|
||||
packages/core/examples/vite-vanilla-repl:
|
||||
dependencies:
|
||||
@@ -207,8 +207,8 @@ importers:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
vitest:
|
||||
specifier: ^0.33.0
|
||||
version: 0.33.0(@vitest/ui@0.28.0)
|
||||
specifier: ^0.28.0
|
||||
version: 0.28.0(@vitest/ui@0.28.0)
|
||||
|
||||
packages/osc:
|
||||
dependencies:
|
||||
@@ -405,8 +405,8 @@ importers:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
vitest:
|
||||
specifier: ^0.33.0
|
||||
version: 0.33.0(@vitest/ui@0.28.0)
|
||||
specifier: ^0.28.0
|
||||
version: 0.28.0(@vitest/ui@0.28.0)
|
||||
|
||||
packages/transpiler:
|
||||
dependencies:
|
||||
@@ -430,8 +430,8 @@ importers:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
vitest:
|
||||
specifier: ^0.33.0
|
||||
version: 0.33.0(@vitest/ui@0.28.0)
|
||||
specifier: ^0.28.0
|
||||
version: 0.28.0(@vitest/ui@0.28.0)
|
||||
|
||||
packages/web:
|
||||
dependencies:
|
||||
@@ -488,8 +488,8 @@ importers:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
vitest:
|
||||
specifier: ^0.33.0
|
||||
version: 0.33.0(@vitest/ui@0.28.0)
|
||||
specifier: ^0.28.0
|
||||
version: 0.28.0(@vitest/ui@0.28.0)
|
||||
|
||||
website:
|
||||
dependencies:
|
||||
@@ -2698,13 +2698,6 @@ packages:
|
||||
'@sinclair/typebox': 0.25.24
|
||||
dev: true
|
||||
|
||||
/@jest/schemas@29.6.0:
|
||||
resolution: {integrity: sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
dependencies:
|
||||
'@sinclair/typebox': 0.27.8
|
||||
dev: true
|
||||
|
||||
/@jridgewell/gen-mapping@0.1.1:
|
||||
resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
@@ -2738,10 +2731,6 @@ packages:
|
||||
/@jridgewell/sourcemap-codec@1.4.14:
|
||||
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
|
||||
|
||||
/@jridgewell/sourcemap-codec@1.4.15:
|
||||
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
|
||||
dev: true
|
||||
|
||||
/@jridgewell/trace-mapping@0.3.17:
|
||||
resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
|
||||
dependencies:
|
||||
@@ -3636,10 +3625,6 @@ packages:
|
||||
resolution: {integrity: sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==}
|
||||
dev: true
|
||||
|
||||
/@sinclair/typebox@0.27.8:
|
||||
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
||||
dev: true
|
||||
|
||||
/@supabase/functions-js@2.1.1:
|
||||
resolution: {integrity: sha512-bIR1Puae6W+1/MzPfYBWOG/SCWGo4B5CB7c0ZZksvliNEAzhxNBJ0UFKYINcGdGtxG8ZC+1xr3utWpNZNwnoRw==}
|
||||
dependencies:
|
||||
@@ -4046,11 +4031,11 @@ packages:
|
||||
/@types/chai-subset@1.3.3:
|
||||
resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==}
|
||||
dependencies:
|
||||
'@types/chai': 4.3.5
|
||||
'@types/chai': 4.3.4
|
||||
dev: true
|
||||
|
||||
/@types/chai@4.3.5:
|
||||
resolution: {integrity: sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==}
|
||||
/@types/chai@4.3.4:
|
||||
resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==}
|
||||
dev: true
|
||||
|
||||
/@types/debug@4.1.7:
|
||||
@@ -4126,6 +4111,10 @@ packages:
|
||||
dependencies:
|
||||
'@types/unist': 2.0.6
|
||||
|
||||
/@types/node@18.11.18:
|
||||
resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==}
|
||||
dev: true
|
||||
|
||||
/@types/node@18.16.3:
|
||||
resolution: {integrity: sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q==}
|
||||
|
||||
@@ -4572,34 +4561,26 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@vitest/expect@0.33.0:
|
||||
resolution: {integrity: sha512-sVNf+Gla3mhTCxNJx+wJLDPp/WcstOe0Ksqz4Vec51MmgMth/ia0MGFEkIZmVGeTL5HtjYR4Wl/ZxBxBXZJTzQ==}
|
||||
/@vitest/expect@0.28.0:
|
||||
resolution: {integrity: sha512-GSk1k9/W8JcVHFhCqVX/MdV9SbVbOV4p7U293f7f9xpNK17c1oA47OErsdL0a5lwJoS82RT4ZieTuE1Q9egeaQ==}
|
||||
dependencies:
|
||||
'@vitest/spy': 0.33.0
|
||||
'@vitest/utils': 0.33.0
|
||||
'@vitest/spy': 0.28.0
|
||||
'@vitest/utils': 0.28.0
|
||||
chai: 4.3.7
|
||||
dev: true
|
||||
|
||||
/@vitest/runner@0.33.0:
|
||||
resolution: {integrity: sha512-UPfACnmCB6HKRHTlcgCoBh6ppl6fDn+J/xR8dTufWiKt/74Y9bHci5CKB8tESSV82zKYtkBJo9whU3mNvfaisg==}
|
||||
/@vitest/runner@0.28.0:
|
||||
resolution: {integrity: sha512-SXQO9aubp7Hg4DV4D5DP70wJ/4o0krH1gAPrSt+rhEZQbQvMaBJAHWOxEibwzLkklgoHreaMEvETFILkGQWXww==}
|
||||
dependencies:
|
||||
'@vitest/utils': 0.33.0
|
||||
'@vitest/utils': 0.28.0
|
||||
p-limit: 4.0.0
|
||||
pathe: 1.1.1
|
||||
pathe: 1.1.0
|
||||
dev: true
|
||||
|
||||
/@vitest/snapshot@0.33.0:
|
||||
resolution: {integrity: sha512-tJjrl//qAHbyHajpFvr8Wsk8DIOODEebTu7pgBrP07iOepR5jYkLFiqLq2Ltxv+r0uptUb4izv1J8XBOwKkVYA==}
|
||||
/@vitest/spy@0.28.0:
|
||||
resolution: {integrity: sha512-gYBDQIP0QDvxrscl2Id0BTbzLUbuAzFiFur3eHxH9Yt5cM6YCH/kxBrSHhmXTbu92UenLx53Gwq17u5N0zGNDQ==}
|
||||
dependencies:
|
||||
magic-string: 0.30.1
|
||||
pathe: 1.1.1
|
||||
pretty-format: 29.6.1
|
||||
dev: true
|
||||
|
||||
/@vitest/spy@0.33.0:
|
||||
resolution: {integrity: sha512-Kv+yZ4hnH1WdiAkPUQTpRxW8kGtH8VRTnus7ZTGovFYM1ZezJpvGtb9nPIjPnptHbsyIAxYZsEpVPYgtpjGnrg==}
|
||||
dependencies:
|
||||
tinyspy: 2.1.1
|
||||
tinyspy: 1.0.2
|
||||
dev: true
|
||||
|
||||
/@vitest/ui@0.28.0:
|
||||
@@ -4612,12 +4593,14 @@ packages:
|
||||
sirv: 2.0.2
|
||||
dev: true
|
||||
|
||||
/@vitest/utils@0.33.0:
|
||||
resolution: {integrity: sha512-pF1w22ic965sv+EN6uoePkAOTkAPWM03Ri/jXNyMIKBb/XHLDPfhLvf/Fa9g0YECevAIz56oVYXhodLvLQ/awA==}
|
||||
/@vitest/utils@0.28.0:
|
||||
resolution: {integrity: sha512-Dt+jDZbwriZWzJ5Hi9nAUnz9IPgNb+ACE96tWiXPp/u9NmCYWIWcuNoUOYS8HQyGFz31GiNYGvaZ4ZEDjAgi1g==}
|
||||
dependencies:
|
||||
diff-sequences: 29.4.3
|
||||
cli-truncate: 3.1.0
|
||||
diff: 5.1.0
|
||||
loupe: 2.3.6
|
||||
pretty-format: 29.6.1
|
||||
picocolors: 1.0.0
|
||||
pretty-format: 27.5.1
|
||||
dev: true
|
||||
|
||||
/@vscode/emmet-helper@2.8.6:
|
||||
@@ -4686,12 +4669,6 @@ packages:
|
||||
engines: {node: '>=0.4.0'}
|
||||
dev: true
|
||||
|
||||
/acorn@8.10.0:
|
||||
resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/acorn@8.8.2:
|
||||
resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
@@ -5537,6 +5514,14 @@ packages:
|
||||
resolution: {integrity: sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
/cli-truncate@3.1.0:
|
||||
resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
dependencies:
|
||||
slice-ansi: 5.0.0
|
||||
string-width: 5.1.2
|
||||
dev: true
|
||||
|
||||
/cli-width@3.0.0:
|
||||
resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==}
|
||||
engines: {node: '>= 10'}
|
||||
@@ -6193,11 +6178,6 @@ packages:
|
||||
/didyoumean@1.2.2:
|
||||
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
||||
|
||||
/diff-sequences@29.4.3:
|
||||
resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
dev: true
|
||||
|
||||
/diff@5.1.0:
|
||||
resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
@@ -8043,6 +8023,11 @@ packages:
|
||||
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
/is-fullwidth-code-point@4.0.0:
|
||||
resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
/is-glob@4.0.3:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -8813,13 +8798,6 @@ packages:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.4.14
|
||||
|
||||
/magic-string@0.30.1:
|
||||
resolution: {integrity: sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
dev: true
|
||||
|
||||
/make-dir@2.1.0:
|
||||
resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -9673,13 +9651,13 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
/mlly@1.4.0:
|
||||
resolution: {integrity: sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==}
|
||||
/mlly@1.2.0:
|
||||
resolution: {integrity: sha512-+c7A3CV0KGdKcylsI6khWyts/CYrGTrRVo4R/I7u/cUsy0Conxa6LUhiEzVKIw14lc2L5aiO4+SeVe4TeGRKww==}
|
||||
dependencies:
|
||||
acorn: 8.10.0
|
||||
pathe: 1.1.1
|
||||
pkg-types: 1.0.3
|
||||
ufo: 1.1.2
|
||||
acorn: 8.8.2
|
||||
pathe: 1.1.0
|
||||
pkg-types: 1.0.2
|
||||
ufo: 1.1.1
|
||||
dev: true
|
||||
|
||||
/modify-values@1.0.1:
|
||||
@@ -10658,10 +10636,6 @@ packages:
|
||||
resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==}
|
||||
dev: true
|
||||
|
||||
/pathe@1.1.1:
|
||||
resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==}
|
||||
dev: true
|
||||
|
||||
/pathval@1.1.1:
|
||||
resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
|
||||
dev: true
|
||||
@@ -10735,12 +10709,12 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/pkg-types@1.0.3:
|
||||
resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
|
||||
/pkg-types@1.0.2:
|
||||
resolution: {integrity: sha512-hM58GKXOcj8WTqUXnsQyJYXdeAPbythQgEF3nTcEo+nkD49chjQ9IKm/QJy9xf6JakXptz86h7ecP2024rrLaQ==}
|
||||
dependencies:
|
||||
jsonc-parser: 3.2.0
|
||||
mlly: 1.4.0
|
||||
pathe: 1.1.1
|
||||
mlly: 1.2.0
|
||||
pathe: 1.1.0
|
||||
dev: true
|
||||
|
||||
/pkg@5.8.1:
|
||||
@@ -10936,6 +10910,15 @@ packages:
|
||||
engines: {node: ^14.13.1 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/pretty-format@27.5.1:
|
||||
resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
|
||||
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
|
||||
dependencies:
|
||||
ansi-regex: 5.0.1
|
||||
ansi-styles: 5.2.0
|
||||
react-is: 17.0.2
|
||||
dev: true
|
||||
|
||||
/pretty-format@29.4.3:
|
||||
resolution: {integrity: sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
@@ -10945,15 +10928,6 @@ packages:
|
||||
react-is: 18.2.0
|
||||
dev: true
|
||||
|
||||
/pretty-format@29.6.1:
|
||||
resolution: {integrity: sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
dependencies:
|
||||
'@jest/schemas': 29.6.0
|
||||
ansi-styles: 5.2.0
|
||||
react-is: 18.2.0
|
||||
dev: true
|
||||
|
||||
/prismjs@1.29.0:
|
||||
resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -11101,6 +11075,10 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/react-is@17.0.2:
|
||||
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
|
||||
dev: true
|
||||
|
||||
/react-is@18.2.0:
|
||||
resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
|
||||
dev: true
|
||||
@@ -11864,6 +11842,14 @@ packages:
|
||||
resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
/slice-ansi@5.0.0:
|
||||
resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
ansi-styles: 6.2.1
|
||||
is-fullwidth-code-point: 4.0.0
|
||||
dev: true
|
||||
|
||||
/smart-buffer@4.2.0:
|
||||
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
|
||||
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
|
||||
@@ -12014,8 +12000,8 @@ packages:
|
||||
tslib: 2.5.0
|
||||
dev: false
|
||||
|
||||
/std-env@3.3.3:
|
||||
resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==}
|
||||
/std-env@3.3.2:
|
||||
resolution: {integrity: sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA==}
|
||||
dev: true
|
||||
|
||||
/stdopt@2.2.0:
|
||||
@@ -12397,7 +12383,7 @@ packages:
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@jridgewell/source-map': 0.3.2
|
||||
acorn: 8.10.0
|
||||
acorn: 8.8.2
|
||||
commander: 2.20.3
|
||||
source-map-support: 0.5.21
|
||||
dev: true
|
||||
@@ -12476,13 +12462,13 @@ packages:
|
||||
resolution: {integrity: sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==}
|
||||
dev: true
|
||||
|
||||
/tinypool@0.6.0:
|
||||
resolution: {integrity: sha512-FdswUUo5SxRizcBc6b1GSuLpLjisa8N8qMyYoP3rl+bym+QauhtJP5bvZY1ytt8krKGmMLYIRl36HBZfeAoqhQ==}
|
||||
/tinypool@0.3.1:
|
||||
resolution: {integrity: sha512-zLA1ZXlstbU2rlpA4CIeVaqvWq41MTWqLY3FfsAXgC8+f7Pk7zroaJQxDgxn1xNudKW6Kmj4808rPFShUlIRmQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
dev: true
|
||||
|
||||
/tinyspy@2.1.1:
|
||||
resolution: {integrity: sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==}
|
||||
/tinyspy@1.0.2:
|
||||
resolution: {integrity: sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
dev: true
|
||||
|
||||
@@ -12719,8 +12705,8 @@ packages:
|
||||
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
|
||||
dev: true
|
||||
|
||||
/ufo@1.1.2:
|
||||
resolution: {integrity: sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==}
|
||||
/ufo@1.1.1:
|
||||
resolution: {integrity: sha512-MvlCc4GHrmZdAllBc0iUDowff36Q9Ndw/UzqmEKyrfSzokTd9ZCy1i+IIk5hrYKkjoYVQyNbrw7/F8XJ2rEwTg==}
|
||||
dev: true
|
||||
|
||||
/uglify-js@3.17.4:
|
||||
@@ -13053,17 +13039,19 @@ packages:
|
||||
replace-ext: 1.0.1
|
||||
dev: false
|
||||
|
||||
/vite-node@0.33.0(@types/node@18.16.3):
|
||||
resolution: {integrity: sha512-19FpHYbwWWxDr73ruNahC+vtEdza52kA90Qb3La98yZ0xULqV8A5JLNPUff0f5zID4984tW7l3DH2przTJUZSw==}
|
||||
engines: {node: '>=v14.18.0'}
|
||||
/vite-node@0.28.0(@types/node@18.11.18):
|
||||
resolution: {integrity: sha512-4w+hFGfAfsfCchVpZFkIEEEGxF+OA1lVIPc7Dijf/k/nJRUGU80RWnDubA0jmc7CApg/UbnSkIsYGh8v4eJ1HA==}
|
||||
engines: {node: '>=v14.16.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.3.4
|
||||
mlly: 1.4.0
|
||||
pathe: 1.1.1
|
||||
mlly: 1.2.0
|
||||
pathe: 1.1.0
|
||||
picocolors: 1.0.0
|
||||
vite: 4.3.3(@types/node@18.16.3)
|
||||
source-map: 0.6.1
|
||||
source-map-support: 0.5.21
|
||||
vite: 4.3.3(@types/node@18.11.18)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- less
|
||||
@@ -13093,6 +13081,39 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/vite@4.3.3(@types/node@18.11.18):
|
||||
resolution: {integrity: sha512-MwFlLBO4udZXd+VBcezo3u8mC77YQk+ik+fbc0GZWGgzfbPP+8Kf0fldhARqvSYmtIWoAJ5BXPClUbMTlqFxrA==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@types/node': '>= 14'
|
||||
less: '*'
|
||||
sass: '*'
|
||||
stylus: '*'
|
||||
sugarss: '*'
|
||||
terser: ^5.4.0
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
less:
|
||||
optional: true
|
||||
sass:
|
||||
optional: true
|
||||
stylus:
|
||||
optional: true
|
||||
sugarss:
|
||||
optional: true
|
||||
terser:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/node': 18.11.18
|
||||
esbuild: 0.17.18
|
||||
postcss: 8.4.23
|
||||
rollup: 3.21.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
dev: true
|
||||
|
||||
/vite@4.3.3(@types/node@18.16.3):
|
||||
resolution: {integrity: sha512-MwFlLBO4udZXd+VBcezo3u8mC77YQk+ik+fbc0GZWGgzfbPP+8Kf0fldhARqvSYmtIWoAJ5BXPClUbMTlqFxrA==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
@@ -13135,9 +13156,9 @@ packages:
|
||||
dependencies:
|
||||
vite: 4.3.3(@types/node@18.16.3)
|
||||
|
||||
/vitest@0.33.0(@vitest/ui@0.28.0):
|
||||
resolution: {integrity: sha512-1CxaugJ50xskkQ0e969R/hW47za4YXDUfWJDxip1hwbnhUjYolpfUn2AMOulqG/Dtd9WYAtkHmM/m3yKVrEejQ==}
|
||||
engines: {node: '>=v14.18.0'}
|
||||
/vitest@0.28.0(@vitest/ui@0.28.0):
|
||||
resolution: {integrity: sha512-qgPw3vHEOc36wJBknmmeWIpuaLnXtWZinkeT8aOTyIuBYipgpabVzWjWgCilKDa8F+ZrY2cHeH2xGUCYOc5/XA==}
|
||||
engines: {node: '>=v14.16.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@edge-runtime/vm': '*'
|
||||
@@ -13145,9 +13166,6 @@ packages:
|
||||
'@vitest/ui': '*'
|
||||
happy-dom: '*'
|
||||
jsdom: '*'
|
||||
playwright: '*'
|
||||
safaridriver: '*'
|
||||
webdriverio: '*'
|
||||
peerDependenciesMeta:
|
||||
'@edge-runtime/vm':
|
||||
optional: true
|
||||
@@ -13159,37 +13177,31 @@ packages:
|
||||
optional: true
|
||||
jsdom:
|
||||
optional: true
|
||||
playwright:
|
||||
optional: true
|
||||
safaridriver:
|
||||
optional: true
|
||||
webdriverio:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/chai': 4.3.5
|
||||
'@types/chai': 4.3.4
|
||||
'@types/chai-subset': 1.3.3
|
||||
'@types/node': 18.16.3
|
||||
'@vitest/expect': 0.33.0
|
||||
'@vitest/runner': 0.33.0
|
||||
'@vitest/snapshot': 0.33.0
|
||||
'@vitest/spy': 0.33.0
|
||||
'@types/node': 18.11.18
|
||||
'@vitest/expect': 0.28.0
|
||||
'@vitest/runner': 0.28.0
|
||||
'@vitest/spy': 0.28.0
|
||||
'@vitest/ui': 0.28.0
|
||||
'@vitest/utils': 0.33.0
|
||||
acorn: 8.10.0
|
||||
'@vitest/utils': 0.28.0
|
||||
acorn: 8.8.2
|
||||
acorn-walk: 8.2.0
|
||||
cac: 6.7.14
|
||||
chai: 4.3.7
|
||||
debug: 4.3.4
|
||||
local-pkg: 0.4.3
|
||||
magic-string: 0.30.1
|
||||
pathe: 1.1.1
|
||||
pathe: 1.1.0
|
||||
picocolors: 1.0.0
|
||||
std-env: 3.3.3
|
||||
source-map: 0.6.1
|
||||
std-env: 3.3.2
|
||||
strip-literal: 1.0.1
|
||||
tinybench: 2.5.0
|
||||
tinypool: 0.6.0
|
||||
vite: 4.3.3(@types/node@18.16.3)
|
||||
vite-node: 0.33.0(@types/node@18.16.3)
|
||||
tinypool: 0.3.1
|
||||
tinyspy: 1.0.2
|
||||
vite: 4.3.3(@types/node@18.11.18)
|
||||
vite-node: 0.28.0(@types/node@18.11.18)
|
||||
why-is-node-running: 2.2.2
|
||||
transitivePeerDependencies:
|
||||
- less
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`runs examples > example "_euclidRot" example index 0 1`] = `
|
||||
[
|
||||
@@ -3545,96 +3545,96 @@ exports[`runs examples > example "saw" example index 1 1`] = `
|
||||
|
||||
exports[`runs examples > example "scale" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/6 | n:0 note:C3 ]",
|
||||
"[ 1/6 → 1/3 | n:2 note:E3 ]",
|
||||
"[ 1/3 → 1/2 | n:4 note:G3 ]",
|
||||
"[ 1/2 → 2/3 | n:6 note:B3 ]",
|
||||
"[ 2/3 → 5/6 | n:4 note:G3 ]",
|
||||
"[ 5/6 → 1/1 | n:2 note:E3 ]",
|
||||
"[ 1/1 → 7/6 | n:0 note:C3 ]",
|
||||
"[ 7/6 → 4/3 | n:2 note:E3 ]",
|
||||
"[ 4/3 → 3/2 | n:4 note:G3 ]",
|
||||
"[ 3/2 → 5/3 | n:6 note:B3 ]",
|
||||
"[ 5/3 → 11/6 | n:4 note:G3 ]",
|
||||
"[ 11/6 → 2/1 | n:2 note:E3 ]",
|
||||
"[ 2/1 → 13/6 | n:0 note:C3 ]",
|
||||
"[ 13/6 → 7/3 | n:2 note:E3 ]",
|
||||
"[ 7/3 → 5/2 | n:4 note:G3 ]",
|
||||
"[ 5/2 → 8/3 | n:6 note:B3 ]",
|
||||
"[ 8/3 → 17/6 | n:4 note:G3 ]",
|
||||
"[ 17/6 → 3/1 | n:2 note:E3 ]",
|
||||
"[ 3/1 → 19/6 | n:0 note:C3 ]",
|
||||
"[ 19/6 → 10/3 | n:2 note:E3 ]",
|
||||
"[ 10/3 → 7/2 | n:4 note:G3 ]",
|
||||
"[ 7/2 → 11/3 | n:6 note:B3 ]",
|
||||
"[ 11/3 → 23/6 | n:4 note:G3 ]",
|
||||
"[ 23/6 → 4/1 | n:2 note:E3 ]",
|
||||
"[ 0/1 → 1/6 | note:C2 ]",
|
||||
"[ 1/6 → 1/3 | note:E2 ]",
|
||||
"[ 1/3 → 1/2 | note:G2 ]",
|
||||
"[ 1/2 → 2/3 | note:B2 ]",
|
||||
"[ 2/3 → 5/6 | note:G2 ]",
|
||||
"[ 5/6 → 1/1 | note:E2 ]",
|
||||
"[ 1/1 → 7/6 | note:C2 ]",
|
||||
"[ 7/6 → 4/3 | note:E2 ]",
|
||||
"[ 4/3 → 3/2 | note:G2 ]",
|
||||
"[ 3/2 → 5/3 | note:B2 ]",
|
||||
"[ 5/3 → 11/6 | note:G2 ]",
|
||||
"[ 11/6 → 2/1 | note:E2 ]",
|
||||
"[ 2/1 → 13/6 | note:C2 ]",
|
||||
"[ 13/6 → 7/3 | note:E2 ]",
|
||||
"[ 7/3 → 5/2 | note:G2 ]",
|
||||
"[ 5/2 → 8/3 | note:B2 ]",
|
||||
"[ 8/3 → 17/6 | note:G2 ]",
|
||||
"[ 17/6 → 3/1 | note:E2 ]",
|
||||
"[ 3/1 → 19/6 | note:C2 ]",
|
||||
"[ 19/6 → 10/3 | note:E2 ]",
|
||||
"[ 10/3 → 7/2 | note:G2 ]",
|
||||
"[ 7/2 → 11/3 | note:B2 ]",
|
||||
"[ 11/3 → 23/6 | note:G2 ]",
|
||||
"[ 23/6 → 4/1 | note:E2 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "scale" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | n:0 note:C3 s:piano ]",
|
||||
"[ 0/1 → 1/4 | n:7 note:C4 s:piano ]",
|
||||
"[ 1/4 → 1/2 | n:4 note:G3 s:piano ]",
|
||||
"[ 1/2 → 3/4 | n:2 note:E3 s:piano ]",
|
||||
"[ 1/2 → 3/4 | n:7 note:C4 s:piano ]",
|
||||
"[ 3/4 → 1/1 | n:4 note:G3 s:piano ]",
|
||||
"[ 1/1 → 5/4 | n:0 note:C3 s:piano ]",
|
||||
"[ 1/1 → 5/4 | n:7 note:C4 s:piano ]",
|
||||
"[ 5/4 → 3/2 | n:4 note:G3 s:piano ]",
|
||||
"[ 3/2 → 7/4 | n:2 note:E3 s:piano ]",
|
||||
"[ 3/2 → 7/4 | n:7 note:C4 s:piano ]",
|
||||
"[ 7/4 → 2/1 | n:4 note:G3 s:piano ]",
|
||||
"[ 2/1 → 9/4 | n:0 note:C3 s:piano ]",
|
||||
"[ 2/1 → 9/4 | n:7 note:C4 s:piano ]",
|
||||
"[ 9/4 → 5/2 | n:4 note:G3 s:piano ]",
|
||||
"[ 5/2 → 11/4 | n:2 note:Eb3 s:piano ]",
|
||||
"[ 5/2 → 11/4 | n:7 note:C4 s:piano ]",
|
||||
"[ 11/4 → 3/1 | n:4 note:G3 s:piano ]",
|
||||
"[ 3/1 → 13/4 | n:0 note:C3 s:piano ]",
|
||||
"[ 3/1 → 13/4 | n:7 note:C4 s:piano ]",
|
||||
"[ 13/4 → 7/2 | n:4 note:G3 s:piano ]",
|
||||
"[ 7/2 → 15/4 | n:2 note:Eb3 s:piano ]",
|
||||
"[ 7/2 → 15/4 | n:7 note:C4 s:piano ]",
|
||||
"[ 15/4 → 4/1 | n:4 note:G3 s:piano ]",
|
||||
"[ 0/1 → 1/6 | note:C2 ]",
|
||||
"[ 1/6 → 1/3 | note:E2 ]",
|
||||
"[ 1/3 → 1/2 | note:G2 ]",
|
||||
"[ 1/2 → 2/3 | note:B2 ]",
|
||||
"[ 2/3 → 5/6 | note:G2 ]",
|
||||
"[ 5/6 → 1/1 | note:E2 ]",
|
||||
"[ 1/1 → 7/6 | note:C2 ]",
|
||||
"[ 7/6 → 4/3 | note:Eb2 ]",
|
||||
"[ 4/3 → 3/2 | note:G2 ]",
|
||||
"[ 3/2 → 5/3 | note:Bb2 ]",
|
||||
"[ 5/3 → 11/6 | note:G2 ]",
|
||||
"[ 11/6 → 2/1 | note:Eb2 ]",
|
||||
"[ 2/1 → 13/6 | note:C2 ]",
|
||||
"[ 13/6 → 7/3 | note:E2 ]",
|
||||
"[ 7/3 → 5/2 | note:G2 ]",
|
||||
"[ 5/2 → 8/3 | note:B2 ]",
|
||||
"[ 8/3 → 17/6 | note:G2 ]",
|
||||
"[ 17/6 → 3/1 | note:E2 ]",
|
||||
"[ 3/1 → 19/6 | note:C2 ]",
|
||||
"[ 19/6 → 10/3 | note:Eb2 ]",
|
||||
"[ 10/3 → 7/2 | note:G2 ]",
|
||||
"[ 7/2 → 11/3 | note:Bb2 ]",
|
||||
"[ 11/3 → 23/6 | note:G2 ]",
|
||||
"[ 23/6 → 4/1 | note:Eb2 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "scale" example index 2 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | n:10 note:C5 s:folkharp ]",
|
||||
"[ 1/8 → 1/4 | n:2 note:F3 s:folkharp ]",
|
||||
"[ 1/4 → 3/8 | n:7 note:F4 s:folkharp ]",
|
||||
"[ 3/8 → 1/2 | n:4 note:A3 s:folkharp ]",
|
||||
"[ 1/2 → 5/8 | n:2 note:F3 s:folkharp ]",
|
||||
"[ 5/8 → 3/4 | n:5 note:C4 s:folkharp ]",
|
||||
"[ 3/4 → 7/8 | n:9 note:A4 s:folkharp ]",
|
||||
"[ 7/8 → 1/1 | n:8 note:G4 s:folkharp ]",
|
||||
"[ 1/1 → 9/8 | n:7 note:F4 s:folkharp ]",
|
||||
"[ 9/8 → 5/4 | n:1 note:D3 s:folkharp ]",
|
||||
"[ 5/4 → 11/8 | n:1 note:D3 s:folkharp ]",
|
||||
"[ 11/8 → 3/2 | n:6 note:D4 s:folkharp ]",
|
||||
"[ 3/2 → 13/8 | n:2 note:F3 s:folkharp ]",
|
||||
"[ 13/8 → 7/4 | n:4 note:A3 s:folkharp ]",
|
||||
"[ 7/4 → 15/8 | n:6 note:D4 s:folkharp ]",
|
||||
"[ 15/8 → 2/1 | n:10 note:C5 s:folkharp ]",
|
||||
"[ 2/1 → 17/8 | n:4 note:A3 s:folkharp ]",
|
||||
"[ 17/8 → 9/4 | n:0 note:C3 s:folkharp ]",
|
||||
"[ 9/4 → 19/8 | n:8 note:G4 s:folkharp ]",
|
||||
"[ 19/8 → 5/2 | n:2 note:F3 s:folkharp ]",
|
||||
"[ 5/2 → 21/8 | n:7 note:F4 s:folkharp ]",
|
||||
"[ 21/8 → 11/4 | n:6 note:D4 s:folkharp ]",
|
||||
"[ 11/4 → 23/8 | n:11 note:D5 s:folkharp ]",
|
||||
"[ 23/8 → 3/1 | n:3 note:G3 s:folkharp ]",
|
||||
"[ 3/1 → 25/8 | n:0 note:C3 s:folkharp ]",
|
||||
"[ 25/8 → 13/4 | n:11 note:D5 s:folkharp ]",
|
||||
"[ 13/4 → 27/8 | n:4 note:A3 s:folkharp ]",
|
||||
"[ 27/8 → 7/2 | n:9 note:A4 s:folkharp ]",
|
||||
"[ 7/2 → 29/8 | n:10 note:C5 s:folkharp ]",
|
||||
"[ 29/8 → 15/4 | n:12 note:F5 s:folkharp ]",
|
||||
"[ 15/4 → 31/8 | n:1 note:D3 s:folkharp ]",
|
||||
"[ 31/8 → 4/1 | n:4 note:A3 s:folkharp ]",
|
||||
"[ 0/1 → 1/8 | note:C3 s:folkharp ]",
|
||||
"[ 1/8 → 1/4 | note:B2 s:folkharp ]",
|
||||
"[ 1/4 → 3/8 | note:A2 s:folkharp ]",
|
||||
"[ 3/8 → 1/2 | note:G2 s:folkharp ]",
|
||||
"[ 1/2 → 5/8 | note:F2 s:folkharp ]",
|
||||
"[ 5/8 → 3/4 | note:E2 s:folkharp ]",
|
||||
"[ 3/4 → 7/8 | note:D2 s:folkharp ]",
|
||||
"[ 7/8 → 1/1 | note:C2 s:folkharp ]",
|
||||
"[ 1/1 → 9/8 | note:C3 s:folkharp ]",
|
||||
"[ 9/8 → 5/4 | note:Bb2 s:folkharp ]",
|
||||
"[ 5/4 → 11/8 | note:Ab2 s:folkharp ]",
|
||||
"[ 11/8 → 3/2 | note:G2 s:folkharp ]",
|
||||
"[ 3/2 → 13/8 | note:F2 s:folkharp ]",
|
||||
"[ 13/8 → 7/4 | note:Eb2 s:folkharp ]",
|
||||
"[ 7/4 → 15/8 | note:D2 s:folkharp ]",
|
||||
"[ 15/8 → 2/1 | note:C2 s:folkharp ]",
|
||||
"[ 2/1 → 17/8 | note:C3 s:folkharp ]",
|
||||
"[ 17/8 → 9/4 | note:B2 s:folkharp ]",
|
||||
"[ 9/4 → 19/8 | note:A2 s:folkharp ]",
|
||||
"[ 19/8 → 5/2 | note:G2 s:folkharp ]",
|
||||
"[ 5/2 → 21/8 | note:F2 s:folkharp ]",
|
||||
"[ 21/8 → 11/4 | note:E2 s:folkharp ]",
|
||||
"[ 11/4 → 23/8 | note:D2 s:folkharp ]",
|
||||
"[ 23/8 → 3/1 | note:C2 s:folkharp ]",
|
||||
"[ 3/1 → 25/8 | note:C3 s:folkharp ]",
|
||||
"[ 25/8 → 13/4 | note:Bb2 s:folkharp ]",
|
||||
"[ 13/4 → 27/8 | note:Ab2 s:folkharp ]",
|
||||
"[ 27/8 → 7/2 | note:G2 s:folkharp ]",
|
||||
"[ 7/2 → 29/8 | note:F2 s:folkharp ]",
|
||||
"[ 29/8 → 15/4 | note:Eb2 s:folkharp ]",
|
||||
"[ 15/4 → 31/8 | note:D2 s:folkharp ]",
|
||||
"[ 31/8 → 4/1 | note:C2 s:folkharp ]",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -3874,60 +3874,6 @@ exports[`runs examples > example "sine" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "slice" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 3/16 | begin:0.875 end:1 _slices:8 s:breaks165 ]",
|
||||
"[ 3/16 → 3/8 | begin:0.75 end:0.875 _slices:8 s:breaks165 ]",
|
||||
"[ 3/8 → 9/16 | begin:0.625 end:0.75 _slices:8 s:breaks165 ]",
|
||||
"[ 9/16 → 21/32 | begin:0 end:0.125 _slices:8 s:breaks165 ]",
|
||||
"[ 21/32 → 3/4 | begin:0.5 end:0.625 _slices:8 s:breaks165 ]",
|
||||
"[ 3/4 → 15/16 | begin:0.375 end:0.5 _slices:8 s:breaks165 ]",
|
||||
"[ (15/16 → 1/1) ⇝ 9/8 | begin:0.25 end:0.375 _slices:8 s:breaks165 ]",
|
||||
"[ 15/16 ⇜ (1/1 → 9/8) | begin:0.25 end:0.375 _slices:8 s:breaks165 ]",
|
||||
"[ 9/8 → 21/16 | begin:0.125 end:0.25 _slices:8 s:breaks165 ]",
|
||||
"[ 21/16 → 3/2 | begin:0 end:0.125 _slices:8 s:breaks165 ]",
|
||||
"[ 3/2 → 27/16 | begin:0 end:0.125 _slices:8 s:breaks165 ]",
|
||||
"[ 27/16 → 15/8 | begin:0.125 end:0.25 _slices:8 s:breaks165 ]",
|
||||
"[ 15/8 → 63/32 | begin:0.25 end:0.375 _slices:8 s:breaks165 ]",
|
||||
"[ (63/32 → 2/1) ⇝ 33/16 | begin:0.25 end:0.375 _slices:8 s:breaks165 ]",
|
||||
"[ 63/32 ⇜ (2/1 → 33/16) | begin:0.25 end:0.375 _slices:8 s:breaks165 ]",
|
||||
"[ 33/16 → 9/4 | begin:0.375 end:0.5 _slices:8 s:breaks165 ]",
|
||||
"[ 9/4 → 75/32 | begin:0.5 end:0.625 _slices:8 s:breaks165 ]",
|
||||
"[ 75/32 → 39/16 | begin:0 end:0.125 _slices:8 s:breaks165 ]",
|
||||
"[ 39/16 → 21/8 | begin:0.625 end:0.75 _slices:8 s:breaks165 ]",
|
||||
"[ 21/8 → 45/16 | begin:0.75 end:0.875 _slices:8 s:breaks165 ]",
|
||||
"[ 45/16 → 3/1 | begin:0.875 end:1 _slices:8 s:breaks165 ]",
|
||||
"[ 3/1 → 51/16 | begin:0 end:0.125 _slices:8 s:breaks165 ]",
|
||||
"[ 51/16 → 27/8 | begin:0.125 end:0.25 _slices:8 s:breaks165 ]",
|
||||
"[ 27/8 → 57/16 | begin:0.25 end:0.375 _slices:8 s:breaks165 ]",
|
||||
"[ 57/16 → 15/4 | begin:0.375 end:0.5 _slices:8 s:breaks165 ]",
|
||||
"[ 15/4 → 123/32 | begin:0.5 end:0.625 _slices:8 s:breaks165 ]",
|
||||
"[ 123/32 → 63/16 | begin:0 end:0.125 _slices:8 s:breaks165 ]",
|
||||
"[ (63/16 → 4/1) ⇝ 33/8 | begin:0.625 end:0.75 _slices:8 s:breaks165 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "slice" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | begin:0 end:0.25 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
"[ 1/4 → 1/2 | begin:0.25 end:0.5 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
"[ 1/2 → 3/4 | begin:0.25 end:0.5 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
"[ 3/4 → 1/1 | begin:0.5 end:0.75 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
"[ 1/1 → 5/4 | begin:0 end:0.25 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
"[ 5/4 → 3/2 | begin:0.25 end:0.5 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
"[ 3/2 → 7/4 | begin:0.25 end:0.5 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
"[ 7/4 → 2/1 | begin:0.75 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
"[ 2/1 → 9/4 | begin:0 end:0.25 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
"[ 9/4 → 5/2 | begin:0.25 end:0.5 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
"[ 5/2 → 11/4 | begin:0.25 end:0.5 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
"[ 11/4 → 3/1 | begin:0.5 end:0.75 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
"[ 3/1 → 13/4 | begin:0 end:0.25 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
"[ 13/4 → 7/2 | begin:0.25 end:0.5 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
"[ 7/2 → 15/4 | begin:0.25 end:0.5 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
"[ 15/4 → 4/1 | begin:0.75 _slices:[0 0.25 0.5 0.75] s:breaks125 speed:0.5 unit:c ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "slow" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/1 | s:bd ]",
|
||||
@@ -4431,60 +4377,6 @@ exports[`runs examples > example "velocity" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "voicing" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/1 | note:E4 ]",
|
||||
"[ 0/1 → 1/1 | note:G4 ]",
|
||||
"[ 0/1 → 1/1 | note:C5 ]",
|
||||
"[ 1/1 → 2/1 | note:E4 ]",
|
||||
"[ 1/1 → 2/1 | note:A4 ]",
|
||||
"[ 1/1 → 2/1 | note:C5 ]",
|
||||
"[ 2/1 → 3/1 | note:F4 ]",
|
||||
"[ 2/1 → 3/1 | note:A4 ]",
|
||||
"[ 2/1 → 3/1 | note:C5 ]",
|
||||
"[ 3/1 → 4/1 | note:D4 ]",
|
||||
"[ 3/1 → 4/1 | note:G4 ]",
|
||||
"[ 3/1 → 4/1 | note:B4 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "voicing" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | note:64 ]",
|
||||
"[ 1/8 → 1/4 | note:67 ]",
|
||||
"[ 1/4 → 3/8 | note:72 ]",
|
||||
"[ 3/8 → 1/2 | note:76 ]",
|
||||
"[ 1/2 → 5/8 | note:79 ]",
|
||||
"[ 5/8 → 3/4 | note:84 ]",
|
||||
"[ 3/4 → 7/8 | note:88 ]",
|
||||
"[ 7/8 → 1/1 | note:91 ]",
|
||||
"[ 1/1 → 9/8 | note:64 ]",
|
||||
"[ 9/8 → 5/4 | note:69 ]",
|
||||
"[ 5/4 → 11/8 | note:72 ]",
|
||||
"[ 11/8 → 3/2 | note:76 ]",
|
||||
"[ 3/2 → 13/8 | note:81 ]",
|
||||
"[ 13/8 → 7/4 | note:84 ]",
|
||||
"[ 7/4 → 15/8 | note:88 ]",
|
||||
"[ 15/8 → 2/1 | note:93 ]",
|
||||
"[ 2/1 → 17/8 | note:65 ]",
|
||||
"[ 17/8 → 9/4 | note:69 ]",
|
||||
"[ 9/4 → 19/8 | note:72 ]",
|
||||
"[ 19/8 → 5/2 | note:77 ]",
|
||||
"[ 5/2 → 21/8 | note:81 ]",
|
||||
"[ 21/8 → 11/4 | note:84 ]",
|
||||
"[ 11/4 → 23/8 | note:89 ]",
|
||||
"[ 23/8 → 3/1 | note:93 ]",
|
||||
"[ 3/1 → 25/8 | note:62 ]",
|
||||
"[ 25/8 → 13/4 | note:67 ]",
|
||||
"[ 13/4 → 27/8 | note:71 ]",
|
||||
"[ 27/8 → 7/2 | note:74 ]",
|
||||
"[ 7/2 → 29/8 | note:79 ]",
|
||||
"[ 29/8 → 15/4 | note:83 ]",
|
||||
"[ 15/4 → 31/8 | note:86 ]",
|
||||
"[ 31/8 → 4/1 | note:91 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "voicings" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/1 | note:B3 ]",
|
||||
@@ -4523,6 +4415,31 @@ exports[`runs examples > example "vowel" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "webdirt" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | s:bd n:0 ]",
|
||||
"[ 1/8 → 1/4 | s:bd n:0 ]",
|
||||
"[ 1/4 → 1/2 | s:hh n:0 ]",
|
||||
"[ 1/2 → 3/4 | s:sd n:0 ]",
|
||||
"[ 3/4 → 1/1 | s:hh n:0 ]",
|
||||
"[ 1/1 → 9/8 | s:bd n:1 ]",
|
||||
"[ 9/8 → 5/4 | s:bd n:1 ]",
|
||||
"[ 5/4 → 3/2 | s:hh n:1 ]",
|
||||
"[ 3/2 → 7/4 | s:sd n:1 ]",
|
||||
"[ 7/4 → 2/1 | s:hh n:1 ]",
|
||||
"[ 2/1 → 17/8 | s:bd n:0 ]",
|
||||
"[ 17/8 → 9/4 | s:bd n:0 ]",
|
||||
"[ 9/4 → 5/2 | s:hh n:0 ]",
|
||||
"[ 5/2 → 11/4 | s:sd n:0 ]",
|
||||
"[ 11/4 → 3/1 | s:hh n:0 ]",
|
||||
"[ 3/1 → 25/8 | s:bd n:1 ]",
|
||||
"[ 25/8 → 13/4 | s:bd n:1 ]",
|
||||
"[ 13/4 → 7/2 | s:hh n:1 ]",
|
||||
"[ 7/2 → 15/4 | s:sd n:1 ]",
|
||||
"[ 15/4 → 4/1 | s:hh n:1 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "when" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/3 | note:c3 ]",
|
||||
|
||||
+3011
-2985
File diff suppressed because it is too large
Load Diff
@@ -60,7 +60,6 @@ const toneHelpersMocked = {
|
||||
vol: mockNode,
|
||||
out: id,
|
||||
osc: id,
|
||||
samples: id,
|
||||
adsr: id,
|
||||
getDestination: id,
|
||||
players: mockNode,
|
||||
|
||||
@@ -43,18 +43,14 @@ const sidebar = SIDEBAR[langCode];
|
||||
</li>
|
||||
))
|
||||
}
|
||||
<li>
|
||||
<a class={`py-0.5 mb-4 w-full hover:bg-lineHighlight block`} href={`${Astro.site?.pathname}ref`}> Reference</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<script is:inline>
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
var target = document.querySelector('[aria-current="page"]');
|
||||
const nav = document.querySelector('.nav-groups');
|
||||
if (nav && target && target.offsetTop > window.innerHeight - 100) {
|
||||
nav.scrollTop = target.offsetTop;
|
||||
}
|
||||
});
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
var target = document.querySelector('[aria-current="page"]');
|
||||
if (target && target.offsetTop > window.innerHeight - 100) {
|
||||
document.querySelector('.nav-groups').scrollTop = target.offsetTop;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
---
|
||||
import { docs } from '../../../../doc.json';
|
||||
|
||||
type Props = {
|
||||
currentPage: string;
|
||||
};
|
||||
|
||||
const { currentPage } = Astro.props as Props;
|
||||
const { BASE_URL } = import.meta.env;
|
||||
let currentPageMatch = currentPage.slice(BASE_URL.length, currentPage.endsWith('/') ? -1 : undefined);
|
||||
---
|
||||
|
||||
<nav aria-labelledby="grid-left" class="max-h-full overflow-auto pb-20 text-foreground">
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
class={`py-0.5 mb-4 w-full hover:bg-lineHighlight block`}
|
||||
href={`${Astro.site?.pathname}workshop/getting-started`}>Back to Docs</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<div class="nav-group pb-4">
|
||||
<a class="hover:bg-lineHighlight w-full block" href={Astro.site?.pathname + 'ref'}><h2>Reference</h2></a>
|
||||
<ul>
|
||||
{
|
||||
docs
|
||||
.filter(
|
||||
({ tags }) =>
|
||||
!tags?.find((t) => t.title === 'noautocomplete') && !tags?.find((t) => t.title === 'superdirtonly'),
|
||||
)
|
||||
.filter(({ name, description }) => name && !name.startsWith('_') && !!description)
|
||||
// @ts-ignore
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map(({ name }) => {
|
||||
const link = 'ref/' + name;
|
||||
const url = Astro.site?.pathname + link;
|
||||
return (
|
||||
<li class="">
|
||||
<a
|
||||
class={`pl-4 py-0.5 w-full hover:bg-lineHighlight block${
|
||||
currentPageMatch === link ? ' bg-lineHighlight' : ''
|
||||
}`}
|
||||
href={url}
|
||||
aria-current={currentPageMatch === link ? 'page' : false}
|
||||
>
|
||||
{name}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<script is:inline>
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
var target = document.querySelector('[aria-current="page"]');
|
||||
const nav = document.querySelector('.nav-groups');
|
||||
if (nav && target && target.offsetTop > window.innerHeight - 100) {
|
||||
nav.scrollTop = target.offsetTop;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -8,10 +8,9 @@ type Props = {
|
||||
frontmatter: Frontmatter;
|
||||
headings: MarkdownHeading[];
|
||||
githubEditUrl: string;
|
||||
hideMoreMenu?: boolean;
|
||||
};
|
||||
|
||||
const { frontmatter, headings, githubEditUrl, hideMoreMenu } = Astro.props as Props;
|
||||
const { frontmatter, headings, githubEditUrl } = Astro.props as Props;
|
||||
const title = frontmatter.title;
|
||||
const currentPage = Astro.url.pathname;
|
||||
---
|
||||
@@ -23,13 +22,9 @@ const currentPage = Astro.url.pathname;
|
||||
<slot />
|
||||
</div>
|
||||
</section>
|
||||
{
|
||||
!hideMoreMenu && (
|
||||
<nav class="block sm:hidden">
|
||||
<MoreMenu editHref={githubEditUrl} />
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
<nav class="block sm:hidden">
|
||||
<MoreMenu editHref={githubEditUrl} />
|
||||
</nav>
|
||||
</article>
|
||||
<style>
|
||||
.content {
|
||||
|
||||
@@ -1,268 +0,0 @@
|
||||
import useEvent from '@strudel.cycles/react/src/hooks/useEvent.mjs';
|
||||
import useFrame from '@strudel.cycles/react/src/hooks/useFrame.mjs';
|
||||
import { getAudioContext } from '@strudel.cycles/webaudio';
|
||||
import { midi2note } from '@strudel.cycles/core';
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import Claviature from '@components/Claviature';
|
||||
|
||||
let Button = (props) => <button {...props} className="bg-lineHighlight p-2 rounded-md color-foreground" />;
|
||||
|
||||
function plotValues(ctx, values, min, max, color) {
|
||||
let { width, height } = ctx.canvas;
|
||||
ctx.strokeStyle = color;
|
||||
const thickness = 8;
|
||||
ctx.lineWidth = thickness;
|
||||
ctx.beginPath();
|
||||
|
||||
let x = (f) => ((f - min) / (max - min)) * width;
|
||||
let y = (i) => (1 - i / values.length) * height;
|
||||
values.forEach((f, i, a) => {
|
||||
ctx.lineTo(x(f), y(i));
|
||||
});
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function getColor(cssVariable) {
|
||||
if (typeof document === 'undefined') {
|
||||
return 'white';
|
||||
}
|
||||
const dummyElement = document.createElement('div');
|
||||
dummyElement.style.color = cssVariable;
|
||||
// Append the dummy element to the document body
|
||||
document.body.appendChild(dummyElement);
|
||||
// Get the computed style of the dummy element
|
||||
const styles = getComputedStyle(dummyElement);
|
||||
// Get the value of the CSS variable
|
||||
const color = styles.getPropertyValue(cssVariable);
|
||||
document.body.removeChild(dummyElement);
|
||||
return color;
|
||||
}
|
||||
|
||||
let pitchColor = '#eab308';
|
||||
let frequencyColor = '#3b82f6';
|
||||
|
||||
export function PitchSlider({
|
||||
buttons = [],
|
||||
animatable = false,
|
||||
plot = false,
|
||||
showPitchSlider = false,
|
||||
showFrequencySlider = false,
|
||||
pitchStep = '0.001',
|
||||
min = 55,
|
||||
max = 7040,
|
||||
initial = 220,
|
||||
baseFrequency = min,
|
||||
zeroOffset = 0,
|
||||
claviature,
|
||||
}) {
|
||||
const oscRef = useRef();
|
||||
const activeRef = useRef();
|
||||
const freqRef = useRef(initial);
|
||||
const historyRef = useRef([freqRef.current]);
|
||||
const frameRef = useRef();
|
||||
const canvasRef = useRef();
|
||||
const [hz, setHz] = useState(freqRef.current);
|
||||
|
||||
useEffect(() => {
|
||||
freqRef.current = hz;
|
||||
}, [hz]);
|
||||
|
||||
useEvent('mouseup', () => {
|
||||
oscRef.current?.stop();
|
||||
activeRef.current = false;
|
||||
});
|
||||
|
||||
let freqSlider2freq = (progress) => min + progress * (max - min);
|
||||
let pitchSlider2freq = (progress) => min * 2 ** (progress * Math.log2(max / min));
|
||||
let freq2freqSlider = (freq) => (freq - min) / (max - min);
|
||||
let freq2pitchSlider = (freq) => {
|
||||
const [minOct, maxOct] = [Math.log2(min), Math.log2(max)];
|
||||
return (Math.log2(freq) - minOct) / (maxOct - minOct);
|
||||
};
|
||||
|
||||
const freqSlider = freq2freqSlider(hz);
|
||||
const pitchSlider = freq2pitchSlider(hz);
|
||||
|
||||
let startOsc = (hz) => {
|
||||
if (oscRef.current) {
|
||||
oscRef.current.stop();
|
||||
}
|
||||
oscRef.current = getAudioContext().createOscillator();
|
||||
oscRef.current.frequency.value = hz;
|
||||
oscRef.current.connect(getAudioContext().destination);
|
||||
oscRef.current.start();
|
||||
activeRef.current = true;
|
||||
setHz(hz);
|
||||
};
|
||||
|
||||
let startSweep = (exp = false) => {
|
||||
let f = min;
|
||||
startOsc(f);
|
||||
const frame = () => {
|
||||
if (f < max) {
|
||||
if (!exp) {
|
||||
f += 10;
|
||||
} else {
|
||||
f *= 1.01;
|
||||
}
|
||||
oscRef.current.frequency.value = f;
|
||||
frameRef.current = requestAnimationFrame(frame);
|
||||
} else {
|
||||
oscRef.current.stop();
|
||||
cancelAnimationFrame(frameRef.current);
|
||||
}
|
||||
setHz(f);
|
||||
};
|
||||
requestAnimationFrame(frame);
|
||||
};
|
||||
|
||||
useFrame(() => {
|
||||
historyRef.current.push(freqRef.current);
|
||||
historyRef.current = historyRef.current.slice(-1000);
|
||||
if (canvasRef.current) {
|
||||
let ctx = canvasRef.current.getContext('2d');
|
||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
if (showFrequencySlider) {
|
||||
plotValues(ctx, historyRef.current, min, max, frequencyColor);
|
||||
}
|
||||
if (showPitchSlider) {
|
||||
const [minOct, maxOct] = [Math.log2(min), Math.log2(max)];
|
||||
let perceptual = historyRef.current.map((v) => Math.log2(v));
|
||||
plotValues(ctx, perceptual, minOct, maxOct, pitchColor);
|
||||
}
|
||||
}
|
||||
}, plot);
|
||||
|
||||
let handleChangeFrequency = (f) => {
|
||||
setHz(f);
|
||||
if (oscRef.current) {
|
||||
oscRef.current.frequency.value = f;
|
||||
}
|
||||
};
|
||||
let handleMouseDown = () => {
|
||||
cancelAnimationFrame(frameRef.current);
|
||||
startOsc(hz);
|
||||
};
|
||||
|
||||
let exponent, activeNote, activeNoteLabel;
|
||||
if (showPitchSlider) {
|
||||
const expOffset = baseFrequency ? Math.log2(baseFrequency / min) : 0;
|
||||
exponent = freq2pitchSlider(hz) * Math.log2(max / min) - expOffset;
|
||||
let semitones = parseFloat((exponent * 12).toFixed(2));
|
||||
if (zeroOffset) {
|
||||
semitones = semitones + zeroOffset;
|
||||
const isWhole = Math.round(semitones) === semitones;
|
||||
activeNote = midi2note(Math.round(semitones));
|
||||
activeNoteLabel = (!isWhole ? '~' : '') + activeNote;
|
||||
semitones = !isWhole ? semitones.toFixed(2) : semitones;
|
||||
exponent = (
|
||||
<>
|
||||
(<span className="text-yellow-500">{semitones}</span> - {zeroOffset})/12
|
||||
</>
|
||||
);
|
||||
} else if (semitones % 12 === 0) {
|
||||
exponent = <span className="text-yellow-500">{semitones / 12}</span>;
|
||||
} else if (semitones % 1 === 0) {
|
||||
exponent = (
|
||||
<>
|
||||
<span className="text-yellow-500">{semitones}</span>/12
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
exponent = <span className="text-yellow-500">{exponent.toFixed(2)}</span>;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<span className="font-mono">
|
||||
{showFrequencySlider && <span className="text-blue-500">{hz.toFixed(0)}Hz</span>}
|
||||
{showFrequencySlider && showPitchSlider && <> = </>}
|
||||
{showPitchSlider && (
|
||||
<>
|
||||
{baseFrequency || min}Hz * 2<sup>{exponent}</sup>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
{claviature && (
|
||||
<>
|
||||
{' '}
|
||||
= <span className="text-yellow-500">{activeNoteLabel}</span>
|
||||
</>
|
||||
)}
|
||||
<div>
|
||||
{showFrequencySlider && (
|
||||
<div className="flex space-x-1 items-center">
|
||||
<input
|
||||
type="range"
|
||||
value={freqSlider}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.001}
|
||||
onMouseDown={handleMouseDown}
|
||||
className={`block w-full max-w-[600px] accent-blue-500 `}
|
||||
onChange={(e) => {
|
||||
const f = freqSlider2freq(parseFloat(e.target.value));
|
||||
handleChangeFrequency(f);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{showPitchSlider && (
|
||||
<div>
|
||||
<input
|
||||
type="range"
|
||||
value={pitchSlider}
|
||||
min={0}
|
||||
max={1}
|
||||
//step=".001"
|
||||
step={pitchStep}
|
||||
onMouseDown={handleMouseDown}
|
||||
className={`block w-full max-w-[600px] accent-yellow-500`}
|
||||
onChange={(e) => {
|
||||
const f = pitchSlider2freq(parseFloat(e.target.value));
|
||||
handleChangeFrequency(f);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="px-2">
|
||||
{plot && <canvas ref={canvasRef} className="w-full max-w-[584px] h-[300px]" height="600" width={800} />}
|
||||
</div>
|
||||
<div className="space-x-2">
|
||||
{animatable && (
|
||||
<Button onClick={() => startSweep()}>
|
||||
<span style={{ color: '#3b82f6' }}>Frequency Sweep</span>
|
||||
</Button>
|
||||
)}
|
||||
{animatable && (
|
||||
<Button onClick={() => startSweep(true)}>
|
||||
<span style={{ color: '#eab308' }}>Pitch Sweep</span>
|
||||
</Button>
|
||||
)}
|
||||
{buttons.map((f, i) => (
|
||||
<Button key={(f, i)} onMouseDown={() => startOsc(f)}>
|
||||
{f}Hz
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
{claviature && (
|
||||
<Claviature
|
||||
onMouseDown={(note) => {
|
||||
const f = 440 * 2 ** ((note - 69) / 12);
|
||||
handleChangeFrequency(f);
|
||||
cancelAnimationFrame(frameRef.current);
|
||||
startOsc(f);
|
||||
}}
|
||||
options={{
|
||||
range: ['A1', 'A5'],
|
||||
scaleY: 0.75,
|
||||
scaleX: 0.86,
|
||||
colorize: activeNote ? [{ keys: [activeNote], color: '#eab308' }] : [],
|
||||
labels: activeNote ? { [activeNote]: activeNote } : {},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -15,7 +15,7 @@ export const OPEN_GRAPH = {
|
||||
export type Frontmatter = {
|
||||
title: string;
|
||||
description: string;
|
||||
layout?: string;
|
||||
layout: string;
|
||||
image?: { src: string; alt: string };
|
||||
dir?: 'ltr' | 'rtl';
|
||||
ogLocale?: string;
|
||||
@@ -89,7 +89,6 @@ export const SIDEBAR: Sidebar = {
|
||||
{ text: 'Accumulation', link: 'learn/accumulation' },
|
||||
{ text: 'Tonal Functions', link: 'learn/tonal' },
|
||||
],
|
||||
Understand: [{ text: 'Pitch', link: 'understand/pitch' }],
|
||||
Development: [
|
||||
{ text: 'REPL', link: 'technical-manual/repl' },
|
||||
{ text: 'Sounds', link: 'technical-manual/sounds' },
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
---
|
||||
import HeadCommon from '../components/HeadCommon.astro';
|
||||
import HeadSEO from '../components/HeadSEO.astro';
|
||||
import Header from '../components/Header/Header.astro';
|
||||
import PageContent from '../components/PageContent/PageContent.astro';
|
||||
import RightSidebar from '../components/RightSidebar/RightSidebar.astro';
|
||||
import * as CONFIG from '../config';
|
||||
import type { MarkdownHeading } from 'astro';
|
||||
import '../docs/docs.css';
|
||||
import ReferenceSidebar from '../components/LeftSidebar/ReferenceSidebar.astro';
|
||||
|
||||
type Props = {
|
||||
frontmatter: CONFIG.Frontmatter;
|
||||
headings: MarkdownHeading[];
|
||||
};
|
||||
|
||||
const { frontmatter, headings } = Astro.props as Props;
|
||||
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
||||
const currentPage = Astro.url.pathname;
|
||||
const currentFile = `src/pages${currentPage.replace(/\/$/, '')}.mdx`;
|
||||
const githubEditUrl = `${CONFIG.GITHUB_EDIT_URL}/${currentFile}`;
|
||||
---
|
||||
|
||||
<html dir={frontmatter.dir ?? 'ltr'} lang={frontmatter.lang ?? 'en'} class="initial dark">
|
||||
<head>
|
||||
<HeadCommon />
|
||||
<HeadSEO frontmatter={frontmatter} canonicalUrl={canonicalURL} />
|
||||
<title>
|
||||
{frontmatter.title ? `${frontmatter.title} 🌀 ${CONFIG.SITE.title}` : CONFIG.SITE.title}
|
||||
</title>
|
||||
</head>
|
||||
|
||||
<body class="h-app-height text-gray-50 bg-background">
|
||||
<div class="w-full h-full space-y-4 flex flex-col">
|
||||
<header class="max-w-full fixed top-0 w-full z-[100]">
|
||||
<Header currentPage={currentPage} />
|
||||
</header>
|
||||
<main class="relative pt-16">
|
||||
<div class="h-full top-0 overflow-auto min-w-[300px] flex xl:justify-center pr-4 pl-4 md:pl-[300px] xl:pl-0">
|
||||
<aside title="Site Navigation" class="w-[300px] px-6 left-0 hidden md:block fixed h-full">
|
||||
<ReferenceSidebar currentPage={currentPage} />
|
||||
</aside>
|
||||
<PageContent hideMoreMenu={true} frontmatter={frontmatter} headings={headings} githubEditUrl={githubEditUrl}>
|
||||
<slot />
|
||||
</PageContent>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -319,10 +319,6 @@ Sampler effects are functions that can be used to change the behaviour of sample
|
||||
|
||||
<JsDoc client:idle name="Pattern.chop" h={0} />
|
||||
|
||||
### slice
|
||||
|
||||
<JsDoc client:idle name="Pattern.slice" h={0} />
|
||||
|
||||
### speed
|
||||
|
||||
<JsDoc client:idle name="speed" h={0} />
|
||||
|
||||
@@ -10,13 +10,20 @@ import { JsDoc } from '../../docs/JsDoc';
|
||||
|
||||
These functions use [tonaljs](https://github.com/tonaljs/tonal) to provide helpers for musical operations.
|
||||
|
||||
### voicing()
|
||||
|
||||
<JsDoc client:idle name="voicing" h={0} />
|
||||
|
||||
### scale(name)
|
||||
|
||||
<JsDoc client:idle name="scale" h={0} />
|
||||
Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like scaleTranspose.
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`"0 2 4 6 4 2"
|
||||
.scale("C2:major C2:minor").slow(2))
|
||||
.note().s("piano")`}
|
||||
/>
|
||||
|
||||
Note that the scale root is octaved here. You can also omit the octave, then index zero will default to octave 3.
|
||||
|
||||
All the available scale names can be found [here](https://github.com/tonaljs/tonal/blob/main/packages/scale-type/data.ts).
|
||||
|
||||
### transpose(semitones)
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
import ReferenceLayout from '../../layouts/ReferenceLayout.astro';
|
||||
import { docs } from '../../../../doc.json';
|
||||
import JsDoc from '../../docs/JsDoc.astro';
|
||||
|
||||
export const getSynonyms = (doc) => {
|
||||
const synonyms = doc.tags?.find((t) => t.title === 'synonyms');
|
||||
if (synonyms) {
|
||||
return synonyms.text?.split(', ');
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
export function getStaticPaths() {
|
||||
let paths: any = [];
|
||||
docs
|
||||
.filter((doc) => !!doc.name)
|
||||
.forEach((doc) => {
|
||||
paths.push({ params: { ref: doc.name } });
|
||||
const synonyms = getSynonyms(doc);
|
||||
if (synonyms) {
|
||||
synonyms.forEach((synonym) => {
|
||||
paths.push({ params: { ref: synonym } });
|
||||
});
|
||||
}
|
||||
});
|
||||
return paths;
|
||||
}
|
||||
|
||||
const { ref } = Astro.params;
|
||||
const entry = docs.find((d) => d.name === ref || getSynonyms(d).includes(ref));
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
---
|
||||
|
||||
<ReferenceLayout frontmatter={{ title: entry.name || 'Reference', description: '' }} headings={[]}>
|
||||
<h1>{entry!.name}</h1>
|
||||
<JsDoc name={entry.longname} />
|
||||
</ReferenceLayout>
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
import ReferenceLayout from '../../layouts/ReferenceLayout.astro';
|
||||
---
|
||||
|
||||
<ReferenceLayout frontmatter={{ title: 'Reference', description: 'All functions' }} headings={[]}>
|
||||
<h1>Reference</h1>
|
||||
<p>Welcome to the Reference! Pick an Item on the left</p>
|
||||
</ReferenceLayout>
|
||||
@@ -1,169 +0,0 @@
|
||||
---
|
||||
title: Understanding Pitch
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '../../docs/MiniRepl';
|
||||
import { PitchSlider } from '../../components/PitchSlider';
|
||||
import Box from '@components/Box.astro';
|
||||
|
||||
# Understanding Pitch
|
||||
|
||||
Let's learn how pitch works! The slider below controls the <span style="color:#3b82f6;">frequency</span> of an oscillator, producing a pitch:
|
||||
|
||||
{/* <PitchSlider client:load showFrequencySlider plot /> */}
|
||||
|
||||
<PitchSlider client:load showFrequencySlider min={20} max={20000} />
|
||||
|
||||
- Drag the slider to hear a pitch
|
||||
- Move the slider to change the pitch
|
||||
- Observe how the Hz number changes
|
||||
- <span className="text-red-300">Caution</span>: The higher frequencies could be disturbing for children or animals!
|
||||
|
||||
The Hz number is the frequency of the pitch you're hearing.
|
||||
The higher the frequency, the higher the pitch and vice versa.
|
||||
A pitch occurs whenever something is vibrating / oscillating at a frequency, in this case it's your speaker.
|
||||
The unit **Hz** describes how many times that oscillation happens per second.
|
||||
Our eyes are too slow to actually see the oscillation on the speaker, but we can <a href="https://www.youtube.com/watch?v=CDMBWw7OuJQ" target="_blank">see it in slow motion</a>.
|
||||
|
||||
<Box>
|
||||
|
||||
The hearing range of a newborn is said to be between 20Hz and 20000Hz.
|
||||
The upper limit decreases with age. What's your upper limit?
|
||||
|
||||
</Box>
|
||||
|
||||
In Strudel, we can play frequencies directly with the `freq` control:
|
||||
|
||||
<MiniRepl client:visible tune={`freq("200 [300,500] 400 [500,<600 670 712 670>]")`} />
|
||||
|
||||
## Frequency vs Pitch Perception
|
||||
|
||||
Maybe you have already noticed that the <span style="color:#3b82f6;">frequency slider</span> is "lopsided",
|
||||
meaning the pitch changes more in the left region and less in the right region.<br/>
|
||||
To make that more obvious, let's add a <span style="color:#eab308">pitch slider</span>
|
||||
that controls the frequency on a different scale:
|
||||
|
||||
<PitchSlider animatable plot showFrequencySlider showPitchSlider client:load />
|
||||
|
||||
Try out the buttons above to sweep through the frequency range in 2 different ways:
|
||||
|
||||
- Frequency Sweep: <span style="color:#3b82f6;">frequency rises linear</span> , <span style="color:#eab308">pitch rises logarithmic</span>
|
||||
- Pitch Sweep: <span style="color:#3b82f6;">frequency rises exponential</span> , <span style="color:#eab308">pitch rises linear</span>
|
||||
|
||||
<Box>
|
||||
|
||||
Don't be scared of these mathematical terms:
|
||||
|
||||
- "logarithmic" is just a fancy way of saying "it starts fast and slows down"
|
||||
- "exponential" is just a fancy way of saying "it starts slow and gets faster"
|
||||
|
||||
</Box>
|
||||
|
||||
Most of the time, we might want to control pitch in a way that matches our perception,
|
||||
which is what the <span style="color:#eab308">pitch slider</span> does.
|
||||
|
||||
## From Hz to Semitones
|
||||
|
||||
Because Hz does not match our perception, let's try to find a unit for pitch that matches.
|
||||
To approach that unit of pitch, let's look at how frequency behaves when it is doubled:
|
||||
|
||||
<PitchSlider client:load showPitchSlider showFrequencySlider pitchStep={1 / 7} />
|
||||
|
||||
- Use the now stepped pitch slider above
|
||||
- Can you hear how these pitches seem related to each other?
|
||||
|
||||
<Box>
|
||||
|
||||
In musical terms, a pitch with double the frequency of another is an `octave` higher.
|
||||
|
||||
</Box>
|
||||
|
||||
Because octaves are pretty far apart, octaves are typically divided into 12 smaller parts:
|
||||
|
||||
<PitchSlider client:load showPitchSlider showFrequencySlider pitchStep={1 / 12} min={440} max={880} initial={440} />
|
||||
|
||||
This step is also called a semitone, which is the most common division of pitched music.
|
||||
For example, the keys on a piano keyboard are also divided into semitones.
|
||||
|
||||
In Strudel, we could do that with `freq` like this:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`freq(
|
||||
"0 4 7 12"
|
||||
.fmap(n => 440 * 2**(n/12))
|
||||
)`}
|
||||
/>
|
||||
|
||||
Of course, this can be written shorter with note, as we will see below.
|
||||
|
||||
## From Semitones to MIDI numbers
|
||||
|
||||
Now we know what the distance of a semitone is.
|
||||
Above, we used an arbitrary base frequency of 440Hz, which means the exponent 0 is equal to 440Hz.
|
||||
Typically, 440Hz is standardized to the number 69, which leads to this calculation:
|
||||
|
||||
<PitchSlider
|
||||
client:load
|
||||
showPitchSlider
|
||||
showFrequencySlider
|
||||
baseFrequency={440}
|
||||
zeroOffset={69}
|
||||
pitchStep={1 / 12 / 7}
|
||||
min={440 / 8}
|
||||
max={7040}
|
||||
initial={440}
|
||||
/>
|
||||
|
||||
The yellow number is now a MIDI number, covering more than the whole human hearing range with numbers from 0 to 127.
|
||||
In Strudel, we can use MIDI numbers inside `note`:
|
||||
|
||||
<MiniRepl client:visible tune={`note("69 73 76 81")`} />
|
||||
|
||||
## From MIDI numbers to notes
|
||||
|
||||
In western music theory, notes are used instead of numbers.
|
||||
For each midi number, there is at least one note label:
|
||||
|
||||
<PitchSlider
|
||||
client:load
|
||||
showPitchSlider
|
||||
showFrequencySlider
|
||||
baseFrequency={440}
|
||||
zeroOffset={69}
|
||||
pitchStep={1 / 48}
|
||||
min={440 / 8}
|
||||
max={880}
|
||||
initial={440}
|
||||
claviature
|
||||
/>
|
||||
|
||||
A full note label consists of a letter (A-G), 0 or more accidentals (b | #) and an octave number.
|
||||
This system is also known as [Scientific Pitch Notation](https://en.wikipedia.org/wiki/Scientific_pitch_notation).
|
||||
In Strudel, these note labels can also be used inside `note` as an alternative to midi numbers:
|
||||
|
||||
<MiniRepl client:visible tune={`note("A4 C#5 E5 A5").piano()`} />
|
||||
|
||||
## Open Questions
|
||||
|
||||
Now that we have learned about different representations of pitch, there are still open questions:
|
||||
|
||||
- Why 12 notes? What about different divisions of the octave?
|
||||
- Why are notes labeled as they are? Why only 7 letters?
|
||||
- Are there other labeling systems?
|
||||
- What about Just Intonation Systems?
|
||||
- What about Timbre?
|
||||
|
||||
All those questions are important to ask and will be answered in another article.
|
||||
|
||||
## Definition
|
||||
|
||||
At first, I wanted to start this article with a definition, but then thought it might be a good idea to focus on intuitive exploration.
|
||||
Maybe you now understand this definition much better:
|
||||
|
||||
<Box>
|
||||
|
||||
From [wikipedia](<https://en.wikipedia.org/wiki/Pitch_(music)>): "Pitch is a perceptual property of sounds that allows their ordering on a frequency-related scale, or more commonly, pitch is the quality that makes it possible to judge sounds as "higher" and "lower" in the sense associated with musical melodies."
|
||||
|
||||
</Box>
|
||||
@@ -151,18 +151,13 @@ export function Repl({ embedded = false }) {
|
||||
// init code
|
||||
useEffect(() => {
|
||||
initCode().then((decoded) => {
|
||||
let msg;
|
||||
if (decoded) {
|
||||
setCode(decoded);
|
||||
msg = `I have loaded the code from the URL.`;
|
||||
} else if (latestCode) {
|
||||
setCode(latestCode);
|
||||
msg = `Your last session has been loaded!`;
|
||||
} /* if(randomTune) */ else {
|
||||
setCode(randomTune);
|
||||
msg = `A random code snippet named "${name}" has been loaded!`;
|
||||
}
|
||||
logger(`Welcome to Strudel! ${msg} Press play or hit ctrl+enter to run it!`, 'highlight');
|
||||
logger(
|
||||
`Welcome to Strudel! ${
|
||||
decoded ? `I have loaded the code from the URL.` : `A random code snippet named "${name}" has been loaded!`
|
||||
} Press play or hit ctrl+enter to run it!`,
|
||||
'highlight',
|
||||
);
|
||||
setCode(decoded || latestCode || randomTune);
|
||||
setPending(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
+40
-60
@@ -68,35 +68,32 @@ stack(
|
||||
`;
|
||||
|
||||
export const giantSteps = `// John Coltrane - Giant Steps
|
||||
|
||||
let melody = note(
|
||||
"[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]",
|
||||
)
|
||||
setVoicingRange('lefthand', ['E3', 'G4']);
|
||||
|
||||
stack(
|
||||
// melody
|
||||
melody.color('#F8E71C'),
|
||||
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]",
|
||||
).color('#F8E71C'),
|
||||
// chords
|
||||
chord(
|
||||
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]"
|
||||
).dict('lefthand')
|
||||
.anchor(melody).mode('duck')
|
||||
.voicing().color('#7ED321'),
|
||||
).voicings('lefthand').color('#7ED321'),
|
||||
// bass
|
||||
note(
|
||||
seq(
|
||||
"[B2 D2] [G2 Bb2] [Eb2 Bb3] [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 Bb3] [C#2 F#2]"
|
||||
).color('#00B8D4')
|
||||
).slow(20)
|
||||
.pianoroll({fold:1})`;
|
||||
).slow(20).note()
|
||||
//.pianoroll({fold:1})`;
|
||||
|
||||
export const zeldasRescue = `// Koji Kondo - Princess Zelda's Rescue
|
||||
stack(
|
||||
@@ -137,31 +134,28 @@ const drums = stack(
|
||||
s("[~ hh]*2").delay(.3).delayfeedback(.5).delaytime(.125).gain(.4)
|
||||
);
|
||||
|
||||
const thru = (x) => x.transpose("<0 1>/8").transpose(-1);
|
||||
const synths = stack(
|
||||
|
||||
"<eb4 d4 c4 b3>/2"
|
||||
.scale("<C:minor!3 C:melodic:minor>/2")
|
||||
.struct("[~ x]*2")
|
||||
.scale(timeCat([3,'C minor'],[1,'C melodic minor'])
|
||||
.slow(8)).struct("[~ x]*2")
|
||||
.layer(
|
||||
x=>x.scaleTranspose(0).early(0),
|
||||
x=>x.scaleTranspose(2).early(1/8),
|
||||
x=>x.scaleTranspose(7).early(1/4),
|
||||
x=>x.scaleTranspose(8).early(3/8)
|
||||
).note().apply(keys).mask("<~ x>/16")
|
||||
).apply(thru).note().apply(keys).mask("<~ x>/16")
|
||||
.color('darkseagreen'),
|
||||
|
||||
note("<C2 Bb1 Ab1 [G1 [G2 G1]]>/2")
|
||||
note("<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".apply(thru))
|
||||
.struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2))
|
||||
.s('sawtooth').attack(0.001).decay(0.2).sustain(1).cutoff(500)
|
||||
.color('brown'),
|
||||
chord("<Cm7 Bb7 Fm7 G7b13>/2")
|
||||
.struct("~ [x@0.2 ~]".fast(2))
|
||||
.dict('lefthand').voicing()
|
||||
.every(2, early(1/8))
|
||||
.apply(keys).sustain(0)
|
||||
"<Cm7 Bb7 Fm7 G7b13>/2".struct("~ [x@0.2 ~]".fast(2))
|
||||
.voicings('lefthand')
|
||||
.apply(thru).every(2, early(1/8)).note().apply(keys).sustain(0)
|
||||
.delay(.4).delaytime(.12)
|
||||
.mask("<x@7 ~>/8".early(1/4))
|
||||
).add(note("<-1 0>/8"))
|
||||
)
|
||||
stack(
|
||||
drums.fast(2).color('tomato'),
|
||||
synths
|
||||
@@ -269,20 +263,15 @@ export const festivalOfFingers = `// "Festival of fingers"
|
||||
|
||||
const chords = "<Cm7 Fm7 G7 F#7>";
|
||||
stack(
|
||||
chord(chords).dict('lefthand').voicing().struct("x(3,8,-1)")
|
||||
.velocity(.5).off(1/7,x=>x.add(note(12)).velocity(.2)),
|
||||
|
||||
chords.rootNotes(2).struct("x(4,8,-2)").note(),
|
||||
|
||||
chords.voicings('lefthand').struct("x(3,8,-1)").velocity(.5).off(1/7,x=>x.transpose(12).velocity(.2)),
|
||||
chords.rootNotes(2).struct("x(4,8,-2)"),
|
||||
chords.rootNotes(4)
|
||||
.scale(cat('C minor','F dorian','G dorian','F# mixolydian'))
|
||||
.struct("x(3,8,-2)".fast(2))
|
||||
.scaleTranspose("0 4 0 6".early(".125 .5")).layer(scaleTranspose("0,<2 [4,6] [5,7]>/4"))
|
||||
.note()
|
||||
|
||||
).slow(2)
|
||||
.velocity(sine.struct("x*8").add(3/5).mul(2/5).fast(8))
|
||||
.piano()`;
|
||||
.note().piano()`;
|
||||
|
||||
// iter, echo, echoWith
|
||||
export const undergroundPlumber = `// "Underground plumber"
|
||||
@@ -538,10 +527,10 @@ stack(
|
||||
.gain(.4) // turn down
|
||||
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
|
||||
//.hush()
|
||||
,chord("<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>")
|
||||
.dict('lefthand').voicing() // chords
|
||||
.add(note("0,.04")) // add second, slightly detuned voice
|
||||
.add(note(perlin.range(0,.5))) // random pitch variation
|
||||
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings('lefthand') // chords
|
||||
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
|
||||
.add(perlin.range(0,.5)) // random pitch variation
|
||||
.note() // wrap in "note"
|
||||
.s('sawtooth') // waveform
|
||||
.gain(.16) // turn down
|
||||
.cutoff(500) // fixed cutoff
|
||||
@@ -570,15 +559,14 @@ samples({
|
||||
perc: ['perc/002_perc2.wav'],
|
||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||
|
||||
chord("<C^7 Am7 Dm7 G7>*2").dict('lefthand').anchor("G4").voicing()
|
||||
.stack(n("0@6 [<1 2> <2 0> 1]@2").scale('C5 major'))
|
||||
.slow(4)
|
||||
"C^7 Am7 Dm7 G7".slow(2).voicings('lefthand')
|
||||
.stack("0@6 [<1 2> <2 0> 1]@2".scale('C5 major'))
|
||||
.note().slow(4)
|
||||
.s("gm_epiano1:1")
|
||||
.color('steelblue')
|
||||
.stack(
|
||||
n("<-7 ~@2 [~@2 -7] -9 ~@2 [~@2 -9] -10!2 ~ [~@2 -10] -5 ~ [-3 -2 -10]@2>*2")
|
||||
.scale('C3 major')
|
||||
.s('sawtooth').color('brown')
|
||||
"<-7 ~@2 [~@2 -7] -9 ~@2 [~@2 -9] -10!2 ~ [~@2 -10] -5 ~ [-3 -2 -10]@2>*2".scale('C3 major')
|
||||
.note().s('sawtooth').color('brown')
|
||||
)
|
||||
.attack(0.05).decay(.1).sustain(.7)
|
||||
.cutoff(perlin.range(800,2000))
|
||||
@@ -676,10 +664,8 @@ stack(
|
||||
s("mt lt ht").struct("x(3,8)").fast(2).gain(.5).room(.5).sometimes(x=>x.speed(".5")),
|
||||
s("misc:2").speed(1).delay(.5).delaytime(1/3).gain(.4),
|
||||
// chords
|
||||
chord("[~ Gm7] ~ [~ Dm7] ~")
|
||||
.dict('lefthand').voicing()
|
||||
.add(note("0,.1"))
|
||||
.s('sawtooth').gain(.8)
|
||||
note("[~ Gm7] ~ [~ Dm7] ~".voicings('lefthand').superimpose(x=>x.add(.1)))
|
||||
.s('sawtooth').gain(.5)
|
||||
.cutoff(perlin.range(400,3000).slow(8))
|
||||
.decay(perlin.range(0.05,.2)).sustain(0)
|
||||
.delay(.9).room(1),
|
||||
@@ -709,11 +695,7 @@ setVoicingRange('lefthand', ['c3','a4'])
|
||||
stack(
|
||||
s('bass').loopAt(8).clip(1),
|
||||
s("bd*2, ~ sd,hh*4"),
|
||||
chord("Abm7")
|
||||
.mode("below:G4")
|
||||
.dict('lefthand')
|
||||
.voicing()
|
||||
.struct("x(3,8,1)".slow(2)),
|
||||
note("Abm7".voicings('lefthand').struct("x(3,8,1)".slow(2))),
|
||||
"0 1 2 3".scale('ab4 minor pentatonic')
|
||||
.superimpose(x=>x.add(.1))
|
||||
.sometimes(x=>x.add(12))
|
||||
@@ -877,7 +859,7 @@ export const loungeSponge = `// "Lounge sponge"
|
||||
await loadOrc('github:kunstmusik/csound-live-code/master/livecode.orc')
|
||||
|
||||
stack(
|
||||
chord("<C^7 A7 Dm7 Fm7>/2").dict('lefthand').voicing()
|
||||
note("<C^7 A7 Dm7 Fm7>/2".voicings('lefthand'))
|
||||
.cutoff(sine.range(500,2000).round().slow(16))
|
||||
.euclidLegato(3,8).csound('FM1')
|
||||
,
|
||||
@@ -896,11 +878,9 @@ export const arpoon = `// "Arpoon"
|
||||
|
||||
await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
|
||||
n("[0,3] 2 [1,3] 2".fast(3).lastOf(4, fast(2))).clip(2)
|
||||
.offset("<<1 2> 2 1 1>")
|
||||
.chord("<<Am7 C^7> C7 F^7 [Fm7 E7b9]>")
|
||||
.dict('lefthand').voicing()
|
||||
.add(perlin.range(0,0.2).add("<-12 0>/8").note())
|
||||
note("<<Am7 C^7> C7 F^7 [Fm7 E7b9]>".voicings('lefthand'))
|
||||
.arp("[0,3] 2 [1,3] 2".fast(3).lastOf(4, fast(2))).clip(2)
|
||||
.add(perlin.range(0,0.2).add("<0 12>/8").note())
|
||||
.cutoff(perlin.range(500,4000)).resonance(12)
|
||||
.gain("<.5 .8>*16")
|
||||
.decay(.16).sustain(0.5)
|
||||
|
||||
Reference in New Issue
Block a user