我正在处理电话原始电话声音和录音,我想在 .Net C# 项目中将它们标准化到一定的音量级别。
声音是原始音频字节的集合(单声道无头 16 位签名 PCM 音频 16000Hz)。
音频被分成 3200 字节 == 100ms 的块。
有什么建议如何增加音量/幅度以使声音更大吗?
我不知道是否需要添加常量或乘法值,或者是否需要每 1,2,3.... 字节执行一次?也许已经有一个开源解决方案了?
回答我自己的问题(其他人)。
解决方案是将每个样本(16位PCM时为2字节)乘以一个常数值。
避免溢出或增加太多,您可以通过查找最高样本值来计算可以使用的最高常数值,并计算乘法因子以使其达到可能的最高样本值,在16位PCM情况下,即32676或其他值。
这是一个小例子:
public byte[] IncreaseDecibel(byte[] audioBuffer, float multiplier)
{
// Max range -32768 and 32767
var highestValue = GetHighestAbsoluteSample(audioBuffer);
var highestPosibleMultiplier = (float)Int16.MaxValue/highestValue; // Int16.MaxValue = 32767
if (multiplier > highestPosibleMultiplier)
{
multiplier = highestPosibleMultiplier;
}
for (var i = 0; i < audioBuffer.Length; i = i + 2)
{
Int16 sample = BitConverter.ToInt16(audioBuffer, i);
sample *= (Int16)(sample * multiplier);
byte[] sampleBytes = GetLittleEndianBytesFromShort(sample);
audioBuffer[i] = sampleBytes[sampleBytes.Length-2];
audioBuffer[i+1] = sampleBytes[sampleBytes.Length-1];
}
return audioBuffer;
}
// 添加了 GetHighestAbsoluteSample,希望它仍然正确,因为代码随着时间的推移而发生了变化
/// <summary>
/// Peak sample value
/// </summary>
/// <param name="audioBuffer">audio</param>
/// <returns>0 - 32768</returns>
public static short GetHighestAbsoluteSample(byte[] audioBuffer)
{
Int16 highestAbsoluteValue = 0;
for (var i = 0; i < (audioBuffer.Length-1); i = i + 2)
{
Int16 sample = ByteConverter.GetShortFromLittleEndianBytes(audioBuffer, i);
// prevent Math.Abs overflow exception
if (sample == Int16.MinValue)
{
sample += 1;
}
var absoluteValue = Math.Abs(sample);
if (absoluteValue > highestAbsoluteValue)
{
highestAbsoluteValue = absoluteValue;
}
}
return (highestAbsoluteValue > LowestPossibleAmplitude) ?
highestAbsoluteValue : LowestPossibleAmplitude;
}
@托尼db
我修改了你添加的方法
public static byte[] IncreaseDecibel(byte[] audioBuffer, float multiplier)
{
// Max range -32768 and 32767
var highestValue = GetHighestAbsoluteSample(audioBuffer);
var highestPosibleMultiplier = (float)Int16.MaxValue / highestValue; // Int16.MaxValue = 32767
if (multiplier > highestPosibleMultiplier)
{
multiplier = highestPosibleMultiplier;
}
for (var i = 0; i < audioBuffer.Length; i = i + 2)
{
short sample = BitConverter.ToInt16(audioBuffer, i);
sample *= (short)(sample * multiplier);
byte[] sampleBytes = GetLittleEndianBytesFromShort(sample);
if (!BitConverter.IsLittleEndian)
{
sampleBytes = sampleBytes.Reverse().ToArray();
}
audioBuffer[i] = sampleBytes[sampleBytes.Length - 2];
audioBuffer[i + 1] = sampleBytes[sampleBytes.Length - 1];
}
return audioBuffer;
}
public static short GetHighestAbsoluteSample(byte[] audioBuffer)
{
short highestAbsoluteValue = 0;
for (var i = 0; i < (audioBuffer.Length - 1); i = i + 2)
{
short sample;
if (BitConverter.IsLittleEndian)
{
sample = (short)((audioBuffer[i + 1] << 8) | audioBuffer[i]);
}
else
{
byte[] tempBuffer = audioBuffer.Reverse().ToArray();
sample= (short)((tempBuffer[i + 1] << 8) | tempBuffer[i]);
}
// prevent Math.Abs overflow exception
if (sample == Int16.MinValue)
{
sample += 1;
}
var absoluteValue = Math.Abs(sample);
if (absoluteValue > highestAbsoluteValue)
{
highestAbsoluteValue = absoluteValue;
}
}
return highestAbsoluteValue;
}
public static byte[] GetLittleEndianBytesFromShort(short data)
{
byte[] b = new byte[2];
b[0] = (byte)data;
b[1] = (byte)(data >> 8 & 0xFF);
return b;
}
由于上一个答案中添加的方法返回了编译错误。但声音似乎不太对劲。也许应该有一个正确的倍数值。