我认为正在发生的事情是您将它们相加为无符号。典型的声波既有正面的也有负面的,这就是为什么它们以它们的方式相加(某些部分抵消)。如果您有一些 8 位样本为 -96,另一个样本为 96,并且您将它们相加,您将得到 0。如果您拥有的是无符号音频,您将得到样本 32 和 224 的总和 = 256(偏移和溢出) .
您需要做的是在求和之前签署它们。要对 8 位样本进行签名,请将它们转换为带符号的 int 类型并从所有样本中减去 128。我假设您拥有的是 WAV 文件,并且您需要在总和后再次取消签名。
Audacity 可能会进行浮点处理。我听说过一些关于浮点的真正可疑的说法,比如它具有“无限动态范围”和类似的垃圾,但它不像整数那样以相同的确定和明显的方式剪辑。浮点具有与整数相同的有限值范围,但最大值和最小值相距更远。(这是最简单的说法。)浮点可以允许音频中更大的幅度变化,但要注意的是整体信噪比低于整数。
由于奇怪的失真,我最好的猜测是它来自你正在使用的面具& 0xFF
。如果你想实际剪辑而不是溢出,你需要自己做。
for (int i = 0; i < samplesLength; i++) {
if (samples[i] > 127) {
samples[i] = 127;
} else if (samples[i] < -128) {
samples[i] = -128;
}
}
否则说你有两个样本是 125,求和得到 250(11111010)。然后你取消签名(加 128)并得到 378(101111010)。一个 & 会得到 1111010,即 122。其他数字可能会得到实际上为负数或接近 0 的结果。
如果您想在 8 位以外的位置进行剪辑,则位深度 n 的满量程将为例如 32767positive (2 ^ (n - 1)) - 1
和negative 2 ^ (n - 1)
-32768 用于 16 位。
除了裁剪之外,您可以做的另一件事是搜索裁剪和规范化。就像是:
double[] normalize(double[] samples, int length, int destBits) {
double fsNeg = -pow(2, destBits - 1);
double fsPos = -fsNeg - 1;
double peak = 0;
double norm = 1;
for (int i = 0; i < length; i++) {
// find highest clip if there is one
if (samples[i] < fsNeg || samples[i] > fsPos) {
norm = abs(samples[i]);
if (norm > peak) {
norm = peak;
}
}
}
if (peak != 0) {
// ratio to reduce to where there is not a clip
norm = -fsNeg / peak;
for (int i = 0; i < length; i++) {
samples[i] *= norm;
}
}
return samples;
}