19

这个问题通常作为另一个问题的一部分被问到,但事实证明答案很长。我决定在这里回答它,以便我可以在其他地方链接到它。

虽然我目前不知道 Java 可以为我们生成音频样本的方式,但如果将来发生变化,这可能是它的一个地方。我知道JavaFX有一些类似的东西,例如AudioSpectrumListener,但仍然不是直接访问示例的方法。


我正在javax.sound.sampled用于播放和/或录制,但我想对音频做点什么。

也许我想直观地显示它或以某种方式处理它。

如何使用 Java Sound 访问音频样本数据?

也可以看看:

4

2 回答 2

65

嗯,最简单的答案是,目前 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;数组,byteAudioInputStream.
  • float[] samples;我们要填充的输出样本数组。
  • float sample;我们目前正在处理的样本。
  • long temp;用于一般操作的中间值。
  • int i;byte数组中当前样本数据开始的位置。

我们将float[]数组中的所有样本标准化为-1f <= sample <= 1f. 我见过的所有浮点音频都是这样来的,而且非常方便。

如果我们的源音频还不是这样(例如整数样本),我们可以使用以下方法自行对其进行归一化:

sample = sample / fullScale(bitsPerSample);

fullScale2 bitsPerSample-1在哪里,即Math.pow(2, bitsPerSample-1)


如何将byte数组强制转换为有意义的数据?

byte数组包含拆分并排成一行的样本帧。这实际上非常简单,除了称为endianness的东西,它是byte每个样本数据包中 s 的顺序。

这是一个图表。这个样本(打包到一个byte数组中)保存十进制值 9999:

  24 位样本作为大端:

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

 24 位样本为 little-endian:

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

它们具有相同的二进制值;但是,byte顺序颠倒了。

  • 在 big-endian 中,较重要的 s 位于较不重要byte的 s 之前byte
  • 在 little-endian 中,较不重要byte的 s 在较重要的 s 之前bytes

WAV文件以 little-endian 顺序存储,AIFF 文件以 big-endian 顺序存储。字节序可以从 获得AudioFormat.isBigEndian

为了连接bytes 并将它们放入我们的long temp变量中,我们:

  1. 将每个byte与掩码0xFF(即0b1111_1111)按位与,以避免在自动提升时进行符号扩展。byte( char,byte并在对它们执行算术运算时short提升int。) 另请参阅Java 中的作用是什么value & 0xff
  2. 将每个位移byte入位置。
  3. 按位或bytes 在一起。

这是一个 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)
    );
}

请注意,移位顺序是根据字节顺序颠倒的。

这也可以概括为一个循环,可以在这个答案底部的完整代码中看到。(参见unpackAnyBitpackAnyBit方法。)

现在我们已经将bytes 连接在一起,我们可以采取更多步骤将它们变成样本。接下来的步骤取决于实际的编码。

我如何解码Encoding.PCM_SIGNED

必须扩展二进制补码符号。这意味着如果最高有效位 (MSB) 设置为 1,我们将用 1 填充其上方的所有位。如果设置了符号位,算术右移 ( >>) 将自动为我们完成填充,所以我通常这样做:

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

(哪里Long.SIZE是 64。如果我们的temp变量不是 a long,我们会使用别的东西。如果我们使用 egint temp代替,我们会使用 32。)

为了理解这是如何工作的,下面是一个将 8 位符号扩展为 16 位的图表:

11111111是字节值-1,但是short的高位是0。
 将字节的 MSB 移入短路的 MSB 位置。

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

 将其移回,右移将所有高位填充为 1。
 我们现在有了 -1 的空头值。

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

正值(MSB 中为 0)保持不变。这是算术右移的一个很好的属性。

然后对样本进行归一化,如一些假设中所述。

如果您的代码很简单,您可能不需要编写显式符号扩展

当从一种整数类型转换为更大的类型时,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 通常是 IEEE 32 位或 IEEE 64 位,并且已经标准化为±1.0. 样本可以通过实用方法Float#intBitsToFloatDouble#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.ULAWEncoding.ALAW

这些是在电话等中更常见的压扩压缩编解码器。我假设它们支持它们,javax.sound.sampled因为它们被Sun 的 Au 格式使用。(但是,它不仅限于这种类型的容器。例如,WAV 可以包含这些编码。)

您可以将A-lawμ-law概念化,就像它们是浮点格式一样。这些是 PCM 格式,但值的范围是非线性的。

有两种解码它们的方法。我将展示使用数学公式的方式。您还可以通过直接操作此博客文章中描述的二进制文件来解码它们,但它看起来更深奥。

对于两者,压缩数据都是 8 位的。标准 A 律在解码时为 13 位,μ 律在解码时为 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);

现在我们可以应用扩展。

翻译成 Java 的 μ-law 公式为:

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

转换为 Java 的 A-law 公式为:

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;
    }
}
于 2014-11-09T03:28:19.793 回答
0

这是您从当前播放的声音中获取实际样本数据的方式。另一个出色的答案将告诉您数据的含义。除了我的 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();
    }
}
于 2016-08-07T18:22:24.077 回答