音频:在字节数组样本的变化量(Audio: Change Volume of samples in

2019-08-08 01:01发布

我在读一个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赫兹,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;
}

如果我乘的声音了严重的失真audioSamplevolume 。 如果我不和两个阵列与比较Arrays.compare(array, audioSample)我可以得出结论,字节数组被转换到正确int和周围的其他方法。

任何人可以帮助我吗? 什么我会收到什么错在这里? 谢谢! :)

Answer 1:

你确定你正在阅读的8位单声道音频? 否则,一个字节不等于一个样品,你可以不只是规模的每个字节。 例如,如果它是你必须分析每对字节作为16位整数的16位数据,即规模,然后将它写回为两个字节。



Answer 2:

问题在整型,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;
}


Answer 3:

通过罗迪答案是一个很好的起点,但它不足以得到好的结果。

它引入了溢出的速度不够快在Android上实时音频。

TL; DR:我涉及LUT和增益压缩改进的解决方案

private static int N_SHORTS = 0xffff;
private static final short[] VOLUME_NORM_LUT = new short[N_SHORTS];
private static int MAX_NEGATIVE_AMPLITUDE = 0x8000;

static {
    precomputeVolumeNormLUT();
}    

private static void normalizeVolume(byte[] audioSamples, int start, int len) {
    for (int i = start; i < start+len; i+=2) {
        // convert byte pair to int
        short s1 = audioSamples[i+1];
        short s2 = audioSamples[i];

        s1 = (short) ((s1 & 0xff) << 8);
        s2 = (short) (s2 & 0xff);

        short res = (short) (s1 | s2);

        res = VOLUME_NORM_LUT[res+MAX_NEGATIVE_AMPLITUDE];
        audioSamples[i] = (byte) res;
        audioSamples[i+1] = (byte) (res >> 8);
    }
}

private static void precomputeVolumeNormLUT() {
    for(int s=0; s<N_SHORTS; s++) {
        double v = s-MAX_NEGATIVE_AMPLITUDE;
        double sign = Math.signum(v);
        // Non-linear volume boost function
        // fitted exponential through (0,0), (10000, 25000), (32767, 32767)
        VOLUME_NORM_LUT[s]=(short)(sign*(1.240769e-22 - (-4.66022/0.0001408133)*
                           (1 - Math.exp(-0.0001408133*v*sign))));
    }
}

这个作品非常好,提升声音好听,不具有限幅的问题,可以在Android上运行的实时性。

如何我到了那里

我的任务是包的专有闭源TTS引擎(由客户提供),使其作为一个标准的Android TextToSpeechService工作。 客户抱怨说,音量太低,即使流音量设置为最高。

我必须找到一个方法来提高Java中的体积实时,同时避免削波和失真。

两个问题与罗迪的解决方案:

  1. 代码正在运行有点太慢了实时操作在手机上(浮动慢)
  2. 它不会阻止溢出 ,这可能会导致不好的和显着的文物

我来到这个解决方案:

计算速度可通过交易为CPU RAM和使用的查找表(LUT),即能够提高预先计算对于每个输入值短的体积升压函数值在那里。

这样,你牺牲的RAM 128K,但无害化处理过程中摆脱了浮点和乘法的完全,这在我的情况下,是一个双赢。

至于溢出 ,周围有此两种方式。 丑一种是简单地替换短程分别Short.MIN_VALUE或Short.MAX_VALUE以外的值。 它不会阻止削波,但至少它不会溢出和文物的方式同样令人不安。

但是,我发现了一个更好的办法,这是应用非线性提升 (也称为增益压缩)。 您可以使用一个指数函数,而不是仅仅预计算的乘法LUT,可以预先计算非线性提升。 实际上,功能发挥得很好的LUT和任何类似的功能,可以预先计算这种方式。

找到功能良好的推动作用和最佳参数,最好的办法是用了一段时间不同的功能实验,一个简单但很好的工具是https://mycurvefit.com/

上述功能之一似乎是有前途的,我只是做一个小的修改,使负值以对称的方式工作。

一些参数打后,我得出的结论,我会取得好成绩,如果函数通过[0,0],[10000,25000]和[32767,32767]。

我需要一个相当大的音量提升,你可能希望更加微妙。

MyCurveFit给我该组参数:Y 0 = 1.240769e-22,V 0 = -4.66022,K = 0.0001408133

在LUT最终提升功能,以预先计算如下:

免责声明:我不是一个DSP专家,有人警告我说,像这样的提升是不适合的Hi-Fi音乐,这样,因为它引入了在音色的变化,谐波和其他微妙的文物。 但它的速度快,工作得非常好我的目的,我认为这是可以接受的涉及语音和Lo-Fi无线的东西,在一般的多种用途。



Answer 4:

你确定一个字节是一个样本? 在这种格式规范,它看起来像一个样品有2个byttes。 而且不要忘了让头保持不变。

WAVE PCM格式的音效档



文章来源: Audio: Change Volume of samples in byte array