Skip to content

fix: streaming contract, flush lifecycle, and polyphase interpolation defects - #56

Merged
tphakala merged 26 commits into
masterfrom
fix/issue-51-streaming-contract
Jul 17, 2026
Merged

fix: streaming contract, flush lifecycle, and polyphase interpolation defects#56
tphakala merged 26 commits into
masterfrom
fix/issue-51-streaming-contract

Conversation

@tphakala

Copy link
Copy Markdown
Owner

Closes #51.

This branch fixes the streaming contract issues behind the clicks reported in #51 and several defects found while investigating, with the streaming path pinned bit-exact before any engine change (chunked Process plus one final Flush now provably equals one-shot processing, enforced by a new equivalence test).

Engine fixes: the polyphase phase-boundary coefficient interpolation used a wrapped neighbor, degrading THD+N massively at ratios with active sub-phase interpolation (measured 86.26 dB improvement at 44100 to 64000; exact-rational ratios like 44100 to 48000 are bit-identical, which is why the existing suite never saw it); severe non-integer downsampling (beyond roughly 1:16) corrupted output and grew internal history without bound; Flush over-padded each FIR stage by one zero, emitting the phantom samples observed in #51 (Process+Flush now totals within [floor(nratio), ceil(nratio)+1]); Flush is now terminal (second Flush returns empty, Process after Flush starts a fresh stream); Process returns caller-owned memory at every ratio including unity and empty results; the cubic (QualityQuick) stage no longer computes its first output segments from a fictional zero history and now drains its true tail on flush; NaN sample rates are rejected by all constructors; half-band stage construction errors propagate instead of silently substituting a nearest-neighbor stub.

API and docs: new Latency() on SimpleResampler and SimpleResamplerFloat32 returns the startup deficit in output samples for priming real-time FIFOs; QualityQuick through NewEngine and NewEngineFloat32 now uses cubic interpolation, matching New() and the documented contract; GetLatency and GetMemoryUsage account for all stages and coefficient banks; the streaming contract (variable per-call output length, Flush end-of-stream only, one instance per channel) is documented on the API, in doc.go, and in the README, with a runnable real-time FIFO example at examples/streaming that uses deficit-driven input sizing (static floor sizing underruns on long runs, static ceil grows the FIFO without bound).

Tests: new pins for chunked-streaming equivalence, flush length and lifecycle, severe-ratio integrity, latency accuracy (float64 and float32), zero-allocation ProcessInto for QualityQuick, and stage-adapter accounting; several previously vacuous tests (latency, buffer integrity, multi-reset, flush-multi) now assert real behavior.

Behavior changes for existing users are listed in the CHANGELOG: flush output length shrinks by the phantom padding, a second Flush returns empty, QualityQuick via NewEngine has near-zero latency, and non-exact-rational ratio output changes bit-for-bit (it is now correct). Follow-ups tracked in #52, #53, #54, #55.

tphakala added 24 commits July 17, 2026 11:27
At severe non-integer downsampling ratios the fixed-point accumulator
overshoots the per-call limit by up to one step, making consumed exceed
the history length. The old guard then skipped the trim while still
rebasing the accumulator: unbounded history growth and re-read of stale
samples (stuttering, over-emission).
#51)

Each FIR stage's Process retains exactly N-1 history samples (the filter
delay line), so N-1 padding zeros drain it. The stages padded N zeros,
pushing one extra all-zero reference position through the filter and
emitting a phantom output sample: Process(470)+Flush at 44100 to 48000
QualityHigh returned 514 where ceil(470*ratio)+1 = 513 is the maximum.

Padding combination: N-1 in all three stages (no contingency needed).
- DFTStage.Flush: tapsPerPhase -> tapsPerPhase-1
- DFTDecimationStage.Flush: numTaps -> numTaps-1
- PolyphaseStage.Flush: tapsPerPhase -> tapsPerPhase-1

The polyphase lower-bound contingency did not trigger; tapsPerPhase-1
satisfies both bounds of TestFlushLength_Canonical across all 6 rate
pairs x 2 presets. TestStreamingEquivalence stays bit-exact because
Flush is altered identically on the one-shot and chunked sides.
…llback

newHalfBandStage silently swallowed newPolyphaseStage's error and
substituted stubStage (nearest-neighbor resampling), so a caller of the
public New() constructor could get a "working" resampler with drastically
degraded audio and no signal that anything failed.

newHalfBandStage now returns (pipeline.Stage, error) and createStage
propagates the tuple directly, matching the StagePolyphase and StageFFT
branches. The rest of the chain (createStage -> newConstantRateResampler ->
New()) already propagated errors correctly.

Inspection shows this failure is currently unreachable through any public
New() input: BuildPipeline only ever emits StageHalfBand specs with Ratio
pinned to 0.5 or 2.0, both of which route through deterministic,
bounds-clamped filter design math in engine.NewResampler that cannot fail.
The signature change makes that guarantee explicit instead of relying on
silent substitution.
…on flush

CubicStage.Process emitted immediately from a fictional zero-filled
history instead of withholding output like the FIR stages do, so the
final cubicLatencySamples of real input were silently dropped while
the first samples were computed from implicit pre-silence. Process now
withholds emission until cubicLatencySamples real samples have primed
the history window, and Flush pads that many zeros through the same
path to drain the true tail, then resets to fresh state.

Also removes the dead histPos field and fixes the stale "doesn't
buffer" doc comment.
Adds two cubic-stage flush tests requested by second review:

TestCubicStage_FlushTailTracksRamp checks the flushed tail's actual
values, not just its length: the first sample must still track the
ramp's real final value, with later samples decaying toward the zero
padding. Uses a bounded 0..1 ramp rather than
TestCubicStage_FlushEmitsTail's unbounded one, since that ramp's huge
final-value-to-zero discontinuity makes cubic interpolation overshoot
substantially in the interior of the affected segment (a known, minor
characteristic already documented on CubicStage.Flush, not a defect),
which would make a tight tolerance fail on the correct implementation.

TestCubicStage_FlushAfterPartialPriming covers Process being fed fewer
samples than cubicLatencySamples before Flush: confirmed no panic, the
total count still lands within the established tolerance, and the
terminal-flush lifecycle holds.
NewPolyphaseStage builds the cubic sub-phase interpolation banks (B/C/D)
by sampling the prototype at the current phase and its neighbours. The old
getCoeff wrapped the neighbour phase within the same tap (phase % numPhases),
which at each phase boundary picked a prototype sample numPhases-1 positions
away instead of the adjacent one. filterBank.coeffs is the flat prototype, so
the adjacent sample to (tap t, phase L-1) is coeffs[t*L + L] (phase 0 of tap
t+1) and to (tap t, phase 0) is coeffs[t*L - 1] (phase L-1 of tap t-1);
out-of-range positions clamp to 0.0.

The wrap injected a large coefficient discontinuity at each boundary. It was
invisible for exact-rational ratios (44100<->48000 == 80/147, where the
fixed-point sub-phase x is identically 0 so the banks are never consulted),
which is why the whole existing regression/soxr suite (all exact-rational or
integer/DFT ratios) never caught it. For ratios with active sub-phase
interpolation it collapsed THD+N by 77 to 111 dB: measured -54 dB wrapped
versus -141 dB flat at 44100->64000 QualityHigh, and similar at 32000->44100,
8000->44100, 16000->44100, 11025->48000, 44100->192000. Flat matches soxr's
HQ-class THD+N.

phase_wrap_measure_test.go records the measurement (a degenerate exact-ratio
probe documenting the x==0 masking, plus an active-ratio probe that reaches
the code) and guards the fix: it asserts the production banks match the flat
reconstruction and that the active-ratio flat path stays >= 40 dB better than
the old wrap. No existing golden moved; full suite green.
…ProcessInto

Two contract gaps in the cubic (QualityQuick) streaming path, both surfaced by
this branch (cubic Flush now emits a real tail, and QualityQuick now maps to
the cubic stage):

1. Resampler.Flush early-returned the cubic tail and bypassed the samplesOut
   accounting done on the FIR path, so GetStatistics undercounted samplesOut
   by the flush-tail length. The cubic branch now adds its emitted tail to
   samplesOut before returning.

2. CubicStage.Process allocated a fresh output slice every call, violating the
   documented zero-allocation ProcessInto contract for QualityQuick. The stage
   now reuses a persistent output buffer via growStableLen, mirroring the FIR
   stages: processZeroCopy fills the reused buffer with append (so an
   off-by-one in the size bound can never write out of range) and returns an
   alias, while the owning Process wrapper returns a copy. The engine's
   ProcessZeroCopy path calls processZeroCopy; Process keeps the owning wrapper.

Tests: TestCubicStage_FlushUpdatesSamplesOut pins the samplesOut invariant and
TestProcessInto_ZeroAllocs_QualityQuick pins zero allocations on the warm
ProcessInto loop. Chunked-equivalence and cubic content tests stay bit-exact.
The type-level streaming example discarded the Process and Flush errors with
`_`, unlike every other example on this branch. Handle them with log.Fatal to
match doc.go's float32 example style.
…nfig.Validate

CubicStage.Process forwarded processZeroCopy's empty result during the
priming window, but that slice has cap > 0 and aliases the internal output
buffer; a caller appending to it was corrupted by the next Process call.
Return a fresh []F{} literal on the empty path, matching the sibling stages.

Config.Validate used NaN-blind comparisons (c.InputRate <= 0, ratio < min),
so New/NewMultiChannel/NewStereo/NewSimple and the preset constructors
accepted NaN rates and built a passthrough pipeline that emitted garbage.
Reject NaN with the positive-comparison idiom already used in
internal/engine/resampler.go.
DFTStage.Process and DFTDecimationStage.Process had duplicate copy branches;
the decimation factor==1 path returned an aliased input slice. Collapse both
into a single unconditional owned copy after the empty guard so Process always
returns caller-owned memory regardless of factor.

StageAdapter.GetMemoryUsage summed only the base polyphase coefficient bank,
undercounting the four cubic-interpolation banks (a, b, c, d) by 4x; sum all
four and add the missing cubic-stage branch for symmetry with GetLatency.

Also: soften the cubic priming comment to describe the bounded startup
neighbor accurately, add the latency invariant note, reconcile the getCoeff
THD figure to the committed 86.26 dB measurement, cross-reference
Resampler.Latency() from StageAdapter.GetLatency, apply the buffer
growth-slack policy to DFT phaseBufs, use the min() builtin for the polyphase
consumed cap, hoist deficitIn below the cubic early return, and drop the
issue #51 parentheticals from production comments.
Reconcile the polyphase THD figure to the committed 86.26 dB measurement in
the CHANGELOG (previously an uncommitted 77-111 dB range). Tag every
Unreleased Fixed/Changed bullet with (#51) for issue-ref consistency and state
the NaN-rejection coverage confidently now that Config.Validate rejects NaN.

README/doc.go Latency section: add a callout that Latency() exists on the
NewEngine (SimpleResampler/SimpleResamplerFloat32) path only, while New(config)
users get the input-domain GetLatency()/GetInfo() figure that is not for FIFO
priming; soften the withhold/already-fed wording to the measured +-2 sample
tolerance; align the Real-Time heading casing across both surfaces.

doc.go: point multi-channel streaming readers to ProcessMulti/FlushMulti
instead of the Stereo Processing section, which never mentions ProcessMulti.

stages.go: reword the stubStage doc; it is test-only now, not an unimplemented
stub. examples/streaming: document the first-callback precondition and the
per-callback allocation and FIFO reslice tradeoffs.
Add a float32 latency case mirroring one row of TestLatency_MatchesMeasuredDeficit,
a float32 QualityQuick ProcessInto zero-alloc test, and stage-adapter unit
tests covering GetLatency's cubic branch and GetMemoryUsage's decimation,
cubic, and four-bank polyphase paths (constructed directly since the public
pipeline cannot reach the cubic branch). Broaden the float32 streaming
equivalence test with a downsample ratio, a second quality, and a second chunk
size. Name the severe-ratio magic numbers as derived consts and modernize the
callback loop to range-over-int.
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 59 minutes

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 966ce94b-7bae-4dfe-aef1-4c50fd309b70

📥 Commits

Reviewing files that changed from the base of the PR and between c1f9143 and 84e4371.

📒 Files selected for processing (31)
  • CHANGELOG.md
  • README.md
  • aliasing_test.go
  • convenience.go
  • convenience_float32_test.go
  • doc.go
  • examples/streaming/main.go
  • flush_length_test.go
  • flush_lifecycle_test.go
  • flush_multi_test.go
  • internal/engine/buffer_integrity_test.go
  • internal/engine/cubic.go
  • internal/engine/cubic_flush_test.go
  • internal/engine/debug_latency_test.go
  • internal/engine/dft_stage.go
  • internal/engine/phase_wrap_measure_test.go
  • internal/engine/polyphase_stage.go
  • internal/engine/resampler.go
  • internal/engine/reset_state_test.go
  • internal/engine/severe_ratio_test.go
  • internal/engine/stage_adapter.go
  • internal/engine/stage_adapter_test.go
  • latency_test.go
  • nan_validation_test.go
  • pipeline_builder.go
  • processinto_test.go
  • quality_quick_test.go
  • resample.go
  • stages.go
  • stages_test.go
  • streaming_equivalence_test.go
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-51-streaming-contract

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.29787% with 11 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/engine/resampler.go 73.91% 3 Missing and 3 partials ⚠️
internal/engine/cubic.go 91.66% 1 Missing and 1 partial ⚠️
internal/engine/stage_adapter.go 88.23% 1 Missing and 1 partial ⚠️
stages.go 66.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@tphakala
tphakala requested a review from Copilot July 17, 2026 13:04

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces comprehensive improvements and bug fixes for streaming, latency, and quality in the audio resampler. Key changes include fixing a polyphase phase-boundary coefficient interpolation bug (which significantly improves THD+N for active sub-phase interpolation ratios), resolving severe downsampling corruption, ensuring Flush is terminal and does not over-pad, and returning owned buffers from Process to prevent aliasing. Additionally, it introduces a Latency() method to report startup deficits for real-time FIFO priming, adds robust NaN rate validation, ensures proper error propagation for half-band stages, and optimizes the QualityQuick cubic interpolation path to be zero-allocation once warm. New tests and documentation examples have been added to validate these streaming contracts and behaviors. I have no feedback to provide as no review comments were submitted.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown

Benchmark Comparison

goos: linux
goarch: amd64
pkg: github.com/tphakala/go-audio-resampler
cpu: AMD EPYC 7763 64-Core Processor                
                                    │ /home/runner/work/_temp/base.bench │  /home/runner/work/_temp/pr.bench  │
                                    │               sec/op               │    sec/op     vs base              │
ProcessMultiSequential-4                                    12.11m ±  2%   11.97m ±  2%       ~ (p=0.132 n=6)
ProcessMultiParallel-4                                      7.670m ± 16%   7.330m ±  8%       ~ (p=0.937 n=6)
ProcessMultiChannels/Mono-4                                 6.089m ±  2%   6.079m ±  2%       ~ (p=0.699 n=6)
ProcessMultiChannels/Stereo-4                               7.307m ± 17%   7.246m ± 15%       ~ (p=0.937 n=6)
ProcessMultiChannels/Quad-4                                 11.68m ±  3%   13.23m ± 20%       ~ (p=0.485 n=6)
ProcessMultiChannels/5.1-4                                  19.24m ±  7%   18.41m ±  4%       ~ (p=0.180 n=6)
ProcessMultiChannels/7.1-4                                  22.91m ±  2%   22.87m ±  3%       ~ (p=1.000 n=6)
Process_Allocations-4                                       4.948m ±  2%   4.899m ±  1%       ~ (p=0.310 n=6)
ProcessInto_Allocations-4                                   4.749m ±  1%   4.746m ±  3%       ~ (p=0.937 n=6)
Process_Allocations_48to32-4                                9.235m ±  2%   9.063m ±  4%       ~ (p=0.132 n=6)
ProcessInto_Allocations_48to32-4                            8.682m ±  0%   8.667m ±  1%       ~ (p=0.394 n=6)
Process_Allocations_48to32_5s-4                             15.33m ±  2%   15.52m ±  2%       ~ (p=0.180 n=6)
ProcessInto_Allocations_48to32_5s-4                         14.55m ±  5%   14.57m ±  3%       ~ (p=0.937 n=6)
ProcessInto_Warm_48to32-4                                   14.44m ±  1%   14.49m ±  2%  +0.35% (p=0.015 n=6)
ProcessIntoFloat32_Warm_48to32-4                            12.08m ±  0%   12.04m ±  0%  -0.35% (p=0.009 n=6)
ProcessFloat32Into_Warm_48to32-4                            16.17m ±  1%   16.32m ±  4%  +0.92% (p=0.015 n=6)
geomean                                                     10.59m         10.60m        +0.02%

                                    │ /home/runner/work/_temp/base.bench │   /home/runner/work/_temp/pr.bench    │
                                    │                B/op                │     B/op       vs base                │
ProcessMultiSequential-4                                    4.193Mi ± 0%   4.193Mi ±  0%       ~ (p=0.149 n=6)
ProcessMultiParallel-4                                      3.918Mi ± 2%   3.916Mi ±  1%       ~ (p=0.394 n=6)
ProcessMultiChannels/Mono-4                                 1.919Mi ± 0%   1.914Mi ±  0%       ~ (p=1.000 n=6)
ProcessMultiChannels/Stereo-4                               3.916Mi ± 2%   3.903Mi ±  2%       ~ (p=0.667 n=6)
ProcessMultiChannels/Quad-4                                 8.386Mi ± 0%   8.584Mi ±  2%       ~ (p=0.329 n=6)
ProcessMultiChannels/5.1-4                                  13.62Mi ± 5%   13.62Mi ±  0%       ~ (p=0.623 n=6)
ProcessMultiChannels/7.1-4                                  18.99Mi ± 0%   18.99Mi ±  0%       ~ (p=1.000 n=6)
Process_Allocations-4                                       441.5Ki ± 2%   441.5Ki ±  1%       ~ (p=0.905 n=6)
ProcessInto_Allocations-4                                   68.36Ki ± 8%   62.67Ki ±  9%       ~ (p=0.197 n=6)
Process_Allocations_48to32-4                                3.455Mi ± 0%   3.435Mi ±  1%       ~ (p=0.182 n=6)
ProcessInto_Allocations_48to32-4                            538.0Ki ± 8%   538.0Ki ±  8%       ~ (p=1.000 n=6)
Process_Allocations_48to32_5s-4                             6.384Mi ± 0%   6.384Mi ±  0%       ~ (p=1.000 n=6) ¹
ProcessInto_Allocations_48to32_5s-4                         1.493Mi ± 0%   1.493Mi ±  0%       ~ (p=1.000 n=6) ¹
ProcessInto_Warm_48to32-4                                   1.484Mi ± 0%   1.484Mi ±  0%       ~ (p=1.000 n=6) ¹
ProcessIntoFloat32_Warm_48to32-4                            592.9Ki ± 0%   592.9Ki ±  0%       ~ (p=1.000 n=6) ¹
ProcessFloat32Into_Warm_48to32-4                            1.484Mi ± 0%   1.484Mi ± 17%       ~ (p=1.000 n=6)
geomean                                                     2.123Mi        2.113Mi        -0.47%
¹ all samples are equal

                                    │ /home/runner/work/_temp/base.bench │  /home/runner/work/_temp/pr.bench  │
                                    │             allocs/op              │ allocs/op   vs base                │
ProcessMultiSequential-4                                   10.00 ± 10%     11.00 ± 9%       ~ (p=0.242 n=6)
ProcessMultiParallel-4                                     17.00 ±  6%     17.00 ± 0%       ~ (p=1.000 n=6)
ProcessMultiChannels/Mono-4                                5.000 ±  0%     5.000 ± 0%       ~ (p=1.000 n=6) ¹
ProcessMultiChannels/Stereo-4                              17.00 ±  0%     17.00 ± 0%       ~ (p=1.000 n=6) ¹
ProcessMultiChannels/Quad-4                                32.00 ±  3%     32.50 ± 5%       ~ (p=0.494 n=6)
ProcessMultiChannels/5.1-4                                 49.00 ±  4%     49.00 ± 2%       ~ (p=1.000 n=6)
ProcessMultiChannels/7.1-4                                 65.00 ±  2%     65.50 ± 4%       ~ (p=1.000 n=6)
Process_Allocations-4                                      1.000 ±  0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
ProcessInto_Allocations-4                                  0.000 ±  0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Process_Allocations_48to32-4                               2.000 ±  0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
ProcessInto_Allocations_48to32-4                           0.000 ±  0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Process_Allocations_48to32_5s-4                            2.000 ±  0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
ProcessInto_Allocations_48to32_5s-4                        0.000 ±  0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
ProcessInto_Warm_48to32-4                                  0.000 ±  0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
ProcessIntoFloat32_Warm_48to32-4                           0.000 ±  0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
ProcessFloat32Into_Warm_48to32-4                           0.000 ±  0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                                ²               +0.74%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/tphakala/go-audio-resampler/internal/engine
                                            │ /home/runner/work/_temp/base.bench │  /home/runner/work/_temp/pr.bench  │
                                            │               sec/op               │    sec/op     vs base              │
Resampler_CD2DAT-4                                                  4.294m ±  1%   4.294m ±  4%       ~ (p=0.818 n=6)
Resampler_Process-4                                                 99.49µ ± 14%   97.98µ ±  1%  -1.52% (p=0.004 n=6)
Crossover_16_SIMD-4                                                 6.251n ±  1%   6.297n ±  3%       ~ (p=0.366 n=6)
Crossover_16_Unrolled-4                                             12.78n ±  0%   12.80n ±  6%       ~ (p=0.290 n=6)
Crossover_20_SIMD-4                                                 6.550n ±  0%   6.582n ±  0%  +0.50% (p=0.002 n=6)
Crossover_20_Unrolled-4                                             15.95n ±  1%   15.96n ±  0%       ~ (p=0.859 n=6)
Crossover_32_SIMD-4                                                 7.197n ±  1%   7.183n ±  0%  -0.19% (p=0.017 n=6)
Crossover_32_Unrolled-4                                             25.79n ±  0%   25.86n ±  5%       ~ (p=0.242 n=6)
Crossover_48_SIMD-4                                                 8.121n ±  0%   8.145n ±  2%  +0.28% (p=0.002 n=6)
Crossover_48_Unrolled-4                                             38.83n ±  0%   38.82n ±  1%       ~ (p=0.452 n=6)
Crossover_64_SIMD-4                                                 9.276n ±  8%   9.184n ±  1%  -0.99% (p=0.041 n=6)
Crossover_64_Unrolled-4                                             52.71n ±  0%   52.72n ±  0%       ~ (p=0.390 n=6)
Crossover_128_SIMD-4                                                15.49n ±  1%   15.53n ±  0%       ~ (p=0.290 n=6)
Crossover_128_Unrolled-4                                            112.3n ±  0%   112.3n ±  1%       ~ (p=0.874 n=6)
Dot20_SIMD-4                                                        6.559n ±  0%   6.560n ± 11%       ~ (p=0.195 n=6)
Dot20_GoUnrolled4-4                                                 13.64n ±  0%   13.98n ±  2%  +2.49% (p=0.039 n=6)
Dot20_GoSimple-4                                                    31.19n ±  0%   31.27n ±  0%  +0.26% (p=0.017 n=6)
PolyphaseIteration_SIMD-4                                           11.52n ±  1%   11.54n ±  3%       ~ (p=0.416 n=6)
PolyphaseIteration_GoUnrolled-4                                     26.45n ±  0%   26.44n ±  1%       ~ (p=0.848 n=6)
Func_ConvolveValid_Taps32-4                                         32.98µ ±  1%   32.65µ ±  2%       ~ (p=0.065 n=6)
Func_ConvolveValid_Taps64-4                                         44.82µ ±  2%   44.56µ ±  1%       ~ (p=0.485 n=6)
Func_ConvolveValid_Taps128-4                                        75.20µ ±  1%   74.47µ ±  3%       ~ (p=0.310 n=6)
Func_ConvolveValid_Taps256-4                                        125.4µ ±  1%   124.6µ ±  1%  -0.62% (p=0.009 n=6)
Func_ConvolveValidMulti_2Phase_Taps64-4                             89.39µ ±  0%   89.46µ ±  1%       ~ (p=0.937 n=6)
Func_ConvolveValidMulti_4Phase_Taps64-4                             179.7µ ±  1%   177.5µ ±  9%       ~ (p=0.065 n=6)
Func_DotProduct_20-4                                                7.825n ±  2%   7.810n ±  1%       ~ (p=0.623 n=6)
Func_DotProduct_32-4                                                8.487n ±  1%   8.429n ±  0%  -0.68% (p=0.006 n=6)
Func_DotProduct_64-4                                                10.35n ±  0%   10.33n ±  1%       ~ (p=0.374 n=6)
Func_DotProduct_128-4                                               16.42n ±  0%   16.32n ±  3%       ~ (p=0.054 n=6)
Func_Interleave2_4096-4                                             1.307µ ±  1%   1.307µ ±  2%       ~ (p=0.526 n=6)
Func_Interleave2_8192-4                                             2.631µ ±  3%   2.620µ ±  1%       ~ (p=0.126 n=6)
Func_DFTStage_Quick-4                                               1.306m ±  1%   1.298m ±  0%       ~ (p=0.180 n=6)
Func_DFTStage_Medium-4                                              2.049m ±  1%   2.053m ±  1%       ~ (p=0.818 n=6)
Func_DFTStage_VeryHigh-4                                            3.178m ±  1%   3.193m ±  1%       ~ (p=0.065 n=6)
Func_PolyphaseStage_Quick_Down-4                                    2.218m ±  1%   2.210m ±  1%       ~ (p=1.000 n=6)
Func_PolyphaseStage_Medium_Down-4                                   2.213m ±  1%   2.216m ±  1%       ~ (p=0.699 n=6)
Func_PolyphaseStage_VeryHigh_Down-4                                 3.487m ±  0%   3.502m ±  4%       ~ (p=0.093 n=6)
Func_PolyphaseStage_VeryHigh_Up-4                                   1.679m ±  3%   1.648m ±  3%       ~ (p=0.310 n=6)
Func_CubicInterpolation_20Taps-4                                    73.54n ±  1%   73.58n ±  0%       ~ (p=0.675 n=6)
Func_CubicInterpolation_32Taps-4                                    120.8n ±  0%   121.0n ±  7%  +0.12% (p=0.035 n=6)
Func_CubicInterpolation_64Taps-4                                    247.2n ±  2%   247.4n ±  3%       ~ (p=0.610 n=6)
Func_CubicInterpolation_100Taps-4                                   388.8n ±  0%   389.2n ±  0%       ~ (p=0.061 n=6)
Func_CubicInterpolation_64Taps_Unrolled4-4                          242.6n ±  0%   242.5n ±  0%       ~ (p=0.491 n=6)
Func_FullPipeline_48kTo32k_Quick-4                                  341.7µ ±  1%   344.4µ ±  1%  +0.78% (p=0.015 n=6)
Func_FullPipeline_48kTo32k_Medium-4                                 3.048m ±  5%   3.049m ±  1%       ~ (p=1.000 n=6)
Func_FullPipeline_48kTo32k_VeryHigh-4                               4.650m ±  1%   4.687m ±  1%       ~ (p=0.240 n=6)
Func_FullPipeline_44kTo48k_VeryHigh-4                               5.941m ±  2%   6.053m ±  1%  +1.89% (p=0.004 n=6)
Float64Resampler-4                                                  4.330m ±  1%   4.393m ±  2%  +1.46% (p=0.015 n=6)
Float32Resampler-4                                                  3.546m ±  1%   3.586m ±  0%  +1.14% (p=0.009 n=6)
Float64DFTStage-4                                                   2.522m ±  2%   2.511m ±  2%       ~ (p=0.937 n=6)
Float32DFTStage-4                                                   1.913m ±  1%   1.937m ±  0%  +1.27% (p=0.002 n=6)
Float64Polyphase-4                                                  2.009m ±  1%   2.005m ±  1%       ~ (p=0.240 n=6)
Float32Polyphase-4                                                  1.577m ±  1%   1.608m ±  0%  +1.93% (p=0.002 n=6)
Precision_Float64_44kTo48k_VeryHigh-4                               5.996m ±  1%   6.010m ±  2%       ~ (p=1.000 n=6)
Precision_Float32_44kTo48k_VeryHigh-4                               4.188m ±  2%   4.201m ± 10%       ~ (p=0.589 n=6)
Precision_Float64_48kTo32k_VeryHigh-4                               4.687m ±  1%   4.671m ±  1%       ~ (p=0.589 n=6)
Precision_Float32_48kTo32k_VeryHigh-4                               3.561m ±  5%   3.594m ±  1%       ~ (p=0.065 n=6)
Precision_Float64_48kTo44k_VeryHigh-4                               5.140m ±  2%   5.177m ±  2%       ~ (p=0.310 n=6)
Precision_Float32_48kTo44k_VeryHigh-4                               3.977m ±  1%   4.005m ±  1%       ~ (p=0.065 n=6)
Precision_Float64_44kTo48k_Quick-4                                  418.0µ ±  0%   456.2µ ±  1%  +9.14% (p=0.002 n=6)
Precision_Float32_44kTo48k_Quick-4                                  402.7µ ±  1%   418.8µ ±  1%  +4.00% (p=0.002 n=6)
Precision_Float64_48kTo32k_Quick-4                                  346.7µ ±  1%   355.3µ ±  4%  +2.46% (p=0.002 n=6)
Precision_Float32_48kTo32k_Quick-4                                  336.4µ ±  1%   322.9µ ±  1%  -4.03% (p=0.002 n=6)
Precision_PolyphaseStage_Float64_VeryHigh-4                         3.524m ±  1%   3.623m ±  1%  +2.81% (p=0.002 n=6)
Precision_PolyphaseStage_Float32_VeryHigh-4                         3.065m ±  0%   3.088m ±  0%  +0.78% (p=0.004 n=6)
Precision_DFTStage_Float64_VeryHigh-4                               3.221m ±  1%   3.336m ±  1%  +3.57% (p=0.002 n=6)
Precision_DFTStage_Float32_VeryHigh-4                               2.153m ±  1%   2.191m ±  1%  +1.75% (p=0.004 n=6)
DFTStage_Only-4                                                     2.500m ±  3%   2.554m ±  1%  +2.16% (p=0.002 n=6)
PolyphaseStage_Only-4                                               2.014m ±  2%   2.010m ± 10%       ~ (p=0.937 n=6)
Resampler_QualityLow-4                                              3.421m ±  2%   3.493m ±  2%  +2.11% (p=0.041 n=6)
Resampler_QualityMedium-4                                           3.399m ±  2%   3.457m ±  1%  +1.71% (p=0.015 n=6)
ConvolveValid_Separate-4                                            2.675m ±  0%   2.663m ±  0%  -0.43% (p=0.002 n=6)
ConvolveValidMulti-4                                                2.675m ±  0%   2.668m ±  0%       ~ (p=0.132 n=6)
Interleave_Scalar-4                                                 65.44µ ±  0%   65.36µ ±  0%       ~ (p=0.589 n=6)
Interleave2-4                                                       18.22µ ±  3%   19.91µ ±  1%  +9.24% (p=0.002 n=6)
DotProduct_20-4                                                     6.569n ±  0%   6.567n ±  0%       ~ (p=0.937 n=6)
DotProduct_20_Manual-4                                              43.57n ±  0%   43.63n ±  0%       ~ (p=0.082 n=6)
DotProduct_20_Unrolled4-4                                           13.61n ±  0%   13.61n ±  0%       ~ (p=0.461 n=6)
Throughput_Quick_48kTo32k-4                                         345.2µ ±  1%   355.0µ ±  1%  +2.85% (p=0.002 n=6)
Throughput_Low_48kTo32k-4                                           3.170m ±  2%   3.165m ±  3%       ~ (p=0.589 n=6)
Throughput_Medium_48kTo32k-4                                        3.114m ±  2%   3.135m ±  2%       ~ (p=0.485 n=6)
Throughput_High_48kTo32k-4                                          3.802m ±  2%   3.747m ±  2%       ~ (p=0.093 n=6)
Throughput_VeryHigh_48kTo32k-4                                      4.745m ±  2%   4.703m ±  2%       ~ (p=0.310 n=6)
Throughput_VeryHigh_44kTo48k-4                                      6.151m ±  3%   5.973m ±  3%       ~ (p=0.132 n=6)
Throughput_VeryHigh_48kTo96k-4                                      3.427m ±  5%   3.381m ±  4%       ~ (p=0.310 n=6)
geomean                                                             26.63µ         26.74µ        +0.43%

                                            │ /home/runner/work/_temp/base.bench │   /home/runner/work/_temp/pr.bench   │
                                            │                B/op                │     B/op      vs base                │
Resampler_CD2DAT-4                                                1.122Mi ± 0%     1.120Mi ± 0%       ~ (p=0.446 n=6)
Resampler_Process-4                                               25.36Ki ± 0%     25.36Ki ± 0%       ~ (p=0.318 n=6)
Crossover_16_SIMD-4                                                 0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_16_Unrolled-4                                             0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_20_SIMD-4                                                 0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_20_Unrolled-4                                             0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_32_SIMD-4                                                 0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_32_Unrolled-4                                             0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_48_SIMD-4                                                 0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_48_Unrolled-4                                             0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_64_SIMD-4                                                 0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_64_Unrolled-4                                             0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_128_SIMD-4                                                0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_128_Unrolled-4                                            0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Dot20_SIMD-4                                                        0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Dot20_GoUnrolled4-4                                                 0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Dot20_GoSimple-4                                                    0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
PolyphaseIteration_SIMD-4                                           0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
PolyphaseIteration_GoUnrolled-4                                     0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_ConvolveValid_Taps32-4                                         0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_ConvolveValid_Taps64-4                                         0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_ConvolveValid_Taps128-4                                        0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_ConvolveValid_Taps256-4                                        0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_ConvolveValidMulti_2Phase_Taps64-4                             0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_ConvolveValidMulti_4Phase_Taps64-4                             0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_DotProduct_20-4                                                0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_DotProduct_32-4                                                0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_DotProduct_64-4                                                0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_DotProduct_128-4                                               0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_Interleave2_4096-4                                             0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_Interleave2_8192-4                                             0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_DFTStage_Quick-4                                             764.5Ki ± 0%     765.0Ki ± 0%       ~ (p=0.310 n=6)
Func_DFTStage_Medium-4                                            771.9Ki ± 0%     771.9Ki ± 0%       ~ (p=0.554 n=6)
Func_DFTStage_VeryHigh-4                                          783.4Ki ± 0%     784.2Ki ± 0%  +0.11% (p=0.019 n=6)
Func_PolyphaseStage_Quick_Down-4                                  585.3Ki ± 0%     585.3Ki ± 0%       ~ (p=0.788 n=6)
Func_PolyphaseStage_Medium_Down-4                                 585.3Ki ± 0%     585.2Ki ± 0%       ~ (p=0.305 n=6)
Func_PolyphaseStage_VeryHigh_Down-4                               595.8Ki ± 0%     597.1Ki ± 0%  +0.22% (p=0.024 n=6)
Func_PolyphaseStage_VeryHigh_Up-4                                 216.2Ki ± 0%     216.1Ki ± 0%       ~ (p=0.939 n=6)
Func_CubicInterpolation_20Taps-4                                    0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_CubicInterpolation_32Taps-4                                    0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_CubicInterpolation_64Taps-4                                    0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_CubicInterpolation_100Taps-4                                   0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_CubicInterpolation_64Taps_Unrolled4-4                          0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_FullPipeline_48kTo32k_Quick-4                                256.0Ki ± 0%     256.7Ki ± 0%  +0.29% (p=0.002 n=6)
Func_FullPipeline_48kTo32k_Medium-4                               1.044Mi ± 0%     1.039Mi ± 0%  -0.44% (p=0.013 n=6)
Func_FullPipeline_48kTo32k_VeryHigh-4                             1.074Mi ± 1%     1.070Mi ± 1%       ~ (p=0.089 n=6)
Func_FullPipeline_44kTo48k_VeryHigh-4                             1.157Mi ± 1%     1.154Mi ± 0%       ~ (p=0.784 n=6)
Float64Resampler-4                                                1.124Mi ± 1%     1.117Mi ± 0%  -0.56% (p=0.035 n=6)
Float32Resampler-4                                                569.5Ki ± 0%     569.0Ki ± 0%       ~ (p=0.177 n=6)
Float64DFTStage-4                                                 709.7Ki ± 0%     710.1Ki ± 0%       ~ (p=0.846 n=6)
Float32DFTStage-4                                                 353.0Ki ± 0%     352.9Ki ± 0%       ~ (p=0.732 n=6)
Float64Polyphase-4                                                395.1Ki ± 0%     393.3Ki ± 0%  -0.47% (p=0.032 n=6)
Float32Polyphase-4                                                199.2Ki ± 0%     199.4Ki ± 1%  +0.07% (p=0.022 n=6)
Precision_Float64_44kTo48k_VeryHigh-4                             1.157Mi ± 1%     1.157Mi ± 1%       ~ (p=0.636 n=6)
Precision_Float32_44kTo48k_VeryHigh-4                             576.3Ki ± 0%     574.9Ki ± 0%  -0.25% (p=0.028 n=6)
Precision_Float64_48kTo32k_VeryHigh-4                             1.070Mi ± 1%     1.072Mi ± 1%       ~ (p=0.924 n=6)
Precision_Float32_48kTo32k_VeryHigh-4                             537.5Ki ± 0%     537.5Ki ± 1%       ~ (p=0.870 n=6)
Precision_Float64_48kTo44k_VeryHigh-4                             1.182Mi ± 1%     1.179Mi ± 0%       ~ (p=0.232 n=6)
Precision_Float32_48kTo44k_VeryHigh-4                             590.4Ki ± 0%     590.4Ki ± 0%       ~ (p=0.113 n=6)
Precision_Float64_44kTo48k_Quick-4                                376.0Ki ± 0%     377.4Ki ± 0%  +0.38% (p=0.002 n=6)
Precision_Float32_44kTo48k_Quick-4                                192.0Ki ± 0%     192.7Ki ± 0%  +0.35% (p=0.002 n=6)
Precision_Float64_48kTo32k_Quick-4                                256.0Ki ± 0%     256.8Ki ± 0%  +0.29% (p=0.002 n=6)
Precision_Float32_48kTo32k_Quick-4                                128.0Ki ± 0%     128.3Ki ± 0%  +0.27% (p=0.002 n=6)
Precision_PolyphaseStage_Float64_VeryHigh-4                       595.8Ki ± 0%     597.1Ki ± 0%  +0.22% (p=0.022 n=6)
Precision_PolyphaseStage_Float32_VeryHigh-4                       300.8Ki ± 0%     300.6Ki ± 0%       ~ (p=0.455 n=6)
Precision_DFTStage_Float64_VeryHigh-4                             783.8Ki ± 0%     783.4Ki ± 0%       ~ (p=0.991 n=6)
Precision_DFTStage_Float32_VeryHigh-4                             386.7Ki ± 0%     386.7Ki ± 0%       ~ (p=0.714 n=6)
DFTStage_Only-4                                                   710.3Ki ± 0%     709.7Ki ± 0%       ~ (p=0.251 n=6)
PolyphaseStage_Only-4                                             395.0Ki ± 1%     394.5Ki ± 0%       ~ (p=0.420 n=6)
Resampler_QualityLow-4                                            1.103Mi ± 1%     1.098Mi ± 1%       ~ (p=0.069 n=6)
Resampler_QualityMedium-4                                         1.101Mi ± 0%     1.098Mi ± 1%       ~ (p=0.303 n=6)
ConvolveValid_Separate-4                                            0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
ConvolveValidMulti-4                                                0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Interleave_Scalar-4                                                 0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Interleave2-4                                                       0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
DotProduct_20-4                                                     0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
DotProduct_20_Manual-4                                              0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
DotProduct_20_Unrolled4-4                                           0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
Throughput_Quick_48kTo32k-4                                       256.0Ki ± 0%     256.0Ki ± 0%       ~ (p=1.000 n=6) ¹
Throughput_Low_48kTo32k-4                                         1.040Mi ± 1%     1.042Mi ± 0%       ~ (p=0.409 n=6)
Throughput_Medium_48kTo32k-4                                      1.041Mi ± 0%     1.038Mi ± 1%       ~ (p=0.924 n=6)
Throughput_High_48kTo32k-4                                        1.050Mi ± 0%     1.051Mi ± 0%       ~ (p=0.662 n=6)
Throughput_VeryHigh_48kTo32k-4                                    1.068Mi ± 0%     1.068Mi ± 0%       ~ (p=1.000 n=6)
Throughput_VeryHigh_44kTo48k-4                                    1.155Mi ± 0%     1.155Mi ± 1%       ~ (p=0.346 n=6)
Throughput_VeryHigh_48kTo96k-4                                    783.6Ki ± 0%     783.6Ki ± 0%       ~ (p=0.595 n=6)
geomean                                                                        ²                 -0.02%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                            │ /home/runner/work/_temp/base.bench │  /home/runner/work/_temp/pr.bench  │
                                            │             allocs/op              │ allocs/op   vs base                │
Resampler_CD2DAT-4                                                  2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Resampler_Process-4                                                 2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_16_SIMD-4                                                 0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_16_Unrolled-4                                             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_20_SIMD-4                                                 0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_20_Unrolled-4                                             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_32_SIMD-4                                                 0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_32_Unrolled-4                                             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_48_SIMD-4                                                 0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_48_Unrolled-4                                             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_64_SIMD-4                                                 0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_64_Unrolled-4                                             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_128_SIMD-4                                                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Crossover_128_Unrolled-4                                            0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Dot20_SIMD-4                                                        0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Dot20_GoUnrolled4-4                                                 0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Dot20_GoSimple-4                                                    0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
PolyphaseIteration_SIMD-4                                           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
PolyphaseIteration_GoUnrolled-4                                     0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_ConvolveValid_Taps32-4                                         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_ConvolveValid_Taps64-4                                         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_ConvolveValid_Taps128-4                                        0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_ConvolveValid_Taps256-4                                        0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_ConvolveValidMulti_2Phase_Taps64-4                             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_ConvolveValidMulti_4Phase_Taps64-4                             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_DotProduct_20-4                                                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_DotProduct_32-4                                                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_DotProduct_64-4                                                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_DotProduct_128-4                                               0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_Interleave2_4096-4                                             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_Interleave2_8192-4                                             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_DFTStage_Quick-4                                               1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_DFTStage_Medium-4                                              1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_DFTStage_VeryHigh-4                                            1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_PolyphaseStage_Quick_Down-4                                    1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_PolyphaseStage_Medium_Down-4                                   1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_PolyphaseStage_VeryHigh_Down-4                                 1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_PolyphaseStage_VeryHigh_Up-4                                   1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_CubicInterpolation_20Taps-4                                    0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_CubicInterpolation_32Taps-4                                    0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_CubicInterpolation_64Taps-4                                    0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_CubicInterpolation_100Taps-4                                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_CubicInterpolation_64Taps_Unrolled4-4                          0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_FullPipeline_48kTo32k_Quick-4                                  1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_FullPipeline_48kTo32k_Medium-4                                 2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_FullPipeline_48kTo32k_VeryHigh-4                               2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Func_FullPipeline_44kTo48k_VeryHigh-4                               2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Float64Resampler-4                                                  2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Float32Resampler-4                                                  2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Float64DFTStage-4                                                   1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Float32DFTStage-4                                                   1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Float64Polyphase-4                                                  1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Float32Polyphase-4                                                  1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Precision_Float64_44kTo48k_VeryHigh-4                               2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Precision_Float32_44kTo48k_VeryHigh-4                               2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Precision_Float64_48kTo32k_VeryHigh-4                               2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Precision_Float32_48kTo32k_VeryHigh-4                               2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Precision_Float64_48kTo44k_VeryHigh-4                               2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Precision_Float32_48kTo44k_VeryHigh-4                               2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Precision_Float64_44kTo48k_Quick-4                                  1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Precision_Float32_44kTo48k_Quick-4                                  1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Precision_Float64_48kTo32k_Quick-4                                  1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Precision_Float32_48kTo32k_Quick-4                                  1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Precision_PolyphaseStage_Float64_VeryHigh-4                         1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Precision_PolyphaseStage_Float32_VeryHigh-4                         1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Precision_DFTStage_Float64_VeryHigh-4                               1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Precision_DFTStage_Float32_VeryHigh-4                               1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
DFTStage_Only-4                                                     1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
PolyphaseStage_Only-4                                               1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Resampler_QualityLow-4                                              2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Resampler_QualityMedium-4                                           2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
ConvolveValid_Separate-4                                            0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
ConvolveValidMulti-4                                                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Interleave_Scalar-4                                                 0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Interleave2-4                                                       0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
DotProduct_20-4                                                     0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
DotProduct_20_Manual-4                                              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
DotProduct_20_Unrolled4-4                                           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
Throughput_Quick_48kTo32k-4                                         1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
Throughput_Low_48kTo32k-4                                           2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Throughput_Medium_48kTo32k-4                                        2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Throughput_High_48kTo32k-4                                          2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Throughput_VeryHigh_48kTo32k-4                                      2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Throughput_VeryHigh_44kTo48k-4                                      2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
Throughput_VeryHigh_48kTo96k-4                                      1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                                        ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                        │ /home/runner/work/_temp/base.bench │  /home/runner/work/_temp/pr.bench  │
                                        │                B/s                 │     B/s       vs base              │
Func_ConvolveValid_Taps32-4                                     947.5Mi ± 1%   957.0Mi ± 2%       ~ (p=0.065 n=6)
Func_ConvolveValid_Taps64-4                                     697.2Mi ± 2%   701.4Mi ± 1%       ~ (p=0.485 n=6)
Func_ConvolveValid_Taps128-4                                    415.5Mi ± 1%   419.6Mi ± 3%       ~ (p=0.310 n=6)
Func_ConvolveValid_Taps256-4                                    249.2Mi ± 0%   250.7Mi ± 1%  +0.62% (p=0.009 n=6)
Func_ConvolveValidMulti_2Phase_Taps64-4                         699.1Mi ± 0%   698.7Mi ± 1%       ~ (p=0.937 n=6)
Func_ConvolveValidMulti_4Phase_Taps64-4                         695.4Mi ± 1%   704.1Mi ± 8%       ~ (p=0.065 n=6)
Func_Interleave2_4096-4                                         46.69Gi ± 1%   46.70Gi ± 2%       ~ (p=0.937 n=6)
Func_Interleave2_8192-4                                         46.40Gi ± 3%   46.60Gi ± 1%       ~ (p=0.132 n=6)
Throughput_Quick_48kTo32k-4                                     1.036Gi ± 1%   1.007Gi ± 1%  -2.76% (p=0.002 n=6)
Throughput_Low_48kTo32k-4                                       115.5Mi ± 2%   115.7Mi ± 3%       ~ (p=0.589 n=6)
Throughput_Medium_48kTo32k-4                                    117.6Mi ± 2%   116.8Mi ± 2%       ~ (p=0.485 n=6)
Throughput_High_48kTo32k-4                                      96.33Mi ± 2%   97.74Mi ± 2%       ~ (p=0.093 n=6)
Throughput_VeryHigh_48kTo32k-4                                  77.18Mi ± 2%   77.87Mi ± 2%       ~ (p=0.310 n=6)
Throughput_VeryHigh_44kTo48k-4                                  54.70Mi ± 3%   56.33Mi ± 3%       ~ (p=0.102 n=6)
Throughput_VeryHigh_48kTo96k-4                                  106.8Mi ± 5%   108.3Mi ± 4%       ~ (p=0.310 n=6)
geomean                                                         514.8Mi        517.6Mi       +0.54%

                                            │ /home/runner/work/_temp/base.bench │ /home/runner/work/_temp/pr.bench  │
                                            │                MS/s                │    MS/s      vs base              │
Func_ConvolveValid_Taps32-4                                           124.2 ± 1%    125.4 ± 2%       ~ (p=0.058 n=6)
Func_ConvolveValid_Taps64-4                                           91.38 ± 2%    91.94 ± 1%       ~ (p=0.485 n=6)
Func_ConvolveValid_Taps128-4                                          54.47 ± 1%    55.00 ± 3%       ~ (p=0.258 n=6)
Func_ConvolveValid_Taps256-4                                          32.66 ± 1%    32.86 ± 1%  +0.60% (p=0.009 n=6)
Func_ConvolveValidMulti_2Phase_Taps64-4                               91.64 ± 0%    91.58 ± 1%       ~ (p=0.909 n=6)
Func_ConvolveValidMulti_4Phase_Taps64-4                               91.15 ± 1%    92.28 ± 8%       ~ (p=0.065 n=6)
Func_Interleave2_4096-4                                              6.266k ± 1%   6.268k ± 2%       ~ (p=0.965 n=6)
Func_Interleave2_8192-4                                              6.229k ± 3%   6.255k ± 1%       ~ (p=0.132 n=6)
Func_DFTStage_Quick-4                                                 36.75 ± 1%    36.98 ± 0%       ~ (p=0.180 n=6)
Func_DFTStage_Medium-4                                                23.43 ± 1%    23.38 ± 1%       ~ (p=0.781 n=6)
Func_DFTStage_VeryHigh-4                                              15.10 ± 1%    15.03 ± 1%       ~ (p=0.071 n=6)
Func_PolyphaseStage_Quick_Down-4                                      21.64 ± 1%    21.72 ± 1%       ~ (p=1.000 n=6)
Func_PolyphaseStage_Medium_Down-4                                     21.69 ± 1%    21.66 ± 1%       ~ (p=0.623 n=6)
Func_PolyphaseStage_VeryHigh_Down-4                                   13.77 ± 0%    13.71 ± 4%       ~ (p=0.149 n=6)
Func_PolyphaseStage_VeryHigh_Up-4                                     28.59 ± 3%    29.12 ± 3%       ~ (p=0.310 n=6)
Func_FullPipeline_48kTo32k_Quick-4                                    140.4 ± 1%    139.4 ± 1%  -0.75% (p=0.017 n=6)
Func_FullPipeline_48kTo32k_Medium-4                                   15.75 ± 5%    15.75 ± 1%       ~ (p=0.978 n=6)
Func_FullPipeline_48kTo32k_VeryHigh-4                                 10.32 ± 1%    10.24 ± 1%       ~ (p=0.160 n=6)
Func_FullPipeline_44kTo48k_VeryHigh-4                                 7.423 ± 2%    7.285 ± 1%  -1.85% (p=0.004 n=6)
Precision_Float64_44kTo48k_VeryHigh-4                                 7.356 ± 1%    7.338 ± 2%       ~ (p=0.970 n=6)
Precision_Float32_44kTo48k_VeryHigh-4                                 10.54 ± 3%    10.50 ± 9%       ~ (p=0.589 n=6)
Precision_Float64_48kTo32k_VeryHigh-4                                 10.25 ± 1%    10.28 ± 1%       ~ (p=0.617 n=6)
Precision_Float32_48kTo32k_VeryHigh-4                                 13.48 ± 5%    13.36 ± 1%       ~ (p=0.058 n=6)
Precision_Float64_48kTo44k_VeryHigh-4                                 9.338 ± 2%    9.271 ± 2%       ~ (p=0.310 n=6)
Precision_Float32_48kTo44k_VeryHigh-4                                 12.06 ± 1%    11.98 ± 1%       ~ (p=0.058 n=6)
Precision_Float64_44kTo48k_Quick-4                                   105.50 ± 0%    96.66 ± 1%  -8.37% (p=0.002 n=6)
Precision_Float32_44kTo48k_Quick-4                                    109.5 ± 1%    105.3 ± 1%  -3.84% (p=0.002 n=6)
Precision_Float64_48kTo32k_Quick-4                                    138.4 ± 1%    135.1 ± 3%  -2.35% (p=0.002 n=6)
Precision_Float32_48kTo32k_Quick-4                                    142.7 ± 1%    148.7 ± 1%  +4.20% (p=0.002 n=6)
Precision_PolyphaseStage_Float64_VeryHigh-4                           13.62 ± 1%    13.25 ± 1%  -2.75% (p=0.002 n=6)
Precision_PolyphaseStage_Float32_VeryHigh-4                           15.66 ± 0%    15.54 ± 0%  -0.73% (p=0.006 n=6)
Precision_DFTStage_Float64_VeryHigh-4                                 14.90 ± 1%    14.39 ± 1%  -3.46% (p=0.002 n=6)
Precision_DFTStage_Float32_VeryHigh-4                                 22.29 ± 1%    21.91 ± 1%  -1.70% (p=0.006 n=6)
Throughput_Quick_48kTo32k-4                                           139.1 ± 1%    135.2 ± 1%  -2.77% (p=0.002 n=6)
Throughput_Low_48kTo32k-4                                             15.14 ± 2%    15.16 ± 3%       ~ (p=0.556 n=6)
Throughput_Medium_48kTo32k-4                                          15.41 ± 2%    15.30 ± 2%       ~ (p=0.457 n=6)
Throughput_High_48kTo32k-4                                            12.62 ± 2%    12.82 ± 2%       ~ (p=0.093 n=6)
Throughput_VeryHigh_48kTo32k-4                                        10.12 ± 2%    10.21 ± 2%       ~ (p=0.292 n=6)
Throughput_VeryHigh_44kTo48k-4                                        7.171 ± 3%    7.383 ± 3%       ~ (p=0.132 n=6)
Throughput_VeryHigh_48kTo96k-4                                        14.00 ± 5%    14.20 ± 4%       ~ (p=0.310 n=6)
geomean                                                               34.15         34.02       -0.40%

                                           │ /home/runner/work/_temp/base.bench │ /home/runner/work/_temp/pr.bench │
                                           │               GFLOPS               │   GFLOPS    vs base              │
Func_DotProduct_20-4                                                 2.556 ± 2%   2.561 ± 1%       ~ (p=0.732 n=6)
Func_DotProduct_32-4                                                 3.771 ± 1%   3.797 ± 0%  +0.70% (p=0.006 n=6)
Func_DotProduct_64-4                                                 6.186 ± 0%   6.193 ± 1%       ~ (p=0.457 n=6)
Func_DotProduct_128-4                                                7.795 ± 0%   7.841 ± 3%       ~ (p=0.063 n=6)
Func_CubicInterpolation_20Taps-4                                     1.903 ± 1%   1.902 ± 0%       ~ (p=0.740 n=6)
Func_CubicInterpolation_32Taps-4                                     1.853 ± 0%   1.851 ± 6%       ~ (p=0.074 n=6)
Func_CubicInterpolation_64Taps-4                                     1.813 ± 2%   1.812 ± 2%       ~ (p=0.610 n=6)
Func_CubicInterpolation_100Taps-4                                    1.800 ± 0%   1.798 ± 0%       ~ (p=0.065 n=6)
Func_CubicInterpolation_64Taps_Unrolled4-4                           1.847 ± 0%   1.847 ± 0%       ~ (p=0.522 n=6)
geomean                                                              2.779        2.783       +0.14%

                                           │ /home/runner/work/_temp/base.bench │ /home/runner/work/_temp/pr.bench  │
                                           │            M_samples/s             │ M_samples/s  vs base              │
Func_CubicInterpolation_20Taps-4                                     13.60 ± 1%    13.59 ± 0%       ~ (p=0.675 n=6)
Func_CubicInterpolation_32Taps-4                                     8.274 ± 0%    8.264 ± 6%  -0.12% (p=0.041 n=6)
Func_CubicInterpolation_64Taps-4                                     4.046 ± 2%    4.043 ± 2%       ~ (p=0.617 n=6)
Func_CubicInterpolation_100Taps-4                                    2.572 ± 0%    2.569 ± 0%       ~ (p=0.061 n=6)
Func_CubicInterpolation_64Taps_Unrolled4-4                           4.122 ± 0%    4.123 ± 0%       ~ (p=0.626 n=6)
geomean                                                              5.454         5.450       -0.07%

pkg: github.com/tphakala/go-audio-resampler/internal/filter
                             │ /home/runner/work/_temp/base.bench │ /home/runner/work/_temp/pr.bench  │
                             │               sec/op               │   sec/op     vs base              │
KaiserWindow/length_51-4                              1.230µ ± 1%   1.225µ ± 1%       ~ (p=0.076 n=6)
KaiserWindow/length_101-4                             2.415µ ± 1%   2.399µ ± 1%  -0.64% (p=0.039 n=6)
KaiserWindow/length_201-4                             4.747µ ± 0%   4.749µ ± 2%       ~ (p=0.615 n=6)
DesignLowPassFilter-4                                 7.614µ ± 1%   7.552µ ± 1%       ~ (p=0.180 n=6)
ComputeFrequencyResponse-4                            4.334m ± 0%   4.343m ± 0%       ~ (p=0.180 n=6)
DesignPolyphaseFilterBank-4                           185.4µ ± 1%   184.8µ ± 1%       ~ (p=0.937 n=6)
PolyphaseGetCoefficient-4                             2.216n ± 0%   2.219n ± 0%       ~ (p=0.725 n=6)
PolyphaseFrequencyResponse-4                          189.3µ ± 0%   188.8µ ± 1%  -0.25% (p=0.015 n=6)
geomean                                               8.807µ        8.784µ       -0.27%

                             │ /home/runner/work/_temp/base.bench │   /home/runner/work/_temp/pr.bench   │
                             │                B/op                │     B/op      vs base                │
KaiserWindow/length_51-4                             416.0 ± 0%       416.0 ± 0%       ~ (p=1.000 n=6) ¹
KaiserWindow/length_101-4                            896.0 ± 0%       896.0 ± 0%       ~ (p=1.000 n=6) ¹
KaiserWindow/length_201-4                          1.750Ki ± 0%     1.750Ki ± 0%       ~ (p=1.000 n=6) ¹
DesignLowPassFilter-4                              3.500Ki ± 0%     3.500Ki ± 0%       ~ (p=1.000 n=6) ¹
ComputeFrequencyResponse-4                         24.00Ki ± 0%     24.00Ki ± 0%       ~ (p=1.000 n=6) ¹
DesignPolyphaseFilterBank-4                        130.3Ki ± 0%     130.3Ki ± 0%       ~ (p=1.000 n=6) ¹
PolyphaseGetCoefficient-4                            0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
PolyphaseFrequencyResponse-4                       12.12Ki ± 0%     12.12Ki ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                         ²                 +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                             │ /home/runner/work/_temp/base.bench │  /home/runner/work/_temp/pr.bench  │
                             │             allocs/op              │ allocs/op   vs base                │
KaiserWindow/length_51-4                             1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
KaiserWindow/length_101-4                            1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
KaiserWindow/length_201-4                            1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
DesignLowPassFilter-4                                2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
ComputeFrequencyResponse-4                           3.000 ± 0%     3.000 ± 0%       ~ (p=1.000 n=6) ¹
DesignPolyphaseFilterBank-4                          6.000 ± 0%     6.000 ± 0%       ~ (p=1.000 n=6) ¹
PolyphaseGetCoefficient-4                            0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
PolyphaseFrequencyResponse-4                         4.000 ± 0%     4.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                         ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/tphakala/go-audio-resampler/internal/mathutil
                                   │ /home/runner/work/_temp/base.bench │ /home/runner/work/_temp/pr.bench  │
                                   │               sec/op               │   sec/op     vs base              │
BesselI0_Small-4                                            4.012n ± 2%   4.015n ± 1%       ~ (p=0.853 n=6)
BesselI0_Large-4                                            15.43n ± 1%   15.40n ± 1%       ~ (p=0.234 n=6)
KaiserBeta-4                                                2.506n ± 5%   2.502n ± 1%       ~ (p=0.515 n=6)
EstimateFilterLength-4                                      1.407n ± 0%   1.405n ± 0%       ~ (p=0.400 n=6)
FilterLengthCalculation/Quick-4                             2.230n ± 0%   2.228n ± 0%       ~ (p=0.920 n=6)
FilterLengthCalculation/Low-4                               2.237n ± 1%   2.236n ± 3%       ~ (p=0.734 n=6)
FilterLengthCalculation/Medium-4                            2.233n ± 0%   2.230n ± 0%       ~ (p=0.227 n=6)
FilterLengthCalculation/High-4                              2.232n ± 1%   2.228n ± 2%       ~ (p=0.110 n=6)
FilterLengthCalculation/VeryHigh-4                          2.229n ± 1%   2.231n ± 9%       ~ (p=0.781 n=6)
KaiserBetaCalculation/Quick-4                               2.812n ± 0%   2.821n ± 1%       ~ (p=0.223 n=6)
KaiserBetaCalculation/Low-4                                 32.96n ± 0%   32.98n ± 0%       ~ (p=0.775 n=6)
KaiserBetaCalculation/Medium-4                              32.95n ± 0%   32.99n ± 0%       ~ (p=0.139 n=6)
KaiserBetaCalculation/High-4                                32.97n ± 0%   32.98n ± 0%       ~ (p=0.331 n=6)
KaiserBetaCalculation/VeryHigh-4                            32.96n ± 0%   32.94n ± 0%       ~ (p=0.961 n=6)
geomean                                                     5.720n        5.719n       -0.02%

                                   │ /home/runner/work/_temp/base.bench │  /home/runner/work/_temp/pr.bench  │
                                   │                B/op                │    B/op     vs base                │
BesselI0_Small-4                                           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
BesselI0_Large-4                                           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
KaiserBeta-4                                               0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
EstimateFilterLength-4                                     0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
FilterLengthCalculation/Quick-4                            0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
FilterLengthCalculation/Low-4                              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
FilterLengthCalculation/Medium-4                           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
FilterLengthCalculation/High-4                             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
FilterLengthCalculation/VeryHigh-4                         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
KaiserBetaCalculation/Quick-4                              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
KaiserBetaCalculation/Low-4                                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
KaiserBetaCalculation/Medium-4                             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
KaiserBetaCalculation/High-4                               0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
KaiserBetaCalculation/VeryHigh-4                           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                               ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                   │ /home/runner/work/_temp/base.bench │  /home/runner/work/_temp/pr.bench  │
                                   │             allocs/op              │ allocs/op   vs base                │
BesselI0_Small-4                                           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
BesselI0_Large-4                                           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
KaiserBeta-4                                               0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
EstimateFilterLength-4                                     0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
FilterLengthCalculation/Quick-4                            0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
FilterLengthCalculation/Low-4                              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
FilterLengthCalculation/Medium-4                           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
FilterLengthCalculation/High-4                             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
FilterLengthCalculation/VeryHigh-4                         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
KaiserBetaCalculation/Quick-4                              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
KaiserBetaCalculation/Low-4                                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
KaiserBetaCalculation/Medium-4                             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
KaiserBetaCalculation/High-4                               0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
KaiserBetaCalculation/VeryHigh-4                           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                               ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/tphakala/go-audio-resampler/internal/simdops
                              │ /home/runner/work/_temp/base.bench │ /home/runner/work/_temp/pr.bench  │
                              │               sec/op               │   sec/op     vs base              │
DirectF64DotProduct-4                                  9.258n ± 2%   9.261n ± 1%       ~ (p=0.729 n=6)
IndirectF64DotProduct-4                                10.40n ± 0%   10.40n ± 0%       ~ (p=0.998 n=6)
DirectF32DotProduct-4                                  7.691n ± 7%   7.703n ± 0%       ~ (p=0.937 n=6)
IndirectF32DotProduct-4                                8.834n ± 0%   8.806n ± 0%  -0.31% (p=0.015 n=6)
DirectF64ConvolveValid-4                               778.8n ± 0%   779.7n ± 0%       ~ (p=0.623 n=6)
IndirectF64ConvolveValid-4                             779.9n ± 2%   780.0n ± 0%       ~ (p=1.000 n=6)
DirectF64DotProduct_Large-4                            86.94n ± 0%   86.80n ± 0%       ~ (p=0.779 n=6)
IndirectF64DotProduct_Large-4                          87.60n ± 1%   87.42n ± 1%       ~ (p=0.699 n=6)
geomean                                                48.43n        48.40n       -0.05%

                              │ /home/runner/work/_temp/base.bench │  /home/runner/work/_temp/pr.bench  │
                              │                B/op                │    B/op     vs base                │
DirectF64DotProduct-4                                 0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
IndirectF64DotProduct-4                               0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
DirectF32DotProduct-4                                 0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
IndirectF32DotProduct-4                               0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
DirectF64ConvolveValid-4                              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
IndirectF64ConvolveValid-4                            0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
DirectF64DotProduct_Large-4                           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
IndirectF64DotProduct_Large-4                         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                          ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                              │ /home/runner/work/_temp/base.bench │  /home/runner/work/_temp/pr.bench  │
                              │             allocs/op              │ allocs/op   vs base                │
DirectF64DotProduct-4                                 0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
IndirectF64DotProduct-4                               0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
DirectF32DotProduct-4                                 0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
IndirectF32DotProduct-4                               0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
DirectF64ConvolveValid-4                              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
IndirectF64ConvolveValid-4                            0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
DirectF64DotProduct_Large-4                           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
IndirectF64DotProduct_Large-4                         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                          ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

Extract the per-plan chunked run in TestStreamingEquivalence_Float64 into a
helper to drop cognitive complexity below 50; replace append([]float64(nil),
x...) with slices.Clone(x) in the streaming and flush-lifecycle tests. In the
streaming example, name the loop count and tone constants, use range-over-int
and max(), and start the FIFO zero-length with capacity so the priming zeros
are appended (satisfies makezero).

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes multiple engine and streaming-contract defects that caused audible clicks/distortions in chunked real-time usage, and adds tests and documentation to pin the corrected behavior (chunked Process + single terminal Flush matches one-shot processing bit-exactly).

Changes:

  • Fix polyphase coefficient boundary interpolation (avoid wrapped neighbor), severe downsampling history/accumulator behavior, and Flush padding/lifecycle so streaming is click-free and deterministic.
  • Introduce Latency() on SimpleResampler/SimpleResamplerFloat32 for real-time FIFO priming, and document the streaming contract (single Flush at end-of-stream, per-channel instance).
  • Add extensive regression tests covering streaming equivalence, flush length/lifecycle, NaN validation, QualityQuick cubic behavior, memory/latency accounting, and buffer/aliasing invariants.

Reviewed changes

Copilot reviewed 31 out of 31 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
streaming_equivalence_test.go Pins bit-exact equivalence between chunked streaming (Process + final Flush) and one-shot processing.
stages.go Propagates half-band stage construction errors instead of silently falling back to a stub.
stages_test.go Tests half-band stage creation and ensures no stub fallback occurs in pipelines.
resample.go Makes config validation NaN-safe for sample rates and ratio bounds.
README.md Documents streaming/FIFO usage, terminal Flush, and adds a runnable streaming example link.
quality_quick_test.go Ensures QualityQuick via NewEngine maps to cubic (low-latency) behavior.
processinto_test.go Adds warm-path zero-allocation assertions for ProcessInto on QualityQuick (float64/float32).
pipeline_builder.go Updates half-band stage creation to return (Stage, error) and propagate errors.
nan_validation_test.go Adds tests ensuring NaN sample rates are rejected by constructors.
latency_test.go Verifies Latency() matches measured startup deficit (float64 and float32).
internal/engine/stage_adapter.go Improves latency/memory reporting to account for more stages and coefficient banks.
internal/engine/stage_adapter_test.go Adds coverage for new latency/memory accounting branches (cubic/decimation/banks).
internal/engine/severe_ratio_test.go Adds regression test for severe non-integer downsampling monotonicity and bounded history.
internal/engine/reset_state_test.go Strengthens reset determinism tests and adds reset-after-flush coverage (float64/float32).
internal/engine/resampler.go Makes validation NaN-safe, fixes cubic ProcessZeroCopy path, adds terminal cubic-tail flush accounting, and implements Latency().
internal/engine/polyphase_stage.go Fixes coefficient neighbor indexing, severe-downsample history consumption, and makes Flush terminal with correct padding.
internal/engine/phase_wrap_measure_test.go Adds measurement/regression harness validating the phase-boundary interpolation fix via THD+N and bank equality.
internal/engine/dft_stage.go Ensures Process always returns owned memory, improves buffer growth policy, and makes Flush terminal with correct padding.
internal/engine/debug_latency_test.go Replaces/debug refactors latency test to validate Latency() against measured deficit.
internal/engine/cubic.go Fixes cubic priming/tail behavior, enforces owned-return contract, and adds reusable output buffer for warm zero-alloc streaming.
internal/engine/cubic_flush_test.go Adds comprehensive cubic-stage tail, lifecycle, ownership, and streaming-equivalence tests.
internal/engine/buffer_integrity_test.go Strengthens tests to assert determinism and that returned buffers are not corrupted across calls.
flush_multi_test.go Improves multi-channel flush test to validate sample content equality, not just length, and pins empty-flush behavior.
flush_lifecycle_test.go Pins terminal Flush lifecycle semantics across ratios/qualities (including QualityQuick).
flush_length_test.go Pins Process+Flush total length bounds to prevent phantom padding samples.
examples/streaming/main.go Adds runnable real-time FIFO streaming example using Latency()-primed buffering and deficit-driven sizing.
doc.go Updates package docs with streaming contract, FIFO/latency guidance, and safer example error handling.
convenience.go Documents streaming usage, makes Flush semantics explicit, adds Latency() accessors, and corrects QualityQuick engine mapping.
convenience_float32_test.go Strengthens float32 flush test to require a real drained tail after warm processing.
CHANGELOG.md Documents behavioral changes and fixes affecting streaming, flush lifecycle/length, QualityQuick mapping, and NaN validation.
aliasing_test.go Adds regression test ensuring unity-ratio output does not alias caller input.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread processinto_test.go
Comment on lines +166 to +172
allocs := testing.AllocsPerRun(100, func() {
r.Reset()
_, _ = r.ProcessInto(input, output)
})
if allocs != 0 {
t.Fatalf("ProcessInto allocated %.0f times per call; expected 0", allocs)
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 84e4371. The helper now captures the error into a pre-declared variable inside the closure and asserts it with require.NoError after AllocsPerRun returns. Same treatment applied to the float32 helper and the three sibling zero-alloc tests that shared the pattern.

Comment thread processinto_test.go
Comment on lines +567 to +573
allocs := testing.AllocsPerRun(100, func() {
r.Reset()
_, _ = r.ProcessInto(input, output)
})
if allocs != 0 {
t.Fatalf("ProcessInto allocated %.0f times per call; expected 0", allocs)
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 84e4371, together with the float64 helper and the three inline sibling tests using the same pattern. Allocation assertions still measure 0 allocs.

The AllocsPerRun closures in the zero-alloc tests and helpers discarded the
ProcessInto/ProcessFloat32Into error, so an erroring call (which may allocate
nothing) would still satisfy the 0-alloc assertion and mask a regression.
Capture the error in a pre-declared var (assigning to an already-escaped var
inside the closure does not allocate) and require.NoError after AllocsPerRun
returns, across all five sites (two warm helpers plus the three inline
sibling tests).
@tphakala
tphakala merged commit 823e02c into master Jul 17, 2026
17 checks passed
@tphakala
tphakala deleted the fix/issue-51-streaming-contract branch July 17, 2026 13:24
tphakala added a commit that referenced this pull request Jul 17, 2026
…56

CodeRabbit review: the CHANGELOG attributes the defect to #51, so the
active-interpolation THD comment now names both the reporting issue and
the fixing PR instead of the PR alone.
tphakala added a commit that referenced this pull request Jul 17, 2026
…ad code retirement (#57)

* refactor(engine): extract shared cubic interp bank builder

The phase-wrap measurement test previously re-derived the Catmull-Rom
bank construction to build its differential reference, so a formula
change would have required lockstep edits. NewPolyphaseStage and the
test now share buildCubicInterpBanks and the test varies only the
boundary-indexing policy. The bank-equality assertion pins the
extraction bit-for-bit.

Refs #53

* test(engine): pin full-pipeline THD at active-interpolation ratios

quality_regression_test.go previously exercised only exact-rational or
integer ratios where the sub-phase interpolation banks are never
consulted, so the phase-boundary indexing fix from #56 was guarded only
at stage level. Add THD assertions through the whole resampler at
32000->44100 and 44100->64000 for High, Medium, and Low. High gets a
dedicated tighter limit (-145 dB, worst measured -151.63 dB); Medium and
Low fall within margin of the existing constants and reuse them.

Refs #54

* fix: report the true startup deficit from constantRateResampler.GetLatency

GetLatency summed per-stage group-delay heuristics whose terms live in
different rate domains, so multi-stage pipelines mis-reported the
startup deficit (672 reported vs 703 measured at 44100 to 96000
QualityHigh). The engine Resampler now exposes StartupDeficit(), the
un-rounded deficit in output samples that Latency() ceils, CubicStage
mirrors it, and GetLatency accumulates each stage's fractional deficit
through the downstream stage ratios before rounding once. Accuracy is
now within 2 samples across presets and ratios, pinned by a numeric
test on the New(config) path.

Refs #52

* refactor: retire duplicate polyphase design path and dead LinearStage

internal/filter/polyphase.go carried a parallel polyphase design
implementation that diverged from the engine (edge clamping, DC-gain
normalization, unchecked GetCoefficient, duplicated cubic constants)
and was consumed only by the cmd/analyze-filter diagnostic, so the tool
analyzed a filter the product never ships. Remove both along with the
production-dead LinearStage and its constants; the engine's design code
in internal/engine is now the single polyphase implementation. kaiser.go
stays, the engine depends on it.

Refs #55

* refactor: harden startup-deficit contract and align docs from review

Gate review follow-ups: README no longer describes the retired
group-delay semantics of GetLatency on the New(config) path; the
duck-typed deficit assertion uses a named startupDeficitStage interface
with compile-time assertions in stages.go so a refactor of the
StageAdapter embedding fails the build instead of silently degrading to
the group-delay fallback; the cubic deficit formula lives only in
CubicStage.StartupDeficit with the engine delegating to it; the config
path latency test now covers QualityQuick and a new test pins the
fallback branch including its downstream-ratio conversion;
TestCubicStageGetters asserts the new getter; stale or imprecise
comments corrected (StageAdapter.GetLatency consumer note, THD margin
arithmetic, duplicated bank-build description, what the bank-equality
pin does and does not guard).

Refs #52 #53 #54

* docs(test): attribute phase-boundary defect to issue #51 alongside PR #56

CodeRabbit review: the CHANGELOG attributes the defect to #51, so the
active-interpolation THD comment now names both the reporting issue and
the fixing PR instead of the PR alone.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

clicks / distortions when resampling

2 participants