1 /*
2  * Copyright (c) 1999, 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.  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.DataInputStream;
29 import java.io.EOFException;
30 import java.io.IOException;
31 import java.io.InputStream;
32 
33 import javax.sound.sampled.AudioFileFormat;
34 import javax.sound.sampled.AudioFormat;
35 import javax.sound.sampled.AudioSystem;
36 import javax.sound.sampled.UnsupportedAudioFileException;
37 
38 /**
39  * WAVE file reader.
40  *
41  * @author Kara Kytle
42  * @author Jan Borgersen
43  * @author Florian Bomers
44  */
45 public final class WaveFileReader extends SunFileReader {
46 
47     @Override
getAudioFileFormatImpl(final InputStream stream)48     StandardFileFormat getAudioFileFormatImpl(final InputStream stream)
49             throws UnsupportedAudioFileException, IOException {
50 
51         // assumes sream is rewound
52 
53         int nread = 0;
54         int fmt;
55         int length = 0;
56         int wav_type = 0;
57         short channels;
58         long sampleRate;
59         long avgBytesPerSec;
60         short blockAlign;
61         int sampleSizeInBits;
62         AudioFormat.Encoding encoding = null;
63 
64         DataInputStream dis = new DataInputStream( stream );
65 
66         int magic = dis.readInt();
67         long /* unsigned int */ fileLength = rllong(dis) & 0xffffffffL;
68         int waveMagic = dis.readInt();
69         long totallength;
70         if (fileLength <= 0) {
71             fileLength = AudioSystem.NOT_SPECIFIED;
72             totallength = AudioSystem.NOT_SPECIFIED;
73         } else {
74             totallength = fileLength + 8;
75         }
76 
77         if ((magic != WaveFileFormat.RIFF_MAGIC) || (waveMagic != WaveFileFormat.WAVE_MAGIC)) {
78             // not WAVE, throw UnsupportedAudioFileException
79             throw new UnsupportedAudioFileException("not a WAVE file");
80         }
81 
82         // find and read the "fmt" chunk
83         // we break out of this loop either by hitting EOF or finding "fmt "
84         while(true) {
85 
86             try {
87                 fmt = dis.readInt();
88                 nread += 4;
89                 if( fmt==WaveFileFormat.FMT_MAGIC ) {
90                     // we've found the 'fmt' chunk
91                     break;
92                 } else {
93                     // else not 'fmt', skip this chunk
94                     length = rllong(dis);
95                     nread += 4;
96                     if (length % 2 > 0) length++;
97                     nread += dis.skipBytes(length);
98                 }
99             } catch (EOFException eof) {
100                                 // we've reached the end of the file without finding the 'fmt' chunk
101                 throw new UnsupportedAudioFileException("Not a valid WAV file");
102             }
103         }
104 
105         // Read the format chunk size.
106         length = rllong(dis);
107         nread += 4;
108 
109         // This is the nread position at the end of the format chunk
110         int endLength = nread + length;
111 
112         // Read the wave format data out of the format chunk.
113 
114         // encoding.
115         wav_type = rlshort(dis); nread += 2;
116 
117         if (wav_type == WaveFileFormat.WAVE_FORMAT_PCM)
118             encoding = AudioFormat.Encoding.PCM_SIGNED;  // if 8-bit, we need PCM_UNSIGNED, below...
119         else if ( wav_type == WaveFileFormat.WAVE_FORMAT_ALAW )
120             encoding = AudioFormat.Encoding.ALAW;
121         else if ( wav_type == WaveFileFormat.WAVE_FORMAT_MULAW )
122             encoding = AudioFormat.Encoding.ULAW;
123         else {
124             // we don't support any other WAVE formats....
125             throw new UnsupportedAudioFileException("Not a supported WAV file");
126         }
127         // channels
128         channels = rlshort(dis); nread += 2;
129         if (channels <= 0) {
130             throw new UnsupportedAudioFileException("Invalid number of channels");
131         }
132 
133         // sample rate.
134         sampleRate = rllong(dis); nread += 4;
135 
136         // this is the avgBytesPerSec
137         avgBytesPerSec = rllong(dis); nread += 4;
138 
139         // this is blockAlign value
140         blockAlign = rlshort(dis); nread += 2;
141 
142         // this is the PCM-specific value bitsPerSample
143         sampleSizeInBits = (int)rlshort(dis); nread += 2;
144         if (sampleSizeInBits <= 0) {
145             throw new UnsupportedAudioFileException("Invalid bitsPerSample");
146         }
147 
148         // if sampleSizeInBits==8, we need to use PCM_UNSIGNED
149         if ((sampleSizeInBits==8) && encoding.equals(AudioFormat.Encoding.PCM_SIGNED))
150             encoding = AudioFormat.Encoding.PCM_UNSIGNED;
151 
152         // skip any difference between the length of the format chunk
153         // and what we read
154 
155         // if the length of the chunk is odd, there's an extra pad byte
156         // at the end.  i've never seen this in the fmt chunk, but we
157         // should check to make sure.
158 
159         if (length % 2 != 0) length += 1;
160 
161         // $$jb: 07.28.99: endLength>nread, not length>nread.
162         //       This fixes #4257986
163         if (endLength > nread)
164             nread += dis.skipBytes(endLength - nread);
165 
166         // we have a format now, so find the "data" chunk
167         // we break out of this loop either by hitting EOF or finding "data"
168         // $$kk: if "data" chunk precedes "fmt" chunk we are hosed -- can this legally happen?
169         nread = 0;
170         while(true) {
171             try{
172                 int datahdr = dis.readInt();
173                 nread+=4;
174                 if (datahdr == WaveFileFormat.DATA_MAGIC) {
175                     // we've found the 'data' chunk
176                     break;
177                 } else {
178                     // else not 'data', skip this chunk
179                     int thisLength = rllong(dis); nread += 4;
180                     if (thisLength % 2 > 0) thisLength++;
181                     nread += dis.skipBytes(thisLength);
182                 }
183             } catch (EOFException eof) {
184                 // we've reached the end of the file without finding the 'data' chunk
185                 throw new UnsupportedAudioFileException("Not a valid WAV file");
186             }
187         }
188         // this is the length of the data chunk
189         long /* unsigned int */ dataLength = rllong(dis) & 0xffffffffL; nread += 4;
190 
191         // now build the new AudioFileFormat and return
192         final int frameSize = calculatePCMFrameSize(sampleSizeInBits, channels);
193         AudioFormat format = new AudioFormat(encoding,
194                                              (float)sampleRate,
195                                              sampleSizeInBits, channels,
196                                              frameSize,
197                                              (float)sampleRate, false);
198 
199         long frameLength = dataLength / format.getFrameSize();
200         return new WaveFileFormat(AudioFileFormat.Type.WAVE, totallength,
201                                   format, frameLength);
202     }
203 }
204