1 /* 2 * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.media.sound; 27 28 import java.io.DataInputStream; 29 import java.io.DataOutputStream; 30 import java.io.IOException; 31 import java.io.InputStream; 32 33 import javax.sound.sampled.AudioFileFormat; 34 import javax.sound.sampled.AudioFormat; 35 import javax.sound.sampled.AudioSystem; 36 import javax.sound.sampled.UnsupportedAudioFileException; 37 38 /** 39 * AIFF file reader and writer. 40 * 41 * @author Kara Kytle 42 * @author Jan Borgersen 43 * @author Florian Bomers 44 */ 45 public final class AiffFileReader extends SunFileReader { 46 47 @Override 48 AudioFileFormat getAudioFileFormatImpl(final InputStream stream) 49 throws UnsupportedAudioFileException, IOException { 50 DataInputStream dis = new DataInputStream(stream); 51 52 // assumes a stream at the beginning of the file which has already 53 // passed the magic number test... 54 // leaves the input stream at the beginning of the audio data 55 int fileRead = 0; 56 int dataLength = 0; 57 AudioFormat format = null; 58 59 // Read the magic number 60 int magic = dis.readInt(); 61 62 // $$fb: fix for 4369044: javax.sound.sampled.AudioSystem.getAudioInputStream() works wrong with Cp037 63 if (magic != AiffFileFormat.AIFF_MAGIC) { 64 // not AIFF, throw exception 65 throw new UnsupportedAudioFileException("not an AIFF file"); 66 } 67 68 int length = dis.readInt(); 69 int iffType = dis.readInt(); 70 fileRead += 12; 71 72 int totallength; 73 if(length <= 0 ) { 74 length = AudioSystem.NOT_SPECIFIED; 75 totallength = AudioSystem.NOT_SPECIFIED; 76 } else { 77 totallength = length + 8; 78 } 79 80 // Is this an AIFC or just plain AIFF file. 81 boolean aifc = false; 82 // $$fb: fix for 4369044: javax.sound.sampled.AudioSystem.getAudioInputStream() works wrong with Cp037 83 if (iffType == AiffFileFormat.AIFC_MAGIC) { 84 aifc = true; 85 } 86 87 // Loop through the AIFF chunks until 88 // we get to the SSND chunk. 89 boolean ssndFound = false; 90 while (!ssndFound) { 91 // Read the chunk name 92 int chunkName = dis.readInt(); 93 int chunkLen = dis.readInt(); 94 fileRead += 8; 95 96 int chunkRead = 0; 97 98 // Switch on the chunk name. 99 switch (chunkName) { 100 case AiffFileFormat.FVER_MAGIC: 101 // Ignore format version for now. 102 break; 103 104 case AiffFileFormat.COMM_MAGIC: 105 // AIFF vs. AIFC 106 // $$fb: fix for 4399551: Repost of bug candidate: cannot replay aif file (Review ID: 108108) 107 if ((!aifc && chunkLen < 18) || (aifc && chunkLen < 22)) { 108 throw new UnsupportedAudioFileException("Invalid AIFF/COMM chunksize"); 109 } 110 // Read header info. 111 int channels = dis.readUnsignedShort(); 112 if (channels <= 0) { 113 throw new UnsupportedAudioFileException("Invalid number of channels"); 114 } 115 dis.readInt(); // numSampleFrames 116 int sampleSizeInBits = dis.readUnsignedShort(); 117 if (sampleSizeInBits < 1 || sampleSizeInBits > 32) { 118 throw new UnsupportedAudioFileException("Invalid AIFF/COMM sampleSize"); 119 } 120 float sampleRate = (float) read_ieee_extended(dis); 121 chunkRead += (2 + 4 + 2 + 10); 122 123 // If this is not AIFC then we assume it's 124 // a linearly encoded file. 125 AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED; 126 127 if (aifc) { 128 int enc = dis.readInt(); chunkRead += 4; 129 switch (enc) { 130 case AiffFileFormat.AIFC_PCM: 131 encoding = AudioFormat.Encoding.PCM_SIGNED; 132 break; 133 case AiffFileFormat.AIFC_ULAW: 134 encoding = AudioFormat.Encoding.ULAW; 135 sampleSizeInBits = 8; // Java Sound convention 136 break; 137 default: 138 throw new UnsupportedAudioFileException("Invalid AIFF encoding"); 139 } 140 } 141 int frameSize = calculatePCMFrameSize(sampleSizeInBits, channels); 142 //$fb what's that ?? 143 //if (sampleSizeInBits == 8) { 144 // encoding = AudioFormat.Encoding.PCM_SIGNED; 145 //} 146 format = new AudioFormat(encoding, sampleRate, 147 sampleSizeInBits, channels, 148 frameSize, sampleRate, true); 149 break; 150 case AiffFileFormat.SSND_MAGIC: 151 // Data chunk. 152 // we are getting *weird* numbers for chunkLen sometimes; 153 // this really should be the size of the data chunk.... 154 int dataOffset = dis.readInt(); 155 int blocksize = dis.readInt(); 156 chunkRead += 8; 157 158 // okay, now we are done reading the header. we need to set the size 159 // of the data segment. we know that sometimes the value we get for 160 // the chunksize is absurd. this is the best i can think of:if the 161 // value seems okay, use it. otherwise, we get our value of 162 // length by assuming that everything left is the data segment; 163 // its length should be our original length (for all AIFF data chunks) 164 // minus what we've read so far. 165 // $$kk: we should be able to get length for the data chunk right after 166 // we find "SSND." however, some aiff files give *weird* numbers. what 167 // is going on?? 168 169 if (chunkLen < length) { 170 dataLength = chunkLen - chunkRead; 171 } else { 172 // $$kk: 11.03.98: this seems dangerous! 173 dataLength = length - (fileRead + chunkRead); 174 } 175 ssndFound = true; 176 break; 177 } // switch 178 fileRead += chunkRead; 179 // skip the remainder of this chunk 180 if (!ssndFound) { 181 int toSkip = chunkLen - chunkRead; 182 if (toSkip > 0) { 183 fileRead += dis.skipBytes(toSkip); 184 } 185 } 186 } // while 187 188 if (format == null) { 189 throw new UnsupportedAudioFileException("missing COMM chunk"); 190 } 191 AudioFileFormat.Type type = aifc?AudioFileFormat.Type.AIFC:AudioFileFormat.Type.AIFF; 192 193 return new AiffFileFormat(type, totallength, format, dataLength / format.getFrameSize()); 194 } 195 196 // HELPER METHODS 197 /** write_ieee_extended(DataOutputStream dos, double f) throws IOException { 198 * Extended precision IEEE floating-point conversion routine. 199 * @argument DataOutputStream 200 * @argument double 201 * @return void 202 * @exception IOException 203 */ 204 private void write_ieee_extended(DataOutputStream dos, double f) throws IOException { 205 206 int exponent = 16398; 207 double highMantissa = f; 208 209 // For now write the integer portion of f 210 // $$jb: 03.30.99: stay in synch with JMF on this!!!! 211 while (highMantissa < 44000) { 212 highMantissa *= 2; 213 exponent--; 214 } 215 dos.writeShort(exponent); 216 dos.writeInt( ((int) highMantissa) << 16); 217 dos.writeInt(0); // low Mantissa 218 } 219 220 221 /** 222 * read_ieee_extended 223 * Extended precision IEEE floating-point conversion routine. 224 * @argument DataInputStream 225 * @return double 226 * @exception IOException 227 */ 228 private double read_ieee_extended(DataInputStream dis) throws IOException { 229 230 double f = 0; 231 int expon = 0; 232 long hiMant = 0, loMant = 0; 233 long t1, t2; 234 double HUGE = 3.40282346638528860e+38; 235 236 237 expon = dis.readUnsignedShort(); 238 239 t1 = (long)dis.readUnsignedShort(); 240 t2 = (long)dis.readUnsignedShort(); 241 hiMant = t1 << 16 | t2; 242 243 t1 = (long)dis.readUnsignedShort(); 244 t2 = (long)dis.readUnsignedShort(); 245 loMant = t1 << 16 | t2; 246 247 if (expon == 0 && hiMant == 0 && loMant == 0) { 248 f = 0; 249 } else { 250 if (expon == 0x7FFF) 251 f = HUGE; 252 else { 253 expon -= 16383; 254 expon -= 31; 255 f = (hiMant * Math.pow(2, expon)); 256 expon -= 32; 257 f += (loMant * Math.pow(2, expon)); 258 } 259 } 260 261 return f; 262 } 263 }