summaryrefslogtreecommitdiff
path: root/songdbj/com/jcraft/jorbis/VorbisFile.java.new
diff options
context:
space:
mode:
Diffstat (limited to 'songdbj/com/jcraft/jorbis/VorbisFile.java.new')
-rw-r--r--songdbj/com/jcraft/jorbis/VorbisFile.java.new1240
1 files changed, 1240 insertions, 0 deletions
diff --git a/songdbj/com/jcraft/jorbis/VorbisFile.java.new b/songdbj/com/jcraft/jorbis/VorbisFile.java.new
new file mode 100644
index 0000000..1f822b0
--- /dev/null
+++ b/songdbj/com/jcraft/jorbis/VorbisFile.java.new
@@ -0,0 +1,1240 @@
+/* JOrbis
+ * Copyright (C) 2000 ymnk, JCraft,Inc.
+ *
+ * Written by: 2000 ymnk<ymnk@jcraft.com>
+ *
+ * Many thanks to
+ * Monty <monty@xiph.org> and
+ * The XIPHOPHORUS Company http://www.xiph.org/ .
+ * JOrbis has been based on their awesome works, Vorbis codec.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package com.jcraft.jorbis;
+
+import com.jcraft.jogg.*;
+import java.io.InputStream;
+
+public class VorbisFile{
+ static final int CHUNKSIZE=4096;
+ static final int SEEK_SET=0;
+
+ InputStream datasource;
+ boolean seekable=false;
+ long offset;
+ long end;
+
+ SyncState oy=new SyncState();
+
+ int links;
+ Comment[] vc;
+ Info[] vi;
+
+ long[] offsets;
+ long[] dataoffsets;
+ int[] serialnos;
+ long[] pcmlengths;
+
+
+
+ // Decoding working state local storage
+ long pcm_offset;
+ boolean decode_ready=false;
+ int current_serialno;
+ int current_link;
+
+ float bittrack;
+ float samptrack;
+
+ StreamState os=new StreamState(); // take physical pages, weld into a logical
+ // stream of packets
+ DspState vd=new DspState(); // central working state for
+ // the packet->PCM decoder
+ Block vb=new Block(vd); // local working space for packet->PCM decode
+
+ //ov_callbacks callbacks;
+
+ public VorbisFile(String file) throws JOrbisException {
+ super();
+ InputStream is=null;
+ try{ is=new java.io.BufferedInputStream(new java.io.FileInputStream(file));}
+ catch(Exception e){
+ throw new JOrbisException("VorbisFile: "+e.toString());
+ }
+ int ret=open(is, null, 0);
+ if(ret==-1){
+ throw new JOrbisException("VorbisFile: open return -1");
+ }
+ }
+
+ public VorbisFile(InputStream is, byte[] initial, int ibytes)
+ throws JOrbisException {
+ super();
+ int ret=open(is, initial, ibytes);
+ if(ret==-1){
+ }
+ }
+
+ private int get_data(){
+ int index=oy.buffer(CHUNKSIZE);
+ byte[] buffer=oy.data;
+// int bytes=callbacks.read_func(buffer, index, 1, CHUNKSIZE, datasource);
+ int bytes=0;
+ try{
+ bytes=datasource.read(buffer, index, CHUNKSIZE);
+ }
+ catch(Exception e){System.err.println(e);}
+ oy.wrote(bytes);
+ return bytes;
+ }
+
+ private void seek_helper(int offst){
+ //callbacks.seek_func(datasource, offst, SEEK_SET);
+ fseek64_wrap(datasource, offst, SEEK_SET);
+ this.offset=offst;
+ oy.reset();
+ }
+
+ private int get_next_page(Page page, int boundary){
+ if(boundary>0) boundary+=offset;
+ while(true){
+ int more;
+ if(boundary>0 && offset>=boundary)return -1;
+ more=oy.pageseek(page);
+ if(more<0){offset-=more;}
+ else{
+ if(more==0){
+ if(boundary==0)return -1;
+ if(get_data()<=0)return -1;
+ }
+ else{
+ int ret=(int)offset; //!!!
+ offset+=more;
+ return ret;
+ }
+ }
+ }
+ }
+
+ private int get_prev_page(Page page){
+ int begin=(int)offset; //!!!
+ int ret;
+ int offst=-1;
+ while(offst==-1){
+ begin-=CHUNKSIZE;
+ seek_helper(begin);
+ while(offset<begin+CHUNKSIZE){
+ ret=get_next_page(page, begin+CHUNKSIZE-((int)offset));
+ if(ret==-1){ break; }
+ else{ offst=ret; }
+ }
+ }
+ seek_helper((int)offset); //!!!
+ ret=get_next_page(page, CHUNKSIZE);
+ if(ret==-1){
+ System.err.println("Missed page fencepost at end of logical bitstream Exiting");
+ System.exit(1);
+ }
+ return offst;
+ }
+
+ void bisect_forward_serialno(int begin, int searched, int end, int currentno, int m){
+ int endsearched=end;
+ int next=end;
+ Page page=new Page();
+ int ret;
+ while(searched<endsearched){
+ int bisect;
+ if(endsearched-searched<CHUNKSIZE){
+ bisect=searched;
+ }
+ else{
+ bisect=(searched+endsearched)/2;
+ }
+
+ seek_helper(bisect);
+ ret=get_next_page(page, -1);
+ if(ret<0 || page.serialno()!=currentno){
+ endsearched=bisect;
+ if(ret>=0)next=ret;
+ }
+ else{
+ searched=ret+page.header_len+page.body_len;
+ }
+ }
+ seek_helper(next);
+ ret=get_next_page(page, -1);
+
+ if(searched>=end || ret==-1){
+ links=m+1;
+ offsets=new long[m+2];
+ offsets[m+1]=searched;
+ }
+ else{
+ bisect_forward_serialno(next, (int)offset, end, page.serialno(), m+1);
+ }
+ offsets[m]=begin;
+ }
+
+ // uses the local ogg_stream storage in vf; this is important for
+ // non-streaming input sources
+ int fetch_headers(Info vi, Comment vc, int[] serialno){
+ //System.err.println("fetch_headers");
+ Page og=new Page();
+ Packet op=new Packet();
+ int ret;
+
+ ret=get_next_page(og, CHUNKSIZE);
+ if(ret==-1){
+ System.err.println("Did not find initial header for bitstream.");
+ return -1;
+ }
+
+ if(serialno!=null)serialno[0]=og.serialno();
+
+ os.init(og.serialno());
+
+ // extract the initial header from the first page and verify that the
+ // Ogg bitstream is in fact Vorbis data
+
+ vi.init();
+ vc.init();
+
+ int i=0;
+ while(i<3){
+ os.pagein(og);
+ while(i<3){
+ int result=os.packetout(op);
+ if(result==0)break;
+ if(result==-1){
+ System.err.println("Corrupt header in logical bitstream.");
+ //goto bail_header;
+ vi.clear();
+ vc.clear();
+ os.clear();
+ return -1;
+ }
+ if(vi.synthesis_headerin(vc, op)!=0){
+ System.err.println("Illegal header in logical bitstream.");
+ //goto bail_header;
+ vi.clear();
+ vc.clear();
+ os.clear();
+ return -1;
+ }
+ i++;
+ }
+ if(i<3)
+ if(get_next_page(og, 1)<0){
+ System.err.println("Missing header in logical bitstream.");
+ //goto bail_header;
+ vi.clear();
+ vc.clear();
+ os.clear();
+ return -1;
+ }
+ }
+ return 0;
+
+// bail_header:
+// vorbis_info_clear(vi);
+// vorbis_comment_clear(vc);
+// ogg_stream_clear(&vf->os);
+// return -1;
+ }
+
+ // last step of the OggVorbis_File initialization; get all the
+ // vorbis_info structs and PCM positions. Only called by the seekable
+ // initialization (local stream storage is hacked slightly; pay
+ // attention to how that's done)
+ void prefetch_all_headers(Info first_i,Comment first_c, int dataoffset){
+ Page og=new Page();
+ int ret;
+
+ vi=new Info[links];
+ vc=new Comment[links];
+ dataoffsets=new long[links];
+ pcmlengths=new long[links];
+ serialnos=new int[links];
+
+ for(int i=0;i<links;i++){
+ if(first_i!=null && first_c!=null && i==0){
+ // we already grabbed the initial header earlier. This just
+ // saves the waste of grabbing it again
+ // !!!!!!!!!!!!!
+ vi[i]=first_i;
+ //memcpy(vf->vi+i,first_i,sizeof(vorbis_info));
+ vc[i]=first_c;
+ //memcpy(vf->vc+i,first_c,sizeof(vorbis_comment));
+ dataoffsets[i]=dataoffset;
+ }
+ else{
+ // seek to the location of the initial header
+ seek_helper((int)offsets[i]); //!!!
+ if(fetch_headers(vi[i], vc[i], null)==-1){
+ System.err.println("Error opening logical bitstream #"+(i+1)+"\n");
+ dataoffsets[i]=-1;
+ }
+ else{
+ dataoffsets[i]=offset;
+ os.clear();
+ }
+ }
+
+ // get the serial number and PCM length of this link. To do this,
+ // get the last page of the stream
+ {
+ int end=(int)offsets[i+1]; //!!!
+ seek_helper(end);
+
+ while(true){
+ ret=get_prev_page(og);
+ if(ret==-1){
+ // this should not be possible
+ System.err.println("Could not find last page of logical "+
+ "bitstream #"+(i)+"\n");
+ vi[i].clear();
+ vc[i].clear();
+ break;
+ }
+ if(og.granulepos()!=-1){
+ serialnos[i]=og.serialno();
+ pcmlengths[i]=og.granulepos();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ int make_decode_ready(){
+ if(decode_ready)System.exit(1);
+ vd.synthesis_init(vi[0]);
+ vb.init(vd);
+ decode_ready=true;
+ return(0);
+ }
+
+ int open_seekable(){
+ Info initial_i=new Info();
+ Comment initial_c=new Comment();
+ int serialno,end;
+ int ret;
+ int dataoffset;
+ Page og=new Page();
+System.out.println("open_seekable");
+ // is this even vorbis...?
+ int[] foo=new int[1];
+ ret=fetch_headers(initial_i, initial_c, foo);
+ serialno=foo[0];
+ dataoffset=(int)offset; //!!
+ os.clear();
+ if(ret==-1)return(-1);
+
+ // we can seek, so set out learning all about this file
+ seekable=true;
+ //(callbacks.seek_func)(datasource, 0, SEEK_END);
+ fseek64_wrap(datasource, (int)offset, SEEK_SET);
+ //offset=end=(callbacks.tell_func)(datasource);
+ end=(int)offset;
+
+ // We get the offset for the last page of the physical bitstream.
+ // Most OggVorbis files will contain a single logical bitstream
+ end=get_prev_page(og);
+
+ // moer than one logical bitstream?
+ if(og.serialno()!=serialno){
+ // Chained bitstream. Bisect-search each logical bitstream
+ // section. Do so based on serial number only
+ bisect_forward_serialno(0,0,end+1,serialno,0);
+ }
+ else{
+ // Only one logical bitstream
+ bisect_forward_serialno(0,end,end+1,serialno,0);
+ }
+ prefetch_all_headers(initial_i, initial_c, dataoffset);
+
+System.out.println("?");
+ return(raw_seek(0));
+ }
+
+ int open_nonseekable(){
+ //System.err.println("open_nonseekable");
+ // we cannot seek. Set up a 'single' (current) logical bitstream entry
+ links=1;
+ vi=new Info[links]; vi[0]=new Info(); // ??
+ vc=new Comment[links]; vc[0]=new Comment(); // ?? bug?
+
+ // Try to fetch the headers, maintaining all the storage
+ int[]foo=new int[1];
+ if(fetch_headers(vi[0], vc[0], foo)==-1)return(-1);
+ current_serialno=foo[0];
+ make_decode_ready();
+ return 0;
+ }
+
+ // clear out the current logical bitstream decoder
+ void decode_clear(){
+ os.clear();
+ vd.clear();
+ vb.clear();
+ decode_ready=false;
+ bittrack=0.f;
+ samptrack=0.f;
+ }
+
+ // fetch and process a packet. Handles the case where we're at a
+ // bitstream boundary and dumps the decoding machine. If the decoding
+ // machine is unloaded, it loads it. It also keeps pcm_offset up to
+ // date (seek and read both use this. seek uses a special hack with
+ // readp).
+ //
+ // return: -1) hole in the data (lost packet)
+ // 0) need more date (only if readp==0)/eof
+ // 1) got a packet
+
+ int process_packet(int readp){
+System.out.println("porcess_packet:"+ readp+" , decode_ready="+decode_ready);
+ Page og=new Page();
+
+ // handle one packet. Try to fetch it from current stream state
+ // extract packets from page
+ while(true){
+ // process a packet if we can. If the machine isn't loaded,
+ // neither is a page
+ if(decode_ready){
+ Packet op=new Packet();
+ int result=os.packetout(op);
+ long granulepos;
+ // if(result==-1)return(-1); // hole in the data. For now, swallow
+ // and go. We'll need to add a real
+ // error code in a bit.
+ if(result>0){
+ // got a packet. process it
+ granulepos=op.granulepos;
+ if(vb.synthesis(op)==0){ // lazy check for lazy
+ // header handling. The
+ // header packets aren't
+ // audio, so if/when we
+ // submit them,
+ // vorbis_synthesis will
+ // reject them
+ // suck in the synthesis data and track bitrate
+ {
+ int oldsamples=vd.synthesis_pcmout(null, null);
+ vd.synthesis_blockin(vb);
+ samptrack+=vd.synthesis_pcmout(null, null)-oldsamples;
+ bittrack+=op.bytes*8;
+ }
+
+ // update the pcm offset.
+ if(granulepos!=-1 && op.e_o_s==0){
+ int link=(seekable?current_link:0);
+ int samples;
+ // this packet has a pcm_offset on it (the last packet
+ // completed on a page carries the offset) After processing
+ // (above), we know the pcm position of the *last* sample
+ // ready to be returned. Find the offset of the *first*
+ //
+ // As an aside, this trick is inaccurate if we begin
+ // reading anew right at the last page; the end-of-stream
+ // granulepos declares the last frame in the stream, and the
+ // last packet of the last page may be a partial frame.
+ // So, we need a previous granulepos from an in-sequence page
+ // to have a reference point. Thus the !op.e_o_s clause above
+
+ samples=vd.synthesis_pcmout(null, null);
+ granulepos-=samples;
+ for(int i=0;i<link;i++){
+ granulepos+=pcmlengths[i];
+ }
+ pcm_offset=granulepos;
+ }
+ return(1);
+ }
+ }
+ }
+
+ if(readp==0)return(0);
+ if(get_next_page(og,-1)<0)return(0); // eof. leave unitialized
+
+ // bitrate tracking; add the header's bytes here, the body bytes
+ // are done by packet above
+ bittrack+=og.header_len*8;
+
+ // has our decoding just traversed a bitstream boundary?
+ if(decode_ready){
+ if(current_serialno!=og.serialno()){
+ decode_clear();
+ }
+ }
+
+ // Do we need to load a new machine before submitting the page?
+ // This is different in the seekable and non-seekable cases.
+ //
+ // In the seekable case, we already have all the header
+ // information loaded and cached; we just initialize the machine
+ // with it and continue on our merry way.
+ //
+ // In the non-seekable (streaming) case, we'll only be at a
+ // boundary if we just left the previous logical bitstream and
+ // we're now nominally at the header of the next bitstream
+
+ if(!decode_ready){
+ int i;
+ if(seekable){
+ current_serialno=og.serialno();
+
+ // match the serialno to bitstream section. We use this rather than
+ // offset positions to avoid problems near logical bitstream
+ // boundaries
+ for(i=0;i<links;i++){
+ if(serialnos[i]==current_serialno)break;
+ }
+ if(i==links)return(-1); // sign of a bogus stream. error out,
+ // leave machine uninitialized
+ current_link=i;
+
+ os.init(current_serialno);
+ os.reset();
+
+ }
+ else{
+ // we're streaming
+ // fetch the three header packets, build the info struct
+ int foo[]=new int[1];
+ fetch_headers(vi[0], vc[0], foo);
+ current_serialno=foo[0];
+ current_link++;
+ i=0;
+ }
+ make_decode_ready();
+ }
+ os.pagein(og);
+ }
+ }
+
+ //The helpers are over; it's all toplevel interface from here on out
+ // clear out the OggVorbis_File struct
+ int clear(){
+ vb.clear();
+ vd.clear();
+ os.clear();
+
+ if(vi!=null && links!=0){
+ for(int i=0;i<links;i++){
+ vi[i].clear();
+ vc[i].clear();
+ }
+ vi=null;
+ vc=null;
+ }
+ if(dataoffsets!=null)dataoffsets=null;
+ if(pcmlengths!=null)pcmlengths=null;
+ if(serialnos!=null)serialnos=null;
+ if(offsets!=null)offsets=null;
+ oy.clear();
+ //if(datasource!=null)(vf->callbacks.close_func)(vf->datasource);
+ //memset(vf,0,sizeof(OggVorbis_File));
+ return(0);
+ }
+
+ static int fseek64_wrap(InputStream fis,
+ //int64_t off,
+ int off,
+ int whence){
+
+ if(!fis.markSupported()){ return -1; }
+ try{
+ try{if(whence==0){ fis.reset(); }}
+ catch(Exception ee){System.out.println(ee);}
+ fis.skip(off);
+ }
+ catch(Exception e){ System.out.println(e);
+ //return -1;
+ }
+ return 0;
+ }
+
+ // inspects the OggVorbis file and finds/documents all the logical
+ // bitstreams contained in it. Tries to be tolerant of logical
+ // bitstream sections that are truncated/woogie.
+ //
+ // return: -1) error
+ // 0) OK
+
+ int open(InputStream is, byte[] initial, int ibytes){
+ return open_callbacks(is, initial, ibytes//, callbacks
+ );
+ }
+
+ int open_callbacks(InputStream is, byte[] initial,
+ int ibytes//, callbacks callbacks
+ ){
+// int offset=callbacks.seek_func(f,0,SEEK_CUR);
+ int _offset=fseek64_wrap(is, (int)offset, SEEK_SET);
+ int ret;
+ // memset(vf,0,sizeof(OggVorbis_File));
+ datasource=is;
+ //callbacks = _callbacks;
+
+ // init the framing state
+ oy.init();
+
+ // perhaps some data was previously read into a buffer for testing
+ // against other stream types. Allow initialization from this
+ // previously read data (as we may be reading from a non-seekable
+ // stream)
+ if(initial!=null){
+ int index=oy.buffer(ibytes);
+ System.arraycopy(initial, 0, oy.data, index, ibytes);
+ oy.wrote(ibytes);
+ }
+
+System.out.println("open_callbacks="+_offset);
+ // can we seek? Stevens suggests the seek test was portable
+ if(_offset!=-1){ ret=open_seekable(); }
+ else{ ret=open_nonseekable(); }
+
+System.out.println("ret="+ret);
+
+ if(ret!=0){
+ datasource=null;
+ clear();
+ }
+
+ return(ret);
+ }
+
+ // How many logical bitstreams in this physical bitstream?
+ public int streams(){
+ return links;
+ }
+
+ // Is the FILE * associated with vf seekable?
+ public boolean seekable(){
+ return seekable;
+ }
+
+ // returns the bitrate for a given logical bitstream or the entire
+ // physical bitstream. If the file is open for random access, it will
+ // find the *actual* average bitrate. If the file is streaming, it
+ // returns the nominal bitrate (if set) else the average of the
+ // upper/lower bounds (if set) else -1 (unset).
+ //
+ // If you want the actual bitrate field settings, get them from the
+ // vorbis_info structs
+
+ public int bitrate(int i){
+ if(i>=links)return(-1);
+ if(!seekable && i!=0)return(bitrate(0));
+ if(i<0){
+ long bits=0;
+ for(int j=0;j<links;j++){
+ bits+=(offsets[j+1]-dataoffsets[j])*8;
+ }
+ return((int)Math.rint(bits/time_total(-1)));
+ }
+ else{
+ if(seekable){
+ // return the actual bitrate
+ return((int)Math.rint((offsets[i+1]-dataoffsets[i])*8/time_total(i)));
+ }
+ else{
+ // return nominal if set
+ if(vi[i].bitrate_nominal>0){
+ return vi[i].bitrate_nominal;
+ }
+ else{
+ if(vi[i].bitrate_upper>0){
+ if(vi[i].bitrate_lower>0){
+ return (vi[i].bitrate_upper+vi[i].bitrate_lower)/2;
+ }else{
+ return vi[i].bitrate_upper;
+ }
+ }
+ return(-1);
+ }
+ }
+ }
+ }
+
+ // returns the actual bitrate since last call. returns -1 if no
+ // additional data to offer since last call (or at beginning of stream)
+ public int bitrate_instant(){
+ int _link=(seekable?current_link:0);
+ if(samptrack==0)return(-1);
+ int ret=(int)(bittrack/samptrack*vi[_link].rate+.5);
+ bittrack=0.f;
+ samptrack=0.f;
+ return(ret);
+ }
+
+ public int serialnumber(int i){
+ if(i>=links)return(-1);
+ if(!seekable && i>=0)return(serialnumber(-1));
+ if(i<0){
+ return(current_serialno);
+ }
+ else{
+ return(serialnos[i]);
+ }
+ }
+
+ // returns: total raw (compressed) length of content if i==-1
+ // raw (compressed) length of that logical bitstream for i==0 to n
+ // -1 if the stream is not seekable (we can't know the length)
+
+ public long raw_total(int i){
+System.out.println("raw_total: "+seekable);
+ if(!seekable || i>=links)return(-1);
+ if(i<0){
+ long acc=0; // bug?
+ for(int j=0;j<links;j++){
+ acc+=raw_total(j);
+ }
+ return(acc);
+ }
+ else{
+ return(offsets[i+1]-offsets[i]);
+ }
+ }
+
+ // returns: total PCM length (samples) of content if i==-1
+ // PCM length (samples) of that logical bitstream for i==0 to n
+ // -1 if the stream is not seekable (we can't know the length)
+ public long pcm_total(int i){
+ if(!seekable || i>=links)return(-1);
+ if(i<0){
+ long acc=0;
+ for(int j=0;j<links;j++){
+ acc+=pcm_total(j);
+ }
+ return(acc);
+ }
+ else{
+ return(pcmlengths[i]);
+ }
+ }
+
+ // returns: total seconds of content if i==-1
+ // seconds in that logical bitstream for i==0 to n
+ // -1 if the stream is not seekable (we can't know the length)
+ public float time_total(int i){
+ if(!seekable || i>=links)return(-1);
+ if(i<0){
+ float acc=0;
+ for(int j=0;j<links;j++){
+ acc+=time_total(j);
+ }
+ return(acc);
+ }
+ else{
+ return((float)(pcmlengths[i])/vi[i].rate);
+ }
+ }
+
+ // seek to an offset relative to the *compressed* data. This also
+ // immediately sucks in and decodes pages to update the PCM cursor. It
+ // will cross a logical bitstream boundary, but only if it can't get
+ // any packets out of the tail of the bitstream we seek to (so no
+ // surprises).
+ //
+ // returns zero on success, nonzero on failure
+
+ public int raw_seek(int pos){
+System.out.println("raw_seek: "+pos);
+ if(!seekable)return(-1); // don't dump machine if we can't seek
+ if(pos<0 || pos>offsets[links]){
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ }
+System.out.println("#1");
+ // clear out decoding machine state
+ pcm_offset=-1;
+System.out.println("#2");
+ decode_clear();
+System.out.println("#3");
+ // seek
+ seek_helper(pos);
+
+ // we need to make sure the pcm_offset is set. We use the
+ // _fetch_packet helper to process one packet with readp set, then
+ // call it until it returns '0' with readp not set (the last packet
+ // from a page has the 'granulepos' field set, and that's how the
+ // helper updates the offset
+System.out.println("#4");
+ switch(process_packet(1)){
+ case 0:
+System.out.println("?0");
+ // oh, eof. There are no packets remaining. Set the pcm offset to
+ // the end of file
+ pcm_offset=pcm_total(-1);
+ return(0);
+ case -1:
+System.out.println("?-1");
+ // error! missing data or invalid bitstream structure
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ default:
+System.out.println("?break");
+ // all OK
+ break;
+ }
+System.out.println("pcm_offset="+pcm_offset);
+ while(true){
+ switch(process_packet(0)){
+ case 0:
+ // the offset is set. If it's a bogus bitstream with no offset
+ // information, it's not but that's not our fault. We still run
+ // gracefully, we're just missing the offset
+ return(0);
+ case -1:
+ // error! missing data or invalid bitstream structure
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ default:
+ // continue processing packets
+ break;
+ }
+ }
+
+ // seek_error:
+ // dump the machine so we're in a known state
+ //pcm_offset=-1;
+ //decode_clear();
+ //return -1;
+ }
+
+ // seek to a sample offset relative to the decompressed pcm stream
+ // returns zero on success, nonzero on failure
+
+ public int pcm_seek(long pos){
+ int link=-1;
+ long total=pcm_total(-1);
+
+ if(!seekable)return(-1); // don't dump machine if we can't seek
+ if(pos<0 || pos>total){
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ }
+
+ // which bitstream section does this pcm offset occur in?
+ for(link=links-1;link>=0;link--){
+ total-=pcmlengths[link];
+ if(pos>=total)break;
+ }
+
+ // search within the logical bitstream for the page with the highest
+ // pcm_pos preceeding (or equal to) pos. There is a danger here;
+ // missing pages or incorrect frame number information in the
+ // bitstream could make our task impossible. Account for that (it
+ // would be an error condition)
+ {
+ long target=pos-total;
+ int end=(int)offsets[link+1];
+ int begin=(int)offsets[link];
+ int best=begin;
+
+ Page og=new Page();
+ while(begin<end){
+ int bisect;
+ int ret;
+
+ if(end-begin<CHUNKSIZE){
+ bisect=begin;
+ }
+ else{
+ bisect=(end+begin)/2;
+ }
+
+ seek_helper(bisect);
+ ret=get_next_page(og,end-bisect);
+
+ if(ret==-1){
+ end=bisect;
+ }
+ else{
+ long granulepos=og.granulepos();
+ if(granulepos<target){
+ best=ret; // raw offset of packet with granulepos
+ begin=(int)offset; // raw offset of next packet
+ }
+ else{
+ end=bisect;
+ }
+ }
+ }
+ // found our page. seek to it (call raw_seek).
+ if(raw_seek(best)!=0){
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ }
+ }
+
+ // verify result
+ if(pcm_offset>=pos){
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ }
+ if(pos>pcm_total(-1)){
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ }
+
+ // discard samples until we reach the desired position. Crossing a
+ // logical bitstream boundary with abandon is OK.
+ while(pcm_offset<pos){
+ float[][] pcm;
+ int target=(int)(pos-pcm_offset);
+ float[][][] _pcm=new float[1][][];
+ int[] _index=new int[info(-1).channels];
+ int samples=vd.synthesis_pcmout(_pcm, _index);
+ pcm=_pcm[0];
+
+ if(samples>target)samples=target;
+ vd.synthesis_read(samples);
+ pcm_offset+=samples;
+
+ if(samples<target)
+ if(process_packet(1)==0){
+ pcm_offset=pcm_total(-1); // eof
+ }
+ }
+ return 0;
+
+ // seek_error:
+ // dump machine so we're in a known state
+ //pcm_offset=-1;
+ //decode_clear();
+ //return -1;
+ }
+
+ // seek to a playback time relative to the decompressed pcm stream
+ // returns zero on success, nonzero on failure
+ public int time_seek(float seconds){
+ // translate time to PCM position and call pcm_seek
+
+ int link=-1;
+ long pcm_total=pcm_total(-1);
+ float time_total=time_total(-1);
+
+ if(!seekable)return(-1); // don't dump machine if we can't seek
+ if(seconds<0 || seconds>time_total){
+ //goto seek_error;
+ pcm_offset=-1;
+ decode_clear();
+ return -1;
+ }
+
+ // which bitstream section does this time offset occur in?
+ for(link=links-1;link>=0;link--){
+ pcm_total-=pcmlengths[link];
+ time_total-=time_total(link);
+ if(seconds>=time_total)break;
+ }
+
+ // enough information to convert time offset to pcm offset
+ {
+ long target=(long)(pcm_total+(seconds-time_total)*vi[link].rate);
+ return(pcm_seek(target));
+ }
+
+ //seek_error:
+ // dump machine so we're in a known state
+ //pcm_offset=-1;
+ //decode_clear();
+ //return -1;
+ }
+
+ // tell the current stream offset cursor. Note that seek followed by
+ // tell will likely not give the set offset due to caching
+ public long raw_tell(){
+ return(offset);
+ }
+
+ // return PCM offset (sample) of next PCM sample to be read
+ public long pcm_tell(){
+ return(pcm_offset);
+ }
+
+ // return time offset (seconds) of next PCM sample to be read
+ public float time_tell(){
+ // translate time to PCM position and call pcm_seek
+
+ int link=-1;
+ long pcm_total=0;
+ float time_total=0.f;
+
+ if(seekable){
+ pcm_total=pcm_total(-1);
+ time_total=time_total(-1);
+
+ // which bitstream section does this time offset occur in?
+ for(link=links-1;link>=0;link--){
+ pcm_total-=pcmlengths[link];
+ time_total-=time_total(link);
+ if(pcm_offset>=pcm_total)break;
+ }
+ }
+
+ return((float)time_total+(float)(pcm_offset-pcm_total)/vi[link].rate);
+ }
+
+ // link: -1) return the vorbis_info struct for the bitstream section
+ // currently being decoded
+ // 0-n) to request information for a specific bitstream section
+ //
+ // In the case of a non-seekable bitstream, any call returns the
+ // current bitstream. NULL in the case that the machine is not
+ // initialized
+
+ public Info info(int link){
+ if(seekable){
+ if(link<0){
+ if(decode_ready){
+ return vi[current_link];
+ }
+ else{
+ return null;
+ }
+ }
+ else{
+ if(link>=links){
+ return null;
+ }
+ else{
+ return vi[link];
+ }
+ }
+ }
+ else{
+ if(decode_ready){
+ return vi[0];
+ }
+ else{
+ return null;
+ }
+ }
+ }
+
+ public Comment comment(int link){
+ if(seekable){
+ if(link<0){
+ if(decode_ready){ return vc[current_link]; }
+ else{ return null; }
+ }
+ else{
+ if(link>=links){ return null;}
+ else{ return vc[link]; }
+ }
+ }
+ else{
+ if(decode_ready){ return vc[0]; }
+ else{ return null; }
+ }
+ }
+
+ int host_is_big_endian() {
+ return 1;
+// short pattern = 0xbabe;
+// unsigned char *bytewise = (unsigned char *)&pattern;
+// if (bytewise[0] == 0xba) return 1;
+// assert(bytewise[0] == 0xbe);
+// return 0;
+ }
+
+ // up to this point, everything could more or less hide the multiple
+ // logical bitstream nature of chaining from the toplevel application
+ // if the toplevel application didn't particularly care. However, at
+ // the point that we actually read audio back, the multiple-section
+ // nature must surface: Multiple bitstream sections do not necessarily
+ // have to have the same number of channels or sampling rate.
+ //
+ // read returns the sequential logical bitstream number currently
+ // being decoded along with the PCM data in order that the toplevel
+ // application can take action on channel/sample rate changes. This
+ // number will be incremented even for streamed (non-seekable) streams
+ // (for seekable streams, it represents the actual logical bitstream
+ // index within the physical bitstream. Note that the accessor
+ // functions above are aware of this dichotomy).
+ //
+ // input values: buffer) a buffer to hold packed PCM data for return
+ // length) the byte length requested to be placed into buffer
+ // bigendianp) should the data be packed LSB first (0) or
+ // MSB first (1)
+ // word) word size for output. currently 1 (byte) or
+ // 2 (16 bit short)
+ //
+ // return values: -1) error/hole in data
+ // 0) EOF
+ // n) number of bytes of PCM actually returned. The
+ // below works on a packet-by-packet basis, so the
+ // return length is not related to the 'length' passed
+ // in, just guaranteed to fit.
+ //
+ // *section) set to the logical bitstream number
+
+ int read(byte[] buffer,int length,
+ int bigendianp, int word, int sgned, int[] bitstream){
+ int host_endian = host_is_big_endian();
+ int index=0;
+
+ while(true){
+ if(decode_ready){
+ float[][] pcm;
+ float[][][] _pcm=new float[1][][];
+ int[] _index=new int[info(-1).channels];
+ int samples=vd.synthesis_pcmout(_pcm, _index);
+ pcm=_pcm[0];
+ if(samples!=0){
+ // yay! proceed to pack data into the byte buffer
+ int channels=info(-1).channels;
+ int bytespersample=word * channels;
+ if(samples>length/bytespersample)samples=length/bytespersample;
+
+ // a tight loop to pack each size
+ {
+ int val;
+ if(word==1){
+ int off=(sgned!=0?0:128);
+ for(int j=0;j<samples;j++){
+ for(int i=0;i<channels;i++){
+ val=(int)(pcm[i][_index[i]+j]*128. + 0.5);
+ if(val>127)val=127;
+ else if(val<-128)val=-128;
+ buffer[index++]=(byte)(val+off);
+ }
+ }
+ }
+ else{
+ int off=(sgned!=0?0:32768);
+
+ if(host_endian==bigendianp){
+ if(sgned!=0){
+ for(int i=0;i<channels;i++) { // It's faster in this order
+ int src=_index[i];
+ int dest=i;
+ for(int j=0;j<samples;j++) {
+ val=(int)(pcm[i][src+j]*32768. + 0.5);
+ if(val>32767)val=32767;
+ else if(val<-32768)val=-32768;
+ buffer[dest]=(byte)(val>>>8);
+ buffer[dest+1]=(byte)(val);
+ dest+=channels*2;
+ }
+ }
+ }
+ else{
+ for(int i=0;i<channels;i++) {
+ float[] src=pcm[i];
+ int dest=i;
+ for(int j=0;j<samples;j++) {
+ val=(int)(src[j]*32768. + 0.5);
+ if(val>32767)val=32767;
+ else if(val<-32768)val=-32768;
+ buffer[dest]=(byte)((val+off)>>>8);
+ buffer[dest+1]=(byte)(val+off);
+ dest+=channels*2;
+ }
+ }
+ }
+ }
+ else if(bigendianp!=0){
+ for(int j=0;j<samples;j++){
+ for(int i=0;i<channels;i++){
+ val=(int)(pcm[i][j]*32768. + 0.5);
+ if(val>32767)val=32767;
+ else if(val<-32768)val=-32768;
+ val+=off;
+ buffer[index++]=(byte)(val>>>8);
+ buffer[index++]=(byte)val;
+ }
+ }
+ }
+ else{
+ //int val;
+ for(int j=0;j<samples;j++){
+ for(int i=0;i<channels;i++){
+ val=(int)(pcm[i][j]*32768. + 0.5);
+ if(val>32767)val=32767;
+ else if(val<-32768)val=-32768;
+ val+=off;
+ buffer[index++]=(byte)val;
+ buffer[index++]=(byte)(val>>>8);
+ }
+ }
+ }
+ }
+ }
+
+ vd.synthesis_read(samples);
+ pcm_offset+=samples;
+ if(bitstream!=null)bitstream[0]=current_link;
+ return(samples*bytespersample);
+ }
+ }
+
+ // suck in another packet
+ switch(process_packet(1)){
+ case 0:
+ return(0);
+ case -1:
+ return -1;
+ default:
+ break;
+ }
+ }
+ }
+
+ public int getLinks(){return links;}
+ public Info[] getInfo(){return vi;}
+ public Comment[] getComment(){return vc;}
+
+ public static void main(String[] arg){
+ try{
+ VorbisFile foo=new VorbisFile(arg[0]);
+ int links=foo.getLinks();
+ System.out.println("links="+links);
+ Comment[] comment=foo.getComment();
+ Info[] info=foo.getInfo();
+ for(int i=0; i<links; i++){
+ System.out.println(info[i]);
+ System.out.println(comment[i]);
+ }
+ System.out.println("raw_total: "+foo.raw_total(-1));
+ System.out.println("pcm_total: "+foo.pcm_total(-1));
+ System.out.println("time_total: "+foo.time_total(-1));
+ }
+ catch(Exception e){
+ System.err.println(e);
+ }
+ }
+}