summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/analysis.c105
-rw-r--r--lib/framing.c1623
-rw-r--r--lib/info.c543
-rw-r--r--lib/mapping0.c383
-rw-r--r--lib/psy.c110
-rw-r--r--lib/psytune.c64
-rw-r--r--lib/res0.c384
-rw-r--r--lib/vorbisfile.c1156
8 files changed, 4261 insertions, 107 deletions
diff --git a/lib/analysis.c b/lib/analysis.c
new file mode 100644
index 00000000..54735277
--- /dev/null
+++ b/lib/analysis.c
@@ -0,0 +1,105 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE Ogg Vorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
+ * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE. *
+ * PLEASE READ THESE TERMS DISTRIBUTING. *
+ * *
+ * THE OggSQUISH SOURCE CODE IS (C) COPYRIGHT 1994-2000 *
+ * by Monty <monty@xiph.org> and The XIPHOPHORUS Company *
+ * http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: single-block PCM analysis mode dispatch
+ last mod: $Id: analysis.c,v 1.31.2.1 2000/08/15 08:33:43 xiphmont Exp $
+
+ ********************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include "vorbis/codec.h"
+#include "bitwise.h"
+#include "registry.h"
+#include "scales.h"
+#include "os.h"
+
+/* decides between modes, dispatches to the appropriate mapping. */
+int vorbis_analysis(vorbis_block *vb,ogg_packet *op){
+ vorbis_dsp_state *vd=vb->vd;
+ vorbis_info *vi=vd->vi;
+ int type;
+ int mode=0;
+
+ vb->glue_bits=0;
+ vb->time_bits=0;
+ vb->floor_bits=0;
+ vb->res_bits=0;
+
+ /* first things first. Make sure encode is ready */
+ _oggpack_reset(&vb->opb);
+ /* Encode the packet type */
+ _oggpack_write(&vb->opb,0,1);
+
+ /* currently lazy. Short block dispatches to 0, long to 1. */
+
+ if(vb->W &&vi->modes>1)mode=1;
+ type=vi->map_type[vi->mode_param[mode]->mapping];
+ vb->mode=mode;
+
+ /* Encode frame mode, pre,post windowsize, then dispatch */
+ _oggpack_write(&vb->opb,mode,vd->modebits);
+ if(vb->W){
+ _oggpack_write(&vb->opb,vb->lW,1);
+ _oggpack_write(&vb->opb,vb->nW,1);
+ /*fprintf(stderr,"*");
+ }else{
+ fprintf(stderr,".");*/
+ }
+
+ if(_mapping_P[type]->forward(vb,vd->mode[mode]))
+ return(-1);
+
+ /* set up the packet wrapper */
+
+ op->packet=_oggpack_buffer(&vb->opb);
+ op->bytes=_oggpack_bytes(&vb->opb);
+ op->b_o_s=0;
+ op->e_o_s=vb->eofflag;
+ op->frameno=vb->frameno;
+ op->packetno=vb->sequence; /* for sake of completeness */
+
+ return(0);
+}
+
+/* there was no great place to put this.... */
+void _analysis_output(char *base,int i,double *v,int n,int bark,int dB){
+#ifdef ANALYSIS
+ int j;
+ FILE *of;
+ char buffer[80];
+ sprintf(buffer,"%s_%d.m",base,i);
+ of=fopen(buffer,"w");
+
+ if(!of)perror("failed to open data dump file");
+
+ for(j=0;j<n;j++){
+ if(dB && v[j]==0)
+ fprintf(of,"\n\n");
+ else{
+ if(bark)
+ fprintf(of,"%g ",toBARK(22050.*j/n));
+ else
+ fprintf(of,"%g ",(double)j);
+
+ if(dB){
+ fprintf(of,"%g\n",todB(fabs(v[j])));
+ }else{
+ fprintf(of,"%g\n",v[j]);
+ }
+ }
+ }
+ fclose(of);
+#endif
+}
diff --git a/lib/framing.c b/lib/framing.c
new file mode 100644
index 00000000..60bd5bcc
--- /dev/null
+++ b/lib/framing.c
@@ -0,0 +1,1623 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE Ogg Vorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
+ * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE. *
+ * PLEASE READ THESE TERMS DISTRIBUTING. *
+ * *
+ * THE OggSQUISH SOURCE CODE IS (C) COPYRIGHT 1994-2000 *
+ * by Monty <monty@xiph.org> and the XIPHOPHORUS Company *
+ * http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: code raw [Vorbis] packets into framed OggSquish stream and
+ decode Ogg streams back into raw packets
+ last mod: $Id: framing.c,v 1.21.2.1 2000/08/15 08:33:43 xiphmont Exp $
+
+ note: The CRC code is directly derived from public domain code by
+ Ross Williams (ross@guest.adelaide.edu.au). See docs/framing.html
+ for details.
+
+ ********************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include "vorbis/codec.h"
+#include "misc.h"
+
+/* A complete description of Ogg framing exists in docs/framing.html */
+
+int ogg_page_version(ogg_page *og){
+ return((int)(og->header[4]));
+}
+
+int ogg_page_continued(ogg_page *og){
+ return((int)(og->header[5]&0x01));
+}
+
+int ogg_page_bos(ogg_page *og){
+ return((int)(og->header[5]&0x02));
+}
+
+int ogg_page_eos(ogg_page *og){
+ return((int)(og->header[5]&0x04));
+}
+
+int64_t ogg_page_frameno(ogg_page *og){
+ unsigned char *page=og->header;
+ int64_t pcmpos=page[13]&(0xff);
+ pcmpos= (pcmpos<<8)|(page[12]&0xff);
+ pcmpos= (pcmpos<<8)|(page[11]&0xff);
+ pcmpos= (pcmpos<<8)|(page[10]&0xff);
+ pcmpos= (pcmpos<<8)|(page[9]&0xff);
+ pcmpos= (pcmpos<<8)|(page[8]&0xff);
+ pcmpos= (pcmpos<<8)|(page[7]&0xff);
+ pcmpos= (pcmpos<<8)|(page[6]&0xff);
+ return(pcmpos);
+}
+
+int ogg_page_serialno(ogg_page *og){
+ return(og->header[14] |
+ (og->header[15]<<8) |
+ (og->header[16]<<16) |
+ (og->header[17]<<24));
+}
+
+int ogg_page_pageno(ogg_page *og){
+ return(og->header[18] |
+ (og->header[19]<<8) |
+ (og->header[20]<<16) |
+ (og->header[21]<<24));
+}
+
+/* helper to initialize lookup for direct-table CRC */
+
+static unsigned vorbis_size32_t crc_lookup[256];
+static int crc_ready=0;
+
+static unsigned vorbis_size32_t _ogg_crc_entry(unsigned long index){
+ int i;
+ unsigned long r;
+
+ r = index << 24;
+ for (i=0; i<8; i++)
+ if (r & 0x80000000UL)
+ r = (r << 1) ^ 0x04c11db7; /* The same as the ethernet generator
+ polynomial, although we use an
+ unreflected alg and an init/final
+ of 0, not 0xffffffff */
+ else
+ r<<=1;
+ return (r & 0xffffffffUL);
+}
+
+/* mind this in threaded code; sync_init and stream_init call it.
+ It's thread safe only after the first time it returns */
+
+static void _ogg_crc_init(void){
+ if(!crc_ready){
+ /* initialize the crc_lookup table */
+ int i;
+ for (i=0;i<256;i++)
+ crc_lookup[i]=_ogg_crc_entry((unsigned long)i);
+ crc_ready=0;
+ }
+}
+
+/* init the encode/decode logical stream state */
+
+int ogg_stream_init(ogg_stream_state *os,int serialno){
+ if(os){
+ memset(os,0,sizeof(ogg_stream_state));
+ os->body_storage=16*1024;
+ os->body_data=malloc(os->body_storage*sizeof(char));
+
+ os->lacing_storage=1024;
+ os->lacing_vals=malloc(os->lacing_storage*sizeof(int));
+ os->pcm_vals=malloc(os->lacing_storage*sizeof(int64_t));
+
+ /* initialize the crc_lookup table if not done */
+ _ogg_crc_init();
+
+ os->serialno=serialno;
+
+ return(0);
+ }
+ return(-1);
+}
+
+/* _clear does not free os, only the non-flat storage within */
+int ogg_stream_clear(ogg_stream_state *os){
+ if(os){
+ if(os->body_data)free(os->body_data);
+ if(os->lacing_vals)free(os->lacing_vals);
+ if(os->pcm_vals)free(os->pcm_vals);
+
+ memset(os,0,sizeof(ogg_stream_state));
+ }
+ return(0);
+}
+
+int ogg_stream_destroy(ogg_stream_state *os){
+ if(os){
+ ogg_stream_clear(os);
+ free(os);
+ }
+ return(0);
+}
+
+/* Helpers for ogg_stream_encode; this keeps the structure and
+ what's happening fairly clear */
+
+static void _os_body_expand(ogg_stream_state *os,int needed){
+ if(os->body_storage<=os->body_fill+needed){
+ os->body_storage+=(needed+1024);
+ os->body_data=realloc(os->body_data,os->body_storage);
+ }
+}
+
+static void _os_lacing_expand(ogg_stream_state *os,int needed){
+ if(os->lacing_storage<=os->lacing_fill+needed){
+ os->lacing_storage+=(needed+32);
+ os->lacing_vals=realloc(os->lacing_vals,os->lacing_storage*sizeof(int));
+ os->pcm_vals=realloc(os->pcm_vals,os->lacing_storage*sizeof(int64_t));
+ }
+}
+
+/* checksum the page */
+/* Direct table CRC; note that this will be faster in the future if we
+ perform the checksum silmultaneously with other copies */
+
+static void _os_checksum(ogg_page *og){
+ unsigned vorbis_size32_t crc_reg=0;
+ int i;
+
+ for(i=0;i<og->header_len;i++)
+ crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->header[i]];
+ for(i=0;i<og->body_len;i++)
+ crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->body[i]];
+
+ og->header[22]=crc_reg&0xff;
+ og->header[23]=(crc_reg>>8)&0xff;
+ og->header[24]=(crc_reg>>16)&0xff;
+ og->header[25]=(crc_reg>>24)&0xff;
+}
+
+/* submit data to the internal buffer of the framing engine */
+int ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){
+ int lacing_vals=op->bytes/255+1,i;
+
+ if(os->body_returned){
+ /* advance packet data according to the body_returned pointer. We
+ had to keep it around to return a pointer into the buffer last
+ call */
+
+ os->body_fill-=os->body_returned;
+ if(os->body_fill)
+ memmove(os->body_data,os->body_data+os->body_returned,
+ os->body_fill*sizeof(char));
+ os->body_returned=0;
+ }
+
+ /* make sure we have the buffer storage */
+ _os_body_expand(os,op->bytes);
+ _os_lacing_expand(os,lacing_vals);
+
+ /* Copy in the submitted packet. Yes, the copy is a waste; this is
+ the liability of overly clean abstraction for the time being. It
+ will actually be fairly easy to eliminate the extra copy in the
+ future */
+
+ memcpy(os->body_data+os->body_fill,op->packet,op->bytes);
+ os->body_fill+=op->bytes;
+
+ /* Store lacing vals for this packet */
+ for(i=0;i<lacing_vals-1;i++){
+ os->lacing_vals[os->lacing_fill+i]=255;
+ os->pcm_vals[os->lacing_fill+i]=os->pcmpos;
+ }
+ os->lacing_vals[os->lacing_fill+i]=(op->bytes)%255;
+ os->pcmpos=os->pcm_vals[os->lacing_fill+i]=op->frameno;
+
+ /* flag the first segment as the beginning of the packet */
+ os->lacing_vals[os->lacing_fill]|= 0x100;
+
+ os->lacing_fill+=lacing_vals;
+
+ /* for the sake of completeness */
+ os->packetno++;
+
+ if(op->e_o_s)os->e_o_s=1;
+
+ return(0);
+}
+
+/* This will flush remaining packets into a page (returning nonzero),
+ even if there is not enough data to trigger a flush normally
+ (undersized page). If there are no packets or partial packets to
+ flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will
+ try to flush a normal sized page like ogg_stream_pageout; a call to
+ ogg_stream_flush does not gurantee that all packets have flushed.
+ Only a return value of 0 from ogg_stream_flush indicates all packet
+ data is flushed into pages.
+
+ ogg_stream_page will flush the last page in a stream even if it's
+ undersized; you almost certainly want to use ogg_stream_pageout
+ (and *not* ogg_stream_flush) unless you need to flush an undersized
+ page in the middle of a stream for some reason. */
+
+int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){
+ int i;
+ int vals=0;
+ int maxvals=(os->lacing_fill>255?255:os->lacing_fill);
+ int bytes=0;
+ long acc=0;
+ int64_t pcm_pos=os->pcm_vals[0];
+
+ if(maxvals==0)return(0);
+
+ /* construct a page */
+ /* decide how many segments to include */
+
+ /* If this is the initial header case, the first page must only include
+ the initial header packet */
+ if(os->b_o_s==0){ /* 'initial header page' case */
+ pcm_pos=0;
+ for(vals=0;vals<maxvals;vals++){
+ if((os->lacing_vals[vals]&0x0ff)<255){
+ vals++;
+ break;
+ }
+ }
+ }else{
+ for(vals=0;vals<maxvals;vals++){
+ if(acc>4096)break;
+ acc+=os->lacing_vals[vals]&0x0ff;
+ pcm_pos=os->pcm_vals[vals];
+ }
+ }
+
+ /* construct the header in temp storage */
+ memcpy(os->header,"OggS",4);
+
+ /* stream structure version */
+ os->header[4]=0x00;
+
+ /* continued packet flag? */
+ os->header[5]=0x00;
+ if((os->lacing_vals[0]&0x100)==0)os->header[5]|=0x01;
+ /* first page flag? */
+ if(os->b_o_s==0)os->header[5]|=0x02;
+ /* last page flag? */
+ if(os->e_o_s && os->lacing_fill==vals)os->header[5]|=0x04;
+ os->b_o_s=1;
+
+ /* 64 bits of PCM position */
+ for(i=6;i<14;i++){
+ os->header[i]=(pcm_pos&0xff);
+ pcm_pos>>=8;
+ }
+
+ /* 32 bits of stream serial number */
+ {
+ long serialno=os->serialno;
+ for(i=14;i<18;i++){
+ os->header[i]=(serialno&0xff);
+ serialno>>=8;
+ }
+ }
+
+ /* 32 bits of page counter (we have both counter and page header
+ because this val can roll over) */
+ if(os->pageno==-1)os->pageno=0; /* because someone called
+ stream_reset; this would be a
+ strange thing to do in an
+ encode stream, but it has
+ plausible uses */
+ {
+ long pageno=os->pageno++;
+ for(i=18;i<22;i++){
+ os->header[i]=(pageno&0xff);
+ pageno>>=8;
+ }
+ }
+
+ /* zero for computation; filled in later */
+ os->header[22]=0;
+ os->header[23]=0;
+ os->header[24]=0;
+ os->header[25]=0;
+
+ /* segment table */
+ os->header[26]=vals&0xff;
+ for(i=0;i<vals;i++)
+ bytes+=os->header[i+27]=(os->lacing_vals[i]&0xff);
+
+ /* set pointers in the ogg_page struct */
+ og->header=os->header;
+ og->header_len=os->header_fill=vals+27;
+ og->body=os->body_data+os->body_returned;
+ og->body_len=bytes;
+
+ /* advance the lacing data and set the body_returned pointer */
+
+ os->lacing_fill-=vals;
+ memmove(os->lacing_vals,os->lacing_vals+vals,os->lacing_fill*sizeof(int));
+ memmove(os->pcm_vals,os->pcm_vals+vals,os->lacing_fill*sizeof(int64_t));
+ os->body_returned+=bytes;
+
+ /* calculate the checksum */
+
+ _os_checksum(og);
+
+ /* done */
+ return(1);
+}
+
+
+/* This constructs pages from buffered packet segments. The pointers
+returned are to static buffers; do not free. The returned buffers are
+good only until the next call (using the same ogg_stream_state) */
+
+int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og){
+
+ if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */
+ os->body_fill-os->body_returned > 4096 ||/* 'page nominal size' case */
+ os->lacing_fill>=255 || /* 'segment table full' case */
+ (os->lacing_fill&&!os->b_o_s)){ /* 'initial header page' case */
+
+ return(ogg_stream_flush(os,og));
+ }
+
+ /* not enough data to construct a page and not end of stream */
+ return(0);
+}
+
+int ogg_stream_eof(ogg_stream_state *os){
+ return os->e_o_s;
+}
+
+/* DECODING PRIMITIVES: packet streaming layer **********************/
+
+/* This has two layers to place more of the multi-serialno and paging
+ control in the application's hands. First, we expose a data buffer
+ using ogg_sync_buffer(). The app either copies into the
+ buffer, or passes it directly to read(), etc. We then call
+ ogg_sync_wrote() to tell how many bytes we just added.
+
+ Pages are returned (pointers into the buffer in ogg_sync_state)
+ by ogg_sync_pageout(). The page is then submitted to
+ ogg_stream_pagein() along with the appropriate
+ ogg_stream_state* (ie, matching serialno). We then get raw
+ packets out calling ogg_stream_packetout() with a
+ ogg_stream_state. See the 'frame-prog.txt' docs for details and
+ example code. */
+
+/* initialize the struct to a known state */
+int ogg_sync_init(ogg_sync_state *oy){
+ if(oy){
+ memset(oy,0,sizeof(ogg_sync_state));
+ _ogg_crc_init();
+ }
+ return(0);
+}
+
+/* clear non-flat storage within */
+int ogg_sync_clear(ogg_sync_state *oy){
+ if(oy){
+ if(oy->data)free(oy->data);
+ ogg_sync_init(oy);
+ }
+ return(0);
+}
+
+char *ogg_sync_buffer(ogg_sync_state *oy, long size){
+
+ /* first, clear out any space that has been previously returned */
+ if(oy->returned){
+ oy->fill-=oy->returned;
+ if(oy->fill>0)
+ memmove(oy->data,oy->data+oy->returned,
+ (oy->fill)*sizeof(char));
+ oy->returned=0;
+ }
+
+ if(size>oy->storage-oy->fill){
+ /* We need to extend the internal buffer */
+ long newsize=size+oy->fill+4096; /* an extra page to be nice */
+
+ if(oy->data)
+ oy->data=realloc(oy->data,newsize);
+ else
+ oy->data=malloc(newsize);
+ oy->storage=newsize;
+ }
+
+ /* expose a segment at least as large as requested at the fill mark */
+ return((char *)oy->data+oy->fill);
+}
+
+int ogg_sync_wrote(ogg_sync_state *oy, long bytes){
+ if(oy->fill+bytes>oy->storage)return(-1);
+ oy->fill+=bytes;
+ return(0);
+}
+
+/* sync the stream. This is meant to be useful for finding page
+ boundaries.
+
+ return values for this:
+ -n) skipped n bytes
+ 0) page not ready; more data (no bytes skipped)
+ n) page synced at current location; page length n bytes
+
+*/
+
+long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){
+ unsigned char *page=oy->data+oy->returned;
+ unsigned char *next;
+ long bytes=oy->fill-oy->returned;
+
+ if(oy->headerbytes==0){
+ int headerbytes,i;
+ if(bytes<27)return(0); /* not enough for a header */
+
+ /* verify capture pattern */
+ if(memcmp(page,"OggS",4))goto sync_fail;
+
+ headerbytes=page[26]+27;
+ if(bytes<headerbytes)return(0); /* not enough for header + seg table */
+
+ /* count up body length in the segment table */
+
+ for(i=0;i<page[26];i++)
+ oy->bodybytes+=page[27+i];
+ oy->headerbytes=headerbytes;
+ }
+
+ if(oy->bodybytes+oy->headerbytes>bytes)return(0);
+
+ /* The whole test page is buffered. Verify the checksum */
+ {
+ /* Grab the checksum bytes, set the header field to zero */
+ char chksum[4];
+ ogg_page log;
+
+ memcpy(chksum,page+22,4);
+ memset(page+22,0,4);
+
+ /* set up a temp page struct and recompute the checksum */
+ log.header=page;
+ log.header_len=oy->headerbytes;
+ log.body=page+oy->headerbytes;
+ log.body_len=oy->bodybytes;
+ _os_checksum(&log);
+
+ /* Compare */
+ if(memcmp(chksum,page+22,4)){
+ /* D'oh. Mismatch! Corrupt page (or miscapture and not a page
+ at all) */
+ /* replace the computed checksum with the one actually read in */
+ memcpy(page+22,chksum,4);
+
+ /* Bad checksum. Lose sync */
+ goto sync_fail;
+ }
+ }
+
+ /* yes, have a whole page all ready to go */
+ {
+ unsigned char *page=oy->data+oy->returned;
+ long bytes;
+
+ if(og){
+ og->header=page;
+ og->header_len=oy->headerbytes;
+ og->body=page+oy->headerbytes;
+ og->body_len=oy->bodybytes;
+ }
+
+ oy->unsynced=0;
+ oy->returned+=(bytes=oy->headerbytes+oy->bodybytes);
+ oy->headerbytes=0;
+ oy->bodybytes=0;
+ return(bytes);
+ }
+
+ sync_fail:
+
+ oy->headerbytes=0;
+ oy->bodybytes=0;
+
+ /* search for possible capture */
+ next=memchr(page+1,'O',bytes-1);
+ if(!next)
+ next=oy->data+oy->fill;
+
+ oy->returned=next-oy->data;
+ return(-(next-page));
+}
+
+/* sync the stream and get a page. Keep trying until we find a page.
+ Supress 'sync errors' after reporting the first.
+
+ return values:
+ -1) recapture (hole in data)
+ 0) need more data
+ 1) page returned
+
+ Returns pointers into buffered data; invalidated by next call to
+ _stream, _clear, _init, or _buffer */
+
+int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){
+
+ /* all we need to do is verify a page at the head of the stream
+ buffer. If it doesn't verify, we look for the next potential
+ frame */
+
+ while(1){
+ long ret=ogg_sync_pageseek(oy,og);
+ if(ret>0){
+ /* have a page */
+ return(1);
+ }
+ if(ret==0){
+ /* need more data */
+ return(0);
+ }
+
+ /* head did not start a synced page... skipped some bytes */
+ if(!oy->unsynced){
+ oy->unsynced=1;
+ return(-1);
+ }
+
+ /* loop. keep looking */
+
+ }
+}
+
+/* add the incoming page to the stream state; we decompose the page
+ into packet segments here as well. */
+
+int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){
+ unsigned char *header=og->header;
+ unsigned char *body=og->body;
+ long bodysize=og->body_len;
+ int segptr=0;
+
+ int version=ogg_page_version(og);
+ int continued=ogg_page_continued(og);
+ int bos=ogg_page_bos(og);
+ int eos=ogg_page_eos(og);
+ int64_t pcmpos=ogg_page_frameno(og);
+ int serialno=ogg_page_serialno(og);
+ int pageno=ogg_page_pageno(og);
+ int segments=header[26];
+
+ /* clean up 'returned data' */
+ {
+ long lr=os->lacing_returned;
+ long br=os->body_returned;
+
+ /* body data */
+ if(br){
+ os->body_fill-=br;
+ if(os->body_fill)
+ memmove(os->body_data,os->body_data+br,os->body_fill);
+ os->body_returned=0;
+ }
+
+ if(lr){
+ /* segment table */
+ if(os->lacing_fill-lr){
+ memmove(os->lacing_vals,os->lacing_vals+lr,
+ (os->lacing_fill-lr)*sizeof(int));
+ memmove(os->pcm_vals,os->pcm_vals+lr,
+ (os->lacing_fill-lr)*sizeof(int64_t));
+ }
+ os->lacing_fill-=lr;
+ os->lacing_packet-=lr;
+ os->lacing_returned=0;
+ }
+ }
+
+ /* check the serial number */
+ if(serialno!=os->serialno)return(-1);
+ if(version>0)return(-1);
+
+ _os_lacing_expand(os,segments+1);
+
+ /* are we in sequence? */
+ if(pageno!=os->pageno){
+ int i;
+
+ /* unroll previous partial packet (if any) */
+ for(i=os->lacing_packet;i<os->lacing_fill;i++)
+ os->body_fill-=os->lacing_vals[i]&0xff;
+ os->lacing_fill=os->lacing_packet;
+
+ /* make a note of dropped data in segment table */
+ if(os->pageno!=-1){
+ os->lacing_vals[os->lacing_fill++]=0x400;
+ os->lacing_packet++;
+ }
+
+ /* are we a 'continued packet' page? If so, we'll need to skip
+ some segments */
+ if(continued){
+ bos=0;
+ for(;segptr<segments;segptr++){
+ int val=header[27+segptr];
+ body+=val;
+ bodysize-=val;
+ if(val<255){
+ segptr++;
+ break;
+ }
+ }
+ }
+ }
+
+ if(bodysize){
+ _os_body_expand(os,bodysize);
+ memcpy(os->body_data+os->body_fill,body,bodysize);
+ os->body_fill+=bodysize;
+ }
+
+ {
+ int saved=-1;
+ while(segptr<segments){
+ int val=header[27+segptr];
+ os->lacing_vals[os->lacing_fill]=val;
+ os->pcm_vals[os->lacing_fill]=-1;
+
+ if(bos){
+ os->lacing_vals[os->lacing_fill]|=0x100;
+ bos=0;
+ }
+
+ if(val<255)saved=os->lacing_fill;
+
+ os->lacing_fill++;
+ segptr++;
+
+ if(val<255)os->lacing_packet=os->lacing_fill;
+ }
+
+ /* set the pcmpos on the last pcmval of the last full packet */
+ if(saved!=-1){
+ os->pcm_vals[saved]=pcmpos;
+ }
+
+ }
+
+ if(eos){
+ os->e_o_s=1;
+ if(os->lacing_fill>0)
+ os->lacing_vals[os->lacing_fill-1]|=0x200;
+ }
+
+ os->pageno=pageno+1;
+
+ return(0);
+}
+
+/* clear things to an initial state. Good to call, eg, before seeking */
+int ogg_sync_reset(ogg_sync_state *oy){
+ oy->fill=0;
+ oy->returned=0;
+ oy->unsynced=0;
+ oy->headerbytes=0;
+ oy->bodybytes=0;
+ return(0);
+}
+
+int ogg_stream_reset(ogg_stream_state *os){
+ os->body_fill=0;
+ os->body_returned=0;
+
+ os->lacing_fill=0;
+ os->lacing_packet=0;
+ os->lacing_returned=0;
+
+ os->header_fill=0;
+
+ os->e_o_s=0;
+ os->b_o_s=0;
+ os->pageno=-1;
+ os->packetno=0;
+ os->pcmpos=0;
+
+ return(0);
+}
+
+int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op){
+
+ /* The last part of decode. We have the stream broken into packet
+ segments. Now we need to group them into packets (or return the
+ out of sync markers) */
+
+ int ptr=os->lacing_returned;
+
+ if(os->lacing_packet<=ptr)return(0);
+
+ if(os->lacing_vals[ptr]&0x400){
+ /* We lost sync here; let the app know */
+ os->lacing_returned++;
+
+ /* we need to tell the codec there's a gap; it might need to
+ handle previous packet dependencies. */
+ os->packetno++;
+ return(-1);
+ }
+
+ /* Gather the whole packet. We'll have no holes or a partial packet */
+ {
+ int size=os->lacing_vals[ptr]&0xff;
+ int bytes=0;
+
+ op->packet=os->body_data+os->body_returned;
+ op->e_o_s=os->lacing_vals[ptr]&0x200; /* last packet of the stream? */
+ op->b_o_s=os->lacing_vals[ptr]&0x100; /* first packet of the stream? */
+ bytes+=size;
+
+ while(size==255){
+ int val=os->lacing_vals[++ptr];
+ size=val&0xff;
+ if(val&0x200)op->e_o_s=0x200;
+ bytes+=size;
+ }
+
+ op->packetno=os->packetno;
+ op->frameno=os->pcm_vals[ptr];
+ op->bytes=bytes;
+
+ os->body_returned+=bytes;
+ os->lacing_returned=ptr+1;
+ }
+ os->packetno++;
+ return(1);
+}
+
+#ifdef _V_SELFTEST
+#include <stdio.h>
+
+ogg_stream_state os_en, os_de;
+ogg_sync_state oy;
+
+void checkpacket(ogg_packet *op,int len, int no, int pos){
+ long j;
+ static int sequence=0;
+ static int lastno=0;
+
+ if(op->bytes!=len){
+ fprintf(stderr,"incorrect packet length!\n");
+ exit(1);
+ }
+ if(op->frameno!=pos){
+ fprintf(stderr,"incorrect packet position!\n");
+ exit(1);
+ }
+
+ /* packet number just follows sequence/gap; adjust the input number
+ for that */
+ if(no==0){
+ sequence=0;
+ }else{
+ sequence++;
+ if(no>lastno+1)
+ sequence++;
+ }
+ lastno=no;
+ if(op->packetno!=sequence){
+ fprintf(stderr,"incorrect packet sequence %ld != %d\n",
+ (long)(op->packetno),sequence);
+ exit(1);
+ }
+
+ /* Test data */
+ for(j=0;j<op->bytes;j++)
+ if(op->packet[j]!=((j+no)&0xff)){
+ fprintf(stderr,"body data mismatch (1) at pos %ld: %x!=%lx!\n\n",
+ j,op->packet[j],(j+no)&0xff);
+ exit(1);
+ }
+}
+
+void check_page(unsigned char *data,const int *header,ogg_page *og){
+ long j;
+ /* Test data */
+ for(j=0;j<og->body_len;j++)
+ if(og->body[j]!=data[j]){
+ fprintf(stderr,"body data mismatch (2) at pos %ld: %x!=%x!\n\n",
+ j,data[j],og->body[j]);
+ exit(1);
+ }
+
+ /* Test header */
+ for(j=0;j<og->header_len;j++){
+ if(og->header[j]!=header[j]){
+ fprintf(stderr,"header content mismatch at pos %ld:\n",j);
+ for(j=0;j<header[26]+27;j++)
+ fprintf(stderr," (%ld)%02x:%02x",j,header[j],og->header[j]);
+ fprintf(stderr,"\n");
+ exit(1);
+ }
+ }
+ if(og->header_len!=header[26]+27){
+ fprintf(stderr,"header length incorrect! (%ld!=%d)\n",
+ og->header_len,header[26]+27);
+ exit(1);
+ }
+}
+
+void print_header(ogg_page *og){
+ int j;
+ fprintf(stderr,"\nHEADER:\n");
+ fprintf(stderr," capture: %c %c %c %c version: %d flags: %x\n",
+ og->header[0],og->header[1],og->header[2],og->header[3],
+ (int)og->header[4],(int)og->header[5]);
+
+ fprintf(stderr," pcmpos: %d serialno: %d pageno: %d\n",
+ (og->header[9]<<24)|(og->header[8]<<16)|
+ (og->header[7]<<8)|og->header[6],
+ (og->header[17]<<24)|(og->header[16]<<16)|
+ (og->header[15]<<8)|og->header[14],
+ (og->header[21]<<24)|(og->header[20]<<16)|
+ (og->header[19]<<8)|og->header[18]);
+
+ fprintf(stderr," checksum: %02x:%02x:%02x:%02x\n segments: %d (",
+ (int)og->header[22],(int)og->header[23],
+ (int)og->header[24],(int)og->header[25],
+ (int)og->header[26]);
+
+ for(j=27;j<og->header_len;j++)
+ fprintf(stderr,"%d ",(int)og->header[j]);
+ fprintf(stderr,")\n\n");
+}
+
+void copy_page(ogg_page *og){
+ unsigned char *temp=malloc(og->header_len);
+ memcpy(temp,og->header,og->header_len);
+ og->header=temp;
+
+ temp=malloc(og->body_len);
+ memcpy(temp,og->body,og->body_len);
+ og->body=temp;
+}
+
+void error(void){
+ fprintf(stderr,"error!\n");
+ exit(1);
+}
+
+/* 17 only */
+const int head1_0[] = {0x4f,0x67,0x67,0x53,0,0x06,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,0,0,0,0,
+ 0x15,0xed,0xec,0x91,
+ 1,
+ 17};
+
+/* 17, 254, 255, 256, 500, 510, 600 byte, pad */
+const int head1_1[] = {0x4f,0x67,0x67,0x53,0,0x02,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,0,0,0,0,
+ 0x59,0x10,0x6c,0x2c,
+ 1,
+ 17};
+const int head2_1[] = {0x4f,0x67,0x67,0x53,0,0x04,
+ 0x07,0x18,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,1,0,0,0,
+ 0x89,0x33,0x85,0xce,
+ 13,
+ 254,255,0,255,1,255,245,255,255,0,
+ 255,255,90};
+
+/* nil packets; beginning,middle,end */
+const int head1_2[] = {0x4f,0x67,0x67,0x53,0,0x02,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,0,0,0,0,
+ 0xff,0x7b,0x23,0x17,
+ 1,
+ 0};
+const int head2_2[] = {0x4f,0x67,0x67,0x53,0,0x04,
+ 0x07,0x28,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,1,0,0,0,
+ 0x5c,0x3f,0x66,0xcb,
+ 17,
+ 17,254,255,0,0,255,1,0,255,245,255,255,0,
+ 255,255,90,0};
+
+/* large initial packet */
+const int head1_3[] = {0x4f,0x67,0x67,0x53,0,0x02,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,0,0,0,0,
+ 0x01,0x27,0x31,0xaa,
+ 18,
+ 255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,10};
+
+const int head2_3[] = {0x4f,0x67,0x67,0x53,0,0x04,
+ 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,1,0,0,0,
+ 0x7f,0x4e,0x8a,0xd2,
+ 4,
+ 255,4,255,0};
+
+
+/* continuing packet test */
+const int head1_4[] = {0x4f,0x67,0x67,0x53,0,0x02,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,0,0,0,0,
+ 0xff,0x7b,0x23,0x17,
+ 1,
+ 0};
+
+const int head2_4[] = {0x4f,0x67,0x67,0x53,0,0x00,
+ 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,1,0,0,0,
+ 0x34,0x24,0xd5,0x29,
+ 17,
+ 255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255};
+
+const int head3_4[] = {0x4f,0x67,0x67,0x53,0,0x05,
+ 0x07,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,2,0,0,0,
+ 0xc8,0xc3,0xcb,0xed,
+ 5,
+ 10,255,4,255,0};
+
+
+/* page with the 255 segment limit */
+const int head1_5[] = {0x4f,0x67,0x67,0x53,0,0x02,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,0,0,0,0,
+ 0xff,0x7b,0x23,0x17,
+ 1,
+ 0};
+
+const int head2_5[] = {0x4f,0x67,0x67,0x53,0,0x00,
+ 0x07,0xfc,0x03,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,1,0,0,0,
+ 0xed,0x2a,0x2e,0xa7,
+ 255,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10};
+
+const int head3_5[] = {0x4f,0x67,0x67,0x53,0,0x04,
+ 0x07,0x00,0x04,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,2,0,0,0,
+ 0x6c,0x3b,0x82,0x3d,
+ 1,
+ 50};
+
+
+/* packet that overspans over an entire page */
+const int head1_6[] = {0x4f,0x67,0x67,0x53,0,0x02,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,0,0,0,0,
+ 0xff,0x7b,0x23,0x17,
+ 1,
+ 0};
+
+const int head2_6[] = {0x4f,0x67,0x67,0x53,0,0x00,
+ 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,1,0,0,0,
+ 0x3c,0xd9,0x4d,0x3f,
+ 17,
+ 100,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255};
+
+const int head3_6[] = {0x4f,0x67,0x67,0x53,0,0x01,
+ 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,2,0,0,0,
+ 0xbd,0xd5,0xb5,0x8b,
+ 17,
+ 255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255};
+
+const int head4_6[] = {0x4f,0x67,0x67,0x53,0,0x05,
+ 0x07,0x10,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,3,0,0,0,
+ 0xef,0xdd,0x88,0xde,
+ 7,
+ 255,255,75,255,4,255,0};
+
+/* packet that overspans over an entire page */
+const int head1_7[] = {0x4f,0x67,0x67,0x53,0,0x02,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,0,0,0,0,
+ 0xff,0x7b,0x23,0x17,
+ 1,
+ 0};
+
+const int head2_7[] = {0x4f,0x67,0x67,0x53,0,0x00,
+ 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,1,0,0,0,
+ 0x3c,0xd9,0x4d,0x3f,
+ 17,
+ 100,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255};
+
+const int head3_7[] = {0x4f,0x67,0x67,0x53,0,0x05,
+ 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x02,0x03,0x04,2,0,0,0,
+ 0xd4,0xe0,0x60,0xe5,
+ 1,0};
+
+void test_pack(const int *pl, const int **headers){
+ unsigned char *data=malloc(1024*1024); /* for scripted test cases only */
+ long inptr=0;
+ long outptr=0;
+ long deptr=0;
+ long depacket=0;
+ long pcm_pos=7;
+ int i,j,packets,pageno=0,pageout=0;
+ int eosflag=0;
+ int bosflag=0;
+
+ ogg_stream_reset(&os_en);
+ ogg_stream_reset(&os_de);
+ ogg_sync_reset(&oy);
+
+ for(packets=0;;packets++)if(pl[packets]==-1)break;
+
+ for(i=0;i<packets;i++){
+ /* construct a test packet */
+ ogg_packet op;
+ int len=pl[i];
+
+ op.packet=data+inptr;
+ op.bytes=len;
+ op.e_o_s=(pl[i+1]<0?1:0);
+ op.frameno=pcm_pos;
+
+ pcm_pos+=1024;
+
+ for(j=0;j<len;j++)data[inptr++]=i+j;
+
+ /* submit the test packet */
+ ogg_stream_packetin(&os_en,&op);
+
+ /* retrieve any finished pages */
+ {
+ ogg_page og;
+
+ while(ogg_stream_pageout(&os_en,&og)){
+ /* We have a page. Check it carefully */
+
+ fprintf(stderr,"%d, ",pageno);
+
+ if(headers[pageno]==NULL){
+ fprintf(stderr,"coded too many pages!\n");
+ exit(1);
+ }
+
+ check_page(data+outptr,headers[pageno],&og);
+
+ outptr+=og.body_len;
+ pageno++;
+
+ /* have a complete page; submit it to sync/decode */
+
+ {
+ ogg_page og_de;
+ ogg_packet op_de;
+ char *buf=ogg_sync_buffer(&oy,og.header_len+og.body_len);
+ memcpy(buf,og.header,og.header_len);
+ memcpy(buf+og.header_len,og.body,og.body_len);
+ ogg_sync_wrote(&oy,og.header_len+og.body_len);
+
+ while(ogg_sync_pageout(&oy,&og_de)>0){
+ /* got a page. Happy happy. Verify that it's good. */
+
+ check_page(data+deptr,headers[pageout],&og_de);
+ deptr+=og_de.body_len;
+ pageout++;
+
+ /* submit it to deconstitution */
+ ogg_stream_pagein(&os_de,&og_de);
+
+ /* packets out? */
+ while(ogg_stream_packetout(&os_de,&op_de)>0){
+
+ /* verify the packet! */
+ /* check data */
+ if(memcmp(data+depacket,op_de.packet,op_de.bytes)){
+ fprintf(stderr,"packet data mismatch in decode! pos=%ld\n",
+ depacket);
+ exit(1);
+ }
+ /* check bos flag */
+ if(bosflag==0 && op_de.b_o_s==0){
+ fprintf(stderr,"b_o_s flag not set on packet!\n");
+ exit(1);
+ }
+ if(bosflag && op_de.b_o_s){
+ fprintf(stderr,"b_o_s flag incorrectly set on packet!\n");
+ exit(1);
+ }
+ bosflag=1;
+ depacket+=op_de.bytes;
+
+ /* check eos flag */
+ if(eosflag){
+ fprintf(stderr,"Multiple decoded packets with eos flag!\n");
+ exit(1);
+ }
+
+ if(op_de.e_o_s)eosflag=1;
+
+ /* check pcmpos flag */
+ if(op_de.frameno!=-1){
+ fprintf(stderr," pcm:%ld ",(long)op_de.frameno);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ free(data);
+ if(headers[pageno]!=NULL){
+ fprintf(stderr,"did not write last page!\n");
+ exit(1);
+ }
+ if(headers[pageout]!=NULL){
+ fprintf(stderr,"did not decode last page!\n");
+ exit(1);
+ }
+ if(inptr!=outptr){
+ fprintf(stderr,"encoded page data incomplete!\n");
+ exit(1);
+ }
+ if(inptr!=deptr){
+ fprintf(stderr,"decoded page data incomplete!\n");
+ exit(1);
+ }
+ if(inptr!=depacket){
+ fprintf(stderr,"decoded packet data incomplete!\n");
+ exit(1);
+ }
+ if(!eosflag){
+ fprintf(stderr,"Never got a packet with EOS set!\n");
+ exit(1);
+ }
+ fprintf(stderr,"ok.\n");
+}
+
+int main(void){
+
+ ogg_stream_init(&os_en,0x04030201);
+ ogg_stream_init(&os_de,0x04030201);
+ ogg_sync_init(&oy);
+
+ /* Exercise each code path in the framing code. Also verify that
+ the checksums are working. */
+
+ {
+ /* 17 only */
+ const int packets[]={17, -1};
+ const int *headret[]={head1_0,NULL};
+
+ fprintf(stderr,"testing single page encoding... ");
+ test_pack(packets,headret);
+ }
+
+ {
+ /* 17, 254, 255, 256, 500, 510, 600 byte, pad */
+ const int packets[]={17, 254, 255, 256, 500, 510, 600, -1};
+ const int *headret[]={head1_1,head2_1,NULL};
+
+ fprintf(stderr,"testing basic page encoding... ");
+ test_pack(packets,headret);
+ }
+
+ {
+ /* nil packets; beginning,middle,end */
+ const int packets[]={0,17, 254, 255, 0, 256, 0, 500, 510, 600, 0, -1};
+ const int *headret[]={head1_2,head2_2,NULL};
+
+ fprintf(stderr,"testing basic nil packets... ");
+ test_pack(packets,headret);
+ }
+
+ {
+ /* large initial packet */
+ const int packets[]={4345,259,255,-1};
+ const int *headret[]={head1_3,head2_3,NULL};
+
+ fprintf(stderr,"testing initial-packet lacing > 4k... ");
+ test_pack(packets,headret);
+ }
+
+ {
+ /* continuing packet test */
+ const int packets[]={0,4345,259,255,-1};
+ const int *headret[]={head1_4,head2_4,head3_4,NULL};
+
+ fprintf(stderr,"testing single packet page span... ");
+ test_pack(packets,headret);
+ }
+
+ /* page with the 255 segment limit */
+ {
+
+ const int packets[]={0,10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,50,-1};
+ const int *headret[]={head1_5,head2_5,head3_5,NULL};
+
+ fprintf(stderr,"testing max packet segments... ");
+ test_pack(packets,headret);
+ }
+
+ {
+ /* packet that overspans over an entire page */
+ const int packets[]={0,100,9000,259,255,-1};
+ const int *headret[]={head1_6,head2_6,head3_6,head4_6,NULL};
+
+ fprintf(stderr,"testing very large packets... ");
+ test_pack(packets,headret);
+ }
+
+ {
+ /* term only page. why not? */
+ const int packets[]={0,100,4080,-1};
+ const int *headret[]={head1_7,head2_7,head3_7,NULL};
+
+ fprintf(stderr,"testing zero data page (1 nil packet)... ");
+ test_pack(packets,headret);
+ }
+
+
+
+ {
+ /* build a bunch of pages for testing */
+ unsigned char *data=malloc(1024*1024);
+ int pl[]={0,100,4079,2956,2057,76,34,912,0,234,1000,1000,1000,300,-1};
+ int inptr=0,i,j;
+ ogg_page og[5];
+
+ ogg_stream_reset(&os_en);
+
+ for(i=0;pl[i]!=-1;i++){
+ ogg_packet op;
+ int len=pl[i];
+
+ op.packet=data+inptr;
+ op.bytes=len;
+ op.e_o_s=(pl[i+1]<0?1:0);
+ op.frameno=(i+1)*1000;
+
+ for(j=0;j<len;j++)data[inptr++]=i+j;
+ ogg_stream_packetin(&os_en,&op);
+ }
+
+ free(data);
+
+ /* retrieve finished pages */
+ for(i=0;i<5;i++){
+ if(ogg_stream_pageout(&os_en,&og[i])==0){
+ fprintf(stderr,"Too few pages output building sync tests!\n");
+ exit(1);
+ }
+ copy_page(&og[i]);
+ }
+
+ /* Test lost pages on pagein/packetout: no rollback */
+ {
+ ogg_page temp;
+ ogg_packet test;
+
+ fprintf(stderr,"Testing loss of pages... ");
+
+ ogg_sync_reset(&oy);
+ ogg_stream_reset(&os_de);
+ for(i=0;i<5;i++){
+ memcpy(ogg_sync_buffer(&oy,og[i].header_len),og[i].header,
+ og[i].header_len);
+ ogg_sync_wrote(&oy,og[i].header_len);
+ memcpy(ogg_sync_buffer(&oy,og[i].body_len),og[i].body,og[i].body_len);
+ ogg_sync_wrote(&oy,og[i].body_len);
+ }
+
+ ogg_sync_pageout(&oy,&temp);
+ ogg_stream_pagein(&os_de,&temp);
+ ogg_sync_pageout(&oy,&temp);
+ ogg_stream_pagein(&os_de,&temp);
+ ogg_sync_pageout(&oy,&temp);
+ /* skip */
+ ogg_sync_pageout(&oy,&temp);
+ ogg_stream_pagein(&os_de,&temp);
+
+ /* do we get the expected results/packets? */
+
+ if(ogg_stream_packetout(&os_de,&test)!=1)error();
+ checkpacket(&test,0,0,0);
+ if(ogg_stream_packetout(&os_de,&test)!=1)error();
+ checkpacket(&test,100,1,-1);
+ if(ogg_stream_packetout(&os_de,&test)!=1)error();
+ checkpacket(&test,4079,2,3000);
+ if(ogg_stream_packetout(&os_de,&test)!=-1){
+ fprintf(stderr,"Error: loss of page did not return error\n");
+ exit(1);
+ }
+ if(ogg_stream_packetout(&os_de,&test)!=1)error();
+ checkpacket(&test,76,5,-1);
+ if(ogg_stream_packetout(&os_de,&test)!=1)error();
+ checkpacket(&test,34,6,-1);
+ fprintf(stderr,"ok.\n");
+ }
+
+ /* Test lost pages on pagein/packetout: rollback with continuation */
+ {
+ ogg_page temp;
+ ogg_packet test;
+
+ fprintf(stderr,"Testing loss of pages (rollback required)... ");
+
+ ogg_sync_reset(&oy);
+ ogg_stream_reset(&os_de);
+ for(i=0;i<5;i++){
+ memcpy(ogg_sync_buffer(&oy,og[i].header_len),og[i].header,
+ og[i].header_len);
+ ogg_sync_wrote(&oy,og[i].header_len);
+ memcpy(ogg_sync_buffer(&oy,og[i].body_len),og[i].body,og[i].body_len);
+ ogg_sync_wrote(&oy,og[i].body_len);
+ }
+
+ ogg_sync_pageout(&oy,&temp);
+ ogg_stream_pagein(&os_de,&temp);
+ ogg_sync_pageout(&oy,&temp);
+ ogg_stream_pagein(&os_de,&temp);
+ ogg_sync_pageout(&oy,&temp);
+ ogg_stream_pagein(&os_de,&temp);
+ ogg_sync_pageout(&oy,&temp);
+ /* skip */
+ ogg_sync_pageout(&oy,&temp);
+ ogg_stream_pagein(&os_de,&temp);
+
+ /* do we get the expected results/packets? */
+
+ if(ogg_stream_packetout(&os_de,&test)!=1)error();
+ checkpacket(&test,0,0,0);
+ if(ogg_stream_packetout(&os_de,&test)!=1)error();
+ checkpacket(&test,100,1,-1);
+ if(ogg_stream_packetout(&os_de,&test)!=1)error();
+ checkpacket(&test,4079,2,3000);
+ if(ogg_stream_packetout(&os_de,&test)!=1)error();
+ checkpacket(&test,2956,3,4000);
+ if(ogg_stream_packetout(&os_de,&test)!=-1){
+ fprintf(stderr,"Error: loss of page did not return error\n");
+ exit(1);
+ }
+ if(ogg_stream_packetout(&os_de,&test)!=1)error();
+ checkpacket(&test,300,13,14000);
+ fprintf(stderr,"ok.\n");
+ }
+
+ /* the rest only test sync */
+ {
+ ogg_page og_de;
+ /* Test fractional page inputs: incomplete capture */
+ fprintf(stderr,"Testing sync on partial inputs... ");
+ ogg_sync_reset(&oy);
+ memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header,
+ 3);
+ ogg_sync_wrote(&oy,3);
+ if(ogg_sync_pageout(&oy,&og_de)>0)error();
+
+ /* Test fractional page inputs: incomplete fixed header */
+ memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+3,
+ 20);
+ ogg_sync_wrote(&oy,20);
+ if(ogg_sync_pageout(&oy,&og_de)>0)error();
+
+ /* Test fractional page inputs: incomplete header */
+ memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+23,
+ 5);
+ ogg_sync_wrote(&oy,5);
+ if(ogg_sync_pageout(&oy,&og_de)>0)error();
+
+ /* Test fractional page inputs: incomplete body */
+
+ memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+28,
+ og[1].header_len-28);
+ ogg_sync_wrote(&oy,og[1].header_len-28);
+ if(ogg_sync_pageout(&oy,&og_de)>0)error();
+
+ memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body,1000);
+ ogg_sync_wrote(&oy,1000);
+ if(ogg_sync_pageout(&oy,&og_de)>0)error();
+
+ memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body+1000,
+ og[1].body_len-1000);
+ ogg_sync_wrote(&oy,og[1].body_len-1000);
+ if(ogg_sync_pageout(&oy,&og_de)<=0)error();
+
+ fprintf(stderr,"ok.\n");
+ }
+
+ /* Test fractional page inputs: page + incomplete capture */
+ {
+ ogg_page og_de;
+ fprintf(stderr,"Testing sync on 1+partial inputs... ");
+ ogg_sync_reset(&oy);
+
+ memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header,
+ og[1].header_len);
+ ogg_sync_wrote(&oy,og[1].header_len);
+
+ memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body,
+ og[1].body_len);
+ ogg_sync_wrote(&oy,og[1].body_len);
+
+ memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header,
+ 20);
+ ogg_sync_wrote(&oy,20);
+ if(ogg_sync_pageout(&oy,&og_de)<=0)error();
+ if(ogg_sync_pageout(&oy,&og_de)>0)error();
+
+ memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+20,
+ og[1].header_len-20);
+ ogg_sync_wrote(&oy,og[1].header_len-20);
+ memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body,
+ og[1].body_len);
+ ogg_sync_wrote(&oy,og[1].body_len);
+ if(ogg_sync_pageout(&oy,&og_de)<=0)error();
+
+ fprintf(stderr,"ok.\n");
+ }
+
+ /* Test recapture: garbage + page */
+ {
+ ogg_page og_de;
+ fprintf(stderr,"Testing search for capture... ");
+ ogg_sync_reset(&oy);
+
+ /* 'garbage' */
+ memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body,
+ og[1].body_len);
+ ogg_sync_wrote(&oy,og[1].body_len);
+
+ memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header,
+ og[1].header_len);
+ ogg_sync_wrote(&oy,og[1].header_len);
+
+ memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body,
+ og[1].body_len);
+ ogg_sync_wrote(&oy,og[1].body_len);
+
+ memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header,
+ 20);
+ ogg_sync_wrote(&oy,20);
+ if(ogg_sync_pageout(&oy,&og_de)>0)error();
+ if(ogg_sync_pageout(&oy,&og_de)<=0)error();
+ if(ogg_sync_pageout(&oy,&og_de)>0)error();
+
+ memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header+20,
+ og[2].header_len-20);
+ ogg_sync_wrote(&oy,og[2].header_len-20);
+ memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body,
+ og[2].body_len);
+ ogg_sync_wrote(&oy,og[2].body_len);
+ if(ogg_sync_pageout(&oy,&og_de)<=0)error();
+
+ fprintf(stderr,"ok.\n");
+ }
+
+ /* Test recapture: page + garbage + page */
+ {
+ ogg_page og_de;
+ fprintf(stderr,"Testing recapture... ");
+ ogg_sync_reset(&oy);
+
+ memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header,
+ og[1].header_len);
+ ogg_sync_wrote(&oy,og[1].header_len);
+
+ memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body,
+ og[1].body_len);
+ ogg_sync_wrote(&oy,og[1].body_len);
+
+ memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header,
+ og[2].header_len);
+ ogg_sync_wrote(&oy,og[2].header_len);
+
+ memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header,
+ og[2].header_len);
+ ogg_sync_wrote(&oy,og[2].header_len);
+
+ if(ogg_sync_pageout(&oy,&og_de)<=0)error();
+
+ memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body,
+ og[2].body_len-5);
+ ogg_sync_wrote(&oy,og[2].body_len-5);
+
+ memcpy(ogg_sync_buffer(&oy,og[3].header_len),og[3].header,
+ og[3].header_len);
+ ogg_sync_wrote(&oy,og[3].header_len);
+
+ memcpy(ogg_sync_buffer(&oy,og[3].body_len),og[3].body,
+ og[3].body_len);
+ ogg_sync_wrote(&oy,og[3].body_len);
+
+ if(ogg_sync_pageout(&oy,&og_de)>0)error();
+ if(ogg_sync_pageout(&oy,&og_de)<=0)error();
+
+ fprintf(stderr,"ok.\n");
+ }
+ }
+
+ return(0);
+}
+
+#endif
+
+
+
+
diff --git a/lib/info.c b/lib/info.c
new file mode 100644
index 00000000..c04080eb
--- /dev/null
+++ b/lib/info.c
@@ -0,0 +1,543 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE Ogg Vorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
+ * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE. *
+ * PLEASE READ THESE TERMS DISTRIBUTING. *
+ * *
+ * THE OggSQUISH SOURCE CODE IS (C) COPYRIGHT 1994-2000 *
+ * by Monty <monty@xiph.org> and The XIPHOPHORUS Company *
+ * http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: maintain the info structure, info <-> header packets
+ last mod: $Id: info.c,v 1.25.2.1 2000/08/15 08:33:44 xiphmont Exp $
+
+ ********************************************************************/
+
+/* general handling of the header and the vorbis_info structure (and
+ substructures) */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "vorbis/codec.h"
+#include "vorbis/backends.h"
+#include "bitwise.h"
+#include "sharedbook.h"
+#include "bookinternal.h"
+#include "registry.h"
+#include "window.h"
+#include "psy.h"
+#include "misc.h"
+#include "os.h"
+
+/* helpers */
+static int ilog2(unsigned int v){
+ int ret=0;
+ while(v>1){
+ ret++;
+ v>>=1;
+ }
+ return(ret);
+}
+
+static void _v_writestring(oggpack_buffer *o,char *s){
+ while(*s){
+ _oggpack_write(o,*s++,8);
+ }
+}
+
+static void _v_readstring(oggpack_buffer *o,char *buf,int bytes){
+ while(bytes--){
+ *buf++=_oggpack_read(o,8);
+ }
+}
+
+void vorbis_comment_init(vorbis_comment *vc){
+ memset(vc,0,sizeof(vorbis_comment));
+}
+
+void vorbis_comment_add(vorbis_comment *vc,char *comment){
+ vc->user_comments=realloc(vc->user_comments,
+ (vc->comments+2)*sizeof(char *));
+ vc->comment_lengths=realloc(vc->comment_lengths,
+ (vc->comments+2)*sizeof(int));
+ vc->user_comments[vc->comments]=strdup(comment);
+ vc->comment_lengths[vc->comments]=strlen(comment);
+ vc->comments++;
+ vc->user_comments[vc->comments]=NULL;
+}
+
+void vorbis_comment_add_tag(vorbis_comment *vc, char *tag, char *contents){
+ char *comment=alloca(strlen(tag)+strlen(contents)+2); /* +2 for = and \0 */
+ strcpy(comment, tag);
+ strcat(comment, "=");
+ strcat(comment, contents);
+ vorbis_comment_add(vc, comment);
+}
+
+/* This is more or less the same as strncasecmp - but that doesn't exist
+ * everywhere, and this is a fairly trivial function, so we include it */
+static int tagcompare(const char *s1, const char *s2, int n){
+ int c=0;
+ while(c < n){
+ if(toupper(s1[c]) != toupper(s2[c]))
+ return !0;
+ c++;
+ }
+ return 0;
+}
+
+char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count){
+ long i;
+ int found = 0;
+ int taglen = strlen(tag);
+ char *fulltag = alloca(taglen+ 2);
+
+ strcpy(fulltag, tag);
+ strcat(fulltag, "=");
+
+ for(i=0;i<vc->comments;i++){
+ if(!tagcompare(vc->user_comments[i], fulltag, taglen)){
+ if(count == found)
+ /* We return a pointer to the data, not a copy */
+ return vc->user_comments[i] + taglen + 1;
+ else
+ found++;
+ }
+ }
+ return NULL; /* didn't find anything */
+}
+
+void vorbis_comment_clear(vorbis_comment *vc){
+ if(vc){
+ long i;
+ for(i=0;i<vc->comments;i++)
+ if(vc->user_comments[i])free(vc->user_comments[i]);
+ if(vc->user_comments)free(vc->user_comments);
+ if(vc->comment_lengths)free(vc->comment_lengths);
+ if(vc->vendor)free(vc->vendor);
+ }
+ memset(vc,0,sizeof(vorbis_comment));
+}
+
+/* used by synthesis, which has a full, alloced vi */
+void vorbis_info_init(vorbis_info *vi){
+ memset(vi,0,sizeof(vorbis_info));
+}
+
+void vorbis_info_clear(vorbis_info *vi){
+ int i;
+
+ for(i=0;i<vi->modes;i++)
+ if(vi->mode_param[i])free(vi->mode_param[i]);
+ /*if(vi->mode_param)free(vi->mode_param);*/
+
+ for(i=0;i<vi->maps;i++) /* unpack does the range checking */
+ _mapping_P[vi->map_type[i]]->free_info(vi->map_param[i]);
+ /*if(vi->map_param)free(vi->map_param);*/
+
+ for(i=0;i<vi->times;i++) /* unpack does the range checking */
+ _time_P[vi->time_type[i]]->free_info(vi->time_param[i]);
+ /*if(vi->time_param)free(vi->time_param);*/
+
+ for(i=0;i<vi->floors;i++) /* unpack does the range checking */
+ _floor_P[vi->floor_type[i]]->free_info(vi->floor_param[i]);
+ /*if(vi->floor_param)free(vi->floor_param);*/
+
+ for(i=0;i<vi->residues;i++) /* unpack does the range checking */
+ _residue_P[vi->residue_type[i]]->free_info(vi->residue_param[i]);
+ /*if(vi->residue_param)free(vi->residue_param);*/
+
+ /* the static codebooks *are* freed if you call info_clear, because
+ decode side does alloc a 'static' codebook. Calling clear on the
+ full codebook does not clear the static codebook (that's our
+ responsibility) */
+ for(i=0;i<vi->books;i++){
+ /* just in case the decoder pre-cleared to save space */
+ if(vi->book_param[i]){
+ vorbis_staticbook_clear(vi->book_param[i]);
+ free(vi->book_param[i]);
+ }
+ }
+ /*if(vi->book_param)free(vi->book_param);*/
+
+ for(i=0;i<vi->psys;i++)
+ _vi_psy_free(vi->psy_param[i]);
+ /*if(vi->psy_param)free(vi->psy_param);*/
+
+ memset(vi,0,sizeof(vorbis_info));
+}
+
+/* Header packing/unpacking ********************************************/
+
+static int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb){
+ vi->version=_oggpack_read(opb,32);
+ if(vi->version!=0)return(-1);
+
+ vi->channels=_oggpack_read(opb,8);
+ vi->rate=_oggpack_read(opb,32);
+
+ vi->bitrate_upper=_oggpack_read(opb,32);
+ vi->bitrate_nominal=_oggpack_read(opb,32);
+ vi->bitrate_lower=_oggpack_read(opb,32);
+
+ vi->blocksizes[0]=1<<_oggpack_read(opb,4);
+ vi->blocksizes[1]=1<<_oggpack_read(opb,4);
+
+ if(vi->rate<1)goto err_out;
+ if(vi->channels<1)goto err_out;
+ if(vi->blocksizes[0]<8)goto err_out;
+ if(vi->blocksizes[1]<vi->blocksizes[0])goto err_out;
+
+ if(_oggpack_read(opb,1)!=1)goto err_out; /* EOP check */
+
+ return(0);
+ err_out:
+ vorbis_info_clear(vi);
+ return(-1);
+}
+
+static int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb){
+ int i;
+ int vendorlen=_oggpack_read(opb,32);
+ if(vendorlen<0)goto err_out;
+ vc->vendor=calloc(vendorlen+1,1);
+ _v_readstring(opb,vc->vendor,vendorlen);
+ vc->comments=_oggpack_read(opb,32);
+ if(vc->comments<0)goto err_out;
+ vc->user_comments=calloc(vc->comments+1,sizeof(char **));
+ vc->comment_lengths=calloc(vc->comments+1, sizeof(int));
+
+ for(i=0;i<vc->comments;i++){
+ int len=_oggpack_read(opb,32);
+ if(len<0)goto err_out;
+ vc->comment_lengths[i]=len;
+ vc->user_comments[i]=calloc(len+1,1);
+ _v_readstring(opb,vc->user_comments[i],len);
+ }
+ if(_oggpack_read(opb,1)!=1)goto err_out; /* EOP check */
+
+ return(0);
+ err_out:
+ vorbis_comment_clear(vc);
+ return(-1);
+}
+
+/* all of the real encoding details are here. The modes, books,
+ everything */
+static int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb){
+ int i;
+
+ /* codebooks */
+ vi->books=_oggpack_read(opb,8)+1;
+ /*vi->book_param=calloc(vi->books,sizeof(static_codebook *));*/
+ for(i=0;i<vi->books;i++){
+ vi->book_param[i]=calloc(1,sizeof(static_codebook));
+ if(vorbis_staticbook_unpack(opb,vi->book_param[i]))goto err_out;
+ }
+
+ /* time backend settings */
+ vi->times=_oggpack_read(opb,6)+1;
+ /*vi->time_type=malloc(vi->times*sizeof(int));*/
+ /*vi->time_param=calloc(vi->times,sizeof(void *));*/
+ for(i=0;i<vi->times;i++){
+ vi->time_type[i]=_oggpack_read(opb,16);
+ if(vi->time_type[i]<0 || vi->time_type[i]>=VI_TIMEB)goto err_out;
+ vi->time_param[i]=_time_P[vi->time_type[i]]->unpack(vi,opb);
+ if(!vi->time_param[i])goto err_out;
+ }
+
+ /* floor backend settings */
+ vi->floors=_oggpack_read(opb,6)+1;
+ /*vi->floor_type=malloc(vi->floors*sizeof(int));*/
+ /*vi->floor_param=calloc(vi->floors,sizeof(void *));*/
+ for(i=0;i<vi->floors;i++){
+ vi->floor_type[i]=_oggpack_read(opb,16);
+ if(vi->floor_type[i]<0 || vi->floor_type[i]>=VI_FLOORB)goto err_out;
+ vi->floor_param[i]=_floor_P[vi->floor_type[i]]->unpack(vi,opb);
+ if(!vi->floor_param[i])goto err_out;
+ }
+
+ /* residue backend settings */
+ vi->residues=_oggpack_read(opb,6)+1;
+ /*vi->residue_type=malloc(vi->residues*sizeof(int));*/
+ /*vi->residue_param=calloc(vi->residues,sizeof(void *));*/
+ for(i=0;i<vi->residues;i++){
+ vi->residue_type[i]=_oggpack_read(opb,16);
+ if(vi->residue_type[i]<0 || vi->residue_type[i]>=VI_RESB)goto err_out;
+ vi->residue_param[i]=_residue_P[vi->residue_type[i]]->unpack(vi,opb);
+ if(!vi->residue_param[i])goto err_out;
+ }
+
+ /* map backend settings */
+ vi->maps=_oggpack_read(opb,6)+1;
+ /*vi->map_type=malloc(vi->maps*sizeof(int));*/
+ /*vi->map_param=calloc(vi->maps,sizeof(void *));*/
+ for(i=0;i<vi->maps;i++){
+ vi->map_type[i]=_oggpack_read(opb,16);
+ if(vi->map_type[i]<0 || vi->map_type[i]>=VI_MAPB)goto err_out;
+ vi->map_param[i]=_mapping_P[vi->map_type[i]]->unpack(vi,opb);
+ if(!vi->map_param[i])goto err_out;
+ }
+
+ /* mode settings */
+ vi->modes=_oggpack_read(opb,6)+1;
+ /*vi->mode_param=calloc(vi->modes,sizeof(void *));*/
+ for(i=0;i<vi->modes;i++){
+ vi->mode_param[i]=calloc(1,sizeof(vorbis_info_mode));
+ vi->mode_param[i]->blockflag=_oggpack_read(opb,1);
+ vi->mode_param[i]->windowtype=_oggpack_read(opb,16);
+ vi->mode_param[i]->transformtype=_oggpack_read(opb,16);
+ vi->mode_param[i]->mapping=_oggpack_read(opb,8);
+
+ if(vi->mode_param[i]->windowtype>=VI_WINDOWB)goto err_out;
+ if(vi->mode_param[i]->transformtype>=VI_WINDOWB)goto err_out;
+ if(vi->mode_param[i]->mapping>=vi->maps)goto err_out;
+ }
+
+ if(_oggpack_read(opb,1)!=1)goto err_out; /* top level EOP check */
+
+ return(0);
+ err_out:
+ vorbis_info_clear(vi);
+ return(-1);
+}
+
+/* The Vorbis header is in three packets; the initial small packet in
+ the first page that identifies basic parameters, a second packet
+ with bitstream comments and a third packet that holds the
+ codebook. */
+
+int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,ogg_packet *op){
+ oggpack_buffer opb;
+
+ if(op){
+ _oggpack_readinit(&opb,op->packet,op->bytes);
+
+ /* Which of the three types of header is this? */
+ /* Also verify header-ness, vorbis */
+ {
+ char buffer[6];
+ int packtype=_oggpack_read(&opb,8);
+ memset(buffer,0,6);
+ _v_readstring(&opb,buffer,6);
+ if(memcmp(buffer,"vorbis",6)){
+ /* not a vorbis header */
+ return(-1);
+ }
+ switch(packtype){
+ case 0x01: /* least significant *bit* is read first */
+ if(!op->b_o_s){
+ /* Not the initial packet */
+ return(-1);
+ }
+ if(vi->rate!=0){
+ /* previously initialized info header */
+ return(-1);
+ }
+
+ return(_vorbis_unpack_info(vi,&opb));
+
+ case 0x03: /* least significant *bit* is read first */
+ if(vi->rate==0){
+ /* um... we didn't get the initial header */
+ return(-1);
+ }
+
+ return(_vorbis_unpack_comment(vc,&opb));
+
+ case 0x05: /* least significant *bit* is read first */
+ if(vi->rate==0 || vc->vendor==NULL){
+ /* um... we didn;t get the initial header or comments yet */
+ return(-1);
+ }
+
+ return(_vorbis_unpack_books(vi,&opb));
+
+ default:
+ /* Not a valid vorbis header type */
+ return(-1);
+ break;
+ }
+ }
+ }
+ return(-1);
+}
+
+/* pack side **********************************************************/
+
+static int _vorbis_pack_info(oggpack_buffer *opb,vorbis_info *vi){
+ /* preamble */
+ _oggpack_write(opb,0x01,8);
+ _v_writestring(opb,"vorbis");
+
+ /* basic information about the stream */
+ _oggpack_write(opb,0x00,32);
+ _oggpack_write(opb,vi->channels,8);
+ _oggpack_write(opb,vi->rate,32);
+
+ _oggpack_write(opb,vi->bitrate_upper,32);
+ _oggpack_write(opb,vi->bitrate_nominal,32);
+ _oggpack_write(opb,vi->bitrate_lower,32);
+
+ _oggpack_write(opb,ilog2(vi->blocksizes[0]),4);
+ _oggpack_write(opb,ilog2(vi->blocksizes[1]),4);
+ _oggpack_write(opb,1,1);
+
+ return(0);
+}
+
+static int _vorbis_pack_comment(oggpack_buffer *opb,vorbis_comment *vc){
+ char temp[]="Xiphophorus libVorbis I 20000508";
+
+ /* preamble */
+ _oggpack_write(opb,0x03,8);
+ _v_writestring(opb,"vorbis");
+
+ /* vendor */
+ _oggpack_write(opb,strlen(temp),32);
+ _v_writestring(opb,temp);
+
+ /* comments */
+
+ _oggpack_write(opb,vc->comments,32);
+ if(vc->comments){
+ int i;
+ for(i=0;i<vc->comments;i++){
+ if(vc->user_comments[i]){
+ _oggpack_write(opb,vc->comment_lengths[i],32);
+ _v_writestring(opb,vc->user_comments[i]);
+ }else{
+ _oggpack_write(opb,0,32);
+ }
+ }
+ }
+ _oggpack_write(opb,1,1);
+
+ return(0);
+}
+
+static int _vorbis_pack_books(oggpack_buffer *opb,vorbis_info *vi){
+ int i;
+ _oggpack_write(opb,0x05,8);
+ _v_writestring(opb,"vorbis");
+
+ /* books */
+ _oggpack_write(opb,vi->books-1,8);
+ for(i=0;i<vi->books;i++)
+ if(vorbis_staticbook_pack(vi->book_param[i],opb))goto err_out;
+
+ /* times */
+ _oggpack_write(opb,vi->times-1,6);
+ for(i=0;i<vi->times;i++){
+ _oggpack_write(opb,vi->time_type[i],16);
+ _time_P[vi->time_type[i]]->pack(vi->time_param[i],opb);
+ }
+
+ /* floors */
+ _oggpack_write(opb,vi->floors-1,6);
+ for(i=0;i<vi->floors;i++){
+ _oggpack_write(opb,vi->floor_type[i],16);
+ _floor_P[vi->floor_type[i]]->pack(vi->floor_param[i],opb);
+ }
+
+ /* residues */
+ _oggpack_write(opb,vi->residues-1,6);
+ for(i=0;i<vi->residues;i++){
+ _oggpack_write(opb,vi->residue_type[i],16);
+ _residue_P[vi->residue_type[i]]->pack(vi->residue_param[i],opb);
+ }
+
+ /* maps */
+ _oggpack_write(opb,vi->maps-1,6);
+ for(i=0;i<vi->maps;i++){
+ _oggpack_write(opb,vi->map_type[i],16);
+ _mapping_P[vi->map_type[i]]->pack(vi,vi->map_param[i],opb);
+ }
+
+ /* modes */
+ _oggpack_write(opb,vi->modes-1,6);
+ for(i=0;i<vi->modes;i++){
+ _oggpack_write(opb,vi->mode_param[i]->blockflag,1);
+ _oggpack_write(opb,vi->mode_param[i]->windowtype,16);
+ _oggpack_write(opb,vi->mode_param[i]->transformtype,16);
+ _oggpack_write(opb,vi->mode_param[i]->mapping,8);
+ }
+ _oggpack_write(opb,1,1);
+
+ return(0);
+err_out:
+ return(-1);
+}
+
+int vorbis_analysis_headerout(vorbis_dsp_state *v,
+ vorbis_comment *vc,
+ ogg_packet *op,
+ ogg_packet *op_comm,
+ ogg_packet *op_code){
+ vorbis_info *vi=v->vi;
+ oggpack_buffer opb;
+
+ /* first header packet **********************************************/
+
+ _oggpack_writeinit(&opb);
+ if(_vorbis_pack_info(&opb,vi))goto err_out;
+
+ /* build the packet */
+ if(v->header)free(v->header);
+ v->header=malloc(_oggpack_bytes(&opb));
+ memcpy(v->header,opb.buffer,_oggpack_bytes(&opb));
+ op->packet=v->header;
+ op->bytes=_oggpack_bytes(&opb);
+ op->b_o_s=1;
+ op->e_o_s=0;
+ op->frameno=0;
+
+ /* second header packet (comments) **********************************/
+
+ _oggpack_reset(&opb);
+ if(_vorbis_pack_comment(&opb,vc))goto err_out;
+
+ if(v->header1)free(v->header1);
+ v->header1=malloc(_oggpack_bytes(&opb));
+ memcpy(v->header1,opb.buffer,_oggpack_bytes(&opb));
+ op_comm->packet=v->header1;
+ op_comm->bytes=_oggpack_bytes(&opb);
+ op_comm->b_o_s=0;
+ op_comm->e_o_s=0;
+ op_comm->frameno=0;
+
+ /* third header packet (modes/codebooks) ****************************/
+
+ _oggpack_reset(&opb);
+ if(_vorbis_pack_books(&opb,vi))goto err_out;
+
+ if(v->header2)free(v->header2);
+ v->header2=malloc(_oggpack_bytes(&opb));
+ memcpy(v->header2,opb.buffer,_oggpack_bytes(&opb));
+ op_code->packet=v->header2;
+ op_code->bytes=_oggpack_bytes(&opb);
+ op_code->b_o_s=0;
+ op_code->e_o_s=0;
+ op_code->frameno=0;
+
+ _oggpack_writeclear(&opb);
+ return(0);
+ err_out:
+ _oggpack_writeclear(&opb);
+ memset(op,0,sizeof(ogg_packet));
+ memset(op_comm,0,sizeof(ogg_packet));
+ memset(op_code,0,sizeof(ogg_packet));
+
+ if(v->header)free(v->header);
+ if(v->header1)free(v->header1);
+ if(v->header2)free(v->header2);
+ v->header=NULL;
+ v->header1=NULL;
+ v->header2=NULL;
+ return(-1);
+}
+
diff --git a/lib/mapping0.c b/lib/mapping0.c
new file mode 100644
index 00000000..989e169b
--- /dev/null
+++ b/lib/mapping0.c
@@ -0,0 +1,383 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE Ogg Vorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
+ * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE. *
+ * PLEASE READ THESE TERMS DISTRIBUTING. *
+ * *
+ * THE OggSQUISH SOURCE CODE IS (C) COPYRIGHT 1994-2000 *
+ * by Monty <monty@xiph.org> and The XIPHOPHORUS Company *
+ * http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: channel mapping 0 implementation
+ last mod: $Id: mapping0.c,v 1.14.2.1 2000/08/15 08:33:44 xiphmont Exp $
+
+ ********************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "vorbis/codec.h"
+#include "vorbis/backends.h"
+#include "bitwise.h"
+#include "bookinternal.h"
+#include "registry.h"
+#include "psy.h"
+#include "misc.h"
+
+/* simplistic, wasteful way of doing this (unique lookup for each
+ mode/submapping); there should be a central repository for
+ identical lookups. That will require minor work, so I'm putting it
+ off as low priority.
+
+ Why a lookup for each backend in a given mode? Because the
+ blocksize is set by the mode, and low backend lookups may require
+ parameters from other areas of the mode/mapping */
+
+typedef struct {
+ vorbis_info_mode *mode;
+ vorbis_info_mapping0 *map;
+
+ vorbis_look_time **time_look;
+ vorbis_look_floor **floor_look;
+
+ vorbis_look_residue **residue_look;
+ vorbis_look_psy *psy_look;
+
+ vorbis_func_time **time_func;
+ vorbis_func_floor **floor_func;
+ vorbis_func_residue **residue_func;
+
+ int ch;
+ double **decay;
+ long lastframe; /* if a different mode is called, we need to
+ invalidate decay */
+} vorbis_look_mapping0;
+
+static void free_info(vorbis_info_mapping *i){
+ if(i){
+ memset(i,0,sizeof(vorbis_info_mapping0));
+ free(i);
+ }
+}
+
+static void free_look(vorbis_look_mapping *look){
+ int i;
+ vorbis_look_mapping0 *l=(vorbis_look_mapping0 *)look;
+ if(l){
+ for(i=0;i<l->map->submaps;i++){
+ l->time_func[i]->free_look(l->time_look[i]);
+ l->floor_func[i]->free_look(l->floor_look[i]);
+ l->residue_func[i]->free_look(l->residue_look[i]);
+ if(l->psy_look)_vp_psy_clear(l->psy_look+i);
+ }
+
+ if(l->decay){
+ for(i=0;i<l->ch;i++){
+ if(l->decay[i])free(l->decay[i]);
+ }
+ free(l->decay);
+ }
+
+ free(l->time_func);
+ free(l->floor_func);
+ free(l->residue_func);
+ free(l->time_look);
+ free(l->floor_look);
+ free(l->residue_look);
+ if(l->psy_look)free(l->psy_look);
+ memset(l,0,sizeof(vorbis_look_mapping0));
+ free(l);
+ }
+}
+
+static vorbis_look_mapping *look(vorbis_dsp_state *vd,vorbis_info_mode *vm,
+ vorbis_info_mapping *m){
+ int i;
+ vorbis_info *vi=vd->vi;
+ vorbis_look_mapping0 *look=calloc(1,sizeof(vorbis_look_mapping0));
+ vorbis_info_mapping0 *info=look->map=(vorbis_info_mapping0 *)m;
+ look->mode=vm;
+
+ look->time_look=calloc(info->submaps,sizeof(vorbis_look_time *));
+ look->floor_look=calloc(info->submaps,sizeof(vorbis_look_floor *));
+
+ look->residue_look=calloc(info->submaps,sizeof(vorbis_look_residue *));
+ if(vi->psys)look->psy_look=calloc(info->submaps,sizeof(vorbis_look_psy));
+
+ look->time_func=calloc(info->submaps,sizeof(vorbis_func_time *));
+ look->floor_func=calloc(info->submaps,sizeof(vorbis_func_floor *));
+ look->residue_func=calloc(info->submaps,sizeof(vorbis_func_residue *));
+
+ for(i=0;i<info->submaps;i++){
+ int timenum=info->timesubmap[i];
+ int floornum=info->floorsubmap[i];
+ int resnum=info->residuesubmap[i];
+
+ look->time_func[i]=_time_P[vi->time_type[timenum]];
+ look->time_look[i]=look->time_func[i]->
+ look(vd,vm,vi->time_param[timenum]);
+ look->floor_func[i]=_floor_P[vi->floor_type[floornum]];
+ look->floor_look[i]=look->floor_func[i]->
+ look(vd,vm,vi->floor_param[floornum]);
+ look->residue_func[i]=_residue_P[vi->residue_type[resnum]];
+ look->residue_look[i]=look->residue_func[i]->
+ look(vd,vm,vi->residue_param[resnum]);
+
+ if(vi->psys && vd->analysisp){
+ int psynum=info->psysubmap[i];
+ _vp_psy_init(look->psy_look+i,vi->psy_param[psynum],
+ vi->blocksizes[vm->blockflag]/2,vi->rate);
+ }
+ }
+
+ look->ch=vi->channels;
+ if(vi->psys){
+ look->decay=calloc(vi->channels,sizeof(double *));
+ for(i=0;i<vi->channels;i++)
+ look->decay[i]=calloc(vi->blocksizes[vm->blockflag]/2,sizeof(double));
+ }
+
+ return(look);
+}
+
+static void pack(vorbis_info *vi,vorbis_info_mapping *vm,oggpack_buffer *opb){
+ int i;
+ vorbis_info_mapping0 *info=(vorbis_info_mapping0 *)vm;
+
+ _oggpack_write(opb,info->submaps-1,4);
+ /* we don't write the channel submappings if we only have one... */
+ if(info->submaps>1){
+ for(i=0;i<vi->channels;i++)
+ _oggpack_write(opb,info->chmuxlist[i],4);
+ }
+ for(i=0;i<info->submaps;i++){
+ _oggpack_write(opb,info->timesubmap[i],8);
+ _oggpack_write(opb,info->floorsubmap[i],8);
+ _oggpack_write(opb,info->residuesubmap[i],8);
+ }
+}
+
+/* also responsible for range checking */
+static vorbis_info_mapping *unpack(vorbis_info *vi,oggpack_buffer *opb){
+ int i;
+ vorbis_info_mapping0 *info=calloc(1,sizeof(vorbis_info_mapping0));
+ memset(info,0,sizeof(vorbis_info_mapping0));
+
+ info->submaps=_oggpack_read(opb,4)+1;
+
+ if(info->submaps>1){
+ for(i=0;i<vi->channels;i++){
+ info->chmuxlist[i]=_oggpack_read(opb,4);
+ if(info->chmuxlist[i]>=info->submaps)goto err_out;
+ }
+ }
+ for(i=0;i<info->submaps;i++){
+ info->timesubmap[i]=_oggpack_read(opb,8);
+ if(info->timesubmap[i]>=vi->times)goto err_out;
+ info->floorsubmap[i]=_oggpack_read(opb,8);
+ if(info->floorsubmap[i]>=vi->floors)goto err_out;
+ info->residuesubmap[i]=_oggpack_read(opb,8);
+ if(info->residuesubmap[i]>=vi->residues)goto err_out;
+ }
+
+ return info;
+
+ err_out:
+ free_info(info);
+ return(NULL);
+}
+
+#include <stdio.h>
+#include "os.h"
+#include "lpc.h"
+#include "lsp.h"
+#include "envelope.h"
+#include "mdct.h"
+#include "psy.h"
+#include "bitwise.h"
+#include "spectrum.h"
+#include "scales.h"
+
+/* no time mapping implementation for now */
+static long seq=0;
+static int forward(vorbis_block *vb,vorbis_look_mapping *l){
+ vorbis_dsp_state *vd=vb->vd;
+ vorbis_info *vi=vd->vi;
+ vorbis_look_mapping0 *look=(vorbis_look_mapping0 *)l;
+ vorbis_info_mapping0 *info=look->map;
+ vorbis_info_mode *mode=look->mode;
+ int n=vb->pcmend;
+ int i,j;
+ double *window=vd->window[vb->W][vb->lW][vb->nW][mode->windowtype];
+
+ double **pcmbundle=alloca(sizeof(double *)*vi->channels);
+ int *nonzero=alloca(sizeof(int)*vi->channels);
+
+ /* time domain pre-window: NONE IMPLEMENTED */
+
+ /* window the PCM data: takes PCM vector, vb; modifies PCM vector */
+
+ for(i=0;i<vi->channels;i++){
+ double *pcm=vb->pcm[i];
+ for(j=0;j<n;j++)
+ pcm[j]*=window[j];
+ }
+
+ /* time-domain post-window: NONE IMPLEMENTED */
+
+ /* transform the PCM data; takes PCM vector, vb; modifies PCM vector */
+ /* only MDCT right now.... */
+ for(i=0;i<vi->channels;i++){
+ double *pcm=vb->pcm[i];
+ mdct_forward(vd->transform[vb->W][0],pcm,pcm);
+ }
+
+ {
+ double *floor=_vorbis_block_alloc(vb,n*sizeof(double)/2);
+
+ for(i=0;i<vi->channels;i++){
+ double *pcm=vb->pcm[i];
+ double *decay=look->decay[i];
+ int submap=info->chmuxlist[i];
+
+ /* if some other mode/mapping was called last frame, our decay
+ accumulator is out of date. Clear it. */
+ if(look->lastframe+1 != vb->sequence)
+ memset(decay,0,n*sizeof(double)/2);
+
+ /* perform psychoacoustics; do masking */
+ _vp_compute_mask(look->psy_look+submap,pcm,floor,decay);
+
+ _analysis_output("decay",seq,decay,n/2,0,1);
+ _analysis_output("mdct",seq,pcm,n/2,0,1);
+ _analysis_output("lmdct",seq,pcm,n/2,0,0);
+ _analysis_output("prefloor",seq,floor,n/2,0,1);
+
+ /* perform floor encoding */
+ nonzero[i]=look->floor_func[submap]->
+ forward(vb,look->floor_look[submap],floor,floor);
+
+ _analysis_output("floor",seq,floor,n/2,0,1);
+
+ /* apply the floor, do optional noise levelling */
+ _vp_apply_floor(look->psy_look+submap,pcm,floor);
+
+ _analysis_output("res",seq++,pcm,n/2,0,0);
+
+#ifdef TRAIN_RES
+ if(nonzero[i]){
+ FILE *of;
+ char buffer[80];
+ int i;
+
+ sprintf(buffer,"residue_%d.vqd",vb->mode);
+ of=fopen(buffer,"a");
+ for(i=0;i<n/2;i++)
+ fprintf(of,"%.2f, ",pcm[i]);
+ fprintf(of,"\n");
+ fclose(of);
+ }
+#endif
+
+ }
+
+ /* perform residue encoding with residue mapping; this is
+ multiplexed. All the channels belonging to one submap are
+ encoded (values interleaved), then the next submap, etc */
+
+ for(i=0;i<info->submaps;i++){
+ int ch_in_bundle=0;
+ for(j=0;j<vi->channels;j++){
+ if(info->chmuxlist[j]==i && nonzero[j]==1){
+ pcmbundle[ch_in_bundle++]=vb->pcm[j];
+ }
+ }
+
+ look->residue_func[i]->forward(vb,look->residue_look[i],
+ pcmbundle,ch_in_bundle);
+ }
+ }
+
+ look->lastframe=vb->sequence;
+ return(0);
+}
+
+static int inverse(vorbis_block *vb,vorbis_look_mapping *l){
+ vorbis_dsp_state *vd=vb->vd;
+ vorbis_info *vi=vd->vi;
+ vorbis_look_mapping0 *look=(vorbis_look_mapping0 *)l;
+ vorbis_info_mapping0 *info=look->map;
+ vorbis_info_mode *mode=look->mode;
+ int i,j;
+ long n=vb->pcmend=vi->blocksizes[vb->W];
+
+ double *window=vd->window[vb->W][vb->lW][vb->nW][mode->windowtype];
+ double **pcmbundle=alloca(sizeof(double *)*vi->channels);
+ int *nonzero=alloca(sizeof(int)*vi->channels);
+
+ /* time domain information decode (note that applying the
+ information would have to happen later; we'll probably add a
+ function entry to the harness for that later */
+ /* NOT IMPLEMENTED */
+
+ /* recover the spectral envelope; store it in the PCM vector for now */
+ for(i=0;i<vi->channels;i++){
+ double *pcm=vb->pcm[i];
+ int submap=info->chmuxlist[i];
+ nonzero[i]=look->floor_func[submap]->
+ inverse(vb,look->floor_look[submap],pcm);
+ _analysis_output("ifloor",seq+i,pcm,n/2,0,1);
+ }
+
+ /* recover the residue, apply directly to the spectral envelope */
+
+ for(i=0;i<info->submaps;i++){
+ int ch_in_bundle=0;
+ for(j=0;j<vi->channels;j++){
+ if(info->chmuxlist[j]==i && nonzero[j])
+ pcmbundle[ch_in_bundle++]=vb->pcm[j];
+ }
+
+ look->residue_func[i]->inverse(vb,look->residue_look[i],pcmbundle,ch_in_bundle);
+ }
+
+ /* transform the PCM data; takes PCM vector, vb; modifies PCM vector */
+ /* only MDCT right now.... */
+ for(i=0;i<vi->channels;i++){
+ double *pcm=vb->pcm[i];
+ _analysis_output("out",seq+i,pcm,n/2,0,1);
+ mdct_backward(vd->transform[vb->W][0],pcm,pcm);
+ }
+
+ /* now apply the decoded pre-window time information */
+ /* NOT IMPLEMENTED */
+
+ /* window the data */
+ for(i=0;i<vi->channels;i++){
+ double *pcm=vb->pcm[i];
+ if(nonzero[i])
+ for(j=0;j<n;j++)
+ pcm[j]*=window[j];
+ else
+ for(j=0;j<n;j++)
+ pcm[j]=0.;
+ _analysis_output("final",seq++,pcm,n,0,0);
+ }
+
+ /* now apply the decoded post-window time information */
+ /* NOT IMPLEMENTED */
+
+ /* all done! */
+ return(0);
+}
+
+/* export hooks */
+vorbis_func_mapping mapping0_exportbundle={
+ &pack,&unpack,&look,&free_info,&free_look,&forward,&inverse
+};
+
+
+
diff --git a/lib/psy.c b/lib/psy.c
index 35710de9..fa59a11e 100644
--- a/lib/psy.c
+++ b/lib/psy.c
@@ -12,7 +12,7 @@
********************************************************************
function: psychoacoustics not including preecho
- last mod: $Id: psy.c,v 1.23.4.9 2000/08/09 20:22:04 xiphmont Exp $
+ last mod: $Id: psy.c,v 1.23.4.10 2000/08/15 08:33:44 xiphmont Exp $
********************************************************************/
@@ -303,28 +303,6 @@ void _vp_psy_clear(vorbis_look_psy *p){
}
}
-static void compute_decay_adaptive(vorbis_look_psy *p,double *f, double *decay, int n){
- /* handle decay */
- int i;
- double decscale=1.-pow(p->vi->decay_coeff,n);
- double attscale=1.-pow(p->vi->attack_coeff,n);
- static int frameno=0;
-
- _analysis_output("drive",frameno,f,n,0,1);
- _analysis_output("decay",frameno++,decay,n,0,1);
-
- for(i=0;i<n;i++){
- double del=f[i]-decay[i];
- if(del>0)
- /* add energy */
- decay[i]+=del*attscale;
- else
- /* remove energy */
- decay[i]+=del*decscale;
- if(decay[i]>f[i])f[i]=decay[i];
- }
-}
-
static void compute_decay_fixed(vorbis_look_psy *p,double *f, double *decay, int n){
/* handle decay */
int i;
@@ -333,9 +311,6 @@ static void compute_decay_fixed(vorbis_look_psy *p,double *f, double *decay, int
static int frameno=0;
- _analysis_output("drive",frameno,f,n,0,1);
- _analysis_output("decay",frameno++,decay,n,0,1);
-
for(i=0;i<n;i++){
double pre=decay[i];
if(decay[i]){
@@ -543,7 +518,7 @@ static void bark_noise(long n,double *b,double *f,double *noise){
memset(norm,0,n*sizeof(double));
while(hi<n){
- val=todB(f[i]*f[i])+200.;
+ val=todB(f[i]*f[i])+400.;
del=1./(i-lo);
noise[lo]+=val*del;
noise[i]-=val*del;
@@ -558,8 +533,8 @@ static void bark_noise(long n,double *b,double *f,double *noise){
i++;
- for(;b[hi]-.5<b[i] && hi<n;hi++);
- for(;b[lo]+.5<b[i] && lo<i;lo++);
+ for(;hi<n && b[hi]-.3<b[i];hi++);
+ for(;lo<i-1 && b[lo]+.3<b[i];lo++);
if(i==hi)hi++;
}
@@ -569,8 +544,7 @@ static void bark_noise(long n,double *b,double *f,double *noise){
long hilo=hi-lo;
for(;i<n;i++){
- val=todB(f[i]*f[i])+200.;
-
+ val=todB(f[i]*f[i])+400.;
del=1./(hii);
noise[i]-=val*del;
norm[i]-=del;
@@ -582,7 +556,7 @@ static void bark_noise(long n,double *b,double *f,double *noise){
norm[i]-=del;
}
for(i=1,lo=n-ilo;lo<n;lo++,i++){
- val=todB(f[n-i]*f[n-i])+200.;
+ val=todB(f[n-i]*f[n-i])+400.;
del=1./ilo;
noise[lo]+=val*del;
norm[lo]+=del;
@@ -605,10 +579,13 @@ static void bark_noise(long n,double *b,double *f,double *noise){
for(i=0;i<n;i++){
val+=norm[i];
acc+=noise[i];
- if(val==0)
+ if(val==0){
noise[i]=0.;
- else
- noise[i]=sqrt(fromdB(acc/val-200.));
+ norm[i]=0;
+ }else{
+ double v=acc/val-400;
+ noise[i]=sqrt(fromdB(v));
+ }
}
}
@@ -625,23 +602,24 @@ void _vp_compute_mask(vorbis_look_psy *p,double *f,
double *flr,
double *decay){
double *smooth=alloca(sizeof(double)*p->n);
- double *seed=alloca(sizeof(double)*p->n);
int i,n=p->n;
double specmax=0.;
+ double *seed=alloca(sizeof(double)*p->n);
+ double *seed2=alloca(sizeof(double)*p->n);
+
memset(flr,0,n*sizeof(double));
- memset(seed,0,n*sizeof(double));
/* noise masking */
if(p->vi->noisemaskp){
- /* don't use the smoothed data for noise */
- bark_noise(n,p->bark,f,smooth);
- /*_analysis_output("noise",frameno,work2,n,0,1);*/
- seed_point(p,p->noiseatt,smooth,flr,specmax);
+ memset(seed,0,n*sizeof(double));
+ bark_noise(n,p->bark,f,seed);
+ seed_point(p,p->noiseatt,seed,flr,specmax);
+
}
+ /* smooth the data is that's called for ********************************/
for(i=0;i<n;i++)smooth[i]=fabs(f[i]);
-
if(p->vi->smoothp){
/* compute power^.5 of three neighboring bins to smooth for peaks
that get split twixt bins/peaks that nail the bin. This evens
@@ -660,7 +638,7 @@ void _vp_compute_mask(vorbis_look_psy *p,double *f,
smooth[n-1]=sqrt(acc);
}
- /* find the highest peak so we know the limits */
+ /* find the highest peak so we know the limits *************************/
for(i=0;i<n;i++){
if(smooth[i]>specmax)specmax=smooth[i];
}
@@ -678,52 +656,32 @@ void _vp_compute_mask(vorbis_look_psy *p,double *f,
}
}
- /* peak attenuation */
+ /* peak attenuation ******/
if(p->vi->peakattp){
memset(seed,0,n*sizeof(double));
seed_att(p,p->peakatt,smooth,seed,specmax);
max_seeds(p,seed,flr);
}
- /* seed the tone masking */
+ /* tone masking */
if(p->vi->tonemaskp){
memset(seed,0,n*sizeof(double));
+ memset(seed2,0,n*sizeof(double));
- seed_generic(p,p->tonecurves,smooth,flr,seed,specmax);
-
- /* chase the seeds */
- max_seeds(p,seed,seed);
+ seed_generic(p,p->tonecurves,smooth,flr,seed2,specmax);
+ max_seeds(p,seed2,seed2);
+
+ for(i=0;i<n;i++)if(seed2[i]<flr[i])seed2[i]=flr[i];
+ for(i=0;i<n;i++)if(seed2[i]<decay[i])seed2[i]=decay[i];
+ seed_generic(p,p->tonecurves,smooth,seed2,seed,specmax);
+ max_seeds(p,seed,seed);
+
if(p->vi->decayp)
compute_decay_fixed(p,seed,decay,n);
-
+
for(i=0;i<n;i++)if(flr[i]<seed[i])flr[i]=seed[i];
-
- }
-
- /* noise culling */
- if(p->vi->noisecullp){
- int j;
-
- /* march down from Nyquist to the first peak that exceeds the
- masking threshhold */
- for(i=n-1;i>0;i--){
- if(flr[i]<fabs(f[i])){
- break;
- }else{
- f[i]=0;
- }
- }
-
- /* take the edge off the steep ATH rise in treble */
- /*for(j=i+1;j<n;j++)
- flr[j]=flr[j-1];*/
-
- /* Smooth the falloff a bit */
-
- /* cull out addiitonal bands */
-
-
+
}
frameno++;
diff --git a/lib/psytune.c b/lib/psytune.c
index 2b5120bb..5191db0a 100644
--- a/lib/psytune.c
+++ b/lib/psytune.c
@@ -13,7 +13,7 @@
function: simple utility that runs audio through the psychoacoustics
without encoding
- last mod: $Id: psytune.c,v 1.4.4.2 2000/07/31 19:41:00 xiphmont Exp $
+ last mod: $Id: psytune.c,v 1.4.4.3 2000/08/15 08:33:45 xiphmont Exp $
********************************************************************/
@@ -34,7 +34,7 @@ static vorbis_info_psy _psy_set0={
1,/*athp*/
1,/*decayp*/
1,/*smoothp*/
- 1,.2,
+ 0,.2,
-100.,
-140.,
@@ -64,28 +64,27 @@ static vorbis_info_psy _psy_set0={
{-40.,-40.,-40.,-40.,-40.,-50.,-60.,-70.,-80.,-90.,-100.}, /*16000*/
},
- 0,/* peakattp */
- /* 0 10 20 30 40 50 60 70 80 90 100 */
+ 1,/* peakattp */
{{-14.,-16.,-18.,-19.,-20.,-21.,-22.,-22.,-22.,-22.,-22.}, /*63*/
{-14.,-16.,-18.,-19.,-20.,-21.,-22.,-22.,-22.,-22.,-22.}, /*88*/
{-14.,-16.,-18.,-19.,-20.,-21.,-22.,-22.,-22.,-22.,-22.}, /*125*/
- {-14.,-16.,-18.,-19.,-20.,-21.,-22.,-22.,-22.,-22.,-22.}, /*175*/
- {-14.,-16.,-18.,-19.,-20.,-21.,-22.,-22.,-22.,-22.,-22.}, /*250*/
- {-14.,-16.,-18.,-19.,-20.,-20.,-20.,-21.,-22.,-22.,-22.}, /*350*/
- {-14.,-16.,-18.,-18.,-18.,-19.,-20.,-20.,-20.,-22.,-22.}, /*500*/
- {-14.,-16.,-18.,-18.,-18.,-18.,-18.,-19.,-20.,-22.,-22.}, /*700*/
- {-14.,-16.,-16.,-17.,-18.,-18.,-18.,-19.,-20.,-22.,-22.}, /*1000*/
- {-14.,-16.,-16.,-17.,-18.,-18.,-18.,-19.,-20.,-22.,-22.}, /*1400*/
- {-14.,-15.,-18.,-18.,-18.,-18.,-18.,-19.,-20.,-22.,-22.}, /*2000*/
- {-14.,-14.,-18.,-18.,-18.,-18.,-18.,-19.,-20.,-22.,-22.}, /*2800*/
- {-12.,-13.,-16.,-17.,-17.,-17.,-18.,-19.,-20.,-22.,-22.}, /*4000*/
- {-10.,-12.,-14.,-15.,-16.,-16.,-17.,-18.,-20.,-22.,-22.}, /*5600*/
- { -8.,-11.,-12.,-13.,-14.,-15.,-16.,-17.,-18.,-22.,-22.}, /*8000*/
- { -8.,-10.,-12.,-13.,-14.,-15.,-16.,-16.,-16.,-22.,-22.}, /*11500*/
- { -8.,-10.,-12.,-13.,-14.,-15.,-16.,-16.,-16.,-22.,-22.}, /*16000*/
+ { -7., -8., -9.,-10.,-10.,-11.,-12.,-13.,-15.,-18.,-20.}, /*8000*/
+ { -7., -8., -9.,-10.,-10.,-11.,-12.,-13.,-15.,-18.,-20.}, /*8000*/
+ { -7., -8., -9.,-10.,-10.,-11.,-12.,-13.,-15.,-18.,-20.}, /*8000*/
+ { -7., -8., -9.,-10.,-10.,-11.,-12.,-13.,-15.,-18.,-20.}, /*8000*/
+ { -7., -8., -9.,-10.,-10.,-11.,-12.,-13.,-15.,-18.,-20.}, /*8000*/
+ { -7., -8., -9.,-10.,-10.,-11.,-12.,-13.,-15.,-18.,-20.}, /*8000*/
+ { -7., -8., -9.,-10.,-10.,-11.,-12.,-13.,-15.,-18.,-20.}, /*8000*/
+ { -7., -8., -9.,-10.,-10.,-11.,-12.,-13.,-15.,-18.,-20.}, /*8000*/
+ { -7., -8., -9.,-10.,-10.,-11.,-12.,-13.,-15.,-18.,-20.}, /*8000*/
+ { -7., -8., -9.,-10.,-10.,-11.,-12.,-13.,-15.,-18.,-20.}, /*8000*/
+ { -7., -8., -9.,-10.,-10.,-11.,-12.,-13.,-15.,-18.,-20.}, /*8000*/
+ { -7., -8., -9.,-10.,-10.,-11.,-12.,-13.,-15.,-16.,-17.}, /*8000*/
+ { -6., -7., -9., -9., -9., -9.,-10.,-11.,-12.,-13.,-14.}, /*11500*/
+ { -6., -6., -9., -9., -9., -9., -9., -9.,-10.,-11.,-12.}, /*16000*/
},
- 0,/*noisemaskp */
+ 1,/*noisemaskp */
/* 0 10 20 30 40 50 60 70 80 90 100 */
{{-99.,-99.,-99.,-99.,-99.,-99.,-99.,-199.,-199.,-199.,-199.}, /*63*/
{-99.,-99.,-99.,-99.,-99.,-99.,-99.,-199.,-199.,-199.,-199.}, /*88*/
@@ -93,25 +92,25 @@ static vorbis_info_psy _psy_set0={
{-99.,-99.,-99.,-99.,-99.,-99.,-99.,-199.,-199.,-199.,-199.}, /*175*/
{-99.,-99.,-99.,-99.,-99.,-99.,-99.,-199.,-199.,-199.,-199.}, /*250*/
{-99.,-99.,-99.,-99.,-99.,-99.,-99.,-199.,-199.,-199.,-199.}, /*350*/
- {-99.,-99.,-99.,-99.,-99.,-99.,-99.,-199.,-199.,-199.,-199.}, /*500*/
- {-99.,-99.,-99.,-99.,-99.,-99.,-99.,-199.,-199.,-199.,-199.}, /*700*/
- {-99.,-99.,-99.,-99.,-99.,-99.,-99.,-199.,-199.,-199.,-199.}, /*100*/
- {-99.,-99.,-99.,-99.,-99.,-99.,-99.,-199.,-199.,-199.,-199.}, /*1400/
- { -0., -0., -0., -0., -0., -0., -0., -0., -0., -0., -0.}, /*2000*/
- { -0., -0., -0., -0., -0., -0., -0., -0., -0., -0., -0.}, /*2800*/
+ {-99.,-99.,-99.,-99.,-99.,-99.,-99.,-199.,-199.,-199.,-199.}, /*350*/
+ {-99.,-99.,-99.,-99.,-99.,-99.,-99.,-199.,-199.,-199.,-199.}, /*350*/
+ { -6., -6., -6., -6., -6., -6., -6., -6., -6., -6., -6.}, /*2000*/
+ { -6., -6., -6., -6., -6., -6., -6., -6., -6., -6., -6.}, /*2000*/
+ { -6., -6., -6., -6., -6., -6., -6., -6., -6., -6., -6.}, /*2000*/
+ { -6., -6., -6., -6., -6., -6., -6., -6., -6., -6., -6.}, /*2800*/
{ -0., -0., -0., -0., -0., -0., -0., -0., -0., -0., -0.}, /*4000*/
- { -0., -0., -0., -0., -0., -0., -0., -0., -0., -0., -0.}, /*5600*/
- { -0., -0., -0., -0., -0., -0., -0., -0., -0., -0., -0.}, /*8000*/
- { -0., -0., -0., -0., -0., -0., -0., -0., -0., -0., -0.}, /*11500*/
- { -0., -0., -0., -0., -0., -0., -0., -0., -0., -0., -0.}, /*16000*/
+ { 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.}, /*5600*/
+ { 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4.}, /*8000*/
+ { 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4.}, /*11500*/
+ { 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4.}, /*16000*/
},
110.,
- .9998, .9998, /* attack/decay control */
+ -0, -.005, /* attack/decay control */
};
-static int noisy=1;
+static int noisy=0;
void analysis(char *base,int i,double *v,int n,int bark,int dB){
if(noisy){
int j;
@@ -305,6 +304,9 @@ int main(int argc,char *argv[]){
analysis("floor",frameno,floor,framesize/2,1,1);
_vp_apply_floor(&p_look,pcm[i],floor);
+ /*r(j=0;j<framesize/2;j++)
+ if(fabs(pcm[i][j])<1.)pcm[i][j]=0;*/
+
analysis("quant",frameno,pcm[i],framesize/2,1,1);
/* re-add floor */
diff --git a/lib/res0.c b/lib/res0.c
new file mode 100644
index 00000000..1f07a36e
--- /dev/null
+++ b/lib/res0.c
@@ -0,0 +1,384 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE Ogg Vorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
+ * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE. *
+ * PLEASE READ THESE TERMS DISTRIBUTING. *
+ * *
+ * THE OggSQUISH SOURCE CODE IS (C) COPYRIGHT 1994-2000 *
+ * by Monty <monty@xiph.org> and The XIPHOPHORUS Company *
+ * http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: residue backend 0 implementation
+ last mod: $Id: res0.c,v 1.15.4.1 2000/08/15 08:33:46 xiphmont Exp $
+
+ ********************************************************************/
+
+/* Slow, slow, slow, simpleminded and did I mention it was slow? The
+ encode/decode loops are coded for clarity and performance is not
+ yet even a nagging little idea lurking in the shadows. Oh and BTW,
+ it's slow. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <stdio.h>
+#include "vorbis/codec.h"
+#include "bitwise.h"
+#include "registry.h"
+#include "bookinternal.h"
+#include "sharedbook.h"
+#include "misc.h"
+#include "os.h"
+
+typedef struct {
+ vorbis_info_residue0 *info;
+ int map;
+
+ int parts;
+ codebook *phrasebook;
+ codebook ***partbooks;
+
+ int partvals;
+ int **decodemap;
+} vorbis_look_residue0;
+
+void free_info(vorbis_info_residue *i){
+ if(i){
+ memset(i,0,sizeof(vorbis_info_residue0));
+ free(i);
+ }
+}
+
+void free_look(vorbis_look_residue *i){
+ int j;
+ if(i){
+ vorbis_look_residue0 *look=(vorbis_look_residue0 *)i;
+ for(j=0;j<look->parts;j++)
+ if(look->partbooks[j])free(look->partbooks[j]);
+ free(look->partbooks);
+ for(j=0;j<look->partvals;j++)
+ free(look->decodemap[j]);
+ free(look->decodemap);
+ memset(i,0,sizeof(vorbis_look_residue0));
+ free(i);
+ }
+}
+
+void pack(vorbis_info_residue *vr,oggpack_buffer *opb){
+ vorbis_info_residue0 *info=(vorbis_info_residue0 *)vr;
+ int j,acc=0;
+ _oggpack_write(opb,info->begin,24);
+ _oggpack_write(opb,info->end,24);
+
+ _oggpack_write(opb,info->grouping-1,24); /* residue vectors to group and
+ code with a partitioned book */
+ _oggpack_write(opb,info->partitions-1,6); /* possible partition choices */
+ _oggpack_write(opb,info->groupbook,8); /* group huffman book */
+ for(j=0;j<info->partitions;j++){
+ _oggpack_write(opb,info->secondstages[j],4); /* zero *is* a valid choice */
+ acc+=info->secondstages[j];
+ }
+ for(j=0;j<acc;j++)
+ _oggpack_write(opb,info->booklist[j],8);
+
+}
+
+/* vorbis_info is for range checking */
+vorbis_info_residue *unpack(vorbis_info *vi,oggpack_buffer *opb){
+ int j,acc=0;
+ vorbis_info_residue0 *info=calloc(1,sizeof(vorbis_info_residue0));
+
+ info->begin=_oggpack_read(opb,24);
+ info->end=_oggpack_read(opb,24);
+ info->grouping=_oggpack_read(opb,24)+1;
+ info->partitions=_oggpack_read(opb,6)+1;
+ info->groupbook=_oggpack_read(opb,8);
+ for(j=0;j<info->partitions;j++){
+ acc+=info->secondstages[j]=_oggpack_read(opb,4);
+ }
+ for(j=0;j<acc;j++)
+ info->booklist[j]=_oggpack_read(opb,8);
+
+ if(info->groupbook>=vi->books)goto errout;
+ for(j=0;j<acc;j++)
+ if(info->booklist[j]>=vi->books)goto errout;
+
+ return(info);
+ errout:
+ free_info(info);
+ return(NULL);
+}
+
+vorbis_look_residue *look (vorbis_dsp_state *vd,vorbis_info_mode *vm,
+ vorbis_info_residue *vr){
+ vorbis_info_residue0 *info=(vorbis_info_residue0 *)vr;
+ vorbis_look_residue0 *look=calloc(1,sizeof(vorbis_look_residue0));
+ int j,k,acc=0;
+ int dim;
+ look->info=info;
+ look->map=vm->mapping;
+
+ look->parts=info->partitions;
+ look->phrasebook=vd->fullbooks+info->groupbook;
+ dim=look->phrasebook->dim;
+
+ look->partbooks=calloc(look->parts,sizeof(codebook **));
+
+ for(j=0;j<look->parts;j++){
+ int stages=info->secondstages[j];
+ if(stages){
+ look->partbooks[j]=malloc(stages*sizeof(codebook *));
+ for(k=0;k<stages;k++)
+ look->partbooks[j][k]=vd->fullbooks+info->booklist[acc++];
+ }
+ }
+
+ look->partvals=rint(pow(look->parts,dim));
+ look->decodemap=malloc(look->partvals*sizeof(int *));
+ for(j=0;j<look->partvals;j++){
+ long val=j;
+ long mult=look->partvals/look->parts;
+ look->decodemap[j]=malloc(dim*sizeof(int));
+ for(k=0;k<dim;k++){
+ long deco=val/mult;
+ val-=deco*mult;
+ mult/=look->parts;
+ look->decodemap[j][k]=deco;
+ }
+ }
+
+ return(look);
+}
+
+
+/* does not guard against invalid settings; eg, a subn of 16 and a
+ subgroup request of 32. Max subn of 128 */
+static int _testhack(double *vec,int n,vorbis_look_residue0 *look,
+ int auxparts,int auxpartnum){
+ vorbis_info_residue0 *info=look->info;
+ int i,j=0;
+ double max,localmax=0.;
+ double temp[128];
+ double entropy[8];
+
+ /* setup */
+ for(i=0;i<n;i++)temp[i]=fabs(vec[i]);
+
+ /* handle case subgrp==1 outside */
+ for(i=0;i<n;i++)
+ if(temp[i]>localmax)localmax=temp[i];
+ max=localmax;
+
+ for(i=0;i<n;i++)temp[i]=rint(temp[i]);
+
+ while(1){
+ entropy[j]=localmax;
+ n>>=1;
+ j++;
+
+ if(n<=0)break;
+ for(i=0;i<n;i++){
+ temp[i]+=temp[i+n];
+ }
+ localmax=0.;
+ for(i=0;i<n;i++)
+ if(temp[i]>localmax)localmax=temp[i];
+ }
+
+ for(i=0;i<auxparts-1;i++)
+ if(auxpartnum<info->blimit[i] &&
+ entropy[info->subgrp[i]]<=info->entmax[i] &&
+ max<=info->ampmax[i])
+ break;
+
+ return(i);
+}
+
+static int _encodepart(oggpack_buffer *opb,double *vec, int n,
+ int stages, codebook **books,int mode,int part){
+ int i,j,bits=0;
+
+ for(j=0;j<stages;j++){
+ int dim=books[j]->dim;
+ int step=n/dim;
+ for(i=0;i<step;i++){
+ int entry=vorbis_book_besterror(books[j],vec+i,step,0);
+#ifdef TRAIN_RESENT
+ {
+ char buf[80];
+ FILE *f;
+ sprintf(buf,"res0_%da%d_%d.vqd",mode,j,part);
+ f=fopen(buf,"a");
+ fprintf(f,"%d\n",entry);
+ fclose(f);
+ }
+#endif
+ bits+=vorbis_book_encode(books[j],entry,opb);
+ }
+ }
+
+ return(bits);
+}
+
+static int _decodepart(oggpack_buffer *opb,double *work,double *vec, int n,
+ int stages, codebook **books){
+ int i,j;
+
+ memset(work,0,sizeof(double)*n);
+ for(j=0;j<stages;j++){
+ int dim=books[j]->dim;
+ int step=n/dim;
+ for(i=0;i<step;i++)
+ if(vorbis_book_decodevs(books[j],work+i,opb,step,0)==-1)
+ return(-1);
+ }
+
+ for(i=0;i<n;i++)
+ vec[i]*=work[i];
+
+ return(0);
+}
+
+int forward(vorbis_block *vb,vorbis_look_residue *vl,
+ double **in,int ch){
+ long i,j,k,l;
+ vorbis_look_residue0 *look=(vorbis_look_residue0 *)vl;
+ vorbis_info_residue0 *info=look->info;
+
+ /* move all this setup out later */
+ int samples_per_partition=info->grouping;
+ int possible_partitions=info->partitions;
+ int partitions_per_word=look->phrasebook->dim;
+ int n=info->end-info->begin;
+ long phrasebits=0,resbitsT=0;
+ long *resbits=alloca(sizeof(long)*possible_partitions);
+ long *resvals=alloca(sizeof(long)*possible_partitions);
+
+ int partvals=n/samples_per_partition;
+ int partwords=(partvals+partitions_per_word-1)/partitions_per_word;
+ long **partword=_vorbis_block_alloc(vb,ch*sizeof(long *));
+
+ partvals=partwords*partitions_per_word;
+
+ /* we find the patition type for each partition of each
+ channel. We'll go back and do the interleaved encoding in a
+ bit. For now, clarity */
+
+ memset(resbits,0,sizeof(long)*possible_partitions);
+ memset(resvals,0,sizeof(long)*possible_partitions);
+
+ for(i=0;i<ch;i++){
+ partword[i]=_vorbis_block_alloc(vb,n/samples_per_partition*sizeof(long));
+ memset(partword[i],0,n/samples_per_partition*sizeof(long));
+ }
+
+ for(i=info->begin,l=0;i<info->end;i+=samples_per_partition,l++){
+ for(j=0;j<ch;j++)
+ /* do the partition decision based on the number of 'bits'
+ needed to encode the block */
+ partword[j][l]=
+ _testhack(in[j]+i,samples_per_partition,look,possible_partitions,l);
+
+ }
+ /* we code the partition words for each channel, then the residual
+ words for a partition per channel until we've written all the
+ residual words for that partition word. Then write the next
+ parition channel words... */
+
+ for(i=info->begin,l=0;i<info->end;){
+ /* first we encode a partition codeword for each channel */
+ for(j=0;j<ch;j++){
+ long val=partword[j][l];
+ for(k=1;k<partitions_per_word;k++)
+ val= val*possible_partitions+partword[j][l+k];
+ phrasebits+=vorbis_book_encode(look->phrasebook,val,&vb->opb);
+ }
+ /* now we encode interleaved residual values for the partitions */
+ for(k=0;k<partitions_per_word;k++,l++,i+=samples_per_partition)
+ for(j=0;j<ch;j++){
+ resbits[partword[j][l]]+=
+ _encodepart(&vb->opb,in[j]+i,samples_per_partition,
+ info->secondstages[partword[j][l]],
+ look->partbooks[partword[j][l]],look->map,partword[j][l]);
+ resvals[partword[j][l]]+=samples_per_partition;
+ }
+
+ }
+
+ for(i=0;i<possible_partitions;i++)resbitsT+=resbits[i];
+ /*fprintf(stderr,
+ "Encoded %ld res vectors in %ld phrasing and %ld res bits\n\t",
+ ch*(info->end-info->begin),phrasebits,resbitsT);
+ for(i=0;i<possible_partitions;i++)
+ fprintf(stderr,"%ld(%ld):%ld ",i,resvals[i],resbits[i]);
+ fprintf(stderr,"\n");*/
+
+ return(0);
+}
+
+/* a truncated packet here just means 'stop working'; it's not an error */
+int inverse(vorbis_block *vb,vorbis_look_residue *vl,double **in,int ch){
+ long i,j,k,l,transend=vb->pcmend/2;
+ vorbis_look_residue0 *look=(vorbis_look_residue0 *)vl;
+ vorbis_info_residue0 *info=look->info;
+
+ /* move all this setup out later */
+ int samples_per_partition=info->grouping;
+ int partitions_per_word=look->phrasebook->dim;
+ int n=info->end-info->begin;
+
+ int partvals=n/samples_per_partition;
+ int partwords=(partvals+partitions_per_word-1)/partitions_per_word;
+ int **partword=alloca(ch*sizeof(long *));
+ double *work=alloca(sizeof(double)*samples_per_partition);
+ partvals=partwords*partitions_per_word;
+
+ /* make sure we're zeroed up to the start */
+ for(j=0;j<ch;j++)
+ memset(in[j],0,sizeof(double)*info->begin);
+
+ for(i=info->begin,l=0;i<info->end;){
+ /* fetch the partition word for each channel */
+ for(j=0;j<ch;j++){
+ int temp=vorbis_book_decode(look->phrasebook,&vb->opb);
+ if(temp==-1)goto eopbreak;
+ partword[j]=look->decodemap[temp];
+ if(partword[j]==NULL)goto errout;
+ }
+
+ /* now we decode interleaved residual values for the partitions */
+ for(k=0;k<partitions_per_word;k++,l++,i+=samples_per_partition)
+ for(j=0;j<ch;j++){
+ int part=partword[j][k];
+ if(_decodepart(&vb->opb,work,in[j]+i,samples_per_partition,
+ info->secondstages[part],
+ look->partbooks[part])==-1)goto eopbreak;
+ }
+ }
+
+ eopbreak:
+ if(i<transend){
+ for(j=0;j<ch;j++)
+ memset(in[j]+i,0,sizeof(double)*(transend-i));
+ }
+
+ return(0);
+
+ errout:
+ for(j=0;j<ch;j++)
+ memset(in[j],0,sizeof(double)*transend);
+ return(0);
+}
+
+vorbis_func_residue residue0_exportbundle={
+ &pack,
+ &unpack,
+ &look,
+ &free_info,
+ &free_look,
+ &forward,
+ &inverse
+};
diff --git a/lib/vorbisfile.c b/lib/vorbisfile.c
new file mode 100644
index 00000000..eb49fc80
--- /dev/null
+++ b/lib/vorbisfile.c
@@ -0,0 +1,1156 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE Ogg Vorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
+ * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE. *
+ * PLEASE READ THESE TERMS DISTRIBUTING. *
+ * *
+ * THE OggSQUISH SOURCE CODE IS (C) COPYRIGHT 1994-2000 *
+ * by Monty <monty@xiph.org> and The XIPHOPHORUS Company *
+ * http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: stdio-based convenience library for opening/seeking/decoding
+ last mod: $Id: vorbisfile.c,v 1.25.4.1 2000/08/15 08:33:46 xiphmont Exp $
+
+ ********************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+
+#include "vorbis/codec.h"
+#include "vorbis/vorbisfile.h"
+
+#include "os.h"
+#include "misc.h"
+
+/* A 'chained bitstream' is a Vorbis bitstream that contains more than
+ one logical bitstream arranged end to end (the only form of Ogg
+ multiplexing allowed in a Vorbis bitstream; grouping [parallel
+ multiplexing] is not allowed in Vorbis) */
+
+/* A Vorbis file can be played beginning to end (streamed) without
+ worrying ahead of time about chaining (see decoder_example.c). If
+ we have the whole file, however, and want random access
+ (seeking/scrubbing) or desire to know the total length/time of a
+ file, we need to account for the possibility of chaining. */
+
+/* We can handle things a number of ways; we can determine the entire
+ bitstream structure right off the bat, or find pieces on demand.
+ This example determines and caches structure for the entire
+ bitstream, but builds a virtual decoder on the fly when moving
+ between links in the chain. */
+
+/* There are also different ways to implement seeking. Enough
+ information exists in an Ogg bitstream to seek to
+ sample-granularity positions in the output. Or, one can seek by
+ picking some portion of the stream roughly in the desired area if
+ we only want course navigation through the stream. */
+
+/*************************************************************************
+ * Many, many internal helpers. The intention is not to be confusing;
+ * rampant duplication and monolithic function implementation would be
+ * harder to understand anyway. The high level functions are last. Begin
+ * grokking near the end of the file */
+
+/* read a little more data from the file/pipe into the ogg_sync framer */
+#define CHUNKSIZE 4096
+static long _get_data(OggVorbis_File *vf){
+ char *buffer=ogg_sync_buffer(&vf->oy,CHUNKSIZE);
+ long bytes=(vf->callbacks.read_func)(buffer,1,CHUNKSIZE,vf->datasource);
+ ogg_sync_wrote(&vf->oy,bytes);
+ return(bytes);
+}
+
+/* save a tiny smidge of verbosity to make the code more readable */
+static void _seek_helper(OggVorbis_File *vf,long offset){
+ (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET);
+ vf->offset=offset;
+ ogg_sync_reset(&vf->oy);
+}
+
+/* The read/seek functions track absolute position within the stream */
+
+/* from the head of the stream, get the next page. boundary specifies
+ if the function is allowed to fetch more data from the stream (and
+ how much) or only use internally buffered data.
+
+ boundary: -1) unbounded search
+ 0) read no additional data; use cached only
+ n) search for a new page beginning for n bytes
+
+ return: -1) did not find a page
+ n) found a page at absolute offset n */
+
+static long _get_next_page(OggVorbis_File *vf,ogg_page *og,int boundary){
+ if(boundary>0)boundary+=vf->offset;
+ while(1){
+ long more;
+
+ if(boundary>0 && vf->offset>=boundary)return(-1);
+ more=ogg_sync_pageseek(&vf->oy,og);
+
+ if(more<0){
+ /* skipped n bytes */
+ vf->offset-=more;
+ }else{
+ if(more==0){
+ /* send more paramedics */
+ if(!boundary)return(-1);
+ if(_get_data(vf)<=0)return(-1);
+ }else{
+ /* got a page. Return the offset at the page beginning,
+ advance the internal offset past the page end */
+ long ret=vf->offset;
+ vf->offset+=more;
+ return(ret);
+
+ }
+ }
+ }
+}
+
+/* find the latest page beginning before the current stream cursor
+ position. Much dirtier than the above as Ogg doesn't have any
+ backward search linkage. no 'readp' as it will certainly have to
+ read. */
+static long _get_prev_page(OggVorbis_File *vf,ogg_page *og){
+ long begin=vf->offset;
+ long ret;
+ int offset=-1;
+
+ while(offset==-1){
+ begin-=CHUNKSIZE;
+ _seek_helper(vf,begin);
+ while(vf->offset<begin+CHUNKSIZE){
+ ret=_get_next_page(vf,og,begin+CHUNKSIZE-vf->offset);
+ if(ret==-1){
+ break;
+ }else{
+ offset=ret;
+ }
+ }
+ }
+
+ /* we have the offset. Actually snork and hold the page now */
+ _seek_helper(vf,offset);
+ ret=_get_next_page(vf,og,CHUNKSIZE);
+ if(ret==-1){
+ /* this shouldn't be possible */
+ fprintf(stderr,"Missed page fencepost at end of logical bitstream. "
+ "Exiting.\n");
+ exit(1);
+ }
+ return(offset);
+}
+
+/* finds each bitstream link one at a time using a bisection search
+ (has to begin by knowing the offset of the lb's initial page).
+ Recurses for each link so it can alloc the link storage after
+ finding them all, then unroll and fill the cache at the same time */
+static void _bisect_forward_serialno(OggVorbis_File *vf,
+ long begin,
+ long searched,
+ long end,
+ long currentno,
+ long m){
+ long endsearched=end;
+ long next=end;
+ ogg_page og;
+ long ret;
+
+ /* the below guards against garbage seperating the last and
+ first pages of two links. */
+ while(searched<endsearched){
+ long bisect;
+
+ if(endsearched-searched<CHUNKSIZE){
+ bisect=searched;
+ }else{
+ bisect=(searched+endsearched)/2;
+ }
+
+ _seek_helper(vf,bisect);
+ ret=_get_next_page(vf,&og,-1);
+ if(ret<0 || ogg_page_serialno(&og)!=currentno){
+ endsearched=bisect;
+ if(ret>=0)next=ret;
+ }else{
+ searched=ret+og.header_len+og.body_len;
+ }
+ }
+
+ _seek_helper(vf,next);
+ ret=_get_next_page(vf,&og,-1);
+
+ if(searched>=end || ret==-1){
+ vf->links=m+1;
+ vf->offsets=malloc((m+2)*sizeof(int64_t));
+ vf->offsets[m+1]=searched;
+ }else{
+ _bisect_forward_serialno(vf,next,vf->offset,
+ end,ogg_page_serialno(&og),m+1);
+ }
+
+ vf->offsets[m]=begin;
+}
+
+/* uses the local ogg_stream storage in vf; this is important for
+ non-streaming input sources */
+static int _fetch_headers(OggVorbis_File *vf,vorbis_info *vi,vorbis_comment *vc,
+ long *serialno){
+ ogg_page og;
+ ogg_packet op;
+ int i,ret;
+
+ ret=_get_next_page(vf,&og,CHUNKSIZE);
+ if(ret==-1){
+ fprintf(stderr,"Did not find initial header for bitstream.\n");
+ return -1;
+ }
+
+ if(serialno)*serialno=ogg_page_serialno(&og);
+ ogg_stream_init(&vf->os,ogg_page_serialno(&og));
+
+ /* extract the initial header from the first page and verify that the
+ Ogg bitstream is in fact Vorbis data */
+
+ vorbis_info_init(vi);
+ vorbis_comment_init(vc);
+
+ i=0;
+ while(i<3){
+ ogg_stream_pagein(&vf->os,&og);
+ while(i<3){
+ int result=ogg_stream_packetout(&vf->os,&op);
+ if(result==0)break;
+ if(result==-1){
+ fprintf(stderr,"Corrupt header in logical bitstream.\n");
+ goto bail_header;
+ }
+ if(vorbis_synthesis_headerin(vi,vc,&op)){
+ fprintf(stderr,"Illegal header in logical bitstream.\n");
+ goto bail_header;
+ }
+ i++;
+ }
+ if(i<3)
+ if(_get_next_page(vf,&og,1)<0){
+ fprintf(stderr,"Missing header in logical bitstream.\n");
+ goto bail_header;
+ }
+ }
+ 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) */
+static void _prefetch_all_headers(OggVorbis_File *vf,vorbis_info *first_i,
+ vorbis_comment *first_c,
+ long dataoffset){
+ ogg_page og;
+ int i,ret;
+
+ vf->vi=calloc(vf->links,sizeof(vorbis_info));
+ vf->vc=calloc(vf->links,sizeof(vorbis_info));
+ vf->dataoffsets=malloc(vf->links*sizeof(int64_t));
+ vf->pcmlengths=malloc(vf->links*sizeof(int64_t));
+ vf->serialnos=malloc(vf->links*sizeof(long));
+
+ for(i=0;i<vf->links;i++){
+ if(first_i && first_c && i==0){
+ /* we already grabbed the initial header earlier. This just
+ saves the waste of grabbing it again */
+ memcpy(vf->vi+i,first_i,sizeof(vorbis_info));
+ memcpy(vf->vc+i,first_c,sizeof(vorbis_comment));
+ vf->dataoffsets[i]=dataoffset;
+ }else{
+
+ /* seek to the location of the initial header */
+
+ _seek_helper(vf,vf->offsets[i]);
+ if(_fetch_headers(vf,vf->vi+i,vf->vc+i,NULL)==-1){
+ fprintf(stderr,"Error opening logical bitstream #%d.\n\n",i+1);
+ vf->dataoffsets[i]=-1;
+ }else{
+ vf->dataoffsets[i]=vf->offset;
+ ogg_stream_clear(&vf->os);
+ }
+ }
+
+ /* get the serial number and PCM length of this link. To do this,
+ get the last page of the stream */
+ {
+ long end=vf->offsets[i+1];
+ _seek_helper(vf,end);
+
+ while(1){
+ ret=_get_prev_page(vf,&og);
+ if(ret==-1){
+ /* this should not be possible */
+ fprintf(stderr,"Could not find last page of logical "
+ "bitstream #%d\n\n",i);
+ vorbis_info_clear(vf->vi+i);
+ vorbis_comment_clear(vf->vc+i);
+ break;
+ }
+ if(ogg_page_frameno(&og)!=-1){
+ vf->serialnos[i]=ogg_page_serialno(&og);
+ vf->pcmlengths[i]=ogg_page_frameno(&og);
+ break;
+ }
+ }
+ }
+ }
+}
+
+static int _make_decode_ready(OggVorbis_File *vf){
+ if(vf->decode_ready)exit(1);
+ vorbis_synthesis_init(&vf->vd,vf->vi);
+ vorbis_block_init(&vf->vd,&vf->vb);
+ vf->decode_ready=1;
+ return(0);
+}
+
+static int _open_seekable(OggVorbis_File *vf){
+ vorbis_info initial_i;
+ vorbis_comment initial_c;
+ long serialno,end;
+ int ret;
+ long dataoffset;
+ ogg_page og;
+
+ /* is this even vorbis...? */
+ ret=_fetch_headers(vf,&initial_i,&initial_c,&serialno);
+ dataoffset=vf->offset;
+ ogg_stream_clear(&vf->os);
+ if(ret==-1)return(-1);
+
+ /* we can seek, so set out learning all about this file */
+ vf->seekable=1;
+ (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END);
+ vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource);
+
+ /* 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(vf,&og);
+
+ /* moer than one logical bitstream? */
+ if(ogg_page_serialno(&og)!=serialno){
+
+ /* Chained bitstream. Bisect-search each logical bitstream
+ section. Do so based on serial number only */
+ _bisect_forward_serialno(vf,0,0,end+1,serialno,0);
+
+ }else{
+
+ /* Only one logical bitstream */
+ _bisect_forward_serialno(vf,0,end,end+1,serialno,0);
+
+ }
+
+ _prefetch_all_headers(vf,&initial_i,&initial_c,dataoffset);
+ return(ov_raw_seek(vf,0));
+
+}
+
+static int _open_nonseekable(OggVorbis_File *vf){
+ /* we cannot seek. Set up a 'single' (current) logical bitstream entry */
+ vf->links=1;
+ vf->vi=calloc(vf->links,sizeof(vorbis_info));
+ vf->vc=calloc(vf->links,sizeof(vorbis_info));
+
+ /* Try to fetch the headers, maintaining all the storage */
+ if(_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno)==-1)return(-1);
+ _make_decode_ready(vf);
+
+ return 0;
+}
+
+/* clear out the current logical bitstream decoder */
+static void _decode_clear(OggVorbis_File *vf){
+ ogg_stream_clear(&vf->os);
+ vorbis_dsp_clear(&vf->vd);
+ vorbis_block_clear(&vf->vb);
+ vf->decode_ready=0;
+
+ vf->bittrack=0.;
+ vf->samptrack=0.;
+}
+
+/* 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
+*/
+
+static int _process_packet(OggVorbis_File *vf,int readp){
+ ogg_page og;
+
+ /* handle one packet. Try to fetch it from current stream state */
+ /* extract packets from page */
+ while(1){
+
+ /* process a packet if we can. If the machine isn't loaded,
+ neither is a page */
+ if(vf->decode_ready){
+ ogg_packet op;
+ int result=ogg_stream_packetout(&vf->os,&op);
+ int64_t frameno;
+
+ /* 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 */
+ frameno=op.frameno;
+ if(!vorbis_synthesis(&vf->vb,&op)){ /* 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=vorbis_synthesis_pcmout(&vf->vd,NULL);
+ vorbis_synthesis_blockin(&vf->vd,&vf->vb);
+ vf->samptrack+=vorbis_synthesis_pcmout(&vf->vd,NULL)-oldsamples;
+ vf->bittrack+=op.bytes*8;
+ }
+
+ /* update the pcm offset. */
+ if(frameno!=-1 && !op.e_o_s){
+ int link=(vf->seekable?vf->current_link:0);
+ int i,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
+ frameno 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 frameno from an in-sequence page
+ to have a reference point. Thus the !op.e_o_s clause
+ above */
+
+ samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
+
+ frameno-=samples;
+ for(i=0;i<link;i++)
+ frameno+=vf->pcmlengths[i];
+ vf->pcm_offset=frameno;
+ }
+ return(1);
+ }
+ }
+ }
+
+ if(!readp)return(0);
+ if(_get_next_page(vf,&og,-1)<0)return(0); /* eof. leave unitialized */
+
+ /* bitrate tracking; add the header's bytes here, the body bytes
+ are done by packet above */
+ vf->bittrack+=og.header_len*8;
+
+ /* has our decoding just traversed a bitstream boundary? */
+ if(vf->decode_ready){
+ if(vf->current_serialno!=ogg_page_serialno(&og)){
+ _decode_clear(vf);
+ }
+ }
+
+ /* 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(!vf->decode_ready){
+ int link;
+ if(vf->seekable){
+ vf->current_serialno=ogg_page_serialno(&og);
+
+ /* match the serialno to bitstream section. We use this rather than
+ offset positions to avoid problems near logical bitstream
+ boundaries */
+ for(link=0;link<vf->links;link++)
+ if(vf->serialnos[link]==vf->current_serialno)break;
+ if(link==vf->links)return(-1); /* sign of a bogus stream. error out,
+ leave machine uninitialized */
+
+ vf->current_link=link;
+
+ ogg_stream_init(&vf->os,vf->current_serialno);
+ ogg_stream_reset(&vf->os);
+
+ }else{
+ /* we're streaming */
+ /* fetch the three header packets, build the info struct */
+
+ _fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno);
+ vf->current_link++;
+ link=0;
+ }
+
+ _make_decode_ready(vf);
+ }
+ ogg_stream_pagein(&vf->os,&og);
+ }
+}
+
+/**********************************************************************
+ * The helpers are over; it's all toplevel interface from here on out */
+
+/* clear out the OggVorbis_File struct */
+int ov_clear(OggVorbis_File *vf){
+ if(vf){
+ vorbis_block_clear(&vf->vb);
+ vorbis_dsp_clear(&vf->vd);
+ ogg_stream_clear(&vf->os);
+
+ if(vf->vi && vf->links){
+ int i;
+ for(i=0;i<vf->links;i++){
+ vorbis_info_clear(vf->vi+i);
+ vorbis_comment_clear(vf->vc+i);
+ }
+ free(vf->vi);
+ free(vf->vc);
+ }
+ if(vf->dataoffsets)free(vf->dataoffsets);
+ if(vf->pcmlengths)free(vf->pcmlengths);
+ if(vf->serialnos)free(vf->serialnos);
+ if(vf->offsets)free(vf->offsets);
+ ogg_sync_clear(&vf->oy);
+ if(vf->datasource)(vf->callbacks.close_func)(vf->datasource);
+ memset(vf,0,sizeof(OggVorbis_File));
+ }
+#ifdef DEBUG_LEAKS
+ _VDBG_dump();
+#endif
+ return(0);
+}
+
+static int _fseek64_wrap(FILE *f,int64_t off,int whence){
+ return fseek(f,(int)off,whence);
+}
+
+/* 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 ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){
+ ov_callbacks callbacks = {
+ (size_t (*)(void *, size_t, size_t, void *)) fread,
+ (int (*)(void *, int64_t, int)) _fseek64_wrap,
+ (int (*)(void *)) fclose,
+ (long (*)(void *)) ftell
+ };
+
+ return ov_open_callbacks((void *)f, vf, initial, ibytes, callbacks);
+}
+
+
+int ov_open_callbacks(void *f,OggVorbis_File *vf,char *initial,long ibytes,
+ ov_callbacks callbacks)
+{
+ long offset=callbacks.seek_func(f,0,SEEK_CUR);
+ int ret;
+
+ memset(vf,0,sizeof(OggVorbis_File));
+ vf->datasource=f;
+ vf->callbacks = callbacks;
+
+ /* init the framing state */
+ ogg_sync_init(&vf->oy);
+
+ /* 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){
+ char *buffer=ogg_sync_buffer(&vf->oy,ibytes);
+ memcpy(buffer,initial,ibytes);
+ ogg_sync_wrote(&vf->oy,ibytes);
+ }
+
+ /* can we seek? Stevens suggests the seek test was portable */
+ if(offset!=-1){
+ ret=_open_seekable(vf);
+ }else{
+ ret=_open_nonseekable(vf);
+ }
+ if(ret){
+ vf->datasource=NULL;
+ ov_clear(vf);
+ }
+ return(ret);
+}
+
+/* How many logical bitstreams in this physical bitstream? */
+long ov_streams(OggVorbis_File *vf){
+ return vf->links;
+}
+
+/* Is the FILE * associated with vf seekable? */
+long ov_seekable(OggVorbis_File *vf){
+ return vf->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 */
+
+long ov_bitrate(OggVorbis_File *vf,int i){
+ if(i>=vf->links)return(-1);
+ if(!vf->seekable && i!=0)return(ov_bitrate(vf,0));
+ if(i<0){
+ int64_t bits=0;
+ int i;
+ for(i=0;i<vf->links;i++)
+ bits+=(vf->offsets[i+1]-vf->dataoffsets[i])*8;
+ return(rint(bits/ov_time_total(vf,-1)));
+ }else{
+ if(vf->seekable){
+ /* return the actual bitrate */
+ return(rint((vf->offsets[i+1]-vf->dataoffsets[i])*8/ov_time_total(vf,i)));
+ }else{
+ /* return nominal if set */
+ if(vf->vi[i].bitrate_nominal>0){
+ return vf->vi[i].bitrate_nominal;
+ }else{
+ if(vf->vi[i].bitrate_upper>0){
+ if(vf->vi[i].bitrate_lower>0){
+ return (vf->vi[i].bitrate_upper+vf->vi[i].bitrate_lower)/2;
+ }else{
+ return vf->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) */
+long ov_bitrate_instant(OggVorbis_File *vf){
+ int link=(vf->seekable?vf->current_link:0);
+ long ret;
+ if(vf->samptrack==0)return(-1);
+ ret=vf->bittrack/vf->samptrack*vf->vi[link].rate+.5;
+ vf->bittrack=0.;
+ vf->samptrack=0.;
+ return(ret);
+}
+
+/* Guess */
+long ov_serialnumber(OggVorbis_File *vf,int i){
+ if(i>=vf->links)return(-1);
+ if(!vf->seekable && i>=0)return(ov_serialnumber(vf,-1));
+ if(i<0){
+ return(vf->current_serialno);
+ }else{
+ return(vf->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)
+*/
+int64_t ov_raw_total(OggVorbis_File *vf,int i){
+ if(!vf->seekable || i>=vf->links)return(-1);
+ if(i<0){
+ long acc=0;
+ int i;
+ for(i=0;i<vf->links;i++)
+ acc+=ov_raw_total(vf,i);
+ return(acc);
+ }else{
+ return(vf->offsets[i+1]-vf->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)
+*/
+int64_t ov_pcm_total(OggVorbis_File *vf,int i){
+ if(!vf->seekable || i>=vf->links)return(-1);
+ if(i<0){
+ int64_t acc=0;
+ int i;
+ for(i=0;i<vf->links;i++)
+ acc+=ov_pcm_total(vf,i);
+ return(acc);
+ }else{
+ return(vf->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)
+*/
+double ov_time_total(OggVorbis_File *vf,int i){
+ if(!vf->seekable || i>=vf->links)return(-1);
+ if(i<0){
+ double acc=0;
+ int i;
+ for(i=0;i<vf->links;i++)
+ acc+=ov_time_total(vf,i);
+ return(acc);
+ }else{
+ return((float)(vf->pcmlengths[i])/vf->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 */
+
+int ov_raw_seek(OggVorbis_File *vf,long pos){
+
+ if(!vf->seekable)return(-1); /* don't dump machine if we can't seek */
+ if(pos<0 || pos>vf->offsets[vf->links])goto seek_error;
+
+ /* clear out decoding machine state */
+ vf->pcm_offset=-1;
+ _decode_clear(vf);
+
+ /* seek */
+ _seek_helper(vf,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 'frameno' field set, and that's how the
+ helper updates the offset */
+
+ switch(_process_packet(vf,1)){
+ case 0:
+ /* oh, eof. There are no packets remaining. Set the pcm offset to
+ the end of file */
+ vf->pcm_offset=ov_pcm_total(vf,-1);
+ return(0);
+ case -1:
+ /* error! missing data or invalid bitstream structure */
+ goto seek_error;
+ default:
+ /* all OK */
+ break;
+ }
+
+ while(1){
+ switch(_process_packet(vf,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;
+ default:
+ /* continue processing packets */
+ break;
+ }
+ }
+
+ seek_error:
+ /* dump the machine so we're in a known state */
+ vf->pcm_offset=-1;
+ _decode_clear(vf);
+ return -1;
+}
+
+/* seek to a sample offset relative to the decompressed pcm stream
+
+ returns zero on success, nonzero on failure */
+
+int ov_pcm_seek(OggVorbis_File *vf,int64_t pos){
+ int link=-1;
+ int64_t total=ov_pcm_total(vf,-1);
+
+ if(!vf->seekable)return(-1); /* don't dump machine if we can't seek */
+ if(pos<0 || pos>total)goto seek_error;
+
+ /* which bitstream section does this pcm offset occur in? */
+ for(link=vf->links-1;link>=0;link--){
+ total-=vf->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) */
+ {
+ int64_t target=pos-total;
+ long end=vf->offsets[link+1];
+ long begin=vf->offsets[link];
+ long best=begin;
+
+ ogg_page og;
+ while(begin<end){
+ long bisect;
+ long ret;
+
+ if(end-begin<CHUNKSIZE){
+ bisect=begin;
+ }else{
+ bisect=(end+begin)/2;
+ }
+
+ _seek_helper(vf,bisect);
+ ret=_get_next_page(vf,&og,end-bisect);
+
+ if(ret==-1){
+ end=bisect;
+ }else{
+ int64_t frameno=ogg_page_frameno(&og);
+ if(frameno<target){
+ best=ret; /* raw offset of packet with frameno */
+ begin=vf->offset; /* raw offset of next packet */
+ }else{
+ end=bisect;
+ }
+ }
+ }
+
+ /* found our page. seek to it (call raw_seek). */
+
+ if(ov_raw_seek(vf,best))goto seek_error;
+ }
+
+ /* verify result */
+ if(vf->pcm_offset>=pos)goto seek_error;
+ if(pos>ov_pcm_total(vf,-1))goto seek_error;
+
+ /* discard samples until we reach the desired position. Crossing a
+ logical bitstream boundary with abandon is OK. */
+ while(vf->pcm_offset<pos){
+ double **pcm;
+ long target=pos-vf->pcm_offset;
+ long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
+
+ if(samples>target)samples=target;
+ vorbis_synthesis_read(&vf->vd,samples);
+ vf->pcm_offset+=samples;
+
+ if(samples<target)
+ if(_process_packet(vf,1)==0)
+ vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */
+ }
+ return 0;
+
+ seek_error:
+ /* dump machine so we're in a known state */
+ vf->pcm_offset=-1;
+ _decode_clear(vf);
+ return -1;
+}
+
+/* seek to a playback time relative to the decompressed pcm stream
+ returns zero on success, nonzero on failure */
+int ov_time_seek(OggVorbis_File *vf,double seconds){
+ /* translate time to PCM position and call ov_pcm_seek */
+
+ int link=-1;
+ int64_t pcm_total=ov_pcm_total(vf,-1);
+ double time_total=ov_time_total(vf,-1);
+
+ if(!vf->seekable)return(-1); /* don't dump machine if we can't seek */
+ if(seconds<0 || seconds>time_total)goto seek_error;
+
+ /* which bitstream section does this time offset occur in? */
+ for(link=vf->links-1;link>=0;link--){
+ pcm_total-=vf->pcmlengths[link];
+ time_total-=ov_time_total(vf,link);
+ if(seconds>=time_total)break;
+ }
+
+ /* enough information to convert time offset to pcm offset */
+ {
+ int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate;
+ return(ov_pcm_seek(vf,target));
+ }
+
+ seek_error:
+ /* dump machine so we're in a known state */
+ vf->pcm_offset=-1;
+ _decode_clear(vf);
+ return -1;
+}
+
+/* tell the current stream offset cursor. Note that seek followed by
+ tell will likely not give the set offset due to caching */
+int64_t ov_raw_tell(OggVorbis_File *vf){
+ return(vf->offset);
+}
+
+/* return PCM offset (sample) of next PCM sample to be read */
+int64_t ov_pcm_tell(OggVorbis_File *vf){
+ return(vf->pcm_offset);
+}
+
+/* return time offset (seconds) of next PCM sample to be read */
+double ov_time_tell(OggVorbis_File *vf){
+ /* translate time to PCM position and call ov_pcm_seek */
+
+ int link=-1;
+ int64_t pcm_total=0;
+ double time_total=0.;
+
+ if(vf->seekable){
+ pcm_total=ov_pcm_total(vf,-1);
+ time_total=ov_time_total(vf,-1);
+
+ /* which bitstream section does this time offset occur in? */
+ for(link=vf->links-1;link>=0;link--){
+ pcm_total-=vf->pcmlengths[link];
+ time_total-=ov_time_total(vf,link);
+ if(vf->pcm_offset>=pcm_total)break;
+ }
+ }
+
+ return((double)time_total+(double)(vf->pcm_offset-pcm_total)/vf->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 */
+
+vorbis_info *ov_info(OggVorbis_File *vf,int link){
+ if(vf->seekable){
+ if(link<0)
+ if(vf->decode_ready)
+ return vf->vi+vf->current_link;
+ else
+ return NULL;
+ else
+ if(link>=vf->links)
+ return NULL;
+ else
+ return vf->vi+link;
+ }else{
+ if(vf->decode_ready)
+ return vf->vi;
+ else
+ return NULL;
+ }
+}
+
+/* grr, strong typing, grr, no templates/inheritence, grr */
+vorbis_comment *ov_comment(OggVorbis_File *vf,int link){
+ if(vf->seekable){
+ if(link<0)
+ if(vf->decode_ready)
+ return vf->vc+vf->current_link;
+ else
+ return NULL;
+ else
+ if(link>=vf->links)
+ return NULL;
+ else
+ return vf->vc+link;
+ }else{
+ if(vf->decode_ready)
+ return vf->vc;
+ else
+ return NULL;
+ }
+}
+
+int host_is_big_endian() {
+ 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.
+
+ ov_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 */
+
+long ov_read(OggVorbis_File *vf,char *buffer,int length,
+ int bigendianp,int word,int sgned,int *bitstream){
+ int i,j;
+ int host_endian = host_is_big_endian();
+
+ while(1){
+ if(vf->decode_ready){
+ double **pcm;
+ long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
+ if(samples){
+ /* yay! proceed to pack data into the byte buffer */
+
+ long channels=ov_info(vf,-1)->channels;
+ long 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:128);
+ for(j=0;j<samples;j++)
+ for(i=0;i<channels;i++){
+ val=(int)(pcm[i][j]*128. + 0.5);
+ if(val>127)val=127;
+ else if(val<-128)val=-128;
+ *buffer++=val+off;
+ }
+ }else{
+ int off=(sgned?0:32768);
+
+ if(host_endian==bigendianp){
+ if(sgned){
+ for(i=0;i<channels;i++) { /* It's faster in this order */
+ double *src=pcm[i];
+ short *dest=((short *)buffer)+i;
+ for(j=0;j<samples;j++) {
+ val=(int)(src[j]*32768. + 0.5);
+ if(val>32767)val=32767;
+ else if(val<-32768)val=-32768;
+ *dest=val;
+ dest+=channels;
+ }
+ }
+ }else{
+ for(i=0;i<channels;i++) {
+ double *src=pcm[i];
+ short *dest=((short *)buffer)+i;
+ for(j=0;j<samples;j++) {
+ val=(int)(src[j]*32768. + 0.5);
+ if(val>32767)val=32767;
+ else if(val<-32768)val=-32768;
+ *dest=val+off;
+ dest+=channels;
+ }
+ }
+ }
+ }else if(bigendianp){
+ for(j=0;j<samples;j++)
+ for(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++=(val>>8);
+ *buffer++=(val&0xff);
+ }
+ }else{
+ int val;
+ for(j=0;j<samples;j++)
+ for(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++=(val&0xff);
+ *buffer++=(val>>8);
+ }
+
+ }
+ }
+ }
+
+ vorbis_synthesis_read(&vf->vd,samples);
+ vf->pcm_offset+=samples;
+ if(bitstream)*bitstream=vf->current_link;
+ return(samples*bytespersample);
+ }
+ }
+
+ /* suck in another packet */
+ switch(_process_packet(vf,1)){
+ case 0:
+ return(0);
+ case -1:
+ return -1;
+ default:
+ break;
+ }
+ }
+}
+
+
+
+