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.Type; 33 import javax.sound.sampled.AudioFormat; 34 import javax.sound.sampled.AudioSystem; 35 import javax.sound.sampled.UnsupportedAudioFileException; 36 37 /** 38 * AIFF file reader and writer. 39 * 40 * @author Kara Kytle 41 * @author Jan Borgersen 42 * @author Florian Bomers 43 */ 44 public final class AiffFileReader extends SunFileReader { 45 46 @Override 47 StandardFileFormat getAudioFileFormatImpl(final InputStream stream) 48 throws UnsupportedAudioFileException, IOException { 49 DataInputStream dis = new DataInputStream(stream); 50 51 AudioFormat format = null; 52 53 // Read the magic number 54 int magic = dis.readInt(); 55 56 // $$fb: fix for 4369044: javax.sound.sampled.AudioSystem.getAudioInputStream() works wrong with Cp037 57 if (magic != AiffFileFormat.AIFF_MAGIC) { 58 // not AIFF, throw exception 59 throw new UnsupportedAudioFileException("not an AIFF file"); 60 } 61 62 long /* unsigned 32bit */ frameLength = 0; 63 int length = dis.readInt(); 64 int iffType = dis.readInt(); 65 66 final long totallength; 67 if(length <= 0 ) { 68 length = AudioSystem.NOT_SPECIFIED; 69 totallength = AudioSystem.NOT_SPECIFIED; 70 } else { 71 totallength = length + 8; 72 } 73 74 // Is this an AIFC or just plain AIFF file. 75 boolean aifc = false; 76 // $$fb: fix for 4369044: javax.sound.sampled.AudioSystem.getAudioInputStream() works wrong with Cp037 77 if (iffType == AiffFileFormat.AIFC_MAGIC) { 78 aifc = true; 79 } 80 81 // Loop through the AIFF chunks until 82 // we get to the SSND chunk. 83 boolean ssndFound = false; 84 while (!ssndFound) { 85 // Read the chunk name 86 int chunkName = dis.readInt(); 87 int chunkLen = dis.readInt(); 88 89 int chunkRead = 0; 90 91 // Switch on the chunk name. 92 switch (chunkName) { 93 case AiffFileFormat.FVER_MAGIC: 94 // Ignore format version for now. 95 break; 96 97 case AiffFileFormat.COMM_MAGIC: 98 // AIFF vs. AIFC 99 // $$fb: fix for 4399551: Repost of bug candidate: cannot replay aif file (Review ID: 108108) 100 if ((!aifc && chunkLen < 18) || (aifc && chunkLen < 22)) { 101 throw new UnsupportedAudioFileException("Invalid AIFF/COMM chunksize"); 102 } 103 // Read header info. 104 int channels = dis.readUnsignedShort(); 105 if (channels <= 0) { 106 throw new UnsupportedAudioFileException("Invalid number of channels"); 107 } 108 frameLength = dis.readInt() & 0xffffffffL; // numSampleFrames 109 110 int sampleSizeInBits = dis.readUnsignedShort(); 111 if (sampleSizeInBits < 1 || sampleSizeInBits > 32) { 112 throw new UnsupportedAudioFileException("Invalid AIFF/COMM sampleSize"); 113 } 114 float sampleRate = (float) read_ieee_extended(dis); 115 chunkRead += (2 + 4 + 2 + 10); 116 117 // If this is not AIFC then we assume it's 118 // a linearly encoded file. 119 AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED; 120 121 if (aifc) { 122 int enc = dis.readInt(); chunkRead += 4; 123 switch (enc) { 124 case AiffFileFormat.AIFC_PCM: 125 encoding = AudioFormat.Encoding.PCM_SIGNED; 126 break; 127 case AiffFileFormat.AIFC_ULAW: 128 encoding = AudioFormat.Encoding.ULAW; 129 sampleSizeInBits = 8; // Java Sound convention 130 break; 131 default: 132 throw new UnsupportedAudioFileException("Invalid AIFF encoding"); 133 } 134 } 135 int frameSize = calculatePCMFrameSize(sampleSizeInBits, channels); 136 //$fb what's that ?? 137 //if (sampleSizeInBits == 8) { 138 // encoding = AudioFormat.Encoding.PCM_SIGNED; 139 //} 140 format = new AudioFormat(encoding, sampleRate, 141 sampleSizeInBits, channels, 142 frameSize, sampleRate, true); 143 break; 144 case AiffFileFormat.SSND_MAGIC: 145 // Data chunk. 146 int dataOffset = dis.readInt(); // for now unused in javasound 147 int blocksize = dis.readInt(); // for now unused in javasound 148 chunkRead += 8; 149 ssndFound = true; 150 break; 151 } // switch 152 // skip the remainder of this chunk 153 if (!ssndFound) { 154 int toSkip = chunkLen - chunkRead; 155 if (toSkip > 0) { 156 dis.skipBytes(toSkip); 157 } 158 } 159 } // while 160 161 if (format == null) { 162 throw new UnsupportedAudioFileException("missing COMM chunk"); 163 } 164 Type type = aifc ? Type.AIFC : Type.AIFF; 165 166 return new AiffFileFormat(type, totallength, format, frameLength); 167 } 168 169 // HELPER METHODS 170 /** 171 * read_ieee_extended 172 * Extended precision IEEE floating-point conversion routine. 173 * @argument DataInputStream 174 * @return double 175 * @exception IOException 176 */ 177 private double read_ieee_extended(DataInputStream dis) throws IOException { 178 179 double f = 0; 180 int expon = 0; 181 long hiMant = 0, loMant = 0; 182 long t1, t2; 183 double HUGE = 3.40282346638528860e+38; 184 185 186 expon = dis.readUnsignedShort(); 187 188 t1 = (long)dis.readUnsignedShort(); 189 t2 = (long)dis.readUnsignedShort(); 190 hiMant = t1 << 16 | t2; 191 192 t1 = (long)dis.readUnsignedShort(); 193 t2 = (long)dis.readUnsignedShort(); 194 loMant = t1 << 16 | t2; 195 196 if (expon == 0 && hiMant == 0 && loMant == 0) { 197 f = 0; 198 } else { 199 if (expon == 0x7FFF) 200 f = HUGE; 201 else { 202 expon -= 16383; 203 expon -= 31; 204 f = (hiMant * Math.pow(2, expon)); 205 expon -= 32; 206 f += (loMant * Math.pow(2, expon)); 207 } 208 } 209 210 return f; 211 } 212 }