问题 音频:更改字节数组中的样本量


我正在使用一个wav文件读取字节数组 这种方法(如下所示)。现在我把它存储在我的字节数组中,我想改变音量。

private byte[] getAudioFileData(final String filePath) {
    byte[] data = null;
    try {
    final ByteArrayOutputStream baout = new ByteArrayOutputStream();
    final File file = new File(filePath);
    final AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file);

    byte[] buffer = new byte[4096];
    int c;
    while ((c = audioInputStream.read(buffer, 0, buffer.length)) != -1) {
        baout.write(buffer, 0, c);
    }
    audioInputStream.close();
    baout.close();
    data = baout.toByteArray();
    } catch (Exception e) {
    e.printStackTrace();
    }
    return data;
}

编辑: 按要求提供有关音频格式的一些信息:

PCM_SIGNED 44100.0 Hz,16位,单声道,2字节/帧,小端

从物理课开始,我记得你可以通过将正弦值乘以0到1之间的数字来改变正弦波的幅度。

编辑:更新了16位样本的代码:

private byte[] adjustVolume(byte[] audioSamples, double volume) {
    byte[] array = new byte[audioSamples.length];
    for (int i = 0; i < array.length; i+=2) {
        // convert byte pair to int
        int audioSample = (int) ((audioSamples[i+1] & 0xff) << 8) | (audioSamples[i] & 0xff);

        audioSample = (int) (audioSample * volume);

        // convert back
        array[i] = (byte) audioSample;
        array[i+1] = (byte) (audioSample >> 8);

    }
    return array;
}

如果我繁殖,声音会严重失真 audioSample 同 volume。如果我不这样做,并将两个数组进行比较 Arrays.compare(array, audioSample) 我可以得出结论,字节数组正在被正确转换为int,反之亦然。

有人可以帮帮我吗?我在这里弄错了什么?谢谢! :)


2378
2018-01-23 17:36


起源

您可以在dsp.stackexchange.com上获得更好的答案 - egrunin
1)为了更好的帮助,发布一个 SSCCE。 2)报告 audioInputStream.getFormat()。 - Andrew Thompson
@egrunin谢谢!我可以将其复制并粘贴到那里或移动主题的规则是什么? - Macks
@AndrewThompson感谢您的提示。 audioInputStream.getFormat() 说:PCM_SIGNED 44100.0 Hz,16位,单声道,2字节/帧,小端 - Macks
嗯。见 由@johusman回答。 - Andrew Thompson


答案:


你确定你正在阅读8位单声道音频吗?否则,一个字节不等于一个样本,并且您不能只缩放每个字节。例如。如果是16位数据,则​​必须将每对字节解析为16位整数,对其进行缩放,然后将其写回为两个字节。


7
2018-01-23 17:45



谢谢。我的音频样本大小为16位。我将阅读有关如何正确转换我的字节数组的信息,并在我设法完成后向您提供反馈。 - Macks
嘿。 :)我终于设法正确地将我的字节数组转换为int和back。然而,如果我将样本乘以,我的声音比以前更加严重失真 volume。我已经更新了问题中的代码。你能来看看吗?那太好了。谢谢! :) - Macks
只是快速浏览一下,但我怀疑你没有正确处理负值(你转换为 int,这是32位,也许你可以使用 short?)。请记住,java的有符号整数是两个补码。 - johusman
非常感谢你,这解决了我的问题!你真棒,谢谢! :) - Macks


int类型的问题,java中int的大小是4个字节,样本大小是2个字节

这个代码:

private byte[] adjustVolume(byte[] audioSamples, float volume) {
        byte[] array = new byte[audioSamples.length];
        for (int i = 0; i < array.length; i+=2) {
            // convert byte pair to int
            short buf1 = audioSamples[i+1];
            short buf2 = audioSamples[i];

            buf1 = (short) ((buf1 & 0xff) << 8);
            buf2 = (short) (buf2 & 0xff);

            short res= (short) (buf1 | buf2);
            res = (short) (res * volume);

            // convert back
            array[i] = (byte) res;
            array[i+1] = (byte) (res >> 8);

        }
        return array;
}

6
2017-09-25 11:33



是否可以控制立体声音量? - nadous
是的,很容易一旦你知道PCM是16位LL RR LL RR LL RR LL(其中每个字符是1个字节)所以我只是将i递增4并重用你的代码。 - nadous


你确定一个字节是一个样本吗?在此格式规范中,它看起来像样本有2个字节。并且不要忘记让标题保持不变。

WAVE PCM声音文件格式


1
2018-01-23 17:46



感谢您的帮助。我在问题中更新了我的代码,但它仍然无效。 :( - Macks