diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f455741f5..a7f65b5bf 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -150,6 +150,7 @@ Important: Always publish with `pnpm`, as `npm` does not support overriding main
## useful commands
+
```sh
#regenerate the test snapshots (ex: when updating or creating new pattern functions)
pnpm snapshot
@@ -160,6 +161,81 @@ pnpm run osc
#build the standalone version
pnpm tauri build
```
+
+## version tag patching
+
+here's a little guide on how to patch patterns in the database to prevent breaking old patterns due to breaking changes in newer versions.
+
+the general tactic is to use `// @version x.y` to tag a pattern with a specific strudel version. when a pattern is evaluated, this metadata will de-activate any breaking changes that came after the specified version.
+for example, in version 1.1, the default value for `fanchor` was changed from `0.5` to `0`.
+if play a pattern that was made before that change, sounds that use filter evenlopes can sound very different, so by adding `// @version 1.0` will make it sound like it used to.
+before releasing a new version with breaking changes, we can edit all patterns in the database, inserting the version tag they were created under:
+
+as an example, to release version 1.2, do the following:
+
+1. get date range
+
+```sh
+# get date of last version:
+git log -1 --format=%aI @strudel/core@1.1.0
+# 2024-05-31T23:07:26+02:00
+
+# get date of current version:
+git log -1 --format=%aI @strudel/core@1.2.0
+# 2025-05-01T12:39:24+02:00
+# might also use todays timestamp if version is not yet released
+```
+
+now we know, all patterns between these 2 dates have to receive a version tag (unless they already have one).
+
+2. get patterns in question
+
+```sql
+SELECT *
+FROM code_v1
+WHERE code NOT LIKE '%@version%'
+AND created_at > '2024-05-31T23:07:26+02:00'
+AND created_at < '2025-05-01T12:39:24+02:00'
+ORDER BY created_at ASC;
+```
+
+this gives us all unversioned patterns that were saved between 1.1.0 and 1.2.0. in this case, it's 9373 patterns!
+
+3. insert version tags
+
+we are now ready to insert the version tag to these patterns.
+before updating thousands of patterns, it's probably a good idea to test if a single one gets udpated:
+
+```sql
+UPDATE code_v1
+SET code = code || E'\n// @version 1.1'
+WHERE hash = 'Ns2sMB40yIw4';
+```
+
+after [verifying](https://strudel.cc/?Ns2sMB40yIw4) that the version tag has been added, let's insert it everywhere:
+
+```sql
+UPDATE code_v1
+SET code = code || E'\n// @version 1.1'
+WHERE code NOT LIKE '%@version%'
+AND created_at > '2024-05-31T23:07:26+02:00'
+AND created_at < '2025-05-01T12:39:24+02:00'
+```
+
+4. verify
+
+we can verify that the edits worked by querying all patterns that contain the new version tag:
+
+```sql
+SELECT *
+FROM code_v1
+WHERE code LIKE '%@version 1.1%'
+AND created_at > '2024-05-31T23:07:26+02:00'
+AND created_at < '2025-05-01T12:39:24+02:00'
+ORDER BY created_at ASC;
+```
+
+
## Have Fun
Remember to have fun, and that this project is driven by the passion of volunteers!
diff --git a/README.md b/README.md
index d54302ac6..4aa90fd38 100644
--- a/README.md
+++ b/README.md
@@ -38,13 +38,7 @@ Licensing info for the default sound banks can be found over on the [dough-sampl
## Contributing
-There are many ways to contribute to this project! See [contribution guide](./CONTRIBUTING.md).
-
-
-
-
-
-Made with [contrib.rocks](https://contrib.rocks).
+There are many ways to contribute to this project! See [contribution guide](./CONTRIBUTING.md). You can find the full list of contributors [here](https://codeberg.org/uzu/strudel/activity/contributors).
## Community
diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs
index c933e7fe2..6bd6fde67 100644
--- a/packages/core/signal.mjs
+++ b/packages/core/signal.mjs
@@ -264,7 +264,7 @@ export const randrun = (n) => {
const rands = timeToRands(t.floor().add(0.5), n);
const nums = rands
.map((n, i) => [n, i])
- .sort((a, b) => a[0] > b[0] - a[0] < b[0])
+ .sort((a, b) => (a[0] > b[0]) - (a[0] < b[0]))
.map((x) => x[1]);
const i = t.cyclePos().mul(n).floor() % n;
return nums[i];
diff --git a/packages/soundfonts/gm.mjs b/packages/soundfonts/gm.mjs
index c0e57dcb0..d8b7687d7 100644
--- a/packages/soundfonts/gm.mjs
+++ b/packages/soundfonts/gm.mjs
@@ -537,7 +537,7 @@ export default {
],
gm_synth_bass_1: [
// Synth Bass 1: Bass
- '0380_Aspirin_sf2_file',
+ // '0380_Aspirin_sf2_file', // broken in safari https://codeberg.org/uzu/strudel/issues/1384
'0380_Chaos_sf2_file',
'0380_FluidR3_GM_sf2_file',
// 0380_GeneralUserGS_sf2_file // laut
diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs
index ccb69cceb..198e7b418 100644
--- a/packages/superdough/superdough.mjs
+++ b/packages/superdough/superdough.mjs
@@ -126,7 +126,7 @@ export const getAudioDevices = async () => {
return devicesMap;
};
-const defaultDefaultValues = {
+let defaultDefaultValues = {
s: 'triangle',
gain: 0.8,
postgain: 1,
@@ -150,6 +150,17 @@ const defaultDefaultValues = {
fft: 8,
};
+const defaultDefaultDefaultValues = Object.freeze({ ...defaultDefaultValues });
+
+export function setDefault(control, value) {
+ // const main = getControlName(control); // we cant do this because superdough is independent of strudel/core
+ defaultDefaultValues[control] = value;
+}
+
+export function resetDefaults() {
+ defaultDefaultValues = { ...defaultDefaultDefaultValues };
+}
+
let defaultControls = new Map(Object.entries(defaultDefaultValues));
export function setDefaultValue(key, value) {
diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs
index 13cfa13a5..88e14e5ab 100644
--- a/packages/superdough/synth.mjs
+++ b/packages/superdough/synth.mjs
@@ -121,7 +121,10 @@ export function registerSynthSounds() {
const gainAdjustment = 1 / Math.sqrt(voices);
getPitchEnvelope(o.parameters.get('detune'), value, begin, holdend);
const vibratoOscillator = getVibratoOscillator(o.parameters.get('detune'), value, begin);
- const fm = applyFM(o.parameters.get('frequency'), value, begin);
+ // const fm = applyFM(o.parameters.get('frequency'), value, begin);
+ // https://codeberg.org/uzu/strudel/issues/1428
+ // if you think about re-enabling this, please test with fm > 1 first
+ // it's like 10x gain, so it's really dangerous
let envGain = gainNode(1);
envGain = o.connect(envGain);
@@ -133,7 +136,7 @@ export function registerSynthSounds() {
destroyAudioWorkletNode(o);
envGain.disconnect();
onended();
- fm?.stop();
+ // fm?.stop();
vibratoOscillator?.stop();
},
begin,
diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap
index 50d4f2095..500392041 100644
--- a/test/__snapshots__/examples.test.mjs.snap
+++ b/test/__snapshots__/examples.test.mjs.snap
@@ -9047,46 +9047,46 @@ exports[`runs examples > example "shrink" example index 3 1`] = `
exports[`runs examples > example "shuffle" example index 0 1`] = `
[
- "[ 0/1 → 1/4 | note:c s:piano ]",
+ "[ 0/1 → 1/4 | note:e s:piano ]",
"[ 1/4 → 1/2 | note:d s:piano ]",
- "[ 1/2 → 3/4 | note:e s:piano ]",
- "[ 3/4 → 1/1 | note:f s:piano ]",
- "[ 1/1 → 5/4 | note:c s:piano ]",
- "[ 5/4 → 3/2 | note:d s:piano ]",
- "[ 3/2 → 7/4 | note:e s:piano ]",
- "[ 7/4 → 2/1 | note:f s:piano ]",
- "[ 2/1 → 9/4 | note:c s:piano ]",
- "[ 9/4 → 5/2 | note:d s:piano ]",
+ "[ 1/2 → 3/4 | note:f s:piano ]",
+ "[ 3/4 → 1/1 | note:c s:piano ]",
+ "[ 1/1 → 5/4 | note:e s:piano ]",
+ "[ 5/4 → 3/2 | note:c s:piano ]",
+ "[ 3/2 → 7/4 | note:f s:piano ]",
+ "[ 7/4 → 2/1 | note:d s:piano ]",
+ "[ 2/1 → 9/4 | note:d s:piano ]",
+ "[ 9/4 → 5/2 | note:c s:piano ]",
"[ 5/2 → 11/4 | note:e s:piano ]",
"[ 11/4 → 3/1 | note:f s:piano ]",
"[ 3/1 → 13/4 | note:c s:piano ]",
- "[ 13/4 → 7/2 | note:d s:piano ]",
- "[ 7/2 → 15/4 | note:e s:piano ]",
- "[ 15/4 → 4/1 | note:f s:piano ]",
+ "[ 13/4 → 7/2 | note:e s:piano ]",
+ "[ 7/2 → 15/4 | note:f s:piano ]",
+ "[ 15/4 → 4/1 | note:d s:piano ]",
]
`;
exports[`runs examples > example "shuffle" example index 1 1`] = `
[
- "[ 0/1 → 1/8 | note:c s:piano ]",
+ "[ 0/1 → 1/8 | note:e s:piano ]",
"[ 1/8 → 1/4 | note:d s:piano ]",
- "[ 1/4 → 3/8 | note:e s:piano ]",
- "[ 3/8 → 1/2 | note:f s:piano ]",
+ "[ 1/4 → 3/8 | note:f s:piano ]",
+ "[ 3/8 → 1/2 | note:c s:piano ]",
"[ 1/2 → 1/1 | note:g s:piano ]",
- "[ 1/1 → 9/8 | note:c s:piano ]",
- "[ 9/8 → 5/4 | note:d s:piano ]",
- "[ 5/4 → 11/8 | note:e s:piano ]",
- "[ 11/8 → 3/2 | note:f s:piano ]",
+ "[ 1/1 → 9/8 | note:e s:piano ]",
+ "[ 9/8 → 5/4 | note:c s:piano ]",
+ "[ 5/4 → 11/8 | note:f s:piano ]",
+ "[ 11/8 → 3/2 | note:d s:piano ]",
"[ 3/2 → 2/1 | note:g s:piano ]",
- "[ 2/1 → 17/8 | note:c s:piano ]",
- "[ 17/8 → 9/4 | note:d s:piano ]",
+ "[ 2/1 → 17/8 | note:d s:piano ]",
+ "[ 17/8 → 9/4 | note:c s:piano ]",
"[ 9/4 → 19/8 | note:e s:piano ]",
"[ 19/8 → 5/2 | note:f s:piano ]",
"[ 5/2 → 3/1 | note:g s:piano ]",
"[ 3/1 → 25/8 | note:c s:piano ]",
- "[ 25/8 → 13/4 | note:d s:piano ]",
- "[ 13/4 → 27/8 | note:e s:piano ]",
- "[ 27/8 → 7/2 | note:f s:piano ]",
+ "[ 25/8 → 13/4 | note:e s:piano ]",
+ "[ 13/4 → 27/8 | note:f s:piano ]",
+ "[ 27/8 → 7/2 | note:d s:piano ]",
"[ 7/2 → 4/1 | note:g s:piano ]",
]
`;
diff --git a/website/src/pages/de/workshop/first-sounds.mdx b/website/src/pages/de/workshop/first-sounds.mdx
index 3695843b0..64a58db02 100644
--- a/website/src/pages/de/workshop/first-sounds.mdx
+++ b/website/src/pages/de/workshop/first-sounds.mdx
@@ -95,7 +95,7 @@ Diese Kombinationen von Buchstaben stehen für verschiedene Teile eines Schlagze
- `mt` = **m**iddle tom
- `ht` = **h**igh tom
- `rd` = **r**i**d**e cymbal
-- `rd` = **cr**ash cymbal
+- `cr` = **cr**ash cymbal
Probier verschiedene Sounds aus!
diff --git a/website/src/pages/understand/cycles.mdx b/website/src/pages/understand/cycles.mdx
index 1c0a778a6..bcf987c4d 100644
--- a/website/src/pages/understand/cycles.mdx
+++ b/website/src/pages/understand/cycles.mdx
@@ -67,6 +67,8 @@ Or using 2 beats per cycle:
s("bd sd, hh*4")`}
/>
+You can use the `setcps` method to set the global tempo in cycles per second. `setcpm(x)` is the same as `setcps(x / 60)`.
+
To set a specific bpm, use `setcpm(bpm/bpc)`
diff --git a/website/src/repl/useReplContext.jsx b/website/src/repl/useReplContext.jsx
index 12621c50a..ac30fe342 100644
--- a/website/src/repl/useReplContext.jsx
+++ b/website/src/repl/useReplContext.jsx
@@ -13,6 +13,7 @@ import {
resetGlobalEffects,
resetLoadedSounds,
initAudioOnFirstClick,
+ resetDefaults,
} from '@strudel/webaudio';
import { setVersionDefaultsFrom } from './util.mjs';
import { StrudelMirror, defaultSettings } from '@strudel/codemirror';
@@ -181,6 +182,7 @@ export function useReplContext() {
const resetEditor = async () => {
(await getModule('@strudel/tonal'))?.resetVoicings();
+ resetDefaults();
resetGlobalEffects();
clearCanvas();
clearHydra();