Lowering: patch repeat restarts pitch and arpeggio, not the envelope; README store-kind rollback note #37

Merged
aqua merged 2 commits from aqua/repeat-pitch-only into main 2026-11-11 09:27:29 +00:00
9 changed files with 104 additions and 38 deletions

View file

@ -164,3 +164,10 @@ dotnet build -p:CultLibRoot=<path> # or set CULTLIB_ROOT=<path>
```
Without an override the sibling `..\CultLib` checkout is used and must itself be at that revision.
**Stores are one-way, and rolling back can lose data.** Since the pin move (#35) list elements carry ids, and a store holding any element id is stamped with a newer header. What an older build does with it depends on the store kind:
- **Single-file stores** (header v3 with ids): the patch catalog (`patches/aquasynth-patch-cultcache.cc`), the speech training store and the daemon store. Pins before `cultlib-unity-v2.0.0` / `caching-unity-v2.0.0`, including NuGet `cultlib-v1.0.32`, read these without complaint, silently drop the ids, and can rewrite elements without them. Only v2.0.0 and later refuse by name.
- **Directory stores** (header v5 with ids): the IPA trial results store (`ipa-trial-results.cc`, `UseDirectoryStore`). These are refused by name from `cultlib-unity-v1.0.57` / `caching-unity-v1.2.0`.
So: take a copy of any store before the upgrade and restore from it if you roll back, never write with the new pin and then read with an old one, and upgrade every reader before any writer. Stores written before the move still open; ids are minted on load.

View file

@ -29,12 +29,10 @@ public static partial class FaustEmitter
if (patch.Repeat is { } repeat)
{
var interval = parameters.Expression("/patch/repeat", repeat.IntervalSeconds);
source.AppendLine($"age = time - floor(time / {interval}) * {interval};");
}
else
{
source.AppendLine("age = time;");
// sfxr's repeat restarts the pitch slide and the arpeggio, not the envelope: age keeps running, the voices wrap their pitch age.
source.AppendLine($"repeat_interval = {interval};");
}
source.AppendLine("age = time;");
source.AppendLine("clip01(x) = min(1.0, max(0.0, x));");
source.AppendLine("wrap01(x) = x - floor(x);");
source.AppendLine("slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));");
@ -325,6 +323,9 @@ public static partial class FaustEmitter
? $"{voiceAge} = wrap01(age / {F(voice.Note.TriggerIntervalSeconds)}) * {F(voice.Note.TriggerIntervalSeconds)};"
: $"{voiceAge} = age;");
var pitchAge = patch.Repeat is null ? voiceAge : $"{name}_pitch_age";
if (patch.Repeat is not null) source.AppendLine($"{pitchAge} = {voiceAge} - floor({voiceAge} / repeat_interval) * repeat_interval;");
var minFreq = parameters.Expression(OwnerField(ownerPath, "pitch/min_freq"), voice.Pitch.MinFrequencyHz);
var noteFreq = NoteFrequencyExpression(source, patch.Playback, voice.Note, name, OwnerField(ownerPath, "note/frequency"), parameters);
var noteGate = NoteGateExpression(source, patch.Playback, voice.Note, name, OwnerField(ownerPath, "note/gate"), parameters);
@ -333,7 +334,7 @@ public static partial class FaustEmitter
var vibratoDepth = parameters.Expression(OwnerField(ownerPath, "pitch/vibrato"), voice.Pitch.VibratoDepth);
var vibratoHz = parameters.Expression(OwnerField(ownerPath, "pitch/vibrato_hz"), voice.Pitch.VibratoHz);
var vibratoDelay = parameters.Expression(OwnerField(ownerPath, "pitch/vibrato_delay"), voice.Pitch.VibratoDelaySeconds);
var baseFreq = $"max({minFreq}, {noteFreq} * pow(2.0, {pitchRamp} * {voiceAge} + 0.5 * {pitchDelta} * {voiceAge} * {voiceAge}))";
var baseFreq = $"max({minFreq}, {noteFreq} * pow(2.0, {pitchRamp} * {pitchAge} + 0.5 * {pitchDelta} * {pitchAge} * {pitchAge}))";
var hasVibrato = voice.Pitch.VibratoDepth != 0 && voice.Pitch.VibratoHz > 0 ||
parameters.IsBound(OwnerField(ownerPath, "pitch/vibrato")) ||
parameters.IsBound(OwnerField(ownerPath, "pitch/vibrato_hz"));
@ -342,7 +343,7 @@ public static partial class FaustEmitter
: "";
var arpeggio = voice.Arpeggio is null
? "1.0"
: $"select2({voiceAge} < {parameters.Expression(OwnerField(ownerPath, "arpeggio/delay"), voice.Arpeggio.DelaySeconds)}, {parameters.Expression(OwnerField(ownerPath, "arpeggio/multiplier"), voice.Arpeggio.Multiplier)}, 1.0)";
: $"select2({pitchAge} < {parameters.Expression(OwnerField(ownerPath, "arpeggio/delay"), voice.Arpeggio.DelaySeconds)}, {parameters.Expression(OwnerField(ownerPath, "arpeggio/multiplier"), voice.Arpeggio.Multiplier)}, 1.0)";
var frequency = $"(({baseFreq}){vibrato}) * {arpeggio} * pow(2.0, patch_mod_pitch + {pitch})";
var dutyExpression = $"clip01({parameters.Expression(OwnerField(ownerPath, "osc/duty"), voice.Oscillator.Duty)} + {parameters.Expression(OwnerField(ownerPath, "duty/ramp"), voice.Duty.RampPerSecond)} * {voiceAge} + patch_mod_duty + {duty})";
var fmIndex = $"max(0.0, {parameters.Expression(OwnerField(ownerPath, "fm/index"), voice.Fm.Index)} + patch_mod_fm_index + {fmIndexMod}) * {FmDecay(parameters.Expression(OwnerField(ownerPath, "fm/decay"), voice.Fm.IndexDecaySeconds), voice.Fm.IndexDecaySeconds, parameters.IsBound(OwnerField(ownerPath, "fm/decay")), voiceAge)}";

View file

@ -0,0 +1,44 @@
using AquaSynth.Dsl;
namespace AquaSynth.Dsl.Tests;
// sfxr's repeat restarts the pitch slide and the arpeggio; it does not restart the envelope. Before this, `patch repeat` wrapped the
// whole age, so a repeat shorter than sustain+decay never reached the release (the 2026-11-11 explosion/golf/powerup finding).
public sealed class RepeatLoweringTests
{
private const string Script = """
patch repeat=0.11315
voice
wave=sine
freq=57.5946
sustain=0.1306122449
decay=0.1777777778
pitch_ramp=-0.208544
arp_delay=0.05
arp_mult=1.5
""";
[Fact]
public void RepeatWrapsPitchAndArpeggioAgeButNotTheEnvelope()
{
var source = FaustEmitter.EmitScript(Script).Source;
Assert.Contains("age = time;", source);
Assert.DoesNotContain("floor(time /", source);
Assert.Contains("voice_0_pitch_age = voice_0_age - floor(voice_0_age / repeat_interval) * repeat_interval;", source);
Assert.Contains("0.208544", source);
Assert.Contains("* voice_0_pitch_age", source);
Assert.Contains("select2(voice_0_pitch_age <", source);
Assert.Contains("oneshot_adsr_at(voice_0_age,", source);
Assert.DoesNotContain("oneshot_adsr_at(voice_0_pitch_age", source);
}
[Fact]
public void WithoutRepeatThereIsNoPitchAge()
{
var source = FaustEmitter.EmitScript(Script.Replace("patch repeat=0.11315\n", "")).Source;
Assert.DoesNotContain("pitch_age", source);
Assert.DoesNotContain("repeat_interval", source);
}
}

View file

@ -85,18 +85,18 @@ Sustained patches such as the thruster keep sounding until stopped; the render c
| `patch/advanced-glass-creature` | start | none | decays | 3 s | unheard |
| `patch/advanced-human-larynx-graph` | start | curves/breath_phrase/depth 0..1 (default 0.9); curves/fold_onset/depth 0..1 (default 0.8); curves/lip_vowel/depth 0..1 (default 0.65); human/constriction/diameter 0.02..3 (default 0.95); human/constriction/index 0..6 (default 4.8); human/fold/opening 0..1 (default 0.16); human/fold/tension 0..1 (default 0.58); human/lip/opening 0..2 (default 1.2); human/subglottal 0..1.5 (default 0.78); human/tongue/diameter 0.05..3 (default 1.25); human/tongue/index 0..6 (default 3.3) | decays | 3 s | unheard |
| `patch/advanced-machine-breath` | start | none | decays | 3 s | unheard |
| `patch/advanced-ritual-sequence` | start | none | sustained | 3 s | unheard |
| `patch/advanced-ritual-sequence` | start | none | decays | 3 s | unheard |
| `patch/advanced-subtractive-drums` | start | none | decays | 3 s | unheard |
| `patch/advanced-syrinx-voice` | start | curves/beak_phrase/depth 0..1 (default 0.72); curves/left_phrase_opening/depth 0..1 (default 0.85); curves/left_phrase_pressure/depth 0..1 (default 0.92); curves/right_phrase_opening/depth 0..1 (default 0.85); curves/right_phrase_pressure/depth 0..1 (default 0.92); voice/beak/opening 0..1.5 (default 0.88); voice/left/opening 0..1 (default 0.23); voice/left/pressure 0..1.5 (default 0.78); voice/left/tension 0..1 (default 0.43); voice/right/opening 0..1 (default 0.19); voice/right/pressure 0..1.5 (default 0.66); voice/right/tension 0..1 (default 0.5) | decays | 3 s | unheard |
| `patch/bfxr-coin-spark` | start | none | decays | 3 s | unheard |
| `patch/bfxr-portal-chirp` | start | none | decays | 3 s | unheard |
| `patch/bfxr-shield-pop` | start | none | decays | 3 s | unheard |
| `patch/bfxr-ui-bloom` | start | none | sustained | 3 s | unheard |
| `patch/bfxr-ui-bloom` | start | none | decays | 3 s | unheard |
| `patch/dx7-algorithm-32-additive-organ` | start | macro/drawbar 0..1 (default 0.78); macro/shimmer 0..0.3 (default 0.08) | decays | 3 s | unheard |
| `patch/dx7-algorithm-8-bright-pair` | start | macro/brightness 0..5 (default 2.6); macro/strike 0.02..0.6 (default 0.18) | decays | 3 s | unheard |
| `patch/dx7-public-domain-mc-mm-5-3` | start | none | decays | 3 s | unheard |
| `patch/dx7-public-domain-prc-synth1-calibrated` | start | none | decays | 3 s | unheard |
| `patch/examples-language-tour` | start | none | sustained | 3 s | unheard |
| `patch/examples-language-tour` | start | none | decays | 3 s | unheard |
| `patch/fm-bell-bell` | start | none | decays | 3 s | unheard |
| `patch/fm-bell-chime` | start | none | decays | 3 s | unheard |
| `patch/fm-bell-coin` | start | none | decays | 3 s | unheard |
@ -108,7 +108,7 @@ Sustained patches such as the thruster keep sounding until stopped; the render c
| `patch/sfxr-jump` | start | none | decays | 3 s | unheard |
| `patch/sfxr-laser` | start | none | decays | 3 s | unheard |
| `patch/sfxr-pickup` | start | none | decays | 3 s | unheard |
| `patch/sfxr-powerup` | start | none | sustained | 3 s | unheard |
| `patch/sfxr-powerup` | start | none | decays | 3 s | unheard |
| `patch/wobble-bass-growl` | start | none | decays | 3 s | unheard |
| `patch/wobble-bass-neuro` | start | none | decays | 3 s | unheard |
| `patch/wobble-bass-talker` | start | none | decays | 3 s | unheard |

View file

@ -859,7 +859,7 @@
"kind": "start",
"detail": "plays from the moment it is started; no gate, trigger or audio-feature input. Sliders, where listed, are live while it plays."
},
"behaviour": "sustained: still sounding at the end of the render window, runs until stopped",
"behaviour": "decays: dies away inside the render window",
"duration_s": 3,
"duration_note": "the render window, not the effect's length",
"render": {
@ -1106,7 +1106,7 @@
"kind": "start",
"detail": "plays from the moment it is started; no gate, trigger or audio-feature input. Sliders, where listed, are live while it plays."
},
"behaviour": "sustained: still sounding at the end of the render window, runs until stopped",
"behaviour": "decays: dies away inside the render window",
"duration_s": 3,
"duration_note": "the render window, not the effect's length",
"render": {
@ -1265,7 +1265,7 @@
"kind": "start",
"detail": "plays from the moment it is started; no gate, trigger or audio-feature input. Sliders, where listed, are live while it plays."
},
"behaviour": "sustained: still sounding at the end of the render window, runs until stopped",
"behaviour": "decays: dies away inside the render window",
"duration_s": 3,
"duration_note": "the render window, not the effect's length",
"render": {
@ -1565,7 +1565,7 @@
"kind": "start",
"detail": "plays from the moment it is started; no gate, trigger or audio-feature input. Sliders, where listed, are live while it plays."
},
"behaviour": "sustained: still sounding at the end of the render window, runs until stopped",
"behaviour": "decays: dies away inside the render window",
"duration_s": 3,
"duration_note": "the render window, not the effect's length",
"render": {

View file

@ -3,7 +3,8 @@ declare name "advanced-ritual-sequence";
declare options "[nvoices:1]";
time = ba.time / ma.SR;
age = time - floor(time / 0.42) * 0.42;
repeat_interval = 0.42;
age = time;
clip01(x) = min(1.0, max(0.0, x));
wrap01(x) = x - floor(x);
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
@ -41,8 +42,9 @@ patch_mod_formant_mix = 0.0;
patch_mod_fm_index = 0 + 0.75 * lfo_tri(2.38, 0);
voice_0_age = age;
voice_0_freq = ((max(20, 196 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * select2(voice_0_age < 0.065, 1.4983, 1.0) * pow(2.0, patch_mod_pitch + 0.0);
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 196 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * select2(voice_0_age < 0.065, 1.4983, 1.0) * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 196 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * select2(voice_0_age < 0.065, 1.4983, 1.0) * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2))) * (max(0.0, 1.8 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.32, 0.0001))) / ma.PI));
voice_0_pitch_age = voice_0_age - floor(voice_0_age / repeat_interval) * repeat_interval;
voice_0_freq = ((max(20, 196 * pow(2.0, 0 * voice_0_pitch_age + 0.5 * 0 * voice_0_pitch_age * voice_0_pitch_age)))) * select2(voice_0_pitch_age < 0.065, 1.4983, 1.0) * pow(2.0, patch_mod_pitch + 0.0);
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 196 * pow(2.0, 0 * voice_0_pitch_age + 0.5 * 0 * voice_0_pitch_age * voice_0_pitch_age)))) * select2(voice_0_pitch_age < 0.065, 1.4983, 1.0) * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 196 * pow(2.0, 0 * voice_0_pitch_age + 0.5 * 0 * voice_0_pitch_age * voice_0_pitch_age)))) * select2(voice_0_pitch_age < 0.065, 1.4983, 1.0) * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2))) * (max(0.0, 1.8 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.32, 0.0001))) / ma.PI));
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0);
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
@ -52,8 +54,9 @@ voice_0_formants = voice_0_phased;
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.001, 0, 0.8474576, 0.5, 0.045) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.1534);
voice_1_age = age;
voice_1_freq = ((max(20, 293.66 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * select2(voice_1_age < 0.065, 1.3348, 1.0) * pow(2.0, patch_mod_pitch + 0.0);
voice_1_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 293.66 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * select2(voice_1_age < 0.065, 1.3348, 1.0) * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 293.66 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * select2(voice_1_age < 0.065, 1.3348, 1.0) * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2))) * (max(0.0, 1.8 + patch_mod_fm_index + 0.0) * exp(-voice_1_age / max(0.32, 0.0001))) / ma.PI));
voice_1_pitch_age = voice_1_age - floor(voice_1_age / repeat_interval) * repeat_interval;
voice_1_freq = ((max(20, 293.66 * pow(2.0, 0 * voice_1_pitch_age + 0.5 * 0 * voice_1_pitch_age * voice_1_pitch_age)))) * select2(voice_1_pitch_age < 0.065, 1.3348, 1.0) * pow(2.0, patch_mod_pitch + 0.0);
voice_1_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 293.66 * pow(2.0, 0 * voice_1_pitch_age + 0.5 * 0 * voice_1_pitch_age * voice_1_pitch_age)))) * select2(voice_1_pitch_age < 0.065, 1.3348, 1.0) * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 293.66 * pow(2.0, 0 * voice_1_pitch_age + 0.5 * 0 * voice_1_pitch_age * voice_1_pitch_age)))) * select2(voice_1_pitch_age < 0.065, 1.3348, 1.0) * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2))) * (max(0.0, 1.8 + patch_mod_fm_index + 0.0) * exp(-voice_1_age / max(0.32, 0.0001))) / ma.PI));
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0);
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
@ -63,8 +66,9 @@ voice_1_formants = voice_1_phased;
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0.001, 0, 0.8474576, 0.38, 0.035) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.09);
voice_2_age = age;
voice_2_freq = ((max(20, 392 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age)))) * select2(voice_2_age < 0.065, 1.2599, 1.0) * pow(2.0, patch_mod_pitch + 0.0);
voice_2_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 392 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age)))) * select2(voice_2_age < 0.065, 1.2599, 1.0) * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 392 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age)))) * select2(voice_2_age < 0.065, 1.2599, 1.0) * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2))) * (max(0.0, 1.8 + patch_mod_fm_index + 0.0) * exp(-voice_2_age / max(0.32, 0.0001))) / ma.PI));
voice_2_pitch_age = voice_2_age - floor(voice_2_age / repeat_interval) * repeat_interval;
voice_2_freq = ((max(20, 392 * pow(2.0, 0 * voice_2_pitch_age + 0.5 * 0 * voice_2_pitch_age * voice_2_pitch_age)))) * select2(voice_2_pitch_age < 0.065, 1.2599, 1.0) * pow(2.0, patch_mod_pitch + 0.0);
voice_2_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 392 * pow(2.0, 0 * voice_2_pitch_age + 0.5 * 0 * voice_2_pitch_age * voice_2_pitch_age)))) * select2(voice_2_pitch_age < 0.065, 1.2599, 1.0) * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 392 * pow(2.0, 0 * voice_2_pitch_age + 0.5 * 0 * voice_2_pitch_age * voice_2_pitch_age)))) * select2(voice_2_pitch_age < 0.065, 1.2599, 1.0) * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2))) * (max(0.0, 1.8 + patch_mod_fm_index + 0.0) * exp(-voice_2_age / max(0.32, 0.0001))) / ma.PI));
voice_2_colored = (voice_2_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
voice_2_driven = ma.tanh(voice_2_colored * (1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0);
voice_2_folded = voice_2_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_2_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
@ -74,7 +78,8 @@ voice_2_formants = voice_2_phased;
voice_2 = ((voice_2_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_2_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_2_age, 0.001, 0, 0.8474576, 0.32, 0.028) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.07);
voice_3_age = age;
voice_3_freq = ((max(20, 3100 * pow(2.0, 0 * voice_3_age + 0.5 * 0 * voice_3_age * voice_3_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
voice_3_pitch_age = voice_3_age - floor(voice_3_age / repeat_interval) * repeat_interval;
voice_3_freq = ((max(20, 3100 * pow(2.0, 0 * voice_3_pitch_age + 0.5 * 0 * voice_3_pitch_age * voice_3_pitch_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
voice_3_osc = no.noise;
voice_3_colored = (voice_3_osc * (1.0 - clip01(1 + patch_mod_noise + 0.0)) + no.noise * clip01(1 + patch_mod_noise + 0.0));
voice_3_driven = ma.tanh(voice_3_colored * (1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0);

View file

@ -3,7 +3,8 @@ declare name "bfxr-ui-bloom";
declare options "[nvoices:1]";
time = ba.time / ma.SR;
age = time - floor(time / 0.18) * 0.18;
repeat_interval = 0.18;
age = time;
clip01(x) = min(1.0, max(0.0, x));
wrap01(x) = x - floor(x);
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
@ -41,8 +42,9 @@ patch_mod_formant_mix = 0.0;
patch_mod_fm_index = 0.0;
voice_0_age = age;
voice_0_freq = ((max(20, 392 * pow(2.0, 0.18 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * select2(voice_0_age < 0.08, 1.25, 1.0) * pow(2.0, patch_mod_pitch + 0.0);
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 392 * pow(2.0, 0.18 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * select2(voice_0_age < 0.08, 1.25, 1.0) * pow(2.0, patch_mod_pitch + 0.0)) + 0));
voice_0_pitch_age = voice_0_age - floor(voice_0_age / repeat_interval) * repeat_interval;
voice_0_freq = ((max(20, 392 * pow(2.0, 0.18 * voice_0_pitch_age + 0.5 * 0 * voice_0_pitch_age * voice_0_pitch_age)))) * select2(voice_0_pitch_age < 0.08, 1.25, 1.0) * pow(2.0, patch_mod_pitch + 0.0);
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 392 * pow(2.0, 0.18 * voice_0_pitch_age + 0.5 * 0 * voice_0_pitch_age * voice_0_pitch_age)))) * select2(voice_0_pitch_age < 0.08, 1.25, 1.0) * pow(2.0, patch_mod_pitch + 0.0)) + 0));
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.06 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.06 + patch_mod_drive + 0.0) * 12.0);
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
@ -52,8 +54,9 @@ voice_0_formants = voice_0_phased;
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 0.8196721, 0.5, 0.05) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.1464);
voice_1_age = age;
voice_1_freq = ((max(20, 588 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
voice_1_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 588 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
voice_1_pitch_age = voice_1_age - floor(voice_1_age / repeat_interval) * repeat_interval;
voice_1_freq = ((max(20, 588 * pow(2.0, 0 * voice_1_pitch_age + 0.5 * 0 * voice_1_pitch_age * voice_1_pitch_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
voice_1_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 588 * pow(2.0, 0 * voice_1_pitch_age + 0.5 * 0 * voice_1_pitch_age * voice_1_pitch_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.06 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.06 + patch_mod_drive + 0.0) * 12.0);
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);

View file

@ -3,7 +3,8 @@ declare name "examples-language-tour";
declare options "[nvoices:1]";
time = ba.time / ma.SR;
age = time - floor(time / 0.4739614) * 0.4739614;
repeat_interval = 0.4739614;
age = time;
clip01(x) = min(1.0, max(0.0, x));
wrap01(x) = x - floor(x);
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
@ -41,8 +42,9 @@ patch_mod_formant_mix = 0 + 0.08 * lfo_sin(5, 0) + 0 + 0.12 * lfo_tri(2, 0);
patch_mod_fm_index = 0.0;
voice_0_age = age;
voice_0_freq = ((max(20, 220 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 5) * 0.02))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 220 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 5) * 0.02))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
voice_0_pitch_age = voice_0_age - floor(voice_0_age / repeat_interval) * repeat_interval;
voice_0_freq = ((max(20, 220 * pow(2.0, 0 * voice_0_pitch_age + 0.5 * 0 * voice_0_pitch_age * voice_0_pitch_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 5) * 0.02))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 220 * pow(2.0, 0 * voice_0_pitch_age + 0.5 * 0 * voice_0_pitch_age * voice_0_pitch_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 5) * 0.02))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
@ -52,8 +54,9 @@ voice_0_formants = ((voice_0_phased : fi.resonbp(620, 6.888889, 1.0)) * 1 + (voi
voice_0 = ((voice_0_phased * (1.0 - clip01(0.45 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0.45 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.002, 0, 1, 0.2, 0.03) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.12);
voice_1_age = age;
voice_1_freq = ((max(20, 440 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
voice_1_osc = 1.0 - 4.0 * abs(wrap01(os.phasor(1.0, ((max(20, 440 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) - 0.5);
voice_1_pitch_age = voice_1_age - floor(voice_1_age / repeat_interval) * repeat_interval;
voice_1_freq = ((max(20, 440 * pow(2.0, 0 * voice_1_pitch_age + 0.5 * 0 * voice_1_pitch_age * voice_1_pitch_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
voice_1_osc = 1.0 - 4.0 * abs(wrap01(os.phasor(1.0, ((max(20, 440 * pow(2.0, 0 * voice_1_pitch_age + 0.5 * 0 * voice_1_pitch_age * voice_1_pitch_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) - 0.5);
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
@ -63,8 +66,9 @@ voice_1_formants = voice_1_phased;
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 1, 0.18, 0.02) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.04);
voice_2_age = age;
voice_2_freq = ((max(20, 230.3403 * pow(2.0, 0.7460228 * voice_2_age + 0.5 * 0.00000008 * voice_2_age * voice_2_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
voice_2_osc = 2.0 * wrap01(os.phasor(1.0, ((max(20, 230.3403 * pow(2.0, 0.7460228 * voice_2_age + 0.5 * 0.00000008 * voice_2_age * voice_2_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) - 1.0;
voice_2_pitch_age = voice_2_age - floor(voice_2_age / repeat_interval) * repeat_interval;
voice_2_freq = ((max(20, 230.3403 * pow(2.0, 0.7460228 * voice_2_pitch_age + 0.5 * 0.00000008 * voice_2_pitch_age * voice_2_pitch_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
voice_2_osc = 2.0 * wrap01(os.phasor(1.0, ((max(20, 230.3403 * pow(2.0, 0.7460228 * voice_2_pitch_age + 0.5 * 0.00000008 * voice_2_pitch_age * voice_2_pitch_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) - 1.0;
voice_2_colored = (voice_2_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
voice_2_driven = ma.tanh(voice_2_colored * (1.0 + clip01(0.1212819 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.1212819 + patch_mod_drive + 0.0) * 12.0);
voice_2_folded = voice_2_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_2_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);

View file

@ -3,7 +3,8 @@ declare name "sfxr-powerup";
declare options "[nvoices:1]";
time = ba.time / ma.SR;
age = time - floor(time / 0.11315) * 0.11315;
repeat_interval = 0.11315;
age = time;
clip01(x) = min(1.0, max(0.0, x));
wrap01(x) = x - floor(x);
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
@ -41,8 +42,9 @@ patch_mod_formant_mix = 0.0;
patch_mod_fm_index = 0.0;
voice_0_age = age;
voice_0_freq = ((max(20, 57.5946 * pow(2.0, -0.208544 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 3.9602) * 0.09))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 57.5946 * pow(2.0, -0.208544 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 3.9602) * 0.09))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
voice_0_pitch_age = voice_0_age - floor(voice_0_age / repeat_interval) * repeat_interval;
voice_0_freq = ((max(20, 57.5946 * pow(2.0, -0.208544 * voice_0_pitch_age + 0.5 * 0 * voice_0_pitch_age * voice_0_pitch_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 3.9602) * 0.09))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 57.5946 * pow(2.0, -0.208544 * voice_0_pitch_age + 0.5 * 0 * voice_0_pitch_age * voice_0_pitch_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 3.9602) * 0.09))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);