Подтвердить что ты не робот

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

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

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


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

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

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

См. также:

4b9b3361

Ответ 1

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

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

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

Это очень длинный ответ, но я хотел дать как можно более полное описание.


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

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

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

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

lpcm_graph

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

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


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

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

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

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

Масштабирование просто, просто:

sample = sample / fullScale(bitsPerSample);

Где fullScale - 2 bitsPerSample - 1.


Как я могу принудить массив байтов к значимым данным?

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

Вот диаграмма. Этот пакет содержит десятичное значение 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

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

  • В биг-эндиантах более значимые байты появляются перед менее значительными байтами.
  • В little-endian младшие байты перед более значительными байтами.

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

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

  • Побитовый И каждый байт с маской 0xFF (которая 0b1111_1111), чтобы избежать расширения знака, когда байт автоматически продвигается. (char, байт и короткий передаются на int, когда на них выполняется арифметика.)
  • Бит сдвигает каждый байт в позицию.
  • Побитовое ИЛИ байты вместе.

Вот пример из 24 бит:

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

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

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

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

Как декодировать Encoding.PCM_SIGNED?

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

int extensionBits = bitsPerLong - bitsPerSample;
sample = (temp << extensionBits) >> extensionBits.

(Где bitsPerLong равно 64.)

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

 This is the byte value -1 but the upper bits of the short are 0.
 Shift the byte 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 a 1.
 We now have the short value of -1.

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

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

Затем масштабируйте его.

Как декодировать Encoding.PCM_UNSIGNED?

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

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

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

sample = temp - fullScale(bitsPerSample);

Затем масштабируйте его.

Как декодировать Encoding.PCM_FLOAT?

Это новое, поскольку Java 7.

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

// IEEE 32-bit
sample = Float.intBitsToFloat((int) temp);
// IEEE 64-bit
sample = (float) Double.longBitsToDouble(temp);

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

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

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

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

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

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

  • Некоторые из битов стандартно инвертируются для хранения из-за некоторой архаической причины, связанной с целостностью данных.
  • Они хранятся как знак и величина, а не два дополнения.
  • Уравнение также ожидает диапазон ±1.0, поэтому необходимо масштабировать 8-битное значение.

Для & mu; -law все биты инвертируются так:

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

Для закона А каждый другой бит инвертируется так:

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

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

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

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

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

sample = temp / fullScale(8);

Теперь мы можем применить разложение.

Уравнение & mu; -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.ceil;
import static java.lang.Math.pow;
import static java.lang.Math.signum;
import static java.lang.Math.abs;
import static java.lang.Math.log;
import static java.lang.Math.exp;

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

    /**
     * Converts:
     * <ul>
     * <li>from a byte array ({@code byte[]})
     * <li>to an audio sample array ({@code float[]}).
     * </ul>
     * 
     * @param bytes   the byte array, filled by the {@code InputStream}.
     * @param samples an array to fill up with audio samples.
     * @param blen    the return value of {@code InputStream.read}.
     * @param fmt     the source {@code AudioFormat}.
     * 
     * @return the number of valid audio samples converted.
     * 
     * @throws NullPointerException
     *  if {@code bytes}, {@code samples} or {@code fmt} is {@code null}
     * @throws ArrayIndexOutOfBoundsException
     *  if {@code (bytes.length < blen)}
     *  or {@code (samples.length < blen / bytesPerSample(fmt.getBitsPerSample()))}.
     */
    public static int unpack(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 = signUnsigned(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:
     * <ul>
     * <li>from an audio sample array ({@code float[]})
     * <li>to a byte array ({@code byte[]}).
     * </ul>
     * 
     * @param samples an array of audio samples to encode.
     * @param bytes   an array to fill up with bytes.
     * @param slen    the return value of {@code unpack}.
     * @param fmt     the destination {@code AudioFormat}.
     * 
     * @return the number of valid bytes converted.
     * 
     * @throws NullPointerException
     *  if {@code samples}, {@code bytes} or {@code fmt} is {@code null}
     * @throws ArrayIndexOutOfBoundsException
     *  if {@code(samples.length < slen)}
     *  or {@code (bytes.length < slen * bytesPerSample(fmt.getSampleSizeInBits()))}
     */
    public static int pack(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 = unsignSigned(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,
     * with {@code (int) ceil(bitsPerSample / 8.0)}.
     * <p>
     * This is generally equivalent to the optimization
     * {@code ((bitsPerSample + 7) >>> 3)}. (Except for
     * the invalid argument {@code bitsPerSample <= 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 {@code 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);
    }

    /**
     * Computes the largest magnitude representable by the audio format,
     * with {@code pow(2.0, bitsPerSample - 1)}.
     * <p>
     * For {@code bitsPerSample < 64}, this is generally equivalent to
     * the optimization {@code (1L << (bitsPerSample - 1L))}. (Except for
     * the invalid argument {@code bitsPerSample <= 0}.)
     * <p>
     * The result is returned as a {@code double} because, in the case that
     * {@code bitsPerSample == 64}, a {@code long} would overflow.
     * 
     * @param bitsPerSample the return value of {@code AudioFormat.getBitsPerSample}.
     * @return the largest magnitude representable by the audio format.
     */
    public static double fullScale(int bitsPerSample) {
        return pow(2.0, 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) << 8L)
                |  (bytes[i + 1] & 0xffL)
            );
        } else {
            return (
                   (bytes[i    ] & 0xffL)
                | ((bytes[i + 1] & 0xffL) << 8L)
            );
        }
    }

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

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

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

    private static void pack24Bit(byte[]  bytes,
                                  int     i,
                                  long    temp,
                                  boolean isBigEndian) {
        if (isBigEndian) {
            bytes[i    ] = (byte) ((temp >>> 16L) & 0xffL);
            bytes[i + 1] = (byte) ((temp >>>  8L) & 0xffL);
            bytes[i + 2] = (byte) ( temp          & 0xffL);
        } else {
            bytes[i    ] = (byte) ( temp          & 0xffL);
            bytes[i + 1] = (byte) ((temp >>>  8L) & 0xffL);
            bytes[i + 2] = (byte) ((temp >>> 16L) & 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 >>> (8L * (bytesPerSample - b - 1L))) & 0xffL
                );
            }
        } else {
            for (int b = 0; b < bytesPerSample; b++) {
                bytes[i + b] = (byte) ((temp >>> (8L * b)) & 0xffL);
            }
        }
    }

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

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

    private static long unsignSigned(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;
    // reciprocal of A
    private static final double RE_A = 1.0 / A;
    // natural logarithm of A
    private static final double LN_A = log(A);
    // if values are below this, the A-law exponent is 0
    private static final double EXP_0 = 1.0 / (1.0 + LN_A);

    private static float bitsToMuLaw(long temp) {
        temp ^= 0xffL;
        if ((temp & 0x80L) == 0x80L) {
            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 < 0L) {
            temp = -temp ^ 0x80L;
        }

        return temp ^ 0xffL;
    }

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

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

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

        if (sample < EXP_0) {
            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 < RE_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 < 0L) {
            temp = -temp ^ 0x80L;
        }

        return temp ^ 0x55L;
    }
}

Ответ 2

Вот как вы получаете фактические данные образца из текущего воспроизводимого звука. отличный отличный ответ скажет вам, что означают данные. Не пробовал это на другой ОС, чем на моем компьютере с Windows 10 YMMV. Для меня он вытягивает текущее системное устройство записи по умолчанию. В Windows установите для него "Stereo Mix" вместо "Microphone", чтобы получить звуковой сигнал. Возможно, вам нужно переключить "Показать отключенные устройства", чтобы увидеть "Стерео 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();
    }
}