1 /* 2 * Copyright (c) 1999, 2016, 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.IOException; 30 import java.io.InputStream; 31 32 import javax.sound.sampled.AudioFileFormat; 33 import javax.sound.sampled.AudioFileFormat.Type; 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 AudioFormat format = null; 53 54 // Read the magic number 55 int magic = dis.readInt(); 56 57 // $$fb: fix for 4369044: javax.sound.sampled.AudioSystem.getAudioInputStream() works wrong with Cp037 58 if (magic != AiffFileFormat.AIFF_MAGIC) { 59 // not AIFF, throw exception 60 throw new UnsupportedAudioFileException("not an AIFF file"); 61 } 62 63 int frameLength = 0; 64 int length = dis.readInt(); 65 int iffType = dis.readInt(); 66 67 int totallength; 68 if(length <= 0 ) { 69 length = AudioSystem.NOT_SPECIFIED; 70 totallength = AudioSystem.NOT_SPECIFIED; 71 } else { 72 totallength = length + 8; 73 } 74 75 // Is this an AIFC or just plain AIFF file. 76 boolean aifc = false; 77 // $$fb: fix for 4369044: javax.sound.sampled.AudioSystem.getAudioInputStream() works wrong with Cp037 78 if (iffType == AiffFileFormat.AIFC_MAGIC) { 79 aifc = true; 80 } 81 82 // Loop through the AIFF chunks until 83 // we get to the SSND chunk. 84 boolean ssndFound = false; 85 while (!ssndFound) { 86 // Read the chunk name 87 int chunkName = dis.readInt(); 88 int chunkLen = dis.readInt(); 89 90 int chunkRead = 0; 91 92 // Switch on the chunk name. 93 switch (chunkName) { 94 case AiffFileFormat.FVER_MAGIC: 95 // Ignore format version for now. 96 break; 97 98 case AiffFileFormat.COMM_MAGIC: 99 // AIFF vs. AIFC 100 // $$fb: fix for 4399551: Repost of bug candidate: cannot replay aif file (Review ID: 108108) 101 if ((!aifc && chunkLen < 18) || (aifc && chunkLen < 22)) { 102 throw new UnsupportedAudioFileException("Invalid AIFF/COMM chunksize"); 103 } 104 // Read header info. 105 int channels = dis.readUnsignedShort(); 106 if (channels <= 0) { 107 throw new UnsupportedAudioFileException("Invalid number of channels"); 108 } 109 frameLength = dis.readInt(); // numSampleFrames 110 if (frameLength < 0) { 111 // AiffFileFormat uses int, unlike AIS which uses long 112 //TODO this (negative) value should be passed as long to AIS 113 frameLength = AudioSystem.NOT_SPECIFIED; 114 } 115 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 int dataOffset = dis.readInt(); // for now unused in javasound 153 int blocksize = dis.readInt(); // for now unused in javasound 154 chunkRead += 8; 155 ssndFound = true; 156 break; 157 } // switch 158 // skip the remainder of this chunk 159 if (!ssndFound) { 160 int toSkip = chunkLen - chunkRead; 161 if (toSkip > 0) { 162 dis.skipBytes(toSkip); 163 } 164 } 165 } // while 166 167 if (format == null) { 168 throw new UnsupportedAudioFileException("missing COMM chunk"); 169 } 170 Type type = aifc ? Type.AIFC : Type.AIFF; 171 172 return new AiffFileFormat(type, totallength, format, frameLength); 173 } 174 175 // HELPER METHODS 176 /** 177 * read_ieee_extended 178 * Extended precision IEEE floating-point conversion routine. 179 * @argument DataInputStream 180 * @return double 181 * @exception IOException 182 */ 183 private double read_ieee_extended(DataInputStream dis) throws IOException { 184 185 double f = 0; 186 int expon = 0; 187 long hiMant = 0, loMant = 0; 188 long t1, t2; 189 double HUGE = 3.40282346638528860e+38; 190 191 192 expon = dis.readUnsignedShort(); 193 194 t1 = (long)dis.readUnsignedShort(); 195 t2 = (long)dis.readUnsignedShort(); 196 hiMant = t1 << 16 | t2; 197 198 t1 = (long)dis.readUnsignedShort(); 199 t2 = (long)dis.readUnsignedShort(); 200 loMant = t1 << 16 | t2; 201 202 if (expon == 0 && hiMant == 0 && loMant == 0) { 203 f = 0; 204 } else { 205 if (expon == 0x7FFF) 206 f = HUGE; 207 else { 208 expon -= 16383; 209 expon -= 31; 210 f = (hiMant * Math.pow(2, expon)); 211 expon -= 32; 212 f += (loMant * Math.pow(2, expon)); 213 } 214 } 215 216 return f; 217 } 218 }