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 import java.io.ByteArrayOutputStream;
  26 import java.io.File;
  27 import java.io.IOException;
  28 import java.io.InputStream;
  29 import java.nio.file.Files;
  30 import java.nio.file.Paths;
  31 import java.util.ArrayList;
  32 import java.util.List;
  33 
  34 import javax.sound.sampled.AudioFileFormat;
  35 import javax.sound.sampled.AudioFormat;
  36 import javax.sound.sampled.AudioInputStream;
  37 import javax.sound.sampled.AudioSystem;
  38 import javax.sound.sampled.UnsupportedAudioFileException;
  39 import javax.sound.sampled.spi.AudioFileWriter;
  40 import javax.sound.sampled.spi.FormatConversionProvider;
  41 
  42 import static java.util.ServiceLoader.load;
  43 import static javax.sound.sampled.AudioFileFormat.Type.AIFC;
  44 import static javax.sound.sampled.AudioFileFormat.Type.AIFF;
  45 import static javax.sound.sampled.AudioFileFormat.Type.AU;
  46 import static javax.sound.sampled.AudioFileFormat.Type.SND;
  47 import static javax.sound.sampled.AudioFileFormat.Type.WAVE;
  48 import static javax.sound.sampled.AudioSystem.NOT_SPECIFIED;
  49 
  50 /**
  51  * @test
  52  * @bug 8038139
  53  */
  54 public final class FrameLengthAfterConversion {
  55 
  56     /**
  57      * We will try to use all formats, in this case all our providers will be
  58      * covered by supported/unsupported formats.
  59      */
  60     private static final List<AudioFormat> formats = new ArrayList<>(23000);
  61 
  62     private static final AudioFormat.Encoding[] encodings = {
  63             AudioFormat.Encoding.ALAW, AudioFormat.Encoding.ULAW,
  64             AudioFormat.Encoding.PCM_SIGNED, AudioFormat.Encoding.PCM_UNSIGNED,
  65             AudioFormat.Encoding.PCM_FLOAT, new AudioFormat.Encoding("Test")
  66     };
  67 
  68     private static final int[] sampleBits = {
  69             1, 4, 8, 11, 16, 20, 24, 32
  70     };
  71 
  72     private static final int[] channels = {
  73             1, 2, 3, 4, 5
  74     };
  75 
  76     private static final AudioFileFormat.Type[] types = {
  77             WAVE, AU, AIFF, AIFC, SND,
  78             new AudioFileFormat.Type("TestName", "TestExt")
  79     };
  80 
  81     private static final int FRAME_LENGTH = 10;
  82 
  83     static {
  84         for (final int sampleSize : sampleBits) {
  85             for (final int channel : channels) {
  86                 for (final AudioFormat.Encoding enc : encodings) {
  87                     final int frameSize = ((sampleSize + 7) / 8) * channel;
  88                     formats.add(new AudioFormat(enc, 44100, sampleSize, channel,
  89                                                 frameSize, 44100, true));
  90                     formats.add(new AudioFormat(enc, 44100, sampleSize, channel,
  91                                                 frameSize, 44100, false));
  92                 }
  93             }
  94         }
  95     }
  96 
  97     public static void main(final String[] args) {
  98         for (final FormatConversionProvider fcp : load(
  99                 FormatConversionProvider.class)) {
 100             System.out.println("fcp = " + fcp);
 101             for (final AudioFormat from : formats) {
 102                 for (final AudioFormat to : formats) {
 103                     testAfterConversion(fcp, to, getStream(from, true));
 104                 }
 105             }
 106         }
 107 
 108         for (final AudioFileWriter afw : load(AudioFileWriter.class)) {
 109             System.out.println("afw = " + afw);
 110             for (final AudioFileFormat.Type type : types) {
 111                 for (final AudioFormat from : formats) {
 112                     testAfterSaveToStream(afw, type, getStream(from, true));
 113                 }
 114             }
 115         }
 116 
 117         for (final AudioFileWriter afw : load(AudioFileWriter.class)) {
 118             System.out.println("afw = " + afw);
 119             for (final AudioFileFormat.Type type : types) {
 120                 for (final AudioFormat from : formats) {
 121                     testAfterSaveToFile(afw, type, getStream(from, true));
 122                 }
 123             }
 124         }
 125 
 126         for (final AudioFileWriter afw : load(AudioFileWriter.class)) {
 127             System.out.println("afw = " + afw);
 128             for (final AudioFileFormat.Type type : types) {
 129                 for (final AudioFormat from : formats) {
 130                     testAfterSaveToFile(afw, type, getStream(from, false));
 131                 }
 132             }
 133         }
 134     }
 135 
 136     /**
 137      * Verifies the frame length after the stream was saved/read to/from
 138      * stream.
 139      */
 140     private static void testAfterSaveToStream(final AudioFileWriter afw,
 141                                               final AudioFileFormat.Type type,
 142                                               final AudioInputStream ais) {
 143         try {
 144             final ByteArrayOutputStream out = new ByteArrayOutputStream();
 145             afw.write(ais, type, out);
 146             final InputStream input = new ByteArrayInputStream(
 147                     out.toByteArray());
 148             validate(AudioSystem.getAudioInputStream(input).getFrameLength());
 149         } catch (IllegalArgumentException | UnsupportedAudioFileException
 150                 | IOException ignored) {
 151         }
 152     }
 153 
 154     /**
 155      * Verifies the frame length after the stream was saved/read to/from file.
 156      */
 157     private static void testAfterSaveToFile(final AudioFileWriter afw,
 158                                             final AudioFileFormat.Type type,
 159                                             AudioInputStream ais) {
 160         try {
 161             final File temp = File.createTempFile("sound", ".tmp");
 162             temp.deleteOnExit();
 163             afw.write(ais, type, temp);
 164             ais = AudioSystem.getAudioInputStream(temp);
 165             final long frameLength = ais.getFrameLength();
 166             ais.close();
 167             Files.delete(Paths.get(temp.getAbsolutePath()));
 168             validate(frameLength);
 169         } catch (IllegalArgumentException | UnsupportedAudioFileException
 170                 | IOException ignored) {
 171         }
 172     }
 173 
 174     /**
 175      * Verifies the frame length after the stream was converted to other
 176      * stream.
 177      *
 178      * @see FormatConversionProvider#getAudioInputStream(AudioFormat,
 179      * AudioInputStream)
 180      */
 181     private static void testAfterConversion(final FormatConversionProvider fcp,
 182                                             final AudioFormat to,
 183                                             final AudioInputStream ais) {
 184         if (fcp.isConversionSupported(to, ais.getFormat())) {
 185             validate(fcp.getAudioInputStream(to, ais).getFrameLength());
 186         }
 187     }
 188 
 189     /**
 190      * Throws an exception if the frameLength is specified and is not equal to
 191      * the gold value.
 192      */
 193     private static void validate(final long frameLength) {
 194         if (frameLength != FRAME_LENGTH) {
 195             System.err.println("Expected: " + FRAME_LENGTH);
 196             System.err.println("Actual: " + frameLength);
 197             throw new RuntimeException();
 198         }
 199     }
 200 
 201     private static AudioInputStream getStream(final AudioFormat format,
 202                                               final boolean frameLength) {
 203         final int dataSize = FRAME_LENGTH * format.getFrameSize();
 204         final InputStream in = new ByteArrayInputStream(new byte[dataSize]);
 205         if (frameLength) {
 206             return new AudioInputStream(in, format, FRAME_LENGTH);
 207         } else {
 208             return new AudioInputStream(in, format, NOT_SPECIFIED);
 209         }
 210     }
 211 }