1 /*
2  * Copyright (c) 1999, 2017, 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.media.sound;
27 
28 import java.io.BufferedInputStream;
29 import java.io.DataInputStream;
30 import java.io.EOFException;
31 import java.io.File;
32 import java.io.FileInputStream;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.net.URL;
36 
37 import javax.sound.sampled.AudioFileFormat;
38 import javax.sound.sampled.AudioInputStream;
39 import javax.sound.sampled.UnsupportedAudioFileException;
40 import javax.sound.sampled.spi.AudioFileReader;
41 
42 /**
43  * Abstract File Reader class.
44  *
45  * @author Jan Borgersen
46  */
47 abstract class SunFileReader extends AudioFileReader {
48 
49     @Override
getAudioFileFormat(final InputStream stream)50     public final StandardFileFormat getAudioFileFormat(final InputStream stream)
51             throws UnsupportedAudioFileException, IOException {
52         stream.mark(200); // The biggest value which was historically used
53         try {
54             return getAudioFileFormatImpl(stream);
55         } catch (final EOFException ignored) {
56             // the header is less than was expected
57             throw new UnsupportedAudioFileException();
58         } finally {
59             stream.reset();
60         }
61     }
62 
63     @Override
getAudioFileFormat(final URL url)64     public final AudioFileFormat getAudioFileFormat(final URL url)
65             throws UnsupportedAudioFileException, IOException {
66         try (InputStream is = url.openStream()) {
67             return getAudioFileFormatImpl(new BufferedInputStream(is));
68         } catch (final EOFException ignored) {
69             // the header is less than was expected
70             throw new UnsupportedAudioFileException();
71         }
72     }
73 
74     @Override
getAudioFileFormat(final File file)75     public final AudioFileFormat getAudioFileFormat(final File file)
76             throws UnsupportedAudioFileException, IOException {
77         try (InputStream is = new FileInputStream(file)) {
78             return getAudioFileFormatImpl(new BufferedInputStream(is));
79         } catch (final EOFException ignored) {
80             // the header is less than was expected
81             throw new UnsupportedAudioFileException();
82         }
83     }
84 
85     @Override
getAudioInputStream(final InputStream stream)86     public AudioInputStream getAudioInputStream(final InputStream stream)
87             throws UnsupportedAudioFileException, IOException {
88         stream.mark(200); // The biggest value which was historically used
89         try {
90             final StandardFileFormat format = getAudioFileFormatImpl(stream);
91             // we've got everything, the stream is supported and it is at the
92             // beginning of the audio data, so return an AudioInputStream
93             return new AudioInputStream(stream, format.getFormat(),
94                                         format.getLongFrameLength());
95         } catch (UnsupportedAudioFileException | EOFException ignored) {
96             // stream is unsupported or the header is less than was expected
97             stream.reset();
98             throw new UnsupportedAudioFileException();
99         }
100     }
101 
102     @Override
getAudioInputStream(final URL url)103     public final AudioInputStream getAudioInputStream(final URL url)
104             throws UnsupportedAudioFileException, IOException {
105         final InputStream urlStream = url.openStream();
106         try {
107             return getAudioInputStream(new BufferedInputStream(urlStream));
108         } catch (final Throwable e) {
109             closeSilently(urlStream);
110             throw e;
111         }
112     }
113 
114     @Override
getAudioInputStream(final File file)115     public final AudioInputStream getAudioInputStream(final File file)
116             throws UnsupportedAudioFileException, IOException {
117         final InputStream fileStream = new FileInputStream(file);
118         try {
119             return getAudioInputStream(new BufferedInputStream(fileStream));
120         } catch (final Throwable e) {
121             closeSilently(fileStream);
122             throw e;
123         }
124     }
125 
126     /**
127      * Obtains the audio file format of the input stream provided. The stream
128      * must point to valid audio file data. Note that default implementation of
129      * {@link #getAudioInputStream(InputStream)} assume that this method leaves
130      * the input stream at the beginning of the audio data.
131      *
132      * @param  stream the input stream from which file format information should
133      *         be extracted
134      * @return an {@code AudioFileFormat} object describing the audio file
135      *         format
136      * @throws UnsupportedAudioFileException if the stream does not point to
137      *         valid audio file data recognized by the system
138      * @throws IOException if an I/O exception occurs
139      * @throws EOFException is used incorrectly by our readers instead of
140      *         UnsupportedAudioFileException if the header is less than was
141      *         expected
142      */
getAudioFileFormatImpl(InputStream stream)143     abstract StandardFileFormat getAudioFileFormatImpl(InputStream stream)
144             throws UnsupportedAudioFileException, IOException;
145 
146     // HELPER METHODS
147 
148     /**
149      * Closes the InputStream when we have read all necessary data from it, and
150      * ignores an IOException.
151      *
152      * @param is the InputStream which should be closed
153      */
closeSilently(final InputStream is)154     private static void closeSilently(final InputStream is) {
155         try {
156             is.close();
157         } catch (final IOException ignored) {
158             // IOException is ignored
159         }
160     }
161 
162     /**
163      * rllong
164      * Protected helper method to read 64 bits and changing the order of
165      * each bytes.
166      * @return 32 bits swapped value.
167      * @exception IOException
168      */
rllong(DataInputStream dis)169     final int rllong(DataInputStream dis) throws IOException {
170 
171         int b1, b2, b3, b4 ;
172         int i = 0;
173 
174         i = dis.readInt();
175 
176         b1 = ( i & 0xFF ) << 24 ;
177         b2 = ( i & 0xFF00 ) << 8;
178         b3 = ( i & 0xFF0000 ) >> 8;
179         b4 = ( i & 0xFF000000 ) >>> 24;
180 
181         i = ( b1 | b2 | b3 | b4 );
182 
183         return i;
184     }
185 
186     /**
187      * big2little
188      * Protected helper method to swap the order of bytes in a 32 bit int
189      * @return 32 bits swapped value
190      */
big2little(int i)191     final int big2little(int i) {
192 
193         int b1, b2, b3, b4 ;
194 
195         b1 = ( i & 0xFF ) << 24 ;
196         b2 = ( i & 0xFF00 ) << 8;
197         b3 = ( i & 0xFF0000 ) >> 8;
198         b4 = ( i & 0xFF000000 ) >>> 24;
199 
200         i = ( b1 | b2 | b3 | b4 );
201 
202         return i;
203     }
204 
205     /**
206      * rlshort
207      * Protected helper method to read 16 bits value. Swap high with low byte.
208      * @return the swapped value.
209      * @exception IOException
210      */
rlshort(DataInputStream dis)211     final short rlshort(DataInputStream dis)  throws IOException {
212 
213         short s=0;
214         short high, low;
215 
216         s = dis.readShort();
217 
218         high = (short)(( s & 0xFF ) << 8) ;
219         low = (short)(( s & 0xFF00 ) >>> 8);
220 
221         s = (short)( high | low );
222 
223         return s;
224     }
225 
226     /**
227      * big2little
228      * Protected helper method to swap the order of bytes in a 16 bit short
229      * @return 16 bits swapped value
230      */
big2littleShort(short i)231     final short big2littleShort(short i) {
232 
233         short high, low;
234 
235         high = (short)(( i & 0xFF ) << 8) ;
236         low = (short)(( i & 0xFF00 ) >>> 8);
237 
238         i = (short)( high | low );
239 
240         return i;
241     }
242 
243     /** Calculates the frame size for PCM frames.
244      * Note that this method is appropriate for non-packed samples.
245      * For instance, 12 bit, 2 channels will return 4 bytes, not 3.
246      * @param sampleSizeInBits the size of a single sample in bits
247      * @param channels the number of channels
248      * @return the size of a PCM frame in bytes.
249      */
calculatePCMFrameSize(int sampleSizeInBits, int channels)250     static final int calculatePCMFrameSize(int sampleSizeInBits, int channels) {
251         try {
252             return Math.multiplyExact((sampleSizeInBits + 7) / 8, channels);
253         } catch (final ArithmeticException ignored) {
254             return 0;
255         }
256     }
257 }
258