e↗entertrainment
← All project stories
Sound & electronics / PROJECT STORY

RemezEngine

Equiripple FIR filter design and the numerical machinery behind it. A companion foundation for the studio’s signal-processing applications.

Numerical DSP library13 MIN READDEVELOPMENT ARCHIVE · OCT 2026
PROJECT REVIEW · 18 October 2026

Inside the project

Prepared with the project’s development agent from source and retained evidence. This is a project-owner assessment, not an independent customer review. Dated checks were not rerun for this website.

RemezEngine is the filter-design layer behind the FIRFilters package: give it band edges, desired gains and weights, and it returns the linear-phase FIR filter that is provably the best of its length. It is most useful where a filter has to meet a stated specification rather than merely sound plausible — audio or RF work on Apple platforms, where you would rather be told a design failed than be handed one that quietly misses. The engine's defining habit is that it refuses to flatter itself: every result is checked against the Chebyshev alternation theorem, and it will not report convergence unless the taps it returns actually achieve the error level it claims. That habit exists for a reason. An earlier version did the opposite, reporting a near-perfect error figure for a filter that was 24 dB off target. As of 18 October 2026 this is a tested library rather than a product: no release, no published package, and nothing a visitor can click.

What stands out

  • Covers all four linear-phase types, including the half-integer bases that even tap counts require — a four-tap design returns exactly symmetric taps.
  • Optimality is checkable rather than asserted: the tests assert the Chebyshev alternation theorem directly (|E|/delta = 1.000000 at all 24 extremals, strictly alternating), which is a proof rather than a comparison against remembered output.
  • The reported error matches the measured error. converged is true only when the exchange met tolerance AND the returned taps demonstrably achieve the reported delta.
  • Failure is loud and specific: it throws rather than returning quietly, rejects specifications asking for gain where the chosen type forces a zero, and separates ordinary non-convergence from a ripple below double-precision resolution — cases whose remedies are opposite.
  • Independently cross-checked for the symmetric types against SemanticCompute v1.20.0's separately written RemezReference: on a 61-tap lowpass, delta 0.0015031 vs 0.0014995 and realised stopband -56.45 dB vs -56.44 dB.
  • The FFT block convolvers agree with direct convolution to 8.9e-16 across a tap-count x input-length x block-size matrix, and at 1024 taps run roughly 65x faster than brute force where a previous arrangement ran 6.9x slower.

Current limits

  • Double-precision wall: a specification whose implied ripple falls below roughly 1e-12 relative to its band levels cannot be resolved. For a Hilbert transformer over 0.05-0.45 the wall falls between 127 and 151 taps. The engine reports this rather than returning noise.
  • Types III and IV have no external oracle — SemanticCompute's family covers Types I and II only, so the antisymmetric work rests on the alternation tests alone.
  • evaluateWeightedErrorNorm reads only the second half of the taps, so it cannot see damage that breaks symmetry, while its documentation says 'any set of taps'. Independently reproduced; not yet fixed.
  • Nine confirmed review findings remain unapplied, mostly documentation inaccuracies in the recently carried-forward functions.
  • A completeness claim in the package's own Archive/README.md is still unproven: three archives were never opened. Unchanged as of 18 Oct.
  • Never run inside an application, never released. The SwiftUI visualiser has never been hosted.

Features & evidence

Equiripple design core (Types I-IV)

component-tested

Parks-McClellan exchange over a Q(w)P(w) factorisation; barycentric delta and interpolant with no matrix to condition; least-squares seed; single-exchange fallback whenever |delta| would decrease.

Recorded evidence

Full suite run 17 Oct 2026 at commit da7087a: 49 tests in 14 suites passing. Structure re-confirmed by static inspection 18 Oct 2026 (singleExchangeStep, leastSquaresSeed, barycentricWeights, padded-set tracking all present).

Honest convergence reporting

component-tested

User reads delta, alternationDeviation and converged to confirm a design is what it claims; design throws on failure unless requireConvergence: false.

Recorded evidence

Failure-reporting suite green 17 Oct 2026; post-condition compares reported delta against error measured from the returned taps.

Fixed-point export

component-tested

User exports designed taps as Q-format integers or a ready-to-compile C header for an embedded target (quantizeTaps, quantizeToQFormat, exportFixedPointCHeader, quantizationAwareDesign).

Recorded evidence

Carried forward and tested 17 Oct 2026, commit da7087a; 10 of the 49 test functions cover these.

Convolution helpers

component-tested

User applies taps to a signal via direct or FFT block convolution (firConvolveFull, firFilterSameLength, fftConvolve, overlapSaveConvolve, plus Float variants).

Recorded evidence

Agreement with direct convolution to 8.9e-16 over the full matrix, 17 Oct 2026; includes a regression test that fails if a block convolver becomes slower than brute force.

Multirate and filter banks

component-tested

User decimates, interpolates, resamples by a rational ratio, or splits into polyphase components and analysis/synthesis banks.

Recorded evidence

Timebase and channel-synchrony tests green 17 Oct 2026; rate changes verified on a tone across five L/M ratios.

Windowed-sinc and Kaiser design

component-tested

User generates a windowed-sinc filter, symmetric or antisymmetric, with Hann/Hamming/Blackman/Kaiser windows.

Recorded evidence

Kaiser reaches its textbook -90 dB sidelobe level at beta = 8, measured 17 Oct 2026.

RemezVisualizer (SwiftUI)

source

Plots impulse response, magnitude and phase, and convergence history; also exports taps, response and error as CSV/JSON.

Recorded evidence

Compiles as part of the package. Never instantiated outside a preview, never rendered, no capture exists. Confirmed 18 Oct 2026.

Distribution

planned

Installation as a versioned package dependency.

Recorded evidence

No remote, no tag, no release as of 18 Oct 2026. FIRFilters' manifest carries a commented placeholder URL, explicitly not a live endpoint.

iOS device/simulator runtime

unknown

Running the package on iOS.

Recorded evidence

A reviewer reported an xcodebuild simulator success on 17 Oct 2026, but its verification step did not complete. Treat as unverified. Sources build and the suite passes at a macOS 13 / iOS 16 floor.

Review updates & corrections
  • No functional change since the 17 October biography. Three source files were flagged as modified on disk; static inspection on 18 Oct confirms they are structurally unchanged, with every key element of the corrected core still present.
  • Test count re-confirmed statically on 18 Oct: 49 @Test functions across three files (RemezDesignTests 19, UtilityTests 20, CarriedForwardTests 10), consistent with the 17 Oct run of 49 tests in 14 suites.
  • Evidence class differs from yesterday: today's inspection is static only. No build, test run or benchmark was performed, per the brief. The most recent executed evidence remains the 17 Oct run at commit da7087a.
  • The unopened-archive caveat from the 17 Oct entry is NOT resolved and could not be addressed today: the artefacts are zip archives and this session has read-only text tools only. The completeness claim in Archive/README.md should still not be repeated on the site.
  • The nine confirmed review findings remain unapplied; the code is identical to commit da7087a.

Workflow walkthrough

A guide to the native workflow. These steps describe the app; only the recordings and images embedded on this page are available as website demonstrations.

  1. Declare the specification: two BandSpec entries, a passband 0 to 0.20 at gain 1 and a stopband 0.25 to 0.5 at gain 0, with equal weights.
  2. Call RemezEngine.designTaps(numTaps: 63, bands: bands, symmetry: .symmetric). It returns 63 linear-phase taps, or throws with an actionable message if the specification cannot be met.
  3. Read delta (about 1.49e-3, i.e. roughly -56.5 dB), alternationDeviation (near zero at the optimum) and converged off the RemezResult to confirm the filter is what it claims.
  4. Verify independently: evaluate |H| from the taps on a dense grid and confirm the measured stopband peak matches delta, about -56.5 dB.
  5. Either hand the taps to a real-time convolver in the FIRFilters package, or call exportFixedPointCHeader to emit a Q15 C header for an embedded target.
Planned captures · not yet available
  • Proposed: RemezVisualizer showing the magnitude response of a 63-tap lowpass with its equiripple stopband, alongside the convergence history.
  • Proposed: test-suite output showing the alternation assertion (|E|/delta = 1.000000 at all 24 extremals, strictly alternating).

Development biography · earlier archive

Development profile compiled from project source and retained records in October 2026. Dates and test results describe those records; they do not imply a new build, live acceptance test or public release.

Biography

Equiripple design is how you get the most stopband attenuation out of a fixed number of filter taps. The Parks–McClellan algorithm finds, for a given length and a given set of frequency bands, the filter whose worst-case weighted error is as small as it can possibly be — and the Chebyshev alternation theorem makes that optimum checkable, because the optimal filter is the unique one whose error touches ±δ with strictly alternating sign at r+1 frequencies.

That theorem is also what makes the algorithm treacherous to implement. The quantity being approximated is the zero-phase amplitude A(ω), which is real and signed. What a naive transform of the taps hands you instead is the real part of the response referred to sample 0, and for a linear-phase filter those differ by a factor of cos(ω(N−1)/2) — a carrier that changes sign roughly N/2 times across the band. An implementation that drives its extremal search with the wrong one will find the carrier's zeros instead of the error's peaks, and then report a beautiful δ for a filter that misses its specification badly. Nothing about the result looks wrong from the inside.

RemezEngine's history is the story of that bug being made and then found. The earliest code dates from 7 December 2025, written inside the NoiceColour application as two files — a struct Remez prototype and an "extended" helper file — which were later folded into a standalone package. Six successive snapshots survive from then through March 2026. All of them share the defect. Measured on the version as found: a 63-tap lowpass specified for roughly −58 dB reported δ = 9.4e-11 while its stopband actually peaked at −33.75 dB, about 24 dB short, and the reported δ and the reported stopband ripple inside the same result object contradicted each other by eight orders of magnitude. Even tap counts were worse than inaccurate: they were structurally impossible, because the tap-construction step mirrored coefficients about (N−1)/2 using integer division, which is not a symmetry axis for even N. A four-tap request returned [c1, c0, c1, 0] — not a linear-phase filter at all. The package had no tests, and its test target was commented out in the manifest.

The current engine replaces the design core outright. Each of the four linear-phase types is written as A(ω) = Q(ω)·P(ω), folding the type's fixed factor into the specification so that all four reduce to one problem: best Chebyshev approximation by a cosine polynomial. δ and the interpolant come from barycentric Lagrange form with no matrix at all — the obvious alternative, solving the (r+1)×(r+1) alternation system, was built first and measured returning garbage above about 250 taps while LAPACK reported success. The exchange starts from a least-squares seed, because a uniform start makes δ at iteration zero a high-order divided difference that underflows; coefficients are recovered by an in-band fit, because sampling the interpolant across the full range extrapolates beyond its node hull wherever a specification has a don't-care region, which imposed a ripple floor no tap count could beat; and whenever a multiple exchange would decrease |δ| the engine falls back to a single exchange, which is monotone by construction and cannot cycle, fixing a period-10 limit cycle on a 32-tap three-band bandpass.

The distinctive idea, though, is not any single numerical choice: it is that the library is built to be unable to flatter itself. Three mechanisms carry that. The tests assert the alternation theorem directly — 24 extremals, |E|/δ = 1.000000 at every one, strictly alternating — which is a proof of optimality rather than a comparison against remembered output, and every response is measured from the taps by an independent DFT so a design cannot pass by lying twice. converged means both that the exchange met its tolerance and that the returned taps demonstrably achieve the δ being reported. And failure is loud: design throws rather than returning quietly, rejects specifications that ask for gain where the chosen type forces a zero, and distinguishes ordinary non-convergence from a requested ripple below what double precision can resolve — cases whose remedies are opposite, since for the second the advice is to reduce the tap count.

The intended audience is a developer building audio or RF signal processing on Apple platforms who needs a filter that provably meets a stated specification, and who would rather be told that a design failed than handed one that quietly does not. A typical journey: call designTaps with band edges, desired gains and weights; read delta, alternationDeviation and converged off the result to confirm the design is what it claims; then either hand the taps to a real-time convolver in the FIRFilters package, or call exportFixedPointCHeader to emit a Q-format C header for an embedded target. Independent validation exists for the symmetric cases: against SemanticCompute v1.20.0's separately written RemezReference on a 61-tap lowpass, δ agrees to 0.0015031 against 0.0014995 and the realised stopband to −56.45 dB against −56.44 dB.

Modules and features

ModuleWhat it doesAuthored or dependency
RemezExchange.swiftThe design core: Q·P factorisation, barycentric δ and interpolant, least-squares seed, exchange with single-exchange fallbackAuthored
RemezEngine.design / designTapsPublic entry points; validation, grid refinement, honest diagnosticsAuthored
RemezResultTaps, δ, extremal frequencies, magnitude/phase, ripple, group delay, converged, iterations, alternationDeviation, linearPhaseTypeAuthored
Fixed-point exportquantizeTaps, quantizeToQFormat, exportFixedPointCHeader, quantizationAwareDesignAuthored
Windowed sincFIRWindowDesign.windowedSinc (Hann/Hamming/Blackman/Kaiser, symmetric and antisymmetric), besseli0Authored
ConvolutionfirConvolveFull, firFilterSameLength, fftConvolve, overlapSaveConvolve + Float variantsAuthored over Accelerate vDSP
Multiratedecimate, interpolate, resample, polyphaseDecompose, analysis/synthesis filter banksAuthored
RemezView.swiftRemezVisualizer, a SwiftUI view plotting impulse response, magnitude/phase and convergence history; plus CSV/JSON export of taps, response and errorAuthored

What a visitor can see, hear or try: nothing, currently. This is a headless library with no executable target, no sample app and no captured media. RemezVisualizer is the only visual surface and it must be hosted in a SwiftUI app to render; it has never been instantiated outside a preview. Any site presentation needs a host app built first.

Technical foundations

Swift 6.2, Foundation and Accelerate only — no third-party dependencies. vDSP for transforms and dot products; LAPACK dgels for the least-squares seed and the in-band coefficient fit (via the deprecated CLAPACK interface, which is the only build warning). Numerics: barycentric Lagrange interpolation with log-domain weight normalisation, Chebyshev recurrence evaluation, and a DCT-I-free in-band fit. Local packages: FIRFilters depends on RemezEngine by relative path and pins its own deployment floor to RemezEngine's; SemanticCompute is a test-time oracle only, deliberately not a dependency.

Maturity by feature, with evidence

FeatureStateEvidence and date
Design core, Types I–IVSource implemented, tests passed49 tests in 14 suites green, 17 Oct 2026, commit da7087a
Alternation-theorem optimalityVerifiedE/δ = 1.000000 at all 24 extremals, strictly alternating
Cross-check vs independent implementationVerified, Types I–II onlySemanticCompute v1.20.0 RemezReference, 61-tap lowpass, agreement to 0.4%
FFT block convolversSource implemented, tests passedAgree with firConvolveFull to 8.9e-16 across the tap-count × input-length × block-size matrix
Fixed-point export, carried-forward functionsSource implemented, tests passedCommit da7087a, 17 Oct 2026
BuildCleanswift build with no warnings beyond pre-existing CLAPACK deprecations
Lower platform floorMeasuredBuilds and all tests pass at macOS 13 / iOS 16
iOS simulator buildUnknownA reviewer reported xcodebuild success; its verifier did not complete. Treat as unverified.
Installed / runtime verifiedNoNever run inside an application; visualiser never hosted
Released / deployedNoNo remote, no tag, no release. Work sits on a local branch; the package is uncommitted in the studio’s main checkout

Limitations

  • Double-precision wall. A specification whose implied ripple falls below roughly 1e-12 relative to its band levels cannot be resolved; the engine reports this instead of returning noise. For a Hilbert transformer over 0.05–0.45 the wall falls between 127 and 151 taps.
  • Types III and IV have no external oracle. SemanticCompute's family covers Types I and II only, so the antisymmetric work rests on the alternation-theorem tests alone.
  • evaluateWeightedErrorNorm reads only the second half of the taps, so it cannot see damage that breaks symmetry, while its documentation says "any set of taps". Confirmed by independent reproduction; not yet fixed.
  • Nine confirmed review findings are unapplied, mostly documentation inaccuracies in the recently carried-forward functions.
  • A completeness claim in the package's own Archive/README.md is not yet proven. Three archive files were never opened. See the warning below.
  • Not published, not installed, not run in an application.

Roadmap (aspirations, kept separate from the above)

Apply the nine confirmed findings; finish the archive survey; fold the branch into the studio’s main checkout; optionally restore the macOS 13 / iOS 16 floor; build a host app so the visualiser can be captured; consider a public release with a versioned git dependency.

FAQs

Why not just use a windowed sinc? For the same length and transition width, equiripple does considerably better. Measured independently at 61 taps: −56.4 dB equiripple against −36.7 dB for a Kaiser windowed sinc.

How do I know a filter is actually optimal and not just plausible? The alternation theorem makes it checkable. Read alternationDeviation (zero at the optimum) and converged, which is true only when the returned taps demonstrably achieve the reported δ.

Does it handle even tap counts? Yes — Types II and IV, with the half-integer bases they require. A four-tap design now comes back exactly symmetric.

Why does design throw instead of returning a filter? Because a filter that silently misses its specification is worse than no filter. Pass requireConvergence: false to receive it anyway, flagged.

Can I use it today? As a local Swift package, yes. There is no published release.

Explore the connections.

Browse all project stories ↗