P20 crossover - convert the crossover module to only use the sink/source api - #10999
P20 crossover - convert the crossover module to only use the sink/source api#10999piotrhoppeintel wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Reworks the Crossover component’s processing path to use the SOF sof_source / sof_sink APIs (instead of stream buffers) as preparation for the pipeline 2.0 transition, and adds dedicated CMocka unit tests for LR4 filter behavior and split topology routing.
Changes:
- Switch crossover processing entrypoint and processing function pointers to
source/sink-based APIs. - Implement
source/sinkcircular-buffer processing incrossover_generic.cfor s16/s24/s32 formats plus passthrough copy viasource_to_sink_copy(). - Add CMocka unit tests and build integration for LR4 filter properties and 2/3/4-way split topology functions.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/cmocka/src/audio/crossover/crossover_test_mocks.c | Adds link-time stubs for source/sink APIs needed by unit tests that compile crossover_generic.c. |
| test/cmocka/src/audio/crossover/crossover_split_test.c | Adds split topology unit tests validating routing/energy behavior for 2/3/4-way splits. |
| test/cmocka/src/audio/crossover/crossover_lr4_test.c | Adds LR4 filter property tests (DC/HF behavior, split symmetry at fc, selectivity, denominator consistency). |
| test/cmocka/src/audio/crossover/CMakeLists.txt | Adds CMocka targets for crossover LR4 and split tests, wiring required sources. |
| test/cmocka/src/audio/CMakeLists.txt | Enables the new crossover test subdirectory behind CONFIG_COMP_CROSSOVER. |
| src/audio/crossover/crossover.h | Updates processing function typedefs to return int and take sof_source/sof_sink handles. |
| src/audio/crossover/crossover.c | Converts module .process implementation to source/sink APIs and updates sink assignment logic accordingly. |
| src/audio/crossover/crossover_generic.c | Reworks per-format processing/passthrough to use source/sink APIs and circular buffer helpers. |
| src/arch/xtensa/configs/unit_test_defconfig | Enables crossover component config for unit-test builds. |
| endif() | ||
| if(CONFIG_COMP_CROSSOVER) | ||
| add_subdirectory(crossover) | ||
| endif() |
There was a problem hiding this comment.
we've had a few PRs recently moving from over cmocka to Zephyr native tasting. Shouldn't this be done as a ztest too?
There was a problem hiding this comment.
Done. Tests moved to the Ztest framework.
|
Please fix PR and commit title btw: #10110 |
9fc9a6b to
2915070
Compare
|
All comments have been addressed. @lyakh please review. |
PR 10999: test resultsRun date: 2026-09-03 11:54 UTC Tested commit: 9ee144db0f9b5256776660864b4a844e5fe0e6de |
| rms_out_hf = rms_q31(out + WARMUP_LEN, SIGNAL_LEN - WARMUP_LEN); | ||
| printf("HP HF: f=%.0fHz input rms=%.4f output rms=%.4f\n", | ||
| f_hf, rms_in, rms_out_hf); | ||
| zassert_true(fabs(rms_out_hf - rms_in) / rms_in < 0.05, |
There was a problem hiding this comment.
do we have any science behind these numbers? Is 5% enough or do we need 0.05%? Similar for 0.01 in line 351 and supposedly other occurrences too.
There was a problem hiding this comment.
zassert_true(fabs(rms_out_hf - rms_in) / rms_in < 0.05,
0.05 represents the maximum allowed relative RMS error of 5%. The test verifies that the LR4 high-pass filter passes a high-frequency signal at 0.45 * FS = 21.6 kHz with approximately unity gain:
[
\frac{|RMS_{out} - RMS_{in}|}{RMS_{in}} < 0.05
]
Since rms_in ≈ 0.3536 , the permitted absolute difference is approximately 0.0177, giving an accepted output range of roughly 0.3359–0.3712 . This corresponds to a gain between 0.95 and 1.05, or approximately −0.45 dB to +0.42 dB.
The tolerance accounts for Q2.30 coefficient quantization, fixed-point rounding, and RMS measurement over a finite window that does not contain an integer number of signal periods. It is a test acceptance threshold, not a filter parameter.
There was a problem hiding this comment.
right, but why 5%? why not 1% or 10% or 0.1%?
There was a problem hiding this comment.
It is a subjective choice and is related to the description above so I added a safety margin that seemed reasonable for this test.
| int32_t delay[LR4_NDELAY], | ||
| const int32_t biquad_coef[BIQUAD_NCOEF]) | ||
| { | ||
| memcpy(coef, biquad_coef, BIQUAD_NCOEF * sizeof(int32_t)); |
There was a problem hiding this comment.
these spaces look strange. There's a similar location in crossover_split_ztest.c
There was a problem hiding this comment.
I've removed spaces.
| double lp_50 = rms_buf(out[0] + WARMUP_LEN, SIGNAL_LEN - WARMUP_LEN); | ||
| double hp_50 = rms_buf(out[1] + WARMUP_LEN, SIGNAL_LEN - WARMUP_LEN); | ||
|
|
||
| /* 20 kHz -- should be dominated by HP (band 1) */ |
There was a problem hiding this comment.
interesting, we split low / high at 1kHz, right? Should we also test frequencies like 950Hz / 1050Hz?
There was a problem hiding this comment.
The main goal of these tests was to verify the migration to the new API. Please don't treat these tests as strictly as the crossover module tests. The test cases were chosen subjectively.
If you see any additional cases or frequencies that should be covered, please let me know. I'll try to add them.
| */ | ||
| for (i = 0; i < CROSSOVER_MAX_LR4; i++) { | ||
| double fc = (i == 0) ? fc_lo : | ||
| (i == 1) ? ((fc_lo + fc_hi) / 2.0) : fc_hi; |
There was a problem hiding this comment.
ok, splitting hairs here, but how about
double fc[CROSSOVER_MAX_LR4] = {fc_lo, (fc_lo + fc_hi) / 2.0, fc_hi};
for (i = 0;...) {
...
compute_biquad_lp(FS, fc[i], bq_lp);
There was a problem hiding this comment.
I've implemented your suggestion.
Add seven LR4 filter tests and ten split topology tests as dedicated Ztest suites running on native_sim. Add the Twister test package and shared crossover mocks. Signed-off-by: Piotr Hoppe <piotr.hoppe@intel.com>
Rework the crossover module to only use the sink/source api to prepare the SOF for the full transition to pipeline 2.0. Signed-off-by: Piotr Hoppe <piotr.hoppe@intel.com>
2915070 to
9ee144d
Compare
Rework the crossover module to only use the sink/source api to
prepare the SOF for the full transition to pipeline 2.0.