амплитуда WAV в Java (стерео или более каналов) - PullRequest
2 голосов
/ 10 февраля 2010

Привет, кто-нибудь знает, как найти амплитуды в файле WAV на Java? Если файл был стерео (или имеет больше каналов), как данные могут быть помещены в массивы?

Спасибо!

Ответы [ 2 ]

2 голосов
/ 10 февраля 2010

Обработка заголовка файла WAV

Следующий трюк представляет собой более сложную задачу, поскольку внутренний формат данных может быть различных типов данных.Если вы смотрите на свой классический Windows WAV-файл, он, вероятно, просто 16-битный PCM или, может быть, 8-битный.Это означает, что вы можете легко загрузить данные в байтовый или короткий массив.

Однако вы найдете другие форматы.Когда вы знаете тип, который у вас есть, Google его.Вы найдете информацию для большинства.

0 голосов
/ 05 февраля 2015

Как открыть WAVE из 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();

После этого типичный файл WAVE кодируется в формате PCM (есть другие кодеки, например, с плавающей точкой). Вы должны прочитать образцы из markSupportedInputStream.

PCM включает в себя множество комбинаций параметров: (Mono | Stereo), (Signed | Unsigned), (8 Bit | 16 Bit), (Big Endian | Little Endian для более чем 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;
}
...