muvox-io / euphonium

Tiny audio platform
GNU Affero General Public License v3.0
96 stars 15 forks source link

Problem: Can't apply the needed filtering without introducing a lot of digital noise #40

Open TooDissing opened 2 years ago

TooDissing commented 2 years ago

Hey,

When adding additional filters or when tuning the EQ up to +12, then I hear pretty bad digital noise. It sounds like clipping, but could also be something else. Not sure how and where to test for this?

I also tried reducing the signal by 50% by multiplying with 0.5. But the digital artifacts still persisted. So it most be somewhere else in the chain.

feelfreelinux commented 2 years ago

If i understand correctly, the issue most likely related to lack of headroom in the EQ code. You could try to lower the value in SoftwareVolumeProcessor.cpp (default volume to something lower) in order to simulate it, but its not really the best solution.

TooDissing commented 2 years ago

was looking at that code. Does the µVox driver override this code?

std::vector<uint8_t> volumeLookup = std::vector<uint8_t>(
        {255, 160, 120, 100, 90, 85, 80, 75, 70, 65, 61, 57, 53, 50, 47, 44, 41,
         38,  35,  32,  29,  26, 23, 20, 17, 14, 12, 10, 8,  6,  4,  2,  0});

Because when running higher than 24 we are adding gain.

TooDissing commented 2 years ago

Oh, and this artifacts seems to be there with even very low volume.

Are we touches gain somewhere else in the platform?

With this recent test I am doing a high pass shelf with negative gain. This should really not introduce clipping

TooDissing commented 2 years ago

are the processor out of juice? And any good way to verify this?

TooDissing commented 2 years ago

Assuming the problem is missing EQ headroom, is there any way I can validate whether or not we are close to the limit? What are the maximum allowed value for the elements in the data_16 array? Then I could start there.

It enters as uint8_t but is then cast to int16_t. With Spotify I assume we are talking a 16 signal.

Been using Camila DSP with the same set of filters and there it is working pretty good.

Any idea on when and where this is converted to a 32bit one (if at all)?

Any other great ideas of where to look?

TooDissing commented 2 years ago

I am a bit lost here. Tried looking at various classes, but didn't find any smoking gun. Did a small "data capture" of the data being processed by the filters (in EqualizerProcessor.h). How ever this data doesn't tell us much if we do not know what the limit is.

So any clue on the maximum value?

Data coming in:

I [eq] EqualizerProcessor.h:155: process; data_16[0] (d): -653
I [eq] EqualizerProcessor.h:156: process; data_16[0] (d): -1931
I [eq] EqualizerProcessor.h:157: process; dataLeft[0]: -0.01992858760058879852
I [eq] EqualizerProcessor.h:158: process; dataRight[0]: -0.05893124267458915710
I [eq] EqualizerProcessor.h:155: process; data_16[100] (d): -3974
I [eq] EqualizerProcessor.h:156: process; data_16[100] (d): -4110
I [eq] EqualizerProcessor.h:157: process; dataLeft[100]: -0.12128055840730667114
I [eq] EqualizerProcessor.h:158: process; dataRight[100]: -0.12543107569217681885
I [eq] EqualizerProcessor.h:155: process; data_16[200] (d): -3728
I [eq] EqualizerProcessor.h:156: process; data_16[200] (d): -3629
I [eq] EqualizerProcessor.h:157: process; dataLeft[200]: -0.11377300322055816650
I [eq] EqualizerProcessor.h:158: process; dataRight[200]: -0.11075167357921600342
I [eq] EqualizerProcessor.h:155: process; data_16[300] (d): -3300
I [eq] EqualizerProcessor.h:156: process; data_16[300] (d): -3868
I [eq] EqualizerProcessor.h:157: process; dataLeft[300]: -0.10071108490228652954
I [eq] EqualizerProcessor.h:158: process; dataRight[300]: -0.11804559826850891113
I [eq] EqualizerProcessor.h:155: process; data_16[400] (d): 133
I [eq] EqualizerProcessor.h:156: process; data_16[400] (d): -819
I [eq] EqualizerProcessor.h:157: process; dataLeft[400]: 0.00405896175652742386
I [eq] EqualizerProcessor.h:158: process; dataRight[400]: -0.02499466016888618469
I [eq] EqualizerProcessor.h:155: process; data_16[500] (d): 4946
I [eq] EqualizerProcessor.h:156: process; data_16[500] (d): 5215
I [eq] EqualizerProcessor.h:157: process; dataLeft[500]: 0.15094454586505889893
I [eq] EqualizerProcessor.h:158: process; dataRight[500]: 0.15915402770042419434
I [eq] EqualizerProcessor.h:155: process; data_16[600] (d): 2968
I [eq] EqualizerProcessor.h:156: process; data_16[600] (d): 2819
I [eq] EqualizerProcessor.h:157: process; dataLeft[600]: 0.09057893604040145874
I [eq] EqualizerProcessor.h:158: process; dataRight[600]: 0.08603167533874511719
I [eq] EqualizerProcessor.h:155: process; data_16[700] (d): -1605
I [eq] EqualizerProcessor.h:156: process; data_16[700] (d): -682
I [eq] EqualizerProcessor.h:157: process; dataLeft[700]: -0.04898220673203468323
I [eq] EqualizerProcessor.h:158: process; dataRight[700]: -0.02081362344324588776
I [eq] EqualizerProcessor.h:155: process; data_16[800] (d): 103
I [eq] EqualizerProcessor.h:156: process; data_16[800] (d): -1696
I [eq] EqualizerProcessor.h:157: process; dataLeft[800]: 0.00314340647310018539
I [eq] EqualizerProcessor.h:158: process; dataRight[800]: -0.05175939202308654785
I [eq] EqualizerProcessor.h:155: process; data_16[900] (d): 2026
I [eq] EqualizerProcessor.h:156: process; data_16[900] (d): 1013
I [eq] EqualizerProcessor.h:157: process; dataLeft[900]: 0.06183050200343132019
I [eq] EqualizerProcessor.h:158: process; dataRight[900]: 0.03091525100171566010
I [eq] EqualizerProcessor.h:155: process; data_16[1000] (d): 1460
I [eq] EqualizerProcessor.h:156: process; data_16[1000] (d): 787
I [eq] EqualizerProcessor.h:157: process; dataLeft[1000]: 0.04455702379345893860
I [eq] EqualizerProcessor.h:158: process; dataRight[1000]: 0.02401806786656379700

Data coming out:

I [eq] EqualizerProcessor.h:230: processed; data_16[0] (d): -219
I [eq] EqualizerProcessor.h:231: processed; data_16[0] (d): -366
I [eq] EqualizerProcessor.h:232: processed; dataLeft[0]: -0.006712
I [eq] EqualizerProcessor.h:233: processed; dataRight[0]: -0.011195
I [eq] EqualizerProcessor.h:230: processed; data_16[100] (d): -2798
I [eq] EqualizerProcessor.h:231: processed; data_16[100] (d): -2824
I [eq] EqualizerProcessor.h:232: processed; dataLeft[100]: -0.085407
I [eq] EqualizerProcessor.h:233: processed; dataRight[100]: -0.086193
I [eq] EqualizerProcessor.h:230: processed; data_16[200] (d): -3145
I [eq] EqualizerProcessor.h:231: processed; data_16[200] (d): -3433
I [eq] EqualizerProcessor.h:232: processed; dataLeft[200]: -0.095996
I [eq] EqualizerProcessor.h:233: processed; dataRight[200]: -0.104772
I [eq] EqualizerProcessor.h:230: processed; data_16[300] (d): -2544
I [eq] EqualizerProcessor.h:231: processed; data_16[300] (d): -2968
I [eq] EqualizerProcessor.h:232: processed; dataLeft[300]: -0.077643
I [eq] EqualizerProcessor.h:233: processed; dataRight[300]: -0.090594
I [eq] EqualizerProcessor.h:230: processed; data_16[400] (d): -1194
I [eq] EqualizerProcessor.h:231: processed; data_16[400] (d): -1360
I [eq] EqualizerProcessor.h:232: processed; dataLeft[400]: -0.036443
I [eq] EqualizerProcessor.h:233: processed; dataRight[400]: -0.041511
I [eq] EqualizerProcessor.h:230: processed; data_16[500] (d): 1863
I [eq] EqualizerProcessor.h:231: processed; data_16[500] (d): 1892
I [eq] EqualizerProcessor.h:232: processed; dataLeft[500]: 0.056879
I [eq] EqualizerProcessor.h:233: processed; dataRight[500]: 0.057752
I [eq] EqualizerProcessor.h:230: processed; data_16[600] (d): 2875
I [eq] EqualizerProcessor.h:231: processed; data_16[600] (d): 2863
I [eq] EqualizerProcessor.h:232: processed; dataLeft[600]: 0.087742
I [eq] EqualizerProcessor.h:233: processed; dataRight[600]: 0.087404
I [eq] EqualizerProcessor.h:230: processed; data_16[700] (d): 1789
I [eq] EqualizerProcessor.h:231: processed; data_16[700] (d): 1925
I [eq] EqualizerProcessor.h:232: processed; dataLeft[700]: 0.054617
I [eq] EqualizerProcessor.h:233: processed; dataRight[700]: 0.058761
I [eq] EqualizerProcessor.h:230: processed; data_16[800] (d): 1312
I [eq] EqualizerProcessor.h:231: processed; data_16[800] (d): 1004
I [eq] EqualizerProcessor.h:232: processed; dataLeft[800]: 0.040055
I [eq] EqualizerProcessor.h:233: processed; dataRight[800]: 0.030647
I [eq] EqualizerProcessor.h:230: processed; data_16[900] (d): 880
I [eq] EqualizerProcessor.h:231: processed; data_16[900] (d): 755
I [eq] EqualizerProcessor.h:232: processed; dataLeft[900]: 0.026875
I [eq] EqualizerProcessor.h:233: processed; dataRight[900]: 0.023065
I [eq] EqualizerProcessor.h:230: processed; data_16[1000] (d): 21
I [eq] EqualizerProcessor.h:231: processed; data_16[1000] (d): 305
I [eq] EqualizerProcessor.h:232: processed; dataLeft[1000]: 0.000668
I [eq] EqualizerProcessor.h:233: processed; dataRight[1000]: 0.009326
feelfreelinux commented 2 years ago

I am a bit lost here. Tried looking at various classes, but didn't find any smoking gun. Did a small "data capture" of the data being processed by the filters (in EqualizerProcessor.h). How ever this data doesn't tell us much if we do not know what the limit is.

So any clue on the maximum value?

Data coming in:

I [eq] EqualizerProcessor.h:155: process; data_16[0] (d): -653
I [eq] EqualizerProcessor.h:156: process; data_16[0] (d): -1931
I [eq] EqualizerProcessor.h:157: process; dataLeft[0]: -0.01992858760058879852
I [eq] EqualizerProcessor.h:158: process; dataRight[0]: -0.05893124267458915710
I [eq] EqualizerProcessor.h:155: process; data_16[100] (d): -3974
I [eq] EqualizerProcessor.h:156: process; data_16[100] (d): -4110
I [eq] EqualizerProcessor.h:157: process; dataLeft[100]: -0.12128055840730667114
I [eq] EqualizerProcessor.h:158: process; dataRight[100]: -0.12543107569217681885
I [eq] EqualizerProcessor.h:155: process; data_16[200] (d): -3728
I [eq] EqualizerProcessor.h:156: process; data_16[200] (d): -3629
I [eq] EqualizerProcessor.h:157: process; dataLeft[200]: -0.11377300322055816650
I [eq] EqualizerProcessor.h:158: process; dataRight[200]: -0.11075167357921600342
I [eq] EqualizerProcessor.h:155: process; data_16[300] (d): -3300
I [eq] EqualizerProcessor.h:156: process; data_16[300] (d): -3868
I [eq] EqualizerProcessor.h:157: process; dataLeft[300]: -0.10071108490228652954
I [eq] EqualizerProcessor.h:158: process; dataRight[300]: -0.11804559826850891113
I [eq] EqualizerProcessor.h:155: process; data_16[400] (d): 133
I [eq] EqualizerProcessor.h:156: process; data_16[400] (d): -819
I [eq] EqualizerProcessor.h:157: process; dataLeft[400]: 0.00405896175652742386
I [eq] EqualizerProcessor.h:158: process; dataRight[400]: -0.02499466016888618469
I [eq] EqualizerProcessor.h:155: process; data_16[500] (d): 4946
I [eq] EqualizerProcessor.h:156: process; data_16[500] (d): 5215
I [eq] EqualizerProcessor.h:157: process; dataLeft[500]: 0.15094454586505889893
I [eq] EqualizerProcessor.h:158: process; dataRight[500]: 0.15915402770042419434
I [eq] EqualizerProcessor.h:155: process; data_16[600] (d): 2968
I [eq] EqualizerProcessor.h:156: process; data_16[600] (d): 2819
I [eq] EqualizerProcessor.h:157: process; dataLeft[600]: 0.09057893604040145874
I [eq] EqualizerProcessor.h:158: process; dataRight[600]: 0.08603167533874511719
I [eq] EqualizerProcessor.h:155: process; data_16[700] (d): -1605
I [eq] EqualizerProcessor.h:156: process; data_16[700] (d): -682
I [eq] EqualizerProcessor.h:157: process; dataLeft[700]: -0.04898220673203468323
I [eq] EqualizerProcessor.h:158: process; dataRight[700]: -0.02081362344324588776
I [eq] EqualizerProcessor.h:155: process; data_16[800] (d): 103
I [eq] EqualizerProcessor.h:156: process; data_16[800] (d): -1696
I [eq] EqualizerProcessor.h:157: process; dataLeft[800]: 0.00314340647310018539
I [eq] EqualizerProcessor.h:158: process; dataRight[800]: -0.05175939202308654785
I [eq] EqualizerProcessor.h:155: process; data_16[900] (d): 2026
I [eq] EqualizerProcessor.h:156: process; data_16[900] (d): 1013
I [eq] EqualizerProcessor.h:157: process; dataLeft[900]: 0.06183050200343132019
I [eq] EqualizerProcessor.h:158: process; dataRight[900]: 0.03091525100171566010
I [eq] EqualizerProcessor.h:155: process; data_16[1000] (d): 1460
I [eq] EqualizerProcessor.h:156: process; data_16[1000] (d): 787
I [eq] EqualizerProcessor.h:157: process; dataLeft[1000]: 0.04455702379345893860
I [eq] EqualizerProcessor.h:158: process; dataRight[1000]: 0.02401806786656379700

Data coming out:

I [eq] EqualizerProcessor.h:230: processed; data_16[0] (d): -219
I [eq] EqualizerProcessor.h:231: processed; data_16[0] (d): -366
I [eq] EqualizerProcessor.h:232: processed; dataLeft[0]: -0.006712
I [eq] EqualizerProcessor.h:233: processed; dataRight[0]: -0.011195
I [eq] EqualizerProcessor.h:230: processed; data_16[100] (d): -2798
I [eq] EqualizerProcessor.h:231: processed; data_16[100] (d): -2824
I [eq] EqualizerProcessor.h:232: processed; dataLeft[100]: -0.085407
I [eq] EqualizerProcessor.h:233: processed; dataRight[100]: -0.086193
I [eq] EqualizerProcessor.h:230: processed; data_16[200] (d): -3145
I [eq] EqualizerProcessor.h:231: processed; data_16[200] (d): -3433
I [eq] EqualizerProcessor.h:232: processed; dataLeft[200]: -0.095996
I [eq] EqualizerProcessor.h:233: processed; dataRight[200]: -0.104772
I [eq] EqualizerProcessor.h:230: processed; data_16[300] (d): -2544
I [eq] EqualizerProcessor.h:231: processed; data_16[300] (d): -2968
I [eq] EqualizerProcessor.h:232: processed; dataLeft[300]: -0.077643
I [eq] EqualizerProcessor.h:233: processed; dataRight[300]: -0.090594
I [eq] EqualizerProcessor.h:230: processed; data_16[400] (d): -1194
I [eq] EqualizerProcessor.h:231: processed; data_16[400] (d): -1360
I [eq] EqualizerProcessor.h:232: processed; dataLeft[400]: -0.036443
I [eq] EqualizerProcessor.h:233: processed; dataRight[400]: -0.041511
I [eq] EqualizerProcessor.h:230: processed; data_16[500] (d): 1863
I [eq] EqualizerProcessor.h:231: processed; data_16[500] (d): 1892
I [eq] EqualizerProcessor.h:232: processed; dataLeft[500]: 0.056879
I [eq] EqualizerProcessor.h:233: processed; dataRight[500]: 0.057752
I [eq] EqualizerProcessor.h:230: processed; data_16[600] (d): 2875
I [eq] EqualizerProcessor.h:231: processed; data_16[600] (d): 2863
I [eq] EqualizerProcessor.h:232: processed; dataLeft[600]: 0.087742
I [eq] EqualizerProcessor.h:233: processed; dataRight[600]: 0.087404
I [eq] EqualizerProcessor.h:230: processed; data_16[700] (d): 1789
I [eq] EqualizerProcessor.h:231: processed; data_16[700] (d): 1925
I [eq] EqualizerProcessor.h:232: processed; dataLeft[700]: 0.054617
I [eq] EqualizerProcessor.h:233: processed; dataRight[700]: 0.058761
I [eq] EqualizerProcessor.h:230: processed; data_16[800] (d): 1312
I [eq] EqualizerProcessor.h:231: processed; data_16[800] (d): 1004
I [eq] EqualizerProcessor.h:232: processed; dataLeft[800]: 0.040055
I [eq] EqualizerProcessor.h:233: processed; dataRight[800]: 0.030647
I [eq] EqualizerProcessor.h:230: processed; data_16[900] (d): 880
I [eq] EqualizerProcessor.h:231: processed; data_16[900] (d): 755
I [eq] EqualizerProcessor.h:232: processed; dataLeft[900]: 0.026875
I [eq] EqualizerProcessor.h:233: processed; dataRight[900]: 0.023065
I [eq] EqualizerProcessor.h:230: processed; data_16[1000] (d): 21
I [eq] EqualizerProcessor.h:231: processed; data_16[1000] (d): 305
I [eq] EqualizerProcessor.h:232: processed; dataLeft[1000]: 0.000668
I [eq] EqualizerProcessor.h:233: processed; dataRight[1000]: 0.009326

Hi! Could you send me a list of different filters you are using? I would like to test it out on my setup and debug :)

TooDissing commented 2 years ago

Hi! Could you send me a list of different filters you are using? I would like to test it out on my setup and debug :)

Sure thing. The list is long, but basically having problems with the default setup as well. The above is with a single filter: hsNegative1->generateNativeHighShelfCoEffs(100, -10.0, Q_FACTOR);.

But here's the entire list of filters needed as it is represented by the Camilla DSP YAML format: https://github.com/LydByDissing/stroem/blob/main/voicing/voicing-dsp-filters-descent.yaml

TooDissing commented 2 years ago

BTW, any reason why the coefficient helper functions, in the ESP DSP Library, isn't used in this project? https://docs.espressif.com/projects/esp-dsp/en/latest/esp-dsp-apis.html#iir

Based on the benchmark documentation of dsps_biquad_f32_ae32(input, input, numSamples, coeffs, w);, each IIR filter takes 17450 CPU cycles to compute (dsps_biquad_f32 - biquad filter for 1024 input samples). With 240Mhz, and if my math is correct, we should have plenty of bandwidth to process several filters.

feelfreelinux commented 2 years ago

BTW, any reason why the coefficient helper functions, in the ESP DSP Library, isn't used in this project? https://docs.espressif.com/projects/esp-dsp/en/latest/esp-dsp-apis.html#iir

Based on the benchmark documentation of dsps_biquad_f32_ae32(input, input, numSamples, coeffs, w);, each IIR filter takes 17450 CPU cycles to compute (dsps_biquad_f32 - biquad filter for 1024 input samples). With 240Mhz, and if my math is correct, we should have plenty of bandwidth to process several filters.

As for Euphonium being software targetted for multiple different architectures, I did not use esp-dsp directly. However, on ESP32 platform it does actually back up to using an assembly optimized biquad_f32_ae32 https://github.com/feelfreelinux/bell/blob/master/src/asm/biquad_f32_ae32.S - see here. It is basically the same version as espressif uses. That's why I do not think the issue is due to performance.

TooDissing commented 2 years ago

BTW, any reason why the coefficient helper functions, in the ESP DSP Library, isn't used in this project? https://docs.espressif.com/projects/esp-dsp/en/latest/esp-dsp-apis.html#iir Based on the benchmark documentation of dsps_biquad_f32_ae32(input, input, numSamples, coeffs, w);, each IIR filter takes 17450 CPU cycles to compute (dsps_biquad_f32 - biquad filter for 1024 input samples). With 240Mhz, and if my math is correct, we should have plenty of bandwidth to process several filters.

As for Euphonium being software targetted for multiple different architectures, I did not use esp-dsp directly. However, on ESP32 platform it does actually back up to using an assembly optimized biquad_f32_ae32 https://github.com/feelfreelinux/bell/blob/master/src/asm/biquad_f32_ae32.S - see here. It is basically the same version as espressif uses. That's why I do not think the issue is due to performance.

Makes sense. Yes I saw that you are using the ASM implementation. My comment was mainly on the calculations of the filter coefficients. But as this will need to be used for multi platforms it makes sense, not to depend on that library.