Java: получение байтового массива из 8-битного wav-файла и его нормализация от -1,0 до 1,0

Потерпите меня, так как я новичок в работе со звуком, и я несколько дней искал в Google решение, но не нашел его.

Итак, я получаю байтовый массив файла .wav с помощью этого (источник: Конвертировать файл WAV в массив байтов в java)

ByteArrayOutputStream out = new ByteArrayOutputStream();
BufferedInputStream in = new BufferedInputStream(new FileInputStream(WAV_FILE));

int read;
byte[] buff = new byte[1024];
while ((read = in.read(buff)) > 0)
{
     out.write(buff, 0, read);
}
out.flush();
byte[] audioBytes = out.toByteArray();

Затем я конвертирую массив байтов в массив с плавающей запятой и нормализую его от -1,0 до 1,0. (источник: Преобразование массива байтов аудиоформата wav в формат с плавающей запятой )

ShortBuffer sbuf =
ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
short[] audioShorts = new short[sbuf.capacity()];
sbuf.get(audioShorts);

float[] audioFloats = new float[audioShorts.length];
for (int i = 0; i < audioShorts.length; i++) {
    audioFloats[i] = ((float)audioShorts[i])/0x8000;
}
return audioFloats;

Позже я преобразовываю это в линейные рисунки, которые выводят форму волны с помощью java.swing.

class Panel2 extends JPanel {
float[] audioFloats;

    Dimension d;
    public Panel2(Dimension d, float[] audioFloats) {
        // set a preferred size for the custom panel.
        this.d = d;
        setPreferredSize(d);
        this.audioFloats = audioFloats;
    }


    @Override
    public void paint(Graphics g) {
        //super.paintComponent(g);
        super.paint(g); 

        //shift by 45 because first 44 bytes used for header
        for (int i = 45; i<audioFloats.length; i++){

            Graphics2D g2 = (Graphics2D) g;
            float inc = (i-45)*((float)d.width)/((float)(audioFloats.length-45-1));
            Line2D lin = new Line2D.Float(inc, d.height/2, inc, (audioFloats[i]*d.height+d.height/2));
            g2.draw(lin);

        }


    }
}

Форма волны выглядит подходящей только для 16-битных файлов WAV (я проверил перекрестную проверку с помощью goldwave, и моя форма волны и их форма выглядят одинаково для 16-битных файлов).

Как это сделать для 8-битных файлов .wav?

Поскольку это домашнее задание, мое единственное ограничение - побайтно читать wav-файл.

Я также знаю, что файлы wav закодированы в формате PCM, и первые 44 байта зарезервированы в качестве заголовка.


person user1153395    schedule 10.10.2012    source источник


Ответы (2)


Вам необходимо адаптировать эту часть кода:

ShortBuffer sbuf =
  ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
short[] audioShorts = new short[sbuf.capacity()];
sbuf.get(audioShorts);

float[] audioFloats = new float[audioShorts.length];
for (int i = 0; i < audioShorts.length; i++) {
    audioFloats[i] = ((float)audioShorts[i])/0x8000;
}

Вам не нужно ByteBuffer, ведь у вас уже есть массив байтов. Так что просто преобразуйте его в числа с плавающей запятой:

float[] audioFloats = new float[audioBytes.length];
for (int i = 0; i < audioBytes.length; i++) {
    audioFloats[i] = ((float)audioBytes[i])/0x80;
}
person Marko Topolnik    schedule 10.10.2012
comment
Спасибо за ответ. Однако он все еще не отображается правильно. Я проверил значения в массиве с плавающей запятой, и почти все они кажутся очень близкими к 1 или -1. значения в 16-битном массиве более разбросаны между -1 и 1. Имеет ли это какое-то отношение к способу получения байтового массива в начале? - person user1153395; 11.10.2012
comment
Для диагностики распечатайте необработанные байтовые значения. Посмотрите, как они распространяются. - person Marko Topolnik; 11.10.2012
comment
У меня есть, они тоже не такие уж и распространенные. Значения либо очень близки к -128, либо 127. Поэтому, когда я выводю сигнал, он похож на гигантский прямоугольник. - person user1153395; 11.10.2012
comment
Если это необработанные данные, ничего другого ожидать не стоит. Почему вы так уверены, что это не реальная записанная форма сигнала? - person Marko Topolnik; 11.10.2012
comment
потому что когда я выводю его, он выглядит как гигантский прямоугольник. Я использовал goldwave, чтобы увидеть реальную форму волны, и она даже не похожа на то, что у меня есть. Так что я знаю, что, должно быть, делаю что-то не так - person user1153395; 11.10.2012

Аудиопотоки обычно чередуются с одним каналом данных, а затем с противоположным каналом данных. Так, например, первые 16 бит будут левым каналом, а следующие 16 бит - правым каналом. Каждый из них считается 1 фреймом данных. Я хотел бы убедиться, что ваш 8-битный поток - это только один канал, потому что похоже, что методы настроены только для чтения одного канала.

Также в вашем примере для преобразования кадров вы захватываете отдельный канал как короткий, а затем находите десятичное число, разделив его на 0x8000 в шестнадцатеричном формате или на максимальное значение короткого со знаком.

short[] audioShorts = new short[sbuf.capacity()];
sbuf.get(audioShorts);
...
audioFloats[i] = ((float)audioShorts[i])/0x8000;

Я предполагаю, что вам нужно прочитать 8-байтовый поток как тип «байт» вместо короткого, а затем разделить его на 128 или максимальное значение 8-битного значения со знаком. Это потребует создания совершенно нового метода, который обрабатывает 8-битные потоки вместо 16-битных потоков. Со следующими изменениями.

byte[] audioBytes = new byte[sbuf.capacity()];
sbuf.get(audioBytes);
...
audioFloats[i] = ((float)audioBytes[i])/0x80;
person Jimmy Johnson    schedule 10.10.2012
comment
Не сработает. На самом деле решение намного проще. Но точка зрения о стерео WAV верна. - person Marko Topolnik; 11.10.2012
comment
Вы знаете, одноканальный это или двухканальный поток? - person Jimmy Johnson; 11.10.2012
comment
извините, что это значит? как я уже сказал, я все еще новичок в аудио. ты имеешь в виду стерео или моно? я думаю это будет моно - person user1153395; 11.10.2012