算术短计算中的精度损失

时间:2015-10-10 18:56:56

标签: java audio

我必须为学校做一个小程序,通过计算两个立体声通道之间的平均值将立体声文件转换为单声道(标准16位wav)。

我有一个参考单声道文件来检查我的结果,我发现我的结果文件中有一些字节被一个位关闭(顶部文件是引用,底部是我从代码中生成的文件):

VBinDiff

以下是我用来进行“转化”的方法的代码:

    /**
 * 
 */
@Override
public void process() {

    int byteCount = 0;
    byte[] header = null;

    header = _input.pop(44);

    if (Convert.toInt(Arrays.copyOfRange(header, 22, 24)) == 2 && Convert.toInt(Arrays.copyOfRange(header, 34, 36)) == 16) {

        byteCount = Convert.toInt(Arrays.copyOfRange(header, 40, 44));

        _output.push(Arrays.copyOfRange(header, 0,4)); // ChunkID
        _output.push(Convert.toByte(byteCount/2 + 36)); // ChunkSize
        _output.push(Arrays.copyOfRange(header, 8, 22)); // Format & Subchunk1ID & Subchunk1Size & AudioFormat
        _output.push(Convert.toByte(1)); // NumChannels
        _output.push(Arrays.copyOfRange(header, 24, 28)); // SampleRate

        int SampleRate = Convert.toInt(Arrays.copyOfRange(header, 24, 28));
        int BitsPerSample = Convert.toInt(Arrays.copyOfRange(header, 34, 36));

        _output.push(Convert.toByte(SampleRate * 1 * BitsPerSample/8)); // ByteRate (SampleRate * NumChannels * BitsPerSample/8)
        _output.push(Convert.toByte(1 * Convert.toInt(Arrays.copyOfRange(header, 34, 36))/8));// BlockAlign (NumChannels * BitsPerSample/8)
        _output.push(Arrays.copyOfRange(header, 34, 40)); // BitsPerSample & Subchunk2ID
        _output.push(Convert.toByte(byteCount/2)); // Subchunk2Size

        for (int i = 0; i < byteCount; i+=4) { // 4 bytes at a time

            short left = ByteBuffer.wrap(_input.pop(2)).order(ByteOrder.LITTLE_ENDIAN).getShort();
            short right = ByteBuffer.wrap(_input.pop(2)).order(ByteOrder.LITTLE_ENDIAN).getShort();

            short mean = (short) ((left + right) / 2); // Mono is the mean between both channels

            _output.push(ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort(mean).array());

        }

        _input.close();
        _output.close();

    } else {

        System.out.println("Error! The file provided is not stereo or 16-bits! Aborting...");
        _input.close();
        _output.close();

    }

}

其中“_output.push()”和“_input.pop()”只是实用工具。它们将从/向文件返回或写入一个字节数组。 “Convert”只是一个从字节数组转换的类。

无论如何,我怀疑短路上的算术计算正在失去精度或类似的东西。知道问题来自哪里,理想情况下如何解决?

1 个答案:

答案 0 :(得分:0)

这已在评论中提及,但我稍后会详细说明。

您正在进行2的整数除法,这将导致结果始终被截断。例如,(2+1)/2 == 3/2 == 1为纯整数运算。这解释了您的结果比参考文件少一个的每种情况。解决它的方法是使用浮点进行中间计算:

short mean = (short)(Math.round((left + right) / 2.0f));

我不确定在您的值大于参考文件的情况下发生了什么。如果您为一个或多个案例发布左右一些样本值,我可以更新我的答案。