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

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

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


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

Возможно я хотел бы показать это визуально или обработать это каким-то образом.

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

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

2 ответа

Решение

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

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

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

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

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

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

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

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

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

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


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

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

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

Здесь показан синусоидальный сигнал, дискретизированный и квантованный до 4-битного:

lpcm_graph

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

Когда у нас есть аудио на компьютере, у нас есть массив этих образцов. Образец массива - это то, что мы хотим превратить 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 Массив содержит примерные кадры, разделенные и все в строке. Это на самом деле очень просто, за исключением того, что называется порядком байтов, который является порядком byte в каждом образце пакета.

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

 24-битный сэмпл как big-endian:

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

 24-битный сэмпл как little-endian:

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

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

  • В big-endian, тем более значимым byte перед менее значимым byte s.
  • В младшем порядке, менее значимым byte перед более значительным bytes,

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

Объединить byte и положить их в наш 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, мы заполняем все биты над ним 1. Арифметическое смещение вправо (>>) заполняет нас автоматически, если установлен бит знака, поэтому я обычно делаю это так:

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

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

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

 11111111 - это значение байта -1, но старшие биты короткого замыкания равны 0.
 Сдвиньте MSB байта в позицию MSB короткого замыкания.

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

 Сдвиньте его назад, и сдвиг вправо заполнит все верхние биты единицами.
 Теперь у нас есть короткое значение -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?

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

Вы можете осмыслить A-закон и μ-закон, как если бы они были в формате с плавающей запятой. These are PCM formats but the range of values is non-linear.

There are two ways to decode them. I'll show the way which uses the mathematical formula. You can also decode them by manipulating the binary directly which is described in this blog post but it's more esoteric-looking.

For both, the compressed data is 8-bit. Standardly A-law is 13-bit when decoded and μ-law is 14-bit when decoded; however, applying the formula yields a range of ±1.0,

Before you can apply the formula, there are three things to do:

  1. Some of the bits are standardly inverted for storage due to reasons involving data integrity.
  2. They're stored as sign and magnitude (rather than two's complement).
  3. The formula also expects a range of ±1.0, so the 8-bit value has to be scaled.

For μ-law all the bits are inverted, so:

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

(Note that we can't use ~, because we don't want to invert the high bits of the long.)

For A-law, every other bit is inverted, so:

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

(XOR can be used to do inversion. See How do you set, clear and toggle a bit?)

To convert from sign and magnitude to two's complement, we:

  1. Check to see if the sign bit was set.
  2. If so, clear the sign bit and negate the number.
// 0x80 == 0b1000_0000
if ((temp & 0x80L) != 0) {
    temp ^= 0x80L;
    temp = -temp;
}

Then scale the encoded numbers, the same way as described in Some Assumptions:

sample = temp / fullScale(8);

Now we can apply the expansion.

The μ-law formula translated to Java is then:

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

The A-law formula translated to Java is then:

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;

Here's the full example code for the 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://stackru.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;
    }
}

Вот как вы получаете фактические данные сэмпла из воспроизводимого в данный момент звука. Другой отличный ответ скажет вам, что означают данные. Не пробовал на другой ОС, кроме моей машины с Windows 10 YMMV. Для меня это тянет текущее системное устройство записи по умолчанию. В Windows установите "Stereo Mix" вместо "Microphone" для воспроизведения звука. Возможно, вам придется переключить "Показать отключенные устройства", чтобы увидеть "Stereo Mix".

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();
    }
}
Другие вопросы по тегам