Filter-chain bypass action: route a node through a processing chain or past it #15

Open
opened 2026-11-12 23:17:21 +01:00 by omp-deck · 0 comments
Collaborator

Why

A processing chain inserted into a signal path — noise suppression and EQ on a microphone being the canonical case — is something you want to engage and disengage frequently and without leaving what you are doing. Doing that by hand means rewiring links in a patchbay, which is exactly the kind of graph surgery this daemon already automates.

A button that flips a node between "through the chain" and "straight past it" turns that into one press, with the LED showing which path is live.

This is kept deliberately generic: the action should express "insert this processing stage into the path, or bypass it", independent of what the chain actually does. Concrete requirements will be added later.

Scope

A new toggle action, assignable to an LED-capable button, that switches a node's signal path between two routings:

  • engaged: source → chain → destination
  • bypassed: source → destination directly

The LED reflects which routing is currently live, derived from the actual link state rather than from a remembered decision, so that a change made outside the daemon is still shown correctly.

Surface area

This is close to the existing connection action and should reuse its machinery rather than grow a parallel implementation:

  • AudioService::SetConnectionState / GetConnectionState already create and destroy links between matched nodes (includes/AudioService.h).
  • Action_Connection already tracks link state through on_link_created / on_link_removed, enforces a desired state when nodes appear, and drives the LED from the observed state (src/Actions/Action_Connection.cpp).
  • NodeMatcher lets every endpoint be a pattern list, so the chain's own nodes can be matched even if their names are not fully stable.

The difference from connection is that this is not one link toggling on and off: it is a choice between two routings, where engaging one implies tearing down the other. Doing it as two independent connection actions would leave a window with both paths live (doubled signal) or neither (silence), so the swap needs to be a single operation.

Open questions

  • Configuration shape. Minimally: source, destination, and the chain's input and output. Whether the chain is given as one node or as a separate in/out pair depends on how filter-chain nodes present themselves.
  • Is bypass a direct link, or no link at all? For a mic feeding an application, "bypassed" plausibly means the raw source connects directly. For an insert on an output it might mean something else. The action may need both behaviours.
  • Ordering during the swap. Make-before-break risks a brief doubled signal, break-before-make a brief dropout. One has to be chosen and documented.
  • Chain not present. If the chain's nodes do not exist (module not loaded), the action should presumably fall back to the direct path and show the LED as bypassed, rather than disconnecting everything.
  • Default state on startup, as connection already has via default-state.
  • Multi-channel / multi-port nodes: whether link creation needs explicit port mapping, or whether the existing port handling is sufficient.

Acceptance

  • A button toggles a node's path between "through the chain" and "direct", with no state where both paths or neither path is live once the toggle has settled.
  • The LED shows the current routing and stays correct when links are changed outside the daemon.
  • Behaviour is defined for the chain's nodes appearing and disappearing at runtime.
  • The action is configurable for any chain; nothing about a specific filter is hardcoded.
  • README documents the action and its parameters.

Out of scope

  • Loading, unloading, or configuring the filter-chain itself. The chain is assumed to exist as PipeWire nodes.
  • Controlling parameters inside the chain (EQ gain, suppression strength). That is a separate continuous-control feature.
## Why A processing chain inserted into a signal path — noise suppression and EQ on a microphone being the canonical case — is something you want to engage and disengage frequently and without leaving what you are doing. Doing that by hand means rewiring links in a patchbay, which is exactly the kind of graph surgery this daemon already automates. A button that flips a node between "through the chain" and "straight past it" turns that into one press, with the LED showing which path is live. This is kept deliberately generic: the action should express "insert this processing stage into the path, or bypass it", independent of what the chain actually does. Concrete requirements will be added later. ## Scope A new toggle action, assignable to an LED-capable button, that switches a node's signal path between two routings: - **engaged**: source → chain → destination - **bypassed**: source → destination directly The LED reflects which routing is currently live, derived from the actual link state rather than from a remembered decision, so that a change made outside the daemon is still shown correctly. ## Surface area This is close to the existing `connection` action and should reuse its machinery rather than grow a parallel implementation: - `AudioService::SetConnectionState` / `GetConnectionState` already create and destroy links between matched nodes (`includes/AudioService.h`). - `Action_Connection` already tracks link state through `on_link_created` / `on_link_removed`, enforces a desired state when nodes appear, and drives the LED from the observed state (`src/Actions/Action_Connection.cpp`). - `NodeMatcher` lets every endpoint be a pattern list, so the chain's own nodes can be matched even if their names are not fully stable. The difference from `connection` is that this is not one link toggling on and off: it is a choice between two routings, where engaging one implies tearing down the other. Doing it as two independent `connection` actions would leave a window with both paths live (doubled signal) or neither (silence), so the swap needs to be a single operation. ## Open questions - **Configuration shape.** Minimally: source, destination, and the chain's input and output. Whether the chain is given as one node or as a separate in/out pair depends on how filter-chain nodes present themselves. - **Is bypass a direct link, or no link at all?** For a mic feeding an application, "bypassed" plausibly means the raw source connects directly. For an insert on an output it might mean something else. The action may need both behaviours. - **Ordering during the swap.** Make-before-break risks a brief doubled signal, break-before-make a brief dropout. One has to be chosen and documented. - **Chain not present.** If the chain's nodes do not exist (module not loaded), the action should presumably fall back to the direct path and show the LED as bypassed, rather than disconnecting everything. - **Default state on startup**, as `connection` already has via `default-state`. - **Multi-channel / multi-port nodes**: whether link creation needs explicit port mapping, or whether the existing port handling is sufficient. ## Acceptance - A button toggles a node's path between "through the chain" and "direct", with no state where both paths or neither path is live once the toggle has settled. - The LED shows the current routing and stays correct when links are changed outside the daemon. - Behaviour is defined for the chain's nodes appearing and disappearing at runtime. - The action is configurable for any chain; nothing about a specific filter is hardcoded. - README documents the action and its parameters. ## Out of scope - Loading, unloading, or configuring the filter-chain itself. The chain is assumed to exist as PipeWire nodes. - Controlling parameters *inside* the chain (EQ gain, suppression strength). That is a separate continuous-control feature.
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
lucas/nanoKontroller2#15
No description provided.