Пиковое отсечение при наложении аудио файлов Java - PullRequest
0 голосов
/ 04 марта 2019

Так что в рамках проекта, над которым я работаю, я пытаюсь наложить несколько аудиоклипов друг на друга, чтобы создать звук толпы, и записать это в новый файл .WAV.

Сначала я создаю представление файла byte [] (16-битный файл PCM .WAV), которое, по-видимому, не вызывает никаких проблем.

public byte[] toByteArray(File file)
{
    try
    {
        AudioInputStream in = AudioSystem.getAudioInputStream(file);

        byte[] byteArray = new byte[(int) file.length()];//make sure the size is correct

        while (in.read(byteArray) != -1) ;//read in byte by byte until end of audio input stream reached

        return byteArray;//return the new byte array
    }

Затем я создаю буфер (целое числомассив, чтобы предотвратить переполнение байтов при добавлении байтов) и попробуйте наслоение в версии байтового массива моих файлов.

 int[] buffer = new int[bufferLength];//buffer of appropriate length
        int offset = 0;//no offset for the very first file

        while(!convertedFiles.isEmpty())//until every sample has been added
        {
            byte[] curr = convertedFiles.pop();//get a sample from list

            if(curr.length+offset < bufferLength)
            {
                for (int i =0; i < curr.length; i++)
                {
                    buffer[i] += curr[i];
                }
            }

           offset = randomiseOffset();//next sample placed in a random location in the buffer
        }

Проблема возникает, когда я пытаюсь реализовать своего рода случайное смещение.Я могу добавить все аудио в свой буфер из индекса 0 (buffer [0]), так что все воспроизводится одновременно, и это работает.Однако, если я пытаюсь распределить отдельные клипы случайным образом по всему буферу, у меня возникают проблемы.

Когда я пытаюсь компенсировать добавление файлов относительно длины буфера, я получаю ужасное статическое и пиковое отсечение.

 buffer[i+offset] += curr[i];

Я понимаю, что мне нужно быть осторожным, чтобы избежать переполнения, поэтому я попытался использовать целочисленный буфер, а не байтовый.

Хотя я не понимаю, почемуон ломается только при вводе смещения.

Я не опубликовал код фактического использования объекта AudioSystem для создания нового файла, так как в любом случае это не дает никакого эффекта.

Я впервые работаю со звуковым программированием, поэтому любая помощь очень ценится.

РЕДАКТИРОВАТЬ:

Ответ Хендрика решил мою проблему, но мне просто нужно было немного изменить предложенный код (некоторый типвопросы конверсии):

    private static short byteToShortLittleEndian(final byte[] buf, final int offset)
{
    int sample = (buf[offset] & 0xff) + ((buf[offset+1] & 0xff) << 8);
    return (short)sample;
}

private static byte[] shortToByteLittleEndian(final short[] samples, final int offset)
{
    byte[] buf = new byte[2];
    int sample = samples[offset];
    buf[0] = (byte) (sample & 0xFF);
    buf[1] = (byte) ((sample >> 8) & 0xFF);
    return buf;
}

1 Ответ

0 голосов
/ 04 марта 2019

Как выглядит ваш randomiseOffset() метод?Учитывает ли это, что каждый аудиосэмпл имеет длину два байта?Если randomiseOffset() дает вам нечетные смещения, вы в конечном итоге смешаете младшие байты одного семпла с старшими байтами другого семпла, что звучит как (обычно ужасный) шум.Возможно, это звук, который вы определили как отсечение.

Чтобы сделать это правильно, вам необходимо сначала декодировать звук, то есть принять во внимание длину семпла (2 байта) и количество каналов (?), Выполнить ваши манипуляции, а затемзакодируйте аудио снова в поток байтов.

Давайте предположим, что у вас есть только один канал, и порядок байтов little-endian .Затем вы расшифруете два байта в примерное значение, например:

private static int byteToShortLittleEndian(final byte[] buf, final int offset) {
    int sample = (buf[offset] & 0xff) + ((buf[offset+1] & 0xff) << 8);
    return (short)sample;
}

Чтобы кодировать , вы будете использовать что-то вроде этого:

private static byte[] shortToByteLittleEndian(final int[] samples, final int offset) {
    byte[] buf = new byte[2];
    int sample = samples[offset];
    buf[0] = sample & 0xFF;
    buf[1] = (sample >> 8) & 0xFF;
    return buf;
}

Вот как в вашем случае используются два метода:

byte[] byteArray = ...;  // your array
// DECODE: convert to sample values
int[] samples = byteArray.length / 2;
for (int i=0; i<samples.length; i++) {
    samples[i] = byteToShortLittleEndian(byteArray, i*2);
}
// now do your manipulation on the samples array
[...]
// ENCODE: convert back to byte values
byte[] byteOut = new byte[byteArray.length];
for (int i=0; i<samples.length; i++) {
    byte[] b = shortToByteLittleEndian(samples, i);
    byteOut[2*i] = b[0];
    byteOut[2*i+1] = b[1];
}
// do something with byteOut ...

(Обратите внимание, что вы можете легко сделать это более эффективным путем массового декодирования / кодирования и не работая с отдельным образцом, как показано выше)Я просто подумал, что это легче понять.)

Во время ваших манипуляций вы должны обращать внимание на значения выборки.Они не должны быть больше Short.MAX_VALUE или меньше Short.MIN_VALUE.Если вы обнаружите, что находитесь за пределами допустимого диапазона, просто масштабируйте весь массив .Таким образом вы избежите отсечения.

Удачи!

...