Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epoxy
GitHub Repository: epoxy/proj11
Path: blob/master/SLICK_HOME/src/org/newdawn/slick/openal/OggInputStream.java
1461 views
1
package org.newdawn.slick.openal;
2
3
import java.io.IOException;
4
import java.io.InputStream;
5
import java.nio.ByteBuffer;
6
import java.nio.ByteOrder;
7
8
import org.lwjgl.BufferUtils;
9
import org.newdawn.slick.util.Log;
10
11
import com.jcraft.jogg.Packet;
12
import com.jcraft.jogg.Page;
13
import com.jcraft.jogg.StreamState;
14
import com.jcraft.jogg.SyncState;
15
import com.jcraft.jorbis.Block;
16
import com.jcraft.jorbis.Comment;
17
import com.jcraft.jorbis.DspState;
18
import com.jcraft.jorbis.Info;
19
20
/**
21
* An input stream that can extract ogg data. This class is a bit of an experiment with continuations
22
* so uses thread where possibly not required. It's just a test to see if continuations make sense in
23
* some cases.
24
*
25
* @author kevin
26
*/
27
public class OggInputStream extends InputStream implements AudioInputStream {
28
/** The conversion buffer size */
29
private int convsize = 4096 * 4;
30
/** The buffer used to read OGG file */
31
private byte[] convbuffer = new byte[convsize]; // take 8k out of the data segment, not the stack
32
/** The stream we're reading the OGG file from */
33
private InputStream input;
34
/** The audio information from the OGG header */
35
private Info oggInfo = new Info(); // struct that stores all the static vorbis bitstream settings
36
/** True if we're at the end of the available data */
37
private boolean endOfStream;
38
39
/** The Vorbis SyncState used to decode the OGG */
40
private SyncState syncState = new SyncState(); // sync and verify incoming physical bitstream
41
/** The Vorbis Stream State used to decode the OGG */
42
private StreamState streamState = new StreamState(); // take physical pages, weld into a logical stream of packets
43
/** The current OGG page */
44
private Page page = new Page(); // one Ogg bitstream page. Vorbis packets are inside
45
/** The current packet page */
46
private Packet packet = new Packet(); // one raw packet of data for decode
47
48
/** The comment read from the OGG file */
49
private Comment comment = new Comment(); // struct that stores all the bitstream user comments
50
/** The Vorbis DSP stat eused to decode the OGG */
51
private DspState dspState = new DspState(); // central working state for the packet->PCM decoder
52
/** The OGG block we're currently working with to convert PCM */
53
private Block vorbisBlock = new Block(dspState); // local working space for packet->PCM decode
54
55
/** Temporary scratch buffer */
56
byte[] buffer;
57
/** The number of bytes read */
58
int bytes = 0;
59
/** The true if we should be reading big endian */
60
boolean bigEndian = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN);
61
/** True if we're reached the end of the current bit stream */
62
boolean endOfBitStream = true;
63
/** True if we're initialise the OGG info block */
64
boolean inited = false;
65
66
/** The index into the byte array we currently read from */
67
private int readIndex;
68
/** The byte array store used to hold the data read from the ogg */
69
private ByteBuffer pcmBuffer = BufferUtils.createByteBuffer(4096 * 500);
70
/** The total number of bytes */
71
private int total;
72
73
/**
74
* Create a new stream to decode OGG data
75
*
76
* @param input The input stream from which to read the OGG file
77
* @throws IOException Indicates a failure to read from the supplied stream
78
*/
79
public OggInputStream(InputStream input) throws IOException {
80
this.input = input;
81
total = input.available();
82
83
init();
84
}
85
86
/**
87
* Get the number of bytes on the stream
88
*
89
* @return The number of the bytes on the stream
90
*/
91
public int getLength() {
92
return total;
93
}
94
95
/**
96
* @see org.newdawn.slick.openal.AudioInputStream#getChannels()
97
*/
98
public int getChannels() {
99
return oggInfo.channels;
100
}
101
102
/**
103
* @see org.newdawn.slick.openal.AudioInputStream#getRate()
104
*/
105
public int getRate() {
106
return oggInfo.rate;
107
}
108
109
/**
110
* Initialise the streams and thread involved in the streaming of OGG data
111
*
112
* @throws IOException Indicates a failure to link up the streams
113
*/
114
private void init() throws IOException {
115
initVorbis();
116
readPCM();
117
}
118
119
/**
120
* @see java.io.InputStream#available()
121
*/
122
public int available() {
123
return endOfStream ? 0 : 1;
124
}
125
126
/**
127
* Initialise the vorbis decoding
128
*/
129
private void initVorbis() {
130
syncState.init();
131
}
132
133
/**
134
* Get a page and packet from that page
135
*
136
* @return True if there was a page available
137
*/
138
private boolean getPageAndPacket() {
139
// grab some data at the head of the stream. We want the first page
140
// (which is guaranteed to be small and only contain the Vorbis
141
// stream initial header) We need the first page to get the stream
142
// serialno.
143
144
// submit a 4k block to libvorbis' Ogg layer
145
int index = syncState.buffer(4096);
146
147
buffer = syncState.data;
148
if (buffer == null) {
149
endOfStream = true;
150
return false;
151
}
152
153
try {
154
bytes = input.read(buffer, index, 4096);
155
} catch (Exception e) {
156
Log.error("Failure reading in vorbis");
157
Log.error(e);
158
endOfStream = true;
159
return false;
160
}
161
syncState.wrote(bytes);
162
163
// Get the first page.
164
if (syncState.pageout(page) != 1) {
165
// have we simply run out of data? If so, we're done.
166
if (bytes < 4096)
167
return false;
168
169
// error case. Must not be Vorbis data
170
Log.error("Input does not appear to be an Ogg bitstream.");
171
endOfStream = true;
172
return false;
173
}
174
175
// Get the serial number and set up the rest of decode.
176
// serialno first; use it to set up a logical stream
177
streamState.init(page.serialno());
178
179
// extract the initial header from the first page and verify that the
180
// Ogg bitstream is in fact Vorbis data
181
182
// I handle the initial header first instead of just having the code
183
// read all three Vorbis headers at once because reading the initial
184
// header is an easy way to identify a Vorbis bitstream and it's
185
// useful to see that functionality seperated out.
186
187
oggInfo.init();
188
comment.init();
189
if (streamState.pagein(page) < 0) {
190
// error; stream version mismatch perhaps
191
Log.error("Error reading first page of Ogg bitstream data.");
192
endOfStream = true;
193
return false;
194
}
195
196
if (streamState.packetout(packet) != 1) {
197
// no page? must not be vorbis
198
Log.error("Error reading initial header packet.");
199
endOfStream = true;
200
return false;
201
}
202
203
if (oggInfo.synthesis_headerin(comment, packet) < 0) {
204
// error case; not a vorbis header
205
Log.error("This Ogg bitstream does not contain Vorbis audio data.");
206
endOfStream = true;
207
return false;
208
}
209
210
// At this point, we're sure we're Vorbis. We've set up the logical
211
// (Ogg) bitstream decoder. Get the comment and codebook headers and
212
// set up the Vorbis decoder
213
214
// The next two packets in order are the comment and codebook headers.
215
// They're likely large and may span multiple pages. Thus we reead
216
// and submit data until we get our two pacakets, watching that no
217
// pages are missing. If a page is missing, error out; losing a
218
// header page is the only place where missing data is fatal. */
219
220
int i = 0;
221
while (i < 2) {
222
while (i < 2) {
223
224
int result = syncState.pageout(page);
225
if (result == 0)
226
break; // Need more data
227
// Don't complain about missing or corrupt data yet. We'll
228
// catch it at the packet output phase
229
230
if (result == 1) {
231
streamState.pagein(page); // we can ignore any errors here
232
// as they'll also become apparent
233
// at packetout
234
while (i < 2) {
235
result = streamState.packetout(packet);
236
if (result == 0)
237
break;
238
if (result == -1) {
239
// Uh oh; data at some point was corrupted or missing!
240
// We can't tolerate that in a header. Die.
241
Log.error("Corrupt secondary header. Exiting.");
242
endOfStream = true;
243
return false;
244
}
245
246
oggInfo.synthesis_headerin(comment, packet);
247
i++;
248
}
249
}
250
}
251
// no harm in not checking before adding more
252
index = syncState.buffer(4096);
253
buffer = syncState.data;
254
try {
255
bytes = input.read(buffer, index, 4096);
256
} catch (Exception e) {
257
Log.error("Failed to read Vorbis: ");
258
Log.error(e);
259
endOfStream = true;
260
return false;
261
}
262
if (bytes == 0 && i < 2) {
263
Log.error("End of file before finding all Vorbis headers!");
264
endOfStream = true;
265
return false;
266
}
267
syncState.wrote(bytes);
268
}
269
270
convsize = 4096 / oggInfo.channels;
271
272
// OK, got and parsed all three headers. Initialize the Vorbis
273
// packet->PCM decoder.
274
dspState.synthesis_init(oggInfo); // central decode state
275
vorbisBlock.init(dspState); // local state for most of the decode
276
// so multiple block decodes can
277
// proceed in parallel. We could init
278
// multiple vorbis_block structures
279
// for vd here
280
281
return true;
282
}
283
284
/**
285
* Decode the OGG file as shown in the jogg/jorbis examples
286
*
287
* @throws IOException Indicates a failure to read from the supplied stream
288
*/
289
private void readPCM() throws IOException {
290
boolean wrote = false;
291
292
while (true) { // we repeat if the bitstream is chained
293
if (endOfBitStream) {
294
if (!getPageAndPacket()) {
295
break;
296
}
297
endOfBitStream = false;
298
}
299
300
if (!inited) {
301
inited = true;
302
return;
303
}
304
305
float[][][] _pcm = new float[1][][];
306
int[] _index = new int[oggInfo.channels];
307
// The rest is just a straight decode loop until end of stream
308
while (!endOfBitStream) {
309
while (!endOfBitStream) {
310
int result = syncState.pageout(page);
311
312
if (result == 0) {
313
break; // need more data
314
}
315
316
if (result == -1) { // missing or corrupt data at this page position
317
Log.error("Corrupt or missing data in bitstream; continuing...");
318
} else {
319
streamState.pagein(page); // can safely ignore errors at
320
// this point
321
while (true) {
322
result = streamState.packetout(packet);
323
324
if (result == 0)
325
break; // need more data
326
if (result == -1) { // missing or corrupt data at this page position
327
// no reason to complain; already complained above
328
} else {
329
// we have a packet. Decode it
330
int samples;
331
if (vorbisBlock.synthesis(packet) == 0) { // test for success!
332
dspState.synthesis_blockin(vorbisBlock);
333
}
334
335
// **pcm is a multichannel float vector. In stereo, for
336
// example, pcm[0] is left, and pcm[1] is right. samples is
337
// the size of each channel. Convert the float values
338
// (-1.<=range<=1.) to whatever PCM format and write it out
339
340
while ((samples = dspState.synthesis_pcmout(_pcm,
341
_index)) > 0) {
342
float[][] pcm = _pcm[0];
343
//boolean clipflag = false;
344
int bout = (samples < convsize ? samples
345
: convsize);
346
347
// convert floats to 16 bit signed ints (host order) and
348
// interleave
349
for (int i = 0; i < oggInfo.channels; i++) {
350
int ptr = i * 2;
351
//int ptr=i;
352
int mono = _index[i];
353
for (int j = 0; j < bout; j++) {
354
int val = (int) (pcm[i][mono + j] * 32767.);
355
// might as well guard against clipping
356
if (val > 32767) {
357
val = 32767;
358
}
359
if (val < -32768) {
360
val = -32768;
361
}
362
if (val < 0)
363
val = val | 0x8000;
364
365
if (bigEndian) {
366
convbuffer[ptr] = (byte) (val >>> 8);
367
convbuffer[ptr + 1] = (byte) (val);
368
} else {
369
convbuffer[ptr] = (byte) (val);
370
convbuffer[ptr + 1] = (byte) (val >>> 8);
371
}
372
ptr += 2 * (oggInfo.channels);
373
}
374
}
375
376
int bytesToWrite = 2 * oggInfo.channels * bout;
377
if (bytesToWrite >= pcmBuffer.remaining()) {
378
Log.warn("Read block from OGG that was too big to be buffered: " + bytesToWrite);
379
} else {
380
pcmBuffer.put(convbuffer, 0, bytesToWrite);
381
}
382
383
wrote = true;
384
dspState.synthesis_read(bout); // tell libvorbis how
385
// many samples we
386
// actually consumed
387
}
388
}
389
}
390
if (page.eos() != 0) {
391
endOfBitStream = true;
392
}
393
394
if ((!endOfBitStream) && (wrote)) {
395
return;
396
}
397
}
398
}
399
400
if (!endOfBitStream) {
401
bytes = 0;
402
int index = syncState.buffer(4096);
403
if (index >= 0) {
404
buffer = syncState.data;
405
try {
406
bytes = input.read(buffer, index, 4096);
407
} catch (Exception e) {
408
Log.error("Failure during vorbis decoding");
409
Log.error(e);
410
endOfStream = true;
411
return;
412
}
413
} else {
414
bytes = 0;
415
}
416
syncState.wrote(bytes);
417
if (bytes == 0) {
418
endOfBitStream = true;
419
}
420
}
421
}
422
423
// clean up this logical bitstream; before exit we see if we're
424
// followed by another [chained]
425
streamState.clear();
426
427
// ogg_page and ogg_packet structs always point to storage in
428
// libvorbis. They're never freed or manipulated directly
429
430
vorbisBlock.clear();
431
dspState.clear();
432
oggInfo.clear(); // must be called last
433
}
434
435
// OK, clean up the framer
436
syncState.clear();
437
endOfStream = true;
438
}
439
440
/**
441
* @see java.io.InputStream#read()
442
*/
443
public int read() throws IOException {
444
if (readIndex >= pcmBuffer.position()) {
445
pcmBuffer.clear();
446
readPCM();
447
readIndex = 0;
448
}
449
if (readIndex >= pcmBuffer.position()) {
450
return -1;
451
}
452
453
int value = pcmBuffer.get(readIndex);
454
if (value < 0) {
455
value = 256 + value;
456
}
457
readIndex++;
458
459
return value;
460
}
461
462
/**
463
* @see org.newdawn.slick.openal.AudioInputStream#atEnd()
464
*/
465
public boolean atEnd() {
466
return endOfStream && (readIndex >= pcmBuffer.position());
467
}
468
469
/**
470
* @see java.io.InputStream#read(byte[], int, int)
471
*/
472
public int read(byte[] b, int off, int len) throws IOException {
473
for (int i=0;i<len;i++) {
474
try {
475
int value = read();
476
if (value >= 0) {
477
b[i] = (byte) value;
478
} else {
479
if (i == 0) {
480
return -1;
481
} else {
482
return i;
483
}
484
}
485
} catch (IOException e) {
486
Log.error(e);
487
return i;
488
}
489
}
490
491
return len;
492
}
493
494
/**
495
* @see java.io.InputStream#read(byte[])
496
*/
497
public int read(byte[] b) throws IOException {
498
return read(b, 0, b.length);
499
}
500
501
/**
502
* @see java.io.InputStream#close()
503
*/
504
public void close() throws IOException {
505
}
506
}
507
508