### ALSA virtual device which combines recording and playback loopback for AEC processing # https://github.com/sanebow/alsa-aec # sanebow@gmail.com # Version: 0.3 (20210506) defaults.pcm.aec.playback_hw.card defaults.pcm.card # playback hw card defaults.pcm.aec.playback_hw.rate 48000 # sample rate supported by the playback card defaults.pcm.aec.capture_hw.card defaults.pcm.card # capture hw card defaults.pcm.aec.capture_hw.rate 16000 # sample rate supported by the capture card ## you may also directly set some PCM device as playback and capture device defaults.pcm.aec.playback_pcm "playback_hw" defaults.pcm.aec.capture_pcm "capture_hw" defaults.pcm.aec.pre_loopidx 4 # loopback subdevice index for stream before aec processing defaults.pcm.aec.post_loopidx 5 # loopback subdevice index for stream after aec processing pcm.playback_hw { @args [ CARD RATE ] @args.CARD { type string default { @func refer name defaults.pcm.aec.playback_hw.card } } @args.RATE { type integer default { @func refer name defaults.pcm.aec.playback_hw.rate } } type plug slave.rate "unchanged" slave.pcm { type rate slave.rate $RATE slave.pcm { type dmix ipc_key 10009 ipc_key_add_uid yes slave { pcm {@func concat strings [ "hw:" $CARD ] } } } } } pcm.capture_hw { @args [ CARD RATE ] @args.CARD { type string default { @func refer name defaults.pcm.aec.capture_hw.card } } @args.RATE { type integer default { @func refer name defaults.pcm.aec.capture_hw.rate } } type rate slave.rate $RATE slave.pcm { type dsnoop ipc_key 20009 ipc_key_add_uid yes slave { pcm {@func concat strings [ "hw:" $CARD ] } rate $RATE } bindings.0 0 } } pcm.loopback_out { @args [ SUBDEVICE ] @args.SUBDEVICE { type integer } type empty slave.pcm { type hw card "Loopback" device 0 subdevice $SUBDEVICE channels 1 rate 16000 format S16_LE } } pcm.loopback_in { @args [ SUBDEVICE ] @args.SUBDEVICE { type integer } type empty slave.pcm { type hw card "Loopback" device 1 subdevice $SUBDEVICE channels 1 rate 16000 format S16_LE } } # audio play to this device will be duplicated to hw (stereo) and loopback (mono) pcm.mloopplay { @args [ PCM LOOPIDX ] @args.PCM { type string default { @func refer name defaults.pcm.aec.playback_pcm } } @args.LOOPIDX { type integer default 4 } type route slave.pcm { type multi slaves.a.pcm $PCM slaves.a.channels 2 slaves.b.pcm { # loopback null sink @func concat strings [ "plug:loopback_out:" $LOOPIDX ] } slaves.b.channels 1 bindings.0 { slave a channel 0 } bindings.1 { slave a channel 1 } bindings.2 { slave b channel 0 } } slave.channels 3 ttable.0.0 1 ttable.1.1 1 ttable.0.2 0.5 ttable.1.2 0.5 } # combine mic (ch 0) and loopback (ch 1) to single recording pcm.mlooprec { @args [ PCM LOOPIDX ] @args.PCM { type string default { @func refer name defaults.pcm.aec.capture_pcm } } @args.LOOPIDX { type integer default 4 } type multi slaves.a.pcm $PCM slaves.a.channels 1 slaves.b.pcm { # loopback capturing interface type dsnoop # to align with capture_hw, avoid PortAudio error ipc_key 10099 slave.pcm { @func concat strings [ "loopback_in:" $LOOPIDX ] } } slaves.b.channels 1 bindings.0 { slave a channel 0 } bindings.1 { slave b channel 0 } } pcm.aec_internal { @args [ CAPPCM PRELOOPIDX POSTLOOPIDX ] @args.CAPPCM { type string default { @func refer name defaults.pcm.aec.capture_pcm } } @args.PRELOOPIDX { type integer default { @func refer name defaults.pcm.aec.pre_loopidx } } @args.POSTLOOPIDX { type integer default { @func refer name defaults.pcm.aec.post_loopidx } } type asym capture.pcm { # -i for aec script @func concat strings [ "mlooprec:" $CAPPCM "," $PRELOOPIDX ] } playback.pcm { # -o for aec script @func concat strings [ "loopback_out:" $POSTLOOPIDX ] } } pcm.aec { @args [ PLYPCM PRELOOPIDX POSTLOOPIDX ] @args.PLYPCM { type string default { @func refer name defaults.pcm.aec.playback_pcm } } @args.PRELOOPIDX { type integer default { @func refer name defaults.pcm.aec.pre_loopidx } } @args.POSTLOOPIDX { type integer default { @func refer name defaults.pcm.aec.post_loopidx } } type asym playback.pcm { @func concat strings [ "mloopplay:" $PLYPCM "," $PRELOOPIDX ] } capture.pcm { @func concat strings [ "loopback_in:" $POSTLOOPIDX ] } }