Аудио: изменение объема выборок в массиве байтов

Я читаю 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 байта/кадра, мало-endian

Из класса физики я вспомнил, что вы можете изменить амплитуду синусоидальной волны, умножив синус-значение на число от 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 и наоборот.

Кто-нибудь может мне помочь? Что я здесь не так? Спасибо!:)

Ответ 1

Вы уверены, что читаете 8-битный монофонический звук? В противном случае один байт не равен одному образцу, и вы не можете просто масштабировать каждый байт. Например. если это 16-битные данные, вам необходимо проанализировать каждую пару байтов в виде 16-разрядного целого, масштабировать, а затем записать обратно в виде двух байтов.

Ответ 2

Проблема в int-типе, размер int в java составляет 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;
}

Ответ 3

Вы уверены, что один байт - это один образец? В этой спецификации формата это выглядит как образец имеет 2 байта. И не забудьте оставить заголовок без изменений.

Формат звукового файла WAVE PCM