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
24import java.io.ByteArrayInputStream;
25
26import javax.sound.sampled.AudioFileFormat;
27import javax.sound.sampled.AudioFormat;
28import javax.sound.sampled.AudioInputStream;
29import javax.sound.sampled.AudioSystem;
30
31/**
32 * @test
33 * @bug 6729836
34 */
35public 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}
248