Compare commits

..

17 Commits

Author SHA1 Message Date
Felix Roos 50f04e8cc8 fix: echoWith / echoForEach in tests 2025-09-15 22:57:18 +02:00
Felix Roos 5e3218a67b breaking: tidal style echoWith + add echoForEach 2025-09-15 22:41:51 +02:00
froos 2e97fb7be2 Merge pull request 'supradough: fix delay + add some jsdoc types' (#1578) from supradough-fixes into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1578
2025-09-15 00:00:18 +02:00
Felix Roos df1934f87b add some typechecking + fix delay 2025-09-14 23:54:51 +02:00
froos d3472feb5c Merge pull request 'added plyWith/plyWithClassic functions' (#1571) from Dsm0/strudel:add_plyWith into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1571
2025-09-14 12:42:57 +02:00
Dsm0 addb6db1c7 updated names + snapshot 2025-09-14 02:21:25 -07:00
froos 566d590453 Merge pull request 'feat: sound alias' (#1494) from Antipathie/strudel:feature/592-sound-alias into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1494
2025-09-14 10:39:11 +02:00
froos e57bd98da0 Merge branch 'main' into feature/592-sound-alias 2025-09-14 10:34:47 +02:00
Dsm0 f5c4373f99 added plyWith/plyWithClassic functions 2025-09-13 17:21:19 -07:00
froos e762291bb7 Merge pull request 'Bug Fix: Allow penv values to be falsy' (#1559) from glossing/strudel:glossing/penv-with-zeros into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1559
2025-09-14 01:20:14 +02:00
Felix Roos 9da027d377 hotfix: format 2025-09-14 01:11:34 +02:00
froos 96dccdc38b Merge pull request 'Update website/src/pages/workshop/first-sounds.mdx' (#1566) from fesmith/strudel:main into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1566
2025-09-14 01:10:47 +02:00
Felix Roos d6254294b0 fix: bring back tests on external PRs (push doesn't cut it for forks) 2025-09-14 01:09:30 +02:00
froos 339c1bb573 Merge pull request 'fix: autocomplete-styles + html rendering' (#1570) from autocomplete-styles into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1570
2025-09-14 01:07:47 +02:00
fesmith 352a3c39d7 Update website/src/pages/workshop/first-sounds.mdx 2025-09-13 00:00:36 +02:00
Aria 38ce593c60 Allow penv values to be falsy 2025-09-09 20:47:09 -05:00
Antipathie 56b1ea3fa5 Add soundAlias function 2025-09-03 00:47:45 +02:00
9 changed files with 357 additions and 51 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
name: Strudel tests
on: [push]
on: [push, pull_request]
jobs:
build:
@@ -19,7 +19,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
cache: "pnpm"
- run: pnpm install
- run: pnpm run format-check
- run: pnpm run lint
+79 -6
View File
@@ -2354,20 +2354,42 @@ export const jux = register('jux', function (func, pat) {
/**
* Superimpose and offset multiple times, applying the given function each time.
* @name echoWith
* @synonyms echowith, stutWith, stutwith
* @name echoForEach
* @param {number} times how many times to repeat
* @param {number} time cycle offset between iterations
* @param {function} func function to apply, given the pattern and the iteration index
* @example
* "<0 [2 4]>"
* .echoWith(4, 1/8, (p,n) => p.add(n*2))
* .scale("C:minor").note()
* .n().scale("C:minor")
*/
export const { echoForEach } = register(['echoForEach'], function (times, time, func, pat) {
return stack(...listRange(0, times - 1).map((i) => func(pat.late(Fraction(time).mul(i)), i)));
});
/**
* Superimpose and offset multiple times, applying the given function each time.
* @name echoWith
* @synonyms echowith, stutWith, stutwith
* @param {number} times how many times to repeat
* @param {number} time cycle offset between iterations
* @param {function} func function to apply, given the pattern and the iteration index
* @example
* "<0 [2 4]>".echoWith(4, 1/8, add(2))
* .n().scale("C:minor")
*/
export const { echoWith, echowith, stutWith, stutwith } = register(
['echoWith', 'echowith', 'stutWith', 'stutwith'],
function (times, time, func, pat) {
return stack(...listRange(0, times - 1).map((i) => func(pat.late(Fraction(time).mul(i)), i)));
if (times < 1) {
return silence;
}
let pats = pat;
for (let i = 1; i < times; i++) {
pat = func(pat.late(time));
pats = pats.stack(pat);
}
return pats;
},
);
@@ -2383,7 +2405,7 @@ export const { echoWith, echowith, stutWith, stutwith } = register(
* s("bd sd").echo(3, 1/6, .8)
*/
export const echo = register('echo', function (times, time, feedback, pat) {
return pat._echoWith(times, time, (pat, i) => pat.gain(Math.pow(feedback, i)));
return pat._echoForEach(times, time, (pat, i) => pat.gain(Math.pow(feedback, i)));
});
/**
@@ -2396,7 +2418,58 @@ export const echo = register('echo', function (times, time, feedback, pat) {
* s("bd sd").stut(3, .8, 1/6)
*/
export const stut = register('stut', function (times, feedback, time, pat) {
return pat._echoWith(times, time, (pat, i) => pat.gain(Math.pow(feedback, i)));
return pat._echoForEach(times, time, (pat, i) => pat.gain(Math.pow(feedback, i)));
});
export const applyN = register('applyN', function (n, func, p) {
let result = p;
for (let i = 0; i < n; i++) {
result = func(result);
}
return result;
});
/**
* The plyWith function repeats each event the given number of times, applying the given function to each event.\n
* @name plyWith
* @synonyms plywith
* @param {number} factor how many times to repeat
* @param {function} func function to apply, given the pattern
* @example
* "<0 [2 4]>"
* .plyWith(4, (p) => p.add(2))
* .scale("C:minor").note()
*/
export const plyWith = register(['plyWith', 'plywith'], function (factor, func, pat) {
const result = pat
.fmap((x) => cat(...listRange(0, factor - 1).map((i) => applyN(i, func, x)))._fast(factor))
.squeezeJoin();
if (__steps) {
result._steps = Fraction(factor).mulmaybe(pat._steps);
}
return result;
});
/**
* The plyForEach function repeats each event the given number of times, applying the given function to each event.
* This version of ply uses the iteration index as an argument to the function, similar to echoWith.
* @name plyForEach
* @synonyms plyforeach
* @param {number} factor how many times to repeat
* @param {function} func function to apply, given the pattern and the iteration index
* @example
* "<0 [2 4]>"
* .plyForEach(4, (p,n) => p.add(n*2))
* .scale("C:minor").note()
*/
export const plyForEach = register(['plyForEach', 'plyforeach'], function (factor, func, pat) {
const result = pat
.fmap((x) => cat(cat(pure(x), ...listRange(1, factor - 1).map((i) => func(pure(x), i))))._fast(factor))
.squeezeJoin();
if (__steps) {
result._steps = Fraction(factor).mulmaybe(pat._steps);
}
return result;
});
/**
+1 -1
View File
@@ -174,7 +174,7 @@ let curves = ['linear', 'exponential'];
export function getPitchEnvelope(param, value, t, holdEnd) {
// envelope is active when any of these values is set
const hasEnvelope = value.pattack ?? value.pdecay ?? value.psustain ?? value.prelease ?? value.penv;
if (!hasEnvelope) {
if (hasEnvelope === undefined) {
return;
}
const penv = nanFallback(value.penv, 1, true);
+13
View File
@@ -113,6 +113,19 @@ export async function aliasBank(...args) {
}
}
/**
* Register an alias for a sound.
* @param {string} original - The original sound name
* @param {string} alias - The alias to use for the sound
*/
export function soundAlias(original, alias) {
if (getSound(original) == null) {
logger('soundAlias: original sound not found');
return;
}
soundMap.setKey(alias, getSound(original));
}
export function getSound(s) {
if (typeof s !== 'string') {
console.warn(`getSound: expected string got "${s}". fall back to triangle`);
+165 -25
View File
@@ -1,4 +1,6 @@
// this is dough, the superdough without dependencies
// @ts-check
// @ts-ignore ignore next line because sampleRate is unknown
const SAMPLE_RATE = typeof sampleRate !== 'undefined' ? sampleRate : 48000;
const PI_DIV_SR = Math.PI / SAMPLE_RATE;
const ISR = 1 / SAMPLE_RATE;
@@ -641,8 +643,8 @@ const note2midi = (note, defaultOctave = 3) => {
}
const chroma = chromas[pc.toLowerCase()];
const offset = acc?.split('').reduce((o, char) => o + accs[char], 0) || 0;
oct = Number(oct || defaultOctave);
return (oct + 1) * 12 + chroma + offset;
const octave = Number(oct || defaultOctave);
return (octave + 1) * 12 + chroma + offset;
};
const midi2freq = (midi) => Math.pow(2, (midi - 69) / 12) * 440;
const note2freq = (note) => {
@@ -654,9 +656,152 @@ const note2freq = (note) => {
};
export class DoughVoice {
/** @type {number} */
id = 0;
/** @type {number[]} */
out = [0, 0];
/** @type {number | undefined} */
attack;
/** @type {number | undefined} */
decay;
/** @type {number | undefined} */
sustain;
/** @type {number} */
release;
/** @type {number} */
_begin;
/** @type {number} */
_duration;
/** @type {any} */
_sound;
/** @type {number} */
_channels = 1;
/** @type {BufferPlayer[] | undefined} */
_buffers;
/** @type {string | undefined} */
unit;
/** @type {ADSR | undefined} */
_penv;
/** @type {number | undefined} */
penv;
/** @type {number | undefined} */
pattack;
/** @type {number | undefined} */
pdecay;
/** @type {number | undefined} */
psustain;
/** @type {number | undefined} */
prelease;
/** @type {number | undefined} */
vib;
_vib;
/** @type {number | undefined} */
vibmod;
/** @type {SineOsc | undefined} */
_fm;
/** @type {number | undefined} */
fmh;
/** @type {number | undefined} */
fmi;
/** @type {ADSR | undefined} */
_fmenv;
/** @type {number | undefined} */
fmattack;
/** @type {number | undefined} */
fmdecay;
/** @type {number | undefined} */
fmsustain;
/** @type {number | undefined} */
fmrelease;
/** @type {ADSR | undefined} */
_lpenv;
lpenv;
/** @type {number | undefined} */
lpattack;
/** @type {number | undefined} */
lpdecay;
/** @type {number | undefined} */
lpsustain;
/** @type {number | undefined} */
lprelease;
/** @type {ADSR | undefined} */
_hpenv;
/** @type {number | undefined} */
hpenv;
/** @type {number | undefined} */
hpattack;
/** @type {number | undefined} */
hpdecay;
/** @type {number | undefined} */
hpsustain;
/** @type {number | undefined} */
hprelease;
/** @type {ADSR | undefined} */
_bpenv;
/** @type {number | undefined} */
bpenv;
/** @type {number | undefined} */
bpattack;
/** @type {number | undefined} */
bpdecay;
/** @type {number | undefined} */
bpsustain;
/** @type {number | undefined} */
bprelease;
/** @type {number | undefined} */
cutoff;
/** @type {number | undefined} */
hcutoff;
/** @type {number | undefined} */
bandf;
/** @type {number | undefined} */
coarse;
/** @type {number | undefined} */
crush;
/** @type {number | undefined} */
distort;
/** @type {number} */
freq;
/** @type {string | undefined} */
note;
/** @type {TwoPoleFilter[] | null | undefined} */
_lpf;
/** @type {TwoPoleFilter[] | null | undefined} */
_hpf;
/** @type {TwoPoleFilter[] | null | undefined} */
_bpf;
/** @type {Chorus[] | null | undefined} */
_chorus;
/** @type {Coarse[] | null | undefined} */
_coarse;
/** @type {Crush[] | null | undefined} */
_crush;
/** @type {Distort[] | null | undefined} */
_distort;
/**
* @param {DoughVoice} value
*/
constructor(value) {
value.freq ??= note2freq(value.note);
// mandatory controls
this.freq ??= note2freq(value.note);
this._begin = value._begin;
this._duration = value._duration;
this.release = value.release ?? 0;
// the rest.. we use $ for readability
let $ = this;
Object.assign($, value);
$.s = $.s ?? getDefaultValue('s');
@@ -773,7 +918,7 @@ export class DoughVoice {
let freq = this.freq * this.speed;
// frequency modulation
if (this._fm) {
if (this._fm && this.fmh !== undefined && this.fmi !== undefined) {
let fmi = this.fmi;
if (this._fmenv) {
const env = this._fmenv.update(t, gate, this.fmattack, this.fmdecay, this.fmsustain, this.fmrelease);
@@ -785,37 +930,31 @@ export class DoughVoice {
}
// vibrato
if (this._vib) {
if (this._vib && this.vibmod !== undefined) {
freq = freq * 2 ** ((this._vib.update(this.vib) * this.vibmod) / 12);
}
// pitch envelope
if (this._penv) {
if (this._penv && this.penv !== undefined) {
const env = this._penv.update(t, gate, this.pattack, this.pdecay, this.psustain, this.prelease);
freq = freq + env * this.penv;
}
// filters
let lpf = this.cutoff;
if (this._lpf) {
if (this._lpenv) {
const env = this._lpenv.update(t, gate, this.lpattack, this.lpdecay, this.lpsustain, this.lprelease);
lpf = this.lpenv * env * lpf + lpf;
}
if (lpf !== undefined && this._lpenv) {
const env = this._lpenv.update(t, gate, this.lpattack, this.lpdecay, this.lpsustain, this.lprelease);
lpf = this.lpenv * env * lpf + lpf;
}
let hpf = this.hcutoff;
if (this._hpf) {
if (this._hpenv) {
const env = this._hpenv.update(t, gate, this.hpattack, this.hpdecay, this.hpsustain, this.hprelease);
hpf = 2 ** this.hpenv * env * hpf + hpf;
}
if (hpf !== undefined && this._hpenv && this.hpenv !== undefined) {
const env = this._hpenv.update(t, gate, this.hpattack, this.hpdecay, this.hpsustain, this.hprelease);
hpf = 2 ** this.hpenv * env * hpf + hpf;
}
let bpf = this.bandf;
if (this._bpf) {
if (this._bpenv) {
const env = this._bpenv.update(t, gate, this.bpattack, this.bpdecay, this.bpsustain, this.bprelease);
bpf = 2 ** this.bpenv * env * bpf + bpf;
}
if (bpf !== undefined && this._bpenv && this.bpenv !== undefined) {
const env = this._bpenv.update(t, gate, this.bpattack, this.bpdecay, this.bpsustain, this.bprelease);
bpf = 2 ** this.bpenv * env * bpf + bpf;
}
// gain envelope
const env = this._adsr.update(t, gate, this.attack, this.decay, this.sustain, this.release);
@@ -891,8 +1030,8 @@ export class Dough {
this.sampleRate = sampleRate;
this.t = Math.floor(currentTime * sampleRate); // samples
// console.log('init dough', this.sampleRate, this.t);
this._delayL = new PitchDelay();
this._delayR = new PitchDelay();
this._delayL = new Delay();
this._delayR = new Delay();
}
loadSample(name, channels, sampleRate) {
BufferPlayer.samples.set(name, { channels, sampleRate });
@@ -965,8 +1104,9 @@ export class Dough {
}
}
// todo: how to change delaytime / delayfeedback from a voice?
const delayL = this._delayL.update(this.delaysend[0], this.delaytime, this.delayspeed);
const delayR = this._delayR.update(this.delaysend[1], this.delaytime, this.delayspeed);
const delayL = this._delayL.update(this.delaysend[0], this.delaytime);
const delayR = this._delayR.update(this.delaysend[1], this.delaytime);
this.delaysend[0] = delayL * this.delayfeedback;
this.delaysend[1] = delayR * this.delayfeedback;
this.out[0] += delayL;
+80 -8
View File
@@ -3301,20 +3301,28 @@ exports[`runs examples > example "echo" example index 0 1`] = `
]
`;
exports[`runs examples > example "echoForEach" example index 0 1`] = `[]`;
exports[`runs examples > example "echoWith" example index 0 1`] = `
[
"[ -3/8 ⇜ (0/1 → 1/8) | note:Bb3 ]",
"[ -1/4 ⇜ (0/1 → 1/4) | note:D4 ]",
"[ -1/8 ⇜ (0/1 → 3/8) | note:F4 ]",
"[ -1/4 ⇜ (0/1 → 1/8) ⇝ 1/4 | note:D4 ]",
"[ -1/8 ⇜ (0/1 → 1/8) ⇝ 3/8 | note:F4 ]",
"[ 0/1 → 1/1 | note:C3 ]",
"[ -1/4 ⇜ (1/8 → 1/4) | note:D4 ]",
"[ -1/8 ⇜ (1/8 → 1/4) ⇝ 3/8 | note:F4 ]",
"[ (1/8 → 1/1) ⇝ 9/8 | note:Eb3 ]",
"[ -1/8 ⇜ (1/4 → 3/8) | note:F4 ]",
"[ (1/4 → 1/1) ⇝ 5/4 | note:G3 ]",
"[ (3/8 → 1/1) ⇝ 11/8 | note:Bb3 ]",
"[ 1/8 ⇜ (1/1 → 9/8) | note:Eb3 ]",
"[ 1/4 ⇜ (1/1 → 5/4) | note:G3 ]",
"[ 3/8 ⇜ (1/1 → 11/8) | note:Bb3 ]",
"[ 1/4 ⇜ (1/1 → 9/8) ⇝ 5/4 | note:G3 ]",
"[ 3/8 ⇜ (1/1 → 9/8) ⇝ 11/8 | note:Bb3 ]",
"[ 1/1 → 3/2 | note:Eb3 ]",
"[ 1/4 ⇜ (9/8 → 5/4) | note:G3 ]",
"[ 3/8 ⇜ (9/8 → 5/4) ⇝ 11/8 | note:Bb3 ]",
"[ 9/8 → 13/8 | note:G3 ]",
"[ 3/8 ⇜ (5/4 → 11/8) | note:Bb3 ]",
"[ 5/4 → 7/4 | note:Bb3 ]",
"[ 11/8 → 15/8 | note:D4 ]",
"[ 3/2 → 2/1 | note:G3 ]",
@@ -3322,17 +3330,23 @@ exports[`runs examples > example "echoWith" example index 0 1`] = `
"[ (7/4 → 2/1) ⇝ 9/4 | note:D4 ]",
"[ (15/8 → 2/1) ⇝ 19/8 | note:F4 ]",
"[ 13/8 ⇜ (2/1 → 17/8) | note:Bb3 ]",
"[ 7/4 ⇜ (2/1 → 9/4) | note:D4 ]",
"[ 15/8 ⇜ (2/1 → 19/8) | note:F4 ]",
"[ 7/4 ⇜ (2/1 → 17/8) ⇝ 9/4 | note:D4 ]",
"[ 15/8 ⇜ (2/1 → 17/8) ⇝ 19/8 | note:F4 ]",
"[ 2/1 → 3/1 | note:C3 ]",
"[ 7/4 ⇜ (17/8 → 9/4) | note:D4 ]",
"[ 15/8 ⇜ (17/8 → 9/4) ⇝ 19/8 | note:F4 ]",
"[ (17/8 → 3/1) ⇝ 25/8 | note:Eb3 ]",
"[ 15/8 ⇜ (9/4 → 19/8) | note:F4 ]",
"[ (9/4 → 3/1) ⇝ 13/4 | note:G3 ]",
"[ (19/8 → 3/1) ⇝ 27/8 | note:Bb3 ]",
"[ 17/8 ⇜ (3/1 → 25/8) | note:Eb3 ]",
"[ 9/4 ⇜ (3/1 → 13/4) | note:G3 ]",
"[ 19/8 ⇜ (3/1 → 27/8) | note:Bb3 ]",
"[ 9/4 ⇜ (3/1 → 25/8) ⇝ 13/4 | note:G3 ]",
"[ 19/8 ⇜ (3/1 → 25/8) ⇝ 27/8 | note:Bb3 ]",
"[ 3/1 → 7/2 | note:Eb3 ]",
"[ 9/4 ⇜ (25/8 → 13/4) | note:G3 ]",
"[ 19/8 ⇜ (25/8 → 13/4) ⇝ 27/8 | note:Bb3 ]",
"[ 25/8 → 29/8 | note:G3 ]",
"[ 19/8 ⇜ (13/4 → 27/8) | note:Bb3 ]",
"[ 13/4 → 15/4 | note:Bb3 ]",
"[ 27/8 → 31/8 | note:D4 ]",
"[ 7/2 → 4/1 | note:G3 ]",
@@ -7365,6 +7379,64 @@ exports[`runs examples > example "ply" example index 0 1`] = `
]
`;
exports[`runs examples > example "plyForEach" example index 0 1`] = `
[
"[ 0/1 → 1/4 | note:C3 ]",
"[ 1/4 → 1/2 | note:Eb3 ]",
"[ 1/2 → 3/4 | note:G3 ]",
"[ 3/4 → 1/1 | note:Bb3 ]",
"[ 1/1 → 9/8 | note:Eb3 ]",
"[ 9/8 → 5/4 | note:G3 ]",
"[ 5/4 → 11/8 | note:Bb3 ]",
"[ 11/8 → 3/2 | note:D4 ]",
"[ 3/2 → 13/8 | note:G3 ]",
"[ 13/8 → 7/4 | note:Bb3 ]",
"[ 7/4 → 15/8 | note:D4 ]",
"[ 15/8 → 2/1 | note:F4 ]",
"[ 2/1 → 9/4 | note:C3 ]",
"[ 9/4 → 5/2 | note:Eb3 ]",
"[ 5/2 → 11/4 | note:G3 ]",
"[ 11/4 → 3/1 | note:Bb3 ]",
"[ 3/1 → 25/8 | note:Eb3 ]",
"[ 25/8 → 13/4 | note:G3 ]",
"[ 13/4 → 27/8 | note:Bb3 ]",
"[ 27/8 → 7/2 | note:D4 ]",
"[ 7/2 → 29/8 | note:G3 ]",
"[ 29/8 → 15/4 | note:Bb3 ]",
"[ 15/4 → 31/8 | note:D4 ]",
"[ 31/8 → 4/1 | note:F4 ]",
]
`;
exports[`runs examples > example "plyWith" example index 0 1`] = `
[
"[ 0/1 → 1/4 | note:C3 ]",
"[ 1/4 → 1/2 | note:Eb3 ]",
"[ 1/2 → 3/4 | note:G3 ]",
"[ 3/4 → 1/1 | note:Bb3 ]",
"[ 1/1 → 9/8 | note:Eb3 ]",
"[ 9/8 → 5/4 | note:G3 ]",
"[ 5/4 → 11/8 | note:Bb3 ]",
"[ 11/8 → 3/2 | note:D4 ]",
"[ 3/2 → 13/8 | note:G3 ]",
"[ 13/8 → 7/4 | note:Bb3 ]",
"[ 7/4 → 15/8 | note:D4 ]",
"[ 15/8 → 2/1 | note:F4 ]",
"[ 2/1 → 9/4 | note:C3 ]",
"[ 9/4 → 5/2 | note:Eb3 ]",
"[ 5/2 → 11/4 | note:G3 ]",
"[ 11/4 → 3/1 | note:Bb3 ]",
"[ 3/1 → 25/8 | note:Eb3 ]",
"[ 25/8 → 13/4 | note:G3 ]",
"[ 13/4 → 27/8 | note:Bb3 ]",
"[ 27/8 → 7/2 | note:D4 ]",
"[ 7/2 → 29/8 | note:G3 ]",
"[ 29/8 → 15/4 | note:Bb3 ]",
"[ 15/4 → 31/8 | note:D4 ]",
"[ 31/8 → 4/1 | note:F4 ]",
]
`;
exports[`runs examples > example "polymeter" example index 0 1`] = `
[
"[ 0/1 → 1/6 | note:c ]",
+8
View File
@@ -59,6 +59,14 @@ Furthermore, strudel also loads instrument samples from [VCSL](https://github.co
To see which sample names are available, open the `sounds` tab in the [REPL](https://strudel.cc/).
You can also create custom aliases for existing sounds using the `soundAlias` function:
<MiniRepl
client:idle
tune={`soundAlias("RolandTR808_bd", "kick")
s("kick")`}
/>
Note that only the sample maps (mapping names to URLs) are loaded initially, while the audio samples themselves are not loaded until they are actually played.
This behaviour of loading things only when they are needed is also called `lazy loading`.
While it saves resources, it can also lead to sounds not being audible the first time they are triggered, because the sound is still loading.
+5 -5
View File
@@ -262,14 +262,14 @@ It is quite common that there are many ways to express the same idea.
**selecting sample numbers separately**
Instead of using ":", we can also use the `n` function to select sample numbers:
<MiniRepl client:visible tune={`n("0 1 [4 2] 3*2").sound("jazz")`} punchcard />
This is shorter and more readable than:
Instead of selecting sample numbers one by one:
<MiniRepl client:visible tune={`sound("jazz:0 jazz:1 [jazz:4 jazz:2] jazz:3*2")`} punchcard />
We can also use the `n` function to make it shorter and more readable:
<MiniRepl client:visible tune={`n("0 1 [4 2] 3*2").sound("jazz")`} punchcard />
## Recap
Now we've learned the basics of the so called Mini-Notation, the rhythm language of Tidal.
+4 -4
View File
@@ -285,7 +285,7 @@ stack(
.mul(gain(sine.struct("x*8").add(3/5).mul(2/5).fast(8)))
.piano()`;
// iter, echo, echoWith
// iter, echo, echoForEach
export const undergroundPlumber = `// "Underground plumber"
// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// @by Felix Roos
@@ -307,7 +307,7 @@ stack(
.cutoff(400).decay(.12).sustain(0)
,
"[g2,[c3 eb3]]".iter(4)
.echoWith(4, 1/8, (x,n)=>x.transpose(n*12).gain(Math.pow(.4,n)))
.echoForEach(4, 1/8, (x,n)=>x.transpose(n*12).gain(Math.pow(.4,n)))
.note().layer(h)
.clip(.1)
)
@@ -432,7 +432,7 @@ export const festivalOfFingers3 = `// "Festival of fingers 3"
setcps(1)
n("[-7*3],0,2,6,[8 7]")
.echoWith(
.echoForEach(
4, // echo 4 times
1/4, // 1/4s between echos
(x,i)=>x
@@ -485,7 +485,7 @@ stack(
.decay(.1).sustain(0) // make notes short
.s('triangle') // waveform
.degradeBy(perlin.range(0,.5)) // randomly controlled random removal :)
.echoWith(4,.125,(x,n)=>x.gain(.15*1/(n+1))) // echo notes
.echoForEach(4,.125,(x,n)=>x.gain(.15*1/(n+1))) // echo notes
//.hush()
)
.slow(3/2)`;