From 5830f4320f1df3ef5900d4f176c8105bd287b4f0 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Tue, 19 Nov 2019 13:34:05 +0200 Subject: [PATCH 1/5] Topology: Apply high-pass for HDA capture streams This patch replaces in topology sof-hda-generic.m4 the use of macro pipe-volume-capture.m4 usage with a new macro pipe-highpass-capture.m4. The used 40 Hz cut-off is sufficient to suppress the DC pulse in the beginning of capture from headset with minimal impact to other applications such as recording music. The capture volume ramp length is changed from 250 ms to 400 ms. The longer ramp is feasible because the microphone path seems to open at 500 ms from capture. It helps to attenuate the remaining noises in the beginning of capture. The patch also adds additional high-pass responses macros for topology with different 20-50 Hz cut-off frequencies into SOF/tools/topology/m4. The changes for Matlab/Octave script example_iir_eq.m that generated the filter coefficients are included here as well. Signed-off-by: Seppo Ingalsuo --- tools/ctl/eq_iir_highpass_20hz_0db_48khz.txt | 1 + tools/ctl/eq_iir_highpass_30hz_0db_48khz.txt | 1 + tools/ctl/eq_iir_highpass_40hz_0db_48khz.txt | 1 + tools/ctl/eq_iir_highpass_50hz_0db_48khz.txt | 1 + .../m4/eq_iir_coef_highpass_20hz_0db_16khz.m4 | 18 +++ .../m4/eq_iir_coef_highpass_20hz_0db_48khz.m4 | 18 +++ .../eq_iir_coef_highpass_20hz_20db_16khz.m4 | 18 +++ .../eq_iir_coef_highpass_20hz_20db_48khz.m4 | 18 +++ .../m4/eq_iir_coef_highpass_30hz_0db_16khz.m4 | 18 +++ .../m4/eq_iir_coef_highpass_30hz_0db_48khz.m4 | 18 +++ .../eq_iir_coef_highpass_30hz_20db_16khz.m4 | 18 +++ .../eq_iir_coef_highpass_30hz_20db_48khz.m4 | 18 +++ .../m4/eq_iir_coef_highpass_40hz_0db_16khz.m4 | 18 +++ .../m4/eq_iir_coef_highpass_40hz_0db_48khz.m4 | 18 +++ .../eq_iir_coef_highpass_40hz_20db_16khz.m4 | 18 +++ .../eq_iir_coef_highpass_40hz_20db_48khz.m4 | 18 +++ .../m4/eq_iir_coef_highpass_50hz_0db_16khz.m4 | 18 +++ .../m4/eq_iir_coef_highpass_50hz_0db_48khz.m4 | 18 +++ tools/topology/sof-hda-generic.m4 | 16 ++- tools/topology/sof/pipe-highpass-capture.m4 | 123 ++++++++++++++++++ tools/tune/eq/example_iir_eq.m | 110 ++++++++-------- 21 files changed, 441 insertions(+), 64 deletions(-) create mode 100644 tools/ctl/eq_iir_highpass_20hz_0db_48khz.txt create mode 100644 tools/ctl/eq_iir_highpass_30hz_0db_48khz.txt create mode 100644 tools/ctl/eq_iir_highpass_40hz_0db_48khz.txt create mode 100644 tools/ctl/eq_iir_highpass_50hz_0db_48khz.txt create mode 100644 tools/topology/m4/eq_iir_coef_highpass_20hz_0db_16khz.m4 create mode 100644 tools/topology/m4/eq_iir_coef_highpass_20hz_0db_48khz.m4 create mode 100644 tools/topology/m4/eq_iir_coef_highpass_20hz_20db_16khz.m4 create mode 100644 tools/topology/m4/eq_iir_coef_highpass_20hz_20db_48khz.m4 create mode 100644 tools/topology/m4/eq_iir_coef_highpass_30hz_0db_16khz.m4 create mode 100644 tools/topology/m4/eq_iir_coef_highpass_30hz_0db_48khz.m4 create mode 100644 tools/topology/m4/eq_iir_coef_highpass_30hz_20db_16khz.m4 create mode 100644 tools/topology/m4/eq_iir_coef_highpass_30hz_20db_48khz.m4 create mode 100644 tools/topology/m4/eq_iir_coef_highpass_40hz_0db_16khz.m4 create mode 100644 tools/topology/m4/eq_iir_coef_highpass_40hz_0db_48khz.m4 create mode 100644 tools/topology/m4/eq_iir_coef_highpass_40hz_20db_16khz.m4 create mode 100644 tools/topology/m4/eq_iir_coef_highpass_40hz_20db_48khz.m4 create mode 100644 tools/topology/m4/eq_iir_coef_highpass_50hz_0db_16khz.m4 create mode 100644 tools/topology/m4/eq_iir_coef_highpass_50hz_0db_48khz.m4 create mode 100644 tools/topology/sof/pipe-highpass-capture.m4 diff --git a/tools/ctl/eq_iir_highpass_20hz_0db_48khz.txt b/tools/ctl/eq_iir_highpass_20hz_0db_48khz.txt new file mode 100644 index 000000000000..b8a91e11868f --- /dev/null +++ b/tools/ctl/eq_iir_highpass_20hz_0db_48khz.txt @@ -0,0 +1 @@ +4607827,0,88,50380800,0,0,0,0,88,2,1,0,0,0,0,0,0,1,1,0,0,0,0,3225193546,2143508228,537150400,3220666496,537150400,0,32690, diff --git a/tools/ctl/eq_iir_highpass_30hz_0db_48khz.txt b/tools/ctl/eq_iir_highpass_30hz_0db_48khz.txt new file mode 100644 index 000000000000..d5077e402bfd --- /dev/null +++ b/tools/ctl/eq_iir_highpass_30hz_0db_48khz.txt @@ -0,0 +1 @@ +4607827,0,88,50380800,0,0,0,0,88,2,1,0,0,0,0,0,0,1,1,0,0,0,0,3227172081,2141520527,536653443,3221660410,536653443,0,32690, diff --git a/tools/ctl/eq_iir_highpass_40hz_0db_48khz.txt b/tools/ctl/eq_iir_highpass_40hz_0db_48khz.txt new file mode 100644 index 000000000000..ebbd75c3c248 --- /dev/null +++ b/tools/ctl/eq_iir_highpass_40hz_0db_48khz.txt @@ -0,0 +1 @@ +4607827,0,88,50380800,0,0,0,0,88,2,1,0,0,0,0,0,0,1,1,0,0,0,0,3229146956,2139532835,536156946,3222653403,536156946,0,32690, diff --git a/tools/ctl/eq_iir_highpass_50hz_0db_48khz.txt b/tools/ctl/eq_iir_highpass_50hz_0db_48khz.txt new file mode 100644 index 000000000000..19d82f920b67 --- /dev/null +++ b/tools/ctl/eq_iir_highpass_50hz_0db_48khz.txt @@ -0,0 +1 @@ +4607827,0,88,50380800,0,0,0,0,88,2,1,0,0,0,0,0,0,1,1,0,0,0,0,3231118179,2137545158,535660909,3223645479,535660909,0,32690, diff --git a/tools/topology/m4/eq_iir_coef_highpass_20hz_0db_16khz.m4 b/tools/topology/m4/eq_iir_coef_highpass_20hz_0db_16khz.m4 new file mode 100644 index 000000000000..cb1fd5451898 --- /dev/null +++ b/tools/topology/m4/eq_iir_coef_highpass_20hz_0db_16khz.m4 @@ -0,0 +1,18 @@ +# 20 Hz second order high-pass, gain 0 dB, created with example_iir_eq.m 27-Nov-2019 +CONTROLBYTES_PRIV(EQIIR_HP20HZ0dB16K_priv, +` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x3c,0xf9,0xb4,0xc0,' +` 0x79,0x05,0x4a,0x7f,0x92,0xf9,0xe5,0x1f,' +` 0xdc,0x0c,0x34,0xc0,0x92,0xf9,0xe5,0x1f,' +` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"' +) diff --git a/tools/topology/m4/eq_iir_coef_highpass_20hz_0db_48khz.m4 b/tools/topology/m4/eq_iir_coef_highpass_20hz_0db_48khz.m4 new file mode 100644 index 000000000000..669c9f3483b8 --- /dev/null +++ b/tools/topology/m4/eq_iir_coef_highpass_20hz_0db_48khz.m4 @@ -0,0 +1,18 @@ +# 20 Hz second order high-pass, gain 0 dB, created with example_iir_eq.m 27-Nov-2019 +CONTROLBYTES_PRIV(EQIIR_HP20HZ0dB48K_priv, +` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x4a,0x8c,0x3c,0xc0,' +` 0x04,0x57,0xc3,0x7f,0xc0,0x43,0x04,0x20,' +` 0x80,0x78,0xf7,0xbf,0xc0,0x43,0x04,0x20,' +` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"' +) diff --git a/tools/topology/m4/eq_iir_coef_highpass_20hz_20db_16khz.m4 b/tools/topology/m4/eq_iir_coef_highpass_20hz_20db_16khz.m4 new file mode 100644 index 000000000000..be0e3d70736e --- /dev/null +++ b/tools/topology/m4/eq_iir_coef_highpass_20hz_20db_16khz.m4 @@ -0,0 +1,18 @@ +# 20 Hz second order high-pass, gain 20 dB, created with example_iir_eq.m 27-Nov-2019 +CONTROLBYTES_PRIV(EQIIR_HP20HZ20dB16K_priv, +` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x3c,0xf9,0xb4,0xc0,' +` 0x79,0x05,0x4a,0x7f,0x92,0xf9,0xe5,0x1f,' +` 0xdc,0x0c,0x34,0xc0,0x92,0xf9,0xe5,0x1f,' +` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"' +) diff --git a/tools/topology/m4/eq_iir_coef_highpass_20hz_20db_48khz.m4 b/tools/topology/m4/eq_iir_coef_highpass_20hz_20db_48khz.m4 new file mode 100644 index 000000000000..a93f8cb8e15b --- /dev/null +++ b/tools/topology/m4/eq_iir_coef_highpass_20hz_20db_48khz.m4 @@ -0,0 +1,18 @@ +# 20 Hz second order high-pass, gain 20 dB, created with example_iir_eq.m 27-Nov-2019 +CONTROLBYTES_PRIV(EQIIR_HP20HZ20dB48K_priv, +` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x4a,0x8c,0x3c,0xc0,' +` 0x04,0x57,0xc3,0x7f,0xc0,0x43,0x04,0x20,' +` 0x80,0x78,0xf7,0xbf,0xc0,0x43,0x04,0x20,' +` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"' +) diff --git a/tools/topology/m4/eq_iir_coef_highpass_30hz_0db_16khz.m4 b/tools/topology/m4/eq_iir_coef_highpass_30hz_0db_16khz.m4 new file mode 100644 index 000000000000..03be97e82651 --- /dev/null +++ b/tools/topology/m4/eq_iir_coef_highpass_30hz_0db_16khz.m4 @@ -0,0 +1,18 @@ +# 30 Hz second order high-pass, gain 0 dB, created with example_iir_eq.m 27-Nov-2019 +CONTROLBYTES_PRIV(EQIIR_HP30HZ0dB16K_priv, +` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x98,0xb5,0x0e,0xc1,' +` 0x17,0x09,0xef,0x7e,0xbf,0x54,0xcf,0x1f,' +` 0x82,0x56,0x61,0xc0,0xbf,0x54,0xcf,0x1f,' +` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"' +) diff --git a/tools/topology/m4/eq_iir_coef_highpass_30hz_0db_48khz.m4 b/tools/topology/m4/eq_iir_coef_highpass_30hz_0db_48khz.m4 new file mode 100644 index 000000000000..a77770b613ac --- /dev/null +++ b/tools/topology/m4/eq_iir_coef_highpass_30hz_0db_48khz.m4 @@ -0,0 +1,18 @@ +# 30 Hz second order high-pass, gain 0 dB, created with example_iir_eq.m 27-Nov-2019 +CONTROLBYTES_PRIV(EQIIR_HP30HZ0dB48K_priv, +` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0xf1,0xbc,0x5a,0xc0,' +` 0x8f,0x02,0xa5,0x7f,0x83,0xae,0xfc,0x1f,' +` 0xfa,0xa2,0x06,0xc0,0x83,0xae,0xfc,0x1f,' +` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"' +) diff --git a/tools/topology/m4/eq_iir_coef_highpass_30hz_20db_16khz.m4 b/tools/topology/m4/eq_iir_coef_highpass_30hz_20db_16khz.m4 new file mode 100644 index 000000000000..7f2445d82416 --- /dev/null +++ b/tools/topology/m4/eq_iir_coef_highpass_30hz_20db_16khz.m4 @@ -0,0 +1,18 @@ +# 30 Hz second order high-pass, gain 20 dB, created with example_iir_eq.m 27-Nov-2019 +CONTROLBYTES_PRIV(EQIIR_HP30HZ20dB16K_priv, +` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x98,0xb5,0x0e,0xc1,' +` 0x17,0x09,0xef,0x7e,0xbf,0x54,0xcf,0x1f,' +` 0x82,0x56,0x61,0xc0,0xbf,0x54,0xcf,0x1f,' +` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"' +) diff --git a/tools/topology/m4/eq_iir_coef_highpass_30hz_20db_48khz.m4 b/tools/topology/m4/eq_iir_coef_highpass_30hz_20db_48khz.m4 new file mode 100644 index 000000000000..88a62e7e12e3 --- /dev/null +++ b/tools/topology/m4/eq_iir_coef_highpass_30hz_20db_48khz.m4 @@ -0,0 +1,18 @@ +# 30 Hz second order high-pass, gain 20 dB, created with example_iir_eq.m 27-Nov-2019 +CONTROLBYTES_PRIV(EQIIR_HP30HZ20dB48K_priv, +` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0xf1,0xbc,0x5a,0xc0,' +` 0x8f,0x02,0xa5,0x7f,0x83,0xae,0xfc,0x1f,' +` 0xfa,0xa2,0x06,0xc0,0x83,0xae,0xfc,0x1f,' +` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"' +) diff --git a/tools/topology/m4/eq_iir_coef_highpass_40hz_0db_16khz.m4 b/tools/topology/m4/eq_iir_coef_highpass_40hz_0db_16khz.m4 new file mode 100644 index 000000000000..5242fc4bc9e9 --- /dev/null +++ b/tools/topology/m4/eq_iir_coef_highpass_40hz_0db_16khz.m4 @@ -0,0 +1,18 @@ +# 40 Hz second order high-pass, gain 0 dB, created with example_iir_eq.m 27-Nov-2019 +CONTROLBYTES_PRIV(EQIIR_HP40HZ0dB16K_priv, +` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0xba,0xf2,0x67,0xc1,' +` 0xc3,0x0d,0x94,0x7e,0xff,0xbf,0xb8,0x1f,' +` 0x03,0x80,0x8e,0xc0,0xff,0xbf,0xb8,0x1f,' +` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"' +) diff --git a/tools/topology/m4/eq_iir_coef_highpass_40hz_0db_48khz.m4 b/tools/topology/m4/eq_iir_coef_highpass_40hz_0db_48khz.m4 new file mode 100644 index 000000000000..ea28ad008dcd --- /dev/null +++ b/tools/topology/m4/eq_iir_coef_highpass_40hz_0db_48khz.m4 @@ -0,0 +1,18 @@ +# 40 Hz second order high-pass, gain 0 dB, created with example_iir_eq.m 27-Nov-2019 +CONTROLBYTES_PRIV(EQIIR_HP40HZ0dB48K_priv, +` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x4c,0xdf,0x78,0xc0,' +` 0x23,0xae,0x86,0x7f,0x12,0x1b,0xf5,0x1f,' +` 0xdb,0xc9,0x15,0xc0,0x12,0x1b,0xf5,0x1f,' +` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"' +) diff --git a/tools/topology/m4/eq_iir_coef_highpass_40hz_20db_16khz.m4 b/tools/topology/m4/eq_iir_coef_highpass_40hz_20db_16khz.m4 new file mode 100644 index 000000000000..d2eb866d312e --- /dev/null +++ b/tools/topology/m4/eq_iir_coef_highpass_40hz_20db_16khz.m4 @@ -0,0 +1,18 @@ +# 40 Hz second order high-pass, gain 20 dB, created with example_iir_eq.m 27-Nov-2019 +CONTROLBYTES_PRIV(EQIIR_HP40HZ20dB16K_priv, +` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0xba,0xf2,0x67,0xc1,' +` 0xc3,0x0d,0x94,0x7e,0xff,0xbf,0xb8,0x1f,' +` 0x03,0x80,0x8e,0xc0,0xff,0xbf,0xb8,0x1f,' +` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"' +) diff --git a/tools/topology/m4/eq_iir_coef_highpass_40hz_20db_48khz.m4 b/tools/topology/m4/eq_iir_coef_highpass_40hz_20db_48khz.m4 new file mode 100644 index 000000000000..d1e8d5f635c4 --- /dev/null +++ b/tools/topology/m4/eq_iir_coef_highpass_40hz_20db_48khz.m4 @@ -0,0 +1,18 @@ +# 40 Hz second order high-pass, gain 20 dB, created with example_iir_eq.m 27-Nov-2019 +CONTROLBYTES_PRIV(EQIIR_HP40HZ20dB48K_priv, +` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x4c,0xdf,0x78,0xc0,' +` 0x23,0xae,0x86,0x7f,0x12,0x1b,0xf5,0x1f,' +` 0xdb,0xc9,0x15,0xc0,0x12,0x1b,0xf5,0x1f,' +` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"' +) diff --git a/tools/topology/m4/eq_iir_coef_highpass_50hz_0db_16khz.m4 b/tools/topology/m4/eq_iir_coef_highpass_50hz_0db_16khz.m4 new file mode 100644 index 000000000000..4d59264b2337 --- /dev/null +++ b/tools/topology/m4/eq_iir_coef_highpass_50hz_0db_16khz.m4 @@ -0,0 +1,18 @@ +# 50 Hz second order high-pass, gain 0 dB, created with example_iir_eq.m 27-Nov-2019 +CONTROLBYTES_PRIV(EQIIR_HP50HZ0dB16K_priv, +` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x57,0xb1,0xc0,0xc1,' +` 0xd2,0x13,0x39,0x7e,0x45,0x3b,0xa2,0x1f,' +` 0x76,0x89,0xbb,0xc0,0x45,0x3b,0xa2,0x1f,' +` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"' +) diff --git a/tools/topology/m4/eq_iir_coef_highpass_50hz_0db_48khz.m4 b/tools/topology/m4/eq_iir_coef_highpass_50hz_0db_48khz.m4 new file mode 100644 index 000000000000..11457d497d62 --- /dev/null +++ b/tools/topology/m4/eq_iir_coef_highpass_50hz_0db_48khz.m4 @@ -0,0 +1,18 @@ +# 50 Hz second order high-pass, gain 0 dB, created with example_iir_eq.m 27-Nov-2019 +CONTROLBYTES_PRIV(EQIIR_HP50HZ0dB48K_priv, +` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x63,0xf3,0x96,0xc0,' +` 0xc6,0x59,0x68,0x7f,0x6d,0x89,0xed,0x1f,' +` 0x27,0xed,0x24,0xc0,0x6d,0x89,0xed,0x1f,' +` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"' +) diff --git a/tools/topology/sof-hda-generic.m4 b/tools/topology/sof-hda-generic.m4 index 7feef6370b40..9544b20348a9 100644 --- a/tools/topology/sof-hda-generic.m4 +++ b/tools/topology/sof-hda-generic.m4 @@ -30,11 +30,13 @@ include(`platform/intel/intel-generic-dmic.m4') # # Define the pipelines # -# PCM0 <---> volume (pipe 1,2) <----> HDA Analog (HDA Analog playback/capture) -# PCM1 <---> volume (pipe 3,4) <----> HDA Digital (HDA Digital playback/capture) -# PCM3 ----> volume (pipe 7) -----> iDisp1 (HDMI/DP playback, BE link 3) -# PCM4 ----> Volume (pipe 8) -----> iDisp2 (HDMI/DP playback, BE link 4) -# PCM5 ----> volume (pipe 9) -----> iDisp3 (HDMI/DP playback, BE link 5) +# PCM0P --> volume (pipe 1) --> HDA Analog (HDA Analog playback) +# PCM0C <-- volume, EQ (pipe 2) <-- HDA Analog (HDA Analog capture) +# PCM1P --> volume (pipe 3) --> HDA Digital (HDA Digital playback) +# PCM1C <-- volume, EQ (pipe 4) <-- HDA Digital (HDA Digital capture) +# PCM3 ----> volume (pipe 7) ----> iDisp1 (HDMI/DP playback, BE link 3) +# PCM4 ----> Volume (pipe 8) ----> iDisp2 (HDMI/DP playback, BE link 4) +# PCM5 ----> volume (pipe 9) ----> iDisp3 (HDMI/DP playback, BE link 5) # # Low Latency playback pipeline 1 on PCM 0 using max 2 channels of s24le. @@ -46,7 +48,7 @@ PIPELINE_PCM_ADD(sof/pipe-volume-playback.m4, # Low Latency capture pipeline 2 on PCM 0 using max 2 channels of s24le. # 1000us deadline on core 0 with priority 0 -PIPELINE_PCM_ADD(sof/pipe-volume-capture.m4, +PIPELINE_PCM_ADD(sof/pipe-highpass-capture.m4, 2, 0, 2, s24le, 1000, 0, 0, 48000, 48000, 48000) @@ -60,7 +62,7 @@ PIPELINE_PCM_ADD(sof/pipe-volume-playback.m4, # Low Latency capture pipeline 4 on PCM 1 using max 2 channels of s24le. # 1000us deadline on core 0 with priority 0 -PIPELINE_PCM_ADD(sof/pipe-volume-capture.m4, +PIPELINE_PCM_ADD(sof/pipe-highpass-capture.m4, 4, 1, 2, s24le, 1000, 0, 0, 48000, 48000, 48000) diff --git a/tools/topology/sof/pipe-highpass-capture.m4 b/tools/topology/sof/pipe-highpass-capture.m4 new file mode 100644 index 000000000000..ecc2a8495911 --- /dev/null +++ b/tools/topology/sof/pipe-highpass-capture.m4 @@ -0,0 +1,123 @@ +# IIR highpass filter, volume control, and PCM +# +# Pipeline Endpoints for connection are :- +# +# host PCM_C <-- B0 <-- Volume <-- B1 <-- EQ_IIR <-- B2 <-- source DAI0 + +# Include topology builder +include(`utils.m4') +include(`buffer.m4') +include(`pcm.m4') +include(`pga.m4') +include(`dai.m4') +include(`mixercontrol.m4') +include(`pipeline.m4') +include(`bytecontrol.m4') +include(`eq_iir.m4') + +# +# Controls +# +# Volume Mixer control with max value of 32 +C_CONTROLMIXER(Master Capture Volume, PIPELINE_ID, + CONTROLMIXER_OPS(volsw, + 256 binds the mixer control to volume get/put handlers, + 256, 256), + CONTROLMIXER_MAX(, 80), + false, + CONTROLMIXER_TLV(TLV 80 steps from -50dB to +30dB for 1dB, vtlv_m50s1), + Channel register and shift for Front Left/Right, + LIST(` ', KCONTROL_CHANNEL(FL, 1, 0), KCONTROL_CHANNEL(FR, 1, 1))) + +# +# Volume Configuration +# + +define(MY_PGA_TOKENS, concat(`pga_tokens_', PIPELINE_ID)) +define(MY_PGA_CONF, concat(`pga_conf_', PIPELINE_ID)) +W_VENDORTUPLES(MY_PGA_TOKENS, sof_volume_tokens, +LIST(` ', `SOF_TKN_VOLUME_RAMP_STEP_TYPE "0"' + ` ', `SOF_TKN_VOLUME_RAMP_STEP_MS "400"')) + +W_DATA(MY_PGA_CONF, MY_PGA_TOKENS) + +# +# IIR configuration +# + +include(`eq_iir_coef_highpass_40hz_0db_48khz.m4') + +# EQ Bytes control with max value of 255 +define(MY_EQIIR_BYTES, concat(`eqiir_bytes_', PIPELINE_ID)) +C_CONTROLBYTES(MY_EQIIR_BYTES, PIPELINE_ID, + CONTROLBYTES_OPS(bytes, + 258 binds the mixer control to bytes get/put handlers, + 258, 258), + CONTROLBYTES_EXTOPS( + 258 binds the mixer control to bytes get/put handlers, + 258, 258), + , , , + CONTROLBYTES_MAX(, 304), + , + EQIIR_HP40HZ0dB48K_priv) + +# +# Components and Buffers +# + +# Host "Highpass Capture" PCM +# with 0 sink and 2 source periods +W_PCM_CAPTURE(PCM_ID, Highpass Capture, 0, 2) + +# "Volume" has 2 sink and 2 source periods +W_PGA(0, PIPELINE_FORMAT, 2, DAI_PERIODS, MY_PGA_CONF, + LIST(` ', "PIPELINE_ID Master Capture Volume")) + +# "EQ_IIR 0" has 2 sink and x source periods +W_EQ_IIR(0, PIPELINE_FORMAT, 2, DAI_PERIODS, + LIST(` ', "MY_EQIIR_BYTES")) + +# Capture Buffers +W_BUFFER(0, COMP_BUFFER_SIZE(2, + COMP_SAMPLE_SIZE(PIPELINE_FORMAT), PIPELINE_CHANNELS, + COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)), + PLATFORM_HOST_MEM_CAP) +W_BUFFER(1, COMP_BUFFER_SIZE(2, + COMP_SAMPLE_SIZE(PIPELINE_FORMAT), PIPELINE_CHANNELS, + COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)), + PLATFORM_HOST_MEM_CAP) +W_BUFFER(2, COMP_BUFFER_SIZE(DAI_PERIODS, + COMP_SAMPLE_SIZE(DAI_FORMAT), PIPELINE_CHANNELS, + COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)), + PLATFORM_DAI_MEM_CAP) + +# +# Pipeline Graph +# +# host PCM_P <-- B0 <-- Volume 0 <-- B1 <-- EQ_IIR 0 <-- B2 <-- sink DAI0 + +P_GRAPH(pipe-pass-vol-capture-PIPELINE_ID, PIPELINE_ID, + LIST(` ', + `dapm(N_PCMC(PCM_ID), N_BUFFER(0))', + `dapm(N_BUFFER(0), N_PGA(0))', + `dapm(N_PGA(0), N_BUFFER(1))', + `dapm(N_BUFFER(1), N_EQ_IIR(0))', + `dapm(N_EQ_IIR(0), N_BUFFER(2))')) + +# +# Pipeline Source and Sinks +# +indir(`define', concat(`PIPELINE_SINK_', PIPELINE_ID), N_BUFFER(2)) +indir(`define', concat(`PIPELINE_PCM_', PIPELINE_ID), Highpass Capture PCM_ID) + +# +# PCM Configuration +# + +PCM_CAPABILITIES(Highpass Capture PCM_ID, `S32_LE,S24_LE,S16_LE', + PCM_MIN_RATE, PCM_MAX_RATE, PIPELINE_CHANNELS, PIPELINE_CHANNELS, + 2, 16, 192, 16384, 65536, 65536) + +undefine(`MY_PGA_TOKENS') +undefine(`MY_PGA_CONF') +undefine(`MY_EQIIR_BYTES') diff --git a/tools/tune/eq/example_iir_eq.m b/tools/tune/eq/example_iir_eq.m index 496bc280fa2b..dd5fa9142908 100644 --- a/tools/tune/eq/example_iir_eq.m +++ b/tools/tune/eq/example_iir_eq.m @@ -116,37 +116,11 @@ function example_iir_eq() bp = eq_iir_blob_pack(bm); eq_alsactl_write(alsa_fn, bp); -%% ------------------------------------ -%% Example 4: 50 Hz high-pass with gain -%% ------------------------------------ -tplg_fn = '../../topology/m4/eq_iir_coef_highpass_50hz_20db_48khz.m4'; -comment = '50 Hz second order high-pass and +20 dB gain'; - -%% Design IIR high-pass -eq_hp = hp_iir_eq(fs); - -%% Quantize and pack filter coefficients plus shifts etc. -bq_hp = eq_iir_blob_quant(eq_hp.p_z, eq_hp.p_p, eq_hp.p_k); - -%% Build blob -channels_in_config = 2; % Setup max 2 channels EQ -assign_response = [0 0]; % Switch to response #0 -num_responses = 1; % One responses: hp -bm = eq_iir_blob_merge(channels_in_config, ... - num_responses, ... - assign_response, ... - bq_hp); - -%% Pack and write file. If there are several EQ instances need -%% to have identifier string to get correct response to each -%% instance (here _HP50HZ20dB48K) and refer to in topology. -bp = eq_iir_blob_pack(bm); -eq_tplg_write(tplg_fn, bp, 'IIR_HP50HZ20dB48K', comment); %% ------------------- -%% Example 5: Flat IIR +%% Example 4: Flat IIR %% ------------------- -comment = 'Flat IIR EQ'; +comment = 'Flat IIR EQ, created with example_iir_eq.m'; alsa_fn = '../../ctl/eq_iir_flat.txt'; tplg_fn = '../../topology/m4/eq_iir_coef_flat.m4'; @@ -171,7 +145,7 @@ function example_iir_eq() eq_tplg_write(tplg_fn, bp, 'IIR', comment); %% --------------------------- -%% Example 6: Pass-through IIR +%% Example 5: Pass-through IIR %% ------------------------------------ alsa_fn = '../../ctl/eq_iir_pass.txt'; @@ -194,32 +168,54 @@ function example_iir_eq() bp = eq_iir_blob_pack(bm); eq_alsactl_write(alsa_fn, bp); -%% -------------------------------------------- -%% Example 7: 50 Hz high-pass with gain, 16 kHz -%% -------------------------------------------- -tplg_fn = '../../topology/m4/eq_iir_coef_highpass_50hz_20db_16khz.m4'; -comment = '50 Hz second order high-pass and +20 dB gain'; - -%% Design IIR high-pass -eq_hp = hp_iir_eq(16e3); - -%% Quantize and pack filter coefficients plus shifts etc. -bq_hp = eq_iir_blob_quant(eq_hp.p_z, eq_hp.p_p, eq_hp.p_k); - -%% Build blob -channels_in_config = 2; % Setup max 2 channels EQ -assign_response = [0 0]; % Switch to response #0 -num_responses = 1; % One response: hp -bm = eq_iir_blob_merge(channels_in_config, ... - num_responses, ... - assign_response, ... - bq_hp); +%% ------------------------------------ +%% Example 6: 20/30/40/50 Hz high-pass +%% ------------------------------------ -%% Pack and write file. If there are several EQ instances need -%% to have identifier string to get correct response to each -%% instance (here _HP50HZ20dB16K) and refer to in topology. -bp = eq_iir_blob_pack(bm); -eq_tplg_write(tplg_fn, bp, 'IIR_HP50HZ20dB16K', comment); +tpath = '../../topology/m4'; +cpath = '../../ctl'; +fs_list = [16e3 48e3]; +fc_list = [20 30 40 50]; +g_list = [0 20]; +for i = 1:length(fs_list) + for j = 1:length(fc_list); + for k = 1:length(g_list); + fs = fs_list(i); + fc = fc_list(j); + g = g_list(k); + fsk = round(fs/1e3); + tplg_fn = sprintf('%s/eq_iir_coef_highpass_%dhz_%ddb_%dkhz.m4', ... + tpath, fc, g, fsk); + alsa_fn = sprintf('%s/eq_iir_highpass_%dhz_%ddb_%dkhz.txt', ... + cpath, fc, g, fsk); + comment = sprintf('%d Hz second order high-pass, gain %d dB, created with example_iir_eq.m', ... + fc, g); + tpid = sprintf('IIR_HP%dHZ%ddB%dK', fc, g, fsk); + + %% Design IIR high-pass + eq_hp = hp_iir_eq(fs, fc, 0); + + %% Quantize and pack filter coefficients plus shifts etc. + bq_hp = eq_iir_blob_quant(eq_hp.p_z, eq_hp.p_p, eq_hp.p_k); + + %% Build blob + channels_in_config = 2; % Setup max 2 channels EQ + assign_response = [0 0]; % Switch to response #0 + num_responses = 1; % One responses: hp + bm = eq_iir_blob_merge(channels_in_config, ... + num_responses, ... + assign_response, ... + bq_hp); + + %% Pack and write file. If there are several EQ instances need + %% to have identifier string to get correct response to each + %% instance (here _HP50HZ20dB48K) and refer to in topology. + bp = eq_iir_blob_pack(bm); + eq_tplg_write(tplg_fn, bp, tpid, comment); + eq_alsactl_write(alsa_fn, bp); + end + end +end end @@ -287,17 +283,17 @@ function example_iir_eq() end -function eq = hp_iir_eq(fs) +function eq = hp_iir_eq(fs, fc, gain_db) % Get defaults for equalizer design eq = eq_defaults(); eq.fs = fs; eq.enable_iir = 1; eq.norm_type = '1k'; -eq.norm_offs_db = 20; +eq.norm_offs_db = gain_db; % Design -eq.peq = [ eq.PEQ_HP2 50 NaN NaN ]; +eq.peq = [ eq.PEQ_HP2 fc NaN NaN ]; eq = eq_compute(eq); eq_plot(eq); From 7ed9577bfbe708820656b712eac4bc57ba536f1b Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Thu, 5 Dec 2019 11:54:07 +0200 Subject: [PATCH 2/5] Volume: Fix for gain ramp step macro value This patch fixes the calculation of macro VOL_RAMP_STEP_CONST that is used in linear step size calculation. The previous version calculated incorrectly the value as inverse. However with used ramp update rate of 1000 us there were no issues seen because the value and inverse were both 1.0. The bug would have triggered with with a faster or slower ramp update rate. Signed-off-by: Seppo Ingalsuo --- src/include/sof/audio/volume.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/include/sof/audio/volume.h b/src/include/sof/audio/volume.h index d57948e3dcff..6597a9e82851 100644 --- a/src/include/sof/audio/volume.h +++ b/src/include/sof/audio/volume.h @@ -75,12 +75,13 @@ struct sof_ipc_ctrl_value_chan; /** * \brief Macro for volume linear gain ramp step computation * Volume gain ramp step as Q1.16 is computed with equation - * step = VOL_RAMP_STEP_CONST/ SOF_TKN_VOLUME_RAMP_STEP_MS. This + * step = VOL_RAMP_STEP_CONST / SOF_TKN_VOLUME_RAMP_STEP_MS. This * macro defines as Q1.16 value the constant term - * (1000 / VOL_RAMP_UPDATE) for step calculation. + * VOL_RAMP_UPDATE / 1000.0 for step calculation. The value 1000 + * is used to to convert microseconds to milliseconds. */ #define VOL_RAMP_STEP_CONST \ - Q_CONVERT_FLOAT(1000.0 / VOL_RAMP_UPDATE_US, VOL_QXY_Y) + Q_CONVERT_FLOAT(VOL_RAMP_UPDATE_US / 1000.0, VOL_QXY_Y) /** * \brief Volume maximum value. From 739d1670b730a47e640a766b84450af393176d14 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Thu, 5 Dec 2019 13:24:14 +0200 Subject: [PATCH 3/5] Volume: Always launch ramp in beginning of copy() operations This patch ensures that a ramp will happen in beginning of streaming even if the driver control sequence would not launch it by successive volume requests for mute and actual volume. Also a check is added into volume ramp update to prevent a volume command in component idle state to schedule repeatedly ramp updates. In that case the volume can be set immediately to ramp end value. Also the ramp is updated for active channels only. There's no need to update for the maximum channels count. Signed-off-by: Seppo Ingalsuo --- src/audio/volume/volume.c | 49 +++++++++++++++++++++++++++++----- src/include/sof/audio/volume.h | 2 ++ 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/audio/volume/volume.c b/src/audio/volume/volume.c index 080c4a3a9ae1..536a2e72b0ec 100644 --- a/src/audio/volume/volume.c +++ b/src/audio/volume/volume.c @@ -82,10 +82,24 @@ static enum task_state vol_work(void *data) int again = 0; int i; + /* No need to ramp in idle state, jump volume to request. */ + if (dev->state == COMP_STATE_READY) { + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) + cd->volume[i] = cd->tvolume[i]; + + vol_sync_host(cd, PLATFORM_MAX_CHANNELS); + return SOF_TASK_STATE_COMPLETED; + } + + /* The first is set and cleared to indicate ongoing ramp, the + * latter is set once to enable self launched ramp only once + * in stream start. + */ cd->vol_ramp_active = true; + cd->ramp_started = true; - /* inc/dec each volume if it's not at target */ - for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) { + /* inc/dec each volume if it's not at target for active channels */ + for (i = 0; i < cd->channels; i++) { /* skip if target reached */ if (cd->volume[i] == cd->tvolume[i]) continue; @@ -228,6 +242,8 @@ static struct comp_dev *volume_new(struct sof_ipc_comp *comp) } cd->vol_ramp_active = false; + cd->channels = 0; /* To be set in prepare() */ + trace_volume_with_ids(dev, "vol->initial_ramp = %d, vol->ramp = %d, " "vol->min_value = %d, vol->max_value = %d", @@ -275,8 +291,12 @@ static int volume_params(struct comp_dev *dev, * \param[in,out] dev Volume base component device. * \param[in] chan Channel number. * \param[in] vol Target volume. + * \param[in] constant_rate_ramp When true do a constant rate + * and variable time length ramp. When false do + * a fixed length and variable rate ramp. */ -static inline int volume_set_chan(struct comp_dev *dev, int chan, int32_t vol) +static inline int volume_set_chan(struct comp_dev *dev, int chan, + int32_t vol, bool constant_rate_ramp) { struct comp_data *cd = comp_get_drvdata(dev); struct sof_ipc_comp_volume *pga = @@ -375,7 +395,7 @@ static inline void volume_set_chan_mute(struct comp_dev *dev, int chan) if (!cd->muted[chan]) { cd->mvolume[chan] = cd->tvolume[chan]; - volume_set_chan(dev, chan, 0); + volume_set_chan(dev, chan, 0, true); cd->muted[chan] = true; } } @@ -391,7 +411,7 @@ static inline void volume_set_chan_unmute(struct comp_dev *dev, int chan) if (cd->muted[chan]) { cd->muted[chan] = false; - volume_set_chan(dev, chan, cd->mvolume[chan]); + volume_set_chan(dev, chan, cd->mvolume[chan], true); } } @@ -438,7 +458,7 @@ static int volume_ctrl_set_cmd(struct comp_dev *dev, if (cd->muted[ch]) { cd->mvolume[ch] = val; } else { - ret = volume_set_chan(dev, ch, val); + ret = volume_set_chan(dev, ch, val, true); if (ret) return ret; } @@ -579,6 +599,10 @@ static int volume_copy(struct comp_dev *dev) tracev_volume_with_ids(dev, "volume_copy()"); + if (!cd->ramp_started) + schedule_task(&cd->volwork, VOL_RAMP_UPDATE_US, + VOL_RAMP_UPDATE_US); + /* Get source, sink, number of frames etc. to process. */ ret = comp_get_copy_limits(dev, &c); if (ret < 0) { @@ -655,6 +679,19 @@ static int volume_prepare(struct comp_dev *dev) for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) vol_sync_host(cd, i); + /* Set current volume to min to ensure ramp starts from minimum + * to previous volume request. Copy() checks for ramp started + * and schedules it if it has not yet started as result of + * driver commands. Ramp is not constant rate to ensure it lasts + * for entire topology specified time. + */ + cd->ramp_started = false; + cd->channels = sinkb->channels; + for (i = 0; i < cd->channels; i++) { + cd->volume[i] = cd->vol_min; + volume_set_chan(dev, i, cd->tvolume[i], false); + } + return 0; err: diff --git a/src/include/sof/audio/volume.h b/src/include/sof/audio/volume.h index 6597a9e82851..b71c09da4f45 100644 --- a/src/include/sof/audio/volume.h +++ b/src/include/sof/audio/volume.h @@ -111,8 +111,10 @@ struct comp_data { int32_t vol_min; /**< minimum volume */ int32_t vol_max; /**< maximum volume */ int32_t vol_ramp_range; /**< max ramp transition */ + unsigned int channels; /**< current channels count */ bool muted[SOF_IPC_MAX_CHANNELS]; /**< set if channel is muted */ bool vol_ramp_active; /**< set if volume is ramped */ + bool ramp_started; /**< control ramp launch */ /**< volume processing function */ void (*scale_vol)(struct comp_dev *dev, struct comp_buffer *sink, struct comp_buffer *source, uint32_t frames); From ab06a84fd8d3312d0ea2bb211edd00c4c8c8b61d Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Thu, 5 Dec 2019 14:23:28 +0200 Subject: [PATCH 4/5] Volume: Simplify ramp calculation and provide constant rate on/off The earlier version of ramp step calculation was more complex than necessary and there was no way to ensure the max. topology defined ramp time happens in beginning of streaming so the step equation was rewritten. The step value is rounded for better match with topology defined ramp length. Normally the ramp is constant rate type where the topology define time happens for vol min-max transition. A smaller transition is completed in less time. The constant rate mode is not used for the first copy() triggered ramp to ensure the ramp from vol min to used volume takes the entire topology defined time. This mode is also as earlier used for legacy kernels where the vol min/max information is not passed. Signed-off-by: Seppo Ingalsuo --- src/audio/volume/volume.c | 60 +++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/src/audio/volume/volume.c b/src/audio/volume/volume.c index 536a2e72b0ec..6023e9100086 100644 --- a/src/audio/volume/volume.c +++ b/src/audio/volume/volume.c @@ -39,6 +39,9 @@ #include #include +/* Shift to use in volume fractional multiplications */ +#define Q_MUL_SHIFT Q_SHIFT_BITS_32(VOL_QXY_Y, VOL_QXY_Y, VOL_QXY_Y) + /** * \brief Synchronize host mmap() volume with real value. * \param[in,out] cd Volume component private data. @@ -305,7 +308,6 @@ static inline int volume_set_chan(struct comp_dev *dev, int chan, int32_t delta; int32_t delta_abs; int32_t inc; - int32_t t_us; /* Limit received volume gain to MIN..MAX range before applying it. * MAX is needed for now for the generic C gain arithmetics to prevent @@ -331,39 +333,41 @@ static inline int volume_set_chan(struct comp_dev *dev, int chan, /* Check ramp type */ switch (pga->ramp) { case SOF_VOLUME_LINEAR: - - /* Assume the ramp length (initial_ramp [ms]) describes - * time of mute to 0 dB ramp. The actual volume scale min/max - * is not known. If initial_ramp is zero (or negative) use - * step size that reaches 0 dB gain in one step (VOL_ZERO_DB). - */ - if (pga->initial_ramp > 0) - inc = MAX(VOL_RAMP_STEP_CONST / pga->initial_ramp, 1); - else - inc = VOL_ZERO_DB; - - /* Scale the increment with actual volume range if it was - * passed via IPC. - */ - if (cd->vol_ramp_range) - inc = q_multsr_32x32(inc, cd->vol_ramp_range, - Q_SHIFT_BITS_32(VOL_QXY_Y, - VOL_QXY_Y, - VOL_QXY_Y)); - + /* Get volume transition delta and absolute value */ delta = cd->tvolume[chan] - cd->volume[chan]; delta_abs = ABS(delta); - /* If the ramp completion would take longer than initial_ramp - * increase the step size to make this ramp complete at - * initial_ramp time. Calculate estimated ramp length [us]. + /* The ramp length (initial_ramp [ms]) describes time of mute + * to vol_max unmuting. Normally the volume ramp has a + * constant linear slope defined this way and variable + * completion time. However in streaming start it is feasible + * to apply the entire topology defined ramp time to unmute to + * any used volume. In this case the ramp rate is not constant. + * Note also the legacy mode without known vol_ramp_range where + * the volume transition always uses the topology defined time. */ - t_us = delta_abs * VOL_RAMP_UPDATE_US / inc; - if (t_us > pga->initial_ramp * 1000) { - inc = (int32_t)((int64_t)inc * delta_abs / VOL_ZERO_DB); - inc = MAX(inc, 1); + if (pga->initial_ramp > 0) { + if (constant_rate_ramp && cd->vol_ramp_range > 0) + inc = q_multsr_32x32(cd->vol_ramp_range, + VOL_RAMP_STEP_CONST, + Q_MUL_SHIFT); + else + inc = q_multsr_32x32(delta_abs, + VOL_RAMP_STEP_CONST, + Q_MUL_SHIFT); + + /* Divide and round to nearest. Note that there will + * be some accumulated error in ramp time the longer + * the ramp and the smaller the transition is. + */ + inc = (2 * inc / pga->initial_ramp + 1) >> 1; + } else { + inc = delta_abs; } + /* Ensure inc is at least one */ + inc = MAX(inc, 1); + /* Invert sign for volume down ramp step */ if (delta < 0) inc = -inc; From 9c184c2a98e08ec483a90aa6575371e953c1a8e3 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Thu, 5 Dec 2019 14:37:52 +0200 Subject: [PATCH 5/5] Volume: Simplify volume synchronization to host This patch removes a trivial function vol_update() and alters vol_sync_host() to update all channels the same time into the host data structure instead of per channel updates. There is no other change to component operation. Signed-off-by: Seppo Ingalsuo --- src/audio/volume/volume.c | 45 +++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/src/audio/volume/volume.c b/src/audio/volume/volume.c index 6023e9100086..2f78d8781ff1 100644 --- a/src/audio/volume/volume.c +++ b/src/audio/volume/volume.c @@ -45,32 +45,27 @@ /** * \brief Synchronize host mmap() volume with real value. * \param[in,out] cd Volume component private data. - * \param[in] chan Channel number. + * \param[in] num_channels Update channels 0 to num_channels -1. */ -static void vol_sync_host(struct comp_data *cd, uint32_t chan) +static void vol_sync_host(struct comp_data *cd, unsigned int num_channels) { - if (!cd->hvol) + int n; + + if (!cd->hvol) { + tracev_volume("vol_sync_host() Warning: null hvol, no update"); return; + } - if (chan < SOF_IPC_MAX_CHANNELS) { - cd->hvol[chan].value = cd->volume[chan]; + if (num_channels < SOF_IPC_MAX_CHANNELS) { + for (n = 0; n < num_channels; n++) + cd->hvol[n].value = cd->volume[n]; } else { - trace_volume_error("vol_sync_host() error: " - "chan = %u < SOF_IPC_MAX_CHANNELS", chan); + trace_volume_error("vol_sync_host() error: channels count %d" + " exceeds SOF_IPC_MAX_CHANNELS", + num_channels); } } -/** - * \brief Update volume with target value. - * \param[in,out] cd Volume component private data. - * \param[in] chan Channel number. - */ -static void vol_update(struct comp_data *cd, uint32_t chan) -{ - cd->volume[chan] = cd->tvolume[chan]; - vol_sync_host(cd, chan); -} - /** * \brief Ramps volume changes over time. * \param[in,out] data Volume base component device. @@ -115,8 +110,8 @@ static enum task_state vol_work(void *data) if (cd->volume[i] < cd->tvolume[i]) { /* ramp up, check if ramp completed */ if (vol >= cd->tvolume[i] || vol >= cd->vol_max) { - vol_update(cd, i); cd->ramp_increment[i] = 0; + cd->volume[i] = cd->tvolume[i]; } else { cd->volume[i] = vol; again = 1; @@ -125,14 +120,14 @@ static enum task_state vol_work(void *data) /* ramp down */ if (vol <= 0) { /* cannot ramp down below 0 */ - vol_update(cd, i); cd->ramp_increment[i] = 0; + cd->volume[i] = cd->tvolume[i]; } else { /* ramp completed ? */ if (vol <= cd->tvolume[i] || vol <= cd->vol_min) { - vol_update(cd, i); cd->ramp_increment[i] = 0; + cd->volume[i] = cd->tvolume[i]; } else { cd->volume[i] = vol; again = 1; @@ -140,10 +135,11 @@ static enum task_state vol_work(void *data) } } - /* sync host with new value */ - vol_sync_host(cd, i); } + /* sync host with new value */ + vol_sync_host(cd, cd->channels); + /* do we need to continue ramping */ if (again) return SOF_TASK_STATE_RESCHEDULE; @@ -680,8 +676,7 @@ static int volume_prepare(struct comp_dev *dev) goto err; } - for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) - vol_sync_host(cd, i); + vol_sync_host(cd, PLATFORM_MAX_CHANNELS); /* Set current volume to min to ensure ramp starts from minimum * to previous volume request. Copy() checks for ramp started