这篇文章的作者是波兰程序员Tomek Rekavek,他正在开发Adobe的Apache软件基金会的一部分的Jackrabbit Oak项目。 该文章于2016年2月24日发表在作者的个人博客上。波兰语“ Radio-3”(所谓的“三驾马车”)以出色的音乐和聪明的主持人而闻名。 另一方面,它遭受广播中响亮而烦人的广告单元的困扰,这些广告单元通常会宣传某种电子产品或药品。 我几乎在工作和在家都经常听Troika的音乐,所以我想知道:如何删除广告? 我想我设法找到了解决方案。
数字信号处理
我的目标是创建一个使广告静音的应用程序。 商业广告块
以叮当声
开始和
结束 ,因此程序应识别这些特定的声音并关闭它们之间的声音。
我知道数学/计算机科学领域被称为
数字信号处理 ,但是DSP在我看来总是很神奇。 好吧,这是学习新知识的绝佳机会。 我花了一两天的时间来弄清楚用来分析音频流的机制。 最后,我找到了我需要的:
互相关或互相关(cross-correlation)。
八度
通常每个人都提到MATLAB的实现。 但是MATLAB是昂贵的应用程序,可简化复杂的数学运算(包括DSP)的执行。 幸运的是,有一个免费的替代品
Octave 。 在Octave中,似乎很容易对两个音频文件进行互相关。 只需要执行以下命令:
pkg load signal jingle = wavread('jingle.wav')(:,1); audio = wavread ('audio.wav')(:,1); [R, lag] = xcorr(jingle, audio); plot(R);
您得到以下图表:

一个清晰可见的峰描述了
jingle.wav
在
audio.wav
中的位置。 令我惊讶的是该方法的简单性:
xcorr()
完成了所有工作,其余代码仅用于读取文件并显示结果。
我想用Java实现相同的算法,然后将有一个工具可以:
- 从标准输入(例如,从ffmpeg)读取音频流,
- 分析它以寻找叮当声,
- 将相同的流输出到stdout和/或将其禁用。
使用stdin和stdout将使您可以将新的
分析仪连接到负责音频广播和结果回放的其他应用程序。
读取声音文件
Java程序应首先读取的是数组中的叮当声(保存为
.wav
文件)。 文件中还有一些其他信息,例如标题,元数据等,但我们只需要声音即可。 一种合适的格式称为PCM,它只是表示声音的数字列表。 将WAV转换为PCM可以ffmpeg:
ffmpeg -i input.wav -f s16le -acodec pcm_s16le output.raw
在这里,每个样本都以相反的字节顺序(小尾数)保存为16位数字。 在Java中,此数字称为
short
,您可以使用
ByteBuffer
类将输入流自动转换为
short
值列表:
ByteBuffer buf = ByteBuffer.allocate(4); buf.order(ByteOrder.LITTLE_ENDIAN); buf.put(bytes); short leftChannel = buf.readShort();
Xcorr逆向工程
为了用Java实现
xcorr()
函数,我研究了Octave
源代码 。 在不更改最终结果的情况下,我可以将xcorr()调用替换为以下几行-需要用Java重写它们:
N = length(audio); M = 2 ^ nextpow2(2 * N - 1); pre = fft(postpad(prepad(jingle(:), length(jingle) + N - 1), M)); post = fft(postpad(audio(:), M)); cor = ifft(pre .* conj(post)); R = real(cor(1:2 * N));
看起来很吓人,但是大多数功能都是对数组的琐碎操作。 互相关基于
快速傅立叶变换在声音样本上的应用。
快速傅立叶变换
作为一个没有使用DSP经验的人,我只是将FFT视为一个函数,该函数采用一个带有声音样本描述的数组-并返回一个包含表示频率的复数的数组。 这种简约的方法效果很好:我从
JTransforms包启动了FFT实现,并获得了与Octave相同的结果。 我认为这在某种程度上是一种
狂热的
货 ,但是该死的,它有效!
在线程上运行xcorr
上面的算法假定
audio
是我们在其中寻找
jingle
的数组。 这并不完全适合广播,因为我们有连续的声音流。 为了进行分析,我创建了一个循环缓冲区,该缓冲区比待识别的铃声的持续时间长一点。 传入流将填充缓冲区,并在缓冲区满后立即运行互相关测试。 如果什么也没找到,则缓冲区的最旧部分将被丢弃-再次,我们期望它会被填充。
我对缓冲区的长度进行了一些实验,并获得了最佳效果,缓冲区大小为叮当大小的1.5倍。
全部放在一起
获得PCM格式的流很容易。 这可以使用上面的
ffmpeg
。 下面的命令将流重定向到标准
java
输入,然后在流中找到相应的模式时输出
Got jingle 0
或
Got jingle 1
。
ffmpeg -loglevel -8 \ -i http://stream3.polskieradio.pl:8904/\;stream \ -f s16le -acodec pcm_s16le - \ | java -jar target/analyzer-1.0.0-SNAPSHOT-jar-with-dependencies.jar \ 2 \ src/test/resources/commercial-start-44.1k.raw 500 \ src/test/resources/commercial-end-44.1k.raw 700
单机版
我还准备了一个简单的独立版本的分析器,它本身连接到Troika流(没有外部
ffmpeg
),并使用
javax.sound
再现了结果。 一切都适合单个JAR文件,并包含带有“星号”和“停止”按钮的基本用户界面。 可以在
这里下载。 如果您不喜欢在计算机上运行其他人的JAR(这是绝对正确的),那么所有资源都在
GitHub上 。
一切似乎
都应该正常工作 :)
进一步的工作
最终目标是在硬件放大器级别禁用广告,接收“真实” FM信号,而不接收某些Internet流。
下一篇文章对此进行了描述。
更新(2018年6月)
黑客新闻上的讨论关于Wykop的讨论关于Reddit的讨论