diff --git a/Architecture.md b/Architecture.md index 29c3db9..c3aca2e 100644 --- a/Architecture.md +++ b/Architecture.md @@ -214,7 +214,7 @@ src/runtime/physics/ engine (loads Rapier once, reports its state, converts be src/ui/ widget toolkit (generic, knows nothing about assets); fonts (the faces the editor ships: Source Sans 3 in 400, 500 and 600, Source Code Pro 400, Latin only, #202/#214/#273), icons + Icon (the menu's tick boxes and current-entry dot as SVG files, and a widget that shows one file - on its own: a symbol the Latin subset lacks is never written as a character, #214); SortableList + sortable (the layer list of every editor that has one: rows dragged into order, eye, duplicate, cross, rename in place; the index arithmetic of a list shown upside down, of a drop in the tile strip and of blocks of rows of any height, kept apart so it is measured without a canvas); blockDrag (`sortBlocks`: the inspector's own rows dragged into order by a `Grip` (the row's mark, wrapped: dots once it drags, lit under the pointer while the block is washed), a block being every row it covers, #310) + on its own: a symbol the Latin subset lacks is never written as a character, #214); SortableList + sortable (the layer list of every editor that has one: rows dragged into order, eye, duplicate, cross, rename in place; the index arithmetic of a list shown upside down, of a drop in the tile strip and of blocks of rows of any height, kept apart so it is measured without a canvas); blockDrag (`sortBlocks`: the inspector's own rows dragged into order by a `Grip` (the row's mark, wrapped: dots once it drags, lit under the pointer while the block is washed), a block being every row it covers, #310); Knob + knob (#378: a slider bent into a circle, its name above and a number field below; where a value sits on the turn and what a drag or the wheel makes of it, kept apart so it is measured without a canvas) src/viewport/ WorldViewport (the drawing of a world or a holder, the base of every view; it draws from a `ViewSource` — the project and word of a change — so a released game draws without the editor, #266; a view through a camera keeps only what that camera `Sees` and lays one @@ -245,7 +245,8 @@ src/viewport/ WorldViewport (the drawing of a world or a holder, the ba CPU and laid side by side onto one canvas, so a frame is cut out like a texture frame), SoundLibrary + fxNodes (what each stage of a sound FX becomes as Web Audio nodes, and how the knobs of a playing voice move) + spectrum (#374: the taps of the voices and - buses playing, and the analysers they feed while a node is listened to) + fxWorklet (the AudioWorklet of the effects Web Audio has no node for: a + buses playing, and the analysers they feed while a node is listened to, several nodes at once, and the + loudest sample of the last stretch on each side, #378) + fxWorklet (the AudioWorklet of the effects Web Audio has no node for: a `Source` plays a sound of its own, a way with a Pitch, #317, or a Chip, #329; an `Effect` sits between two stages, the pitch of a bus, #346, a Bitcrusher, a Phaser, a Flanger, a Limiter, a Tremolo or a Vibrato; `Start` keeps either silent until its voice starts, @@ -418,7 +419,10 @@ src/editor/sound/ SoundEditor (#343: header with Play, the clip's direction src/editor/soundfx/ SoundFxEditor (#311: the graph, a sound to try it on, Play and Stop), PreviewParams (the header's fields for the parameters a tab plays with, shared with the Instrument tab), EqEditor + eqView (#367/#370: the Parametric EQ in a tab of its own, its handles on - the picture, and the spectrum behind the curve, #374) + the picture, and the spectrum behind the curve, #374), CompressorEditor + + compressorView (#378: the Compressor's tab, its knobs, its curve with threshold and + ratio to drag, and the levels in and out tinted where it squeezes), nodeTabs (which + nodes open a tab of their own) src/editor/instrument/ InstrumentEditor (#331: the graph over the keys, Preset, Randomize, Mutate, Audition), PianoKeyboard + keys (the keys drawn, played by the pointer and the computer keyboard) src/editor/routing/ RoutingEditor (#351: the Master routing tab, one per project) diff --git a/Development.md b/Development.md index 1263804..e881009 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 # 1941 editor checks, screenshots in /tmp/gm-e2e-*.png +node tools/seed-e2e.mjs && node tools/e2e.mjs # 1947 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 60fe9fb..16b3128 100644 --- a/Sounds.md +++ b/Sounds.md @@ -435,7 +435,7 @@ response of a Reverb, and as what the Sound FX tab tries out. Its row means noth | **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). | | **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. | -| **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` (0) blends that with the gain that brings a sound at full scale back to full scale. See [below](#compressor-and-limiter). | +| **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` (0) blends that with the gain that brings a sound at full scale back to full scale. A double-click on the node opens its tab: a knob for each number, 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` (1) raises the limited sound towards `ceiling` (-1 dB). Nothing ever goes above the ceiling. 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). `output` (0 dB) and `auto-gain` (1) set the level of the bent sound, `wet` (1) how much of it is heard instead of the sound itself. 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). | @@ -725,6 +725,25 @@ Both (#326) work on the whole sound passing them: on a voice that is one sound, with a Limiter is let go only once what the Limiter still holds is out. - Every number moves with *Set sound FX parameters*: dB values evenly, times and the ratio in their ratio. +- **The Compressor's tab** (#378): a double-click on the node opens it, one tab per node, named + and closed like a [Parametric EQ's](#parametric-eq). It has three parts: + - **Knobs**, one for each of the seven numbers. Drag up or down to turn one: 200 px is the + whole turn, with Shift a tenth of that. A notch of the wheel turns it by a fiftieth. + A double-click sets the default, and a click on the number below lets you type one. Attack, + release and ratio give most of the turn to the low end, where they are used. A knob whose + input is linked is greyed: the link decides its value. + - **The curve**, level in across and level out up, both -60 to 0 dB, before the makeup and + auto-gain. Two points are dragged: the one where it starts to bend sets `threshold`, the one + at its end sets the `ratio` that ends there. A dot on the curve is the level coming in now. + - **The levels** of the last 4 s, newest at the right, +6 to -60 dB: grey what comes in, in + the colour of a sound port what goes out. Where the compressor takes more than 0.5 dB away, + the stretch below the level coming in is tinted by as much as it takes. The makeup is left + out of that, so a raised sound still shows what was squeezed. A level is the loudest sample + of the last 25 ms. The line across is the threshold. + + Each drag, notch or typed number is one undo step. The levels come from the same taps as the + EQ's spectrum: a gain of 1 in front of the node and behind it, fed into an analyser only while + the tab is seen. An EQ's tab and a Compressor's can be listened to at once. - **Measured** (2026-10-07, e2e 34, Chrome offline): an impulse came out of the Compressor 288 samples late and out of the Limiter 240. A 1 kHz sine at full scale through a hard-knee Compressor at -12 dB and 4:1 came out at +0.19 dB with full auto-gain and at -8.81 dB with none