电台广告块

这篇文章的作者是波兰程序员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.wavaudio.wav中的位置。 令我惊讶的是该方法的简单性: xcorr()完成了所有工作,其余代码仅用于读取文件并显示结果。

我想用Java实现相同的算法,然后将有一个工具可以:

  1. 从标准输入(例如,从ffmpeg)读取音频流,
  2. 分析它以寻找叮当声,
  3. 将相同的流输出到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(); // stereo stream short rightChannel = 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 0Got 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的讨论

Source: https://habr.com/ru/post/zh-CN415469/


All Articles