From 9f6ed91fe1a27e52aea56e7b23e201f7933a4c5e Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 7 Nov 2026 15:46:29 +0000 Subject: [PATCH 01/10] topology2: add missing direction attribute to capture pipelines Add explicit direction "capture" to the pipeline."1" scheduler widget in dai-copier-eqiir-module-copier-capture.conf, host-gateway-capture.conf, and webrtc-{aec,ns,ns2}-capture.conf so the pipeline widget direction matches the enclosing capture pipeline class. Signed-off-by: Liam Girdwood --- .../pipelines/cavs/dai-copier-eqiir-module-copier-capture.conf | 1 + .../topology2/include/pipelines/cavs/host-gateway-capture.conf | 1 + .../topology2/include/pipelines/cavs/webrtc-aec-capture.conf | 1 + .../topology2/include/pipelines/cavs/webrtc-ns-capture.conf | 1 + .../topology2/include/pipelines/cavs/webrtc-ns2-capture.conf | 1 + 5 files changed, 5 insertions(+) diff --git a/tools/topology/topology2/include/pipelines/cavs/dai-copier-eqiir-module-copier-capture.conf b/tools/topology/topology2/include/pipelines/cavs/dai-copier-eqiir-module-copier-capture.conf index 0f38da399467..245540faa634 100644 --- a/tools/topology/topology2/include/pipelines/cavs/dai-copier-eqiir-module-copier-capture.conf +++ b/tools/topology/topology2/include/pipelines/cavs/dai-copier-eqiir-module-copier-capture.conf @@ -146,6 +146,7 @@ Class.Pipeline."dai-copier-eqiir-module-copier-capture" { pipeline."1" { priority 0 lp_mode 0 + direction "capture" } } diff --git a/tools/topology/topology2/include/pipelines/cavs/host-gateway-capture.conf b/tools/topology/topology2/include/pipelines/cavs/host-gateway-capture.conf index c9a520f28b80..d4da094c7fe5 100644 --- a/tools/topology/topology2/include/pipelines/cavs/host-gateway-capture.conf +++ b/tools/topology/topology2/include/pipelines/cavs/host-gateway-capture.conf @@ -100,6 +100,7 @@ Class.Pipeline."host-gateway-capture" { pipeline."1" { priority 0 lp_mode 0 + direction "capture" } } diff --git a/tools/topology/topology2/include/pipelines/cavs/webrtc-aec-capture.conf b/tools/topology/topology2/include/pipelines/cavs/webrtc-aec-capture.conf index 76d40f18651b..f6fb5723a3a5 100644 --- a/tools/topology/topology2/include/pipelines/cavs/webrtc-aec-capture.conf +++ b/tools/topology/topology2/include/pipelines/cavs/webrtc-aec-capture.conf @@ -46,6 +46,7 @@ Class.Pipeline."webrtc-aec-capture" { pipeline."1" { priority 0 lp_mode 0 + direction "capture" } # Input copier: S32 DAI → S16 for AECm (fixed-point) diff --git a/tools/topology/topology2/include/pipelines/cavs/webrtc-ns-capture.conf b/tools/topology/topology2/include/pipelines/cavs/webrtc-ns-capture.conf index a43082adc9fb..c3b6b81da3da 100644 --- a/tools/topology/topology2/include/pipelines/cavs/webrtc-ns-capture.conf +++ b/tools/topology/topology2/include/pipelines/cavs/webrtc-ns-capture.conf @@ -44,6 +44,7 @@ Class.Pipeline."webrtc-ns-capture" { pipeline."1" { priority 0 lp_mode 0 + direction "capture" } # DAI → S32 on capture; convert to S16 for NS processing diff --git a/tools/topology/topology2/include/pipelines/cavs/webrtc-ns2-capture.conf b/tools/topology/topology2/include/pipelines/cavs/webrtc-ns2-capture.conf index e305ffd5acf2..25910fbb8825 100644 --- a/tools/topology/topology2/include/pipelines/cavs/webrtc-ns2-capture.conf +++ b/tools/topology/topology2/include/pipelines/cavs/webrtc-ns2-capture.conf @@ -44,6 +44,7 @@ Class.Pipeline."webrtc-ns2-capture" { pipeline."1" { priority 0 lp_mode 0 + direction "capture" } # RNNoise widget — 48 kHz float internally, S16/S32 pipeline From 9431c036db23ae9276c22ba8534cee8e3de83d3b Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 7 Nov 2026 15:50:33 +0000 Subject: [PATCH 02/10] topology2: wire webrtc-hpf and webrtc-agc into capture and add PTL Instantiate webrtc-hpf (DC/<80 Hz removal) and webrtc-agc (digital gain control and saturation limiter) in webrtc-ns-capture.conf alongside webrtc-vad and webrtc-ns to form the complete WebRTC voice pre-processing chain. Add PTL nocodec topology targets (sof-ptl-nocodec-webrtc-{ns,aec,ns2}) in tplg-targets.cmake alongside the existing TGL targets. Signed-off-by: Liam Girdwood --- .../development/cavs-nocodec-webrtc-ns.conf | 16 ++++---- .../topology2/development/tplg-targets.cmake | 12 ++++-- .../pipelines/cavs/webrtc-ns-capture.conf | 40 +++++++++++++++---- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/tools/topology/topology2/development/cavs-nocodec-webrtc-ns.conf b/tools/topology/topology2/development/cavs-nocodec-webrtc-ns.conf index 59c17845d940..047aeeec8a6c 100644 --- a/tools/topology/topology2/development/cavs-nocodec-webrtc-ns.conf +++ b/tools/topology/topology2/development/cavs-nocodec-webrtc-ns.conf @@ -1,17 +1,19 @@ # -# cAVS nocodec topology: webrtc-vad + webrtc-ns capture test +# cAVS nocodec topology: webrtc-hpf + webrtc-vad + webrtc-ns + webrtc-agc capture test # -# Standalone test topology for validating the webrtc_vad and webrtc_ns -# modules on real hardware using the nocodec (loopback) SSP interface. +# Standalone test topology for validating the webrtc_hpf, webrtc_vad, +# webrtc_ns, and webrtc_agc modules on real hardware using the nocodec +# (loopback) SSP interface. # # Pipeline diagram: # -# PCM0 playback ──(1)──────────────(2)──> SSP0 TX +# PCM0 playback ──(1)──────────────────────────(2)──> SSP0 TX # -# SSP0 RX ──────(3)──> VAD + NS ──(4)──> PCM0 capture +# SSP0 RX ──────(3)──> HPF + VAD + NS + AGC ──(4)──> PCM0 capture # -# The VAD module fires NOTIFIER_ID_VAD events; the NS module removes -# stationary background noise. Both run on the same capture pipeline. +# The HPF module removes DC and <80 Hz rumble; the VAD module fires +# NOTIFIER_ID_VAD events; the NS module removes stationary background noise; +# and the AGC module normalizes speech levels. All run on the capture pipeline. # # Board: any cAVS/ACE platform with a nocodec SSP loopback fixture. # Compile: alsatplg -c cavs-nocodec-webrtc-ns.conf -o webrtc-ns.tplg diff --git a/tools/topology/topology2/development/tplg-targets.cmake b/tools/topology/topology2/development/tplg-targets.cmake index 9713c7088182..9284e74e4223 100644 --- a/tools/topology/topology2/development/tplg-targets.cmake +++ b/tools/topology/topology2/development/tplg-targets.cmake @@ -325,17 +325,23 @@ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-tgl-nocodec-crossover.bin,EFX_CROSSOVE "development/cavs-nocodec-rtcaec\;sof-tgl-nocodec-rtcaec\;PLATFORM=tgl,\ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-tgl-nocodec-rtcaec.bin" -# WebRTC noise suppressor (webrtc_ns + webrtc_vad) on TGL nocodec SSP +# WebRTC voice pre-processing (webrtc_hpf + webrtc_vad + webrtc_ns + webrtc_agc) on TGL/PTL nocodec SSP "development/cavs-nocodec-webrtc-ns\;sof-tgl-nocodec-webrtc-ns\;PLATFORM=tgl,\ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-tgl-nocodec-webrtc-ns.bin" +"development/cavs-nocodec-webrtc-ns\;sof-ptl-nocodec-webrtc-ns\;PLATFORM=ptl,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-nocodec-webrtc-ns.bin" -# WebRTC AECm echo canceller (dual SSP: mic + ref) on TGL nocodec +# WebRTC AEC echo canceller (dual SSP: mic + ref) on TGL/PTL nocodec "development/cavs-nocodec-webrtc-aec\;sof-tgl-nocodec-webrtc-aec\;PLATFORM=tgl,\ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-tgl-nocodec-webrtc-aec.bin" +"development/cavs-nocodec-webrtc-aec\;sof-ptl-nocodec-webrtc-aec\;PLATFORM=ptl,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-nocodec-webrtc-aec.bin" -# WebRTC NS2/RNNoise (48 kHz float) on TGL nocodec SSP +# WebRTC NS2/RNNoise (48 kHz float) on TGL/PTL nocodec SSP "development/cavs-nocodec-webrtc-ns2\;sof-tgl-nocodec-webrtc-ns2\;PLATFORM=tgl,\ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-tgl-nocodec-webrtc-ns2.bin" +"development/cavs-nocodec-webrtc-ns2\;sof-ptl-nocodec-webrtc-ns2\;PLATFORM=ptl,\ +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-nocodec-webrtc-ns2.bin" # Topology to test the USB audio offload on an HDA machine, load it with # snd_sof.tplg_filename=sof-hda-generic-uaol.tplg diff --git a/tools/topology/topology2/include/pipelines/cavs/webrtc-ns-capture.conf b/tools/topology/topology2/include/pipelines/cavs/webrtc-ns-capture.conf index c3b6b81da3da..1df6c3367a36 100644 --- a/tools/topology/topology2/include/pipelines/cavs/webrtc-ns-capture.conf +++ b/tools/topology/topology2/include/pipelines/cavs/webrtc-ns-capture.conf @@ -1,12 +1,12 @@ # -# cAVS webrtc-vad + webrtc-ns capture pipeline +# cAVS webrtc-hpf + webrtc-vad + webrtc-ns + webrtc-agc capture pipeline # -# DAI-copier → webrtc-vad → webrtc-ns → module-copier → host +# DAI-copier → webrtc-hpf → webrtc-vad → webrtc-ns → webrtc-agc → module-copier → host # -# webrtc-vad runs first so the VAD event fires on unprocessed mic audio. -# webrtc-ns follows to remove stationary noise before the host receives it. -# Either module can be omitted by editing routes; the component confs are -# independent and can be used separately. +# webrtc-hpf removes DC offset and sub-80 Hz rumble first. +# webrtc-vad runs next so the VAD event fires on high-passed mic audio. +# webrtc-ns removes stationary noise, and webrtc-agc normalizes speech +# levels before the host receives the stream. # # Usage: # Object.Pipeline.webrtc-ns-capture."N" { @@ -20,8 +20,10 @@ + + Class.Pipeline."webrtc-ns-capture" { @@ -69,6 +71,13 @@ Class.Pipeline."webrtc-ns-capture" { ] } + # HPF: 2nd-order Butterworth high-pass filter (<80 Hz rejection) + webrtc-hpf."1" { + Object.Control.mixer.1 { + name 'WebRTC HPF Switch' + } + } + # VAD: pass-through + VAD event webrtc-vad."1" { } @@ -77,6 +86,13 @@ Class.Pipeline."webrtc-ns-capture" { webrtc-ns."1" { } + # AGC: digital automatic gain control + saturation limiter + webrtc-agc."1" { + Object.Control.mixer.1 { + name 'WebRTC AGC Switch' + } + } + # Output copier: S16 → S32 for host module-copier."2" { period_sink_count 1 @@ -103,14 +119,22 @@ Class.Pipeline."webrtc-ns-capture" { Object.Base { route.1 { source module-copier.$index.1 - sink webrtc-vad.$index.1 + sink webrtc-hpf.$index.1 } route.2 { + source webrtc-hpf.$index.1 + sink webrtc-vad.$index.1 + } + route.3 { source webrtc-vad.$index.1 sink webrtc-ns.$index.1 } - route.3 { + route.4 { source webrtc-ns.$index.1 + sink webrtc-agc.$index.1 + } + route.5 { + source webrtc-agc.$index.1 sink module-copier.$index.2 } } From 0352a9007f534eed442aa70d3a36ba131a297d06 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Nov 2026 17:12:32 +0000 Subject: [PATCH 03/10] topology2: cavs-mixin-mixout-hda: add WebRTC AEC capture support Add an optional WebRTC AEC capture variant in cavs-mixin-mixout-hda.conf using webrtc-aec-capture, tap the analog playback pipeline after drc.2.1 via module-copier.2.2 to feed the speaker reference signal to webrtc-aec.3.1 input pin 1, and keep the AEC mixer control names within the 44-byte ALSA control name limit. Signed-off-by: Liam Girdwood --- .../topology2/cavs-mixin-mixout-hda.conf | 144 ++++++++++++++++-- .../topology2/development/tplg-targets.cmake | 3 + 2 files changed, 138 insertions(+), 9 deletions(-) diff --git a/tools/topology/topology2/cavs-mixin-mixout-hda.conf b/tools/topology/topology2/cavs-mixin-mixout-hda.conf index e5ac78e505d1..ebc0c52780b9 100644 --- a/tools/topology/topology2/cavs-mixin-mixout-hda.conf +++ b/tools/topology/topology2/cavs-mixin-mixout-hda.conf @@ -1,3 +1,4 @@ + Define { @@ -21,6 +22,7 @@ Define { EFX_IIR_PARAMS 'passthrough' EFX_DRC_PARAMS 'passthrough' HDA_MIC_ENHANCED_CAPTURE 'false' + AEC_PROCESSING 'false' # Compress capture (encode) via the FFmpeg encoder (ffmpeg_enc). The PCM # name overrides the generic compr-default.conf default. COMPR_ENC_SOURCE @@ -34,6 +36,12 @@ Define { COMPR_ENC_SOURCE 'mixin.4.1' } +IncludeByKey.AEC_PROCESSING { + "true" { + + } +} + # include deep buffer config if buffer size is in 1 - 1000 ms. IncludeByKey.DEEPBUFFER_FW_DMA_MS{ "[1-1000]" "platform/intel/deep-buffer.conf" @@ -439,6 +447,85 @@ IncludeByKey.HDA_MIC_ENHANCED_CAPTURE { } } +IncludeByKey.AEC_PROCESSING { + "true" { + Object.Widget.module-copier."2" { + index 2 + stream_name $HDA_ANALOG_DAI_NAME + num_input_pins 1 + num_output_pins 2 + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $HDA_ANALOG_PLAYBACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $HDA_ANALOG_PLAYBACK_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + + Object.Widget.webrtc-aec [ + { + index 3 + name "webrtc-aec.3.1" + num_input_pins 2 + num_input_audio_formats 2 + num_output_audio_formats 1 + + Object.Base.input_audio_format [ + { + input_pin_index 0 + in_rate $HDA_ANALOG_CAPTURE_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + ibs "$[(($in_channels * ($[($in_rate + 999)] / 1000)) * ($in_bit_depth / 8)) * $WEBRTC_AEC_PERIOD_MS]" + } + { + input_pin_index 1 + in_rate $HDA_ANALOG_PLAYBACK_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + ibs "$[(($in_channels * ($[($in_rate + 999)] / 1000)) * ($in_bit_depth / 8)) * $WEBRTC_AEC_PERIOD_MS]" + } + ] + + Object.Base.output_audio_format [ + { + out_rate $HDA_ANALOG_CAPTURE_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + obs "$[(($out_channels * ($[($out_rate + 999)] / 1000)) * ($out_bit_depth / 8)) * $WEBRTC_AEC_PERIOD_MS]" + } + ] + + Object.Base.input_pin_binding.1 { + input_pin_binding_name "module-copier.4.2" + } + Object.Base.input_pin_binding.2 { + input_pin_binding_name "module-copier.2.2" + } + + Object.Control { + mixer."1" { + name '$ANALOG_CAPTURE_PCM AEC Switch' + } + mixer."2" { + name '$ANALOG_CAPTURE_PCM AEC Suppression Switch' + } + } + } + ] + } +} + Object.PCM.pcm [ { id 0 @@ -466,10 +553,6 @@ Object.PCM.pcm [ # top-level pipeline connections Object.Base.route [ - { - sink 'dai-copier.HDA.$HDA_ANALOG_DAI_NAME.playback' - source 'drc.2.1' - } { source 'mixin.1.1' sink 'mixout.2.1' @@ -480,6 +563,29 @@ Object.Base.route [ } ] +IncludeByKey.AEC_PROCESSING { + "true" { + Object.Base.route [ + { + source 'drc.2.1' + sink 'module-copier.2.2' + } + { + source 'module-copier.2.2' + sink 'dai-copier.HDA.$HDA_ANALOG_DAI_NAME.playback' + } + ] + } + "false" { + Object.Base.route [ + { + source 'drc.2.1' + sink 'dai-copier.HDA.$HDA_ANALOG_DAI_NAME.playback' + } + ] + } +} + IncludeByKey.COMPR_FFMPEG_ENC { "false" { # stock capture BE processing chain feeding the normal analog capture FE @@ -507,12 +613,32 @@ IncludeByKey.COMPR_FFMPEG_ENC { ] } "false" { - Object.Base.route [ - { - source 'module-copier.4.2' - sink 'host-copier.0.capture' + IncludeByKey.AEC_PROCESSING { + "true" { + Object.Base.route [ + { + source 'module-copier.4.2' + sink 'webrtc-aec.3.1' + } + { + source 'module-copier.2.2' + sink 'webrtc-aec.3.1' + } + { + source 'webrtc-aec.3.1' + sink 'host-copier.0.capture' + } + ] } - ] + "false" { + Object.Base.route [ + { + source 'module-copier.4.2' + sink 'host-copier.0.capture' + } + ] + } + } } } } diff --git a/tools/topology/topology2/development/tplg-targets.cmake b/tools/topology/topology2/development/tplg-targets.cmake index 9284e74e4223..38b9594bddc6 100644 --- a/tools/topology/topology2/development/tplg-targets.cmake +++ b/tools/topology/topology2/development/tplg-targets.cmake @@ -343,6 +343,9 @@ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-tgl-nocodec-webrtc-ns2.bin" "development/cavs-nocodec-webrtc-ns2\;sof-ptl-nocodec-webrtc-ns2\;PLATFORM=ptl,\ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-nocodec-webrtc-ns2.bin" +# WebRTC AEC capture on HDA generic +"sof-hda-generic\;sof-hda-generic-webrtc-aec\;HDA_CONFIG=mix,AEC_PROCESSING=true" + # Topology to test the USB audio offload on an HDA machine, load it with # snd_sof.tplg_filename=sof-hda-generic-uaol.tplg "development/sof-hda-generic-uaol\;sof-hda-generic-uaol\;HDA_CONFIG=mix,HDA_MIC_ENHANCED_CAPTURE=true,\ From cbb32d1aa342c8d25ae187109496be4010bb75ed Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Nov 2026 17:12:32 +0000 Subject: [PATCH 04/10] topology2: add WebRTC feature and function topologies for DMIC/SDW Add WebRTC NS/NS2/AEC topology targets and platform/intel configs for DMIC, SDW DMIC, and SDW Jack capture chains (including pipeline ID 450 for SDW Jack WebRTC capture, explicit module-copier S32->S16 conversion after 48->16 kHz SRC, and matching 16 kHz / DMIC1_RATE format sets on webrtc-vad). Signed-off-by: Liam Girdwood --- tools/topology/topology2/cavs-sdw.conf | 11 + .../topology2/development/tplg-targets.cmake | 23 + .../topology2/dmic-webrtc-feature.conf | 31 ++ .../include/common/common_definitions.conf | 2 + .../platform/intel/dmic-generic.conf | 1 + .../platform/intel/dmic1-webrtc.conf | 267 ++++++++++++ .../intel/sdw-dmic-webrtc-feature.conf | 277 ++++++++++++ .../intel/sdw-jack-webrtc-feature.conf | 394 ++++++++++++++++++ 8 files changed, 1006 insertions(+) create mode 100644 tools/topology/topology2/dmic-webrtc-feature.conf create mode 100644 tools/topology/topology2/platform/intel/dmic1-webrtc.conf create mode 100644 tools/topology/topology2/platform/intel/sdw-dmic-webrtc-feature.conf create mode 100644 tools/topology/topology2/platform/intel/sdw-jack-webrtc-feature.conf diff --git a/tools/topology/topology2/cavs-sdw.conf b/tools/topology/topology2/cavs-sdw.conf index 7edd0975d4f6..5d19c41b4d88 100644 --- a/tools/topology/topology2/cavs-sdw.conf +++ b/tools/topology/topology2/cavs-sdw.conf @@ -288,3 +288,14 @@ IncludeByKey.SDW_DMIC_AUDIO_FEATURE_CAPTURE { IncludeByKey.SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE { "true" "platform/intel/sdw-dmic-audio-feature-compress.conf" } + +IncludeByKey.SDW_JACK_WEBRTC_CAPTURE { + "^ns$" "platform/intel/sdw-jack-webrtc-feature.conf" + "^ns2$" "platform/intel/sdw-jack-webrtc-feature.conf" + "^aec$" "platform/intel/sdw-jack-webrtc-feature.conf" +} + +IncludeByKey.SDW_DMIC_WEBRTC_CAPTURE { + "^ns$" "platform/intel/sdw-dmic-webrtc-feature.conf" + "^ns2$" "platform/intel/sdw-dmic-webrtc-feature.conf" +} diff --git a/tools/topology/topology2/development/tplg-targets.cmake b/tools/topology/topology2/development/tplg-targets.cmake index 38b9594bddc6..acabd4b0a1c0 100644 --- a/tools/topology/topology2/development/tplg-targets.cmake +++ b/tools/topology/topology2/development/tplg-targets.cmake @@ -346,6 +346,29 @@ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-nocodec-webrtc-ns2.bin" # WebRTC AEC capture on HDA generic "sof-hda-generic\;sof-hda-generic-webrtc-aec\;HDA_CONFIG=mix,AEC_PROCESSING=true" +# Standalone DMIC WebRTC feature topologies (NS chain at 16 kHz and NS2 RNNoise at 48 kHz) +"dmic-webrtc-feature\;sof-dmic-2ch-webrtc-ns\;PLATFORM=none,NUM_DMICS=2,DMIC1_RATE=16000,\ +WEBRTC_DMIC1_MODE=ns" +"dmic-webrtc-feature\;sof-dmic-2ch-webrtc-ns2\;PLATFORM=none,NUM_DMICS=2,DMIC1_RATE=48000,\ +WEBRTC_DMIC1_MODE=ns2" + +# SoundWire SDCA function topologies with WebRTC capture feature pipelines +"cavs-sdw\;sof-sdca-mic-id4-webrtc-ns\;SDW_JACK=false,SDW_DMIC=1,NUM_HDMIS=0,\ +SDW_DMIC_STREAM=Capture-SmartMic,SDW_DMIC_WEBRTC_CAPTURE=ns" +"cavs-sdw\;sof-sdca-mic-id4-webrtc-ns2\;SDW_JACK=false,SDW_DMIC=1,NUM_HDMIS=0,\ +SDW_DMIC_STREAM=Capture-SmartMic,SDW_DMIC_WEBRTC_CAPTURE=ns2" +"cavs-sdw\;sof-sdca-jack-id0-webrtc-ns\;SDW_JACK_OUT_STREAM=Playback-SimpleJack,\ +SDW_JACK_IN_STREAM=Capture-SimpleJack,NUM_HDMIS=0,SDW_JACK_WEBRTC_CAPTURE=ns" +"cavs-sdw\;sof-sdca-jack-id0-webrtc-aec\;SDW_JACK_OUT_STREAM=Playback-SimpleJack,\ +SDW_JACK_IN_STREAM=Capture-SimpleJack,NUM_HDMIS=0,SDW_JACK_WEBRTC_CAPTURE=aec" + +# PTL PCH-DMIC function topology with 16 kHz DMIC1 WebRTC NS capture +"cavs-sdw\;sof-ptl-dmic-2ch-id2-webrtc-ns\;PLATFORM=ptl,SDW_JACK=false,NUM_HDMIS=0,\ +NUM_DMICS=2,PDM1_MIC_A_ENABLE=0,PDM1_MIC_B_ENABLE=0,DMIC0_ID=2,DMIC1_ID=3,\ +DMIC1_ENABLE=webrtc,DMIC1_RATE=16000,PREPROCESS_PLUGINS=nhlt,\ +NHLT_BIN=nhlt-sof-ptl-dmic-2ch-id2-webrtc-ns.bin,DMIC0_ENHANCED_CAPTURE=true,\ +EFX_DMIC0_TDFB_PARAMS=line2_generic_pm10deg,EFX_DMIC0_DRC_PARAMS=dmic_default" + # Topology to test the USB audio offload on an HDA machine, load it with # snd_sof.tplg_filename=sof-hda-generic-uaol.tplg "development/sof-hda-generic-uaol\;sof-hda-generic-uaol\;HDA_CONFIG=mix,HDA_MIC_ENHANCED_CAPTURE=true,\ diff --git a/tools/topology/topology2/dmic-webrtc-feature.conf b/tools/topology/topology2/dmic-webrtc-feature.conf new file mode 100644 index 000000000000..20cd803929b8 --- /dev/null +++ b/tools/topology/topology2/dmic-webrtc-feature.conf @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/topology/topology2/include/common/common_definitions.conf b/tools/topology/topology2/include/common/common_definitions.conf index 06f0f425c5e2..2a4ba61880f5 100644 --- a/tools/topology/topology2/include/common/common_definitions.conf +++ b/tools/topology/topology2/include/common/common_definitions.conf @@ -75,4 +75,6 @@ Define { SDW_JACK_COMPR_AUDIO_FEATURE_CAPTURE false # No compress audio features capture for jack SDW_DMIC_AUDIO_FEATURE_CAPTURE false # No audio features capture for microphone SDW_DMIC_COMPR_AUDIO_FEATURE_CAPTURE false # No compress audio features capture for microphone + SDW_JACK_WEBRTC_CAPTURE false # No WebRTC capture for jack + SDW_DMIC_WEBRTC_CAPTURE false # No WebRTC capture for microphone } diff --git a/tools/topology/topology2/platform/intel/dmic-generic.conf b/tools/topology/topology2/platform/intel/dmic-generic.conf index c473f1eea470..b42717f76310 100644 --- a/tools/topology/topology2/platform/intel/dmic-generic.conf +++ b/tools/topology/topology2/platform/intel/dmic-generic.conf @@ -1048,4 +1048,5 @@ IncludeByKey.DMIC0_DEEP_BUFFER { IncludeByKey.DMIC1_ENABLE { "passthrough" "platform/intel/dmic1-passthrough.conf" "mfcc" "platform/intel/dmic1-mfcc.conf" + "webrtc" "platform/intel/dmic1-webrtc.conf" } diff --git a/tools/topology/topology2/platform/intel/dmic1-webrtc.conf b/tools/topology/topology2/platform/intel/dmic1-webrtc.conf new file mode 100644 index 000000000000..4b2d0fba3d51 --- /dev/null +++ b/tools/topology/topology2/platform/intel/dmic1-webrtc.conf @@ -0,0 +1,267 @@ + + + + + + +Define { + DMIC1_PCM_NAME "DMIC WebRTC" + WEBRTC_DMIC1_MODE "ns" +} + +Object.Pipeline.host-gateway-capture [ + { + index $DMIC1_HOST_PIPELINE_ID + Object.Widget.host-copier.1 { + stream_name $DMIC1_PCM_CAPS + pcm_id $DMIC1_PCM_ID + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $DMIC1_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $DMIC1_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } +] + +Object.Pipeline.io-gateway-capture [ + { + index $DMIC1_DAI_PIPELINE_ID + Object.Widget.dai-copier.1 { + dai_index 1 + dai_type "DMIC" + copier_type "DMIC" + type dai_out + stream_name $DMIC1_NAME + node_type $DMIC_LINK_INPUT_CLASS + num_input_audio_formats 3 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $DMIC1_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate $DMIC1_RATE + in_bit_depth 32 + in_valid_bit_depth 24 + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + } + { + in_rate $DMIC1_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $DMIC1_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } +] + +IncludeByKey.WEBRTC_DMIC1_MODE { + "^ns$" { + Object.Widget { + module-copier."2" { + index $DMIC1_HOST_PIPELINE_ID + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $DMIC1_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate $DMIC1_RATE + out_bit_depth 16 + out_valid_bit_depth 16 + } + ] + } + webrtc-hpf."1" { + index $DMIC1_HOST_PIPELINE_ID + Object.Control.mixer."1" { + name 'DMIC1 Capture HPF Switch' + } + } + webrtc-vad."1" { + index $DMIC1_HOST_PIPELINE_ID + num_input_audio_formats 4 + num_output_audio_formats 4 + Object.Base.input_audio_format [ + { + in_rate $DMIC1_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate $DMIC1_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + } + { + in_rate $DMIC1_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + in_channels 1 + in_ch_cfg $CHANNEL_CONFIG_MONO + in_ch_map $CHANNEL_MAP_MONO + } + { + in_rate $DMIC1_RATE + in_bit_depth 32 + in_valid_bit_depth 32 + in_channels 1 + in_ch_cfg $CHANNEL_CONFIG_MONO + in_ch_map $CHANNEL_MAP_MONO + } + ] + Object.Base.output_audio_format [ + { + out_rate $DMIC1_RATE + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_rate $DMIC1_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + { + out_rate $DMIC1_RATE + out_bit_depth 16 + out_valid_bit_depth 16 + out_channels 1 + out_ch_cfg $CHANNEL_CONFIG_MONO + out_ch_map $CHANNEL_MAP_MONO + } + { + out_rate $DMIC1_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + out_channels 1 + out_ch_cfg $CHANNEL_CONFIG_MONO + out_ch_map $CHANNEL_MAP_MONO + } + ] + } + webrtc-ns."1" { + index $DMIC1_HOST_PIPELINE_ID + } + webrtc-agc."1" { + index $DMIC1_HOST_PIPELINE_ID + Object.Control.mixer."1" { + name 'DMIC1 Capture AGC Switch' + } + } + module-copier."3" { + index $DMIC1_HOST_PIPELINE_ID + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate $DMIC1_RATE + in_bit_depth 16 + in_valid_bit_depth 16 + } + ] + Object.Base.output_audio_format [ + { + out_rate $DMIC1_RATE + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + + Object.Base.route [ + { + source "dai-copier.DMIC.$DMIC1_NAME.capture" + sink "module-copier.$DMIC1_HOST_PIPELINE_ID.2" + } + { + source "module-copier.$DMIC1_HOST_PIPELINE_ID.2" + sink "webrtc-hpf.$DMIC1_HOST_PIPELINE_ID.1" + } + { + source "webrtc-hpf.$DMIC1_HOST_PIPELINE_ID.1" + sink "webrtc-vad.$DMIC1_HOST_PIPELINE_ID.1" + } + { + source "webrtc-vad.$DMIC1_HOST_PIPELINE_ID.1" + sink "webrtc-ns.$DMIC1_HOST_PIPELINE_ID.1" + } + { + source "webrtc-ns.$DMIC1_HOST_PIPELINE_ID.1" + sink "webrtc-agc.$DMIC1_HOST_PIPELINE_ID.1" + } + { + source "webrtc-agc.$DMIC1_HOST_PIPELINE_ID.1" + sink "module-copier.$DMIC1_HOST_PIPELINE_ID.3" + } + { + source "module-copier.$DMIC1_HOST_PIPELINE_ID.3" + sink "host-copier.$DMIC1_PCM_ID.capture" + } + ] + } + "^ns2$" { + Object.Widget { + webrtc-ns2."1" { + index $DMIC1_HOST_PIPELINE_ID + } + } + + Object.Base.route [ + { + source "dai-copier.DMIC.$DMIC1_NAME.capture" + sink "webrtc-ns2.$DMIC1_HOST_PIPELINE_ID.1" + } + { + source "webrtc-ns2.$DMIC1_HOST_PIPELINE_ID.1" + sink "host-copier.$DMIC1_PCM_ID.capture" + } + ] + } +} + +Object.PCM.pcm [ + { + name "$DMIC1_PCM_NAME" + id $DMIC1_PCM_ID + direction "capture" + Object.Base.fe_dai.1 { + name "$DMIC1_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name $DMIC1_PCM_CAPS + formats 'S32_LE' + channels_min 2 + channels_max 2 + rate_min $DMIC1_RATE + rate_max $DMIC1_RATE + } + } +] diff --git a/tools/topology/topology2/platform/intel/sdw-dmic-webrtc-feature.conf b/tools/topology/topology2/platform/intel/sdw-dmic-webrtc-feature.conf new file mode 100644 index 000000000000..4860286beb58 --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-dmic-webrtc-feature.conf @@ -0,0 +1,277 @@ + + + + + + +Define { + SDW_DMIC_MODULE_COPIER_ID 41 + SDW_DMIC_WEBRTC_CAPTURE_PCM_NAME "Microphone WebRTC" + SDW_DMIC_WEBRTC_CAPTURE_PCM_ID 49 + SDW_DMIC_WEBRTC_CAPTURE_STREAM_NAME "Microphone WebRTC Stream" + SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID 134 +} + +IncludeByKey.SDW_DMIC_WEBRTC_CAPTURE { + "^ns$" { + Object.Pipeline.host-gateway-capture [ + { + index $SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID + + Object.Widget.host-copier.1 { + stream_name "$SDW_DMIC_WEBRTC_CAPTURE_STREAM_NAME" + pcm_id $SDW_DMIC_WEBRTC_CAPTURE_PCM_ID + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate 16000 + in_bit_depth 16 + in_valid_bit_depth 16 + } + ] + Object.Base.output_audio_format [ + { + out_rate 16000 + out_bit_depth 16 + out_valid_bit_depth 16 + } + ] + } + } + ] + + Object.Widget { + src."1" { + index $SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID + rate_in 48000 + rate_out 16000 + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate 16000 + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + module-copier."1" { + index $SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate 16000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate 16000 + out_bit_depth 16 + out_valid_bit_depth 16 + } + ] + } + webrtc-hpf."1" { + index $SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID + Object.Control.mixer."1" { + name 'Microphone WebRTC HPF Switch' + } + } + webrtc-vad."1" { + index $SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID + num_input_audio_formats 4 + num_output_audio_formats 4 + Object.Base.input_audio_format [ + { + in_rate 16000 + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate 16000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + { + in_rate 16000 + in_bit_depth 16 + in_valid_bit_depth 16 + in_channels 1 + in_ch_cfg $CHANNEL_CONFIG_MONO + in_ch_map $CHANNEL_MAP_MONO + } + { + in_rate 16000 + in_bit_depth 32 + in_valid_bit_depth 32 + in_channels 1 + in_ch_cfg $CHANNEL_CONFIG_MONO + in_ch_map $CHANNEL_MAP_MONO + } + ] + Object.Base.output_audio_format [ + { + out_rate 16000 + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_rate 16000 + out_bit_depth 32 + out_valid_bit_depth 32 + } + { + out_rate 16000 + out_bit_depth 16 + out_valid_bit_depth 16 + out_channels 1 + out_ch_cfg $CHANNEL_CONFIG_MONO + out_ch_map $CHANNEL_MAP_MONO + } + { + out_rate 16000 + out_bit_depth 32 + out_valid_bit_depth 32 + out_channels 1 + out_ch_cfg $CHANNEL_CONFIG_MONO + out_ch_map $CHANNEL_MAP_MONO + } + ] + } + webrtc-ns."1" { + index $SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID + } + webrtc-agc."1" { + index $SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID + Object.Control.mixer."1" { + name 'Microphone WebRTC AGC Switch' + } + } + } + + Object.Base.route [ + { + source "module-copier.$SDW_DMIC_MODULE_COPIER_ID.0" + sink "src.$SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source "src.$SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID.1" + sink "module-copier.$SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source "module-copier.$SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID.1" + sink "webrtc-hpf.$SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source "webrtc-hpf.$SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID.1" + sink "webrtc-vad.$SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source "webrtc-vad.$SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID.1" + sink "webrtc-ns.$SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source "webrtc-ns.$SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID.1" + sink "webrtc-agc.$SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source "webrtc-agc.$SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID.1" + sink "host-copier.$SDW_DMIC_WEBRTC_CAPTURE_PCM_ID.capture" + } + ] + + Object.PCM.pcm [ + { + name "$SDW_DMIC_WEBRTC_CAPTURE_PCM_NAME" + id $SDW_DMIC_WEBRTC_CAPTURE_PCM_ID + direction "capture" + Object.Base.fe_dai.1 { + name "$SDW_DMIC_WEBRTC_CAPTURE_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name "$SDW_DMIC_WEBRTC_CAPTURE_STREAM_NAME" + formats 'S16_LE' + rates '16000' + channels_min 2 + channels_max 2 + } + } + ] + } + "^ns2$" { + Object.Pipeline.host-gateway-capture [ + { + index $SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID + + Object.Widget.host-copier.1 { + stream_name "$SDW_DMIC_WEBRTC_CAPTURE_STREAM_NAME" + pcm_id $SDW_DMIC_WEBRTC_CAPTURE_PCM_ID + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate 48000 + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + ] + + Object.Widget { + webrtc-ns2."1" { + index $SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID + } + } + + Object.Base.route [ + { + source "module-copier.$SDW_DMIC_MODULE_COPIER_ID.0" + sink "webrtc-ns2.$SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source "webrtc-ns2.$SDW_DMIC_WEBRTC_CAPTURE_PIPELINE_ID.1" + sink "host-copier.$SDW_DMIC_WEBRTC_CAPTURE_PCM_ID.capture" + } + ] + + Object.PCM.pcm [ + { + name "$SDW_DMIC_WEBRTC_CAPTURE_PCM_NAME" + id $SDW_DMIC_WEBRTC_CAPTURE_PCM_ID + direction "capture" + Object.Base.fe_dai.1 { + name "$SDW_DMIC_WEBRTC_CAPTURE_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name "$SDW_DMIC_WEBRTC_CAPTURE_STREAM_NAME" + formats 'S32_LE' + rates '48000' + channels_min 2 + channels_max 2 + } + } + ] + } +} diff --git a/tools/topology/topology2/platform/intel/sdw-jack-webrtc-feature.conf b/tools/topology/topology2/platform/intel/sdw-jack-webrtc-feature.conf new file mode 100644 index 000000000000..d3815bff3141 --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-jack-webrtc-feature.conf @@ -0,0 +1,394 @@ + + + + + + + + +Define { + SDW_JACK_MODULE_COPIER_ID 11 + SDW_JACK_REF_COPIER "module-copier.1.12" + SDW_JACK_WEBRTC_CAPTURE_PCM_NAME "Jack In WebRTC" + SDW_JACK_WEBRTC_CAPTURE_PCM_ID 45 + SDW_JACK_WEBRTC_CAPTURE_STREAM_NAME "Jack In WebRTC Stream" + SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID 450 +} + +IncludeByKey.SDW_JACK_WEBRTC_CAPTURE { + "^ns$" { + Object.Pipeline.host-gateway-capture [ + { + index $SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID + + Object.Widget.host-copier.1 { + stream_name "$SDW_JACK_WEBRTC_CAPTURE_STREAM_NAME" + pcm_id $SDW_JACK_WEBRTC_CAPTURE_PCM_ID + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate 16000 + in_bit_depth 16 + in_valid_bit_depth 16 + } + ] + Object.Base.output_audio_format [ + { + out_rate 16000 + out_bit_depth 16 + out_valid_bit_depth 16 + } + ] + } + } + ] + + Object.Widget { + src."1" { + index $SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID + rate_in 48000 + rate_out 16000 + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate 16000 + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + module-copier."1" { + index $SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate 16000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate 16000 + out_bit_depth 16 + out_valid_bit_depth 16 + } + ] + } + webrtc-hpf."1" { + index $SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID + Object.Control.mixer."1" { + name 'Jack In WebRTC HPF Switch' + } + } + webrtc-vad."1" { + index $SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID + num_input_audio_formats 4 + num_output_audio_formats 4 + Object.Base.input_audio_format [ + { + in_rate 16000 + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_rate 16000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + { + in_rate 16000 + in_bit_depth 16 + in_valid_bit_depth 16 + in_channels 1 + in_ch_cfg $CHANNEL_CONFIG_MONO + in_ch_map $CHANNEL_MAP_MONO + } + { + in_rate 16000 + in_bit_depth 32 + in_valid_bit_depth 32 + in_channels 1 + in_ch_cfg $CHANNEL_CONFIG_MONO + in_ch_map $CHANNEL_MAP_MONO + } + ] + Object.Base.output_audio_format [ + { + out_rate 16000 + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_rate 16000 + out_bit_depth 32 + out_valid_bit_depth 32 + } + { + out_rate 16000 + out_bit_depth 16 + out_valid_bit_depth 16 + out_channels 1 + out_ch_cfg $CHANNEL_CONFIG_MONO + out_ch_map $CHANNEL_MAP_MONO + } + { + out_rate 16000 + out_bit_depth 32 + out_valid_bit_depth 32 + out_channels 1 + out_ch_cfg $CHANNEL_CONFIG_MONO + out_ch_map $CHANNEL_MAP_MONO + } + ] + } + webrtc-ns."1" { + index $SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID + } + webrtc-agc."1" { + index $SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID + Object.Control.mixer."1" { + name 'Jack In WebRTC AGC Switch' + } + } + } + + Object.Base.route [ + { + source "module-copier.$SDW_JACK_MODULE_COPIER_ID.0" + sink "src.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source "src.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + sink "module-copier.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source "module-copier.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + sink "webrtc-hpf.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source "webrtc-hpf.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + sink "webrtc-vad.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source "webrtc-vad.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + sink "webrtc-ns.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source "webrtc-ns.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + sink "webrtc-agc.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source "webrtc-agc.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + sink "host-copier.$SDW_JACK_WEBRTC_CAPTURE_PCM_ID.capture" + } + ] + + Object.PCM.pcm [ + { + name "$SDW_JACK_WEBRTC_CAPTURE_PCM_NAME" + id $SDW_JACK_WEBRTC_CAPTURE_PCM_ID + direction "capture" + Object.Base.fe_dai.1 { + name "$SDW_JACK_WEBRTC_CAPTURE_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name "$SDW_JACK_WEBRTC_CAPTURE_STREAM_NAME" + formats 'S16_LE' + rates '16000' + channels_min 2 + channels_max 2 + } + } + ] + } + "^ns2$" { + Object.Pipeline.host-gateway-capture [ + { + index $SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID + + Object.Widget.host-copier.1 { + stream_name "$SDW_JACK_WEBRTC_CAPTURE_STREAM_NAME" + pcm_id $SDW_JACK_WEBRTC_CAPTURE_PCM_ID + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate 48000 + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + ] + + Object.Widget { + webrtc-ns2."1" { + index $SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID + } + } + + Object.Base.route [ + { + source "module-copier.$SDW_JACK_MODULE_COPIER_ID.0" + sink "webrtc-ns2.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source "webrtc-ns2.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + sink "host-copier.$SDW_JACK_WEBRTC_CAPTURE_PCM_ID.capture" + } + ] + + Object.PCM.pcm [ + { + name "$SDW_JACK_WEBRTC_CAPTURE_PCM_NAME" + id $SDW_JACK_WEBRTC_CAPTURE_PCM_ID + direction "capture" + Object.Base.fe_dai.1 { + name "$SDW_JACK_WEBRTC_CAPTURE_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name "$SDW_JACK_WEBRTC_CAPTURE_STREAM_NAME" + formats 'S32_LE' + rates '48000' + channels_min 2 + channels_max 2 + } + } + ] + } + "^aec$" { + Object.Pipeline.host-gateway-capture [ + { + index $SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID + + Object.Widget.host-copier.1 { + stream_name "$SDW_JACK_WEBRTC_CAPTURE_STREAM_NAME" + pcm_id $SDW_JACK_WEBRTC_CAPTURE_PCM_ID + num_input_audio_formats 1 + num_output_audio_formats 1 + Object.Base.input_audio_format [ + { + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_rate 48000 + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + ] + + Object.Widget.webrtc-aec [ + { + index $SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID + name "webrtc-aec.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + num_input_pins 2 + num_input_audio_formats 2 + num_output_audio_formats 1 + + Object.Base.input_audio_format [ + { + input_pin_index 0 + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + ibs "$[(($in_channels * ($[($in_rate + 999)] / 1000)) * ($in_bit_depth / 8)) * $WEBRTC_AEC_PERIOD_MS]" + } + { + input_pin_index 1 + in_rate 48000 + in_bit_depth 32 + in_valid_bit_depth 32 + ibs "$[(($in_channels * ($[($in_rate + 999)] / 1000)) * ($in_bit_depth / 8)) * $WEBRTC_AEC_PERIOD_MS]" + } + ] + + Object.Base.output_audio_format [ + { + out_rate 48000 + out_bit_depth 32 + out_valid_bit_depth 32 + obs "$[(($out_channels * ($[($out_rate + 999)] / 1000)) * ($out_bit_depth / 8)) * $WEBRTC_AEC_PERIOD_MS]" + } + ] + + Object.Base.input_pin_binding.1 { + input_pin_binding_name "module-copier.$SDW_JACK_MODULE_COPIER_ID.0" + } + Object.Base.input_pin_binding.2 { + input_pin_binding_name $SDW_JACK_REF_COPIER + } + + Object.Control { + mixer."1" { + name 'Jack In WebRTC AEC Switch' + } + mixer."2" { + name 'Jack In WebRTC AEC Suppression Switch' + } + } + } + ] + + Object.Base.route [ + { + source "module-copier.$SDW_JACK_MODULE_COPIER_ID.0" + sink "webrtc-aec.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source $SDW_JACK_REF_COPIER + sink "webrtc-aec.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + } + { + source "webrtc-aec.$SDW_JACK_WEBRTC_CAPTURE_PIPELINE_ID.1" + sink "host-copier.$SDW_JACK_WEBRTC_CAPTURE_PCM_ID.capture" + } + ] + + Object.PCM.pcm [ + { + name "$SDW_JACK_WEBRTC_CAPTURE_PCM_NAME" + id $SDW_JACK_WEBRTC_CAPTURE_PCM_ID + direction "capture" + Object.Base.fe_dai.1 { + name "$SDW_JACK_WEBRTC_CAPTURE_PCM_NAME" + } + + Object.PCM.pcm_caps.1 { + name "$SDW_JACK_WEBRTC_CAPTURE_STREAM_NAME" + formats 'S32_LE' + rates '48000' + channels_min 2 + channels_max 2 + } + } + ] + } +} From ac24e3d164989fb99ba2732ff42a53a884b863c0 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Nov 2026 17:12:32 +0000 Subject: [PATCH 05/10] audio: webrtc: use non-PIC longcalls flags for external LLEXT libs Compile external WebRTC static libraries (AECm, NS, RNNoise, libfvad) with -fno-pic -fno-pie -mlongcalls -mtext-section-literals -DNDEBUG when building relocatable Xtensa LLEXTs. Without these flags, GCC emits R_XTENSA_PLT internal relocations that the Zephyr LLEXT loader cannot resolve against static archive symbols. Signed-off-by: Liam Girdwood --- src/audio/webrtc_aec/webrtc_aec.cmake | 12 +++++++++++- src/audio/webrtc_ns/webrtc_ns.cmake | 12 +++++++++++- src/audio/webrtc_ns2/webrtc_ns2.cmake | 12 +++++++++++- src/audio/webrtc_vad/webrtc_vad.cmake | 12 +++++++++++- 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/audio/webrtc_aec/webrtc_aec.cmake b/src/audio/webrtc_aec/webrtc_aec.cmake index 094f55e4bb0b..4c42edf068ed 100644 --- a/src/audio/webrtc_aec/webrtc_aec.cmake +++ b/src/audio/webrtc_aec/webrtc_aec.cmake @@ -33,6 +33,16 @@ set(WEBRTC_AECM_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/webrtc-aecm-install" set(_aecm_src "${SOF_WEBRTC_APM_SRC_DIR}/webrtc") +if("${ARCH}" STREQUAL "xtensa") + if(CONFIG_LLEXT_BUILD_PIC) + set(_aecm_arch_cflags "-fPIC -mlongcalls -mtext-section-literals") + else() + set(_aecm_arch_cflags "-fno-pic -fno-pie -mlongcalls -mtext-section-literals") + endif() +else() + set(_aecm_arch_cflags "-fPIC") +endif() + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/webrtc-aecm-build.sh" "#!/bin/sh set -e @@ -42,7 +52,7 @@ OBJ=${CMAKE_CURRENT_BINARY_DIR}/webrtc-aecm-obj mkdir -p \"$OBJ\" \"$INST/lib\" \"$INST/include\" \"$INST/include/webrtc\" -CFLAGS=\"-O2 -fPIC -I${SOF_WEBRTC_APM_SRC_DIR} -I$SRC -I$SRC/common_audio/signal_processing/include -DWEBRTC_ARCH_32_BITS= -DWEBRTC_ARCH_LITTLE_ENDIAN= -Dmalloc=webrtc_malloc -Dcalloc=webrtc_calloc -Drealloc=webrtc_realloc -Dfree=webrtc_free\" +CFLAGS=\"-O2 ${_aecm_arch_cflags} -DNDEBUG -I${SOF_WEBRTC_APM_SRC_DIR} -I$SRC -I$SRC/common_audio/signal_processing/include -DWEBRTC_ARCH_32_BITS= -DWEBRTC_ARCH_LITTLE_ENDIAN= -Dmalloc=webrtc_malloc -Dcalloc=webrtc_calloc -Drealloc=webrtc_realloc -Dfree=webrtc_free\" # AECm core SRCS=\" diff --git a/src/audio/webrtc_ns/webrtc_ns.cmake b/src/audio/webrtc_ns/webrtc_ns.cmake index fb39b40e1793..771001fc8427 100644 --- a/src/audio/webrtc_ns/webrtc_ns.cmake +++ b/src/audio/webrtc_ns/webrtc_ns.cmake @@ -37,6 +37,16 @@ set(WEBRTC_NS_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/webrtc-ns-install" set(_ns_src "${SOF_WEBRTC_APM_SRC_DIR}/webrtc") +if("${ARCH}" STREQUAL "xtensa") + if(CONFIG_LLEXT_BUILD_PIC) + set(_ns_arch_cflags "-fPIC -mlongcalls -mtext-section-literals") + else() + set(_ns_arch_cflags "-fno-pic -fno-pie -mlongcalls -mtext-section-literals") + endif() +else() + set(_ns_arch_cflags "-fPIC") +endif() + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/webrtc-ns-build.sh" "#!/bin/sh set -e @@ -46,7 +56,7 @@ OBJ=${CMAKE_CURRENT_BINARY_DIR}/webrtc-ns-obj mkdir -p \"$OBJ\" \"$INST/lib\" \"$INST/include\" -CFLAGS=\"-O2 -fPIC -DWEBRTC_ARCH_32_BITS= -DWEBRTC_ARCH_LITTLE_ENDIAN= -I${SOF_WEBRTC_APM_SRC_DIR} -I$SRC/modules/audio_processing/ns/include -Dmalloc=webrtc_ns_malloc -Dcalloc=webrtc_ns_calloc -Dfree=webrtc_ns_free\" +CFLAGS=\"-O2 ${_ns_arch_cflags} -DNDEBUG -DWEBRTC_ARCH_32_BITS= -DWEBRTC_ARCH_LITTLE_ENDIAN= -I${SOF_WEBRTC_APM_SRC_DIR} -I$SRC/modules/audio_processing/ns/include -Dmalloc=webrtc_ns_malloc -Dcalloc=webrtc_ns_calloc -Dfree=webrtc_ns_free\" SRCS=\" $SRC/modules/audio_processing/ns/noise_suppression.c diff --git a/src/audio/webrtc_ns2/webrtc_ns2.cmake b/src/audio/webrtc_ns2/webrtc_ns2.cmake index b9c78c55e6ca..d6bd7dda5790 100644 --- a/src/audio/webrtc_ns2/webrtc_ns2.cmake +++ b/src/audio/webrtc_ns2/webrtc_ns2.cmake @@ -34,6 +34,16 @@ set(WEBRTC_NS2_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/rnnoise-install" set(_rnn_src "${SOF_RNNOISE_SRC_DIR}/src") set(_rnn_inc "${SOF_RNNOISE_SRC_DIR}/include") +if("${ARCH}" STREQUAL "xtensa") + if(CONFIG_LLEXT_BUILD_PIC) + set(_rnn_arch_cflags "-fPIC -mlongcalls -mtext-section-literals") + else() + set(_rnn_arch_cflags "-fno-pic -fno-pie -mlongcalls -mtext-section-literals") + endif() +else() + set(_rnn_arch_cflags "-fPIC") +endif() + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/rnnoise-build.sh" "#!/bin/sh set -e @@ -44,7 +54,7 @@ OBJ=${CMAKE_CURRENT_BINARY_DIR}/rnnoise-obj mkdir -p \"$OBJ\" \"$INST/lib\" \"$INST/include\" -CFLAGS=\"-O2 -fPIC -I$SRC -I$INC -DOUTSIDE_SPEEX -DRANDOM_PREFIX=rnn -DFLOAT_APPROX\" +CFLAGS=\"-O2 ${_rnn_arch_cflags} -DNDEBUG -I$SRC -I$INC -DOUTSIDE_SPEEX -DRANDOM_PREFIX=rnn -DFLOAT_APPROX\" # Core RNNoise sources (excludes dump_features.c and train-only files). SRCS=\" diff --git a/src/audio/webrtc_vad/webrtc_vad.cmake b/src/audio/webrtc_vad/webrtc_vad.cmake index d0124c7ded5f..49afd97c3cb4 100644 --- a/src/audio/webrtc_vad/webrtc_vad.cmake +++ b/src/audio/webrtc_vad/webrtc_vad.cmake @@ -39,6 +39,16 @@ set(LIBFVAD_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/libfvad-install" # Rather than invoking its CMakeLists (which would need a CMake toolchain file), # we compile the objects directly and archive them. This mirrors the libshine # approach in ffmpeg.cmake. +if("${ARCH}" STREQUAL "xtensa") + if(CONFIG_LLEXT_BUILD_PIC) + set(_fvad_arch_cflags "-fPIC -mlongcalls -mtext-section-literals") + else() + set(_fvad_arch_cflags "-fno-pic -fno-pie -mlongcalls -mtext-section-literals") + endif() +else() + set(_fvad_arch_cflags "-fPIC") +endif() + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/fvad-build.sh" "#!/bin/sh set -e @@ -66,7 +76,7 @@ SRCS=\" for f in \$SRCS; do bn=\$(basename \"\$f\" .c) - ${CMAKE_C_COMPILER} -O2 -fPIC \ + ${CMAKE_C_COMPILER} -O2 ${_fvad_arch_cflags} -DNDEBUG \ -I$SRC/include \ -I$SRC/src \ -c \"\$f\" -o \"$OBJ/\${bn}.o\" From 2d7c4d7b94f686f230ba006d2b30ab9039d6fec4 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Nov 2026 17:12:32 +0000 Subject: [PATCH 06/10] audio: webrtc: handle Zephyr LL_EXTENSION_BUILD macro in module shims Zephyr sets LL_EXTENSION_BUILD rather than LLEXT_MODULE when compiling relocatable LLEXT sources; check both macros in the WebRTC shim files so malloc/free wrappers and non-userspace BSS placement match the build mode. Also remove the unused include and k_panic() call from webrtc_aec-shims.c. Signed-off-by: Liam Girdwood --- src/audio/webrtc_aec/webrtc_aec-shims.c | 6 ++---- src/audio/webrtc_ns/webrtc_ns-shims.c | 4 ++-- src/audio/webrtc_ns2/webrtc_ns2-shims.c | 6 +++--- src/audio/webrtc_vad/webrtc_vad-shims.c | 4 ++-- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/audio/webrtc_aec/webrtc_aec-shims.c b/src/audio/webrtc_aec/webrtc_aec-shims.c index 868ecfb9e9fd..9943445043a7 100644 --- a/src/audio/webrtc_aec/webrtc_aec-shims.c +++ b/src/audio/webrtc_aec/webrtc_aec-shims.c @@ -11,8 +11,7 @@ #include #include #include -#include -#if !defined(LLEXT_MODULE) +#if !defined(LLEXT_MODULE) && !defined(LL_EXTENSION_BUILD) #include static APP_SYSUSER_BSS struct processing_module *active_aec_mod; #else @@ -73,7 +72,6 @@ void *webrtc_realloc(void *ptr, size_t size) return NULL; } - /* Not called by AEC3 */ - k_panic(); + /* Not called by AECm/AEC3 */ return NULL; } diff --git a/src/audio/webrtc_ns/webrtc_ns-shims.c b/src/audio/webrtc_ns/webrtc_ns-shims.c index 4f5b91362079..ecbe56b7858e 100644 --- a/src/audio/webrtc_ns/webrtc_ns-shims.c +++ b/src/audio/webrtc_ns/webrtc_ns-shims.c @@ -13,7 +13,7 @@ #include #include #include -#if !defined(LLEXT_MODULE) +#if !defined(LLEXT_MODULE) && !defined(LL_EXTENSION_BUILD) #include static APP_SYSUSER_BSS struct processing_module *active_ns_mod; #else @@ -60,7 +60,7 @@ void *webrtc_ns_calloc(size_t nmemb, size_t size) return p; } -#if defined(LLEXT_MODULE) +#if defined(LLEXT_MODULE) || defined(LL_EXTENSION_BUILD) /* ============================ Memory ============================ */ void *malloc(size_t size) diff --git a/src/audio/webrtc_ns2/webrtc_ns2-shims.c b/src/audio/webrtc_ns2/webrtc_ns2-shims.c index e6064d31e0e6..944c214c583a 100644 --- a/src/audio/webrtc_ns2/webrtc_ns2-shims.c +++ b/src/audio/webrtc_ns2/webrtc_ns2-shims.c @@ -9,7 +9,7 @@ #include #include #include -#if !defined(LLEXT_MODULE) +#if !defined(LLEXT_MODULE) && !defined(LL_EXTENSION_BUILD) #include static APP_SYSUSER_BSS struct processing_module *active_ns2_mod; #else @@ -56,7 +56,7 @@ void *webrtc_ns2_calloc(size_t nmemb, size_t size) return p; } -#if defined(LLEXT_MODULE) +#if defined(LLEXT_MODULE) || defined(LL_EXTENSION_BUILD) /* ============================ Memory ============================ */ void *malloc(size_t size) @@ -285,4 +285,4 @@ double exp(double x) { return (double)expf((float)x); } -#endif /* LLEXT_MODULE */ +#endif /* LLEXT_MODULE || LL_EXTENSION_BUILD */ diff --git a/src/audio/webrtc_vad/webrtc_vad-shims.c b/src/audio/webrtc_vad/webrtc_vad-shims.c index b8ac6860a2c5..9f6e6f821f66 100644 --- a/src/audio/webrtc_vad/webrtc_vad-shims.c +++ b/src/audio/webrtc_vad/webrtc_vad-shims.c @@ -17,7 +17,7 @@ #include #include #include -#if !defined(LLEXT_MODULE) +#if !defined(LLEXT_MODULE) && !defined(LL_EXTENSION_BUILD) #include static APP_SYSUSER_BSS struct processing_module *active_vad_mod; #else @@ -29,7 +29,7 @@ void webrtc_vad_set_active_mod(struct processing_module *mod) active_vad_mod = mod; } -#if defined(LLEXT_MODULE) +#if defined(LLEXT_MODULE) || defined(LL_EXTENSION_BUILD) /* ============================ Memory ============================ */ void *malloc(size_t size) From ac6109fd514b7862bdd5f6f3724660e2048583c5 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Nov 2026 17:12:32 +0000 Subject: [PATCH 07/10] audio: webrtc_ns, webrtc_ns2: add weak soft-float math shims Provide __attribute__((weak)) soft-float logf, log10f, expf, cosf, sinf, and atanf shims compiled unconditionally so both relocatable LLEXT and monolithic builds (e.g. CAVS 2.5 where CONFIG_LIBRARY_MANAGER=n) link without an external libm or duplicate symbol collisions. Implement log2f and exp2f via range reduction into [sqrt(2)/2, sqrt(2)] and [-0.5, 0.5] so inputs across the full positive domain stay accurate and continuous. Signed-off-by: Liam Girdwood --- src/audio/webrtc_ns/webrtc_ns-shims.c | 214 ++++++++++++++++++++++-- src/audio/webrtc_ns2/webrtc_ns2-shims.c | 73 ++++---- 2 files changed, 241 insertions(+), 46 deletions(-) diff --git a/src/audio/webrtc_ns/webrtc_ns-shims.c b/src/audio/webrtc_ns/webrtc_ns-shims.c index ecbe56b7858e..c19045f8fbc5 100644 --- a/src/audio/webrtc_ns/webrtc_ns-shims.c +++ b/src/audio/webrtc_ns/webrtc_ns-shims.c @@ -124,42 +124,51 @@ void *memmove(void *dest, const void *src, size_t n) */ -float fabsf(float x) +__attribute__((weak)) float fabsf(float x) { return (x < 0.0f) ? -x : x; } -double fabs(double x) +__attribute__((weak)) double fabs(double x) { return (x < 0.0) ? -x : x; } static inline float ns_exp2f(float x) { - if (x > 127.0f) return 3.40282347e+38f; - if (x < -126.0f) return 0.0f; - int n = (int)x; - if (x < 0.0f && (float)n != x) n--; - float f = x - (float)n; - float p = 1.0f + f * (0.6931472f + f * (0.2402265f + f * 0.0555041f)); union { float f; uint32_t i; } u; - if (n < -126) return 0.0f; - if (n > 127) n = 127; + float f, p; + int n; + + if (x >= 127.0f) + return 3.40282347e+38f; + if (x < -126.0f) + return 0.0f; + + n = (int)(x >= 0.0f ? x + 0.5f : x - 0.5f); + if (n > 127) + return 3.40282347e+38f; + if (n < -126) + return 0.0f; + + f = (x - (float)n) * 0.69314718f; + p = 1.0f + f * (1.0f + f * (0.5f + f * (0.16666667f + + f * (0.04166667f + f * (0.00833333f + f * 0.00138889f))))); u.i = (uint32_t)(n + 127) << 23; return p * u.f; } -float expf(float x) +__attribute__((weak)) float expf(float x) { return ns_exp2f(x * 1.4426950408889634f); } -double exp(double x) +__attribute__((weak)) double exp(double x) { return (double)expf((float)x); } -double tanh(double x) +__attribute__((weak)) double tanh(double x) { if (x > 10.0) return 1.0; if (x < -10.0) return -1.0; @@ -167,10 +176,179 @@ double tanh(double x) return (e2 - 1.0) / (e2 + 1.0); } -float atanf(float x) +__attribute__((weak)) float atanf(float x) +{ + float ax = fabsf(x); + float base = 0.0f; + float u, u2, res; + + if (ax > 2.41421356f) { + base = 1.5707963267948966f; + u = -1.0f / ax; + } else if (ax > 0.41421356f) { + base = 0.7853981633974483f; + u = (ax - 1.0f) / (ax + 1.0f); + } else { + u = ax; + } + + u2 = u * u; + res = base + u * (1.0f + u2 * (-0.33333333f + u2 * (0.20000000f + + u2 * (-0.14285714f + u2 * 0.11111111f)))); + return (x < 0.0f) ? -res : res; +} + +static float ns_log2f(float x) +{ + union { float f; uint32_t i; } u = { .f = x }; + int e, e_bias = 127; + float m, r, s, poly; + + if (x <= 0.0f) + return -127.0f; + + if (u.i < 0x00800000U) { + x *= 16777216.0f; + u.f = x; + e_bias = 151; + } + + e = (int)((u.i >> 23) & 0xff) - e_bias; + u.i = (u.i & 0x007fffff) | 0x3f800000; + m = u.f; + if (m > 1.4142135623730951f) { + m *= 0.5f; + e++; + } + + r = (m - 1.0f) / (m + 1.0f); + s = r * r; + poly = 1.0f + s * (0.33333333f + s * (0.20000000f + + s * (0.14285714f + s * 0.11111111f))); + + return (float)e + (2.8853900817779268f * r) * poly; +} + +__attribute__((weak)) float logf(float x) +{ + return ns_log2f(x) * 0.6931471805599453f; +} + +__attribute__((weak)) double log(double x) +{ + return (double)logf((float)x); +} + +__attribute__((weak)) float sqrtf(float x) +{ + if (x <= 0.0f) + return 0.0f; + return __builtin_sqrtf(x); +} + +__attribute__((weak)) double sqrt(double x) +{ + return (double)sqrtf((float)x); +} + +__attribute__((weak)) float powf(float x, float y) +{ + if (x <= 0.0f) + return (x == 0.0f && y > 0.0f) ? 0.0f : 1.0f; + if (y == 0.0f) + return 1.0f; + if (y == 1.0f) + return x; + if (y == 2.0f) + return x * x; + if (y == 0.5f) + return sqrtf(x); + return ns_exp2f(y * ns_log2f(x)); +} + +static inline float ns_floorf(float x) +{ + int i = (int)x; + + if (x < 0.0f && (float)i != x) + i--; + return (float)i; +} + +__attribute__((weak)) float floorf(float x) +{ + return ns_floorf(x); +} + +__attribute__((weak)) double floor(double x) +{ + return (double)ns_floorf((float)x); +} + +static inline float ns_ceilf(float x) +{ + int i = (int)x; + + if (x > 0.0f && (float)i != x) + i++; + return (float)i; +} + +static inline float ns_roundf(float x) +{ + return (x >= 0.0f) ? ns_floorf(x + 0.5f) : ns_ceilf(x - 0.5f); +} + +static inline void ns_sincosf_poly(float z, float *s_out, float *c_out) +{ + float z2 = z * z; + + *s_out = z * (1.0f + z2 * (-0.166666667f + z2 * (0.008333333f - z2 * 0.0001984127f))); + *c_out = 1.0f + z2 * (-0.5f + z2 * (0.041666667f + z2 * (-0.0013888889f + z2 * 0.0000248016f))); +} + +__attribute__((weak)) float sinf(float x) +{ + const float inv_half_pi = 0.636619772367581343f; + const float half_pi_hi = 1.5707963267948966f; + int k = (int)ns_roundf(x * inv_half_pi); + float z = x - (float)k * half_pi_hi; + float s, c; + + ns_sincosf_poly(z, &s, &c); + + switch ((k % 4 + 4) % 4) { + case 0: return s; + case 1: return c; + case 2: return -s; + case 3: default: return -c; + } +} + +__attribute__((weak)) double sin(double x) +{ + return (double)sinf((float)x); +} + +__attribute__((weak)) float cosf(float x) +{ + const float inv_half_pi = 0.636619772367581343f; + const float half_pi_hi = 1.5707963267948966f; + int k = (int)ns_roundf(x * inv_half_pi); + float z = x - (float)k * half_pi_hi; + float s, c; + + ns_sincosf_poly(z, &s, &c); + + switch ((k % 4 + 4) % 4) { + case 0: return c; + case 1: return -s; + case 2: return -c; + case 3: default: return s; + } +} + +__attribute__((weak)) double cos(double x) { - if (x == 1.0f) - return 0.7853981633974483f; - float x2 = x * x; - return x * (1.0f + 0.28f * x2) / (1.0f + 0.613f * x2); + return (double)cosf((float)x); } diff --git a/src/audio/webrtc_ns2/webrtc_ns2-shims.c b/src/audio/webrtc_ns2/webrtc_ns2-shims.c index 944c214c583a..9fe0ad0a5558 100644 --- a/src/audio/webrtc_ns2/webrtc_ns2-shims.c +++ b/src/audio/webrtc_ns2/webrtc_ns2-shims.c @@ -111,6 +111,7 @@ void *memmove(void *dest, const void *src, size_t n) } return dest; } +#endif /* LLEXT_MODULE || LL_EXTENSION_BUILD */ /* ============================ Math ============================== */ @@ -129,31 +130,45 @@ static inline float ns2_ldexp2(int n) static float ns2_log2f(float x) { union { float f; uint32_t i; } u = { .f = x }; - int e; - float m, z; + int e, e_bias = 127; + float m, r, s, poly; if (x <= 0.0f) return -127.0f; - e = (int)((u.i >> 23) & 0xff) - 127; + if (u.i < 0x00800000U) { + x *= 16777216.0f; + u.f = x; + e_bias = 151; + } + + e = (int)((u.i >> 23) & 0xff) - e_bias; u.i = (u.i & 0x007fffff) | 0x3f800000; m = u.f; - z = m - 1.0f; + if (m > 1.4142135623730951f) { + m *= 0.5f; + e++; + } + + r = (m - 1.0f) / (m + 1.0f); + s = r * r; + poly = 1.0f + s * (0.33333333f + s * (0.20000000f + + s * (0.14285714f + s * 0.11111111f))); - return (float)e + z * (1.44269504f - z * (0.72134752f - z * 0.48089834f)); + return (float)e + (2.8853900817779268f * r) * poly; } -float log10f(float x) +__attribute__((weak)) float log10f(float x) { return ns2_log2f(x) * 0.3010299956639812f; } -double log10(double x) +__attribute__((weak)) double log10(double x) { return (double)log10f((float)x); } -float floorf(float x) +__attribute__((weak)) float floorf(float x) { int i = (int)x; @@ -162,12 +177,12 @@ float floorf(float x) return (float)i; } -double floor(double x) +__attribute__((weak)) double floor(double x) { return (double)floorf((float)x); } -float ceilf(float x) +__attribute__((weak)) float ceilf(float x) { int i = (int)x; @@ -181,24 +196,24 @@ static inline float ns2_roundf(float x) return (x >= 0.0f) ? floorf(x + 0.5f) : ceilf(x - 0.5f); } -float fabsf(float x) +__attribute__((weak)) float fabsf(float x) { return (x < 0.0f) ? -x : x; } -double fabs(double x) +__attribute__((weak)) double fabs(double x) { return (x < 0.0) ? -x : x; } -float sqrtf(float x) +__attribute__((weak)) float sqrtf(float x) { if (x <= 0.0f) return 0.0f; return __builtin_sqrtf(x); } -double sqrt(double x) +__attribute__((weak)) double sqrt(double x) { return (double)sqrtf((float)x); } @@ -211,7 +226,7 @@ static inline void ns2_sincosf_poly(float z, float *s_out, float *c_out) *c_out = 1.0f + z2 * (-0.5f + z2 * (0.041666667f + z2 * (-0.0013888889f + z2 * 0.0000248016f))); } -float sinf(float x) +__attribute__((weak)) float sinf(float x) { const float inv_half_pi = 0.636619772367581343f; const float half_pi_hi = 1.5707963267948966f; @@ -229,12 +244,12 @@ float sinf(float x) } } -double sin(double x) +__attribute__((weak)) double sin(double x) { return (double)sinf((float)x); } -float cosf(float x) +__attribute__((weak)) float cosf(float x) { const float inv_half_pi = 0.636619772367581343f; const float half_pi_hi = 1.5707963267948966f; @@ -252,37 +267,39 @@ float cosf(float x) } } -double cos(double x) +__attribute__((weak)) double cos(double x) { return (double)cosf((float)x); } -float exp2f(float x) +__attribute__((weak)) float exp2f(float x) { int n; float f, p; - if (x > 127.0f) + if (x >= 127.0f) return 3.40282347e+38f; if (x < -126.0f) return 0.0f; - n = (int)x; - if (x < 0.0f && (float)n != x) - n--; - f = x - (float)n; + n = (int)(x >= 0.0f ? x + 0.5f : x - 0.5f); + if (n > 127) + return 3.40282347e+38f; + if (n < -126) + return 0.0f; - p = 1.0f + f * (0.6931472f + f * (0.2402265f + f * 0.0555041f)); + f = (x - (float)n) * 0.69314718f; + p = 1.0f + f * (1.0f + f * (0.5f + f * (0.16666667f + + f * (0.04166667f + f * (0.00833333f + f * 0.00138889f))))); return p * ns2_ldexp2(n); } -float expf(float x) +__attribute__((weak)) float expf(float x) { return exp2f(x * 1.4426950408889634f); } -double exp(double x) +__attribute__((weak)) double exp(double x) { return (double)expf((float)x); } -#endif /* LLEXT_MODULE || LL_EXTENSION_BUILD */ From 709c771dd329328fa11700afb815a4c9561a0b68 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Nov 2026 17:12:32 +0000 Subject: [PATCH 08/10] lib: notifier: expose notifier_event as validated Zephyr syscall When CONFIG_USERSPACE is enabled, webrtc_vad runs in a user thread and invokes notifier_event() on VAD state changes. Instead of exporting raw notifier_event() directly to LLEXT/userspace (where host_notify state is not partitioned for user access), expose notifier_event() via a Zephyr __syscall wrapper and validate the event type and caller buffer in z_vrfy_notifier_event(). Signed-off-by: Liam Girdwood --- src/include/sof/lib/notifier.h | 13 +++++++++++-- src/lib/notifier.c | 5 +++-- zephyr/CMakeLists.txt | 3 +++ zephyr/syscall/notifier.c | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 zephyr/syscall/notifier.c diff --git a/src/include/sof/lib/notifier.h b/src/include/sof/lib/notifier.h index e5413a512cb4..cf93ab359d55 100644 --- a/src/include/sof/lib/notifier.h +++ b/src/include/sof/lib/notifier.h @@ -88,8 +88,17 @@ void notifier_notify_remote(void); /* data_size is required to manage cache coherency for notifications * across cores. */ -void notifier_event(const void *caller_id, enum notify_id event_type, uint32_t core_mask, - void *caller_data, uint32_t data_size); +#if defined(__ZEPHYR__) && defined(CONFIG_SOF_FULL_ZEPHYR_APPLICATION) +#include + +__syscall void notifier_event(const void *caller_id, enum notify_id event_type, + uint32_t core_mask, void *caller_data, uint32_t data_size); +#include +#else +void z_impl_notifier_event(const void *caller_id, enum notify_id event_type, + uint32_t core_mask, void *caller_data, uint32_t data_size); +#define notifier_event z_impl_notifier_event +#endif void init_system_notify(struct sof *sof); diff --git a/src/lib/notifier.c b/src/lib/notifier.c index 16b1df0dba3d..ee52343bd95e 100644 --- a/src/lib/notifier.c +++ b/src/lib/notifier.c @@ -146,8 +146,8 @@ void notifier_notify_remote(void) } -void notifier_event(const void *caller, enum notify_id type, uint32_t core_mask, - void *data, uint32_t data_size) +void z_impl_notifier_event(const void *caller, enum notify_id type, uint32_t core_mask, + void *data, uint32_t data_size) { struct notify_data *notify_data; struct idc_msg notify_msg = { IDC_MSG_NOTIFY, IDC_MSG_NOTIFY_EXT }; @@ -178,6 +178,7 @@ void notifier_event(const void *caller, enum notify_id type, uint32_t core_mask, } } } +EXPORT_SYMBOL(z_impl_notifier_event); __cold void init_system_notify(struct sof *sof) { diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 8da2dc47fdc5..f8434eaa1907 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -629,6 +629,9 @@ zephyr_library_sources_ifdef(CONFIG_USERSPACE syscall/dai.c) zephyr_syscall_header(${SOF_SRC_PATH}/include/user/debug_stream_slot.h) +zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/lib/notifier.h) +zephyr_library_sources_ifdef(CONFIG_USERSPACE syscall/notifier.c) + zephyr_syscall_header(${SOF_SRC_PATH}/include/ipc4/notification.h) zephyr_library_link_libraries(SOF) diff --git a/zephyr/syscall/notifier.c b/zephyr/syscall/notifier.c new file mode 100644 index 000000000000..7f385e56769a --- /dev/null +++ b/zephyr/syscall/notifier.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +#include +#include +#include + +/** + * \brief Userspace verification wrapper for notifier_event(). + * + * Validates the notification type and verifies read access to the caller + * payload buffer before delegating dispatch to supervisor context so user + * threads never dereference kernel-only host_notify state. + */ +static inline void z_vrfy_notifier_event(const void *caller_id, + enum notify_id event_type, + uint32_t core_mask, + void *caller_data, + uint32_t data_size) +{ + K_OOPS(K_SYSCALL_VERIFY(event_type >= NOTIFIER_ID_CPU_FREQ && + event_type < NOTIFIER_ID_COUNT)); + K_OOPS(K_SYSCALL_VERIFY((!caller_data && !data_size) || + (caller_data && data_size))); + if (caller_data && data_size) + K_OOPS(K_SYSCALL_MEMORY_READ(caller_data, data_size)); + + z_impl_notifier_event(caller_id, event_type, core_mask, + caller_data, data_size); +} +#include From 368b5e8488b4bcf90200e5a604f794b93cc760e8 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Nov 2026 17:12:32 +0000 Subject: [PATCH 09/10] audio: webrtc_aec: enable init_config ext and guard input_pins access Set init_config = "1" in webrtc_aec.toml so topology generates the ext init_config metadata expected by IPC4 module setup, and guard input_pins[1] access with nb_input_pins > 1 && input_pins != NULL in webrtc_aec_prepare() so single-source preparation without a second pin descriptor cannot dereference past input_pins. Signed-off-by: Liam Girdwood --- src/audio/webrtc_aec/webrtc_aec.c | 3 ++- src/audio/webrtc_aec/webrtc_aec.toml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/audio/webrtc_aec/webrtc_aec.c b/src/audio/webrtc_aec/webrtc_aec.c index 4c3f8f027a27..d6c8b86283aa 100644 --- a/src/audio/webrtc_aec/webrtc_aec.c +++ b/src/audio/webrtc_aec/webrtc_aec.c @@ -313,7 +313,8 @@ __cold static int webrtc_aec_prepare(struct processing_module *mod, #ifdef CONFIG_IPC_MAJOR_4 /* Apply reference format override from topology pin descriptor. */ - if (cd->ref_src >= 0) + if (cd->ref_src >= 0 && mod->priv.cfg.nb_input_pins > 1 && + mod->priv.cfg.input_pins) ipc4_update_source_format(sources[cd->ref_src], &mod->priv.cfg.input_pins[1].audio_fmt); #endif diff --git a/src/audio/webrtc_aec/webrtc_aec.toml b/src/audio/webrtc_aec/webrtc_aec.toml index a6f10aa3cb4c..7a72ac57c5f1 100644 --- a/src/audio/webrtc_aec/webrtc_aec.toml +++ b/src/audio/webrtc_aec/webrtc_aec.toml @@ -12,6 +12,7 @@ domain_types = "1" load_type = LOAD_TYPE module_type = "9" + init_config = "1" auto_start = "0" sched_caps = [1, 0x00008000] REM # pin = [dir, type, sample rate, size, container, channel-cfg] From 201de5687c495ae837d5fe8b59084dbdc581afcf Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Nov 2026 17:12:32 +0000 Subject: [PATCH 10/10] audio: webrtc_aec: dynamically resolve ref source and drop excess frames Re-resolve mic and playback-reference sources in webrtc_aec_process() and webrtc_aec_is_ready_to_process() because IPC4 playback pipelines may bind or unbind dynamically while capture is active, and drop excess reference frames so playback backpressure does not stall capture or overflow the reference ring buffer. Signed-off-by: Liam Girdwood --- src/audio/webrtc_aec/webrtc_aec.c | 145 +++++++++++++++++++++++------- 1 file changed, 113 insertions(+), 32 deletions(-) diff --git a/src/audio/webrtc_aec/webrtc_aec.c b/src/audio/webrtc_aec/webrtc_aec.c index d6c8b86283aa..482aac572239 100644 --- a/src/audio/webrtc_aec/webrtc_aec.c +++ b/src/audio/webrtc_aec/webrtc_aec.c @@ -168,6 +168,67 @@ static void s16_to_sink(struct sof_sink *dst, int16_t src[][WEBRTC_AEC_FIFO_FRAM sink_commit_buffer(dst, nbytes); } +static void src_drop_frames(struct sof_source *src, int frames) +{ + size_t frame_bytes = source_get_frame_bytes(src); + const void *data_ptr, *buf_start; + size_t buf_size, nbytes; + + if (frames <= 0 || !frame_bytes) + return; + + nbytes = (size_t)frames * frame_bytes; + if (!source_get_data(src, nbytes, &data_ptr, &buf_start, &buf_size)) + source_release_data(src, nbytes); +} + +static void webrtc_aec_resolve_sources(struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks, + int *mic_idx, int *ref_idx) +{ + *mic_idx = 0; + *ref_idx = -1; + + if (num_of_sources <= 1) + return; + +#ifdef CONFIG_IPC_MAJOR_4 + { + uint32_t q0 = IPC4_SINK_QUEUE_ID(source_get_id(sources[0])); + uint32_t q1 = IPC4_SINK_QUEUE_ID(source_get_id(sources[1])); + + if (q0 != q1) { + if (q0 == 0) { + *mic_idx = 0; + *ref_idx = 1; + } else { + *mic_idx = 1; + *ref_idx = 0; + } + return; + } + } +#endif + + if (num_of_sinks > 0) { + if (source_get_pipeline_id(sources[0]) == sink_get_pipeline_id(sinks[0]) && + source_get_pipeline_id(sources[1]) != sink_get_pipeline_id(sinks[0])) { + *mic_idx = 0; + *ref_idx = 1; + return; + } + if (source_get_pipeline_id(sources[1]) == sink_get_pipeline_id(sinks[0]) && + source_get_pipeline_id(sources[0]) != sink_get_pipeline_id(sinks[0])) { + *mic_idx = 1; + *ref_idx = 0; + return; + } + } + + *mic_idx = 0; + *ref_idx = 1; +} + /* ---------------------------------------------------------------- * module_interface operations * ---------------------------------------------------------------- */ @@ -226,21 +287,8 @@ __cold static int webrtc_aec_prepare(struct processing_module *mod, return -EINVAL; } - if (num_of_sources == 1) { - cd->mic_src = 0; - cd->ref_src = -1; - } else if (source_get_pipeline_id(sources[0]) == sink_get_pipeline_id(sinks[0]) && - source_get_pipeline_id(sources[1]) != sink_get_pipeline_id(sinks[0])) { - cd->mic_src = 0; - cd->ref_src = 1; - } else if (source_get_pipeline_id(sources[1]) == sink_get_pipeline_id(sinks[0]) && - source_get_pipeline_id(sources[0]) != sink_get_pipeline_id(sinks[0])) { - cd->mic_src = 1; - cd->ref_src = 0; - } else { - cd->mic_src = 0; - cd->ref_src = 1; - } + webrtc_aec_resolve_sources(sources, num_of_sources, sinks, num_of_sinks, + &cd->mic_src, &cd->ref_src); mic_fmt = source_get_frm_fmt(sources[cd->mic_src]); mic_rate = source_get_rate(sources[cd->mic_src]); @@ -344,29 +392,52 @@ static int webrtc_aec_process(struct processing_module *mod, struct sof_sink **sinks, int num_of_sinks) { struct webrtc_aec_comp_data *cd = module_get_private_data(mod); - struct sof_source *mic = sources[cd->mic_src]; - struct sof_source *ref = cd->ref_src >= 0 ? sources[cd->ref_src] : NULL; - struct sof_sink *out = sinks[0]; - int fmic = (int)source_get_data_frames_available(mic); - int fref = ref ? (int)source_get_data_frames_available(ref) : 0; - int n, drain, c; - - if (fmic <= 0) + struct sof_source *mic; + struct sof_source *ref; + struct sof_sink *out; + int mic_idx, ref_idx; + int fmic, fref, n, drain, c; + bool ref_s32; + + if (num_of_sources < 1 || num_of_sinks < 1) return 0; - /* Cap to available space in input accumulation buffers */ + webrtc_aec_resolve_sources(sources, num_of_sources, sinks, num_of_sinks, + &mic_idx, &ref_idx); + cd->mic_src = mic_idx; + cd->ref_src = ref_idx; + + mic = sources[mic_idx]; + ref = ref_idx >= 0 ? sources[ref_idx] : NULL; + out = sinks[0]; + fmic = (int)source_get_data_frames_available(mic); + fref = ref ? (int)source_get_data_frames_available(ref) : 0; + + /* Cap to available space in input accumulation and output sink buffers */ n = MIN(fmic, WEBRTC_AEC_FIFO_FRAMES - cd->buffered_in_frames); - if (n <= 0) + n = MIN(n, (int)sink_get_free_frames(out)); + if (n <= 0) { + if (ref && fref > 0) { + size_t fb = source_get_frame_bytes(ref); + int keep = fb ? (int)(source_get_min_available(ref) / fb) : 0; + + if (fref > keep) + src_drop_frames(ref, fref - keep); + } return 0; + } /* Convert mic input to per-channel S16 */ src_to_s16(mic, n, cd->mic_buf, cd->buffered_in_frames, cd->channels, cd->is_s32); /* Convert ref input to per-channel S16; if starved/silent, fill with silence */ + ref_s32 = ref ? (source_get_frm_fmt(ref) == SOF_IPC_FRAME_S32_LE) : cd->is_s32; if (fref >= n) { - src_to_s16(ref, n, cd->ref_buf, cd->buffered_in_frames, cd->channels, cd->is_s32); + if (fref > n) + src_drop_frames(ref, fref - n); + src_to_s16(ref, n, cd->ref_buf, cd->buffered_in_frames, cd->channels, ref_s32); } else if (fref > 0) { - src_to_s16(ref, fref, cd->ref_buf, cd->buffered_in_frames, cd->channels, cd->is_s32); + src_to_s16(ref, fref, cd->ref_buf, cd->buffered_in_frames, cd->channels, ref_s32); for (c = 0; c < cd->channels; c++) memset(&cd->ref_buf[c][cd->buffered_in_frames + fref], 0, (size_t)(n - fref) * sizeof(int16_t)); @@ -649,17 +720,27 @@ static bool webrtc_aec_is_ready_to_process(struct processing_module *mod, int num_of_sinks) { struct webrtc_aec_comp_data *cd = module_get_private_data(mod); - struct sof_source *mic; + struct sof_source *mic, *ref; struct sof_sink *out; + int mic_idx, ref_idx; - if (!cd || !cd->configured || num_of_sources <= cd->mic_src || num_of_sinks < 1) + if (!cd || !cd->configured || num_of_sources < 1 || num_of_sinks < 1) return false; - mic = sources[cd->mic_src]; + webrtc_aec_resolve_sources(sources, num_of_sources, sinks, num_of_sinks, + &mic_idx, &ref_idx); + mic = sources[mic_idx]; + ref = ref_idx >= 0 ? sources[ref_idx] : NULL; out = sinks[0]; - return source_get_data_available(mic) >= source_get_min_available(mic) && - sink_get_free_size(out) >= sink_get_min_free_space(out); + if (source_get_data_available(mic) >= source_get_min_available(mic) && + sink_get_free_size(out) >= sink_get_min_free_space(out)) + return true; + + if (ref && source_get_data_available(ref) >= 2 * source_get_min_available(ref)) + return true; + + return false; } static const struct module_interface webrtc_aec_interface = {