summaryrefslogtreecommitdiff
path: root/songdbj/org/tritonus/file/pvorbis/VorbisAudioFileReader.java
diff options
context:
space:
mode:
Diffstat (limited to 'songdbj/org/tritonus/file/pvorbis/VorbisAudioFileReader.java')
-rw-r--r--songdbj/org/tritonus/file/pvorbis/VorbisAudioFileReader.java302
1 files changed, 302 insertions, 0 deletions
diff --git a/songdbj/org/tritonus/file/pvorbis/VorbisAudioFileReader.java b/songdbj/org/tritonus/file/pvorbis/VorbisAudioFileReader.java
new file mode 100644
index 0000000000..a882f11c3a
--- /dev/null
+++ b/songdbj/org/tritonus/file/pvorbis/VorbisAudioFileReader.java
@@ -0,0 +1,302 @@
1/*
2 * VorbisAudioFileReader.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2001 - 2004 by Matthias Pfisterer
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Library General Public License as published
12 * by the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public
21 * License along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25/*
26|<--- this code is formatted to fit into 80 columns --->|
27*/
28
29package org.tritonus.sampled.file.pvorbis;
30
31import java.io.InputStream;
32import java.io.IOException;
33
34import javax.sound.sampled.AudioSystem;
35import javax.sound.sampled.AudioFormat;
36import javax.sound.sampled.AudioFileFormat;
37import javax.sound.sampled.UnsupportedAudioFileException;
38
39import org.tritonus.share.TDebug;
40import org.tritonus.share.sampled.file.TAudioFileFormat;
41import org.tritonus.share.sampled.file.TAudioFileReader;
42
43import org.tritonus.lowlevel.pogg.Buffer;
44import org.tritonus.lowlevel.pogg.Page;
45import org.tritonus.lowlevel.pogg.Packet;
46import org.tritonus.lowlevel.pogg.SyncState;
47import org.tritonus.lowlevel.pogg.StreamState;
48
49
50
51/**
52 * @author Matthias Pfisterer
53 */
54public class VorbisAudioFileReader
55extends TAudioFileReader
56{
57 private static final int INITAL_READ_LENGTH = 4096;
58 private static final int MARK_LIMIT = INITAL_READ_LENGTH + 1;
59
60
61
62 public VorbisAudioFileReader()
63 {
64 super(MARK_LIMIT, true);
65 }
66
67
68
69 protected AudioFileFormat getAudioFileFormat(InputStream inputStream,
70 long lFileSizeInBytes)
71 throws UnsupportedAudioFileException, IOException
72 {
73 if (TDebug.TraceAudioFileReader) { TDebug.out(">VorbisAudioFileReader.getAudioFileFormat(): begin"); }
74 SyncState oggSyncState = new SyncState();
75 StreamState oggStreamState = new StreamState();
76 Page oggPage = new Page();
77 Packet oggPacket = new Packet();
78
79 int bytes = 0;
80
81 // Decode setup
82
83 oggSyncState.init(); // Now we can read pages
84
85 // grab some data at the head of the stream. We want the first page
86 // (which is guaranteed to be small and only contain the Vorbis
87 // stream initial header) We need the first page to get the stream
88 // serialno.
89
90 // submit a 4k block to libvorbis' Ogg layer
91 byte[] abBuffer = new byte[INITAL_READ_LENGTH];
92 bytes = inputStream.read(abBuffer);
93 if (TDebug.TraceAudioFileReader) { TDebug.out("read bytes from input stream: " + bytes); }
94 int nResult = oggSyncState.write(abBuffer, bytes);
95 if (TDebug.TraceAudioFileReader) { TDebug.out("SyncState.write() returned " + nResult); }
96
97 // Get the first page.
98 if (oggSyncState.pageOut(oggPage) != 1)
99 {
100 // have we simply run out of data? If so, we're done.
101 if (bytes < INITAL_READ_LENGTH)
102 {
103 if (TDebug.TraceAudioFileReader) { TDebug.out("stream ended prematurely"); }
104 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
105 // IDEA: throw EOFException?
106 oggSyncState.free();
107 oggStreamState.free();
108 oggPage.free();
109 oggPacket.free();
110 throw new UnsupportedAudioFileException("not a Vorbis stream: ended prematurely");
111 }
112 if (TDebug.TraceAudioFileReader) { TDebug.out("not in Ogg bitstream format"); }
113 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
114 oggSyncState.free();
115 oggStreamState.free();
116 oggPage.free();
117 oggPacket.free();
118 throw new UnsupportedAudioFileException("not a Vorbis stream: not in Ogg bitstream format");
119 }
120
121 // Get the serial number and set up the rest of decode.
122 // serialno first; use it to set up a logical stream
123 int nSerialNo = oggPage.getSerialNo();
124 if (TDebug.TraceAudioFileReader) TDebug.out("serial no.: " + nSerialNo);
125 oggStreamState.init(nSerialNo);
126
127 // extract the initial header from the first page and verify that the
128 // Ogg bitstream is in fact Vorbis data
129
130 // I handle the initial header first instead of just having the code
131 // read all three Vorbis headers at once because reading the initial
132 // header is an easy way to identify a Vorbis bitstream and it's
133 // useful to see that functionality seperated out.
134
135 if (oggStreamState.pageIn(oggPage) < 0)
136 {
137 if (TDebug.TraceAudioFileReader) { TDebug.out("can't read first page of Ogg bitstream data"); }
138 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
139 // error; stream version mismatch perhaps
140 oggSyncState.free();
141 oggStreamState.free();
142 oggPage.free();
143 oggPacket.free();
144 throw new UnsupportedAudioFileException("not a Vorbis stream: can't read first page of Ogg bitstream data");
145 }
146
147 if (oggStreamState.packetOut(oggPacket) != 1)
148 {
149 if (TDebug.TraceAudioFileReader) { TDebug.out("can't read initial header packet"); }
150 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
151 // no page? must not be vorbis
152 oggSyncState.free();
153 oggStreamState.free();
154 oggPage.free();
155 oggPacket.free();
156 throw new UnsupportedAudioFileException("not a Vorbis stream: can't read initial header packet");
157 }
158
159 byte[] abData = oggPacket.getData();
160 if (TDebug.TraceAudioFileReader)
161 {
162 String strData = "";
163 for (int i = 0; i < abData.length; i++)
164 {
165 strData += " " + abData[i];
166 }
167 TDebug.out("packet data: " + strData);
168 }
169
170 int nPacketType = abData[0];
171 if (TDebug.TraceAudioFileReader) { TDebug.out("packet type: " + nPacketType); }
172 if (nPacketType != 1)
173 {
174 if (TDebug.TraceAudioFileReader) { TDebug.out("first packet is not the identification header"); }
175 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
176 oggSyncState.free();
177 oggStreamState.free();
178 oggPage.free();
179 oggPacket.free();
180 throw new UnsupportedAudioFileException("not a Vorbis stream: first packet is not the identification header");
181 }
182 if(abData[1] != 'v' ||
183 abData[2] != 'o' ||
184 abData[3] != 'r' ||
185 abData[4] != 'b' ||
186 abData[5] != 'i' ||
187 abData[6] != 's')
188 {
189 if (TDebug.TraceAudioFileReader) { TDebug.out("not a vorbis header packet"); }
190 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
191 oggSyncState.free();
192 oggStreamState.free();
193 oggPage.free();
194 oggPacket.free();
195 throw new UnsupportedAudioFileException("not a Vorbis stream: not a vorbis header packet");
196 }
197 if (! oggPacket.isBos())
198 {
199 if (TDebug.TraceAudioFileReader) { TDebug.out("initial packet not marked as beginning of stream"); }
200 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
201 oggSyncState.free();
202 oggStreamState.free();
203 oggPage.free();
204 oggPacket.free();
205 throw new UnsupportedAudioFileException("not a Vorbis stream: initial packet not marked as beginning of stream");
206 }
207 int nVersion = (abData[7] & 0xFF) + 256 * (abData[8] & 0xFF) + 65536 * (abData[9] & 0xFF) + 16777216 * (abData[10] & 0xFF);
208 if (TDebug.TraceAudioFileReader) { TDebug.out("version: " + nVersion); }
209 if (nVersion != 0)
210 {
211 if (TDebug.TraceAudioFileReader) { TDebug.out("wrong vorbis version"); }
212 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
213 oggSyncState.free();
214 oggStreamState.free();
215 oggPage.free();
216 oggPacket.free();
217 throw new UnsupportedAudioFileException("not a Vorbis stream: wrong vorbis version");
218 }
219 int nChannels = (abData[11] & 0xFF);
220 float fSampleRate = (abData[12] & 0xFF) + 256 * (abData[13] & 0xFF) + 65536 * (abData[14] & 0xFF) + 16777216 * (abData[15] & 0xFF);
221 if (TDebug.TraceAudioFileReader) { TDebug.out("channels: " + nChannels); }
222 if (TDebug.TraceAudioFileReader) { TDebug.out("rate: " + fSampleRate); }
223
224 // These are only used for error checking.
225 int bitrate_upper = abData[16] + 256 * abData[17] + 65536 * abData[18] + 16777216 * abData[19];
226 int bitrate_nominal = abData[20] + 256 * abData[21] + 65536 * abData[22] + 16777216 * abData[23];
227 int bitrate_lower = abData[24] + 256 * abData[25] + 65536 * abData[26] + 16777216 * abData[27];
228
229 int[] blocksizes = new int[2];
230 blocksizes[0] = 1 << (abData[28] & 0xF);
231 blocksizes[1] = 1 << ((abData[28] >>> 4) & 0xF);
232 if (TDebug.TraceAudioFileReader) { TDebug.out("blocksizes[0]: " + blocksizes[0]); }
233 if (TDebug.TraceAudioFileReader) { TDebug.out("blocksizes[1]: " + blocksizes[1]); }
234
235 if (fSampleRate < 1.0F ||
236 nChannels < 1 ||
237 blocksizes[0] < 8||
238 blocksizes[1] < blocksizes[0] ||
239 (abData[29] & 0x1) != 1)
240 {
241 if (TDebug.TraceAudioFileReader) { TDebug.out("illegal values in initial header"); }
242 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
243 oggSyncState.free();
244 oggStreamState.free();
245 oggPage.free();
246 oggPacket.free();
247 throw new UnsupportedAudioFileException("not a Vorbis stream: illegal values in initial header");
248 }
249
250 oggSyncState.free();
251 oggStreamState.free();
252 oggPage.free();
253 oggPacket.free();
254
255 /*
256 If the file size is known, we derive the number of frames
257 ('frame size') from it.
258 If the values don't fit into integers, we leave them at
259 NOT_SPECIFIED. 'Unknown' is considered less incorrect than
260 a wrong value.
261 */
262 // [fb] not specifying it causes Sun's Wave file writer to write rubbish
263 int nByteSize = AudioSystem.NOT_SPECIFIED;
264 if (lFileSizeInBytes != AudioSystem.NOT_SPECIFIED
265 && lFileSizeInBytes <= Integer.MAX_VALUE)
266 {
267 nByteSize = (int) lFileSizeInBytes;
268 }
269 int nFrameSize = AudioSystem.NOT_SPECIFIED;
270 /* Can we calculate a useful size?
271 Peeking into ogginfo gives the insight that the only
272 way seems to be reading through the file. This is
273 something we do not want, at least not by default.
274 */
275 // nFrameSize = (int) (lFileSizeInBytes / ...;
276
277 AudioFormat format = new AudioFormat(
278 new AudioFormat.Encoding("VORBIS"),
279 fSampleRate,
280 AudioSystem.NOT_SPECIFIED,
281 nChannels,
282 AudioSystem.NOT_SPECIFIED,
283 AudioSystem.NOT_SPECIFIED,
284 true); // this value is chosen arbitrarily
285 if (TDebug.TraceAudioFileReader) { TDebug.out("AudioFormat: " + format); }
286 AudioFileFormat.Type type = new AudioFileFormat.Type("Ogg","ogg");
287 AudioFileFormat audioFileFormat =
288 new TAudioFileFormat(
289 type,
290 format,
291 nFrameSize,
292 nByteSize);
293 if (TDebug.TraceAudioFileReader) { TDebug.out("AudioFileFormat: " + audioFileFormat); }
294 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): end"); }
295 return audioFileFormat;
296 }
297}
298
299
300
301/*** VorbisAudioFileReader.java ***/
302