Open9

ec - Echo Canceller の試行錯誤

PINTOPINTO

エコーキャンセルプログラムのコンパイル

sudo apt-get update
sudo apt-get install -y \
libasound2-dev \
libwebrtc-audio-processing-dev \
libspeexdsp-dev

git clone https://github.com/voice-engine/ec.git
cd ec
git checkout abef2fcfeee895c77cfe913c6ef9fca8ab4b236a
make

マイクのデバイス一覧の確認

arecord -l

スピーカーのデバイス一覧の確認

aplay -l

マイクの対応サンプリングレートを確認
例えば、

card 3: Microphone [USB Microphone], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

のとき

arecord -D hw:3,0 --dump-hw-params

ACCESS:  MMAP_INTERLEAVED RW_INTERLEAVED
FORMAT:  S16_LE
SUBFORMAT:  STD
SAMPLE_BITS: 16
FRAME_BITS: 32
CHANNELS: 2
RATE: [8000 48000]
PERIOD_TIME: [1000 1000000]
PERIOD_SIZE: [16 48000]
PERIOD_BYTES: [64 192000]
PERIODS: [2 1024]
BUFFER_TIME: (666 2000000]
BUFFER_SIZE: [32 96000]
BUFFER_BYTES: [128 384000]
TICK_TIME: ALL

対応サンプリングレート 8000〜48000 の範囲、サンプルビットレート 16bit のみ

スピーカーの対応サンプリングレートを確認
例えば、

card 4: M3 [EMEET OfficeCore M3], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

のとき

aplay -D hw:4,0 --dump-hw-params /usr/share/sounds/alsa/Front_Center.wav

ACCESS:  MMAP_INTERLEAVED RW_INTERLEAVED
FORMAT:  S16_LE
SUBFORMAT:  STD
SAMPLE_BITS: 16
FRAME_BITS: 32
CHANNELS: 2
RATE: 48000
PERIOD_TIME: [1000 1000000]
PERIOD_SIZE: [48 48000]
PERIOD_BYTES: [192 192000]
PERIODS: [2 1024]
BUFFER_TIME: [2000 2000000]
BUFFER_SIZE: [96 96000]
BUFFER_BYTES: [384 384000]
TICK_TIME: ALL

対応サンプリングレート 48000 のみ、サンプルビットレート 16bit のみ

ノイズ音声の生成 16bit 音声じゃないとダメ

sox -n -r 48000 -c 2 -b 16 noise.wav synth 10 whitenoise vol 0.01

ノイズ音声の再生テスト

aplay -D plughw:4,0 noise.wav
PINTOPINTO

マイクとスピーカーのハードウェア情報を確認した結果、マイクとスピーカーの両方が 48kHz のサンプリングレートをサポートしていることが分かりました。次に、ec コマンドを使用してエコーキャンセルを正しく行うために、必要な設定と流れを整理します。

全体の流れ

  1. マイクとスピーカーのサンプリングレートの確認

    • マイクは 8000Hz から 48000Hz までの範囲のサンプリングレートをサポート。
    • スピーカーは 48000Hz 固定でサポート。
    • したがって、エコーキャンセルの処理では 48kHz (48000Hz) のサンプリングレートを使用する必要があります。
  2. .asoundrc の設定
    ALSA でマイクとスピーカーを多重利用するため、dmixdsnoop の設定を .asoundrc ファイルに追加します。次のようにサンプリングレートを 48000 に設定します。

    ~/.asoundrc ファイルの内容:

    pcm.dmix_output {
        type dmix
        ipc_key 1024
        slave {
            pcm "hw:4,0"  # スピーカーのデバイス
            rate 48000    # サンプリングレート 48kHz
        }
    }
    
    pcm.dsnoop_input {
        type dsnoop
        ipc_key 1025
        slave {
            pcm "hw:3,0"  # マイクのデバイス
            rate 48000    # サンプリングレート 48kHz
        }
    }
    
    pcm.!default {
        type asym
        playback.pcm "dmix_output"
        capture.pcm "dsnoop_input"
    }
    
    • dmix_output: スピーカー(hw:4,0)に対する出力デバイスの設定。サンプリングレートは 48000Hz
    • dsnoop_input: マイク(hw:3,0)に対する入力デバイスの設定。サンプリングレートは 48000Hz
  3. サウンド設定の再読み込み
    .asoundrc ファイルの変更を反映させるために、次のコマンドを実行して ALSA の設定を再読み込みします。

    sudo alsactl nrestore
    
  4. スピーカーのテスト
    スピーカーから正しく音声が出力されるかをテストします。次のコマンドで、システム内のテストサウンドを再生して、スピーカーの動作確認を行います。

    aplay -D plughw:4,0 /usr/share/sounds/alsa/Front_Center.wav
    

    もしこれで音声が出力されれば、スピーカーは正常に動作しています。

  5. エコーキャンセルの設定
    ec コマンドを使用して、マイクの音声をスピーカーからの出力音声からエコーキャンセルします。以下のコマンドを使用してエコーキャンセルを実行します。

    ./ec -i dsnoop_input -o dmix_output -r 48000 -c 2 -s
    
    • -i dsnoop_input: マイク(dsnoop_input)から音声を入力。
    • -o dmix_output: スピーカー(dmix_output)に音声を出力。
    • -r 48000: サンプリングレートは 48000Hz
    • -c 2: ステレオ(2チャンネル)設定。
    • -s: システムモードで実行。
  6. 動作確認

    • スピーカーからノイズ音声や音楽などを再生しつつ、マイクで音声を入力し、エコーキャンセルが正しく動作しているかを確認します。

    スピーカーでノイズを再生するためには、次のようにステレオのノイズ音声を作成し、再生します。

    sox -n -r 48000 -c 2 noise.wav synth 10 whitenoise vol 0.01
    aplay -D plughw:4,0 noise.wav
    

    この設定で、スピーカーとマイクのサンプリングレートを 48kHz に揃えてエコーキャンセルが実行されるはずです。

まとめ

  • マイクとスピーカーは両方とも 48kHz をサポートしているため、サンプリングレートを 48kHz に揃える必要があります。
  • .asoundrc ファイルで dmixdsnoop を設定してデバイスの多重利用を可能にし、ec コマンドでエコーキャンセルを実行します。
  • スピーカーでノイズを再生しながら、マイクで音声を入力し、正しくエコーキャンセルが行われているか確認します。

この流れに従ってエコーキャンセルを設定・確認すれば、問題なく動作するはずです。

PINTOPINTO

エラーメッセージ「src/audio.c:85: set_params: Assertion 'err >= 0' failed」は、ec プログラムがオーディオデバイスのパラメータ設定に失敗したことを示しています。これは主に、オーディオデバイスのサンプリングレートやチャンネル数がサポートされていないか、ALSA 設定に何か競合がある可能性があります。

次に、問題を解決するために考えられる対処法をいくつか示します。

1. plughw を使って再試行

ALSA の plug プラグインを使うことで、サンプリングレートやフォーマットの自動変換が行われるため、エラーを回避できる場合があります。

次のように、plughw デバイスを指定して ec を実行してみてください。

./ec -i plughw:3,0 -o plughw:4,0 -r 48000 -c 2 -s

これで、サンプリングレートやチャンネルのフォーマットが自動的に調整され、エラーが解消されるかを確認します。

2. .asoundrc 設定の見直し

.asoundrc の設定で、dsnoopdmix の設定が正しくされているか再度確認してみましょう。特に rate の設定が正しく行われているか確認してください。

pcm.dmix_output {
    type plug
    slave {
        pcm "hw:4,0"
        rate 48000
    }
}

pcm.dsnoop_input {
    type plug
    slave {
        pcm "hw:3,0"
        rate 48000
    }
}

pcm.!default {
    type asym
    playback.pcm "dmix_output"
    capture.pcm "dsnoop_input"
}

ここで、type plug を設定することで、自動的にサンプリングレート変換が行われ、互換性の問題を軽減できます。これを設定した後、alsactl nrestore で ALSA 設定を再読み込みします。

3. PulseAudio の停止

PulseAudio が ALサウンド設定と競合している場合、これが原因で ec の実行に失敗していることがあります。次のコマンドで PulseAudio を一時停止してから、再度 ec を実行してみてください。

pulseaudio --kill
./ec -i dsnoop_input -o dmix_output -r 48000 -c 2 -s

これにより、PulseAudio が ALSA デバイスの管理を行わず、直接 ALSA がデバイスを管理する形になります。

4. ALSA デバイスの利用状況確認

ALSA デバイスが他のプロセスによって占有されている場合も、設定がうまくいかないことがあります。次のコマンドで、デバイスを使用しているプロセスを確認し、該当するプロセスがあれば終了させてください。

fuser -v /dev/snd/*

競合しているプロセスを特定したら、kill コマンドで終了させます。

5. 他のサンプリングレートでテスト

マイクやスピーカーが 48kHz をサポートしていても、特定の条件で問題が発生する場合があります。次に、他のサンプリングレート(44.1kHz)で試してみます。

./ec -i dsnoop_input -o dmix_output -r 44100 -c 2 -s

これで 44.1kHz のサンプリングレートでエコーキャンセルが動作するか確認してください。

6. デバッグログの確認

もし上記の手順でも問題が解決しない場合、dmesg コマンドを使って ALSA デバイスに関連するエラーメッセージを確認し、さらなる情報を得ることができます。

dmesg | grep -i snd

このログでデバイスのエラーメッセージや警告を確認し、必要に応じて対処します。

まとめ

  1. plughw を指定して自動変換を試す。
  2. .asoundrc の設定を見直し、plug プラグインを使用する。
  3. PulseAudio を一時停止して ALSA のみにする。
  4. 他のプロセスがデバイスを占有していないか確認。
  5. 44.1kHz のサンプリングレートで試す。
  6. dmesg でログを確認。

これらの手順を順に試して、エラーが解消されるか確認してみてください。

PINTOPINTO

方向性をいったん見直す。ALSAでループバックデバイスを作る。

aplay -l

**** List of PLAYBACK Hardware Devices ****
card 0: PCH [HDA Intel PCH], device 0: ALC892 Analog [ALC892 Analog]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: PCH [HDA Intel PCH], device 1: ALC892 Digital [ALC892 Digital]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: Microphone [USB Microphone], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 2: NVidia [HDA NVidia], device 3: HDMI 0 [HDMI 0]
  Subdevices: 0/1
  Subdevice #0: subdevice #0
card 2: NVidia [HDA NVidia], device 7: HDMI 1 [HDMI 1]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 2: NVidia [HDA NVidia], device 8: HDMI 2 [HDMI 2]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 2: NVidia [HDA NVidia], device 9: HDMI 3 [HDMI 3]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

sudo modprobe snd-aloop

aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: PCH [HDA Intel PCH], device 0: ALC892 Analog [ALC892 Analog]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: PCH [HDA Intel PCH], device 1: ALC892 Digital [ALC892 Digital]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: Microphone [USB Microphone], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 2: NVidia [HDA NVidia], device 3: HDMI 0 [HDMI 0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 2: NVidia [HDA NVidia], device 7: HDMI 1 [HDMI 1]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 2: NVidia [HDA NVidia], device 8: HDMI 2 [HDMI 2]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 2: NVidia [HDA NVidia], device 9: HDMI 3 [HDMI 3]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 4: Loopback [Loopback], device 0: Loopback PCM [Loopback PCM]
  Subdevices: 7/8
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
  Subdevice #4: subdevice #4
  Subdevice #5: subdevice #5
  Subdevice #6: subdevice #6
  Subdevice #7: subdevice #7
card 4: Loopback [Loopback], device 1: Loopback PCM [Loopback PCM]
  Subdevices: 8/8
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
  Subdevice #4: subdevice #4
  Subdevice #5: subdevice #5
  Subdevice #6: subdevice #6
  Subdevice #7: subdevice #7

aplay -L

hw:CARD=Loopback,DEV=0
    Loopback, Loopback PCM
    Direct hardware device without any conversions
hw:CARD=Loopback,DEV=1
    Loopback, Loopback PCM
    Direct hardware device without any conversions
plughw:CARD=Loopback,DEV=0
    Loopback, Loopback PCM
    Hardware device with all software conversions
plughw:CARD=Loopback,DEV=1
    Loopback, Loopback PCM
    Hardware device with all software conversions

PINTOPINTO
# マイク入力のsource名
pacmd list-sources | grep -E 'index:|name:'

    index: 2
	name: <alsa_output.pci-0000_00_1f.3.iec958-stereo.monitor>
  * index: 7
	name: <alsa_input.usb-AONI_ELECTRONIC_CO._Full_HD_webcam_AN202007180003-02.mono-fallback>
    index: 8
	name: <alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1.monitor>

# スピーカーのsink名
pacmd list-sinks | grep -E 'index:|name:'

    index: 1
	name: <alsa_output.pci-0000_00_1f.3.iec958-stereo>
  * index: 4
	name: <alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1>

alsa_input.usb-AONI_ELECTRONIC_CO._Full_HD_webcam_AN202007180003-02.mono-fallback から取得する音声(マイク入力=source)を、alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1 (スピーカー出力=sink) に loopback するモジュールを有効にする。この設定でマイクから入力された音声がスピーカーの音声に混じって出力される。しかし、このまま使うと、スピーカーから出力される合成済み音声を再びマイクから拾ってしまうのですぐにハウリングする。

pacmd load-module module-loopback \
source='alsa_input.usb-AONI_ELECTRONIC_CO._Full_HD_webcam_AN202007180003-02.mono-fallback' \
sink='alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1'

ループバックモジュールのアンロード方法

pacmd list-modules | grep -E 'index:|module-loopback'

    index: 0
    index: 1
    index: 2
    index: 3
        :
    index: 28
    index: 29
        name: <module-loopback>

pacmd unload-module 29
PINTOPINTO
# null-sink モジュールの追加ロード
pacmd load-module module-null-sink

# 使用可能な sink の確認、* は現在デフォルト、null.monitor が使用したいsink
pacmd list-sources | grep -E 'index:|name:'

    index: 2
	name: <alsa_output.pci-0000_00_1f.3.iec958-stereo.monitor>
  * index: 21
	name: <alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1.monitor>
    index: 24
	name: <null.monitor>

# ロード済みのモジュールの確認
pacmd list-modules | grep -E 'index:|name:'

    index: 0
	name: <module-device-restore>
    index: 1
	name: <module-stream-restore>
    index: 2
	name: <module-card-restore>
    index: 3
	name: <module-augment-properties>
    index: 4
	name: <module-switch-on-port-available>
    index: 5
	name: <module-switch-on-connect>
    index: 6
	name: <module-udev-detect>
    index: 7
	name: <module-alsa-card>
    index: 9
	name: <module-alsa-card>
    index: 10
	name: <module-bluetooth-policy>
    index: 11
	name: <module-bluetooth-discover>
    index: 12
	name: <module-bluez5-discover>
    index: 13
	name: <module-native-protocol-unix>
    index: 14
	name: <module-default-device-restore>
    index: 15
	name: <module-always-sink>
    index: 16
	name: <module-intended-roles>
    index: 17
	name: <module-suspend-on-idle>
    index: 18
	name: <module-systemd-login>
    index: 19
	name: <module-position-event-sounds>
    index: 20
	name: <module-role-cork>
    index: 21
	name: <module-snap-policy>
    index: 22
	name: <module-filter-heuristics>
    index: 23
	name: <module-filter-apply>
    index: 24
	name: <module-native-protocol-tcp>
    index: 25
	name: <module-cli-protocol-unix>
    index: 35
	name: <module-null-sink>

# null-sink モジュールのアンロード
pacmd unload-module 35
PINTOPINTO
pacmd list-sources | grep -E 'index:|name:'
pacmd list-sinks | grep -E 'index:|name:'
pacmd list-modules | grep -E 'index:|Virtual|module-loopback'