Как открыть 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;
}