ダーティハッキングで十分です..

ダーティハッキングで十分です..

soxサウンドカードのパイプ入力を開いたままにし、パイプに音がある場合にのみプレーヤーコマンドを実行しようとします(パイプを殺したりファイルを使用せずに)。

これはsox silence 1 0.1 5% -1 0.1 5%forファイルを使用して簡単に達成できますが、パイプ出力に使用すると機能しません。

これはsox私が使用するロギングコマンドです。

/bin/sox -V2 -q \
-r 48000 -b 16 -c 2 -t alsa hw:CARD=sndrpihifiberry,DEV=0 \
-t wav -r 44100 -b 16 -c 2 - \ 
silence 1 0.1 0.1% -1 2 0.5% \ 
> $streamFile &

パイプに音がある場合にのみ、プレーヤーをパイプに接続または切断したいと思います。それは次のとおりです。

while [ true ]; do 
  
        until [ WAIT FOR  SOUND ]; do
        
        TEST FOR SOUND IN THE PIPE
        
        done
        
        echo "Sound Detected starting @ $(date)" >> $log
        /usr/bin/player < $streamFile &
        PLAYERpid=$!

        until [ WAIT FOR SILENCE ]; do
  
        TEST FOR SILENCE IN THE PIPE

        done

        kill $PLAYERpid
        echo "Silence Detected killing PLAYER @ $(date)" >> $log

done

どんなアイデアがありますか?

ベストアンサー1

ダーティハッキングで十分です..

私はMarcusの反応を尊重しますが、ダーティハッキングが十分であればこれを試してみることができます。

指示:

  • PulseAudioまたはPipeWireが必要(pulseaudio utilsのインストール)
  • 無音検出にはffmpegが必要です。
  • プレーヤーが再生を開始する前に、サウンドの小さな部分が失われます。
  • オーディオの途中で静かな時間中にプレーヤーが死ぬのを防ぐために、サイレント検出に少し調整が必要な場合があります。

1.オーディオ検出

ここでは実際に素晴らしい信号処理を行う必要はありません。サウンドデバイスの低品質キャプチャを録音し、grepに直接転送して存在するかどうかを確認します。何もないそこに...

#!/bin/bash

# Get your PulseAudio source monitor (edit regex to suit you)
pulse_monitor=$(pactl list short sources | awk '$1 = /alsa.*monitor/ {print $2}')

function wait_for_audio() {
  parec --rate 1000 -d $pulse_monitor 2>/dev/null \
  | LC_ALL=C fgrep -qm 1 .
}

while [ true ]; do
  wait_for_audio
  echo "Sound Detected starting @ $(date)" >> $log
  /usr/bin/player < $streamFile &
  PLAYERpid=$!

  ...
done

データの最初のバイトが通過するとすぐに、fgrepがパイプを終了するため、待ち時間は非常に短くなります。その時点から、プレイヤーは明らかに敗北します。一部オーディオですがテストでは許容できました。これは簡単な部分です。

2. 沈黙検知

パイプが「何か」を検出したときにパイプを終了するのではなく、「何も検出できないのを待つ」ので、これは少し難しいです。ここではgrepは使用できません。サイレントを検出する1つの方法は、ffmpeg設定可能なサイレント検出フィルタを使用することです。ただし、パイプラインにffmpegを追加すると、何かが検出されたparec | ffmpeg | fgrep -m1ときにusingが終了しないため、状況が複雑になりますfgrep -m1。ご覧のとおり、シャットダウンparec後はsigpipeで終了しますが、そうではなく、bashはすべてのコマンドが完了するまでパイプから返されません。したがって、パイプの代わりにプロセス置換を使用します。さらに、ffmpegはノイズが非常に多く、無音検出がstdoutの代わりにstderrに出力されるため、ffmpegのstderrとstdoutも交換する必要があります。fgrepffmpeg

detect_silence() {
  # By default, exit after detecting 2 seconds of continuous silence
  SECONDS=${1:-2}
  LC_ALL=C fgrep -m 1 silence_start \
  <(parec \
    --rate 1000 \
    --raw \
    -d ${pulse_monitor} 2>/dev/null \
  | ffmpeg \
    -hide_banner \
    -f s8 \
    -ar 1k \
    -ac 2 \
    -i pipe: \
    -af silencedetect=noise=-50dB:d=${SECONDS} \
    -f null - \
    3>&1 1>&2 2>&3)
}

CPUオーバーヘッド

オーディオ検出<0.3%

オーディオ検出のオーバーヘッドが非常に低いです。品質には興味がなく、元のデータのほんの一部だけが欲しいので、1khzの速度でオーディオをサンプリングしています。 with(bareより約1400%速い)fgrepを使用してオーバーヘッドを減らします。私のRaspberry Pi 4では、pulseaudio CPUは実行後最初の数秒間約0.3%で動作します。それ以来、ほぼゼロに落ちた。LC_ALL=Cgrep

無音検出 ~1% CPU

ffmpegを実行するときにRaspberry Pi 4 CPUの約1%を使用するため、無音検出のオーバーヘッドが少し高くなります。

私のヒントを使ってコマンドを完了してください。

#!/bin/bash

# Get your PulseAudio source monitor (edit regex to suit you)
pulse_monitor=$(pactl list short sources | awk '$1 = /alsa.*monitor/ {print $2}')

function wait_for_audio() {
  # sample audio at 1khz and exit as soon as data is detected
  parec --rate 1000 -d $pulse_monitor 2>/dev/null \
  | LC_ALL=C fgrep -qm 1 .
}

detect_silence() {
  # By default, exit after detecting 2 seconds of continuous silence
  SECONDS=${1:-2}
  LC_ALL=C fgrep -m 1 silence_start \
  <(parec \
    --rate 1000 \
    --raw \
    -d ${pulse_monitor} 2>/dev/null \
  | ffmpeg \
    -hide_banner \
    -f s8 \
    -ar 1k \
    -ac 2 \
    -i pipe: \
    -af silencedetect=noise=-50dB:d=${SECONDS} \
    -f null - \
    3>&1 1>&2 2>&3)
}

while [ true ]; do
  wait_for_audio
  echo "Sound Detected starting @ $(date)" >> $log
  /usr/bin/player < $streamFile &
  PLAYERpid=$!

  detect_silence 2
  echo "Silence Detected starting @ $(date -d "2 seconds ago")" >> $log
  kill $PLAYERpid
  "Silence Detected killing PLAYER @ $(date)" >> $log

done

おすすめ記事