【发布时间】:2012-09-18 01:53:26
【问题描述】:
我正在尝试规范化语音的音频文件。
具体来说,当音频文件包含音量峰值时,我正在尝试将其拉平,因此安静部分更响亮,峰值更安静。
除了从这项任务中学到的知识之外,我对音频处理知之甚少。另外,我的数学很差。
我做了一些研究,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() 中的字节修改为规范化值。 (详见下文)。我有一个简单的过滤器可以单独留下“沉默”(即任何低于某个值的内容)。
我发现输出文件非常嘈杂(即质量严重下降)。我假设错误是在我的规范化算法中,或者是我操作字节的方式。但是,我不确定下一步该去哪里。
这是我目前正在做的事情的精简版。
第 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()中的数学是否有效? - 为什么这会导致文件变得嘈杂,而演示代码中的类似方法不会?
【问题讨论】:
-
“我正在尝试将其拉平,因此安静的部分更响亮,而山峰更安静。” - 那叫dynamic range compression,和归一化不一样。