summaryrefslogtreecommitdiff
path: root/songdbj/org/tritonus/file/WaveAudioFileReader.java
diff options
context:
space:
mode:
Diffstat (limited to 'songdbj/org/tritonus/file/WaveAudioFileReader.java')
-rw-r--r--songdbj/org/tritonus/file/WaveAudioFileReader.java300
1 files changed, 300 insertions, 0 deletions
diff --git a/songdbj/org/tritonus/file/WaveAudioFileReader.java b/songdbj/org/tritonus/file/WaveAudioFileReader.java
new file mode 100644
index 0000000000..62d3f1a9ea
--- /dev/null
+++ b/songdbj/org/tritonus/file/WaveAudioFileReader.java
@@ -0,0 +1,300 @@
1/*
2 * WaveAudioFileReader.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 1999,2000 by Florian Bomers <http://www.bomers.de>
9 * Copyright (c) 1999 by Matthias Pfisterer
10 *
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as published
14 * by the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 */
27
28/*
29|<--- this code is formatted to fit into 80 columns --->|
30*/
31
32package org.tritonus.sampled.file;
33
34import java.io.DataInputStream;
35import java.io.File;
36import java.io.InputStream;
37import java.io.IOException;
38
39import javax.sound.sampled.AudioSystem;
40import javax.sound.sampled.AudioFormat;
41import javax.sound.sampled.AudioFileFormat;
42import javax.sound.sampled.AudioInputStream;
43import javax.sound.sampled.UnsupportedAudioFileException;
44import javax.sound.sampled.spi.AudioFileReader;
45
46import org.tritonus.share.TDebug;
47import org.tritonus.share.sampled.file.TAudioFileFormat;
48import org.tritonus.share.sampled.file.TAudioFileReader;
49
50
51/**
52 * Class for reading wave files.
53 *
54 * @author Florian Bomers
55 * @author Matthias Pfisterer
56 */
57
58public class WaveAudioFileReader extends TAudioFileReader
59{
60 private static final int READ_LIMIT = 1000;
61
62
63
64 public WaveAudioFileReader()
65 {
66 super(READ_LIMIT);
67 }
68
69
70
71 protected void advanceChunk(DataInputStream dis, long prevLength, long prevRead)
72 throws IOException {
73 if (prevLength>0) {
74 dis.skip(((prevLength+1) & 0xFFFFFFFE)-prevRead);
75 }
76 }
77
78
79 protected long findChunk(DataInputStream dis, int key)
80 throws UnsupportedAudioFileException, IOException {
81 // $$fb 1999-12-18: we should take care that we don't exceed
82 // the mark of this stream. When we exceeded the mark and
83 // we notice that we don't support this wave file,
84 // other potential wave file readers have no chance.
85 int thisKey;
86 long chunkLength=0;
87 do {
88 advanceChunk(dis, chunkLength, 0);
89 try {
90 thisKey = dis.readInt();
91 } catch (IOException e)
92 {
93 if (TDebug.TraceAllExceptions)
94 {
95 TDebug.out(e);
96 }
97 // $$fb: when we come here, we skipped past the end of the wave file
98 // without finding the chunk.
99 // IMHO, this is not an IOException, as there are incarnations
100 // of WAVE files which store data in different chunks.
101 // maybe we can find a nice description of the "required chunk" ?
102 throw new UnsupportedAudioFileException(
103 "unsupported WAVE file: required chunk not found.");
104 }
105 chunkLength = readLittleEndianInt(dis) & 0xFFFFFFFF; // unsigned
106 }
107 while (thisKey != key);
108 return chunkLength;
109 }
110
111 protected AudioFormat readFormatChunk(DataInputStream dis,
112 long chunkLength) throws UnsupportedAudioFileException, IOException {
113 String debugAdd="";
114
115 int read=WaveTool.MIN_FMT_CHUNK_LENGTH;
116
117 if (chunkLength<WaveTool.MIN_FMT_CHUNK_LENGTH) {
118 throw new UnsupportedAudioFileException(
119 "corrupt WAVE file: format chunk is too small");
120 }
121
122 short formatCode=readLittleEndianShort(dis);
123 short channelCount = readLittleEndianShort(dis);
124 if (channelCount <= 0) {
125 throw new UnsupportedAudioFileException(
126 "corrupt WAVE file: number of channels must be positive");
127 }
128
129 int sampleRate = readLittleEndianInt(dis);
130 if (sampleRate <= 0) {
131 throw new UnsupportedAudioFileException(
132 "corrupt WAVE file: sample rate must be positive");
133 }
134
135 int avgBytesPerSecond=readLittleEndianInt(dis);
136 int blockAlign=readLittleEndianShort(dis);
137
138 AudioFormat.Encoding encoding;
139 int sampleSizeInBits;
140 int frameSize=0;
141 float frameRate=(float) sampleRate;
142
143 int cbSize = 0;
144 switch (formatCode) {
145 case WaveTool.WAVE_FORMAT_PCM:
146 if (chunkLength<WaveTool.MIN_FMT_CHUNK_LENGTH+2) {
147 throw new UnsupportedAudioFileException(
148 "corrupt WAVE file: format chunk is too small");
149 }
150 sampleSizeInBits = readLittleEndianShort(dis);
151 if (sampleSizeInBits <= 0) {
152 throw new UnsupportedAudioFileException(
153 "corrupt WAVE file: sample size must be positive");
154 }
155 encoding = (sampleSizeInBits <= 8) ?
156 AudioFormat.Encoding.PCM_UNSIGNED : AudioFormat.Encoding.PCM_SIGNED;
157 if (TDebug.TraceAudioFileReader) {
158 debugAdd+=", wBitsPerSample="+sampleSizeInBits;
159 }
160 read+=2;
161 break;
162 case WaveTool.WAVE_FORMAT_ALAW:
163 sampleSizeInBits = 8;
164 encoding = AudioFormat.Encoding.ALAW;
165 break;
166 case WaveTool.WAVE_FORMAT_ULAW:
167 sampleSizeInBits = 8;
168 encoding = AudioFormat.Encoding.ULAW;
169 break;
170 case WaveTool.WAVE_FORMAT_GSM610:
171 if (chunkLength<WaveTool.MIN_FMT_CHUNK_LENGTH+6) {
172 throw new UnsupportedAudioFileException(
173 "corrupt WAVE file: extra GSM bytes are missing");
174 }
175 sampleSizeInBits = readLittleEndianShort(dis); // sample Size (is 0 for GSM)
176 cbSize=readLittleEndianShort(dis);
177 if (cbSize < 2) {
178 throw new UnsupportedAudioFileException(
179 "corrupt WAVE file: extra GSM bytes are corrupt");
180 }
181 int decodedSamplesPerBlock=readLittleEndianShort(dis) & 0xFFFF; // unsigned
182 if (TDebug.TraceAudioFileReader) {
183 debugAdd+=", wBitsPerSample="+sampleSizeInBits
184 +", cbSize="+cbSize
185 +", wSamplesPerBlock="+decodedSamplesPerBlock;
186 }
187 sampleSizeInBits = AudioSystem.NOT_SPECIFIED;
188 encoding = WaveTool.GSM0610;
189 frameSize=blockAlign;
190 frameRate=((float) sampleRate)/((float) decodedSamplesPerBlock);
191 read+=6;
192 break;
193
194 case WaveTool.WAVE_FORMAT_IMA_ADPCM:
195 if (chunkLength < WaveTool.MIN_FMT_CHUNK_LENGTH + 2)
196 {
197 throw new UnsupportedAudioFileException(
198 "corrupt WAVE file: extra GSM bytes are missing");
199 }
200 sampleSizeInBits = readLittleEndianShort(dis);
201 cbSize = readLittleEndianShort(dis);
202 if (cbSize < 2)
203 {
204 throw new UnsupportedAudioFileException(
205 "corrupt WAVE file: extra IMA ADPCM bytes are corrupt");
206 }
207 int samplesPerBlock = readLittleEndianShort(dis) & 0xFFFF; // unsigned
208 if (TDebug.TraceAudioFileReader) {
209 debugAdd+=", wBitsPerSample="+sampleSizeInBits
210 +", cbSize="+cbSize
211 +", wSamplesPerBlock=" + samplesPerBlock;
212 }
213 sampleSizeInBits = AudioSystem.NOT_SPECIFIED;
214 encoding = WaveTool.GSM0610;
215 frameSize = blockAlign;
216 frameRate = ((float) sampleRate)/((float) samplesPerBlock);
217 read += 6;
218 break;
219
220 default:
221 throw new UnsupportedAudioFileException(
222 "unsupported WAVE file: unknown format code "+formatCode);
223 }
224 // if frameSize isn't set, calculate it (the default)
225 if (frameSize==0) {
226 frameSize = calculateFrameSize(sampleSizeInBits, channelCount);
227 }
228
229 if (TDebug.TraceAudioFileReader) {
230 TDebug.out("WaveAudioFileReader.readFormatChunk():");
231 TDebug.out(" read values: wFormatTag="+formatCode
232 +", nChannels="+channelCount
233 +", nSamplesPerSec="+sampleRate
234 +", nAvgBytesPerSec="+avgBytesPerSecond
235 +", nBlockAlign=="+blockAlign
236 +debugAdd);
237 TDebug.out(" constructed values: "
238 +"encoding="+encoding
239 +", sampleRate="+((float) sampleRate)
240 +", sampleSizeInBits="+sampleSizeInBits
241 +", channels="+channelCount
242 +", frameSize="+frameSize
243 +", frameRate="+frameRate);
244 }
245
246 // go to next chunk
247 advanceChunk(dis, chunkLength, read);
248 return new AudioFormat(
249 encoding,
250 (float) sampleRate,
251 sampleSizeInBits,
252 channelCount,
253 frameSize,
254 frameRate,
255 false);
256 }
257
258
259
260 protected AudioFileFormat getAudioFileFormat(InputStream inputStream, long lFileLengthInBytes)
261 throws UnsupportedAudioFileException, IOException {
262 DataInputStream dataInputStream = new DataInputStream(inputStream);
263 int magic = dataInputStream.readInt();
264 if (magic != WaveTool.WAVE_RIFF_MAGIC) {
265 throw new UnsupportedAudioFileException(
266 "not a WAVE file: wrong header magic");
267 }
268 long totalLength = readLittleEndianInt(dataInputStream) & 0xFFFFFFFF; // unsigned
269 magic = dataInputStream.readInt();
270 if (magic != WaveTool.WAVE_WAVE_MAGIC) {
271 throw new UnsupportedAudioFileException("not a WAVE file: wrong header magic");
272 }
273 // search for "fmt " chunk
274 long chunkLength = findChunk(dataInputStream, WaveTool.WAVE_FMT_MAGIC);
275 AudioFormat format = readFormatChunk(dataInputStream, chunkLength);
276
277 // search for "data" chunk
278 long dataChunkLength = findChunk(dataInputStream, WaveTool.WAVE_DATA_MAGIC);
279
280 long frameLength = dataChunkLength / format.getFrameSize();
281 if (format.getEncoding().equals(WaveTool.GSM0610)) {
282 // TODO: should not be necessary
283 frameLength = dataChunkLength;
284 }
285
286 if (TDebug.TraceAudioFileReader) {
287 TDebug.out("WaveAudioFileReader.getAudioFileFormat(): total length: "
288 +totalLength+", frame length = "+frameLength);
289 }
290 return new TAudioFileFormat(AudioFileFormat.Type.WAVE,
291 format,
292 (int) frameLength,
293 (int) (totalLength + WaveTool.CHUNK_HEADER_SIZE));
294 }
295}
296
297
298
299/*** WaveAudioFileReader.java ***/
300