Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/media/sound/AiffFileReader.java
38924 views
/*1* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.media.sound;2627import java.io.DataInputStream;28import java.io.DataOutputStream;29import java.io.File;30import java.io.FileInputStream;31import java.io.IOException;32import java.io.InputStream;33import java.net.URL;3435import javax.sound.sampled.AudioFileFormat;36import javax.sound.sampled.AudioFormat;37import javax.sound.sampled.AudioInputStream;38import javax.sound.sampled.AudioSystem;39import javax.sound.sampled.UnsupportedAudioFileException;404142/**43* AIFF file reader and writer.44*45* @author Kara Kytle46* @author Jan Borgersen47* @author Florian Bomers48*/49public final class AiffFileReader extends SunFileReader {5051private static final int MAX_READ_LENGTH = 8;5253// METHODS TO IMPLEMENT AudioFileReader5455/**56* Obtains the audio file format of the input stream provided. The stream must57* point to valid audio file data. In general, audio file providers may58* need to read some data from the stream before determining whether they59* support it. These parsers must60* be able to mark the stream, read enough data to determine whether they61* support the stream, and, if not, reset the stream's read pointer to its original62* position. If the input stream does not support this, this method may fail63* with an IOException.64* @param stream the input stream from which file format information should be65* extracted66* @return an <code>AudioFileFormat</code> object describing the audio file format67* @throws UnsupportedAudioFileException if the stream does not point to valid audio68* file data recognized by the system69* @throws IOException if an I/O exception occurs70* @see InputStream#markSupported71* @see InputStream#mark72*/73public AudioFileFormat getAudioFileFormat(InputStream stream) throws UnsupportedAudioFileException, IOException {74// fix for 4489272: AudioSystem.getAudioFileFormat() fails for InputStream, but works for URL75AudioFileFormat aff = getCOMM(stream, true);76// the following is not strictly necessary - but was implemented like that in 1.3.0 - 1.4.177// so I leave it as it was. May remove this for 1.5.078stream.reset();79return aff;80}818283/**84* Obtains the audio file format of the URL provided. The URL must85* point to valid audio file data.86* @param url the URL from which file format information should be87* extracted88* @return an <code>AudioFileFormat</code> object describing the audio file format89* @throws UnsupportedAudioFileException if the URL does not point to valid audio90* file data recognized by the system91* @throws IOException if an I/O exception occurs92*/93public AudioFileFormat getAudioFileFormat(URL url) throws UnsupportedAudioFileException, IOException {94AudioFileFormat fileFormat = null;95InputStream urlStream = url.openStream(); // throws IOException96try {97fileFormat = getCOMM(urlStream, false);98} finally {99urlStream.close();100}101return fileFormat;102}103104105/**106* Obtains the audio file format of the File provided. The File must107* point to valid audio file data.108* @param file the File from which file format information should be109* extracted110* @return an <code>AudioFileFormat</code> object describing the audio file format111* @throws UnsupportedAudioFileException if the File does not point to valid audio112* file data recognized by the system113* @throws IOException if an I/O exception occurs114*/115public AudioFileFormat getAudioFileFormat(File file) throws UnsupportedAudioFileException, IOException {116AudioFileFormat fileFormat = null;117FileInputStream fis = new FileInputStream(file); // throws IOException118// part of fix for 4325421119try {120fileFormat = getCOMM(fis, false);121} finally {122fis.close();123}124125return fileFormat;126}127128129130131/**132* Obtains an audio stream from the input stream provided. The stream must133* point to valid audio file data. In general, audio file providers may134* need to read some data from the stream before determining whether they135* support it. These parsers must136* be able to mark the stream, read enough data to determine whether they137* support the stream, and, if not, reset the stream's read pointer to its original138* position. If the input stream does not support this, this method may fail139* with an IOException.140* @param stream the input stream from which the <code>AudioInputStream</code> should be141* constructed142* @return an <code>AudioInputStream</code> object based on the audio file data contained143* in the input stream.144* @throws UnsupportedAudioFileException if the stream does not point to valid audio145* file data recognized by the system146* @throws IOException if an I/O exception occurs147* @see InputStream#markSupported148* @see InputStream#mark149*/150public AudioInputStream getAudioInputStream(InputStream stream) throws UnsupportedAudioFileException, IOException {151// getCOMM leaves the input stream at the beginning of the audio data152AudioFileFormat fileFormat = getCOMM(stream, true); // throws UnsupportedAudioFileException, IOException153154// we've got everything, and the stream is at the155// beginning of the audio data, so return an AudioInputStream.156return new AudioInputStream(stream, fileFormat.getFormat(), fileFormat.getFrameLength());157}158159160/**161* Obtains an audio stream from the URL provided. The URL must162* point to valid audio file data.163* @param url the URL for which the <code>AudioInputStream</code> should be164* constructed165* @return an <code>AudioInputStream</code> object based on the audio file data pointed166* to by the URL167* @throws UnsupportedAudioFileException if the URL does not point to valid audio168* file data recognized by the system169* @throws IOException if an I/O exception occurs170*/171public AudioInputStream getAudioInputStream(URL url) throws UnsupportedAudioFileException, IOException {172InputStream urlStream = url.openStream(); // throws IOException173AudioFileFormat fileFormat = null;174try {175fileFormat = getCOMM(urlStream, false);176} finally {177if (fileFormat == null) {178urlStream.close();179}180}181return new AudioInputStream(urlStream, fileFormat.getFormat(), fileFormat.getFrameLength());182}183184185/**186* Obtains an audio stream from the File provided. The File must187* point to valid audio file data.188* @param file the File for which the <code>AudioInputStream</code> should be189* constructed190* @return an <code>AudioInputStream</code> object based on the audio file data pointed191* to by the File192* @throws UnsupportedAudioFileException if the File does not point to valid audio193* file data recognized by the system194* @throws IOException if an I/O exception occurs195*/196public AudioInputStream getAudioInputStream(File file)197throws UnsupportedAudioFileException, IOException {198199FileInputStream fis = new FileInputStream(file); // throws IOException200AudioFileFormat fileFormat = null;201// part of fix for 4325421202try {203fileFormat = getCOMM(fis, false);204} finally {205if (fileFormat == null) {206fis.close();207}208}209return new AudioInputStream(fis, fileFormat.getFormat(), fileFormat.getFrameLength());210}211212//--------------------------------------------------------------------213214private AudioFileFormat getCOMM(InputStream is, boolean doReset)215throws UnsupportedAudioFileException, IOException {216217DataInputStream dis = new DataInputStream(is);218219if (doReset) {220dis.mark(MAX_READ_LENGTH);221}222223// assumes a stream at the beginning of the file which has already224// passed the magic number test...225// leaves the input stream at the beginning of the audio data226int fileRead = 0;227int dataLength = 0;228AudioFormat format = null;229230// Read the magic number231int magic = dis.readInt();232233// $$fb: fix for 4369044: javax.sound.sampled.AudioSystem.getAudioInputStream() works wrong with Cp037234if (magic != AiffFileFormat.AIFF_MAGIC) {235// not AIFF, throw exception236if (doReset) {237dis.reset();238}239throw new UnsupportedAudioFileException("not an AIFF file");240}241242int length = dis.readInt();243int iffType = dis.readInt();244fileRead += 12;245246int totallength;247if(length <= 0 ) {248length = AudioSystem.NOT_SPECIFIED;249totallength = AudioSystem.NOT_SPECIFIED;250} else {251totallength = length + 8;252}253254// Is this an AIFC or just plain AIFF file.255boolean aifc = false;256// $$fb: fix for 4369044: javax.sound.sampled.AudioSystem.getAudioInputStream() works wrong with Cp037257if (iffType == AiffFileFormat.AIFC_MAGIC) {258aifc = true;259}260261// Loop through the AIFF chunks until262// we get to the SSND chunk.263boolean ssndFound = false;264while (!ssndFound) {265// Read the chunk name266int chunkName = dis.readInt();267int chunkLen = dis.readInt();268fileRead += 8;269270int chunkRead = 0;271272// Switch on the chunk name.273switch (chunkName) {274case AiffFileFormat.FVER_MAGIC:275// Ignore format version for now.276break;277278case AiffFileFormat.COMM_MAGIC:279// AIFF vs. AIFC280// $$fb: fix for 4399551: Repost of bug candidate: cannot replay aif file (Review ID: 108108)281if ((!aifc && chunkLen < 18) || (aifc && chunkLen < 22)) {282throw new UnsupportedAudioFileException("Invalid AIFF/COMM chunksize");283}284// Read header info.285int channels = dis.readUnsignedShort();286if (channels <= 0) {287throw new UnsupportedAudioFileException("Invalid number of channels");288}289dis.readInt(); // numSampleFrames290int sampleSizeInBits = dis.readUnsignedShort();291if (sampleSizeInBits < 1 || sampleSizeInBits > 32) {292throw new UnsupportedAudioFileException("Invalid AIFF/COMM sampleSize");293}294float sampleRate = (float) read_ieee_extended(dis);295chunkRead += (2 + 4 + 2 + 10);296297// If this is not AIFC then we assume it's298// a linearly encoded file.299AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;300301if (aifc) {302int enc = dis.readInt(); chunkRead += 4;303switch (enc) {304case AiffFileFormat.AIFC_PCM:305encoding = AudioFormat.Encoding.PCM_SIGNED;306break;307case AiffFileFormat.AIFC_ULAW:308encoding = AudioFormat.Encoding.ULAW;309sampleSizeInBits = 8; // Java Sound convention310break;311default:312throw new UnsupportedAudioFileException("Invalid AIFF encoding");313}314}315int frameSize = calculatePCMFrameSize(sampleSizeInBits, channels);316//$fb what's that ??317//if (sampleSizeInBits == 8) {318// encoding = AudioFormat.Encoding.PCM_SIGNED;319//}320format = new AudioFormat(encoding, sampleRate,321sampleSizeInBits, channels,322frameSize, sampleRate, true);323break;324case AiffFileFormat.SSND_MAGIC:325// Data chunk.326// we are getting *weird* numbers for chunkLen sometimes;327// this really should be the size of the data chunk....328int dataOffset = dis.readInt();329int blocksize = dis.readInt();330chunkRead += 8;331332// okay, now we are done reading the header. we need to set the size333// of the data segment. we know that sometimes the value we get for334// the chunksize is absurd. this is the best i can think of:if the335// value seems okay, use it. otherwise, we get our value of336// length by assuming that everything left is the data segment;337// its length should be our original length (for all AIFF data chunks)338// minus what we've read so far.339// $$kk: we should be able to get length for the data chunk right after340// we find "SSND." however, some aiff files give *weird* numbers. what341// is going on??342343if (chunkLen < length) {344dataLength = chunkLen - chunkRead;345} else {346// $$kk: 11.03.98: this seems dangerous!347dataLength = length - (fileRead + chunkRead);348}349ssndFound = true;350break;351} // switch352fileRead += chunkRead;353// skip the remainder of this chunk354if (!ssndFound) {355int toSkip = chunkLen - chunkRead;356if (toSkip > 0) {357fileRead += dis.skipBytes(toSkip);358}359}360} // while361362if (format == null) {363throw new UnsupportedAudioFileException("missing COMM chunk");364}365AudioFileFormat.Type type = aifc?AudioFileFormat.Type.AIFC:AudioFileFormat.Type.AIFF;366367return new AiffFileFormat(type, totallength, format, dataLength / format.getFrameSize());368}369370// HELPER METHODS371/** write_ieee_extended(DataOutputStream dos, double f) throws IOException {372* Extended precision IEEE floating-point conversion routine.373* @argument DataOutputStream374* @argument double375* @return void376* @exception IOException377*/378private void write_ieee_extended(DataOutputStream dos, double f) throws IOException {379380int exponent = 16398;381double highMantissa = f;382383// For now write the integer portion of f384// $$jb: 03.30.99: stay in synch with JMF on this!!!!385while (highMantissa < 44000) {386highMantissa *= 2;387exponent--;388}389dos.writeShort(exponent);390dos.writeInt( ((int) highMantissa) << 16);391dos.writeInt(0); // low Mantissa392}393394395/**396* read_ieee_extended397* Extended precision IEEE floating-point conversion routine.398* @argument DataInputStream399* @return double400* @exception IOException401*/402private double read_ieee_extended(DataInputStream dis) throws IOException {403404double f = 0;405int expon = 0;406long hiMant = 0, loMant = 0;407long t1, t2;408double HUGE = ((double)3.40282346638528860e+38);409410411expon = dis.readUnsignedShort();412413t1 = (long)dis.readUnsignedShort();414t2 = (long)dis.readUnsignedShort();415hiMant = t1 << 16 | t2;416417t1 = (long)dis.readUnsignedShort();418t2 = (long)dis.readUnsignedShort();419loMant = t1 << 16 | t2;420421if (expon == 0 && hiMant == 0 && loMant == 0) {422f = 0;423} else {424if (expon == 0x7FFF)425f = HUGE;426else {427expon -= 16383;428expon -= 31;429f = (hiMant * Math.pow(2, expon));430expon -= 32;431f += (loMant * Math.pow(2, expon));432}433}434435return f;436}437}438439440