我正在尝试规范化语音的音频文件.
具体来说,当音频文件包含音量峰值时,我正试图将其调高,因此安静的部分更响亮,峰值更安静.
除了我从完成这项任务中学到的东西之外,我对音频操作知之甚少.而且,我的数学很尴尬.
我做了一些研究,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);
}
Run Code Online (Sandbox Code Playgroud)
在这里,他们getAudioSamples()
用常数修改字节mVolume
.
基于这种方法,我尝试将标准化修改getAudioSamples()
为标准化值,考虑文件中的最大/最小值.(详见下文).我有一个简单的过滤器来单独留下"沉默"(即,任何低于值的东西).
我发现输出文件非常嘈杂(即质量严重下降).我假设错误是在我的规范化算法中,或者是我操纵字节的方式.但是,我不确定下一步该去哪里.
这是我目前正在做的精简版.
读取完整的音频文件,找到buffer.get()
所有AudioSamples的最高和最低值
@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);
}
Run Code Online (Sandbox Code Playgroud)
在类似于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;
}
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
作为参考,这是您的算法对振幅为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,但这是你可以尝试的其他东西.
归档时间: |
|
查看次数: |
6992 次 |
最近记录: |