# HG changeset patch # User serb # Date 1452199800 -10800 # Node ID 1c21a27682a5caef4f7c7c9059015534e2f09e4c # Parent f405bbd07cc69439800d52ad90019b719e92d675 8064800: AudioSystem/WaveFileWriter can't write PCM_FLOAT, but writes it anyway Reviewed-by: prr, amenkov diff -r f405bbd07cc6 -r 1c21a27682a5 jdk/src/java.desktop/share/classes/META-INF/services/javax.sound.sampled.spi.AudioFileWriter --- a/jdk/src/java.desktop/share/classes/META-INF/services/javax.sound.sampled.spi.AudioFileWriter Tue Jan 12 23:33:45 2016 +0300 +++ b/jdk/src/java.desktop/share/classes/META-INF/services/javax.sound.sampled.spi.AudioFileWriter Thu Jan 07 23:50:00 2016 +0300 @@ -2,3 +2,4 @@ com.sun.media.sound.AuFileWriter com.sun.media.sound.AiffFileWriter com.sun.media.sound.WaveFileWriter +com.sun.media.sound.WaveFloatFileWriter diff -r f405bbd07cc6 -r 1c21a27682a5 jdk/src/java.desktop/share/classes/com/sun/media/sound/AiffFileWriter.java --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AiffFileWriter.java Tue Jan 12 23:33:45 2016 +0300 +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AiffFileWriter.java Thu Jan 07 23:50:00 2016 +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 @@ -25,23 +25,22 @@ package com.sun.media.sound; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.IOException; - -import java.io.BufferedOutputStream; -import java.io.DataOutputStream; -import java.io.FileOutputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.RandomAccessFile; import java.io.SequenceInputStream; import java.util.Objects; import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; -import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; //$$fb this class is buggy. Should be replaced in future. @@ -63,6 +62,7 @@ // METHODS TO IMPLEMENT AudioFileWriter + @Override public AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) { AudioFileFormat.Type[] filetypes = new AudioFileFormat.Type[types.length]; @@ -84,6 +84,7 @@ } + @Override public int write(AudioInputStream stream, AudioFileFormat.Type fileType, OutputStream out) throws IOException { Objects.requireNonNull(stream); Objects.requireNonNull(fileType); @@ -106,6 +107,7 @@ } + @Override public int write(AudioInputStream stream, AudioFileFormat.Type fileType, File out) throws IOException { Objects.requireNonNull(stream); Objects.requireNonNull(fileType); @@ -160,6 +162,9 @@ * Throws IllegalArgumentException if not supported. */ private AudioFileFormat getAudioFileFormat(AudioFileFormat.Type type, AudioInputStream stream) { + if (!isFileTypeSupported(type, stream)) { + throw new IllegalArgumentException("File type " + type + " not supported."); + } AudioFormat format = null; AiffFileFormat fileFormat = null; @@ -177,10 +182,6 @@ int fileSize; boolean convert8to16 = false; - if( !types[0].equals(type) ) { - throw new IllegalArgumentException("File type " + type + " not supported."); - } - if( (AudioFormat.Encoding.ALAW.equals(streamEncoding)) || (AudioFormat.Encoding.ULAW.equals(streamEncoding)) ) { diff -r f405bbd07cc6 -r 1c21a27682a5 jdk/src/java.desktop/share/classes/com/sun/media/sound/AuFileWriter.java --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AuFileWriter.java Tue Jan 12 23:33:45 2016 +0300 +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AuFileWriter.java Thu Jan 07 23:50:00 2016 +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 @@ -142,6 +142,9 @@ * Throws IllegalArgumentException if not supported. */ private AudioFileFormat getAudioFileFormat(AudioFileFormat.Type type, AudioInputStream stream) { + if (!isFileTypeSupported(type, stream)) { + throw new IllegalArgumentException("File type " + type + " not supported."); + } AudioFormat format = null; AuFileFormat fileFormat = null; @@ -154,10 +157,6 @@ int sampleSizeInBits; int fileSize; - if( !types[0].equals(type) ) { - throw new IllegalArgumentException("File type " + type + " not supported."); - } - if( (AudioFormat.Encoding.ALAW.equals(streamEncoding)) || (AudioFormat.Encoding.ULAW.equals(streamEncoding)) ) { diff -r f405bbd07cc6 -r 1c21a27682a5 jdk/src/java.desktop/share/classes/com/sun/media/sound/WaveFileWriter.java --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/WaveFileWriter.java Tue Jan 12 23:33:45 2016 +0300 +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/WaveFileWriter.java Thu Jan 07 23:50:00 2016 +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 @@ -25,23 +25,22 @@ package com.sun.media.sound; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.IOException; - -import java.io.BufferedOutputStream; -import java.io.DataOutputStream; -import java.io.FileOutputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.RandomAccessFile; import java.io.SequenceInputStream; import java.util.Objects; import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; -import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; //$$fb this class is buggy. Should be replaced in future. @@ -85,6 +84,7 @@ // METHODS TO IMPLEMENT AudioFileWriter + @Override public AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) { AudioFileFormat.Type[] filetypes = new AudioFileFormat.Type[types.length]; @@ -106,6 +106,7 @@ } + @Override public int write(AudioInputStream stream, AudioFileFormat.Type fileType, OutputStream out) throws IOException { Objects.requireNonNull(stream); Objects.requireNonNull(fileType); @@ -130,6 +131,7 @@ } + @Override public int write(AudioInputStream stream, AudioFileFormat.Type fileType, File out) throws IOException { Objects.requireNonNull(stream); Objects.requireNonNull(fileType); @@ -173,6 +175,9 @@ * Throws IllegalArgumentException if not supported. */ private AudioFileFormat getAudioFileFormat(AudioFileFormat.Type type, AudioInputStream stream) { + if (!isFileTypeSupported(type, stream)) { + throw new IllegalArgumentException("File type " + type + " not supported."); + } AudioFormat format = null; WaveFileFormat fileFormat = null; AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED; @@ -187,9 +192,6 @@ float frameRate; int fileSize; - if (!types[0].equals(type)) { - throw new IllegalArgumentException("File type " + type + " not supported."); - } int waveType = WaveFileFormat.WAVE_FORMAT_PCM; if( AudioFormat.Encoding.ALAW.equals(streamEncoding) || diff -r f405bbd07cc6 -r 1c21a27682a5 jdk/src/java.desktop/share/classes/com/sun/media/sound/WaveFloatFileWriter.java --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/WaveFloatFileWriter.java Tue Jan 12 23:33:45 2016 +0300 +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/WaveFloatFileWriter.java Thu Jan 07 23:50:00 2016 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -22,18 +22,20 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package com.sun.media.sound; import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.util.Objects; import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFileFormat.Type; 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.AudioFileFormat.Type; import javax.sound.sampled.spi.AudioFileWriter; /** @@ -43,10 +45,12 @@ */ public final class WaveFloatFileWriter extends AudioFileWriter { + @Override public Type[] getAudioFileTypes() { - return new Type[] { Type.WAVE }; + return new Type[]{Type.WAVE}; } + @Override public Type[] getAudioFileTypes(AudioInputStream stream) { if (!stream.getFormat().getEncoding().equals(Encoding.PCM_FLOAT)) @@ -92,18 +96,22 @@ this.out = out; } + @Override public void write(int b) throws IOException { out.write(b); } + @Override public void flush() throws IOException { out.flush(); } + @Override public void write(byte[] b, int off, int len) throws IOException { out.write(b, off, len); } + @Override public void write(byte[] b) throws IOException { out.write(b); } @@ -118,8 +126,12 @@ return AudioSystem.getAudioInputStream(targetFormat, ais); } + @Override public int write(AudioInputStream stream, Type fileType, OutputStream out) throws IOException { + Objects.requireNonNull(stream); + Objects.requireNonNull(fileType); + Objects.requireNonNull(out); checkFormat(fileType, stream); if (stream.getFormat().isBigEndian()) @@ -131,8 +143,13 @@ return fpointer; } + @Override public int write(AudioInputStream stream, Type fileType, File out) throws IOException { + Objects.requireNonNull(stream); + Objects.requireNonNull(fileType); + Objects.requireNonNull(out); + checkFormat(fileType, stream); if (stream.getFormat().isBigEndian()) stream = toLittleEndian(stream); @@ -142,5 +159,4 @@ writer.close(); return fpointer; } - } diff -r f405bbd07cc6 -r 1c21a27682a5 jdk/src/java.desktop/share/classes/javax/sound/sampled/AudioSystem.java --- a/jdk/src/java.desktop/share/classes/javax/sound/sampled/AudioSystem.java Tue Jan 12 23:33:45 2016 +0300 +++ b/jdk/src/java.desktop/share/classes/javax/sound/sampled/AudioSystem.java Thu Jan 07 23:50:00 2016 +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 @@ -1187,32 +1187,24 @@ * @see #isFileTypeSupported * @see #getAudioFileTypes */ - public static int write(AudioInputStream stream, AudioFileFormat.Type fileType, - OutputStream out) throws IOException { + public static int write(final AudioInputStream stream, + final AudioFileFormat.Type fileType, + final OutputStream out) throws IOException { Objects.requireNonNull(stream); Objects.requireNonNull(fileType); Objects.requireNonNull(out); - List providers = getAudioFileWriters(); - int bytesWritten = 0; - boolean flag = false; - - for(int i=0; i < providers.size(); i++) { - AudioFileWriter writer = providers.get(i); + for (final AudioFileWriter writer : getAudioFileWriters()) { try { - bytesWritten = writer.write( stream, fileType, out ); // throws IOException - flag = true; - break; - } catch (IllegalArgumentException e) { - // thrown if this provider cannot write the sequence, try the next - continue; + return writer.write(stream, fileType, out); + } catch (final IllegalArgumentException ignored) { + // thrown if this provider cannot write the stream, try next } } - if(!flag) { - throw new IllegalArgumentException("could not write audio file: file type not supported: " + fileType); - } else { - return bytesWritten; - } + // "File type " + type + " not supported." + throw new IllegalArgumentException( + "could not write audio file: file type not supported: " + + fileType); } /** @@ -1232,32 +1224,23 @@ * @see #isFileTypeSupported * @see #getAudioFileTypes */ - public static int write(AudioInputStream stream, AudioFileFormat.Type fileType, - File out) throws IOException { + public static int write(final AudioInputStream stream, + final AudioFileFormat.Type fileType, + final File out) throws IOException { Objects.requireNonNull(stream); Objects.requireNonNull(fileType); Objects.requireNonNull(out); - List providers = getAudioFileWriters(); - int bytesWritten = 0; - boolean flag = false; - - for(int i=0; i < providers.size(); i++) { - AudioFileWriter writer = providers.get(i); + for (final AudioFileWriter writer : getAudioFileWriters()) { try { - bytesWritten = writer.write( stream, fileType, out ); // throws IOException - flag = true; - break; - } catch (IllegalArgumentException e) { - // thrown if this provider cannot write the sequence, try the next - continue; + return writer.write(stream, fileType, out); + } catch (final IllegalArgumentException ignored) { + // thrown if this provider cannot write the stream, try next } } - if (!flag) { - throw new IllegalArgumentException("could not write audio file: file type not supported: " + fileType); - } else { - return bytesWritten; - } + throw new IllegalArgumentException( + "could not write audio file: file type not supported: " + + fileType); } // METHODS FOR INTERNAL IMPLEMENTATION USE diff -r f405bbd07cc6 -r 1c21a27682a5 jdk/test/javax/sound/sampled/spi/AudioFileWriter/WriteUnsupportedAudioFormat.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/sound/sampled/spi/AudioFileWriter/WriteUnsupportedAudioFormat.java Thu Jan 07 23:50:00 2016 +0300 @@ -0,0 +1,208 @@ +/* + * 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 java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFileFormat.Type; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.spi.AudioFileWriter; + +import static java.util.ServiceLoader.load; +import static javax.sound.sampled.AudioFileFormat.Type.AIFC; +import static javax.sound.sampled.AudioFileFormat.Type.AIFF; +import static javax.sound.sampled.AudioFileFormat.Type.AU; +import static javax.sound.sampled.AudioFileFormat.Type.SND; +import static javax.sound.sampled.AudioFileFormat.Type.WAVE; + +/** + * @test + * @bug 8064800 + */ +public final class WriteUnsupportedAudioFormat { + + /** + * We will try to use all formats, in this case all our providers will be + * covered by supported/unsupported formats. + */ + private static final List formats = new ArrayList<>(23000); + + private static final AudioFormat.Encoding[] encodings = { + AudioFormat.Encoding.ALAW, AudioFormat.Encoding.ULAW, + AudioFormat.Encoding.PCM_SIGNED, AudioFormat.Encoding.PCM_UNSIGNED, + AudioFormat.Encoding.PCM_FLOAT, new AudioFormat.Encoding("Test") + }; + + private static final int[] sampleRates = { + /*AudioSystem.NOT_SPECIFIED,*/ 8000, 11025, 16000, 22050, 32000, + 37800, 44056, 44100, 47250, 48000, 50000, 50400, 88200, 96000, + 176400, 192000, 352800, 2822400, 5644800 + }; + + private static final int[] sampleBits = { + /*AudioSystem.NOT_SPECIFIED, 4,*/ 8,/* 11,*/ 16/*, 20*/, 24, + 32/*, 48, 64, 128*/ + }; + + public static final int BUFFER_LEN = 127; + + private static final int[] channels = { + /*AudioSystem.NOT_SPECIFIED,*/ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + }; + + static final Type[] types = { + WAVE, AU, AIFF, AIFC, SND, new Type("TestName", "TestExt") + }; + + private static final File FILE; + + static { + try { + FILE = File.createTempFile("sound", null); + } catch (final IOException e) { + throw new RuntimeException(e); + } + FILE.deleteOnExit(); + + for (final Boolean end : new boolean[]{false, true}) { + for (final int sampleSize : sampleBits) { + for (final int sampleRate : sampleRates) { + for (final int channel : channels) { + for (final AudioFormat.Encoding enc : encodings) { + + if (enc.equals(AudioFormat.Encoding.PCM_FLOAT) + && sampleSize != 32) { + continue; + } + if (enc.equals(AudioFormat.Encoding.ALAW) + && sampleSize != 8) { + continue; + } + if (enc.equals(AudioFormat.Encoding.ULAW) + && sampleSize != 8) { + continue; + } + + final int frameSize = ((sampleSize + 7) / 8) + * channel; + formats.add( + new AudioFormat(enc, sampleRate, sampleSize, + channel, frameSize, + sampleRate, end)); + } + } + } + } + } + } + + public static void main(final String[] args) throws Exception { + for (final AudioFileFormat.Type type : types) { + for (final AudioFormat format : formats) { + testAS(type, format); + for (final AudioFileWriter afw : load(AudioFileWriter.class)) { + testAFW(afw, type, format); + } + } + } + } + + /** + * Tests the part of AudioSystem API, which implemented via AudioFileWriter. + */ + private static void testAS(final AudioFileFormat.Type type, + final AudioFormat format) throws Exception { + final AudioInputStream ais = getStream(format); + final OutputStream buffer = new ByteArrayOutputStream(BUFFER_LEN); + + if (AudioSystem.isFileTypeSupported(type, ais)) { + if (!AudioSystem.isFileTypeSupported(type)) { + throw new RuntimeException(type + ", " + format); + } + try { + AudioSystem.write(ais, type, buffer); + AudioSystem.write(ais, type, FILE); + } catch (final IllegalArgumentException e) { + throw new RuntimeException(type + ", " + format, e); + } + } else { + try { + AudioSystem.write(ais, type, buffer); + throw new RuntimeException(type + ", " + format); + } catch (final IllegalArgumentException ignored) { + } + try { + AudioSystem.write(ais, type, FILE); + throw new RuntimeException(type + ", " + format); + } catch (final IllegalArgumentException ignored) { + } + } + } + + /** + * Tests the AudioFileWriter API directly. + */ + private static void testAFW(final AudioFileWriter afw, + final AudioFileFormat.Type type, + final AudioFormat format) throws Exception { + final AudioInputStream ais = getStream(format); + final OutputStream buffer = new ByteArrayOutputStream(BUFFER_LEN); + + if (afw.isFileTypeSupported(type, ais)) { + if (!afw.isFileTypeSupported(type)) { + throw new RuntimeException(type + "," + format + ',' + afw); + } + try { + afw.write(ais, type, buffer); + afw.write(ais, type, FILE); + } catch (final IllegalArgumentException e) { + throw new RuntimeException(type + "," + format + ',' + afw, e); + } + } else { + try { + afw.write(ais, type, buffer); + throw new RuntimeException(type + "," + format + ',' + afw); + } catch (final IllegalArgumentException ignored) { + } + try { + afw.write(ais, type, FILE); + throw new RuntimeException(type + "," + format + ',' + afw); + } catch (final IllegalArgumentException ignored) { + } + } + } + + private static AudioInputStream getStream(final AudioFormat format) { + final InputStream in = new ByteArrayInputStream(new byte[BUFFER_LEN]); + return new AudioInputStream(in, format, 10); + } +}