Documentation

Bass Management

Semantic type: BassManagement · Category: basics

Routes managed main-channel bass and dedicated LFE inputs to selected subwoofer outputs.

Some configurations use an external asset. See Assets and bundles for resolver and bundle contracts.

Contract

  • Seeded: yes
  • Catalog sample rates: not declared; this does not mean unsupported
  • Assets: impulseResponse (impulseResponse)
  • Catalog-declared latency: dynamic; depends on phase, taps
Semantic name Python constructor keyword Type / count Default Unit Range or values
phase phase string / 1 "IIR" Not declared in catalog IIR, Linear
taps taps string / 1 "16384" Not declared in catalog 8192, 16384, 32768
roles roles integer / 16 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] Not declared in catalog 0 … 3
frequencies frequencies number / 16 [80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80] Hz 20 … 300
slopes slopes integer / 16 [24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24] dB/oct 24, 48, 96
routes routes integer / 16 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] Not declared in catalog 0 … 65535
subs subs integer / 1 0 Not declared in catalog 0 … 65535
lfeFrequency lfe_frequency number / 1 120 Hz 20 … 300
lfeSlope lfe_slope integer / 1 24 dB/oct 24, 48, 96
lfeLowpass lfe_lowpass boolean / 1 false Not declared in catalog Not declared in catalog
bassGain bass_gain number / 1 0 dB -24 … 12
lfeGain lfe_gain number / 1 0 dB -24 … 12
headroom headroom number / 1 0 dB -24 … 0
routeInversions route_inversions integer / 16 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] Not declared in catalog 0 … 65535

Bass Management channel roles and routing

roles, frequencies, slopes, routes, and routeInversions each contain exactly 16 entries. Entry n describes input channel n (zero-based). The channel role codes are:

Value Role Output when Sub routing is configured
0 Full Range The full signal stays on the matching main output.
1 Managed The high-pass signal stays on the matching main output; low frequencies go to the selected Sub outputs.
2 LFE The signal goes only to selected Sub outputs, with optional low-pass filtering controlled by lfeLowpass.
3 Unused This input contributes no signal. The same channel can still serve as a Sub output receiving other inputs.

subs is an output bit mask: bit m reserves output channel m for Sub signals. For example, subs=4 (1 << 2) selects the third output. Every Managed or LFE input needs a nonzero route, and every route must target selected Sub outputs. Main outputs (roles 0/1) cannot also be Sub outputs. A contribution routed to multiple Sub outputs is divided equally among those destinations; contributions from different inputs are summed. headroom scales all outputs. Use channel="all" and provide a bus wide enough for every configured input and Sub output. The effect preserves the bus width: for stereo plus one Sub, pass three channels with a silent third input slot.

The library defaults are all roles 0 and subs=0. With subs=0, routing is unconfigured and every channel passes through, including inputs marked Managed, LFE, or Unused (with headroom and the selected phase delay). The app initializes available input roles to Managed, but that UI initialization does not change the library constructor defaults.

This IIR example splits 40 Hz and 1 kHz stereo input at 80 Hz and sends the bass to output 2. The -6 dB headroom accommodates the summed left/right bass:

import numpy as np
import effetune as et

rate = 48_000
t = np.arange(rate, dtype=np.float64) / rate
mono = (0.1 * np.sin(2 * np.pi * 40 * t)
        + 0.1 * np.sin(2 * np.pi * 1000 * t)).astype(np.float32)
audio = np.zeros((3, rate), dtype=np.float32)
audio[0] = audio[1] = mono  # Channels 0/1: left/right; channel 2: silent Sub slot.
chain = et.Chain([et.BassManagement(
    phase="IIR",
    roles=[1, 1, 3] + [0] * 13,
    routes=[4, 4] + [0] * 14,
    subs=4,  # 1 << 2: third output.
    frequencies=[80] * 16,
    slopes=[24] * 16,
    headroom=-6,
)])
output = chain.process(audio, sample_rate=rate)
assert output.shape == audio.shape
assert np.isfinite(output).all()
assert np.max(np.abs(output[2])) > 0.01
steady = np.abs(np.fft.rfft(output[:, rate // 2:]))
assert steady[0, 20] < 0.1 * steady[0, 500]  # Main: 40 Hz below 1 kHz.
assert steady[2, 500] < 0.1 * steady[2, 20]  # Sub: 1 kHz below 40 Hz.
print("Left, right, Sub:", output.shape)

The equivalent Chain v1 JSON can be loaded with Chain.from_preset(document) in Python or createChain(document) in JavaScript, using the same three-channel audio bus:

{
  "version": 1,
  "chain": [
    {
      "id": "bass",
      "type": "BassManagement",
      "channel": "all",
      "parameters": {
        "phase": "IIR",
        "roles": [1, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        "routes": [4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        "subs": 4,
        "frequencies": [80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80],
        "slopes": [24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24],
        "headroom": -6
      }
    }
  ]
}

Bass Management route polarity

routes and routeInversions are 16-entry integer bit-mask arrays. Entry n applies to input channel n, and bit m selects output channel m, with both indices zero-based. A set bit in routeInversions[n] reverses only that input’s bass or LFE contribution on the corresponding enabled route; it does not affect the matching main output.

When subs is nonzero, routeInversions[n] must be a subset of routes[n], and both route masks must target only output channels selected by subs. A configuration that violates either condition raises ValidationError.

Linear low-pass coefficient asset

An assets.impulseResponse reference is required when phase="Linear", subs is nonzero, and at least one input has either role Managed or role LFE with lfeLowpass=true. IIR mode, unconfigured routing, and Linear routing containing only Full Range, Unused, or unfiltered LFE inputs do not use a filter asset; supplying one for those configurations raises AssetError.

Supply finite planar float32 coefficients at exactly the processing sample rate. There is one IR channel per Managed or low-pass-filtered LFE input, ordered by ascending input channel index. Each IR channel has exactly Number(taps) frames: 8192, 16384, or 32768. An automatic topology is accepted and expanded to diagonal matrix paths. With explicit topology="matrix", the paths must be (inputSlot=n, outputSlot=n, irChannel=k) in that same order; k counts only the filtered inputs. Set Python AssetData.input_count to the processing bus width; JavaScript derives it from the processing bus when preparing ETA1 assets. These diagonal paths compute each input’s low-pass signal; routes then distribute it to Sub outputs.

Design the low-pass FIR around sample Number(taps)/2. The main signal is the delayed input minus the low-pass signal. The reported delay is Number(taps)/2 + 128 samples, including the convolution block delay. The library consumes your coefficients; it does not synthesize them from frequencies/slopes or lfeFrequency/lfeSlope. Changing those values requires preparing corresponding coefficients and a new stream. Use the desired cutoff/slope for each Managed input and the LFE settings for each filtered LFE input; duplicate a filter channel when two inputs use identical coefficients.

This complete NumPy example prepares an 80 Hz, 24 dB/oct low-pass magnitude curve with a -6 dB crossover point, centers and tapers its FIR, and uses two identical IR channels for left/right. The Sub output carries their summed low frequencies. It pads the input to retain delayed output; offline processing otherwise returns the same number of frames as its input.

import numpy as np
import effetune as et

rate, taps, cutoff, slope = 48_000, 8192, 80, 24
# Sample a low-pass magnitude curve and center its FIR at taps / 2.
fft_size = 2 * taps
frequencies = np.fft.rfftfreq(fft_size, 1 / rate)
magnitude = 1 / (1 + (frequencies / cutoff) ** (slope / (20 * np.log10(2))))
bins = np.arange(magnitude.size)
spectrum = magnitude * np.exp(-2j * np.pi * bins * (taps / 2) / fft_size)
impulse = np.fft.irfft(spectrum, n=fft_size)[:taps]
indices = np.arange(taps)
edge = taps * 0.05
window = np.ones(taps)
window[indices < edge] = 0.5 - 0.5 * np.cos(np.pi * indices[indices < edge] / edge)
window[indices > taps - edge] = (
    0.5 - 0.5 * np.cos(np.pi * (taps - indices[indices > taps - edge]) / edge)
)
coefficients = np.ascontiguousarray(np.stack([impulse * window] * 2), dtype=np.float32)
asset = et.AssetData(
    coefficients, rate, topology="matrix",
    paths=(et.ConvolutionPath(0, 0, 0), et.ConvolutionPath(1, 1, 1)),
    input_count=3,
)
chain = et.Chain([et.BassManagement(
    phase="Linear", taps=str(taps),
    roles=[1, 1, 3] + [0] * 13,
    routes=[4, 4] + [0] * 14, subs=4,
    frequencies=[cutoff] * 16, slopes=[slope] * 16, headroom=-6,
    assets={"impulseResponse": "memory:lowpass"},
)], asset_resolver=lambda reference: asset if reference == "memory:lowpass" else None)
t = np.arange(rate, dtype=np.float64) / rate
audio = np.zeros((3, rate), dtype=np.float32)
audio[0] = audio[1] = 0.1 * np.sin(2 * np.pi * 40 * t) + 0.1 * np.sin(2 * np.pi * 1000 * t)
delay = chain.latency_samples(rate, channels=3)
# Include zeros after the signal so delayed output is retained.
output = chain.process(np.pad(audio, ((0, 0), (0, delay))), sample_rate=rate)
aligned = output[:, delay:delay + rate]
assert delay == taps // 2 + 128
assert np.isfinite(aligned).all()
assert np.max(np.abs(aligned[2])) > 0.01
steady = np.abs(np.fft.rfft(aligned[:, rate // 2:]))
assert steady[0, 20] < 0.1 * steady[0, 500]
assert steady[2, 500] < 0.1 * steady[2, 20]
print("Latency and left/right/Sub shape:", delay, aligned.shape)

EffeTune app documentation

The following section is reproduced from the English EffeTune app documentation. Its parameter names and values describe the app UI and can differ from semantic API parameters through transforms or value maps. The generated contract above is authoritative.

Bass Management

Bass Management sends the low-frequency part of selected main channels and any dedicated LFE input to the subwoofer outputs you choose. It is useful when a multi-channel output drives main speakers and one or more subwoofers. Each managed main keeps its higher frequencies on the same output channel, while the subwoofers receive the bass routed to them. The plugin requires the WASM DSP engine.

Until you select a Sub Output, Bass Management does not split bass or distribute it to subwoofers, so the input channels pass through without a crossover. A new instance starts with the actual bus channels set to Managed and no Sub Outputs selected.

Select All in the effect bus routing, then make the output bus wide enough for every main and subwoofer channel you will use. The channel table always shows the input role and its selected subwoofer outputs. A subwoofer output cannot also be a Full Range or Managed main channel. An LFE input may use the same channel number as a subwoofer output; its signal is collected before outputs are assembled, so it is sent only once.

Sound Enhancement Guide

  • For stereo with two subwoofers, use a four-channel bus. Set channels 1 and 2 to Managed, then select channels 3 and 4 as Sub Outputs. Their roles change to LFE automatically; use the Matrix to keep or turn off each main-to-subwoofer route.
  • For surround material, set only the actual main channels to Managed and set the source LFE channel to LFE. Choose the subwoofer outputs explicitly. Do not leave an LFE source as a main channel if it should play only through subwoofers.
  • Start each managed main at 80 Hz and 24 dB/oct. Raise its crossover frequency when that speaker has limited bass extension; use a steeper slope when you need less overlap. Confirm the speaker’s usable range before increasing playback level.
  • When one input is sent to several subwoofers, Bass Management divides that electrical signal equally among them. This does not prevent peaks when several inputs or subwoofers combine. Begin with Headroom below 0 dB when needed, watch the following level meter, and place Brickwall Limiter later in the chain if peak control is required.
  • Use LFE Gain only when your source chain has not already applied its intended LFE level adjustment. It does not add an automatic cinema-style level correction.
  • Follow Bass Management with any per-subwoofer high-pass filter, EQ, or polarity adjustment. Use MultiChannel Panel afterwards for individual trim, mute/solo, and up to 30 ms of placement delay; add a limiter last if needed.

Parameters

  • Phase
    • IIR - Provides the lower-latency crossover mode. It changes phase around the crossover frequency.
    • Linear - Keeps the crossover split time-aligned, but adds visible processing latency and can produce pre-ringing. Use it only when that delay is acceptable.
  • Taps - Selects the Linear filter length: 8192, 16384, or 32768. More Taps improve low-frequency and steep-slope accuracy, while increasing preparation time and latency. The initial setting is 16384; this control affects Linear mode.
  • Headroom - Applies the same attenuation to every output. Lower it when combined bass and LFE signals leave too little level margin.
  • Bass Gain - Adjusts the level of bass separated from Managed main channels before it is mixed into the selected subwoofer outputs.
  • LFE Gain - Adjusts the level of LFE inputs before they are mixed into the selected subwoofer outputs.
  • Channel Role - Sets each input channel to Full Range, Managed, LFE, or Unused.
    • Full Range keeps the source on its matching main output without sending bass to a subwoofer.
    • Managed keeps the high-frequency part on its matching main output and sends its low-frequency part to the selected subwoofers.
    • LFE sends the source only to the selected subwoofers. It does not pass through its matching output as a main channel.
    • Unused reserves an input channel, normally for a subwoofer output.
  • Crossover Frequency - Sets each Managed channel’s crossover point from 20 to 300 Hz. Higher values send more of that main channel’s bass to subwoofers.
  • Slope - Sets the crossover steepness for each Managed channel: 24, 48, or 96 dB/oct. Higher values narrow the overlap between main and subwoofer output.
  • Sub Outputs - Selects the subwoofer output channels used by a Managed or LFE input. Selecting a channel changes its Channel Role to LFE. A newly selected output starts with every current bus input routed ON at normal polarity; use the Matrix to turn off an individual route. When no Sub Outputs are selected, bass splitting and subwoofer routing stop, and the input channels pass through without a crossover.
  • ON and Ø - In each channel-table cell, ON sends that Managed or LFE input to the selected subwoofer output. Ø reverses polarity only for that input-to-subwoofer path, which can help align it with your measured or audible result. Ø is available only while ON is selected; clearing ON also clears Ø. It does not change the input’s main output.
  • LFE Low-pass - When enabled, limits LFE content above the selected LFE Frequency. Leave it off to keep the source LFE bandwidth unchanged.
  • LFE Frequency and LFE Slope - Set the optional LFE low-pass point from 20 to 300 Hz and its 24, 48, or 96 dB/oct slope. They do not filter the bass already separated from managed main channels.

Filter Preparation

Changing Linear settings can briefly reduce or pause sound while the new filters are prepared. If filter preparation cannot complete, reduce Taps and try again. When a previous active configuration remains usable, it continues playing; otherwise the normal main channels pass through with matching delay, reserved subwoofer outputs are silent, and an LFE source is not played until preparation succeeds.

Bypass and Calibration

Host plugin bypass restores the original channel assignment and audio, so Bass Management routing, subwoofer protection, and delay matching do not continue while it is bypassed. For comparisons or temporary muting that keep the configured wiring, use the later MultiChannel Panel instead.

Linear mode describes the crossover itself. Per-subwoofer IIR high-pass/EQ processing or an intentional relative delay later in the chain changes the phase behavior of the complete system. Save the complete calibrated chain as one preset.

Back to all effects