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も交換する必要があります。fgrep
ffmpeg
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=C
grep
無音検出 ~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