/* * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.media.sound; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import javax.sound.sampled.AudioFileFormat.Type; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.UnsupportedAudioFileException; /** * AIFF file reader and writer. * * @author Kara Kytle * @author Jan Borgersen * @author Florian Bomers */ public final class AiffFileReader extends SunFileReader { @Override StandardFileFormat getAudioFileFormatImpl(final InputStream stream) throws UnsupportedAudioFileException, IOException { DataInputStream dis = new DataInputStream(stream); AudioFormat format = null; // Read the magic number int magic = dis.readInt(); // $$fb: fix for 4369044: javax.sound.sampled.AudioSystem.getAudioInputStream() works wrong with Cp037 if (magic != AiffFileFormat.AIFF_MAGIC) { // not AIFF, throw exception throw new UnsupportedAudioFileException("not an AIFF file"); } long /* unsigned 32bit */ frameLength = 0; int length = dis.readInt(); int iffType = dis.readInt(); final long totallength; if(length <= 0 ) { length = AudioSystem.NOT_SPECIFIED; totallength = AudioSystem.NOT_SPECIFIED; } else { totallength = length + 8; } // Is this an AIFC or just plain AIFF file. boolean aifc = false; // $$fb: fix for 4369044: javax.sound.sampled.AudioSystem.getAudioInputStream() works wrong with Cp037 if (iffType == AiffFileFormat.AIFC_MAGIC) { aifc = true; } // Loop through the AIFF chunks until // we get to the SSND chunk. boolean ssndFound = false; while (!ssndFound) { // Read the chunk name int chunkName = dis.readInt(); int chunkLen = dis.readInt(); int chunkRead = 0; // Switch on the chunk name. switch (chunkName) { case AiffFileFormat.FVER_MAGIC: // Ignore format version for now. break; case AiffFileFormat.COMM_MAGIC: // AIFF vs. AIFC // $$fb: fix for 4399551: Repost of bug candidate: cannot replay aif file (Review ID: 108108) if ((!aifc && chunkLen < 18) || (aifc && chunkLen < 22)) { throw new UnsupportedAudioFileException("Invalid AIFF/COMM chunksize"); } // Read header info. int channels = dis.readUnsignedShort(); if (channels <= 0) { throw new UnsupportedAudioFileException("Invalid number of channels"); } frameLength = dis.readInt() & 0xffffffffL; // numSampleFrames int sampleSizeInBits = dis.readUnsignedShort(); if (sampleSizeInBits < 1 || sampleSizeInBits > 32) { throw new UnsupportedAudioFileException("Invalid AIFF/COMM sampleSize"); } float sampleRate = (float) read_ieee_extended(dis); chunkRead += (2 + 4 + 2 + 10); // If this is not AIFC then we assume it's // a linearly encoded file. AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED; if (aifc) { int enc = dis.readInt(); chunkRead += 4; switch (enc) { case AiffFileFormat.AIFC_PCM: encoding = AudioFormat.Encoding.PCM_SIGNED; break; case AiffFileFormat.AIFC_ULAW: encoding = AudioFormat.Encoding.ULAW; sampleSizeInBits = 8; // Java Sound convention break; default: throw new UnsupportedAudioFileException("Invalid AIFF encoding"); } } int frameSize = calculatePCMFrameSize(sampleSizeInBits, channels); //$fb what's that ?? //if (sampleSizeInBits == 8) { // encoding = AudioFormat.Encoding.PCM_SIGNED; //} format = new AudioFormat(encoding, sampleRate, sampleSizeInBits, channels, frameSize, sampleRate, true); break; case AiffFileFormat.SSND_MAGIC: // Data chunk. int dataOffset = dis.readInt(); // for now unused in javasound int blocksize = dis.readInt(); // for now unused in javasound chunkRead += 8; ssndFound = true; break; } // switch // skip the remainder of this chunk if (!ssndFound) { int toSkip = chunkLen - chunkRead; if (toSkip > 0) { dis.skipBytes(toSkip); } } } // while if (format == null) { throw new UnsupportedAudioFileException("missing COMM chunk"); } Type type = aifc ? Type.AIFC : Type.AIFF; return new AiffFileFormat(type, totallength, format, frameLength); } // HELPER METHODS /** * read_ieee_extended * Extended precision IEEE floating-point conversion routine. * @argument DataInputStream * @return double * @exception IOException */ private double read_ieee_extended(DataInputStream dis) throws IOException { double f = 0; int expon = 0; long hiMant = 0, loMant = 0; long t1, t2; double HUGE = 3.40282346638528860e+38; expon = dis.readUnsignedShort(); t1 = (long)dis.readUnsignedShort(); t2 = (long)dis.readUnsignedShort(); hiMant = t1 << 16 | t2; t1 = (long)dis.readUnsignedShort(); t2 = (long)dis.readUnsignedShort(); loMant = t1 << 16 | t2; if (expon == 0 && hiMant == 0 && loMant == 0) { f = 0; } else { if (expon == 0x7FFF) f = HUGE; else { expon -= 16383; expon -= 31; f = (hiMant * Math.pow(2, expon)); expon -= 32; f += (loMant * Math.pow(2, expon)); } } return f; } }