我正在尝试规范化语音音频文件。
具体来说,如果音频文件包含音量峰值,我会尝试将其调高,因此安静的部分会更响亮,峰值会更安静。
我对音频处理知之甚少,超出了我从这项任务中学到的知识。而且,我的数学很尴尬。
我做了一些研究,Xuggle网站提供了一个示例,显示使用以下代码减少音量:(full version here)
@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);
}
在这里,他们将getAudioSamples()
中的字节修改为常量mVolume
。
在这种方法的基础上,考虑到文件中的最大值/最小值,我尝试将规范化将getAudioSamples()
中的字节修改为标准化值。 (详见下文)。我有一个简单的过滤器可以单独留下“沉默”(即,任何低于值的东西)。
我发现输出文件 非常 嘈杂(即,质量严重下降)。我假设错误是在我的规范化算法中,或者是我操纵字节的方式。但是,我不确定下一步该去哪里。
这是我目前所做的精简版。
读取完整的音频文件,并为所有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);
}
在类似于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()
的数学是否有效?答案 0 :(得分:8)
我不认为“最小样本值”的概念是非常有意义的,因为样本值仅表示某个时刻声波的当前“高度”。即其绝对值将在音频剪辑的峰值和零之间变化。因此,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
方法运行后的结果: