1 /* 2 * Copyright (c) 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.io.ByteArrayInputStream; 25 26 import javax.sound.sampled.AudioFileFormat; 27 import javax.sound.sampled.AudioFormat; 28 import javax.sound.sampled.AudioInputStream; 29 import javax.sound.sampled.AudioSystem; 30 31 /** 32 * @test 33 * @bug 6729836 34 */ 35 public final class RecognizeHugeAiffFiles { 36 37 /** 38 * The maximum number of sample frames per AIFF specification. 39 */ 40 private static final /* unsigned int */ long MAX_UNSIGNED_INT = 0xffffffffL; 41 42 /** 43 * The supported aiff sample size in bits. 44 */ 45 private static final byte[] aiffBits = { 46 1, 2, 4, 8, 11, 16, 20, 24, 27, 32 47 }; 48 49 /** 50 * The list of supported sample rates. 51 */ 52 private static final int[] sampleRates = { 53 8000, 11025, 16000, 22050, 32000, 37800, 44056, 44100, 47250, 48000, 54 50000, 50400, 88200, 96000, 176400, 192000, 352800, 2822400, 55 5644800, Integer.MAX_VALUE 56 }; 57 58 /** 59 * The list of supported channels. 60 */ 61 private static final int[] channels = { 62 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 63 }; 64 65 /** 66 * The list of supported number of frames. 67 * <p> 68 * The {@code MAX_UNSIGNED_INT} is a maximum. 69 */ 70 private static final long[] numberOfFrames = { 71 0, 1, 2, 3, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, 72 (long) Integer.MAX_VALUE + 1, MAX_UNSIGNED_INT - 1, MAX_UNSIGNED_INT 73 }; 74 75 public static void main(final String[] args) throws Exception { 76 for (final byte bits : aiffBits) { 77 for (final int sampleRate : sampleRates) { 78 for (final int channel : channels) { 79 for (final long dataSize : numberOfFrames) { 80 testAFF(bits, sampleRate, channel, dataSize); 81 testAIS(bits, sampleRate, channel, dataSize); 82 } 83 } 84 } 85 } 86 } 87 88 /** 89 * Tests the {@code AudioFileFormat} fetched from the fake header. 90 * <p> 91 * Note that the frameLength and byteLength are stored as int which means 92 * that {@code AudioFileFormat} will store the data above {@code MAX_INT} as 93 * NOT_SPECIFIED. 94 */ 95 private static void testAFF(final byte bits, final int rate, 96 final int channel, final long frameLength) 97 throws Exception { 98 final byte[] header = createHeader(bits, rate, channel, frameLength); 99 final ByteArrayInputStream fake = new ByteArrayInputStream(header); 100 final AudioFileFormat aff = AudioSystem.getAudioFileFormat(fake); 101 102 if (aff.getType() != AudioFileFormat.Type.AIFF) { 103 throw new RuntimeException("Error"); 104 } 105 106 if (frameLength <= Integer.MAX_VALUE) { 107 if (aff.getFrameLength() != frameLength) { 108 System.err.println("Expected: " + frameLength); 109 System.err.println("Actual: " + aff.getFrameLength()); 110 throw new RuntimeException(); 111 } 112 } else { 113 if (aff.getFrameLength() != AudioSystem.NOT_SPECIFIED) { 114 System.err.println("Expected: " + AudioSystem.NOT_SPECIFIED); 115 System.err.println("Actual: " + aff.getFrameLength()); 116 throw new RuntimeException(); 117 } 118 } 119 validateFormat(bits, rate, channel, aff.getFormat()); 120 } 121 122 /** 123 * Tests the {@code AudioInputStream} fetched from the fake header. 124 * <p> 125 * Note that the frameLength is stored as long which means that {@code 126 * AudioInputStream} must store all possible data from aiff file. 127 */ 128 private static void testAIS(final byte bits, final int rate, 129 final int channel, final long frameLength) 130 throws Exception { 131 final byte[] header = createHeader(bits, rate, channel, frameLength); 132 final ByteArrayInputStream fake = new ByteArrayInputStream(header); 133 final AudioInputStream ais = AudioSystem.getAudioInputStream(fake); 134 final AudioFormat format = ais.getFormat(); 135 136 if (frameLength != ais.getFrameLength()) { 137 System.err.println("Expected: " + frameLength); 138 System.err.println("Actual: " + ais.getFrameLength()); 139 throw new RuntimeException(); 140 } 141 if (ais.available() < 0) { 142 System.err.println("available should be >=0: " + ais.available()); 143 throw new RuntimeException(); 144 } 145 146 validateFormat(bits, rate, channel, format); 147 } 148 149 /** 150 * Tests that format contains the same data as were provided to the fake 151 * stream. 152 */ 153 private static void validateFormat(final byte bits, final int rate, 154 final int channel, 155 final AudioFormat format) { 156 157 if (Float.compare(format.getSampleRate(), rate) != 0) { 158 System.err.println("Expected: " + rate); 159 System.err.println("Actual: " + format.getSampleRate()); 160 throw new RuntimeException(); 161 } 162 if (format.getChannels() != channel) { 163 System.err.println("Expected: " + channel); 164 System.err.println("Actual: " + format.getChannels()); 165 throw new RuntimeException(); 166 } 167 int frameSize = ((bits + 7) / 8) * channel; 168 if (format.getFrameSize() != frameSize) { 169 System.out.println("Expected: " + frameSize); 170 System.err.println("Actual: " + format.getFrameSize()); 171 throw new RuntimeException(); 172 } 173 } 174 175 private static final int DOUBLE_MANTISSA_LENGTH = 52; 176 private static final int DOUBLE_EXPONENT_LENGTH = 11; 177 private static final long DOUBLE_SIGN_MASK = 0x8000000000000000L; 178 private static final long DOUBLE_EXPONENT_MASK = 0x7FF0000000000000L; 179 private static final long DOUBLE_MANTISSA_MASK = 0x000FFFFFFFFFFFFFL; 180 private static final int DOUBLE_EXPONENT_OFFSET = 1023; 181 182 private static final int EXTENDED_EXPONENT_OFFSET = 16383; 183 private static final int EXTENDED_MANTISSA_LENGTH = 63; 184 private static final int EXTENDED_EXPONENT_LENGTH = 15; 185 private static final long EXTENDED_INTEGER_MASK = 0x8000000000000000L; 186 187 /** 188 * Creates the custom header of the AIFF file. It is expected that all 189 * passed data are supported. 190 */ 191 private static byte[] createHeader(final byte bits, final int rate, 192 final int channel, final long frameLength) { 193 long doubleBits = Double.doubleToLongBits(rate); 194 195 long sign = (doubleBits & DOUBLE_SIGN_MASK) 196 >> (DOUBLE_EXPONENT_LENGTH + DOUBLE_MANTISSA_LENGTH); 197 long doubleExponent = (doubleBits & DOUBLE_EXPONENT_MASK) 198 >> DOUBLE_MANTISSA_LENGTH; 199 long doubleMantissa = doubleBits & DOUBLE_MANTISSA_MASK; 200 201 long extendedExponent = doubleExponent - DOUBLE_EXPONENT_OFFSET 202 + EXTENDED_EXPONENT_OFFSET; 203 long extendedMantissa = doubleMantissa 204 << (EXTENDED_MANTISSA_LENGTH - DOUBLE_MANTISSA_LENGTH); 205 long extendedSign = sign << EXTENDED_EXPONENT_LENGTH; 206 short extendedBits79To64 = (short) (extendedSign | extendedExponent); 207 long extendedBits63To0 = EXTENDED_INTEGER_MASK | extendedMantissa; 208 209 return new byte[]{ 210 // AIFF_MAGIC 211 0x46, 0x4f, 0x52, 0x4d, 212 // fileLength (will use the number of frames for testing) 213 (byte) (frameLength >> 24), (byte) (frameLength >> 16), 214 (byte) (frameLength >> 8), (byte) frameLength, 215 // form aiff 216 0x41, 0x49, 0x46, 0x46, 217 // COMM_MAGIC 218 0x43, 0x4f, 0x4d, 0x4d, 219 // comm chunk size 220 0, 0, 0, 18, 221 // channels 222 (byte) (channel >> 8),(byte) channel, 223 // numSampleFrames 224 (byte) (frameLength >> 24), (byte) (frameLength >> 16), 225 (byte) (frameLength >> 8), (byte) (frameLength), 226 // samplesize 227 (byte) (bits >> 8),(byte) (bits), 228 // samplerate 229 (byte) (extendedBits79To64 >> 8), 230 (byte) extendedBits79To64, 231 (byte) (extendedBits63To0 >> 56), 232 (byte) (extendedBits63To0 >> 48), 233 (byte) (extendedBits63To0 >> 40), 234 (byte) (extendedBits63To0 >> 32), (byte) (extendedBits63To0 >> 24), 235 (byte) (extendedBits63To0 >> 16), (byte) (extendedBits63To0 >> 8), 236 (byte) extendedBits63To0, 237 // SND_MAGIC 238 0x53, 0x53, 0x4e, 0x44, 239 // data chunk size 240 0, 0, 0, 0, 241 // dataOffset 242 0, 0, 0, 0, 243 // blocksize 244 0, 0, 0, 0, 245 }; 246 } 247 }