diff --git a/Architecture.md b/Architecture.md index 673d314..4269e2a 100644 --- a/Architecture.md +++ b/Architecture.md @@ -446,6 +446,10 @@ src/editor/soundfx/ SoundFxEditor (#311: the graph, a sound to try it on, Pla are the Reverb's `PickButton`), LimiterPanel + LimiterFace + limiterView (#397: the Limiter as a device, what it takes away read from its taps and caught from the start of a sound, held once it ends, worked out apart from the drawing), + ModulationPanel + ModulationFace + modulationView (#398: the Tremolo, the + Vibrato, the Phaser and the Flanger as one kind of device, the volume or the + pitch over time, or the response with a lane of where it goes, worked out + apart from the drawing against the worklets' own numbers), nodeParamDrag (a knob's drag as one undo step, for the tab and the node, several settings at once since #389), nodeTabs (which nodes open a tab of their own) diff --git a/Development.md b/Development.md index 677f0b7..4864e49 100644 --- a/Development.md +++ b/Development.md @@ -19,7 +19,7 @@ php tests/php/accounts.test.php # the API's accoun php tests/php/snapshots.test.php # the snapshot store (#231), the same way php tests/php/releases.test.php # the release store (#266), the same way php tests/php/files.test.php # uploaded files in a folder per account (#344), the same way -node tools/seed-e2e.mjs && node tools/e2e.mjs # 1970 editor checks, screenshots in /tmp/gm-e2e-*.png +node tools/seed-e2e.mjs && node tools/e2e.mjs # 1974 editor checks, screenshots in /tmp/gm-e2e-*.png GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step ./e2e.sh start [GM_STEPS] # the same run detached, see below ./e2e.sh status # live progress, or the last run's result diff --git a/Sounds.md b/Sounds.md index 12443dd..afaa4c9 100644 --- a/Sounds.md +++ b/Sounds.md @@ -431,14 +431,14 @@ response of a Reverb, and as what the Sound FX tab tries out. Its row means noth | **Mix** | Blends the sounds at `a` and `b`: at 0 only `a` is heard, at 1 only `b` (0.5 is the default). **Curve** says what lies between: *Crossfade* (the default) gives half of each at 0.5, *Both at center* keeps each full up to the middle. See [below](#the-curve-of-a-mix). In the graph the node is drawn as a blue device of 180x200 graph units (#390): a lever for **Curve** on top, down for *Crossfade* and up, with its lamp lit, for *Both at center*, and below it one knob for `Mix` with *A* and *B* engraved at the ends of its turn. A flip of the lever is one undo step. A linked `Mix` greys the knob. | | **Delay** | An echo: the sound comes back after `time`, again and again, each time `feedback` times as loud. **Sync** says what `time` counts: *Seconds*, or *Beats* in quarter notes at the tempo of the voice. `damping` makes every echo duller than the last. `wet` (0.5) is the sound alone at 0, the sound and its echoes in full at 0.5, the echoes alone at 1. **Ping-pong** sends the echoes left and right in turn. The node is drawn as a device with its echoes on a screen (#393). See [below](#delay-and-the-tail-of-a-voice). | | **Bitcrusher** | Makes the sound rough, like an old console. `bits` rounds it to fewer steps (8 by default), `rate` takes a sample only so many times a second and holds it until the next (8000 Hz by default), `wet` is how much of the crushed sound is heard instead of the sound itself (1 by default). See [below](#bitcrusher). | -| **Phaser** | Sweeps notches through the sound, the swirl of a phaser pedal. **Stages** (2, 4, 6, 8 or 12, 4 by default) is how many allpass stages it has, every two make one notch. They sit around `frequency` (800 Hz by default), and an LFO moves them `depth` octaves wide (2), `rate` times a second (0.5 Hz). `feedback` (0.4) sharpens the notches, `wet` (1) is how much is heard. See [below](#phaser-and-flanger). | -| **Flanger** | Blends the sound with a copy a few ms late: the jet sound of a flanger, and what sfxr calls its phaser. `offset` is how late the copy is (1 ms by default), `sweep` moves that on by so many ms a second from the start of the sound (0), an LFO adds up to `depth` ms (2) `rate` times a second (0.25 Hz). `feedback` (0.5) and `wet` (1) as on the Phaser. See [below](#phaser-and-flanger). | +| **Phaser** | Sweeps notches through the sound, the swirl of a phaser pedal. **Stages** (2, 4, 6, 8 or 12, 4 by default) is how many allpass stages it has, every two make one notch. They sit around `frequency` (800 Hz by default), and an LFO moves them `depth` octaves wide (2), `rate` times a second (0.5 Hz). `feedback` (0.4) sharpens the notches, `wet` (1) is how much is heard. The node is drawn as a mustard device whose screen swirls its notches (#398). See [below](#phaser-and-flanger). | +| **Flanger** | Blends the sound with a copy a few ms late: the jet sound of a flanger, and what sfxr calls its phaser. `offset` is how late the copy is (1 ms by default), `sweep` moves that on by so many ms a second from the start of the sound (0), an LFO adds up to `depth` ms (2) `rate` times a second (0.25 Hz). `feedback` (0.5) and `wet` (1) as on the Phaser. The node is drawn as a steel blue device whose screen draws its comb (#398). See [below](#phaser-and-flanger). | | **Pan** | Moves the sound left or right: `pan` -1 is all left, 1 all right, 0 (the default) leaves it in the middle. A mono sound is spread on both sides and then moved (each side 0.707 at 0). A stereo sound keeps the side it goes to and the other side moves over into it, as Web Audio's `StereoPannerNode` does: at 1 both sides are heard on the right. In an [instrument](Instruments#envelopes) an Envelope can move it. Measured (e2e 34, Chrome offline): a mono sine at 0.5 came out 0.5 / 0 at -1 and 0.354 / 0.354 at 0, a stereo one at 0.5 came out 0.354 / 0.854. In the graph the node is drawn as the Gain's device in cyan (#392), with one knob for `Pan`: the middle stands straight up, the turn goes evenly to *L* and *R* engraved at its ends. A linked `Pan` greys the knob. | | **Compressor** | Squeezes the loudness together: what goes above `threshold` (-24 dB by default) comes out only 1/`ratio` (4) as much louder. `knee` (6 dB) eases into it, `attack` (0.003 s) and `release` (0.25 s) say how fast it turns down and lets go. `makeup` (0 dB) raises it afterwards. **Auto-gain** (off) gives back what it takes from a sound at -18 dB, with the makeup on top. The node is drawn as a device with a meter of what it takes away, and a double-click opens a tab with its curve and the level going in and out. See [below](#compressor-and-limiter). | | **Limiter** | Keeps every peak below `threshold` (-6 dB): it looks 5 ms ahead, so even a sudden peak never gets through, and comes back up in `release` (0.1 s). **Auto-gain** (on) raises the limited sound to `ceiling` (-1 dB). Nothing ever goes above the ceiling. The node is drawn as a brick device whose screen holds how much it took away and when (#397). See [below](#compressor-and-limiter). | | **Saturation** | Bends the sound, from warmth to a hard distortion. **Type**: *Soft (tanh)* (the default), *Cubic*, *Hard clip*, *Tube*, *Fuzz*, *Foldback*, *Sine fold* or *Rectify*. `drive` (12 dB) is how hard the sound is pushed into it, `tone` (1) a low-pass after it from 1 kHz (0) to wide open (1). **Auto-gain** (on) keeps the bent sound about as loud as it came in, `output` (0 dB) is added on top. `wet` (1) how much of it is heard instead of the sound itself. The node is drawn as a copper device whose screen glows hotter the harder it is driven (#396). See [below](#saturation). | -| **Tremolo** | Makes the volume swing `rate` times a second (5 Hz by default), from full down by `depth` percent (50) and back. **Shape**: *Sine* (the default), *Triangle*, *Square* or *Saw*. See [below](#tremolo-and-vibrato). | -| **Vibrato** | Makes the pitch swing `rate` times a second (5 Hz), `depth` semitones up and as far down (0.5). **Shape** as on the Tremolo; a *Square* jumps between two pitches. See [below](#tremolo-and-vibrato). | +| **Tremolo** | Makes the volume swing `rate` times a second (5 Hz by default), from full down by `depth` percent (50) and back. **Shape**: *Sine* (the default), *Triangle*, *Square* or *Saw*. The node is drawn as a cream device with the volume on a screen (#398). See [below](#tremolo-and-vibrato). | +| **Vibrato** | Makes the pitch swing `rate` times a second (5 Hz), `depth` semitones up and as far down (0.5). **Shape** as on the Tremolo; a *Square* jumps between two pitches. The node is drawn as a plum device with the pitch on a screen (#398). See [below](#tremolo-and-vibrato). | | **Reverb** | The sound in a space. **Type**: *Room* (the default), *Hall*, *Cave*, *Plate*, *Custom* or *Sound*. Custom shows the rows `decay` (s, 1.5 by default), `damping` (0.4) and `pre-delay` (s, 0.01); Sound picks a sound asset as the impulse response of a real space, with `pre-delay`. `wet` (0.3) is how much of the reverb is heard instead of the sound itself. The node is drawn as a device with a round screen of its tail (#389). See [below](#reverb). | Every number of these nodes is an **input**: typed in while nothing is connected, or fed from a @@ -964,6 +964,26 @@ FX. A `depth` typed in at 0 leaves the node out. - `rate` (in its ratio) and `depth` move with *Set sound FX parameters*. **Turning the depth up or the rate down after the start** asks for a delay below 0 at the low end of the swing: there it stays at 0, and the lower half of the swing flattens. +- **The devices** (#398): in the graph a Tremolo is a plate of 640x280 graph units in the cream of + an old amp's front, a Vibrato one of the same size in plum, their inputs on the left and their + output on the right. + - **The screen** draws the first seconds of a sound, two swings at least: 1, 2, 4 or 8 s. The + Tremolo's (*Volume over time*) is a wave the volume shapes, the volume as its outline, `wet` + blended in. Above it `DOWN TO 50%`, under it the period (`EVERY 200 ms`) and how deep it dips + (`DIPS -6 dB`). The Vibrato's (*Pitch over time*) is the pitch the sound is read at, in + semitones, cool below and warm above, on a scale of 0.5, 1, 2, 4 or 8 semitones either way. + Under a `wet` below 1 the pitch the sound came at stands beside it, the brighter the less + `wet`. Above it `UP 0.5 st DOWN 0.51 st`, under it the period and how late the voice is + (`LATE 0.93 ms`). + - **A light** plays it in real time, over and over, with a dot where the volume or the pitch is. + The *Rate* lamp beside the name goes from dark at the bottom of the swing to lit at its top. + When nothing swings nothing moves, and the node lets the editor rest. + - Everything is drawn from the settings, nothing is listened to. The pictures use the LFO the + worklet runs: the unit tests hold them against its samples. +- **On the plate**: a button for each shape, `Rate` and `Depth` as big chrome knobs, `Wet` small. A + shape and each turn are one undo step, a wired number locks its knob. `Rate` gives its slow end + more of the turn (5 Hz in the middle of a Tremolo's). At `wet` 0 everything else is drawn faint, + at `depth` 0 everything but the depth. - **Measured** (2026-10-07, e2e 34, Chrome offline): a Tremolo square at 10 Hz, depth 100 %, on a steady 0.5 for a voice starting at 0.1 s was silent before it, then 0.5 for 50 ms and 0 for 50 ms. A Vibrato square at 5 Hz and one semitone on a 1 kHz sine read 1059.5 Hz in the first half of @@ -1083,6 +1103,28 @@ stays. - `frequency` and `offset` move in what they hold (the frequency in octaves), and in an [instrument](Instruments#envelopes) an Envelope can move `frequency`, `offset` and `wet` while a note plays. The other numbers move with *Set sound FX parameters* as well. +- **The devices** (#398): in the graph a Phaser is a plate of 760x280 graph units in mustard, a + Flanger one of 680x280 in steel blue, their inputs on the left and their output on the right. + - **The screen** draws what is left of each frequency, 20 Hz to 20 kHz in octaves, +12 to -36 + dB: the notches of the Phaser, the comb of the Flanger, and the peaks feedback rings. A comb + finer than the screen fills the columns it runs through instead of aliasing. It uses the + allpasses, the feedback and the delay of the worklet, at 48 kHz: the unit tests find the same + dB as the worklet on a sine, within 0.005 dB. + - Under it a **lane** shows where that goes over the first seconds of a sound: the Phaser's turn + in Hz, the Flanger's delay in ms (up to 2.5, 5, 10 or 20). A light runs over the lane in real + time and the response above is drawn where it is, so the notches swirl and the comb jets as + they will. The span is two swings of the LFO at least, for a Flanger as long as its sweep takes + to reach 20 ms. The range they move through is shaded, a thin line marks the Phaser's turn or + the Flanger's first notch (500 Hz at 1 ms). + - Above the screen `2 NOTCHES` (half the stages) or `DELAY 1 ms TO 3 ms`, under it + `TURNS 400 Hz TO 1.6 kHz` and `EVERY 2 s`, or `LFO EVERY 4 s` and `SWEEP +5 ms/s`. At `wet` 0 + it says `DRY`. The *Rate* lamp swings with the light as on the Tremolo. +- **On the plate**: the Phaser has a button for each number of stages and `Freq`, `Depth`, `Rate`, + `Feedback` and `Wet` in a row. The Flanger has `Offset` and `Sweep`, `Rate` and `Depth`, + `Feedback` and `Wet` in three columns of two. `Freq` gives its low end more of the turn (800 Hz + just under the middle), `Sweep` stands straight up at 0. A button and each turn are one undo + step, a wired number locks its knob. At `wet` 0 everything else is drawn faint, without `depth` + the `Rate`. - Without `AudioWorklet` the sound goes through both as it is, as through the Bitcrusher. - **Measured** (2026-10-07, e2e 34, Chrome offline on the Windows runner): a 1 kHz sine at 0.8 through a Phaser at 1 kHz came out at an RMS of 0.0000 with two stages and 0.5657 with four