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 }