用于规范化音频的Java算法

我正在尝试规范化语音的音频文件。

具体来说,当音频文件包含音量峰值时,我正试图将其调高,因此安静的部分更响亮,峰值更安静。

除了我从这项任务中学到的东西之外,我对音频操作知之甚少。 而且,我的数学令人尴尬地弱。

我做了一些研究,Xuggle网站提供了一个示例,显示使用以下代码减少音量:( 此处完整版 )

@Override public void onAudioSamples(IAudioSamplesEvent event) { // get the raw audio byes and adjust it's value ShortBuffer buffer = event.getAudioSamples().getByteBuffer().asShortBuffer(); for (int i = 0; i < buffer.limit(); ++i) buffer.put(i, (short)(buffer.get(i) * mVolume)); super.onAudioSamples(event); } 

在这里,他们通过mVolume的常量修改getAudioSamples()的字节。

基于这种方法,我尝试将规范化将getAudioSamples()的字节修改为标准化值,同时考虑文件中的最大值/分钟。 (详见下文)。 我有一个简单的filter来单独留下“沉默”(即,任何低于值的东西)。

我发现输出文件非常嘈杂(即,质量严重下降)。 我假设错误是在我的规范化算法中,或者是我操纵字节的方式。 但是,我不确定下一步该去哪里。

这是我目前所做的精简版。

第1步:在文件中查找峰值:

读取完整的音频文件,并为所有AudioSamples找到buffer.get()最高和最低值

  @Override public void onAudioSamples(IAudioSamplesEvent event) { IAudioSamples audioSamples = event.getAudioSamples(); ShortBuffer buffer = audioSamples.getByteBuffer().asShortBuffer(); short min = Short.MAX_VALUE; short max = Short.MIN_VALUE; for (int i = 0; i < buffer.limit(); ++i) { short value = buffer.get(i); min = (short) Math.min(min, value); max = (short) Math.max(max, value); } // assign of min/max ommitted for brevity. super.onAudioSamples(event); } 

第2步:规范化所有值:

在类似于step1的循环中,用标准化值替换缓冲区,调用:

  buffer.put(i, normalize(buffer.get(i)); public short normalize(short value) { if (isBackgroundNoise(value)) return value; short rawMin = // min from step1 short rawMax = // max from step1 short targetRangeMin = 1000; short targetRangeMax = 8000; int abs = Math.abs(value); double a = (abs - rawMin) * (targetRangeMax - targetRangeMin); double b = (rawMax - rawMin); double result = targetRangeMin + ( a/b ); // Copy the sign of value to result. result = Math.copySign(result,value); return (short) result; } 

问题:

  • 这是尝试规范化音频文件的有效方法吗?
  • 我的数学在normalize()有效吗?
  • 为什么这会导致文件变得嘈杂,而演示代码中的类似方法却没有?

我不认为“最小样本值”的概念是非常有意义的,因为样本值仅表示在某个时刻的声波的当前“高度”。 即它的绝对值将在音频剪辑的峰值和零之间变化。 因此,具有targetRangeMin似乎是错误的并且可能会导致波形的某些失真。

我认为更好的方法可能是使用某种权重函数,根据其大小减少样本值。 也就是说,较大的值比较小的值减少了很大的百分比。 这也会引入一些失真,但可能不是很明显。

编辑:这是这种方法的示例实现:

 public short normalize(short value) { short rawMax = // max from step1 short targetMax = 8000; //This is the maximum volume reduction double maxReduce = 1 - targetMax/(double)rawMax; int abs = Math.abs(value); double factor = (maxReduce * abs/(double)rawMax); return (short) Math.round((1 - factor) * value); } 

作为参考,这是您的算法对振幅为10000的正弦曲线所做的操作: 原始算法

这解释了为什么在标准化后音频质量变得更差。

这是使用我建议的normalize方法运行后的结果: 建议的算法

音频的“标准化”是增加音频电平的过程,使得最大值等于某个给定值,通常是最大可能值。 今天,在另一个问题中,有人解释了如何做到这一点(见#1): 音量正常化

然而,你继续说“具体来说,音频文件包含音量峰值,我试图将其调高,因此安静的部分更响亮,峰值更安静。” 这称为“压缩”或“限制”(不要与压缩类型混淆,例如编码MP3时使用的压缩类型!)。 您可以在此处阅读更多相关信息: http : //en.wikipedia.org/wiki/Dynamic_range_compression

一个简单的压缩器并不是特别难以实现,但你说你的数学“令人尴尬地弱”。 所以你可能想找到一个已经建成的。 您可以在http://sox.sourceforge.net/中找到一个压缩器,并将其从C转换为Java。 压缩机的唯一java实现我知道谁的源可用(并且它不是很好)在本书中

作为解决问题的替代方法,您可以将文件标准化为每秒1/2秒,然后使用线性插值连接每个段使用的增益值。 你可以在这里阅读有关音频的线性插值: http : //blog.bjornroche.com/2010/10/linear-interpolation-for-audio-in-cc.html

我不知道源代码是否可用于levelator ,但这是你可以尝试的其他东西。