From 3582ae9163e2c95bb7babe6fe4df4ebba44a65f7 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sun, 10 Sep 2023 18:34:21 +0200 Subject: [PATCH 01/13] adding loopBegin and loopEnd --- packages/core/controls.mjs | 2 ++ packages/superdough/sampler.mjs | 17 +++++++---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 71f584275..2b99bdb3b 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -882,6 +882,8 @@ const generic_params = [ ['zdelay'], ['tremolo'], ['zzfx'], + ['loopBegin'], + ['loopEnd'], ]; // TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13 diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index 02e5eada7..60e615349 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -207,7 +207,9 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { n = 0, note, speed = 1, // sample playback speed + loopBegin = 0, begin = 0, + loopEnd = 1, end = 1, } = value; // load sample @@ -242,19 +244,14 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { // rather than the current playback rate, so even if the sound is playing at twice its normal speed, // the midway point through a 10-second audio buffer is still 5." const offset = begin * bufferSource.buffer.duration; - bufferSource.start(time, offset); const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value; - /*if (loop) { - // TODO: idea for loopBegin / loopEnd - // if one of [loopBegin,loopEnd] is <= 1, interpret it as normlized - // if [loopBegin,loopEnd] is bigger >= 1, interpret it as sample number - // this will simplify perfectly looping things, while still keeping the normalized option - // the only drawback is that looping between samples 0 and 1 is not possible (which is not real use case) + if (loop) { bufferSource.loop = true; - bufferSource.loopStart = offset; - bufferSource.loopEnd = offset + duration; + bufferSource.loopStart = loopBegin * bufferDuration - offset; + bufferSource.loopEnd = loopEnd * bufferDuration - offset; duration = loop * duration; - }*/ + } + bufferSource.start(time, offset); const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t); bufferSource.connect(envelope); const out = ac.createGain(); // we need a separate gain for the cutgroups because firefox... From bac8594c5f1db32d618696c1dacf41bb88a33fb4 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sun, 10 Sep 2023 20:36:09 +0200 Subject: [PATCH 02/13] beginning documentation work (breaks) --- packages/core/controls.mjs | 39 ++++++++++++++++------- test/__snapshots__/examples.test.mjs.snap | 18 +++++++++++ website/src/pages/learn/synths.mdx | 11 +++++++ 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 2b99bdb3b..4d427da36 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -309,20 +309,24 @@ const generic_params = [ * */ ['loop'], - // TODO: currently duplicated with "native" legato - // TODO: superdirt legato will do more: https://youtu.be/dQPmE1WaD1k?t=419 /** - * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. + * @name loopBegin + * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample + * @synonyms loopb + * @example + * s("numbers").loopBegin("<0 .25 .5 .75>").loop(1) * - * @name legato - * @param {number | Pattern} duration between 0 and 1, where 1 is the length of the whole hap time - * @noAutocomplete + */ + ['loopBegin', 'loopb'], + /** + * @name loopEnd + * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample + * @synonyms loope * @example - * "c4 eb4 g4 bb4".legato("<0.125 .25 .5 .75 1 2 4>") + * s("numbers").loopEnd("<0 .25 .5 .75>").loop(1) * */ - // ['legato'], - // ['clhatdecay'], + ['loopEnd', 'loope'], /** * bit crusher effect. * @@ -332,6 +336,20 @@ const generic_params = [ * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") * */ + // TODO: currently duplicated with "native" legato + // TODO: superdirt legato will do more: https://youtu.be/dQPmE1WaD1k?t=419 + /** + * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. + * + * @name legato + * @param {number | Pattern} duration between 0 and 1, where 1 is the length of the whole hap time + * @noAutocomplete + * @example + * "c4 eb4 g4 bb4".legato("<0.125 .25 .5 .75 1 2 4>") + * + */ + // ['legato'], + // ['clhatdecay'], ['crush'], /** * fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers @@ -343,7 +361,6 @@ const generic_params = [ * */ ['coarse'], - /** * choose the channel the pattern is sent to in superdirt * @@ -882,8 +899,6 @@ const generic_params = [ ['zdelay'], ['tremolo'], ['zzfx'], - ['loopBegin'], - ['loopEnd'], ]; // TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13 diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 60b39c53d..3e2d81bd1 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -2566,6 +2566,24 @@ exports[`runs examples > example "loopAtCps" example index 0 1`] = ` ] `; +exports[`runs examples > example "loopBegin" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | s:numbers loopBegin:0 loop:1 ]", + "[ 1/1 → 2/1 | s:numbers loopBegin:0.25 loop:1 ]", + "[ 2/1 → 3/1 | s:numbers loopBegin:0.5 loop:1 ]", + "[ 3/1 → 4/1 | s:numbers loopBegin:0.75 loop:1 ]", +] +`; + +exports[`runs examples > example "loopEnd" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | s:numbers loopEnd:0 loop:1 ]", + "[ 1/1 → 2/1 | s:numbers loopEnd:0.25 loop:1 ]", + "[ 2/1 → 3/1 | s:numbers loopEnd:0.5 loop:1 ]", + "[ 3/1 → 4/1 | s:numbers loopEnd:0.75 loop:1 ]", +] +`; + exports[`runs examples > example "lpf" example index 0 1`] = ` [ "[ 0/1 → 1/3 | s:hh cutoff:4000 ]", diff --git a/website/src/pages/learn/synths.mdx b/website/src/pages/learn/synths.mdx index b24726cfb..6a2d73950 100644 --- a/website/src/pages/learn/synths.mdx +++ b/website/src/pages/learn/synths.mdx @@ -78,6 +78,17 @@ You can use fm with any of the above waveforms, although the below examples all +## Wavetable Synthesis + +Strudel can also use wavetables to create custom waveforms instead of the default ones used by WebAudio. There is a default set of wavetables accessible by default (approximatively 1000 coming from the [AKWF](https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/) set) but you can also import/use your own. A wavetable is a one-cycle waveform, which is then repeated to create a sound at the desired frequency. It is a classic but very effective synthesis technique. Wavetable synthesis is based on sample looping, using the `loop`, `loopBegin` and `loopEnd` properties of the sampler: + +- `loop`: either `0` (no loop) or `1` (loop) +- `loopBegin`: start of the loop (between 0 and 1) +- `loopEnd`: end of the loop (between 0 and 1) + + + + ## ZZFX The "Zuper Zmall Zound Zynth" [ZZFX](https://github.com/KilledByAPixel/ZzFX) is also integrated in strudel. From f09b89ed6320c3364b44aa60c9ec00a9efae4c72 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 10 Sep 2023 20:55:59 +0200 Subject: [PATCH 03/13] fix: duration for loops --- packages/superdough/sampler.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index 60e615349..139442995 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -262,7 +262,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { out.disconnect(); onended(); }; - const stop = (endTime, playWholeBuffer = clip === undefined) => { + const stop = (endTime, playWholeBuffer = clip === undefined && loop === undefined) => { let releaseTime = endTime; if (playWholeBuffer) { releaseTime = t + (end - begin) * bufferDuration; From ba27f2ba28a61fa4914ad394cc1e4c22946e000c Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 10 Sep 2023 21:05:57 +0200 Subject: [PATCH 04/13] fix: looped samples pitch --- packages/superdough/sampler.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index 139442995..26611c0a9 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -244,11 +244,10 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { // rather than the current playback rate, so even if the sound is playing at twice its normal speed, // the midway point through a 10-second audio buffer is still 5." const offset = begin * bufferSource.buffer.duration; - const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value; if (loop) { bufferSource.loop = true; - bufferSource.loopStart = loopBegin * bufferDuration - offset; - bufferSource.loopEnd = loopEnd * bufferDuration - offset; + bufferSource.loopStart = loopBegin * bufferSource.buffer.duration - offset; + bufferSource.loopEnd = loopEnd * bufferSource.buffer.duration - offset; duration = loop * duration; } bufferSource.start(time, offset); @@ -265,6 +264,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { const stop = (endTime, playWholeBuffer = clip === undefined && loop === undefined) => { let releaseTime = endTime; if (playWholeBuffer) { + const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value; releaseTime = t + (end - begin) * bufferDuration; } bufferSource.stop(releaseTime + release); From 6de04f77817c4ed739ec580ef3b1d638a790490e Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sun, 10 Sep 2023 23:29:09 +0200 Subject: [PATCH 05/13] turn loop on for samples starting with wt_ --- packages/superdough/sampler.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index 26611c0a9..28f090829 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -196,7 +196,7 @@ export const samples = async (sampleMap, baseUrl = sampleMap._base || '', option const cutGroups = []; export async function onTriggerSample(t, value, onended, bank, resolveUrl) { - const { + let { s, freq, unit, @@ -217,6 +217,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { // no playback return; } + loop = s.startsWith('wt_') ? 1 : value.loop; const ac = getAudioContext(); // destructure adsr here, because the default should be different for synths and samples const { attack = 0.001, decay = 0.001, sustain = 1, release = 0.001 } = value; From 7a74189771f2d14ab584275d9423f158ebb4d1d9 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Mon, 11 Sep 2023 00:41:55 +0200 Subject: [PATCH 06/13] document wavetable synthesis --- website/src/pages/learn/synths.mdx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/website/src/pages/learn/synths.mdx b/website/src/pages/learn/synths.mdx index 6a2d73950..26e944673 100644 --- a/website/src/pages/learn/synths.mdx +++ b/website/src/pages/learn/synths.mdx @@ -80,14 +80,20 @@ You can use fm with any of the above waveforms, although the below examples all ## Wavetable Synthesis -Strudel can also use wavetables to create custom waveforms instead of the default ones used by WebAudio. There is a default set of wavetables accessible by default (approximatively 1000 coming from the [AKWF](https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/) set) but you can also import/use your own. A wavetable is a one-cycle waveform, which is then repeated to create a sound at the desired frequency. It is a classic but very effective synthesis technique. Wavetable synthesis is based on sample looping, using the `loop`, `loopBegin` and `loopEnd` properties of the sampler: +Strudel can also use the sampler to load custom waveforms as a replacement of the default waveforms used by WebAudio for the base synth. A default set of more than 1000 wavetables is accessible by default (coming from the [AKWF](https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/) set). You can also import/use your own. A wavetable is a one-cycle waveform, which is then repeated to create a sound at the desired frequency. It is a classic but very effective synthesis technique. -- `loop`: either `0` (no loop) or `1` (loop) -- `loopBegin`: start of the loop (between 0 and 1) -- `loopEnd`: end of the loop (between 0 and 1) +Any sample preceded by the `wt_` prefix will be loaded as a wavetable. This means that the `loop` argument will be set to `1` by defalt. You can scan over the wavetable by using `loopBegin` and `loopEnd` as well. - - +") +.n("<1 2 3 4 5 6 7 8 9 10>/2").room(0.5).size(0.9) +.s('wt_flute').velocity(0.25).often(n => n.ply(2)) +.release(0.125).decay("<0.1 0.25 0.3 0.4>").sustain(0) +.cutoff(2000).scope({}).cutoff("<1000 2000 4000>").fast(2)`} +/> ## ZZFX From 843d9d52c33b33b6d4b3f556ee325e629b28c9ba Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Mon, 11 Sep 2023 00:46:31 +0200 Subject: [PATCH 07/13] add documentation but it crashes --- packages/core/controls.mjs | 13 +++++++++---- website/src/pages/learn/samples.mdx | 12 ++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 4d427da36..a490463de 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -314,8 +314,10 @@ const generic_params = [ * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample * @synonyms loopb * @example - * s("numbers").loopBegin("<0 .25 .5 .75>").loop(1) - * + * s("numbers") + * .loop(1) + * .begin(0).end(1) + * .loopBegin("<0 .25 .5 .75>") */ ['loopBegin', 'loopb'], /** @@ -323,8 +325,11 @@ const generic_params = [ * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample * @synonyms loope * @example - * s("numbers").loopEnd("<0 .25 .5 .75>").loop(1) - * + * s("numbers") + * .loop(1) + * .begin(0).end(1) + * .loopBegin("<0 .25 .5 .75>") + * .loopEnd("<0.1 .35 .6 .85>") */ ['loopEnd', 'loope'], /** diff --git a/website/src/pages/learn/samples.mdx b/website/src/pages/learn/samples.mdx index 9f79e54a5..b1ef1cd30 100644 --- a/website/src/pages/learn/samples.mdx +++ b/website/src/pages/learn/samples.mdx @@ -303,6 +303,18 @@ Sampler effects are functions that can be used to change the behaviour of sample +### loop + + + +### loopBegin + + + +### loopEnd + + + ### cut From 4a65113bff85ee36dba1511c2f17e9015496aaea Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 11 Sep 2023 00:52:49 +0200 Subject: [PATCH 08/13] do not panic when no description --- website/src/docs/JsDoc.jsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/website/src/docs/JsDoc.jsx b/website/src/docs/JsDoc.jsx index 88a775a5f..1ba80e0a9 100644 --- a/website/src/docs/JsDoc.jsx +++ b/website/src/docs/JsDoc.jsx @@ -12,10 +12,11 @@ export function JsDoc({ name, h = 3, hideDescription, punchcard, canvasHeight }) } const synonyms = getTag('synonyms', item)?.split(', ') || []; const CustomHeading = `h${h}`; - const description = item.description.replaceAll(/\{@link ([a-zA-Z\.]+)?#?([a-zA-Z]*)\}/g, (_, a, b) => { - // console.log(_, 'a', a, 'b', b); - return `${a}${b ? `#${b}` : ''}`; - }); + const description = + item.description?.replaceAll(/\{@link ([a-zA-Z\.]+)?#?([a-zA-Z]*)\}/g, (_, a, b) => { + // console.log(_, 'a', a, 'b', b); + return `${a}${b ? `#${b}` : ''}`; + }) || ''; return ( <> {!!h && {item.longname}} From 78adaaedef56b45095cdce922292989980838cb2 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Mon, 11 Sep 2023 00:56:43 +0200 Subject: [PATCH 09/13] fixing minor bugs and adding description --- packages/core/controls.mjs | 7 +++++++ packages/superdough/sampler.mjs | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index a490463de..42f98a179 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -310,6 +310,9 @@ const generic_params = [ */ ['loop'], /** + * Begin to loop at a specific point in the sample (inbetween `begin` and `end`). + * Note that the loop point must be inbetween `begin` and `end`, and before `loopEnd`! + * * @name loopBegin * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample * @synonyms loopb @@ -321,6 +324,10 @@ const generic_params = [ */ ['loopBegin', 'loopb'], /** + * + * End the looping section at a specific point in the sample (inbetween `begin` and `end`). + * Note that the loop point must be inbetween `begin` and `end`, and after `loopBegin`! + * * @name loopEnd * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample * @synonyms loope diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index 28f090829..76b6a542c 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -249,7 +249,6 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { bufferSource.loop = true; bufferSource.loopStart = loopBegin * bufferSource.buffer.duration - offset; bufferSource.loopEnd = loopEnd * bufferSource.buffer.duration - offset; - duration = loop * duration; } bufferSource.start(time, offset); const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t); From 2deafe214a6c060decdeee0e36830b2f3e8972b5 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 17 Sep 2023 07:59:55 +0200 Subject: [PATCH 10/13] update loop examples --- packages/core/controls.mjs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index b54170c19..0c166e111 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -299,13 +299,14 @@ const generic_params = [ */ ['end'], /** - * Loops the sample (from `begin` to `end`) the specified number of times. + * Loops the sample. * Note that the tempo of the loop is not synced with the cycle tempo. + * To change the loop region, use loopBegin / loopEnd. * * @name loop - * @param {number | Pattern} times How often the sample is looped + * @param {number | Pattern} on If 1, the sample is looped * @example - * s("bd").loop("<1 2 3 4>").osc() + * s("casio").loop(1) * */ ['loop'], @@ -314,13 +315,11 @@ const generic_params = [ * Note that the loop point must be inbetween `begin` and `end`, and before `loopEnd`! * * @name loopBegin - * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample + * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample * @synonyms loopb * @example - * s("numbers") - * .loop(1) - * .begin(0).end(1) - * .loopBegin("<0 .25 .5 .75>") + * s("space").loop(1) + * .loopBegin("<0 .125 .25>").scope() */ ['loopBegin', 'loopb'], /** @@ -329,14 +328,11 @@ const generic_params = [ * Note that the loop point must be inbetween `begin` and `end`, and after `loopBegin`! * * @name loopEnd - * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample + * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample * @synonyms loope * @example - * s("numbers") - * .loop(1) - * .begin(0).end(1) - * .loopBegin("<0 .25 .5 .75>") - * .loopEnd("<0.1 .35 .6 .85>") + * s("space").loop(1) + * .loopEnd("<1 .75 .5 .25>").scope() */ ['loopEnd', 'loope'], /** From b98e24fabf77b8acaa1567485224f53fff6b1c8d Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 17 Sep 2023 08:02:02 +0200 Subject: [PATCH 11/13] docs: fit + splice --- packages/core/pattern.mjs | 19 +++++++++++++------ website/src/pages/learn/samples.mdx | 8 ++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 7e694f498..e3efb427b 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2273,14 +2273,14 @@ export const slice = register( false, // turns off auto-patternification ); -/* +/** * Works the same as slice, but changes the playback speed of each slice to match the duration of its step. * @name splice - * @memberof Pattern - * @returns Pattern * @example * await samples('github:tidalcycles/Dirt-Samples/master') - * s("breaks165").splice(8, "0 1 [2 3 0]@2 3 0@2 7").hurry(0.65) + * s("breaks165") + * .splice(8, "0 1 [2 3 0]@2 3 0@2 7") + * .hurry(0.65) */ export const splice = register( @@ -2307,9 +2307,16 @@ 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. +// the fit 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 - +/** + * Makes the sample fit its event duration. Good for rhythmical loops like drum breaks. + * Similar to loopAt. + * @name fit + * @example + * samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' }) + * s("rhodes/4").fit() + */ export const fit = register('fit', (pat) => pat.withHap((hap) => hap.withValue((v) => ({ diff --git a/website/src/pages/learn/samples.mdx b/website/src/pages/learn/samples.mdx index b1ef1cd30..10d8c7306 100644 --- a/website/src/pages/learn/samples.mdx +++ b/website/src/pages/learn/samples.mdx @@ -327,6 +327,10 @@ Sampler effects are functions that can be used to change the behaviour of sample +### fit + + + ### chop @@ -335,6 +339,10 @@ Sampler effects are functions that can be used to change the behaviour of sample +### splice + + + ### speed From 4490681fa375fb692d27043e302560bcfbd985cc Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 17 Sep 2023 08:03:09 +0200 Subject: [PATCH 12/13] add note about wt_ --- packages/core/controls.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 0c166e111..f988f0a73 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -313,6 +313,7 @@ const generic_params = [ /** * Begin to loop at a specific point in the sample (inbetween `begin` and `end`). * Note that the loop point must be inbetween `begin` and `end`, and before `loopEnd`! + * Note: Samples starting with wt_ will automatically loop! (wt = wavetable) * * @name loopBegin * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample From 3879b01ddd55f3674d0c068478b9829f8ce918a7 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 17 Sep 2023 08:04:02 +0200 Subject: [PATCH 13/13] snapshots --- test/__snapshots__/examples.test.mjs.snap | 63 ++++++++++++++++++----- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index ad0ce7b96..48835a481 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1828,6 +1828,15 @@ exports[`runs examples > example "firstOf" example index 0 1`] = ` ] `; +exports[`runs examples > example "fit" example index 0 1`] = ` +[ + "[ (0/1 → 1/1) ⇝ 4/1 | s:rhodes speed:0.25 unit:c ]", + "[ 0/1 ⇜ (1/1 → 2/1) ⇝ 4/1 | s:rhodes speed:0.25 unit:c ]", + "[ 0/1 ⇜ (2/1 → 3/1) ⇝ 4/1 | s:rhodes speed:0.25 unit:c ]", + "[ 0/1 ⇜ (3/1 → 4/1) | s:rhodes speed:0.25 unit:c ]", +] +`; + exports[`runs examples > example "floor" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:42 ]", @@ -2640,10 +2649,10 @@ exports[`runs examples > example "linger" example index 0 1`] = ` exports[`runs examples > example "loop" example index 0 1`] = ` [ - "[ 0/1 → 1/1 | s:bd loop:1 ]", - "[ 1/1 → 2/1 | s:bd loop:2 ]", - "[ 2/1 → 3/1 | s:bd loop:3 ]", - "[ 3/1 → 4/1 | s:bd loop:4 ]", + "[ 0/1 → 1/1 | s:casio loop:1 ]", + "[ 1/1 → 2/1 | s:casio loop:1 ]", + "[ 2/1 → 3/1 | s:casio loop:1 ]", + "[ 3/1 → 4/1 | s:casio loop:1 ]", ] `; @@ -2667,19 +2676,19 @@ exports[`runs examples > example "loopAtCps" example index 0 1`] = ` exports[`runs examples > example "loopBegin" example index 0 1`] = ` [ - "[ 0/1 → 1/1 | s:numbers loop:1 begin:0 end:1 loopBegin:0 ]", - "[ 1/1 → 2/1 | s:numbers loop:1 begin:0 end:1 loopBegin:0.25 ]", - "[ 2/1 → 3/1 | s:numbers loop:1 begin:0 end:1 loopBegin:0.5 ]", - "[ 3/1 → 4/1 | s:numbers loop:1 begin:0 end:1 loopBegin:0.75 ]", + "[ 0/1 → 1/1 | s:space loop:1 loopBegin:0 analyze:1 ]", + "[ 1/1 → 2/1 | s:space loop:1 loopBegin:0.125 analyze:1 ]", + "[ 2/1 → 3/1 | s:space loop:1 loopBegin:0.25 analyze:1 ]", + "[ 3/1 → 4/1 | s:space loop:1 loopBegin:0 analyze:1 ]", ] `; exports[`runs examples > example "loopEnd" example index 0 1`] = ` [ - "[ 0/1 → 1/1 | s:numbers loop:1 begin:0 end:1 loopBegin:0 loopEnd:0.1 ]", - "[ 1/1 → 2/1 | s:numbers loop:1 begin:0 end:1 loopBegin:0.25 loopEnd:0.35 ]", - "[ 2/1 → 3/1 | s:numbers loop:1 begin:0 end:1 loopBegin:0.5 loopEnd:0.6 ]", - "[ 3/1 → 4/1 | s:numbers loop:1 begin:0 end:1 loopBegin:0.75 loopEnd:0.85 ]", + "[ 0/1 → 1/1 | s:space loop:1 loopEnd:1 analyze:1 ]", + "[ 1/1 → 2/1 | s:space loop:1 loopEnd:0.75 analyze:1 ]", + "[ 2/1 → 3/1 | s:space loop:1 loopEnd:0.5 analyze:1 ]", + "[ 3/1 → 4/1 | s:space loop:1 loopEnd:0.25 analyze:1 ]", ] `; @@ -4341,6 +4350,36 @@ exports[`runs examples > example "speed" example index 1 1`] = ` ] `; +exports[`runs examples > example "splice" example index 0 1`] = ` +[ + "[ 0/1 → 5/26 | speed:0.65 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ 5/26 → 5/13 | speed:0.65 unit:c begin:0.125 end:0.25 _slices:8 s:breaks165 ]", + "[ 5/13 → 20/39 | speed:0.9750000000000001 unit:c begin:0.25 end:0.375 _slices:8 s:breaks165 ]", + "[ 20/39 → 25/39 | speed:0.9750000000000001 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]", + "[ 25/39 → 10/13 | speed:0.9750000000000001 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ 10/13 → 25/26 | speed:0.65 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]", + "[ (25/26 → 1/1) ⇝ 35/26 | speed:0.325 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ 25/26 ⇜ (1/1 → 35/26) | speed:0.325 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ 35/26 → 20/13 | speed:0.65 unit:c begin:0.875 end:1 _slices:8 s:breaks165 ]", + "[ 20/13 → 45/26 | speed:0.65 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ 45/26 → 25/13 | speed:0.65 unit:c begin:0.125 end:0.25 _slices:8 s:breaks165 ]", + "[ (25/13 → 2/1) ⇝ 80/39 | speed:0.9750000000000001 unit:c begin:0.25 end:0.375 _slices:8 s:breaks165 ]", + "[ 25/13 ⇜ (2/1 → 80/39) | speed:0.9750000000000001 unit:c begin:0.25 end:0.375 _slices:8 s:breaks165 ]", + "[ 80/39 → 85/39 | speed:0.9750000000000001 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]", + "[ 85/39 → 30/13 | speed:0.9750000000000001 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ 30/13 → 5/2 | speed:0.65 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]", + "[ 5/2 → 75/26 | speed:0.325 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ (75/26 → 3/1) ⇝ 40/13 | speed:0.65 unit:c begin:0.875 end:1 _slices:8 s:breaks165 ]", + "[ 75/26 ⇜ (3/1 → 40/13) | speed:0.65 unit:c begin:0.875 end:1 _slices:8 s:breaks165 ]", + "[ 40/13 → 85/26 | speed:0.65 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ 85/26 → 45/13 | speed:0.65 unit:c begin:0.125 end:0.25 _slices:8 s:breaks165 ]", + "[ 45/13 → 140/39 | speed:0.9750000000000001 unit:c begin:0.25 end:0.375 _slices:8 s:breaks165 ]", + "[ 140/39 → 145/39 | speed:0.9750000000000001 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]", + "[ 145/39 → 50/13 | speed:0.9750000000000001 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ (50/13 → 4/1) ⇝ 105/26 | speed:0.65 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]", +] +`; + exports[`runs examples > example "square" example index 0 1`] = ` [ "[ 0/1 → 1/2 | note:C3 ]",