Skip to content

Aetherial Audio: Channel Strip — unified TX DSP window #2301

@ten9876

Description

@ten9876

Plan: Aetherial Audio Channel Strip — unified TX DSP window

Goal

Centralize all 7 Aetherial Audio TX DSP control panels into a single
window — the Aetherial Audio Channel Strip — laid out per the
maintainer's concept render (CS-7 "Studio TX Processor"). The
existing per-stage applets and floating editors stay in place during
multi-day iteration so the live UX is never disturbed; once the strip
is feature-complete, the per-stage applets/editors are removed and
the strip becomes the canonical control surface.

Motivation

  • TX DSP is currently 7 floating editors + a CHAIN tile. Power users
    open 3–4 editors at once and tile them by hand. A single window
    removes the tiling work and lets us show the full signal flow.
  • The render establishes a "channel strip" mental model that maps
    cleanly to the chain order radio engineers already think in.
  • The work is multi-day. A duplicate-then-cut-over approach lets us
    iterate freely on the strip without touching anything that's
    currently working.

Architecture decisions

Coexist via duplication (not extraction)

For each of the 7 stages we copy the existing editor source to a
new Strip*Panel.{h,cpp} file, swap the class name, and leave the
engine wiring identical. The strip embeds the duplicates; the live
applets and floating editors keep using the originals.

Rejected alternative: extract a reusable *Body widget per stage
(the AetherDspWidget-from-AetherDspDialog pattern). Cleaner code
shape long-term, but lands a 7-stage refactor during a multi-day
iteration on visual fit — every dimension tweak risks regressions in
the live editors. Duplication trades a temporary code-doubling tax
for total isolation; cutover is a single delete pass.

Naming: Strip*Panel

StripTubePanel, StripGatePanel, StripEqPanel, StripCompPanel,
StripDeEssPanel, StripPuduPanel, StripReverbPanel. Easy to
grep, distinct from the live Client*Editor, makes the cutover
delete unambiguous.

Launch path: Easter-egg nub

Tiny unlabeled button on the AetherialAudio (tx_dsp) container
header, bottom-right corner, color one shade lighter than the panel
background. Click toggles strip visibility. No menu item, no tooltip,
no label — discoverable only to people who know it's there. When the
strip becomes the canonical surface, the nub graduates to a real
"Channel Strip" button.

Bypass: handled by horizontal CHAIN at the top of the strip

The render shows the chain redrawn as one long horizontal link at
the top of the unified window. We embed (or duplicate, see below)
the existing ClientChainWidget there; per-stage bypass continues
to work via the chain tile's existing single-click semantics.

The stage panels themselves do not sprout bypass affordances in
the first iteration — bypass is a chain-tile concern only. (Some
stages already render a "BYPASS" badge in their editor body when
disabled; that visual state is preserved by the duplicate.)

Stages (mapped to render)

Render section Source editor Duplicate target
Tube Preamp ClientTubeEditor StripTubePanel
Expander / Gate ClientGateEditor StripGatePanel
10-Band Parametric EQ ClientEqEditor (+ ClientEqEditorCanvas) StripEqPanel (+ StripEqCanvas)
Compressor ClientCompEditor StripCompPanel
De-Esser ClientDeEssEditor StripDeEssPanel
Exciter · PooDoo ClientPuduEditor StripPuduPanel
Reverb ClientReverbEditor StripReverbPanel

Layout (iteration 1)

┌────────────────────────────────────────────────────────────┐
│ [horizontal CHAIN: MIC → TUBE → EQ → DESS → COMP → GATE …] │
├────────────────────────────────────────────────────────────┤
│  Tube Preamp           │  Expander / Gate                   │
├────────────────────────┴────────────────────────────────────┤
│  10-Band Parametric EQ  (full width)                        │
├────────────────────────┬────────────────────────────────────┤
│  Compressor            │  De-Esser                          │
├────────────────────────┼────────────────────────────────────┤
│  Exciter (PooDoo)      │  Reverb                            │
└────────────────────────┴────────────────────────────────────┘

Footer elements from the render (TX OUTPUT meter, PHASE / DIM /
MASTER / MUTE, S/N decoration, header status LEDs) are iteration
2+
— not part of this plan.

Step-by-step

Step 1 — Launch nub on the AetherialAudio container header

  • File: src/gui/AppletPanel.cpp (and possibly the
    ContainerWidget header bar code).
  • Add a small QPushButton (or QToolButton) with no text, fixed
    size ~10×10 px, background color one step lighter than the
    panel's #0f0f1a (or whatever the current tx_dsp header
    background resolves to — check at the styling code).
  • Place it at the bottom-right of the tx_dsp container header,
    not in the row with the existing pop-out / close affordances.
  • Wire clicked to a new slot MainWindow::toggleAetherialStrip()
    — initially a no-op stub; will be wired to actual strip
    show/hide in step 4.
  • AppSettings key reserved: AetherialStripVisible (default
    False).

Acceptance: Nub renders; clicking does nothing (stub). No
regression in container drag / pop-out / close behavior. Build clean.

Step 2 — Duplicate the 7 control panels

One commit per stage. Each commit:

  • Copies ClientFooEditor.{h,cpp} to StripFooPanel.{h,cpp}.
  • Renames the class everywhere inside (sed-style).
  • Adjusts include guards.
  • Adds the new files to CMakeLists.txt.
  • Does NOT instantiate the duplicate anywhere — it's dead code
    that compiles. No call sites added until step 3.
  • Does NOT modify the original editor.

Order suggestion (smallest/safest first to build confidence):

  1. StripReverbPanel
  2. StripPuduPanel
  3. StripDeEssPanel
  4. StripCompPanel
  5. StripGatePanel
  6. StripTubePanel
  7. StripEqPanel (drag along StripEqCanvas since the EQ editor
    uses a paint helper)

Acceptance per commit: Build clean; no symbol collisions; no
behavior change in the live applet/editor (since nothing instantiates
the duplicate yet).

Step 3 — Build the AetherialAudioStrip window

  • New files: src/gui/AetherialAudioStrip.{h,cpp}.
  • class AetherialAudioStrip : public QWidget (toplevel — no parent
    for window-manager status; Qt::Window flag).
  • Constructor takes AudioEngine* and RadioModel* (whatever the
    underlying editors need — pattern-match on the existing editors'
    constructors).
  • Internally:
    • Vertical layout.
    • Top: horizontal ClientChainWidget (or a strip-local
      StripChainWidget duplicate if we want freedom to restyle the
      chain layout — likely yes).
    • 4 rows beneath, per the layout diagram. Use QGridLayout with
      column-spanned cells for the EQ row.
    • Each cell holds one Strip*Panel.
  • Window title: "Aetherial Audio — Channel Strip".
  • Persists size/position via AppSettings keys
    AetherialStripGeometry, AetherialStripVisible.

Acceptance: AetherialAudioStrip exists and can be
manually instantiated and shown; all 7 stage panels render; engine
parameter changes round-trip both ways (strip ↔ engine ↔ live editor)
because both surfaces share the same AudioEngine atomics.

Step 4 — Wire the nub

  • MainWindow::toggleAetherialStrip() lazy-creates the strip and
    toggles visibility.
  • Restore AetherialStripVisible on startup.
  • Save geometry on close.

Acceptance: Click nub → strip appears. Click nub again → strip
hides. Quit + relaunch with strip visible → strip reopens at last
position/size. No regression to per-stage applets/editors.

Step 5 — Iterate dimensions, fit, polish

This is the multi-day phase.

  • Tune knob sizes / label widths / row heights inside each
    Strip*Panel for the unified layout.
  • Add setCompactMode(bool) per panel as needed (mirroring the
    AetherDspWidget pattern).
  • Adjust grid stretches and minimum sizes on AetherialAudioStrip.
  • Visual fixes only — no behavior changes, no engine wiring changes.
  • Ship-as-you-go: small PRs, each gated on the nub still working
    and the live applets/editors still untouched.

Acceptance: Strip looks and behaves like the concept render at
its target window size; all controls are reachable; nothing
overflows; per-stage signal flow is correct.

Step 6 — Cutover (separate plan, not in this issue)

Out of scope for this issue — tracked separately when iteration 5
converges. Sketch:

  • Delete Client*Applet and Client*Editor files.
  • Drop them from AppletPanel's tx_dsp container.
  • Promote the nub to a real "Channel Strip" toolbar / menu entry.
  • Migrate any AppSettings keys that lived on the per-stage widgets
    if they aren't already engine-side.

Risks

  • Code doubling during iteration. Bug fixes to engine-side
    signal handling may need to be mirrored to both live editor and
    strip duplicate if they touch widget code (rare; most engine
    fixes are in AudioEngine itself, which both surfaces share).
    Mitigation: keep iteration phase short.
  • EQ canvas duplication. ClientEqEditor uses a paint helper
    (ClientEqEditorCanvas). The duplicate must clone both. Verify
    no static state that would be shared incorrectly.
  • Window-state persistence collisions. Strip geometry keys must
    not collide with existing applet pop-out keys.
  • Nub discoverability. Intentional Easter egg; document in the
    cutover plan when promoting it.

Out of scope for iteration 1

  • Footer (TX OUTPUT meter / PHASE / DIM / MASTER / MUTE).
  • Header status LEDs (SIG / TX OUT / POWER / CLIP / BYPASS).
  • "2-STAGE CHAIN" preset selector.
  • LIMIT button on compressor (we don't have a separate limiter
    stage).
  • AIR knob on de-esser (if not already a control on the existing
    editor).
  • S/N decoration.
  • Any rebranding (CS-7 / Studio TX Processor labels).
  • Removal of per-stage applets/editors (step 6, separate issue).

🤖 Plan drafted by Claude. Local copy at ~/.claude/plans/aetherial-audio-strip-unified-window.md.

73, Jeremy KK7GWY & Claude (AI dev partner)

Metadata

Metadata

Assignees

No one assigned

    Labels

    GUIUser interfaceNew FeatureNew feature requestaudioAudio engine and streamingawaiting-responseWaiting for reporter to provide additional informationenhancementImprovement to existing featuremaintainer-reviewRequires maintainer review before any action is taken

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions