--- old/src/java.desktop/share/classes/com/sun/media/sound/AiffFileFormat.java 2016-04-07 21:16:14.000000000 +0300 +++ new/src/java.desktop/share/classes/com/sun/media/sound/AiffFileFormat.java 2016-04-07 21:16:14.000000000 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,17 +25,14 @@ package com.sun.media.sound; -import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioFormat; - /** * AIFF file format. * * @author Jan Borgersen */ - -final class AiffFileFormat extends AudioFileFormat { +final class AiffFileFormat extends StandardFileFormat { static final int AIFF_MAGIC = 1179603533; @@ -70,11 +67,8 @@ /** FVER chunk size in bytes, inclusive magic and length field */ private final int fverChunkSize=0; - AiffFileFormat( AudioFileFormat aff ) { - this( aff.getType(), aff.getByteLength(), aff.getFormat(), aff.getFrameLength() ); - } - - AiffFileFormat(Type type, int byteLength, AudioFormat format, int frameLength) { + AiffFileFormat(final Type type, final long byteLength, + final AudioFormat format, final long frameLength) { super(type, byteLength, format, frameLength); } --- old/src/java.desktop/share/classes/com/sun/media/sound/AiffFileReader.java 2016-04-07 21:16:15.000000000 +0300 +++ new/src/java.desktop/share/classes/com/sun/media/sound/AiffFileReader.java 2016-04-07 21:16:15.000000000 +0300 @@ -45,7 +45,7 @@ public final class AiffFileReader extends SunFileReader { @Override - AudioFileFormat getAudioFileFormatImpl(final InputStream stream) + StandardFileFormat getAudioFileFormatImpl(final InputStream stream) throws UnsupportedAudioFileException, IOException { DataInputStream dis = new DataInputStream(stream); @@ -60,11 +60,11 @@ throw new UnsupportedAudioFileException("not an AIFF file"); } - int frameLength = 0; + long /* unsigned 32bit */ frameLength = 0; int length = dis.readInt(); int iffType = dis.readInt(); - int totallength; + final long totallength; if(length <= 0 ) { length = AudioSystem.NOT_SPECIFIED; totallength = AudioSystem.NOT_SPECIFIED; @@ -106,12 +106,7 @@ if (channels <= 0) { throw new UnsupportedAudioFileException("Invalid number of channels"); } - frameLength = dis.readInt(); // numSampleFrames - if (frameLength < 0) { - // AiffFileFormat uses int, unlike AIS which uses long - //TODO this (negative) value should be passed as long to AIS - frameLength = AudioSystem.NOT_SPECIFIED; - } + frameLength = dis.readInt() & 0xffffffffL; // numSampleFrames int sampleSizeInBits = dis.readUnsignedShort(); if (sampleSizeInBits < 1 || sampleSizeInBits > 32) { --- old/src/java.desktop/share/classes/com/sun/media/sound/AuFileFormat.java 2016-04-07 21:16:15.000000000 +0300 +++ new/src/java.desktop/share/classes/com/sun/media/sound/AuFileFormat.java 2016-04-07 21:16:15.000000000 +0300 @@ -33,7 +33,7 @@ * * @author Jan Borgersen */ -final class AuFileFormat extends AudioFileFormat { +final class AuFileFormat extends StandardFileFormat { // magic numbers static final int AU_SUN_MAGIC = 0x2e736e64; // ".snd" @@ -55,11 +55,18 @@ static final int AU_HEADERSIZE = 24; - private int auType; + /** + * According the specification of AU file format this is the value for + * length field if length is not known. This is a maximum possible value for + * the unsigned int. + */ + static final long /*unsigned int */ UNKNOWN_SIZE = 0xffffffffL; - AuFileFormat(AudioFileFormat.Type type, int lengthInBytes, AudioFormat format, int lengthInFrames) { + private int auType; - super(type,lengthInBytes,format,lengthInFrames); + AuFileFormat(final AudioFileFormat.Type type, final long byteLength, + final AudioFormat format, final long frameLength) { + super(type, byteLength, format, frameLength); AudioFormat.Encoding encoding = format.getEncoding(); --- old/src/java.desktop/share/classes/com/sun/media/sound/AuFileReader.java 2016-04-07 21:16:16.000000000 +0300 +++ new/src/java.desktop/share/classes/com/sun/media/sound/AuFileReader.java 2016-04-07 21:16:16.000000000 +0300 @@ -29,7 +29,6 @@ import java.io.IOException; import java.io.InputStream; -import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioFileFormat.Type; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; @@ -45,7 +44,7 @@ public final class AuFileReader extends SunFileReader { @Override - public AudioFileFormat getAudioFileFormatImpl(final InputStream stream) + public StandardFileFormat getAudioFileFormatImpl(final InputStream stream) throws UnsupportedAudioFileException, IOException { final DataInputStream dis = new DataInputStream(stream); final int magic = dis.readInt(); @@ -56,7 +55,7 @@ } final int headerSize = dis.readInt(); - final int dataSize = dis.readInt(); + final long /* unsigned int */ dataSize = dis.readInt() & 0xffffffffL; final int auType = dis.readInt(); final int sampleRate = dis.readInt(); final int channels = dis.readInt(); @@ -120,21 +119,21 @@ // unsupported filetype, throw exception throw new UnsupportedAudioFileException("not a valid AU file"); } + // now seek past the header + dis.skipBytes(headerSize - AuFileFormat.AU_HEADERSIZE); final int frameSize = calculatePCMFrameSize(sampleSizeInBits, channels); //$$fb 2002-11-02: fix for 4629669: AU file reader: problems with empty files - final int length; - if (dataSize < 0) { - length = AudioSystem.NOT_SPECIFIED; - } else { - //$$fb 2003-10-20: fix for 4940459: AudioInputStream.getFrameLength() returns 0 instead of NOT_SPECIFIED - length = dataSize / frameSize; + //$$fb 2003-10-20: fix for 4940459: AudioInputStream.getFrameLength() returns 0 instead of NOT_SPECIFIED + long frameLength = AudioSystem.NOT_SPECIFIED; + long byteLength = AudioSystem.NOT_SPECIFIED; + if (dataSize != AuFileFormat.UNKNOWN_SIZE) { + frameLength = dataSize / frameSize; + byteLength = dataSize + headerSize; } - // now seek past the header - dis.skipBytes(headerSize - AuFileFormat.AU_HEADERSIZE); final AudioFormat format = new AudioFormat(encoding, sampleRate, sampleSizeInBits, channels, frameSize, sampleRate, true); - return new AuFileFormat(Type.AU, dataSize + headerSize, format, length); + return new AuFileFormat(Type.AU, byteLength, format, frameLength); } } --- old/src/java.desktop/share/classes/com/sun/media/sound/SoftMidiAudioFileReader.java 2016-04-07 21:16:17.000000000 +0300 +++ new/src/java.desktop/share/classes/com/sun/media/sound/SoftMidiAudioFileReader.java 2016-04-07 21:16:17.000000000 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,6 @@ import javax.sound.midi.Receiver; import javax.sound.midi.Sequence; import javax.sound.midi.Track; -import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioFileFormat.Type; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; @@ -56,10 +55,10 @@ private static final AudioFormat format = new AudioFormat(44100, 16, 2, true, false); - private static AudioFileFormat getAudioFileFormat(final Sequence seq) { + private static StandardFileFormat getAudioFileFormat(final Sequence seq) { long totallen = seq.getMicrosecondLength() / 1000000; long len = (long) (format.getFrameRate() * (totallen + 4)); - return new AudioFileFormat(MIDI, format, (int) len); + return new StandardFileFormat(MIDI, format, len); } private AudioInputStream getAudioInputStream(final Sequence seq) @@ -140,7 +139,7 @@ } @Override - AudioFileFormat getAudioFileFormatImpl(final InputStream stream) + StandardFileFormat getAudioFileFormatImpl(final InputStream stream) throws UnsupportedAudioFileException, IOException { try { return getAudioFileFormat(MidiSystem.getSequence(stream)); --- old/src/java.desktop/share/classes/com/sun/media/sound/SunFileReader.java 2016-04-07 21:16:17.000000000 +0300 +++ new/src/java.desktop/share/classes/com/sun/media/sound/SunFileReader.java 2016-04-07 21:16:17.000000000 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,7 +47,7 @@ abstract class SunFileReader extends AudioFileReader { @Override - public final AudioFileFormat getAudioFileFormat(final InputStream stream) + public final StandardFileFormat getAudioFileFormat(final InputStream stream) throws UnsupportedAudioFileException, IOException { stream.mark(200); // The biggest value which was historically used try { @@ -87,11 +87,11 @@ throws UnsupportedAudioFileException, IOException { stream.mark(200); // The biggest value which was historically used try { - final AudioFileFormat fileFormat = getAudioFileFormatImpl(stream); + final StandardFileFormat format = getAudioFileFormatImpl(stream); // we've got everything, the stream is supported and it is at the // beginning of the audio data, so return an AudioInputStream - return new AudioInputStream(stream, fileFormat.getFormat(), - fileFormat.getFrameLength()); + return new AudioInputStream(stream, format.getFormat(), + format.getLongFrameLength()); } catch (UnsupportedAudioFileException | EOFException ignored) { // stream is unsupported or the header is less than was expected stream.reset(); @@ -140,7 +140,7 @@ * UnsupportedAudioFileException if the header is less than was * expected */ - abstract AudioFileFormat getAudioFileFormatImpl(InputStream stream) + abstract StandardFileFormat getAudioFileFormatImpl(InputStream stream) throws UnsupportedAudioFileException, IOException; // HELPER METHODS --- old/src/java.desktop/share/classes/com/sun/media/sound/WaveExtensibleFileReader.java 2016-04-07 21:16:18.000000000 +0300 +++ new/src/java.desktop/share/classes/com/sun/media/sound/WaveExtensibleFileReader.java 2016-04-07 21:16:18.000000000 +0300 @@ -34,7 +34,6 @@ import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioFormat.Encoding; import javax.sound.sampled.AudioInputStream; -import javax.sound.sampled.AudioSystem; import javax.sound.sampled.UnsupportedAudioFileException; /** @@ -167,7 +166,7 @@ } @Override - AudioFileFormat getAudioFileFormatImpl(final InputStream stream) + StandardFileFormat getAudioFileFormatImpl(final InputStream stream) throws UnsupportedAudioFileException, IOException { RIFFReader riffiterator = new RIFFReader(stream); @@ -249,19 +248,17 @@ } else { throw new UnsupportedAudioFileException(); } - long frameLength = dataSize / audioformat.getFrameSize(); - if (frameLength > Integer.MAX_VALUE) { - frameLength = AudioSystem.NOT_SPECIFIED; - } - return new AudioFileFormat(AudioFileFormat.Type.WAVE, audioformat, - (int) frameLength); + return new StandardFileFormat(AudioFileFormat.Type.WAVE, audioformat, + dataSize / audioformat.getFrameSize()); } @Override public AudioInputStream getAudioInputStream(final InputStream stream) throws UnsupportedAudioFileException, IOException { - final AudioFileFormat format = getAudioFileFormat(stream); + final StandardFileFormat format = getAudioFileFormat(stream); + final AudioFormat af = format.getFormat(); + final long length = format.getLongFrameLength(); // we've got everything, the stream is supported and it is at the // beginning of the header, so find the data chunk again and return an // AudioInputStream @@ -269,8 +266,6 @@ while (riffiterator.hasNextChunk()) { RIFFReader chunk = riffiterator.nextChunk(); if (chunk.getFormat().equals("data")) { - final AudioFormat af = format.getFormat(); - final long length = chunk.getSize() / af.getFrameSize(); return new AudioInputStream(chunk, af, length); } } --- old/src/java.desktop/share/classes/com/sun/media/sound/WaveFileFormat.java 2016-04-07 21:16:19.000000000 +0300 +++ new/src/java.desktop/share/classes/com/sun/media/sound/WaveFileFormat.java 2016-04-07 21:16:19.000000000 +0300 @@ -33,7 +33,7 @@ * * @author Jan Borgersen */ -final class WaveFileFormat extends AudioFileFormat { +final class WaveFileFormat extends StandardFileFormat { /** * Wave format type. @@ -73,9 +73,9 @@ static final int WAVE_FORMAT_SX7383 = 0x1C07; static final int WAVE_FORMAT_EXTENSIBLE= 0xFFFE; - WaveFileFormat(AudioFileFormat.Type type, int lengthInBytes, AudioFormat format, int lengthInFrames) { - - super(type,lengthInBytes,format,lengthInFrames); + WaveFileFormat(final AudioFileFormat.Type type, final long byteLength, + final AudioFormat format, final long frameLength) { + super(type, byteLength, format, frameLength); AudioFormat.Encoding encoding = format.getEncoding(); --- old/src/java.desktop/share/classes/com/sun/media/sound/WaveFileReader.java 2016-04-07 21:16:20.000000000 +0300 +++ new/src/java.desktop/share/classes/com/sun/media/sound/WaveFileReader.java 2016-04-07 21:16:20.000000000 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,7 +45,7 @@ public final class WaveFileReader extends SunFileReader { @Override - AudioFileFormat getAudioFileFormatImpl(final InputStream stream) + StandardFileFormat getAudioFileFormatImpl(final InputStream stream) throws UnsupportedAudioFileException, IOException { // assumes sream is rewound @@ -64,9 +64,9 @@ DataInputStream dis = new DataInputStream( stream ); int magic = dis.readInt(); - int fileLength = rllong(dis); + long /* unsigned int */ fileLength = rllong(dis) & 0xffffffffL; int waveMagic = dis.readInt(); - int totallength; + long totallength; if (fileLength <= 0) { fileLength = AudioSystem.NOT_SPECIFIED; totallength = AudioSystem.NOT_SPECIFIED; @@ -186,19 +186,18 @@ } } // this is the length of the data chunk - int dataLength = rllong(dis); nread += 4; + long /* unsigned int */ dataLength = rllong(dis) & 0xffffffffL; nread += 4; // now build the new AudioFileFormat and return - + final int frameSize = calculatePCMFrameSize(sampleSizeInBits, channels); AudioFormat format = new AudioFormat(encoding, (float)sampleRate, sampleSizeInBits, channels, - calculatePCMFrameSize(sampleSizeInBits, channels), + frameSize, (float)sampleRate, false); - return new WaveFileFormat(AudioFileFormat.Type.WAVE, - totallength, - format, - dataLength / format.getFrameSize()); + long frameLength = dataLength / format.getFrameSize(); + return new WaveFileFormat(AudioFileFormat.Type.WAVE, totallength, + format, frameLength); } } --- old/src/java.desktop/share/classes/com/sun/media/sound/WaveFloatFileReader.java 2016-04-07 21:16:20.000000000 +0300 +++ new/src/java.desktop/share/classes/com/sun/media/sound/WaveFloatFileReader.java 2016-04-07 21:16:20.000000000 +0300 @@ -32,7 +32,6 @@ import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioFormat.Encoding; import javax.sound.sampled.AudioInputStream; -import javax.sound.sampled.AudioSystem; import javax.sound.sampled.UnsupportedAudioFileException; /** @@ -43,7 +42,7 @@ public final class WaveFloatFileReader extends SunFileReader { @Override - AudioFileFormat getAudioFileFormatImpl(final InputStream stream) + StandardFileFormat getAudioFileFormatImpl(final InputStream stream) throws UnsupportedAudioFileException, IOException { RIFFReader riffiterator = new RIFFReader(stream); @@ -88,20 +87,17 @@ AudioFormat audioformat = new AudioFormat( Encoding.PCM_FLOAT, samplerate, bits, channels, framesize, samplerate, false); - long frameLength = dataSize / audioformat.getFrameSize(); - if (frameLength > Integer.MAX_VALUE) { - frameLength = AudioSystem.NOT_SPECIFIED; - } - - return new AudioFileFormat(AudioFileFormat.Type.WAVE, audioformat, - (int) frameLength); + return new StandardFileFormat(AudioFileFormat.Type.WAVE, audioformat, + dataSize / audioformat.getFrameSize()); } @Override public AudioInputStream getAudioInputStream(final InputStream stream) throws UnsupportedAudioFileException, IOException { - final AudioFileFormat format = getAudioFileFormat(stream); + final StandardFileFormat format = getAudioFileFormat(stream); + final AudioFormat af = format.getFormat(); + final long length = format.getLongFrameLength(); // we've got everything, the stream is supported and it is at the // beginning of the header, so find the data chunk again and return an // AudioInputStream @@ -109,8 +105,6 @@ while (riffiterator.hasNextChunk()) { RIFFReader chunk = riffiterator.nextChunk(); if (chunk.getFormat().equals("data")) { - final AudioFormat af = format.getFormat(); - final long length = chunk.getSize() / af.getFrameSize(); return new AudioInputStream(chunk, af, length); } } --- old/test/javax/sound/sampled/spi/AudioFileReader/RecognizeHugeWaveExtFiles.java 2016-04-07 21:16:21.000000000 +0300 +++ new/test/javax/sound/sampled/spi/AudioFileReader/RecognizeHugeWaveExtFiles.java 2016-04-07 21:16:21.000000000 +0300 @@ -125,7 +125,7 @@ * Tests the {@code AudioInputStream} fetched from the fake header. *

* Note that the frameLength is stored as long which means that {@code - * AudioInputStream} must store all possible data from au file. + * AudioInputStream} must store all possible data from wave file. */ private static void testAIS(final int[] type, final int rate, final int channel, final long size) @@ -166,8 +166,9 @@ System.err.println("Actual: " + format.getChannels()); throw new RuntimeException(); } - if (format.getFrameSize() != ((bits + 7) / 8) * channel) { - System.err.println("Expected: " + (bits * channel + 1) / 8); + int frameSize = ((bits + 7) / 8) * channel; + if (format.getFrameSize() != frameSize) { + System.err.println("Expected: " + frameSize); System.err.println("Actual: " + format.getFrameSize()); throw new RuntimeException(); } --- /dev/null 2016-04-07 21:16:22.000000000 +0300 +++ new/src/java.desktop/share/classes/com/sun/media/sound/StandardFileFormat.java 2016-04-07 21:16:22.000000000 +0300 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.media.sound; + +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioSystem; + +/** + * An instance of the {@code StandardFileFormat} describes the file's length in + * bytes and the length in sample frames as longs. This will provide an + * additional precision unlike the {@code AudioFileFormat}. + */ +class StandardFileFormat extends AudioFileFormat { + + /** + * File length in bytes stored as long. + */ + private final long byteLength; + + /** + * Audio data length in sample frames stored as long. + */ + private final long frameLength; + + /** + * Constructs {@code StandardFileFormat} object. + * + * @param type the type of the audio file + * @param format the format of the audio data contained in the file + * @param frameLength the audio data length in sample frames, or + * {@code AudioSystem.NOT_SPECIFIED} + */ + StandardFileFormat(final Type type, final AudioFormat format, + final long frameLength) { + this(type, AudioSystem.NOT_SPECIFIED, format, frameLength); + } + + /** + * Constructs {@code StandardFileFormat} object. + * + * @param type the type of the audio file + * @param byteLength the length of the file in bytes, or + * {@code AudioSystem.NOT_SPECIFIED} + * @param format the format of the audio data contained in the file + * @param frameLength the audio data length in sample frames, or + * {@code AudioSystem.NOT_SPECIFIED} + */ + StandardFileFormat(final Type type, final long byteLength, + final AudioFormat format, final long frameLength) { + super(type, clip(byteLength), format, clip(frameLength)); + this.byteLength = byteLength; + this.frameLength = frameLength; + } + + /** + * Replaces the passed value to {@code AudioSystem.NOT_SPECIFIED} if the + * value is greater than {@code Integer.MAX_VALUE}. + * + * @param value which should be clipped + * @return the clipped value + */ + private static int clip(final long value) { + if (value > Integer.MAX_VALUE) { + return AudioSystem.NOT_SPECIFIED; + } + return (int) value; + } + + /** + * Obtains the length of the audio data contained in the file, expressed in + * sample frames. The long precision is used. + * + * @return the number of sample frames of audio data in the file + * @see AudioSystem#NOT_SPECIFIED + */ + public final long getLongFrameLength() { + return frameLength; + } + + /** + * Obtains the size in bytes of the entire audio file (not just its audio + * data). The long precision is used. + * + * @return the audio file length in bytes + * @see AudioSystem#NOT_SPECIFIED + */ + public final long getLongByteLength() { + return byteLength; + } +} --- /dev/null 2016-04-07 21:16:22.000000000 +0300 +++ new/test/javax/sound/sampled/spi/AudioFileReader/RecognizeHugeAiffFiles.java 2016-04-07 21:16:22.000000000 +0300 @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.ByteArrayInputStream; + +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; + +/** + * @test + * @bug 6729836 + */ +public final class RecognizeHugeAiffFiles { + + /** + * The maximum number of sample frames per AIFF specification. + */ + private static final /* unsigned int */ long MAX_UNSIGNED_INT = 0xffffffffL; + + /** + * The supported aiff sample size in bits. + */ + private static final byte[] aiffBits = { + 1, 2, 4, 8, 11, 16, 20, 24, 27, 32 + }; + + /** + * The list of supported sample rates. + */ + private static final int[] sampleRates = { + 8000, 11025, 16000, 22050, 32000, 37800, 44056, 44100, 47250, 48000, + 50000, 50400, 88200, 96000, 176400, 192000, 352800, 2822400, + 5644800, Integer.MAX_VALUE + }; + + /** + * The list of supported channels. + */ + private static final int[] channels = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + }; + + /** + * The list of supported number of frames. + *

+ * The {@code MAX_UNSIGNED_INT} is a maximum. + */ + private static final long[] numberOfFrames = { + 0, 1, 2, 3, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, + (long) Integer.MAX_VALUE + 1, MAX_UNSIGNED_INT - 1, MAX_UNSIGNED_INT + }; + + public static void main(final String[] args) throws Exception { + for (final byte bits : aiffBits) { + for (final int sampleRate : sampleRates) { + for (final int channel : channels) { + for (final long dataSize : numberOfFrames) { + testAFF(bits, sampleRate, channel, dataSize); + testAIS(bits, sampleRate, channel, dataSize); + } + } + } + } + } + + /** + * Tests the {@code AudioFileFormat} fetched from the fake header. + *

+ * Note that the frameLength and byteLength are stored as int which means + * that {@code AudioFileFormat} will store the data above {@code MAX_INT} as + * NOT_SPECIFIED. + */ + private static void testAFF(final byte bits, final int rate, + final int channel, final long frameLength) + throws Exception { + final byte[] header = createHeader(bits, rate, channel, frameLength); + final ByteArrayInputStream fake = new ByteArrayInputStream(header); + final AudioFileFormat aff = AudioSystem.getAudioFileFormat(fake); + + if (aff.getType() != AudioFileFormat.Type.AIFF) { + throw new RuntimeException("Error"); + } + + if (frameLength <= Integer.MAX_VALUE) { + if (aff.getFrameLength() != frameLength) { + System.err.println("Expected: " + frameLength); + System.err.println("Actual: " + aff.getFrameLength()); + throw new RuntimeException(); + } + } else { + if (aff.getFrameLength() != AudioSystem.NOT_SPECIFIED) { + System.err.println("Expected: " + AudioSystem.NOT_SPECIFIED); + System.err.println("Actual: " + aff.getFrameLength()); + throw new RuntimeException(); + } + } + validateFormat(bits, rate, channel, aff.getFormat()); + } + + /** + * Tests the {@code AudioInputStream} fetched from the fake header. + *

+ * Note that the frameLength is stored as long which means that {@code + * AudioInputStream} must store all possible data from aiff file. + */ + private static void testAIS(final byte bits, final int rate, + final int channel, final long frameLength) + throws Exception { + final byte[] header = createHeader(bits, rate, channel, frameLength); + final ByteArrayInputStream fake = new ByteArrayInputStream(header); + final AudioInputStream ais = AudioSystem.getAudioInputStream(fake); + final AudioFormat format = ais.getFormat(); + + if (frameLength != ais.getFrameLength()) { + System.err.println("Expected: " + frameLength); + System.err.println("Actual: " + ais.getFrameLength()); + throw new RuntimeException(); + } + if (ais.available() < 0) { + System.err.println("available should be >=0: " + ais.available()); + throw new RuntimeException(); + } + + validateFormat(bits, rate, channel, format); + } + + /** + * Tests that format contains the same data as were provided to the fake + * stream. + */ + private static void validateFormat(final byte bits, final int rate, + final int channel, + final AudioFormat format) { + + if (Float.compare(format.getSampleRate(), rate) != 0) { + System.err.println("Expected: " + rate); + System.err.println("Actual: " + format.getSampleRate()); + throw new RuntimeException(); + } + if (format.getChannels() != channel) { + System.err.println("Expected: " + channel); + System.err.println("Actual: " + format.getChannels()); + throw new RuntimeException(); + } + int frameSize = ((bits + 7) / 8) * channel; + if (format.getFrameSize() != frameSize) { + System.out.println("Expected: " + frameSize); + System.err.println("Actual: " + format.getFrameSize()); + throw new RuntimeException(); + } + } + + private static final int DOUBLE_MANTISSA_LENGTH = 52; + private static final int DOUBLE_EXPONENT_LENGTH = 11; + private static final long DOUBLE_SIGN_MASK = 0x8000000000000000L; + private static final long DOUBLE_EXPONENT_MASK = 0x7FF0000000000000L; + private static final long DOUBLE_MANTISSA_MASK = 0x000FFFFFFFFFFFFFL; + private static final int DOUBLE_EXPONENT_OFFSET = 1023; + + private static final int EXTENDED_EXPONENT_OFFSET = 16383; + private static final int EXTENDED_MANTISSA_LENGTH = 63; + private static final int EXTENDED_EXPONENT_LENGTH = 15; + private static final long EXTENDED_INTEGER_MASK = 0x8000000000000000L; + + /** + * Creates the custom header of the AIFF file. It is expected that all + * passed data are supported. + */ + private static byte[] createHeader(final byte bits, final int rate, + final int channel, final long frameLength) { + long doubleBits = Double.doubleToLongBits(rate); + + long sign = (doubleBits & DOUBLE_SIGN_MASK) + >> (DOUBLE_EXPONENT_LENGTH + DOUBLE_MANTISSA_LENGTH); + long doubleExponent = (doubleBits & DOUBLE_EXPONENT_MASK) + >> DOUBLE_MANTISSA_LENGTH; + long doubleMantissa = doubleBits & DOUBLE_MANTISSA_MASK; + + long extendedExponent = doubleExponent - DOUBLE_EXPONENT_OFFSET + + EXTENDED_EXPONENT_OFFSET; + long extendedMantissa = doubleMantissa + << (EXTENDED_MANTISSA_LENGTH - DOUBLE_MANTISSA_LENGTH); + long extendedSign = sign << EXTENDED_EXPONENT_LENGTH; + short extendedBits79To64 = (short) (extendedSign | extendedExponent); + long extendedBits63To0 = EXTENDED_INTEGER_MASK | extendedMantissa; + + return new byte[]{ + // AIFF_MAGIC + 0x46, 0x4f, 0x52, 0x4d, + // fileLength (will use the number of frames for testing) + (byte) (frameLength >> 24), (byte) (frameLength >> 16), + (byte) (frameLength >> 8), (byte) frameLength, + // form aiff + 0x41, 0x49, 0x46, 0x46, + // COMM_MAGIC + 0x43, 0x4f, 0x4d, 0x4d, + // comm chunk size + 0, 0, 0, 18, + // channels + (byte) (channel >> 8),(byte) channel, + // numSampleFrames + (byte) (frameLength >> 24), (byte) (frameLength >> 16), + (byte) (frameLength >> 8), (byte) (frameLength), + // samplesize + (byte) (bits >> 8),(byte) (bits), + // samplerate + (byte) (extendedBits79To64 >> 8), + (byte) extendedBits79To64, + (byte) (extendedBits63To0 >> 56), + (byte) (extendedBits63To0 >> 48), + (byte) (extendedBits63To0 >> 40), + (byte) (extendedBits63To0 >> 32), (byte) (extendedBits63To0 >> 24), + (byte) (extendedBits63To0 >> 16), (byte) (extendedBits63To0 >> 8), + (byte) extendedBits63To0, + // SND_MAGIC + 0x53, 0x53, 0x4e, 0x44, + // data chunk size + 0, 0, 0, 0, + // dataOffset + 0, 0, 0, 0, + // blocksize + 0, 0, 0, 0, + }; + } +} --- /dev/null 2016-04-07 21:16:23.000000000 +0300 +++ new/test/javax/sound/sampled/spi/AudioFileReader/RecognizeHugeAuFiles.java 2016-04-07 21:16:23.000000000 +0300 @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.ByteArrayInputStream; + +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; + +/** + * @test + * @bug 6729836 + */ +public final class RecognizeHugeAuFiles { + + /** + * The size of the header's data. + */ + private static final byte AU_HEADER = 44; + + /** + * This value should be used if the size in bytes is unknown. + */ + private static final /* unsigned int */ long MAX_UNSIGNED_INT = 0xffffffffL; + + /** + * The list of supported au formats and sample size in bits per format. + */ + private static final byte[][] auTypeBits = { + {1, 8}, {2, 8}, {3, 16}, {4, 24}, {5, 32}, {6, 32}, {27, 8} + }; + + /** + * The list of supported sample rates(stored as unsigned int). + */ + private static final int[] sampleRates = { + 8000, 11025, 16000, 22050, 32000, 37800, 44056, 44100, 47250, 48000, + 50000, 50400, 88200, 96000, 176400, 192000, 352800, 2822400, + 5644800, Integer.MAX_VALUE + }; + + /** + * The list of supported channels (stored as unsigned int). + */ + private static final int[] channels = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + }; + + /** + * The list of supported size of data (stored as unsigned int). + *

+ * The {@code MAX_UNSIGNED_INT} used if the size in bytes is unknown. + */ + private static final long[] dataSizes = { + 0, 1, 2, 3, Integer.MAX_VALUE - AU_HEADER, Integer.MAX_VALUE - 1, + Integer.MAX_VALUE, (long) Integer.MAX_VALUE + 1, + (long) Integer.MAX_VALUE + AU_HEADER, MAX_UNSIGNED_INT - AU_HEADER, + MAX_UNSIGNED_INT - 1, MAX_UNSIGNED_INT + }; + + public static void main(final String[] args) throws Exception { + for (final byte[] type : auTypeBits) { + for (final int sampleRate : sampleRates) { + for (final int channel : channels) { + for (final long dataSize : dataSizes) { + testAFF(type, sampleRate, channel, dataSize); + testAIS(type, sampleRate, channel, dataSize); + } + } + } + } + } + + /** + * Tests the {@code AudioFileFormat} fetched from the fake header. + *

+ * Note that the frameLength and byteLength are stored as int which means + * that {@code AudioFileFormat} will store the data above {@code MAX_INT} + * as NOT_SPECIFIED. + */ + private static void testAFF(final byte[] type, final int rate, + final int channel, final long size) + throws Exception { + final byte[] header = createHeader(type, rate, channel, size); + final ByteArrayInputStream fake = new ByteArrayInputStream(header); + final AudioFileFormat aff = AudioSystem.getAudioFileFormat(fake); + final AudioFormat format = aff.getFormat(); + + if (aff.getType() != AudioFileFormat.Type.AU) { + throw new RuntimeException("Error"); + } + + final long frameLength = size / format.getFrameSize(); + if (size != MAX_UNSIGNED_INT && frameLength <= Integer.MAX_VALUE) { + if (aff.getFrameLength() != frameLength) { + System.err.println("Expected: " + frameLength); + System.err.println("Actual: " + aff.getFrameLength()); + throw new RuntimeException(); + } + } else { + if (aff.getFrameLength() != AudioSystem.NOT_SPECIFIED) { + System.err.println("Expected: " + AudioSystem.NOT_SPECIFIED); + System.err.println("Actual: " + aff.getFrameLength()); + throw new RuntimeException(); + } + } + + final long byteLength = size + AU_HEADER; + if (byteLength <= Integer.MAX_VALUE) { + if (aff.getByteLength() != byteLength) { + System.err.println("Expected: " + byteLength); + System.err.println("Actual: " + aff.getByteLength()); + throw new RuntimeException(); + } + } else { + if (aff.getByteLength() != AudioSystem.NOT_SPECIFIED) { + System.err.println("Expected: " + AudioSystem.NOT_SPECIFIED); + System.err.println("Actual: " + aff.getByteLength()); + throw new RuntimeException(); + } + } + validateFormat(type[1], rate, channel, aff.getFormat()); + } + + /** + * Tests the {@code AudioInputStream} fetched from the fake header. + *

+ * Note that the frameLength is stored as long which means + * that {@code AudioInputStream} must store all possible data from au file. + */ + private static void testAIS(final byte[] type, final int rate, + final int channel, final long size) + throws Exception { + final byte[] header = createHeader(type, rate, channel, size); + final ByteArrayInputStream fake = new ByteArrayInputStream(header); + final AudioInputStream ais = AudioSystem.getAudioInputStream(fake); + final AudioFormat format = ais.getFormat(); + final long frameLength = size / format.getFrameSize(); + if (size != MAX_UNSIGNED_INT) { + if (frameLength != ais.getFrameLength()) { + System.err.println("Expected: " + frameLength); + System.err.println("Actual: " + ais.getFrameLength()); + throw new RuntimeException(); + } + } else { + if (ais.getFrameLength() != AudioSystem.NOT_SPECIFIED) { + System.err.println("Expected: " + AudioSystem.NOT_SPECIFIED); + System.err.println("Actual: " + ais.getFrameLength()); + throw new RuntimeException(); + } + } + if (ais.available() < 0) { + System.err.println("available should be >=0: " + ais.available()); + throw new RuntimeException(); + } + validateFormat(type[1], rate, channel, format); + } + + /** + * Tests that format contains the same data as were provided to the fake + * stream. + */ + private static void validateFormat(final byte bits, final int rate, + final int channel, + final AudioFormat format) { + + if (Float.compare(format.getSampleRate(), rate) != 0) { + System.out.println("Expected: " + rate); + System.out.println("Actual: " + format.getSampleRate()); + throw new RuntimeException(); + } + if (format.getChannels() != channel) { + System.out.println("Expected: " + channel); + System.out.println("Actual: " + format.getChannels()); + throw new RuntimeException(); + } + int frameSize = ((bits + 7) / 8) * channel; + if (format.getFrameSize() != frameSize) { + System.out.println("Expected: " + frameSize); + System.out.println("Actual: " + format.getFrameSize()); + throw new RuntimeException(); + } + } + + /** + * Creates the custom header of the AU file. It is expected that all passed + * data are supported. + */ + private static byte[] createHeader(final byte[] type, final int rate, + final int channel, final long size) { + return new byte[]{ + // AU_SUN_MAGIC + 0x2e, 0x73, 0x6e, 0x64, + // headerSize + 0, 0, 0, AU_HEADER, + // dataSize + (byte) (size >> 24), (byte) (size >> 16), (byte) (size >> 8), + (byte) size, + // encoding + 0, 0, 0, type[0], + // sampleRate + (byte) (rate >> 24), (byte) (rate >> 16), (byte) (rate >> 8), + (byte) (rate), + // channels + (byte) (channel >> 24), (byte) (channel >> 16), + (byte) (channel >> 8), (byte) (channel), + // data + 0, 0, 0, 0, 0, 0 + }; + } +} --- old/test/javax/sound/sampled/spi/AudioFileReader/RecognizeHugeWaveFloatFiles.java 2016-04-07 21:16:24.000000000 +0300 +++ /dev/null 2016-04-07 21:16:24.000000000 +0300 @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.ByteArrayInputStream; - -import javax.sound.sampled.AudioFileFormat; -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioInputStream; -import javax.sound.sampled.AudioSystem; - -/** - * @test - * @bug 8132782 - */ -public final class RecognizeHugeWaveFloatFiles { - - /** - * The maximum size in bytes per WAVE specification. - */ - private static final /*unsigned int */ long MAX_UNSIGNED_INT = 0xffffffffL; - - /** - * The supported wave pcm_float format and sample size in bits. - */ - private static final byte[][] waveTypeBits = { - {0x0003/*WAVE_FORMAT_IEEE_FLOAT*/, 32} - }; - - /** - * The list of supported sample rates(stored as unsigned int). - */ - private static final int[] sampleRates = { - 8000, 11025, 16000, 22050, 32000, 37800, 44056, 44100, 47250, 48000, - 50000, 50400, 88200, 96000, 176400, 192000, 352800, 2822400, - 5644800, Integer.MAX_VALUE - }; - - /** - * The list of supported channels (stored as unsigned int). - */ - private static final int[] channels = { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - }; - - /** - * The list of supported size of data (stored as unsigned int). - *

- * The {@code MAX_UNSIGNED_INT} is a maximum size. - */ - private static final long[] dataSizes = { - 0, 1, 2, 3, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, - (long) Integer.MAX_VALUE + 1, MAX_UNSIGNED_INT - 1, MAX_UNSIGNED_INT - }; - - public static void main(final String[] args) throws Exception { - for (final byte[] type : waveTypeBits) { - for (final int sampleRate : sampleRates) { - for (final int channel : channels) { - for (final long dataSize : dataSizes) { - testAFF(type, sampleRate, channel, dataSize); - testAIS(type, sampleRate, channel, dataSize); - } - } - } - } - } - - /** - * Tests the {@code AudioFileFormat} fetched from the fake header. - *

- * Note that the frameLength and byteLength are stored as int which means - * that {@code AudioFileFormat} will store the data above {@code MAX_INT} as - * NOT_SPECIFIED. - */ - private static void testAFF(final byte[] type, final int rate, - final int channel, final long size) - throws Exception { - final byte[] header = createHeader(type, rate, channel, size); - final ByteArrayInputStream fake = new ByteArrayInputStream(header); - final AudioFileFormat aff = AudioSystem.getAudioFileFormat(fake); - final AudioFormat format = aff.getFormat(); - - if (aff.getType() != AudioFileFormat.Type.WAVE) { - throw new RuntimeException("Error"); - } - - final long frameLength = size / format.getFrameSize(); - if (frameLength <= Integer.MAX_VALUE) { - if (aff.getFrameLength() != frameLength) { - System.err.println("Expected: " + frameLength); - System.err.println("Actual: " + aff.getFrameLength()); - throw new RuntimeException(); - } - } else { - if (aff.getFrameLength() != AudioSystem.NOT_SPECIFIED) { - System.err.println("Expected: " + AudioSystem.NOT_SPECIFIED); - System.err.println("Actual: " + aff.getFrameLength()); - throw new RuntimeException(); - } - } - validateFormat(type[1], rate, channel, aff.getFormat()); - } - - /** - * Tests the {@code AudioInputStream} fetched from the fake header. - *

- * Note that the frameLength is stored as long which means that {@code - * AudioInputStream} must store all possible data from au file. - */ - private static void testAIS(final byte[] type, final int rate, - final int channel, final long size) - throws Exception { - final byte[] header = createHeader(type, rate, channel, size); - final ByteArrayInputStream fake = new ByteArrayInputStream(header); - final AudioInputStream ais = AudioSystem.getAudioInputStream(fake); - final AudioFormat format = ais.getFormat(); - final long frameLength = size / format.getFrameSize(); - if (frameLength != ais.getFrameLength()) { - System.err.println("Expected: " + frameLength); - System.err.println("Actual: " + ais.getFrameLength()); - throw new RuntimeException(); - } - if (ais.available() < 0) { - System.err.println("available should be >=0: " + ais.available()); - throw new RuntimeException(); - } - - validateFormat(type[1], rate, channel, format); - } - - /** - * Tests that format contains the same data as were provided to the fake - * stream. - */ - private static void validateFormat(final byte bits, final int rate, - final int channel, - final AudioFormat format) { - - if (Float.compare(format.getSampleRate(), rate) != 0) { - System.err.println("Expected: " + rate); - System.err.println("Actual: " + format.getSampleRate()); - throw new RuntimeException(); - } - if (format.getChannels() != channel) { - System.err.println("Expected: " + channel); - System.err.println("Actual: " + format.getChannels()); - throw new RuntimeException(); - } - if (format.getFrameSize() != ((bits + 7) / 8) * channel) { - System.err.println("Expected: " + (bits * channel + 1) / 8); - System.err.println("Actual: " + format.getFrameSize()); - throw new RuntimeException(); - } - } - - /** - * Creates the custom header of the WAVE file. It is expected that all - * passed data are supported. - */ - private static byte[] createHeader(final byte[] type, final int rate, - final int channel, final long size) { - final int frameSize = ((type[1] + 7) / 8) * channel; - return new byte[]{ - // RIFF_MAGIC - 0x52, 0x49, 0x46, 0x46, - // fileLength - -1, -1, -1, -1, - // waveMagic - 0x57, 0x41, 0x56, 0x45, - // FMT_MAGIC - 0x66, 0x6d, 0x74, 0x20, - // size - 16, 0, 0, 0, - // wav_type WAVE_FORMAT_IEEE_FLOAT - type[0], 0, - // channels - (byte) (channel), (byte) (channel >> 8), - // samplerate - (byte) (rate), (byte) (rate >> 8), (byte) (rate >> 16), - (byte) (rate >> 24), - // framerate - 1, 0, 0, 0, - // framesize - (byte) (frameSize), (byte) (frameSize >> 8), - // bits - type[1], 0, - // DATA_MAGIC - 0x64, 0x61, 0x74, 0x61, - // data size - (byte) (size), (byte) (size >> 8), (byte) (size >> 16), - (byte) (size >> 24) - // data - , 0, 0, 0, 0, 0 - }; - } -} --- /dev/null 2016-04-07 21:16:24.000000000 +0300 +++ new/test/javax/sound/sampled/spi/AudioFileReader/RecognizeHugeWaveFiles.java 2016-04-07 21:16:23.000000000 +0300 @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.ByteArrayInputStream; + +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; + +/** + * @test + * @bug 8132782 6729836 + */ +public final class RecognizeHugeWaveFiles { + + /** + * The maximum size in bytes per WAVE specification. + */ + private static final /*unsigned int */ long MAX_UNSIGNED_INT = 0xffffffffL; + + /** + * The supported wave pcm_float format and sample size in bits. + */ + private static final byte[][] waveTypeBits = { + {0x0001/*WAVE_FORMAT_PCM*/,1}, + {0x0001/*WAVE_FORMAT_PCM*/,2}, + {0x0001/*WAVE_FORMAT_PCM*/,4}, + {0x0001/*WAVE_FORMAT_PCM*/,8}, + {0x0001/*WAVE_FORMAT_PCM*/,16}, + {0x0001/*WAVE_FORMAT_PCM*/,20}, + {0x0001/*WAVE_FORMAT_PCM*/,24}, + {0x0001/*WAVE_FORMAT_PCM*/,32}, + {0x0003/*WAVE_FORMAT_IEEE_FLOAT*/, 32}, + {0x0006/*WAVE_FORMAT_ALAW*/, 8}, + {0x0007/*WAVE_FORMAT_MULAW*/, 8} + }; + + /** + * The list of supported sample rates(stored as unsigned int). + */ + private static final int[] sampleRates = { + 8000, 11025, 16000, 22050, 32000, 37800, 44056, 44100, 47250, 48000, + 50000, 50400, 88200, 96000, 176400, 192000, 352800, 2822400, + 5644800, Integer.MAX_VALUE + }; + + /** + * The list of supported channels (stored as unsigned int). + */ + private static final int[] channels = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + }; + + /** + * The list of supported size of data (stored as unsigned int). + *

+ * The {@code MAX_UNSIGNED_INT} is a maximum size. + */ + private static final long[] dataSizes = { + 0, 1, 2, 3, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, + (long) Integer.MAX_VALUE + 1, MAX_UNSIGNED_INT - 1, MAX_UNSIGNED_INT + }; + + public static void main(final String[] args) throws Exception { + for (final byte[] type : waveTypeBits) { + for (final int sampleRate : sampleRates) { + for (final int channel : channels) { + for (final long dataSize : dataSizes) { + testAFF(type, sampleRate, channel, dataSize); + testAIS(type, sampleRate, channel, dataSize); + } + } + } + } + } + + /** + * Tests the {@code AudioFileFormat} fetched from the fake header. + *

+ * Note that the frameLength and byteLength are stored as int which means + * that {@code AudioFileFormat} will store the data above {@code MAX_INT} as + * NOT_SPECIFIED. + */ + private static void testAFF(final byte[] type, final int rate, + final int channel, final long size) + throws Exception { + final byte[] header = createHeader(type, rate, channel, size); + final ByteArrayInputStream fake = new ByteArrayInputStream(header); + final AudioFileFormat aff = AudioSystem.getAudioFileFormat(fake); + final AudioFormat format = aff.getFormat(); + + if (aff.getType() != AudioFileFormat.Type.WAVE) { + throw new RuntimeException("Error"); + } + + final long frameLength = size / format.getFrameSize(); + if (frameLength <= Integer.MAX_VALUE) { + if (aff.getFrameLength() != frameLength) { + System.err.println("Expected: " + frameLength); + System.err.println("Actual: " + aff.getFrameLength()); + throw new RuntimeException(); + } + } else { + if (aff.getFrameLength() != AudioSystem.NOT_SPECIFIED) { + System.err.println("Expected: " + AudioSystem.NOT_SPECIFIED); + System.err.println("Actual: " + aff.getFrameLength()); + throw new RuntimeException(); + } + } + validateFormat(type[1], rate, channel, aff.getFormat()); + } + + /** + * Tests the {@code AudioInputStream} fetched from the fake header. + *

+ * Note that the frameLength is stored as long which means that {@code + * AudioInputStream} must store all possible data from wave file. + */ + private static void testAIS(final byte[] type, final int rate, + final int channel, final long size) + throws Exception { + final byte[] header = createHeader(type, rate, channel, size); + final ByteArrayInputStream fake = new ByteArrayInputStream(header); + final AudioInputStream ais = AudioSystem.getAudioInputStream(fake); + final AudioFormat format = ais.getFormat(); + final long frameLength = size / format.getFrameSize(); + if (frameLength != ais.getFrameLength()) { + System.err.println("Expected: " + frameLength); + System.err.println("Actual: " + ais.getFrameLength()); + throw new RuntimeException(); + } + if (ais.available() < 0) { + System.err.println("available should be >=0: " + ais.available()); + throw new RuntimeException(); + } + + validateFormat(type[1], rate, channel, format); + } + + /** + * Tests that format contains the same data as were provided to the fake + * stream. + */ + private static void validateFormat(final byte bits, final int rate, + final int channel, + final AudioFormat format) { + + if (Float.compare(format.getSampleRate(), rate) != 0) { + System.err.println("Expected: " + rate); + System.err.println("Actual: " + format.getSampleRate()); + throw new RuntimeException(); + } + if (format.getChannels() != channel) { + System.err.println("Expected: " + channel); + System.err.println("Actual: " + format.getChannels()); + throw new RuntimeException(); + } + int frameSize = ((bits + 7) / 8) * channel; + if (format.getFrameSize() != frameSize) { + System.err.println("Expected: " + frameSize); + System.err.println("Actual: " + format.getFrameSize()); + throw new RuntimeException(); + } + } + + /** + * Creates the custom header of the WAVE file. It is expected that all + * passed data are supported. + */ + private static byte[] createHeader(final byte[] type, final int rate, + final int channel, final long size) { + final int frameSize = ((type[1] + 7) / 8) * channel; + return new byte[]{ + // RIFF_MAGIC + 0x52, 0x49, 0x46, 0x46, + // fileLength + -1, -1, -1, -1, + // waveMagic + 0x57, 0x41, 0x56, 0x45, + // FMT_MAGIC + 0x66, 0x6d, 0x74, 0x20, + // size + 16, 0, 0, 0, + // wav_type WAVE_FORMAT_IEEE_FLOAT + type[0], 0, + // channels + (byte) (channel), (byte) (channel >> 8), + // samplerate + (byte) (rate), (byte) (rate >> 8), (byte) (rate >> 16), + (byte) (rate >> 24), + // framerate + 1, 0, 0, 0, + // framesize + (byte) (frameSize), (byte) (frameSize >> 8), + // bits + type[1], 0, + // DATA_MAGIC + 0x64, 0x61, 0x74, 0x61, + // data size + (byte) (size), (byte) (size >> 8), (byte) (size >> 16), + (byte) (size >> 24) + // data + , 0, 0, 0, 0, 0 + }; + } +}