我必须将数字音频信号的位深度从 24 位降低到 16 位。
只取每个样本的 16 个最高有效位(即截断)相当于进行比例计算(out = in * 0xFFFF / 0xFFFFFF)?
我必须将数字音频信号的位深度从 24 位降低到 16 位。
只取每个样本的 16 个最高有效位(即截断)相当于进行比例计算(out = in * 0xFFFF / 0xFFFFFF)?
通过在截断(也称为抖动)之前将精心制作的噪声信号添加到原始信号中,在截断阈值以下,您将获得更好的声音效果。
x * 0xffff / 0xffffff
过于迂腐,但如果您的样本已签名,则不是一个好方法——而且总体上可能不是一个好方法。
是的,您希望源范围中的最大值与目标范围中的最大值相匹配,但是那里使用的值仅用于无符号范围,并且量化步骤的分布意味着您很少使用最大可能的输出值。
如果样本是有符号的,那么峰值正值将是 0x7fff 和 0x7fffff,而峰值负值将是 -0x8000 和 -0x800000。您的第一个问题是确定 +1 是否等于 0x7fff,或者 -1 是否等于 -0x8000。如果您选择后者,那么这是一个简单的移位操作。如果您尝试同时拥有两者,则零不再为零。
之后,您会遇到除法四舍五入为零的问题。这意味着与其他值相比,太多的值被四舍五入为零。这会导致失真。
如果要根据峰值正值进行缩放,正确的形式是:
out = rint((float)in * 0x7fff / 0x7fffff);
如果您稍微摸索一下,您可能会找到一种有效的方法来使用整数算术而不是除法。
对于任何给定输入,这种形式应该正确地四舍五入到最接近的可用输出值,并且它应该将最大可能的输入值映射到最大可能的输出值,但是它会在整个范围内散布一个丑陋的量化步长分布。
大多数人更喜欢:
out = (in + 128) >> 8;
if (out > 0x7fff) out = 0x7fff;
这种形式使声音变得最小一点,以至于正值可能会略微削波,但量化步长是均匀分布的。
您添加 128 是因为右移向负无穷大舍入。平均量化误差为 -128,您添加 128 来纠正此错误以使 0 保持在 0 精确。溢出测试是必要的,因为输入值 0x7fffff 否则会给出 0x8000 的结果,并且当您将其存储在 16-位字它会环绕给出一个峰值负值。
C 学究可能会在关于右移和除法行为的假设中戳破洞,但为了清楚起见,我忽略了这些。
但是,正如其他人指出的那样,您通常不应该在没有抖动的情况下减少音频的位深度,最好是噪声整形。TPDF抖动如下:
out = (in + (rand() & 255) - (rand() & 255)) >> 8;
if (out < -0x8000) out = -0x8000;
if (out > 0x7fff) out = 0x7fff;
rand()
同样,为了清楚起见,我将忽略使用的大问题。
我假设你的意思是(in * 0xFFFF) / 0xFFFFFF
,在这种情况下,是的。
通过添加噪声进行抖动通常会给您带来更好的结果。关键是噪声的形状。流行的pow-r抖动算法具有在许多数字音频工作站应用程序(Cakewalk 的 SONAR、Logic 等)中非常流行的特定形状。
如果您不需要 pow-r 的完整保真度,您可以简单地以相当低的幅度产生一些噪声并将其混合到您的信号中。你会发现这掩盖了一些量化效果。