What actually happens to your audio — every stage, every parameter, and why. To a file, or live: the same chain plays your music and internet radio.
FIR [KAISER] 64-BIT · 30.0M TAPS · FS8 (352.8K/384K) · FLAC 24-BIT · VERIFIED
Try it on Hugging Face no install · up to 30M taps · in your browser →
Windows 10/11 · the filters are already inside · what the difference is
An upsampler is only worth trusting if its signal path is fully documented.
This page is that documentation. Source: github.com/ToxaDev/aura-engine

00Why offline

Every DAC upsamples. It has to — reconstructing an analog waveform from 44,100 samples per second requires an interpolation filter. But a DAC chip does it in real time with a filter of a few hundred to a few thousand taps. AuraEngine removes the real-time constraint entirely: it renders the track offline through FIR filters of 5 thousand to 30 million taps, designed in 128-bit arithmetic, applied in end-to-end 64-bit float. The DAC then receives a 352.8/384/705.6/768 kHz waveform that is already reconstructed — its own filter has almost nothing left to do. How long the filter is, is a setting, not a quality tier: on music a 5k and a 30M render agree below 20 kHz, and what length changes is how steep the wall at the cutoff is (measured).

Since 1.5.0 the same chain also plays. The player prepares the source stages for the whole track ahead of time, runs the convolution on the CPU — or on the graphics card when the CPU cannot keep up — and shows which stages are already in the sound. It plays internet radio the same way, as the stream arrives. The converter stays the place where nothing is rushed: every stage at full length, and the written file verified against the render (the player, radio).

AuraEngine 1.5.0: the rack, Advanced DSP, the player and the list, a file playing The analyzer on a radio stream: S above, O below, on one spectrogram
AuraEngine 1.5.0 — a file playing through the rack it would be converted with, and the analyzer on a FLAC radio stream: S, the stream as it came in, above; O, the output, below.

Nothing is hidden

The engine logs every decision — which filter file it resolved, how many hybrid-phase switches it made, whether true-peak protection acted — and re-decodes every output file to prove it matches the internal math within ±2 LSB. Files that fail are renamed _UNVERIFIED.

Nothing is louder or quieter

Every filter is normalized to exactly unity DC gain (sum(h) == 1.0). One gain is applied to a whole file: the true-peak normaliser, when the render would exceed the ceiling — −0.5 dBTP, or whatever Headroom names; with Album Level on, the tracks of an album share it. The only other is the ISP stage's output limiter, which can hold an over by dipping the gain for about 1.5 ms instead of taking the whole file down. Nothing ever raises a quiet file. Documented, and visible in the log.

01The signature: Hybrid-Phase

Invented for this project. Every FIR filter forces a trade: linear phase keeps the stereo image coherent but pre-rings before every attack; minimum phase hits clean but warps inter-channel timing. Fixed "intermediate-phase" compromise filters carry a little of both flaws, everywhere, all the time. AuraEngine refuses the trade — it renders the track twice in full and switches between the two renders per-transient.

Slow motion ×20 — the HPSS detector sees the hit coming, the engine switches at a zero crossing, the attack lands with no pre-ringing while the sustain keeps linear-phase imaging.

Stereo-linked by construction

One switch plan is computed from the mid signal and applied to both channels at the same sample. The channels can never de-phase against each other — the image stays put while the attacks stay clean.

Engineered to be inaudible

Switches fire only at zero crossings, through a 1 ms fade on a smooth curve (a shorter one used to leave a tick at every switch — fixed in 1.3.2), with a 20 ms anti-chatter hold. The two renders are aligned sample-exact via band-weighted group delay (200–6000 Hz) before blending. Verification: doc 06.

What the detector keys on, precisely. It measures the rise of percussive energy between analysis frames — the start of a sound. It does not measure whether the filter would ring on this material; it knows nothing about the transition band. On music the two coincide, because an attack is both a beginning and a broadband event. On synthetic material they come apart: a steady square wave with arbitrarily steep edges produces no trigger at all, because nothing begins. So the honest name for the stage is minimum phase on attacks, and there is a known blind spot in the first ~24 ms of a file — both are written up, with the numbers, in doc 06 §1.

02The signal path

One file, start to finish. Stages marked optional are user toggles; everything else always runs. The entire CPU sample path is 64-bit float — there is no f32 truncation anywhere between decode and dither.

1
Decode converter/decode.rs · Symphonia

FLAC, WAV, MP3, OGG and AAC/M4A are decoded by the pure-Rust Symphonia library straight into f64 — HE-AAC by the FDK AAC decoder built into the program, at its full rate, and a chained Ogg file to the end of its last link. Integer PCM keeps every bit — a 24-bit master divides by 2²³ exactly — and a lossy decoder's output is kept as it comes, including the samples that sit above full scale, which MP3 and AAC legitimately produce. Nothing is clipped on the way in; the output ceiling decides what happens to those peaks. MP3/AAC encoder padding is trimmed using gapless metadata.

Why it matters: the chain starts bit-exact, and it starts with the whole signal. A 24-bit FLAC enters with all 24 bits intact; an MP3 that peaks at +7 dBFS enters at +7 dBFS.

f64 decode, no clampgapless trimno external tools
2
DC block pipeline/prepare.rs

Per-channel mean removal (default) or an optional 2 Hz single-pole IIR high-pass with the exact closed-form pole r = e^(−2π·2/fs).

Why it matters: a fraction of a millivolt of DC offset would make a 30M-tap filter ring at infrasonic frequencies and poison the true-peak measurement.

static meanor 2 Hz IIR HPF
3
Declip dsp/lab/declip.rs optional · on by default

A master pushed into the rail came back with its peaks flattened. The flat top carries no information about how high the wave was going; the flanks either side of it do. Declip finds those runs and rebuilds the arc across them by constrained autoregressive interpolation, with the rail treated as a lower bound rather than a value.

What clipping leaves behind
The flanks fix the arc; the rail fixes its floor.
A rebuilt peak
The longest flat run in a real track — 57 samples, 1.29 ms — and the arc put back over it, peaking 6.02 dB above the rail.

Why it matters: it refuses far more often than it fires. A run shorter than 17 samples is a limiter working, not a clipper, and the stage stands down and says so — on a 2014 Santana master, twelve tracks, not one run reached the threshold. The output ceiling takes the rebuilt peaks back down, so a declipped file arrives quieter: level-match before comparing. Since 1.5.0 it leaves MP3, AAC, Vorbis and Opus sources as decoded: their samples above full scale are the codec’s, not a clipped master’s.

DC tagrail as a lower boundgated at 17 samples
4
Intersample peak correction dsp/lab/isp.rs optional · on by default

A waveform is not only its samples. Between them the reconstructed signal can go higher than any sample does, and higher than full scale. ISP scans at 4× with a Lanczos kernel and applies the smallest correction that clears the overs. At the end of the chain its second half, a limiter with ~1.5 ms dips, trims the overs the reconstruction itself makes, so the whole file does not come down for a few milliseconds of peaks.

Intersample peaks above full scale
Twenty-three samples of a real track: every sample sits at or below full scale, and the signal between them reaches +3.68 dBTP.

Why it matters: this is where a DAC or a downstream encoder clips a file that never exceeds 0 dBFS. Spans Declip has just rebuilt are left alone — they are deliberately above the old rail, and holding them down is the output ceiling’s job, not this one’s.

ISP tag4× Lanczos scanskips declipped spanslocal output limiter
5
Subsonic filter pipeline/prepare.rs · process.rs optional · on by default, 15 Hz

Runs here, after the repairs above, so a high-pass cannot tilt the flat tops they read. A linear-phase FIR high-pass at the source rate: a Kaiser-windowed sinc low-pass, spectrally inverted, so the response at DC is exactly zero. Flat within 0.01 dB from the corner up — 20, 15 or 10 Hz — and at least 100 dB down from half the corner to DC; 29 813 taps at 44.1 kHz and 20 Hz. Since 1.3.3 the same filter runs a second time at the output rate, after the intersample-peak limiter at the end of the chain: its short gain dips modulate the bass and would put a shelf of infrasound back under the corner.

Why it matters: subtracting the mean removes a constant offset and nothing else. A slow wobble goes through it untouched — one CD in the Audio Science Review thread carries a component near 1 Hz at about 1 % of full scale. This is protection for speakers, not a sound improvement. Linear phase is the point: above the corner nothing moves in level or in time. It costs look-ahead — half the filter — which the converter and the player’s preparation have for free, and a live radio stream pays as a short wait before its first sound.

SUB20 taglinear phase≥100 dB below F/2flat from the cornersecond pass after the limiter
6
Headroom dsp/true_peak.rs

The true-peak ceiling the finished render is normalised to: Off leaves the shipped −0.5 dBTP, and −0.5 / −1.0 / −3.0 dB name their own. Nothing is scaled before the filter.

Why it matters: it is a ceiling, not a target — a file already below it is passed through untouched. Until 1.2.3 this was a gain applied before the convolution, which the absolute ceiling at the far end then cancelled exactly: the control did nothing on any loud master.

Adaptive Headroom (on by default) decides whether to honour that ceiling on this file: it keeps the shipped −0.5 dBTP when the source peak already sits below the target, or when ENOB says the master is a clean high-bit-depth one, and otherwise lowers it as asked. It is a veto on the setting rather than a replacement for it, so with Headroom on Off there is nothing to decide and the stage does not run.

a ceiling, not a gainAHR tagneeds Headroom set
7
Adaptive Apodizer v3 — source forensics converter/apodize.rs optional · any source rate

Masters carry the fingerprints of every filter that ever touched them. The v3 analyzer reads those fingerprints instead of guessing: a time-domain detector isolates the near-Nyquist band, finds the strongest attacks across the track and looks for high-frequency energy rising before them — the signature of a linear-phase brick wall — then measures the ring's exact frequency (per-burst FFT, median across attacks) and places a corrective minimum-phase Kaiser lowpass just below the measured edge, with depth chosen by severity (β=24 strong / β=14 mild). A Welch spectral-cliff detector unmasks fake hi-res — upsampled 44.1/48 kHz masters inside 88.2k+ containers are treated against their original Nyquist — and a mirror-image alias probe catches bad upstream resamplers whose spectral images defeat every other test. A cliff with no pre-ring means the source filter was minimum-phase or already apodized: diagnosed in the log, audio untouched. Static presets (Gentle 20 kHz / Moderate 19 kHz / Strong 18 kHz) are available too.

Why it matters: upsampling scales flaws along with music. Healing the source's pre-ring first gives the big FIR a clean canvas — and tracks that don't need it pass through untouched. Since 1.5.0 it also stands down on a lossy file whose codec has already cut everything below where its cutoff would go: a filter there would only turn the phase.

AA tagmeasured ring cutofffake-hi-res unmaskingmirror-alias probemin-phase · zero pre-ring
8
Upsampling — two paths converter/process.rs

The engine has two resampling architectures, selected by the Polyphase FIR Resampling toggle. Both end at the same output stage.

Standard path any ratio · Rubato + FIR post-filter
  • Rubato SincFixedIn<f64> upsamples first: 512-tap sinc, 512× oversampling, cubic interpolation, ≈−180 dB stopband.
  • The million-tap FIR then runs at the output rate as a spectral-shaping post-filter — partitioned overlap-save, block 32768, Kahan-compensated frequency-domain MAC, parallelized with Rayon.
  • Filter blob missing? The post-filter is skipped with a logged warning — Rubato alone is still a first-class resampler.
Polyphase FIR path integer ratios · the filter IS the resampler
  • The same million-tap filter is decomposed into L sub-filters (h_k[m] = h[m·L+k]) that run at the source rate, in parallel on all CPU cores.
  • Anti-imaging cutoff sits exactly at source Nyquist: fc = src_rate / (2·out_rate). Each source rate takes the filter made for its own ratio — 96 kHz to 384 kHz is ×4 — so a hi-res file keeps what it holds above the CD band.
  • Output length is exactly input × L — no resampler padding at all. Non-integer targets snap down (44.1 kHz × FS8 → 352.8 kHz).
  • Filter blob missing? Hard error — this path refuses to degrade silently.
9
Hybrid-Phase engine hybrid_phase.rs · hpss_native.rs · pipeline/hybrid_mixer.rs optional · ~2× time

Linear-phase filters preserve the stereo field but pre-ring before transients; minimum-phase filters hit hard but smear inter-channel phase. AuraEngine runs both convolutions in full, then a native-Rust HPSS (harmonic/percussive separation) onset detector builds a transient envelope: STFT with time/frequency median masks, 8-band spectral flux, adaptive gate, envelope follower with lookahead. During sustained material you hear the linear-phase branch; one to two analysis frames before each detected attack (~12–23 ms at 44.1 kHz) the engine switches to the minimum-phase branch — at a zero crossing, with a 1 ms fade and a 20 ms anti-chatter hold. The trigger is the start of a sound, not a measurement of where the filter rings.

Why it matters: the switch is stereo-linked — one switching plan computed from the mid signal, applied to both channels at the same sample. Channels never de-phase against each other, so the image stays rock solid while attacks stay clean. The minimum-phase branch is aligned using band-weighted group delay (200–6000 Hz) so both branches sit sample-exact at the perceptual core of the music.

HP tagstereo-linked switchzero-crossing + 1 ms fadeHPSS onset envelope
10
TFS phase dsp/lab/tfs.rs optional · on by default

A different way to spend the phase budget: linear phase below 1.5 kHz, minimum phase above 4 kHz, blended across the band between. The low end keeps the exact timing a linear-phase filter gives it, while the treble is rendered with no pre-ringing ahead of a transient.

Why it matters: the magnitude response is not part of the trade — it matches the linear-phase render to ±0.000003 dB across 20 Hz–20 kHz. Only the phase differs, which is why an ordinary frequency sweep cannot see this stage at all. It is derived from the linear and minimum pair of your filter size on first use and cached — at 30M taps about 12 s and 4.5 GB of RAM, once; since 1.5.0 on a transform twice the filter’s length, which takes its stopband to −193 to −231 dB. It replaces Hybrid-Phase for the file, and the two cannot both run.

TFS tag±0.000003 dB magnitudederived once, then cached
11
Crosstalk cancellation dsp/lab/xtc.rs optional · off by default

Over speakers, each ear hears both of them. XTC cancels what each speaker sends to the wrong ear, so the stereo image is no longer bounded by the speakers themselves. The filter is built from the listening triangle you measure with a tape.

XTC suppression and its head-movement cost
Computed from a symmetric free-field model of the triangle, not measured in a room. Read the right-hand panel first: 20 mm of head movement costs about half the cancellation.

This is the one stage that deliberately colours the signal. Everything else here is a repair or a rendering choice; this rewrites the file for one chair. It will not switch on until the geometry is entered, and the same check is repeated on the Rust side so a hand-edited setting cannot slip past — a canceller built from guessed distances does not cancel less, it cancels the wrong thing. Speakers only, never headphones.

XTC tagneeds a measured triangleone chairnot transparent
12
Album level converter/album.rs optional · on by default · 1.5.0

The true-peak ceiling below is one gain per file, and how deep it cuts depends on what the chain did to that file — so converted one by one, the tracks of an album can come out with their balance changed. The queued files that share a folder and an Album tag are converted as one album: before the first is written every track is prepared and the cut it will need is estimated, and every track takes the deepest of them, in front of its output limiter.

Why it matters: on a 12-track album the spread against the CD went from 6.15 dB to 0.02. The price is level and a wait: an album comes out as quiet as its most demanding track needs, and no track is finished before all of them are prepared. A file without the tag, or the only queued track of its album, converts exactly as before. No filename token — the level goes into the log and an AURA_ALBUM_GAIN tag.

ALBsame folder + Album tagone gain per albumconversion only
13
True-peak limiter converter/dsp/true_peak.rs

4× oversampled intersample-peak scan using an 8-tap Lanczos-4 polyphase sinc interpolator (reflective edge padding, every sample inspected), measured on what the output limiter and the subsonic guard have left. If the reconstructed waveform would exceed −0.5 dBTP (or the Headroom setting), one single linear gain brings the whole file under the ceiling — with Album Level, the album’s shared gain. Quieter material is left bit-exact — no gain is applied at all.

Why it matters: loudness-war masters routinely hide +1…+3 dB intersample peaks. Without this stage they would clip in the DAC's reconstruction filter — as harshness on every loud passage.

ITU-R BS.1770-style−0.5 dBTP ceilingbit-exact when quiet
14
Dither & noise shaping converter/dsp/dither.rs

24-bit TPDF dither with two independent random streams — one per channel, so the dither floor cannot correlate between L and R. At output rates ≤48 kHz a 9th-order Wannamaker psychoacoustic noise shaper pushes the quantization noise where hearing is least sensitive; at hi-res rates pure TPDF is used (the noise floor already sits far above audibility limits in frequency). Samples are clamped to ±(1−LSB) after quantization.

Why it matters: proper dither converts quantization distortion into benign noise — reverb tails and room decay stay intact instead of dissolving into correlated grit.

TPDF 24-bitper-channel RNGWannamaker-9 ≤48 kHz
15
FLAC encode converter/encode.rs · native

Encoded to 24-bit FLAC in-process — no external tool and no subprocess. Frames are written to disk as they fill, so a 90-minute file costs the same peak memory as a three-minute one. The predictor order is searched per block, and the settings were chosen by measuring real converted material rather than by assumption. Cancelled or failed encodes delete the partial file.

24-bit FLACpure Ruststreamingoutput next to source
✓
Bit-perfect verification converter/utils/verify.rs

The file just written is decoded back and compared sample-by-sample against the DSP buffer, with a ±2 LSB tolerance at 24-bit. Pass → ✓ VERIFIED badge. Fail → the file is renamed _UNVERIFIED.flac and flagged in the UI.

Why it matters: this closes the loop. You don't have to trust the pipeline — the pipeline checks itself, on every single file.

every file, every time±2 LSB

03Choosing a path

Both paths share every preparation and output stage. The difference is who does the sample-rate conversion.

Standard (default)Polyphase FIR
ResamplerRubato 512-tap sinc, then FIR post-filter at output rateThe FIR itself, split into L sub-filters at source rate
RatiosAny (44.1 → 384 kHz works directly)Integer only; snaps down otherwise (44.1×FS8 → 352.8 kHz)
Output length~0.4 s trailing resampler padExactly input × L, zero padding
ComputeOne big convolution at output rateL convolutions at source rate, parallel on all cores
TFS phaseStands down, and the row says soRuns
Missing filter blobPost-filter skipped, loggedHard error — no silent downgrade

04GPU acceleration without precision loss

The standard objection to GPU audio is that consumer GPUs compute in f32 — a ~−150 dB noise floor that a 30M-tap convolution would happily amplify. AuraEngine's answer is double-single (DS) arithmetic.

Double-single, enforced at the SPIR-V level

Every complex value on the GPU is a pair of f32s — a high word and an error-correction low word — giving ≈48 effective mantissa bits. The catch: DS math only works if the compiler doesn't "optimize" the correction terms away. WGSL has no way to forbid that, so the compute shaders are written in GLSL with the precise qualifier, compiled to SPIR-V with NoContraction decorations (which Vulkan drivers are required to honour), and loaded through wgpu's SPIRV_SHADER_PASSTHROUGH so no shader translator can touch the operation tree.

Since 1.5.0 both convolvers keep only half of each spectrum — the spectrum of a real signal is symmetric, so the other half held nothing new — and the GPU takes both channels through one complex transform. A 30M ×8 convolver needs 416 MB of video memory instead of 736, and on an RTX 4090 a 30M ×8 conversion runs up to 1.4× faster; the output moves in the last bits only.

Measured result: the GPU pipeline nulls against the f64 CPU reference at ≈−260 dB — about 110 dB below the 24-bit noise floor. A dedicated preflight test (ds_preflight.rs) can prove on any given driver that the two-sum survives compilation.

Adapters without SPIR-V passthrough (e.g. DX12-only) automatically fall back to the CPU convolver — the f64 reference implementation, always available. So does a card that runs out of memory part-way through a batch: since 1.2.5 the refusal is an answer rather than a crash, and the affected filter is convolved on the CPU instead. On the polyphase path the fallback is per phase, so the phases already resident stay on the GPU.

The player uses the same switch differently: it measures whether the CPU can run the chosen filter in real time with room to spare, and moves the convolution to a discrete card only when it cannot — matching the CPU to −280 dBFS, and handing back to the CPU without a gap if the card fails mid-track.

CPU convolverGPU convolver
Precisionf64 + Kahan-compensated MACDS f32 pairs (~48-bit mantissa)
AlgorithmPartitioned overlap-save on half spectra, FFT 65536Partitioned OLA on half spectra, radix-2 FFT in DS, L + iR in one transform
Block size32768 samples256k–2M samples (scales with filter)
Latency (trimmed exactly)2 × block1 × block
Requirement—Vulkan + SPIRV_SHADER_PASSTHROUGH

05Filter design: 128-bit offline, f64 at runtime

The converter deliberately cannot generate filters at runtime. Coefficients come from fir-optimizer, a Python design suite that builds each Kaiser-windowed (β=14) sinc prototype in 128-bit arithmetic (mpmath, 38 significant digits), normalizes DC gain with extended-precision summation, and ships the result as f64 .npy blobs.

A filter per ratio — never rescaled

The matrix covers every conversion the app can perform: 5 tap sizes (5k/1M/5M/10M/30M) × 8 output rates × 2 phase variants = 80 blobs (≈10 GB). Each is designed for its exact ratio — passband to source-Nyquist−2 kHz, stopband from source Nyquist — so the runtime never stretches a filter to a rate it wasn't designed for.

Minimum-phase, done right

The minimum-phase variants are derived by cepstral folding with ≥16× zero-padded FFTs (no time-aliasing), tail-faded with a Hann window, and re-normalized to unity DC gain afterwards. Impulse peak lands at sample zero: mathematically zero pre-ringing.

Measured, not promised

The production 30M-tap blob measures: stopband ≤ −220.9 dB (manifesto requirement: −140 dB), passband ripple ±0.09 nano-dB, unity-gain error 1.1×10⁻¹⁵, transition width 0.05 Hz. Full plots, methodology and the reproduction script: docs/15-measurements.md. Every blob ships with a JSON metadata sidecar, and the runtime logs exactly which file it resolved for each conversion.

06The laws of the DSP core

From DSP_MANIFESTO.md — the axioms every commit is audited against. Violating one degrades the device class; that is the review bar for this codebase.

§1.1

Unity DC gain

No filter may change the track's loudness. sum(h) == 1.0, verified in unit tests for every filter mode.

§1.2

Headroom is a ceiling

The Headroom setting names the output ceiling in dBTP and the true-peak normaliser enforces it at the end. Nothing is scaled before the filter: the source reaches the convolver as decoded. A file already below the ceiling passes through untouched.

§1.3

True-peak at the very end

The intersample scanner sits immediately before quantization, after every processing stage.

§2.1

No time-aliasing

Cepstral minimum-phase conversion uses ≥16× zero-padded FFTs so computation tails never wrap around.

§3.1

Zero pre-ringing

Minimum-phase impulses peak at sample [0] with mathematical zeros before it.

§3.2

Stopband ≥ −140 dB, no ripple

Kaiser, β = 14 from 1M taps up and fitted to the length at 5k. Measured: −185 to −221 dB across the matrix, −193 to −220 dB at 5k.

§4.1

f64 end to end

Any f32 truncation in the sample path is a blocker-class bug. GPU DS arithmetic exists to satisfy this law on f32 hardware.

§4.2

FFT convolution only

Overlap-add/save partitioned convolution, block sizes tuned to cache and VRAM.

07Verification & the audit trail

Per-file proof

Every output FLAC is re-decoded and compared against the internal f64 buffer (±2 LSB at 24-bit). The UI shows ✓ VERIFIED only when the file on disk provably contains the math the log describes.

Per-build proof

More than 900 unit tests pin the invariants: convolver latency is exactly 2 blocks (CPU) / 1 block (GPU), unity gain through the OLA engine, bit-exact polyphase reconstruction, stereo-linked switching, true-peak and dither behaviour — and the player’s streaming stages against the converter’s whole-file code. CI runs them on every push.

The console is the product

AuraEngine keeps a console window next to the UI on purpose — it starts minimized, out of the way, and the same log goes to %LOCALAPPDATA%\AuraEngine\logs. It prints the full DSP trace: which .npy was resolved, ADC-ringing analysis verdicts, hybrid-phase coverage statistics, true-peak decisions, verification results. If you audit audio software for a living, start there — then read the auditor's guide.

08One engine, two ways: convert or play

The question is not offline against real time. Every file in the list has ▶ beside C, its convert button, and both run one engine and one chain — the same filter blobs, phase engine, rack and output stages, from the same code — which does not quietly give up sound for speed. The converter has no deadline. The player prepares ahead what a track needs whole and switches each stage in as soon as it is ready; once every stage is in, what it sends to the device matches the converted file to −120 dB (measured on 30M taps, FS×8, TFS, XTC). When the machine cannot keep up, it moves to the graphics card, then to fewer taps, and says so. A live stream has no future to read ahead into: only Declip and the Adaptive Apodizer stand down there, and the interface shows it.

ConverterPlayer — a filePlayer — a stream
ChainThe whole rack, every stage at full lengthThe same rack; source stages prepared ahead over the whole trackThe same rack as the stream arrives; linear phase looks 50 ms ahead
Whole-track stagesAll, before anything is writtenAll — switched in as they get ready, or before the first soundDeclip and the Adaptive Apodizer stand down
Album LevelOne level per albumEach track at its own level—
HardwareAny machine — 30M at a high FS just takes longerReal time: CPU, or a discrete graphics card; fewer taps if neither keeps up, and it says soAs for a file
Output24-bit FLAC, checked against the f64 renderWASAPI exclusive, or BIT-PERFECTThe same
Best forMusic to keep and play anywhereHearing a setting first; listening at onceInternet radio

Why convert, when the player matches it? Any filter on any machine; one level for a whole album; a file that plays anywhere, with no PC; no CPU and nothing to keep up with while you listen; and a file checked against the render it came from. Why play? To hear a setting before an evening goes into converting; to listen at once, without disk space for hi-res copies; and radio, which cannot be converted.

The big player in listening mode with the scene Bars The same with the scene Speaker: one woofer seen close

What is in the sound, and when

Some stages need the whole track before they can decide. By default the sound starts at once: Declip, ISP, the subsonic filter and Adaptive Headroom run over the track as it is decoded, ahead of what you hear, and the level follows the loudest moments a few seconds ahead until the whole output is measured. The badges under the player light as stages come into the sound, and a thin bar fills until all of them are in.

With Instant start off, nothing plays until every stage is ready — on an i9-14900K, a four-minute CD track at FS×2 in about 1.7 s at 1M taps and 2.1 s at 30M on the graphics card.

Nothing cuts the sound

A seek keeps the old place playing until the new one is ready, then crossfades. The next track is prepared while this one plays and follows it without a gap; a change of rate family costs only the device reopening, about 0.6 s. A rack change is built while the old chain plays and takes over in place, 1.5 s after the last click.

The graphics card, when the CPU cannot keep up

The player measures, on your machine and for the chosen filter, whether the CPU convolves in real time with room to spare, and uses a discrete card only when it does not — 30M taps in about 0.6 GB of video memory on an RTX 4090, matching the CPU to −280 dBFS. If neither keeps up, the tap count steps down once for that track, and the taps chip says so.

BIT-PERFECT, and a guard on the output

BIT-PERFECT sends the decoded file to the device untouched — its own rate and depth, no rack, no volume, no dither — and switches while a track plays. Otherwise the device is opened in WASAPI exclusive mode at the output rate, and what reaches it is checked after the volume control: a block more than 12 dB louder than the volume allows becomes silence.

▶ C M ✕ on every rowConvert all · ×real timetrack memory (M)listening mode (L)Ctrl+Shift+A analyzerfirst-start tour

Every control, key and menu: doc 19.

09Internet radio through the rack

F / R at the head of the list switches it to the radio: featured stations, your favorites and recent ones, a search of the Radio Browser catalog — tens of thousands of stations by name, genre, country, language and format, sorted by votes, plays or quality and filtered down to lossless — or any stream's address. MP3, AAC, HE-AAC, FLAC, Vorbis and Opus over HTTP, .pls/.m3u playlists and HLS, all decoded inside the program, at 44.1 or 48 kHz and their multiples.

Radio Paradise's FLAC stream playing through the rack, above the featured stations The Search tab: jazz, lossless only
StageOn a fileOn a live stream
DC removalPer-channel mean, or the 2 Hz high-passThe 2 Hz high-pass
Declip · Adaptive ApodizerRunStand down — they need the whole track
ISP · Subsonic filter · XTCRunRun as the stream arrives
Adaptive HeadroomDecides on the whole trackDecides on the first 10 s
Linear phaseThe full filter (42.5 s ahead at 30M ×8)Its stream version, 50 ms ahead
TFS · Hybrid-Phase · Continuous AlphaRunRun from the first sound
Album LevelConversion only—

Long filters do not make a stream wait

A linear-phase filter looks ahead by half its length — 42 s at 30M taps and FS×8, more than any broadcast can give. All of that look-ahead is ringing at the wall, so a stream plays a version of the same filter with the same magnitude and stopband, linear phase up to 20 kHz and minimum phase only in the last kilohertz under the wall. It looks 50 ms ahead, differs from the full filter by less than −200 dBFS in the audio band on music, and is derived once per length, then kept.

A dropout without a seam

When the connection drops, the player reconnects, finds where the new connection repeats what it already has, and skips the repeat. The buffer starts at 8 s and grows when the network leaves it empty; the drift between the server's clock and the DAC's is held within a second by dropping or repeating a few milliseconds in a quiet place — nothing is resampled. BIT-PERFECT works on a stream too.

Upsampling cannot add what the codec removed: a 128 kbps MP3 stream stays a 128 kbps MP3, played through a better reconstruction filter. Stations that stream FLAC are where the rack has the most to work with.

10The analyzer

Ctrl+Shift+A opens a window on what you hear now; a row's right-click menu opens one on the file, or on its converted copy. It measures three signals side by side: S, the source; B, the source after the rack's repair stages, before the filter; O, the output of the whole chain, from a pass over the entire track.

S · B · O

The numbers

Integrated, short-term and momentary loudness, LRA, true peak (BS.1770 and the engine's own 4× reading), DR, RMS, clipping runs and overs, DC offset, energy above and below the audio band — over the whole track, a selected stretch, or live.

S | O · O − S · B − S

The spectrogram

A 4096-point FFT at 44.1/48 kHz, Kaiser window, 512 log bands from 20 Hz to Nyquist, three resolutions blended; Log, Lin, Mel and Bark. In O − S, blue is where the chain made it quieter and red where louder.

6 tabs

The views

Spectrum, loudness history, spectrogram, the waveform down to single samples, a histogram, and a stereo view with a vectorscope and correlation.

TXT · TSV · CSV · PNG

Export

The numbers as text in the formats forums use for DR and true-peak reports, or as TSV; the loudness history as CSV; any view as PNG.

The whole-track passes run only while the analyzer is open, so playback without it costs no extra CPU. On a radio stream it measures the stream and the output live — for the song playing and for the whole session — and shows how far the stream’s spectrum really reaches: an MP3 at 128 kbps ends near 16 kHz, a lossless stream reaches half its sample rate. Its views keep what has played — about three minutes of the spectrogram, 30 minutes of the loudness and of the waveform — and follow the live edge until you zoom in or look back.

11Scenes that draw the music

In listening mode the player can draw what it plays. A scene is a GLSL shader in an .aura-vis file; 19 come with the app, and a studio edits them, tunes their settings with sliders and takes Shadertoy's multipass shaders, keeping each one's author, link and licence. The player renders a few seconds ahead of what you hear, so a scene can see a kick coming and have it land as it sounds.

Lineup on a radio stream: each instrument of the song a light of its own, left to right as it sits in the mix Diamond Dust Stage on the same stream

Three scenes with instruments

Stage, Lineup and Diamond Dust Stage draw each instrument of the song on its own — the kit piece by piece, the bass, the voice, the guitars and the keys — with a 282 MB pack of models (HTDemucs, DrumSep, Basic Pitch, ONNX Runtime with DirectML), downloaded once and checked file by file. Each track is analysed once, in the background: 15–25 s for a four-minute track on an RTX 4090. On a radio stream they work too, on a graphics card: the instruments come a few seconds after a station starts, and every one but the drums has its notes about two seconds before it sounds.

Picture delay, and credits

Picture delay, per output device, holds the picture back by what a DAC's filter or active speakers add and Windows does not report. Eleven scenes are adaptations of Shadertoy work by Tonny Espeset, mrange, Virgill, espeon, ixmibrahim after Martijn Steinrucken, weyland, El_Sargo, Jan Mróz, patrickjaillet and Stephane Cuillerdier, each under its own licence (CC BY-NC-SA 3.0, CC BY 3.0 or CC0).

12Get it

Everything above needs filter coefficients, and those are the big part of the download — the full matrix is about 10 GB. So the app ships four ways, each with its filters already inside. Unzip, run aura-engine.exe, drop a track on the window: ▶ plays it, C converts it. The app opens on the filter that came with it: there is no setup step, and no way to land on a setting the package cannot serve.

~12 MB

Compact · 5k taps

Every multiplier, FS2 through FS16, its filters under a megabyte. The window is fitted to the length: the image rejection of the long filters, a gentler wall at the cutoff.

Download →
~140 MB

Starter · 1M taps

Every multiplier, FS2 through FS16. Small enough to try on a whim, and no position on either slider is a dead end.

Download →
~332 MB

Standard · 10M taps

FS8 — 352.8 kHz from 44.1 kHz sources, 384 kHz from 48 kHz ones. Start here.

Download →
~972 MB

Reference · 30M taps

FS8, the longest filter the engine designs: a 0.05 Hz transition band and a measured stopband of −216 to −221 dB.

Download →

Comparing them is the point

Unzip more than one bundle into the same folder. The filter files merge, one copy of the app serves all of them, and the tap slider then offers every size you have — so you can render the same track at 5k and at 30M and hear what a tap count actually buys, instead of taking a number on trust.

What is inside

Each bundle carries both phase types, because the Hybrid-Phase engine renders the minimum-phase branch in full and half a pair would fail. Each carries both rate families, so 44.1 and 48 kHz sources convert alike. Individual filter packs, and the app on its own, are on the releases page.