如果肮脏的黑客就足够了......

如果肮脏的黑客就足够了......

我试图保持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

如果肮脏的黑客就足够了......

我尊重马库斯的回应,但如果肮脏的黑客足够好,你可以尝试这个。

注意事项:

  • 需要 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。检测静音的一种方法是使用ffmpeg,它具有可配置的静音检测过滤器。然而,将 ffmpeg 添加到管道会使事情变得复杂,因为parec | ffmpeg | fgrep -m1当检测到某些内容时, using 不会退出fgrep -m1。您会看到,虽然在终止parec后以 sigpipe 退出,但不会,并且 bash 在所有命令完成之前不会从管道返回。因此我们将使用进程替换而不是管道。另外,ffmpeg 的噪音非常大,它的静音检测输出到 stderr 而不是 stdout,所以我们还要交换 ffmpeg 的 stderr 和 stdoutfgrepffmpeg

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 速率对音频进行采样,因为我不关心质量,我只想要一小部分原始数据。我通过使用fgrepwith来减少开销LC_ALL=C(比 bare 快约 1400% grep)。在我的 Raspberry Pi 4 上,pulseaudio CPU 在运行的前几秒内约为 0.3%。之后,它又跌回几乎为零。

静音检测 ~1% CPU

静音检测开销稍高,因为当我运行 ffmpeg 时,ffmpeg 在我的 Raspberry Pi 4 上使用了约 1% 的 CPU。

用我的技巧完成命令

#!/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

答案2

老实说,信号处理不应该尝试在 shell 脚本中实现。它依赖于对一堆二进制数据定期执行算术。即使每秒运行这个循环数百次,您的计算机也会遇到困难,而这将允许您进行连续的信号处理。

所以,抱歉,这行不通。

您可以使用许多非常好的框架之一来轻松地完成此操作,但由于上述原因,它们都不会为您提供 shell 脚本。没关系,您不需要 shell 脚本,您需要的是有用的东西!

我鼓励你尝试一下GNU 电台。尽管其重点是软件定义无线电,但它对于设计简单的音频处理工具包来说还是相当不错的。如果快速为您构建这个;它对我有用,如下所述,但您可能需要调整“阈值”高(开始)和低(停止)值。

基于 GNU Radio 的声音检测和输出

上面只是一个例子:一旦输入音量超过最大可能值的 10%,它就会开始发出声音,并在低于 5% 时停止。

当您点击 GNU Radio Companion(这是用于设计这些信号处理流程图的工具)中的“生成”按钮时,您会得到一个可执行脚本 - 不是 bash 脚本,而是 Python,但这对您的系统没有任何影响(这就是文件开头的这些#!/usr/bin/env python或行的用途:告诉您的系统如何运行脚本)。#!/bin/bash

你可以用它构建的东西并没有真正的限制——有读取音频文件的源,有更多的信号处理方式,如果你需要做一些 GNU Radio 本身无法完成的事情,用 Python 编写一个块并不是“其实没那么难!

相关内容