-
Notifications
You must be signed in to change notification settings - Fork 366
P20 dcblock #11000
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
piotrhoppeintel
wants to merge
3
commits into
thesofproject:main
Choose a base branch
from
piotrhoppeintel:p20-dcblock
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
P20 dcblock #11000
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| CONFIG_METEORLAKE=y | ||
| CONFIG_COMP_DRC=y | ||
| CONFIG_COMP_DCBLOCK=y |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
|
|
||
| cmocka_test(dcblock_math_test | ||
| dcblock_math_test.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/dcblock/dcblock_generic.c | ||
| ${PROJECT_SOURCE_DIR}/src/math/numbers.c | ||
| ) | ||
|
|
||
| target_include_directories(dcblock_math_test PRIVATE ${PROJECT_SOURCE_DIR}/src/audio) | ||
| target_include_directories(dcblock_math_test PRIVATE ${PROJECT_SOURCE_DIR}/src/audio/dcblock) | ||
|
|
||
| cmocka_test(dcblock_process | ||
| dcblock_process.c | ||
| ) | ||
|
|
||
| target_include_directories(dcblock_process PRIVATE ${PROJECT_SOURCE_DIR}/src/audio) | ||
| target_include_directories(dcblock_process PRIVATE ${PROJECT_SOURCE_DIR}/src/audio/dcblock) | ||
|
|
||
| # make small version of libaudio so we don't have to care | ||
| # about unused missing references | ||
|
|
||
| add_compile_options(-DUNIT_TEST) | ||
|
|
||
| add_library(audio_for_dcblock STATIC | ||
| ${PROJECT_SOURCE_DIR}/src/audio/dcblock/dcblock.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/dcblock/dcblock_generic.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/dcblock/dcblock_ipc3.c | ||
| ${PROJECT_SOURCE_DIR}/src/math/numbers.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module_adapter.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module_adapter_ipc3.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module/generic.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/buffers/comp_buffer.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/buffers/audio_buffer.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/source_api_helper.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/sink_api_helper.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/sink_source_utils.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/audio_stream.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/component.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/data_blob.c | ||
| ${PROJECT_SOURCE_DIR}/src/module/audio/source_api.c | ||
| ${PROJECT_SOURCE_DIR}/src/module/audio/sink_api.c | ||
| ${PROJECT_SOURCE_DIR}/src/ipc/ipc3/helper.c | ||
| ${PROJECT_SOURCE_DIR}/src/ipc/ipc-common.c | ||
| ${PROJECT_SOURCE_DIR}/src/ipc/ipc-helper.c | ||
| ${PROJECT_SOURCE_DIR}/src/lib/objpool.c | ||
| ${PROJECT_SOURCE_DIR}/test/cmocka/src/notifier_mocks.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-graph.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-params.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-schedule.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-stream.c | ||
| ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-xrun.c | ||
| ) | ||
| sof_append_relative_path_definitions(audio_for_dcblock) | ||
|
|
||
| target_link_libraries(audio_for_dcblock PRIVATE sof_options) | ||
|
|
||
| target_link_libraries(dcblock_process PRIVATE audio_for_dcblock) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,327 @@ | ||
| // SPDX-License-Identifier: BSD-3-Clause | ||
| // | ||
| // Copyright(c) 2026 Intel Corporation. | ||
|
|
||
| #include <math.h> | ||
| #include <stdarg.h> | ||
| #include <stddef.h> | ||
| #include <setjmp.h> | ||
| #include <stdint.h> | ||
| #include <stdio.h> | ||
| #include <string.h> | ||
| #include <cmocka.h> | ||
|
|
||
| #include <sof/audio/audio_stream.h> | ||
| #include <sof/audio/format.h> | ||
|
|
||
| #include "dcblock.h" | ||
|
|
||
| #define TEST_CHANNELS 2 | ||
| #define TEST_FRAMES 256 | ||
|
|
||
| /* Q2.30 coefficient close to 1.0 used for the DC removal test cases. */ | ||
| #define R_COEF_NEAR_ONE 1063004406 /* ~0.99 in Q2.30 */ | ||
|
|
||
| /* | ||
| * Reference DC blocking filter implemented in floating point. Mirrors the | ||
| * fixed point recurrence y[n] = x[n] - x[n-1] + R * y[n-1] where R is the | ||
| * Q2.30 coefficient converted to a real number. | ||
| */ | ||
| struct ref_state { | ||
| double x_prev; | ||
| double y_prev; | ||
| }; | ||
|
|
||
| static double dcblock_ref(struct ref_state *s, double r, double x) | ||
| { | ||
| double y = x - s->x_prev + r * s->y_prev; | ||
|
|
||
| s->x_prev = x; | ||
| s->y_prev = y; | ||
| return y; | ||
| } | ||
|
|
||
| /* Build an audio_stream over a linear (non wrapping) sample buffer. */ | ||
| static void setup_stream(struct audio_stream *stream, void *data, | ||
| size_t bytes, enum sof_ipc_frame fmt, int channels) | ||
| { | ||
| memset(stream, 0, sizeof(*stream)); | ||
| stream->addr = data; | ||
| stream->end_addr = (char *)data + bytes; | ||
| stream->r_ptr = data; | ||
| stream->w_ptr = data; | ||
| stream->size = bytes; | ||
| stream->runtime_stream_params.frame_fmt = fmt; | ||
| stream->runtime_stream_params.channels = channels; | ||
| } | ||
|
|
||
| /* Fill the source buffer with a per-channel sinusoid plus a DC offset. */ | ||
| static void gen_input(double *ref_in, int channels, int frames, double dc) | ||
| { | ||
| int ch, i; | ||
|
|
||
| for (i = 0; i < frames; i++) { | ||
| for (ch = 0; ch < channels; ch++) { | ||
| double phase = 2.0 * M_PI * (i + 1) * (ch + 1) / 64.0; | ||
|
|
||
| ref_in[i * channels + ch] = dc + 0.3 * sin(phase); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /* | ||
| * Runs the S32 processing function and compares it to the floating point | ||
| * reference. Returns the mean absolute value of the last quarter of the | ||
| * output which is used to check DC convergence. | ||
| */ | ||
| static double run_s32_case(int32_t r_coeff, double dc, double tol_rel) | ||
| { | ||
| struct comp_data cd; | ||
| struct audio_stream source, sink; | ||
| int32_t src[TEST_FRAMES * TEST_CHANNELS]; | ||
| int32_t dst[TEST_FRAMES * TEST_CHANNELS]; | ||
| double ref_in[TEST_FRAMES * TEST_CHANNELS]; | ||
| struct ref_state rstate[TEST_CHANNELS]; | ||
| double r = (double)r_coeff / (double)ONE_Q2_30; | ||
| double tail_abs_sum = 0.0; | ||
| int tail_count = 0; | ||
| dcblock_func func; | ||
| int ch, i; | ||
|
|
||
| memset(&cd, 0, sizeof(cd)); | ||
| for (ch = 0; ch < TEST_CHANNELS; ch++) | ||
| cd.R_coeffs[ch] = r_coeff; | ||
|
|
||
| memset(rstate, 0, sizeof(rstate)); | ||
| gen_input(ref_in, TEST_CHANNELS, TEST_FRAMES, dc); | ||
|
|
||
| for (i = 0; i < TEST_FRAMES * TEST_CHANNELS; i++) | ||
| src[i] = (int32_t)round(ref_in[i] * 2147483647.0); | ||
|
|
||
| setup_stream(&source, src, sizeof(src), SOF_IPC_FRAME_S32_LE, TEST_CHANNELS); | ||
| setup_stream(&sink, dst, sizeof(dst), SOF_IPC_FRAME_S32_LE, TEST_CHANNELS); | ||
|
|
||
| func = dcblock_find_func(SOF_IPC_FRAME_S32_LE); | ||
| assert_non_null(func); | ||
| assert_int_equal(func(&cd, &csrc, &csnk, TEST_FRAMES), 0); | ||
|
|
||
| for (i = 0; i < TEST_FRAMES; i++) { | ||
| for (ch = 0; ch < TEST_CHANNELS; ch++) { | ||
| int idx = i * TEST_CHANNELS + ch; | ||
| double refy = dcblock_ref(&rstate[ch], r, | ||
| (double)src[idx] / 2147483648.0); | ||
| double outy = (double)dst[idx] / 2147483648.0; | ||
| double delta = fabs(refy - outy); | ||
|
|
||
| if (delta > tol_rel) { | ||
| printf("s32 mismatch idx %d ref %g out %g delta %g\n", | ||
| idx, refy, outy, delta); | ||
| assert_true(delta <= tol_rel); | ||
| } | ||
|
|
||
| if (i >= TEST_FRAMES * 3 / 4) { | ||
| tail_abs_sum += fabs(outy); | ||
| tail_count++; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return tail_count ? tail_abs_sum / tail_count : 0.0; | ||
| } | ||
|
|
||
| /* Passthrough: R = 1.0 gives y[n] = x[n] - x[n-1], a pure differentiator. */ | ||
| static void test_dcblock_passthrough(void **state) | ||
| { | ||
| (void)state; | ||
|
|
||
| /* With no DC and R=1.0 the reference matches bit-close. */ | ||
| run_s32_case(ONE_Q2_30, 0.0, 1.0e-6); | ||
| } | ||
|
|
||
| /* A strong DC offset must be attenuated towards zero in steady state. */ | ||
| static void test_dcblock_dc_removal(void **state) | ||
| { | ||
| (void)state; | ||
|
|
||
| double tail = run_s32_case(R_COEF_NEAR_ONE, 0.5, 1.0e-6); | ||
|
|
||
| /* The residual DC plus small sinusoid must be well below the input DC. */ | ||
| printf("dc_removal tail mean abs = %g\n", tail); | ||
| assert_true(tail < 0.25); | ||
| } | ||
|
|
||
| /* Full scale input must not overflow (saturation path in dcblock_generic). */ | ||
| static void test_dcblock_saturation(void **state) | ||
| { | ||
| (void)state; | ||
|
|
||
| struct comp_data cd; | ||
| struct audio_stream source, sink; | ||
| int32_t src[TEST_FRAMES * TEST_CHANNELS]; | ||
| int32_t dst[TEST_FRAMES * TEST_CHANNELS]; | ||
| dcblock_func func; | ||
| int i; | ||
|
|
||
| memset(&cd, 0, sizeof(cd)); | ||
| for (i = 0; i < TEST_CHANNELS; i++) | ||
| cd.R_coeffs[i] = ONE_Q2_30; | ||
|
|
||
| /* Alternating +full/-full scale is the worst case for the difference. */ | ||
| for (i = 0; i < TEST_FRAMES * TEST_CHANNELS; i++) | ||
| src[i] = (i & 1) ? INT32_MAX : INT32_MIN; | ||
|
|
||
| setup_stream(&source, src, sizeof(src), SOF_IPC_FRAME_S32_LE, TEST_CHANNELS); | ||
| setup_stream(&sink, dst, sizeof(dst), SOF_IPC_FRAME_S32_LE, TEST_CHANNELS); | ||
|
|
||
| func = dcblock_find_func(SOF_IPC_FRAME_S32_LE); | ||
| assert_non_null(func); | ||
| assert_int_equal(func(&cd, &csrc, &csnk, TEST_FRAMES), 0); | ||
|
|
||
| for (i = 0; i < TEST_FRAMES * TEST_CHANNELS; i++) { | ||
| assert_true(dst[i] <= INT32_MAX); | ||
| assert_true(dst[i] >= INT32_MIN); | ||
| } | ||
|
Comment on lines
+190
to
+193
|
||
| } | ||
|
|
||
| /* Bit-exactness check against the floating point reference for S32. */ | ||
| static void test_dcblock_bitexact_s32(void **state) | ||
| { | ||
| (void)state; | ||
|
|
||
| run_s32_case(R_COEF_NEAR_ONE, 0.1, 1.0e-6); | ||
| } | ||
|
|
||
| /* | ||
| * Runs the S16 processing function and compares it to the floating point | ||
| * reference. The component internally works in Q1.31, so the tolerance must | ||
| * account for the 16-bit output quantization step (2 LSB of S16). | ||
| */ | ||
| static void run_s16_case(int32_t r_coeff, double dc) | ||
| { | ||
| struct comp_data cd; | ||
| struct audio_stream source, sink; | ||
| int16_t src[TEST_FRAMES * TEST_CHANNELS]; | ||
| int16_t dst[TEST_FRAMES * TEST_CHANNELS]; | ||
| double ref_in[TEST_FRAMES * TEST_CHANNELS]; | ||
| struct ref_state rstate[TEST_CHANNELS]; | ||
| double r = (double)r_coeff / (double)ONE_Q2_30; | ||
| double tol = 2.0 / 32768.0; | ||
| dcblock_func func; | ||
| int ch, i; | ||
|
|
||
| memset(&cd, 0, sizeof(cd)); | ||
| for (ch = 0; ch < TEST_CHANNELS; ch++) | ||
| cd.R_coeffs[ch] = r_coeff; | ||
|
|
||
| memset(rstate, 0, sizeof(rstate)); | ||
| gen_input(ref_in, TEST_CHANNELS, TEST_FRAMES, dc); | ||
|
|
||
| for (i = 0; i < TEST_FRAMES * TEST_CHANNELS; i++) | ||
| src[i] = (int16_t)round(ref_in[i] * 32767.0); | ||
|
|
||
| setup_stream(&source, src, sizeof(src), SOF_IPC_FRAME_S16_LE, TEST_CHANNELS); | ||
| setup_stream(&sink, dst, sizeof(dst), SOF_IPC_FRAME_S16_LE, TEST_CHANNELS); | ||
|
|
||
| func = dcblock_find_func(SOF_IPC_FRAME_S16_LE); | ||
| assert_non_null(func); | ||
| assert_int_equal(func(&cd, &csrc, &csnk, TEST_FRAMES), 0); | ||
|
|
||
| for (i = 0; i < TEST_FRAMES; i++) { | ||
| for (ch = 0; ch < TEST_CHANNELS; ch++) { | ||
| int idx = i * TEST_CHANNELS + ch; | ||
| double refy = dcblock_ref(&rstate[ch], r, | ||
| (double)src[idx] / 32768.0); | ||
| double outy = (double)dst[idx] / 32768.0; | ||
| double delta = fabs(refy - outy); | ||
|
|
||
| if (delta > tol) { | ||
| printf("s16 mismatch idx %d ref %g out %g delta %g\n", | ||
| idx, refy, outy, delta); | ||
| assert_true(delta <= tol); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /* Bit-exactness check against the floating point reference for S16. */ | ||
| static void test_dcblock_bitexact_s16(void **state) | ||
| { | ||
| (void)state; | ||
|
|
||
| run_s16_case(R_COEF_NEAR_ONE, 0.1); | ||
| } | ||
|
|
||
| /* | ||
| * Runs the S24 (in 32-bit container) processing function and compares it to | ||
| * the floating point reference with a tolerance of 2 LSB of S24. | ||
| */ | ||
| static void run_s24_case(int32_t r_coeff, double dc) | ||
| { | ||
| struct comp_data cd; | ||
| struct audio_stream source, sink; | ||
| int32_t src[TEST_FRAMES * TEST_CHANNELS]; | ||
| int32_t dst[TEST_FRAMES * TEST_CHANNELS]; | ||
| double ref_in[TEST_FRAMES * TEST_CHANNELS]; | ||
| struct ref_state rstate[TEST_CHANNELS]; | ||
| double r = (double)r_coeff / (double)ONE_Q2_30; | ||
| double tol = 2.0 / 8388608.0; | ||
| dcblock_func func; | ||
| int ch, i; | ||
|
|
||
| memset(&cd, 0, sizeof(cd)); | ||
| for (ch = 0; ch < TEST_CHANNELS; ch++) | ||
| cd.R_coeffs[ch] = r_coeff; | ||
|
|
||
| memset(rstate, 0, sizeof(rstate)); | ||
| gen_input(ref_in, TEST_CHANNELS, TEST_FRAMES, dc); | ||
|
|
||
| for (i = 0; i < TEST_FRAMES * TEST_CHANNELS; i++) | ||
| src[i] = (int32_t)round(ref_in[i] * 8388607.0); | ||
|
|
||
| setup_stream(&source, src, sizeof(src), SOF_IPC_FRAME_S24_4LE, TEST_CHANNELS); | ||
| setup_stream(&sink, dst, sizeof(dst), SOF_IPC_FRAME_S24_4LE, TEST_CHANNELS); | ||
|
|
||
| func = dcblock_find_func(SOF_IPC_FRAME_S24_4LE); | ||
| assert_non_null(func); | ||
| func(&cd, &source, &sink, TEST_FRAMES); | ||
|
|
||
| for (i = 0; i < TEST_FRAMES; i++) { | ||
| for (ch = 0; ch < TEST_CHANNELS; ch++) { | ||
| int idx = i * TEST_CHANNELS + ch; | ||
| double refy = dcblock_ref(&rstate[ch], r, | ||
| (double)src[idx] / 8388608.0); | ||
| double outy = (double)dst[idx] / 8388608.0; | ||
| double delta = fabs(refy - outy); | ||
|
|
||
| if (delta > tol) { | ||
| printf("s24 mismatch idx %d ref %g out %g delta %g\n", | ||
| idx, refy, outy, delta); | ||
| assert_true(delta <= tol); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /* Bit-exactness check against the floating point reference for S24. */ | ||
| static void test_dcblock_bitexact_s24(void **state) | ||
| { | ||
| (void)state; | ||
|
|
||
| run_s24_case(R_COEF_NEAR_ONE, 0.1); | ||
| } | ||
|
|
||
| int main(void) | ||
| { | ||
| const struct CMUnitTest tests[] = { | ||
| cmocka_unit_test(test_dcblock_passthrough), | ||
| cmocka_unit_test(test_dcblock_dc_removal), | ||
| cmocka_unit_test(test_dcblock_saturation), | ||
| cmocka_unit_test(test_dcblock_bitexact_s32), | ||
| cmocka_unit_test(test_dcblock_bitexact_s16), | ||
| cmocka_unit_test(test_dcblock_bitexact_s24), | ||
| }; | ||
|
|
||
| cmocka_set_message_output(CM_OUTPUT_TAP); | ||
|
|
||
| return cmocka_run_group_tests(tests, NULL, NULL); | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same question re: cmocka vs. ztest