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
26package com.sun.media.sound;
27
28import java.io.BufferedInputStream;
29import java.io.DataInputStream;
30import java.io.EOFException;
31import java.io.File;
32import java.io.FileInputStream;
33import java.io.IOException;
34import java.io.InputStream;
35import java.net.URL;
36
37import javax.sound.sampled.AudioFileFormat;
38import javax.sound.sampled.AudioInputStream;
39import javax.sound.sampled.UnsupportedAudioFileException;
40import javax.sound.sampled.spi.AudioFileReader;
41
42/**
43 * Abstract File Reader class.
44 *
45 * @author Jan Borgersen
46 */
47abstract class SunFileReader extends AudioFileReader {
48
49    @Override
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
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
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
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
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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