Как использовать данные аудиоданных из Java Sound?

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

Хотя я не знаю, каким образом Java может создавать для нас аудиосэмплы в настоящее время, если это изменится в будущем, это может быть место для этого. Я знаю, что JavaFX имеет некоторые вещи вроде этого, например _2 _, но по-прежнему не имеет прямого доступа к образцам.


Я использую javax.sound.sampled для воспроизведения и / или записи, но мне бы хотелось что-нибудь сделать со звуком.

Возможно, мне хотелось бы отобразить это наглядно или как-то обработать.

Как мне получить доступ к данным аудиоданных, чтобы сделать это с помощью Java Sound?

Смотрите также:


person Radiodef    schedule 09.11.2014    source источник


Ответы (2)


Что ж, самый простой ответ заключается в том, что в настоящий момент Java не может создавать образцы данных для программиста.

Это цитата из официального руководства:

Есть два способа применить обработку сигнала:

  • Вы можете использовать любую обработку, поддерживаемую микшером или его составными строками, запрашивая Control объекты и затем устанавливая элементы управления по желанию пользователя. Типичные элементы управления, поддерживаемые микшерами и линиями, включают элементы управления усилением, панорамированием и реверберацией.

  • Если необходимая вам обработка не обеспечивается микшером или его строками, ваша программа может работать непосредственно с байтами звука, манипулируя ими по своему усмотрению.

На этой странице более подробно обсуждается первый метод, поскольку для второго метода не существует специального API.

Воспроизведение с javax.sound.sampled в значительной степени действует как мост между файлом и аудиоустройством. Байты считываются из файла и отправляются.

Не думайте, что байты являются значимыми аудиосэмплами! Если у вас нет 8-битного файла AIFF, его нет. (С другой стороны, если образцы определенно имеют 8-битную подпись, вы можете выполнять с ними арифметические операции. Использование 8-битных кодов - один из способов избежать описанной здесь сложности. , если вы просто играете.)

Поэтому вместо этого я перечислю типы AudioFormat.Encoding и опишите, как их декодировать самостоятельно. В этом ответе не описано, как их кодировать, но он включен в полный пример кода внизу. Кодирование - это в основном процесс декодирования в обратном порядке.

Это длинный ответ, но я хотел дать подробный обзор.


Немного о цифровом аудио

Обычно при объяснении цифрового звука мы имеем в виду линейную импульсно-кодовую модуляцию (LPCM ).

Сплошная звуковая волна дискретизируется через равные промежутки времени, а амплитуды квантуются до целых чисел некоторого масштаба.

Здесь показана синусоидальная волна, дискретизированная и квантованная до 4-х разряда:

lpcm_graph

(Обратите внимание, что наиболее положительное значение в представлении дополнение до двух на 1 меньше самого отрицательного значения . Это второстепенная деталь, о которой следует помнить. Например, если вы обрезаете аудио и забываете об этом, положительные клипы будут переполняться.)

Когда у нас есть звук на компьютере, у нас есть массив этих образцов. Образец массива - это то, во что мы хотим превратить массив byte.

Чтобы декодировать сэмплы PCM, мы не особо заботимся о частоте дискретизации или количестве каналов, поэтому я не буду много говорить о них здесь. Каналы обычно чередуются, поэтому, если бы у нас был их массив, они бы хранились следующим образом:

Index 0: Sample 0 (Left Channel)
Index 1: Sample 0 (Right Channel)
Index 2: Sample 1 (Left Channel)
Index 3: Sample 1 (Right Channel)
Index 4: Sample 2 (Left Channel)
Index 5: Sample 2 (Right Channel)
...

Другими словами, для стерео образцы в массиве просто чередуются между левым и правым.


Некоторые предположения

Все примеры кода предполагают следующие объявления:

  • byte[] bytes; Массив byte, считанный из AudioInputStream.
  • float[] samples; Выходной массив сэмплов, который мы собираемся заполнить.
  • float sample; Образец, над которым мы сейчас работаем.
  • long temp; Промежуточное значение, используемое для общих манипуляций.
  • int i; Позиция в массиве byte, с которой начинаются данные текущей выборки.

Мы нормализуем все образцы в нашем float[] массиве до диапазона -1f <= sample <= 1f. Все звуки с плавающей запятой, которые я видел, поступают таким образом, и это довольно удобно.

Если наш исходный звук еще не поступил таким образом (как, например, для целочисленных сэмплов), мы можем нормализовать их сами, используя следующее:

sample = sample / fullScale(bitsPerSample);

Где fullScale - это 2 bitsPerSample - 1, то есть Math.pow(2, bitsPerSample-1).


Как мне привести массив byte к значимым данным?

Массив byte содержит образцы кадров, разделенных на одну строку. На самом деле это очень просто, за исключением того, что называется endianness, то есть порядка byte в каждый образец пакета.

Вот диаграмма. Этот образец (упакованный в массив byte) содержит десятичное значение 9999:

  24-bit sample as big-endian:

 bytes[i]   bytes[i + 1] bytes[i + 2]
 ┌──────┐     ┌──────┐     ┌──────┐
 00000000     00100111     00001111

 24-bit sample as little-endian:

 bytes[i]   bytes[i + 1] bytes[i + 2]
 ┌──────┐     ┌──────┐     ┌──────┐
 00001111     00100111     00000000

Они содержат одинаковые двоичные значения; однако byte заказы отменяются.

  • При обратном порядке байтов более значимые byte идут перед менее значимыми byte.
  • В little-endian менее значимые byte идут перед более значимыми bytes.

Файлы WAV хранятся в обратном порядке и Файлы AIFF хранятся с прямым порядком байтов. Порядок байтов можно получить из AudioFormat.isBigEndian < / а>.

Чтобы объединить bytes и поместить их в нашу переменную long temp, мы:

  1. Побитовое И каждый byte с маской 0xFF (которая равна 0b1111_1111), чтобы избежать расширения знака, когда byte автоматически продвигается. (char, byte и short повышаются до int, когда над ними выполняются арифметические операции.) См. Также Что делает value & 0xff в Java?
  2. Битовый сдвиг каждого byte в позицию.
  3. Поразрядное ИЛИ byte вместе.

Вот 24-битный пример:

long temp;
if (isBigEndian) {
    temp = (
          ((bytes[i    ] & 0xffL) << 16)
        | ((bytes[i + 1] & 0xffL) <<  8)
        |  (bytes[i + 2] & 0xffL)
    );
} else {
    temp = (
           (bytes[i    ] & 0xffL)
        | ((bytes[i + 1] & 0xffL) <<  8)
        | ((bytes[i + 2] & 0xffL) << 16)
    );
}

Обратите внимание, что порядок сдвига меняется на обратный в зависимости от порядка байтов.

Это также можно обобщить до цикла, который можно увидеть в полном коде внизу этого ответа. (См. Методы unpackAnyBit и packAnyBit.)

Теперь, когда у нас есть byte, объединенные вместе, мы можем сделать еще несколько шагов, чтобы превратить их в образец. Следующие шаги зависят от фактического кодирования.

Как мне расшифровать Encoding.PCM_SIGNED?

Знак дополнения до двух должен быть расширен. Это означает, что если старший бит (MSB) установлен в 1, мы заполняем все биты над ним единицами. Арифметический сдвиг вправо (>>) выполнит заполнение автоматически, если бит знака установлен, поэтому я обычно делаю это следующим образом:

int bitsToExtend = Long.SIZE - bitsPerSample;
float sample = (temp << bitsToExtend) >> bitsToExtend.

(Где Long.SIZE равно 64. Если бы наша переменная temp не была long, мы бы использовали что-то другое. Если бы мы использовали, например, int temp вместо этого, мы бы использовали 32.)

Чтобы понять, как это работает, вот диаграмма расширения знака с 8-битного на 16-битный:

 11111111 is the byte value -1, but the upper bits of the short are 0.
 Shift the byte's MSB in to the MSB position of the short.

 0000 0000 1111 1111
 <<                8
 ───────────────────
 1111 1111 0000 0000

 Shift it back and the right-shift fills all the upper bits with 1s.
 We now have the short value of -1.

 1111 1111 0000 0000
 >>                8
 ───────────────────
 1111 1111 1111 1111

Положительные значения (которые имели 0 в MSB) остаются без изменений. Это хорошее свойство арифметического сдвига вправо.

Затем нормализуйте образец, как описано в разделе Некоторые предположения.

Возможно, вам не понадобится писать явное расширение знака, если ваш код простой

Java автоматически расширяет знак при преобразовании одного целочисленного типа в более крупный, например byte в int. Если вы знаете, что ваш формат ввода и вывода всегда подписан, вы можете использовать автоматическое расширение знака при объединении байтов на предыдущем шаге.

Вспомните из раздела выше (Как мне преобразовать массив байтов в осмысленные данные?), что мы использовали b & 0xFF, чтобы предотвратить появление расширения знака. Если вы просто удалите & 0xFF из самого высокого byte, расширение знака произойдет автоматически.

Например, следующий код декодирует 16-битные выборки с прямым порядком байтов со знаком:

for (int i = 0; i < bytes.length; i++) {
    int sample = (bytes[i] << 8) // high byte is sign-extended
               | (bytes[i + 1] & 0xFF); // low byte is not
    // ...
}

Как мне расшифровать Encoding.PCM_UNSIGNED?

Превращаем его в число со знаком. Беззнаковые образцы просто смещаются так, например, что:

  • Беззнаковое значение 0 соответствует самому отрицательному значению со знаком.
  • Беззнаковое значение 2 bitsPerSample - 1 соответствует значению со знаком 0.
  • Беззнаковое значение 2 bitsPerSample соответствует наиболее положительному значению со знаком.

Оказывается, это довольно просто. Просто вычтите смещение:

float sample = temp - fullScale(bitsPerSample);

Затем нормализуйте образец, как описано в разделе Некоторые предположения.

Как мне расшифровать Encoding.PCM_FLOAT?

Это новое, начиная с Java 7.

На практике PCM с плавающей запятой обычно является либо 32-битным IEEE, либо 64-битным IEEE и уже нормализован до диапазона ±1.0. Примеры можно получить с помощью служебных методов Float#intBitsToFloat и Double#longBitsToDouble .

// IEEE 32-bit
float sample = Float.intBitsToFloat((int) temp);
// IEEE 64-bit
double sampleAsDouble = Double.longBitsToDouble(temp);
float sample = (float) sampleAsDouble; // or just use double for arithmetic

Как мне декодировать Encoding.ULAW и Encoding.ALAW?

Это кодеки сжатия companding, которые более распространены в телефонах и т. Д. Я полагаю, что они поддерживаются javax.sound.sampled, потому что они используются в формате Au от Sun. (Однако это не ограничивается только этим типом контейнера. Например, WAV может содержать эти кодировки.)

Вы можете концептуализировать A-law и - law, как будто они являются форматом с плавающей запятой. Это форматы PCM, но диапазон значений нелинейный.

Расшифровать их можно двумя способами. Я покажу способ использования математической формулы. Вы также можете декодировать их, напрямую управляя двоичным файлом: описан в этом сообщении блога, но выглядит более эзотерическим.

Для обоих сжатые данные 8-битные. Стандартно A-law составляет 13 бит при декодировании, а -law - 14 бит при декодировании; однако применение формулы дает диапазон ±1.0.

Прежде чем применять формулу, необходимо сделать три вещи:

  1. Некоторые биты обычно инвертируются для хранения по причинам, связанным с целостностью данных.
  2. Они хранятся как знак и величина (а не как дополнение до двух).
  3. Формула также предполагает диапазон ±1.0, поэтому необходимо масштабировать 8-битное значение.

Для -law все биты инвертируются, поэтому:

temp ^= 0xffL; // 0xff == 0b1111_1111

(Обратите внимание, что мы не можем использовать ~, потому что мы не хотим инвертировать старшие биты long.)

Для A-закона все остальные биты инвертируются, поэтому:

temp ^= 0x55L; // 0x55 == 0b0101_0101

(Для инверсии можно использовать XOR. См. Как установить, сбросить и переключить бит? )

Чтобы преобразовать знак и величину в дополнение до двух, мы:

  1. Проверьте, установлен ли бит знака.
  2. Если это так, очистите бит знака и инвертируйте число.
// 0x80 == 0b1000_0000
if ((temp & 0x80L) != 0) {
    temp ^= 0x80L;
    temp = -temp;
}

Затем масштабируйте закодированные числа так же, как описано в Некоторые предположения:

sample = temp / fullScale(8);

Теперь мы можем применить расширение.

Тогда формула -law, переведенная на Java, выглядит так:

sample = (float) (
    signum(sample)
        *
    (1.0 / 255.0)
        *
    (pow(256.0, abs(sample)) - 1.0)
);

Тогда формула A-закона, переведенная на Java, выглядит так:

float signum = signum(sample);
sample = abs(sample);

if (sample < (1.0 / (1.0 + log(87.7)))) {
    sample = (float) (
        sample * ((1.0 + log(87.7)) / 87.7)
    );
} else {
    sample = (float) (
        exp((sample * (1.0 + log(87.7))) - 1.0) / 87.7
    );
}

sample = signum * sample;

Вот полный пример кода для класса SimpleAudioConversion.

package mcve.audio;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioFormat.Encoding;

import static java.lang.Math.*;

/**
 * <p>Performs simple audio format conversion.</p>
 *
 * <p>Example usage:</p>
 *
 * <pre>{@code  AudioInputStream ais = ... ;
 * SourceDataLine  line = ... ;
 * AudioFormat      fmt = ... ;
 *
 * // do setup
 *
 * for (int blen = 0; (blen = ais.read(bytes)) > -1;) {
 *     int slen;
 *     slen = SimpleAudioConversion.decode(bytes, samples, blen, fmt);
 *
 *     // do something with samples
 *
 *     blen = SimpleAudioConversion.encode(samples, bytes, slen, fmt);
 *     line.write(bytes, 0, blen);
 * }}</pre>
 *
 * @author Radiodef
 * @see <a href="http://stackoverflow.com/a/26824664/2891664">Overview on Stack Overflow</a>
 */
public final class SimpleAudioConversion {
    private SimpleAudioConversion() {}

    /**
     * Converts from a byte array to an audio sample float array.
     *
     * @param bytes   the byte array, filled by the AudioInputStream
     * @param samples an array to fill up with audio samples
     * @param blen    the return value of AudioInputStream.read
     * @param fmt     the source AudioFormat
     *
     * @return the number of valid audio samples converted
     *
     * @throws NullPointerException if bytes, samples or fmt is null
     * @throws ArrayIndexOutOfBoundsException
     *         if bytes.length is less than blen or
     *         if samples.length is less than blen / bytesPerSample(fmt.getSampleSizeInBits())
     */
    public static int decode(byte[]      bytes,
                             float[]     samples,
                             int         blen,
                             AudioFormat fmt) {
        int   bitsPerSample = fmt.getSampleSizeInBits();
        int  bytesPerSample = bytesPerSample(bitsPerSample);
        boolean isBigEndian = fmt.isBigEndian();
        Encoding   encoding = fmt.getEncoding();
        double    fullScale = fullScale(bitsPerSample);

        int i = 0;
        int s = 0;
        while (i < blen) {
            long temp = unpackBits(bytes, i, isBigEndian, bytesPerSample);
            float sample = 0f;

            if (encoding == Encoding.PCM_SIGNED) {
                temp = extendSign(temp, bitsPerSample);
                sample = (float) (temp / fullScale);

            } else if (encoding == Encoding.PCM_UNSIGNED) {
                temp = unsignedToSigned(temp, bitsPerSample);
                sample = (float) (temp / fullScale);

            } else if (encoding == Encoding.PCM_FLOAT) {
                if (bitsPerSample == 32) {
                    sample = Float.intBitsToFloat((int) temp);
                } else if (bitsPerSample == 64) {
                    sample = (float) Double.longBitsToDouble(temp);
                }
            } else if (encoding == Encoding.ULAW) {
                sample = bitsToMuLaw(temp);

            } else if (encoding == Encoding.ALAW) {
                sample = bitsToALaw(temp);
            }

            samples[s] = sample;

            i += bytesPerSample;
            s++;
        }

        return s;
    }

    /**
     * Converts from an audio sample float array to a byte array.
     *
     * @param samples an array of audio samples to encode
     * @param bytes   an array to fill up with bytes
     * @param slen    the return value of the decode method
     * @param fmt     the destination AudioFormat
     *
     * @return the number of valid bytes converted
     *
     * @throws NullPointerException if samples, bytes or fmt is null
     * @throws ArrayIndexOutOfBoundsException
     *         if samples.length is less than slen or
     *         if bytes.length is less than slen * bytesPerSample(fmt.getSampleSizeInBits())
     */
    public static int encode(float[]     samples,
                             byte[]      bytes,
                             int         slen,
                             AudioFormat fmt) {
        int   bitsPerSample = fmt.getSampleSizeInBits();
        int  bytesPerSample = bytesPerSample(bitsPerSample);
        boolean isBigEndian = fmt.isBigEndian();
        Encoding   encoding = fmt.getEncoding();
        double    fullScale = fullScale(bitsPerSample);

        int i = 0;
        int s = 0;
        while (s < slen) {
            float sample = samples[s];
            long temp = 0L;

            if (encoding == Encoding.PCM_SIGNED) {
                temp = (long) (sample * fullScale);

            } else if (encoding == Encoding.PCM_UNSIGNED) {
                temp = (long) (sample * fullScale);
                temp = signedToUnsigned(temp, bitsPerSample);

            } else if (encoding == Encoding.PCM_FLOAT) {
                if (bitsPerSample == 32) {
                    temp = Float.floatToRawIntBits(sample);
                } else if (bitsPerSample == 64) {
                    temp = Double.doubleToRawLongBits(sample);
                }
            } else if (encoding == Encoding.ULAW) {
                temp = muLawToBits(sample);

            } else if (encoding == Encoding.ALAW) {
                temp = aLawToBits(sample);
            }

            packBits(bytes, i, temp, isBigEndian, bytesPerSample);

            i += bytesPerSample;
            s++;
        }

        return i;
    }

    /**
     * Computes the block-aligned bytes per sample of the audio format,
     * using Math.ceil(bitsPerSample / 8.0).
     * <p>
     * Round towards the ceiling because formats that allow bit depths
     * in non-integral multiples of 8 typically pad up to the nearest
     * integral multiple of 8. So for example, a 31-bit AIFF file will
     * actually store 32-bit blocks.
     *
     * @param  bitsPerSample the return value of AudioFormat.getSampleSizeInBits
     * @return The block-aligned bytes per sample of the audio format.
     */
    public static int bytesPerSample(int bitsPerSample) {
        return (int) ceil(bitsPerSample / 8.0); // optimization: ((bitsPerSample + 7) >>> 3)
    }

    /**
     * Computes the largest magnitude representable by the audio format,
     * using Math.pow(2.0, bitsPerSample - 1). Note that for two's complement
     * audio, the largest positive value is one less than the return value of
     * this method.
     * <p>
     * The result is returned as a double because in the case that
     * bitsPerSample is 64, a long would overflow.
     *
     * @param bitsPerSample the return value of AudioFormat.getBitsPerSample
     * @return the largest magnitude representable by the audio format
     */
    public static double fullScale(int bitsPerSample) {
        return pow(2.0, bitsPerSample - 1); // optimization: (1L << (bitsPerSample - 1))
    }

    private static long unpackBits(byte[]  bytes,
                                   int     i,
                                   boolean isBigEndian,
                                   int     bytesPerSample) {
        switch (bytesPerSample) {
            case  1: return unpack8Bit(bytes, i);
            case  2: return unpack16Bit(bytes, i, isBigEndian);
            case  3: return unpack24Bit(bytes, i, isBigEndian);
            default: return unpackAnyBit(bytes, i, isBigEndian, bytesPerSample);
        }
    }

    private static long unpack8Bit(byte[] bytes, int i) {
        return bytes[i] & 0xffL;
    }

    private static long unpack16Bit(byte[]  bytes,
                                    int     i,
                                    boolean isBigEndian) {
        if (isBigEndian) {
            return (
                  ((bytes[i    ] & 0xffL) << 8)
                |  (bytes[i + 1] & 0xffL)
            );
        } else {
            return (
                   (bytes[i    ] & 0xffL)
                | ((bytes[i + 1] & 0xffL) << 8)
            );
        }
    }

    private static long unpack24Bit(byte[]  bytes,
                                    int     i,
                                    boolean isBigEndian) {
        if (isBigEndian) {
            return (
                  ((bytes[i    ] & 0xffL) << 16)
                | ((bytes[i + 1] & 0xffL) <<  8)
                |  (bytes[i + 2] & 0xffL)
            );
        } else {
            return (
                   (bytes[i    ] & 0xffL)
                | ((bytes[i + 1] & 0xffL) <<  8)
                | ((bytes[i + 2] & 0xffL) << 16)
            );
        }
    }

    private static long unpackAnyBit(byte[]  bytes,
                                     int     i,
                                     boolean isBigEndian,
                                     int     bytesPerSample) {
        long temp = 0;

        if (isBigEndian) {
            for (int b = 0; b < bytesPerSample; b++) {
                temp |= (bytes[i + b] & 0xffL) << (
                    8 * (bytesPerSample - b - 1)
                );
            }
        } else {
            for (int b = 0; b < bytesPerSample; b++) {
                temp |= (bytes[i + b] & 0xffL) << (8 * b);
            }
        }

        return temp;
    }

    private static void packBits(byte[]  bytes,
                                 int     i,
                                 long    temp,
                                 boolean isBigEndian,
                                 int     bytesPerSample) {
        switch (bytesPerSample) {
            case  1: pack8Bit(bytes, i, temp);
                     break;
            case  2: pack16Bit(bytes, i, temp, isBigEndian);
                     break;
            case  3: pack24Bit(bytes, i, temp, isBigEndian);
                     break;
            default: packAnyBit(bytes, i, temp, isBigEndian, bytesPerSample);
                     break;
        }
    }

    private static void pack8Bit(byte[] bytes, int i, long temp) {
        bytes[i] = (byte) (temp & 0xffL);
    }

    private static void pack16Bit(byte[]  bytes,
                                  int     i,
                                  long    temp,
                                  boolean isBigEndian) {
        if (isBigEndian) {
            bytes[i    ] = (byte) ((temp >>> 8) & 0xffL);
            bytes[i + 1] = (byte) ( temp        & 0xffL);
        } else {
            bytes[i    ] = (byte) ( temp        & 0xffL);
            bytes[i + 1] = (byte) ((temp >>> 8) & 0xffL);
        }
    }

    private static void pack24Bit(byte[]  bytes,
                                  int     i,
                                  long    temp,
                                  boolean isBigEndian) {
        if (isBigEndian) {
            bytes[i    ] = (byte) ((temp >>> 16) & 0xffL);
            bytes[i + 1] = (byte) ((temp >>>  8) & 0xffL);
            bytes[i + 2] = (byte) ( temp         & 0xffL);
        } else {
            bytes[i    ] = (byte) ( temp         & 0xffL);
            bytes[i + 1] = (byte) ((temp >>>  8) & 0xffL);
            bytes[i + 2] = (byte) ((temp >>> 16) & 0xffL);
        }
    }

    private static void packAnyBit(byte[]  bytes,
                                   int     i,
                                   long    temp,
                                   boolean isBigEndian,
                                   int     bytesPerSample) {
        if (isBigEndian) {
            for (int b = 0; b < bytesPerSample; b++) {
                bytes[i + b] = (byte) (
                    (temp >>> (8 * (bytesPerSample - b - 1))) & 0xffL
                );
            }
        } else {
            for (int b = 0; b < bytesPerSample; b++) {
                bytes[i + b] = (byte) ((temp >>> (8 * b)) & 0xffL);
            }
        }
    }

    private static long extendSign(long temp, int bitsPerSample) {
        int bitsToExtend = Long.SIZE - bitsPerSample;
        return (temp << bitsToExtend) >> bitsToExtend;
    }

    private static long unsignedToSigned(long temp, int bitsPerSample) {
        return temp - (long) fullScale(bitsPerSample);
    }

    private static long signedToUnsigned(long temp, int bitsPerSample) {
        return temp + (long) fullScale(bitsPerSample);
    }

    // mu-law constant
    private static final double MU = 255.0;
    // A-law constant
    private static final double A = 87.7;
    // natural logarithm of A
    private static final double LN_A = log(A);

    private static float bitsToMuLaw(long temp) {
        temp ^= 0xffL;
        if ((temp & 0x80L) != 0) {
            temp = -(temp ^ 0x80L);
        }

        float sample = (float) (temp / fullScale(8));

        return (float) (
            signum(sample)
                *
            (1.0 / MU)
                *
            (pow(1.0 + MU, abs(sample)) - 1.0)
        );
    }

    private static long muLawToBits(float sample) {
        double sign = signum(sample);
        sample = abs(sample);

        sample = (float) (
            sign * (log(1.0 + (MU * sample)) / log(1.0 + MU))
        );

        long temp = (long) (sample * fullScale(8));

        if (temp < 0) {
            temp = -temp ^ 0x80L;
        }

        return temp ^ 0xffL;
    }

    private static float bitsToALaw(long temp) {
        temp ^= 0x55L;
        if ((temp & 0x80L) != 0) {
            temp = -(temp ^ 0x80L);
        }

        float sample = (float) (temp / fullScale(8));

        float sign = signum(sample);
        sample = abs(sample);

        if (sample < (1.0 / (1.0 + LN_A))) {
            sample = (float) (sample * ((1.0 + LN_A) / A));
        } else {
            sample = (float) (exp((sample * (1.0 + LN_A)) - 1.0) / A);
        }

        return sign * sample;
    }

    private static long aLawToBits(float sample) {
        double sign = signum(sample);
        sample = abs(sample);

        if (sample < (1.0 / A)) {
            sample = (float) ((A * sample) / (1.0 + LN_A));
        } else {
            sample = (float) ((1.0 + log(A * sample)) / (1.0 + LN_A));
        }

        sample *= sign;

        long temp = (long) (sample * fullScale(8));

        if (temp < 0) {
            temp = -temp ^ 0x80L;
        }

        return temp ^ 0x55L;
    }
}
person Radiodef    schedule 09.11.2014
comment
Огромное желание и желание распространять знания проявляются в включении каждой детали предмета. +1. - person Dev_Man; 27.02.2017
comment
Я также получаю сообщение об ошибке при попытке создать экземпляр SourceDataLine в этом 24-битном образце (я еще не знаю, для чего используется этот класс, я следовал примеру кода из javadoc). Exception in thread "main" java.lang.IllegalArgumentException: No line matching interface SourceDataLine supporting format PCM_SIGNED 44100.0 Hz, 24 bit, stereo, 6 bytes/frame, little-endian is supported. - person Tobiq; 17.05.2020

Таким образом вы получаете фактические данные сэмплов из воспроизводимого в данный момент звука. другой отличный ответ расскажет вам, что означают данные. Я не пробовал его на другой ОС, кроме моей машины с Windows 10 YMMV. Для меня это тянет текущее системное записывающее устройство по умолчанию. В Windows установите для него значение «Стерео микс» вместо «Микрофон», чтобы воспроизводить звук. Возможно, вам придется переключить «Показать отключенные устройства», чтобы увидеть «Стереомикс».

import javax.sound.sampled.*;

public class SampleAudio {

    private static long extendSign(long temp, int bitsPerSample) {
        int extensionBits = 64 - bitsPerSample;
        return (temp << extensionBits) >> extensionBits;
    }

    public static void main(String[] args) throws LineUnavailableException {
        float sampleRate = 8000;
        int sampleSizeBits = 16;
        int numChannels = 1; // Mono
        AudioFormat format = new AudioFormat(sampleRate, sampleSizeBits, numChannels, true, true);
        TargetDataLine tdl = AudioSystem.getTargetDataLine(format);
        tdl.open(format);
        tdl.start();
        if (!tdl.isOpen()) {
            System.exit(1);         
        } 
        byte[] data = new byte[(int)sampleRate*10];
        int read = tdl.read(data, 0, (int)sampleRate*10);
        if (read > 0) {
            for (int i = 0; i < read-1; i = i + 2) {
                long val = ((data[i] & 0xffL) << 8L) | (data[i + 1] & 0xffL);
                long valf = extendSign(val, 16);
                System.out.println(i + "\t" + valf);
            }
        }
        tdl.close();
    }
}
person Carlos Rendon    schedule 07.08.2016