java中的wav幅度(立体声或更多通道)

发布于 2024-08-21 03:00:37 字数 80 浏览 4 评论 0原文

大家好,有谁知道如何用 Java 查找 WAV 文件中的幅度吗?如果文件是立体声的(或有更多通道),如何将数据放入数组中?

谢谢!

Hi does anyone know how to find the amplitudes within a WAV file in Java? If the file was stereo (or has more channels) how can the data be put into arrays?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

Smile简单爱 2024-08-28 03:00:37

处理 WAV 文件头

下一个技巧还有更多这是一个挑战,因为内部数据格式可能是多种数据类型。如果您正在查看经典的 Windows WAV 文件,它可能只是 PCM 16 位或可能是 8 位。这意味着,您可以轻松地将数据加载到字节或短数组中。

但是,您会发现其他格式。当你知道自己的类型后,用谷歌搜索一下。您会找到大多数信息。

Processing a WAV file header

Next trick is a bit more of a challenge as the internal data format could be a variety of data types. If you are looking at your classic windows WAV file, it is probably just PCM 16 bit or maybe 8 bit. Which, means, you can easily load the data into a byte or short array.

However, you will find other formats. When you know the type you have, google it. You'll find information for most.

人心善变 2024-08-28 03:00:37

如何从 inputStream 打开 WAVE

// The WAVE-File-Reader of Java needs to reset on marks
final InputStream markSupportedInputStream;
if (inputStream.markSupported()) {
    markSupportedInputStream = inputStream;
} else {
    // BufferedInputStream wraps an InputStream, buffers the read data
    // and so it can reset on marks

    // Including RIFF header, format chunk and data chunk, standard
    // WAVE files have an overall header size of 44 bytes. 8192 Bytes should
    // be enough. Unconsidered are untypically chucks, like cue chunk,
    // playlist chunk etc.
    final int bufferSize = 8192;
    markSupportedInputStream = new BufferedInputStream(inputStream,
            bufferSize);
}

final AudioInputStream stream;
try {
    stream = AudioSystem.getAudioInputStream(markSupportedInputStream);
} catch (final UnsupportedAudioFileException e) {
    throw new UnsuportedFormatException();
}

final AudioFormat format = stream.getFormat();

final int numChannels = format.getChannels();

之后,典型的 WAVE 文件是 PCM 编码的(还有其他编解码器,如浮点数)。您必须从 markSupportedInputStream 读取示例。

PCM 包括许多参数组合:(单声道|立体声)、(有符号|无符号)、(8 位|16 位)、(8 位以上的大端|小端)。您可以在 format 对象上计算出这一点,例如 format.getChannels()。为此,我编写了一个 PcmCodec 类,其中包含诸如 decodeUnsigned16BitLittleEndian(buffer, offset) 之类的方法。我将样本值标准化为 [-1,1]。

以下是我如何找出 PCM 的方法:

public static boolean isAudioFormatSupported(
        final @NonNull AudioFormat format) {
    final Encoding encoding = format.getEncoding();
    final int numChannels = format.getChannels();
    final int sampleSizeBits = format.getSampleSizeInBits();

    final boolean encodingSupported = (encoding == Encoding.PCM_SIGNED || encoding == Encoding.PCM_UNSIGNED);
    final boolean channelsSupported = (numChannels == AudioSystem.NOT_SPECIFIED
            || numChannels == 1 || numChannels == 2);
    final boolean sampleSizeSupported = (sampleSizeBits == AudioSystem.NOT_SPECIFIED
            || sampleSizeBits == 8 || sampleSizeBits == 16);

    return encodingSupported && channelsSupported && sampleSizeSupported;
}

@NonNull
private static Format toInternalFormat(final @NonNull AudioFormat audioFormat) {
    final Format internalFormat;

    if (audioFormat.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED)) {
        switch (audioFormat.getSampleSizeInBits()) {
        case 8:
            internalFormat = Format.SIGNED_8_BIT;
            break;
        case 16:
        case AudioSystem.NOT_SPECIFIED:
            if (audioFormat.isBigEndian()) {
                internalFormat = Format.SIGNED_16_BIT_BIG_ENDIAN;
            } else {
                internalFormat = Format.SIGNED_16_BIT_LITTLE_ENDIAN;
            }
            break;
        default:
            throw new AssertionError(audioFormat.getSampleSizeInBits()
                    + " Bit not supported");
        }
    } else if (audioFormat.getEncoding().equals(
            AudioFormat.Encoding.PCM_UNSIGNED)) {
        switch (audioFormat.getSampleSizeInBits()) {
        case 8:
            internalFormat = Format.UNSIGNED_8_BIT;
            break;
        case 16:
        case AudioSystem.NOT_SPECIFIED:
            if (audioFormat.isBigEndian()) {
                internalFormat = Format.UNSIGNED_16_BIT_BIG_ENDIAN;
            } else {
                internalFormat = Format.UNSIGNED_16_BIT_LITTLE_ENDIAN;
            }
            break;
        default:
            throw new AssertionError(audioFormat.getSampleSizeInBits()
                    + " Bit not supported");
        }
    } else {
        throw new AssertionError("Neither PCM_SIGNED nor PCM_UNSIGNED");
    }

    return internalFormat;
}

以下是我如何解码特殊 PCM 的示例:
您需要从 markSupportedInputStream 读取到字节数组(缓冲区)。之后,您可以解码字节:

public float decodeMono(final @NonNull byte[] buffer, final int offset) {
    final float sample;
    switch (format) {
    case SIGNED_8_BIT:
        sample = decodeSigned8Bit(buffer, offset);
        break;
    case UNSIGNED_8_BIT:
        sample = decodeUnsigned8Bit(buffer, offset);
        break;
    case SIGNED_16_BIT_BIG_ENDIAN:
        sample = decodeSigned16BitBigEndian(buffer, offset);
        break;
    case SIGNED_16_BIT_LITTLE_ENDIAN:
        sample = decodeSigned16BitLittleEndian(buffer, offset);
        break;
    case UNSIGNED_16_BIT_BIG_ENDIAN:
        sample = decodeUnsigned16BitBigEndian(buffer, offset);
        break;
    case UNSIGNED_16_BIT_LITTLE_ENDIAN:
        sample = decodeUnsigned16BitLittleEndian(buffer, offset);
        break;
    default:
        throw new AssertionError();
    }

    return Util.clamp(sample, -1f, 1f);
}

private static float decodeUnsigned16BitBigEndian(
        final @NonNull byte[] buffer, final int offset) {
    final byte lower, higher;
    higher = buffer[offset];
    lower = buffer[offset + 1];
    final int sampleInt = ((higher & 0xff) << 8 | lower & 0xff) - 0x8000;
    final float sample = (float) sampleInt / (float) 0x7fff;
    return sample;
}

How to open a WAVE from inputStream

// The WAVE-File-Reader of Java needs to reset on marks
final InputStream markSupportedInputStream;
if (inputStream.markSupported()) {
    markSupportedInputStream = inputStream;
} else {
    // BufferedInputStream wraps an InputStream, buffers the read data
    // and so it can reset on marks

    // Including RIFF header, format chunk and data chunk, standard
    // WAVE files have an overall header size of 44 bytes. 8192 Bytes should
    // be enough. Unconsidered are untypically chucks, like cue chunk,
    // playlist chunk etc.
    final int bufferSize = 8192;
    markSupportedInputStream = new BufferedInputStream(inputStream,
            bufferSize);
}

final AudioInputStream stream;
try {
    stream = AudioSystem.getAudioInputStream(markSupportedInputStream);
} catch (final UnsupportedAudioFileException e) {
    throw new UnsuportedFormatException();
}

final AudioFormat format = stream.getFormat();

final int numChannels = format.getChannels();

After that, a typicall WAVE file is PCM encoded (there a other codecs, like floats). You have to read the samples from markSupportedInputStream.

PCM includes many combinations of parameters: (Mono|Stereo), (Signed|Unsigned), (8 Bit|16 Bit), (Big Endian|Little Endian for more than 8 Bit). You can figure out this on the format object like format.getChannels(). For this reason I have written a PcmCodec class with methods like decodeUnsigned16BitLittleEndian(buffer, offset). And I normalize the sample values to [-1,1].

Here is how I figure out what PCM it is:

public static boolean isAudioFormatSupported(
        final @NonNull AudioFormat format) {
    final Encoding encoding = format.getEncoding();
    final int numChannels = format.getChannels();
    final int sampleSizeBits = format.getSampleSizeInBits();

    final boolean encodingSupported = (encoding == Encoding.PCM_SIGNED || encoding == Encoding.PCM_UNSIGNED);
    final boolean channelsSupported = (numChannels == AudioSystem.NOT_SPECIFIED
            || numChannels == 1 || numChannels == 2);
    final boolean sampleSizeSupported = (sampleSizeBits == AudioSystem.NOT_SPECIFIED
            || sampleSizeBits == 8 || sampleSizeBits == 16);

    return encodingSupported && channelsSupported && sampleSizeSupported;
}

@NonNull
private static Format toInternalFormat(final @NonNull AudioFormat audioFormat) {
    final Format internalFormat;

    if (audioFormat.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED)) {
        switch (audioFormat.getSampleSizeInBits()) {
        case 8:
            internalFormat = Format.SIGNED_8_BIT;
            break;
        case 16:
        case AudioSystem.NOT_SPECIFIED:
            if (audioFormat.isBigEndian()) {
                internalFormat = Format.SIGNED_16_BIT_BIG_ENDIAN;
            } else {
                internalFormat = Format.SIGNED_16_BIT_LITTLE_ENDIAN;
            }
            break;
        default:
            throw new AssertionError(audioFormat.getSampleSizeInBits()
                    + " Bit not supported");
        }
    } else if (audioFormat.getEncoding().equals(
            AudioFormat.Encoding.PCM_UNSIGNED)) {
        switch (audioFormat.getSampleSizeInBits()) {
        case 8:
            internalFormat = Format.UNSIGNED_8_BIT;
            break;
        case 16:
        case AudioSystem.NOT_SPECIFIED:
            if (audioFormat.isBigEndian()) {
                internalFormat = Format.UNSIGNED_16_BIT_BIG_ENDIAN;
            } else {
                internalFormat = Format.UNSIGNED_16_BIT_LITTLE_ENDIAN;
            }
            break;
        default:
            throw new AssertionError(audioFormat.getSampleSizeInBits()
                    + " Bit not supported");
        }
    } else {
        throw new AssertionError("Neither PCM_SIGNED nor PCM_UNSIGNED");
    }

    return internalFormat;
}

Here is an example how I decode a special PCM:
You need to read from markSupportedInputStream into a byte array (buffer). After that, you can decode the bytes:

public float decodeMono(final @NonNull byte[] buffer, final int offset) {
    final float sample;
    switch (format) {
    case SIGNED_8_BIT:
        sample = decodeSigned8Bit(buffer, offset);
        break;
    case UNSIGNED_8_BIT:
        sample = decodeUnsigned8Bit(buffer, offset);
        break;
    case SIGNED_16_BIT_BIG_ENDIAN:
        sample = decodeSigned16BitBigEndian(buffer, offset);
        break;
    case SIGNED_16_BIT_LITTLE_ENDIAN:
        sample = decodeSigned16BitLittleEndian(buffer, offset);
        break;
    case UNSIGNED_16_BIT_BIG_ENDIAN:
        sample = decodeUnsigned16BitBigEndian(buffer, offset);
        break;
    case UNSIGNED_16_BIT_LITTLE_ENDIAN:
        sample = decodeUnsigned16BitLittleEndian(buffer, offset);
        break;
    default:
        throw new AssertionError();
    }

    return Util.clamp(sample, -1f, 1f);
}

private static float decodeUnsigned16BitBigEndian(
        final @NonNull byte[] buffer, final int offset) {
    final byte lower, higher;
    higher = buffer[offset];
    lower = buffer[offset + 1];
    final int sampleInt = ((higher & 0xff) << 8 | lower & 0xff) - 0x8000;
    final float sample = (float) sampleInt / (float) 0x7fff;
    return sample;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文