OSDN Git Service

Set optimal mime types and executable settings.
[mikumikustudio/MikuMikuStudio.git] / src / com / jmex / audio / stream / OggInputStream.java
1 /*
2  * Copyright (c) 2003-2009 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 
17  *   may be used to endorse or promote products derived from this software 
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 package com.jmex.audio.stream;
34
35 import java.io.IOException;
36 import java.net.URL;
37 import java.nio.ByteBuffer;
38 import java.nio.ByteOrder;
39 import java.util.Iterator;
40 import java.util.logging.Level;
41 import java.util.logging.Logger;
42
43 import com.jcraft.jogg.Packet;
44 import com.jcraft.jogg.Page;
45 import com.jcraft.jogg.StreamState;
46 import com.jcraft.jogg.SyncState;
47 import com.jcraft.jorbis.Block;
48 import com.jcraft.jorbis.Comment;
49 import com.jcraft.jorbis.DspState;
50 import com.jcraft.jorbis.Info;
51 import com.jmex.audio.filter.Filter;
52
53 /**
54  * Decompresses an Ogg file as it streams from a source.
55  *
56  * @author Arman Ozcelik
57  * @author Joshua Slack
58  * @version $Id: OggInputStream.java,v 1.4 2007/08/20 10:28:29 rherlitz Exp $
59  */
60 public class OggInputStream extends AudioInputStream {
61     private static final Logger logger = Logger.getLogger(OggInputStream.class
62             .getName());
63
64     // temp vars
65     private float[][][] _pcm = new float[1][][];
66     private int[] _index;
67
68     // end of stream
69     private boolean eos = false;
70
71     // sync and verify incoming physical bitstream
72     private SyncState syncState = new SyncState(); 
73
74     // take physical pages, weld into a logical stream of packets
75     private StreamState streamState = new StreamState(); 
76
77     // one Ogg bitstream page.  Vorbis packets are inside
78     private Page page = new Page(); 
79
80     // one raw packet of data for decode
81     private Packet packet = new Packet(); 
82
83     // struct that stores all the static vorbis bitstream settings
84     private Info info = new Info(); 
85
86     // struct that stores all the bitstream user comments
87     private Comment comment = new Comment(); 
88
89     // central working state for the packet->PCM decoder
90     private DspState dspState = new DspState(); 
91
92     // local working space for packet->PCM decode
93     private Block block = new Block(dspState); 
94
95     /// Conversion buffer size
96     private int convsize = 4096 * 6;
97     
98     // Conversion buffer
99     private byte[] convbuffer = new byte[convsize];
100     
101     // where we are in the convbuffer
102     private int convbufferOff = 0;
103
104     // bytes ready in convbuffer.
105     private int convbufferSize = 0;
106
107     // a dummy used by read() to read 1 byte.
108     private byte readDummy[] = new byte[1];
109     
110
111     /**
112      * Creates an OggInputStream that decompressed the specified ogg file.
113      * @throws IOException 
114      */
115     public OggInputStream(URL resource, float length) throws IOException {
116         super(resource, length);
117         try {
118             initVorbis();
119             _index = new int[info.channels];
120         } catch (Exception e) {
121             logger.logp(Level.SEVERE, this.getClass().toString(),
122                     "OggInputStream(URL resource, float lengt)", "Exception", e);
123             eos = true;
124         }
125     }
126
127     @Override
128     public int getBitRate() {
129         return info.rate;
130     }
131
132     @Override
133     public int getDepth() {
134         return 16;
135     }
136
137
138     /**
139      * Reads the next byte of data from this input stream. The value byte is
140      * returned as an int in the range 0 to 255. If no byte is available because
141      * the end of the stream has been reached, the value -1 is returned. This
142      * method blocks until input data is available, the end of the stream is
143      * detected, or an exception is thrown. 
144      * @return the next byte of data, or -1 if the end of the stream is reached.
145      */
146     public int read() throws IOException {
147         int retVal = read(readDummy, 0, 1);
148         return (retVal == -1 ? -1 : readDummy[0]);
149     }
150
151     
152     /**
153      * Reads up to len bytes of data from the input stream into an array of bytes.
154      * @param b the buffer into which the data is read.
155      * @param off the start offset of the data.
156      * @param len the maximum number of bytes read.
157      * @return the total number of bytes read into the buffer, or -1 if there is
158      *         no more data because the end of the stream has been reached. 
159      */
160     public int read(byte b[], int off, int len) throws IOException {
161         if (eos) {
162             return -1;
163         }
164
165         int bytesRead = 0;
166         while (!eos && (len > 0)) {
167             fillConvbuffer();
168             
169             if (!eos) {
170                 int bytesToCopy = Math.min(len, convbufferSize-convbufferOff);
171                 System.arraycopy(convbuffer, convbufferOff, b, off, bytesToCopy);
172                 convbufferOff += bytesToCopy;
173                 bytesRead += bytesToCopy;
174                 len -= bytesToCopy;
175                 off += bytesToCopy;
176             }
177         }
178
179         return bytesRead;
180     }
181
182     
183     /**
184      * Reads up to len bytes of data from the input stream into a ByteBuffer.
185      * @param b the buffer into which the data is read.
186      * @param off the start offset of the data.
187      * @param len the maximum number of bytes read.
188      * @return the total number of bytes read into the buffer, or -1 if there is
189      *         no more data because the end of the stream has been reached. 
190      */
191     public int read(ByteBuffer b, int off, int len) throws IOException {
192         byte[] buffer = new byte[b.capacity()];
193         int bytesRead = read(buffer, off, len);
194         if (bytesRead > 0 && filters.size() > 0) {
195             Iterator<Filter> it = filters.iterator();
196             while (it.hasNext()) {
197                 buffer = it.next().filter(buffer);
198             }
199         }
200         b.put(buffer);
201         b.position(off);
202         return bytesRead;
203     }
204
205
206     /**
207      * Helper function. Decodes a packet to the convbuffer if it is empty. 
208      * Updates convbufferSize, convbufferOff, and eos.
209      */
210     private void fillConvbuffer() throws IOException {
211         if (convbufferOff >= convbufferSize) {
212             convbufferSize = lazyDecodePacket();
213             convbufferOff = 0;
214             if (convbufferSize == -1) {
215                 eos = true;
216             }
217         }
218     }
219
220
221     /**
222      * Returns 0 after EOF is reached, otherwise always return 1.
223      * <p>
224      * Programs should not count on this method to return the actual number of
225      * bytes that could be read without blocking.
226      * @return 1 before EOF and 0 after EOF is reached. 
227      */
228     public int available() throws IOException {
229         return (eos ? 0 : 1);
230     }
231
232
233     /**
234      * OggInputStream does not support mark and reset. This function does nothing.
235      */
236     public void reset() throws IOException {
237     }
238
239
240     /**
241      * OggInputStream does not support mark and reset.
242      * @return false.
243      */
244     public boolean markSupported() {
245         return false;
246     }
247
248
249     /**
250      * Skips over and discards n bytes of data from the input stream. The skip
251      * method may, for a variety of reasons, end up skipping over some smaller
252      * number of bytes, possibly 0. The actual number of bytes skipped is returned. 
253      * @param n the number of bytes to be skipped. 
254      * @return the actual number of bytes skipped.
255      */
256     public long skip(long n) throws IOException {
257         int bytesRead = 0;
258         while (bytesRead < n) {
259             int res = read();
260             if (res == -1) {
261                 break;
262             }
263
264             bytesRead++;
265         }
266         
267         return bytesRead;
268     }
269     
270     
271     /**
272      * Initalizes the vorbis stream. Reads the stream until info and comment are read.
273      */
274     private void initVorbis() throws Exception {
275         // Now we can read pages
276         syncState.init(); 
277
278         // grab some data at the head of the stream.  We want the first page
279         // (which is guaranteed to be small and only contain the Vorbis
280         // stream initial header) We need the first page to get the stream
281         // serialno.
282
283         // submit a 4k block to libvorbis' Ogg layer
284         int index = syncState.buffer(4096);
285         byte buffer[] = syncState.data;
286         int bytes = in.read(buffer, index, 4096);
287         syncState.wrote(bytes);
288
289         // Get the first page.
290         if (syncState.pageout(page) != 1) {
291             // have we simply run out of data?  If so, we're done.
292             if (bytes < 4096)
293                 return;//break;
294
295             // error case.  Must not be Vorbis data
296             throw new Exception("Input does not appear to be an Ogg bitstream.");
297         }
298
299         // Get the serial number and set up the rest of decode.
300         // serialno first; use it to set up a logical stream
301         streamState.init(page.serialno());
302
303         // extract the initial header from the first page and verify that the
304         // Ogg bitstream is in fact Vorbis data
305
306         // I handle the initial header first instead of just having the code
307         // read all three Vorbis headers at once because reading the initial
308         // header is an easy way to identify a Vorbis bitstream and it's
309         // useful to see that functionality seperated out.
310
311         info.init();
312         comment.init();
313         if (streamState.pagein(page) < 0) {
314             // error; stream version mismatch perhaps
315             throw new Exception("Error reading first page of Ogg bitstream data.");
316         }
317
318         if (streamState.packetout(packet) != 1) {
319             // no page? must not be vorbis
320             throw new Exception("Error reading initial header packet.");
321         }
322
323         if (info.synthesis_headerin(comment, packet) < 0) {
324             // error case; not a vorbis header
325             throw new Exception("This Ogg bitstream does not contain Vorbis audio data.");
326         }
327
328         // At this point, we're sure we're Vorbis.  We've set up the logical
329         // (Ogg) bitstream decoder.  Get the comment and codebook headers and
330         // set up the Vorbis decoder
331
332         // The next two packets in order are the comment and codebook headers.
333         // They're likely large and may span multiple pages.  Thus we read
334         // and submit data until we get our two packets, watching that no
335         // pages are missing.  If a page is missing, error out; losing a
336         // header page is the only place where missing data is fatal. 
337
338         
339         int i = 0;
340         while (i < 2) {
341             while (i < 2) {
342
343                 int result = syncState.pageout(page);
344                 if (result == 0)
345                     break; // Need more data
346                 // Don't complain about missing or corrupt data yet.  We'll
347                 // catch it at the packet output phase
348
349                 if (result == 1) {
350                     streamState.pagein(page); // we can ignore any errors here
351                     // as they'll also become apparent
352                     // at packetout
353                     while (i < 2) {
354                         result = streamState.packetout(packet);
355                         if (result == 0) {
356                             break;
357                         }
358                         
359                         if (result == -1) {
360                             // Uh oh; data at some point was corrupted or missing!
361                             // We can't tolerate that in a header.  Die.
362                             throw new Exception("Corrupt secondary header. Exiting.");
363                         }
364
365                         info.synthesis_headerin(comment, packet);
366                         i++;
367                     }
368                 }
369             }
370
371             // no harm in not checking before adding more
372             index = syncState.buffer(4096);
373             buffer = syncState.data;
374             bytes = in.read(buffer, index, 4096);
375
376             // NOTE: This is a bugfix. read will return -1 which will mess up syncState.
377             if (bytes < 0 ) {
378                 bytes = 0;
379             }
380             
381             if (bytes == 0 && i < 2) {
382                 throw new Exception("End of file before finding all Vorbis headers!");
383             }
384
385             syncState.wrote(bytes);
386         }
387
388         convsize = 4096 / info.channels;
389
390         // OK, got and parsed all three headers. Initialize the Vorbis
391         //  packet->PCM decoder.
392         dspState.synthesis_init(info); // central decode state
393         block.init(dspState); // local state for most of the decode
394         // so multiple block decodes can
395         // proceed in parallel.  We could init
396         // multiple vorbis_block structures
397         // for vd here
398     }
399
400
401     /**
402      * Decodes a packet.
403      */
404     private int decodePacket(Packet packet) {
405         // check the endianes of the computer.
406         final boolean bigEndian = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
407         
408         if (block.synthesis(packet) == 0) { 
409             // test for success!
410             dspState.synthesis_blockin(block);
411         }
412
413         // **pcm is a multichannel float vector.  In stereo, for
414         // example, pcm[0] is left, and pcm[1] is right.  samples is
415         // the size of each channel.  Convert the float values
416         // (-1.<=range<=1.) to whatever PCM format and write it out
417         int convOff = 0;
418         int samples;
419         while ((samples = dspState.synthesis_pcmout(_pcm, _index)) > 0) {
420             //logger.info("while() 4");
421             float[][] pcm = _pcm[0];
422             int bout = (samples < convsize ? samples : convsize);
423
424             // convert floats to 16 bit signed ints (host order) and interleave
425             for (int i = 0; i < info.channels; i++) {
426                 int ptr = (i << 1) + convOff;
427
428
429                 int mono = _index[i];
430
431                 for (int j = 0; j < bout; j++) {
432                     int val = (int) (pcm[i][mono + j] * 32767.);
433
434                     // might as well guard against clipping
435                     val = Math.max(-32768, Math.min(32767, val));
436                     val |= (val < 0 ? 0x8000 : 0);
437                     
438                     convbuffer[ptr + 0] = (byte) (bigEndian ? val >>> 8 : val);
439                     convbuffer[ptr + 1] = (byte) (bigEndian ? val : val >>> 8);
440                     ptr += (info.channels << 1);
441                 }
442             }
443
444             convOff += 2 * info.channels * bout;
445
446             // Tell orbis how many samples were consumed
447             dspState.synthesis_read(bout);
448         }
449     
450         return convOff;
451     }
452
453     
454     /**
455      * Decodes the next packet.
456      * @return bytes read into convbuffer of -1 if end of file
457      */
458     private int lazyDecodePacket() throws IOException {
459         int result = getNextPacket(packet);
460         if (result == -1) {
461             return -1;
462         }
463
464         // we have a packet.  Decode it
465         return decodePacket(packet);
466     }
467
468
469     /**
470      * @param packet where to put the packet.
471      */
472     private int getNextPacket(Packet packet) throws IOException {
473         // get next packet.
474         boolean fetchedPacket = false;
475         while (!eos && !fetchedPacket) {
476             int result1 = streamState.packetout(packet);
477             if (result1 == 0) {
478                 // no more packets in page. Fetch new page.
479                 int result2 = 0;
480                 while (!eos && result2 == 0) {
481                     result2 = syncState.pageout(page);
482                     if (result2 == 0) {
483                         fetchData();
484                     }
485                 }
486
487                 // return if we have reaced end of file.
488                 if ((result2 == 0) && (page.eos() != 0)) {
489                     return -1;
490                 }
491                 
492                 if (result2 == 0) {
493                     // need more data fetching page..
494                     fetchData();
495                 } else if (result2 == -1) {
496                     logger.info("syncState.pageout(page) result == -1");
497                     return -1;
498                 } else {
499 //                    int result3 = 
500                         streamState.pagein(page);
501                 }
502             } else if (result1 == -1) {
503                 logger.info("streamState.packetout(packet) result == -1");
504                 return -1;
505             } else {
506                 fetchedPacket = true;
507             }
508         }
509
510         return 0;
511     }
512
513
514     /**
515      * Copys data from input stream to syncState.
516      */
517     private void fetchData() throws IOException {
518         if (!eos) {
519             // copy 4096 bytes from compressed stream to syncState.
520             int index = syncState.buffer(4096);
521             if (index < 0) {
522                 eos = true;
523                 return;
524             }
525             int bytes = in.read(syncState.data, index, 4096);
526             syncState.wrote(bytes); 
527             if (bytes == 0) {
528                 eos = true;
529             }
530         }
531     }
532
533
534     /**
535      * Gets information on the ogg.
536      */
537     public String toString() {
538         String s = "";
539         s = s + "version         " + info.version         + "\n";
540         s = s + "channels        " + info.channels        + "\n";
541         s = s + "rate (hz)       " + info.rate            ;
542         return s;
543     }
544
545     @Override
546     public int getChannelCount() {
547         return info.channels;
548     }
549
550
551     @Override
552     public OggInputStream makeNew() throws IOException {
553         return new OggInputStream(getResource(), getLength());
554     }
555 }