summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMonty <xiphmont@xiph.org>2000-10-14 03:14:07 +0000
committerMonty <xiphmont@xiph.org>2000-10-14 03:14:07 +0000
commita454e26f286b9b9503b10d092110e326a78cebc4 (patch)
treef97fd91a912af87638d901c82de2befe0a68075a
downloadlibvorbis-git-a454e26f286b9b9503b10d092110e326a78cebc4.tar.gz
First take at adding real eror return codes to libvorbis and
libvorbisfile. The change is complete, but not really well tested. Also stripped all the fprintfs from vorbisfile. Monty svn path=/branches/branch_beta3/vorbis/; revision=736
-rw-r--r--include/vorbis/codec.h337
-rw-r--r--lib/analysis.c105
-rw-r--r--lib/block.c729
-rw-r--r--lib/info.c558
-rw-r--r--lib/synthesis.c74
-rw-r--r--lib/vorbis-errors.txt83
-rw-r--r--lib/vorbisfile.c1223
7 files changed, 3109 insertions, 0 deletions
diff --git a/include/vorbis/codec.h b/include/vorbis/codec.h
new file mode 100644
index 00000000..81aafcc2
--- /dev/null
+++ b/include/vorbis/codec.h
@@ -0,0 +1,337 @@
+/********************************************************************
+ * *
+ * 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: libvorbis codec headers
+ last mod: $Id: codec.h,v 1.32.2.1 2000/10/14 03:14:06 xiphmont Exp $
+
+ ********************************************************************/
+
+#ifndef _vorbis_codec_h_
+#define _vorbis_codec_h_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#define MAX_BARK 27
+
+#include <ogg/ogg.h>
+#include "vorbis/codebook.h"
+
+typedef void vorbis_look_transform;
+typedef void vorbis_info_time;
+typedef void vorbis_look_time;
+typedef void vorbis_info_floor;
+typedef void vorbis_look_floor;
+typedef void vorbis_echstate_floor;
+typedef void vorbis_info_residue;
+typedef void vorbis_look_residue;
+typedef void vorbis_info_mapping;
+typedef void vorbis_look_mapping;
+
+/* mode ************************************************************/
+typedef struct {
+ int blockflag;
+ int windowtype;
+ int transformtype;
+ int mapping;
+} vorbis_info_mode;
+
+/* psychoacoustic setup ********************************************/
+#define P_BANDS 17
+#define P_LEVELS 11
+typedef struct vorbis_info_psy{
+ int athp;
+ int decayp;
+ int smoothp;
+
+ int noisecullp;
+ float noisecull_barkwidth;
+
+ float ath_adjatt;
+ float ath_maxatt;
+
+ /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 */
+ /* x: 63 88 125 175 250 350 500 700 1k 1.4k 2k 2.8k 4k 5.6k 8k 11.5k 16k Hz */
+ /* y: 0 10 20 30 40 50 60 70 80 90 100 dB */
+
+ int tonemaskp;
+ float toneatt[P_BANDS][P_LEVELS];
+
+ int peakattp;
+ float peakatt[P_BANDS][P_LEVELS];
+
+ int noisemaskp;
+ float noiseatt[P_BANDS][P_LEVELS];
+
+ float max_curve_dB;
+
+ /* decay setup */
+ float attack_coeff;
+ float decay_coeff;
+} vorbis_info_psy;
+
+/* vorbis_info contains all the setup information specific to the
+ specific compression/decompression mode in progress (eg,
+ psychoacoustic settings, channel setup, options, codebook
+ etc).
+*********************************************************************/
+
+typedef struct vorbis_info{
+ int version;
+ int channels;
+ long rate;
+
+ /* The below bitrate declarations are *hints*.
+ Combinations of the three values carry the following implications:
+
+ all three set to the same value:
+ implies a fixed rate bitstream
+ only nominal set:
+ implies a VBR stream that averages the nominal bitrate. No hard
+ upper/lower limit
+ upper and or lower set:
+ implies a VBR bitstream that obeys the bitrate limits. nominal
+ may also be set to give a nominal rate.
+ none set:
+ the coder does not care to speculate.
+ */
+
+ long bitrate_upper;
+ long bitrate_nominal;
+ long bitrate_lower;
+
+ /* Vorbis supports only short and long blocks, but allows the
+ encoder to choose the sizes */
+
+ long blocksizes[2];
+
+ /* modes are the primary means of supporting on-the-fly different
+ blocksizes, different channel mappings (LR or mid-side),
+ different residue backends, etc. Each mode consists of a
+ blocksize flag and a mapping (along with the mapping setup */
+
+ int modes;
+ int maps;
+ int times;
+ int floors;
+ int residues;
+ int books;
+ int psys; /* encode only */
+
+ vorbis_info_mode *mode_param[64];
+ int map_type[64];
+ vorbis_info_mapping *map_param[64];
+ int time_type[64];
+ vorbis_info_time *time_param[64];
+ int floor_type[64];
+ vorbis_info_floor *floor_param[64];
+ int residue_type[64];
+ vorbis_info_residue *residue_param[64];
+ static_codebook *book_param[256];
+ vorbis_info_psy *psy_param[64]; /* encode only */
+
+ /* for block long/sort tuning; encode only */
+ int envelopesa;
+ float preecho_thresh;
+ float preecho_clamp;
+ float preecho_minenergy;
+} vorbis_info;
+
+/* vorbis_dsp_state buffers the current vorbis audio
+ analysis/synthesis state. The DSP state belongs to a specific
+ logical bitstream ****************************************************/
+typedef struct vorbis_dsp_state{
+ int analysisp;
+ vorbis_info *vi;
+ int modebits;
+
+ float **pcm;
+ float **pcmret;
+ int pcm_storage;
+ int pcm_current;
+ int pcm_returned;
+
+ int preextrapolate;
+ int eofflag;
+
+ long lW;
+ long W;
+ long nW;
+ long centerW;
+
+ ogg_int64_t granulepos;
+ ogg_int64_t sequence;
+
+ ogg_int64_t glue_bits;
+ ogg_int64_t time_bits;
+ ogg_int64_t floor_bits;
+ ogg_int64_t res_bits;
+
+ /* local lookup storage */
+ void *ve; /* envelope lookup */
+ float **window[2][2][2]; /* block, leadin, leadout, type */
+ vorbis_look_transform **transform[2]; /* block, type */
+ codebook *fullbooks;
+ /* backend lookups are tied to the mode, not the backend or naked mapping */
+ vorbis_look_mapping **mode;
+
+ /* local storage, only used on the encoding side. This way the
+ application does not need to worry about freeing some packets'
+ memory and not others'; packet storage is always tracked.
+ Cleared next call to a _dsp_ function */
+ unsigned char *header;
+ unsigned char *header1;
+ unsigned char *header2;
+
+} vorbis_dsp_state;
+
+/* vorbis_block is a single block of data to be processed as part of
+the analysis/synthesis stream; it belongs to a specific logical
+bitstream, but is independant from other vorbis_blocks belonging to
+that logical bitstream. *************************************************/
+
+struct alloc_chain{
+ void *ptr;
+ struct alloc_chain *next;
+};
+
+typedef struct vorbis_block{
+ /* necessary stream state for linking to the framing abstraction */
+ float **pcm; /* this is a pointer into local storage */
+ oggpack_buffer opb;
+
+ long lW;
+ long W;
+ long nW;
+ int pcmend;
+ int mode;
+
+ int eofflag;
+ ogg_int64_t granulepos;
+ ogg_int64_t sequence;
+ vorbis_dsp_state *vd; /* For read-only access of configuration */
+
+ /* local storage to avoid remallocing; it's up to the mapping to
+ structure it */
+ void *localstore;
+ long localtop;
+ long localalloc;
+ long totaluse;
+ struct alloc_chain *reap;
+
+ /* bitmetrics for the frame */
+ long glue_bits;
+ long time_bits;
+ long floor_bits;
+ long res_bits;
+
+} vorbis_block;
+
+#include "vorbis/backends.h"
+
+/* vorbis_info contains all the setup information specific to the
+ specific compression/decompression mode in progress (eg,
+ psychoacoustic settings, channel setup, options, codebook
+ etc). vorbis_info and substructures are in backends.h.
+*********************************************************************/
+
+/* the comments are not part of vorbis_info so that vorbis_info can be
+ static storage */
+typedef struct vorbis_comment{
+ /* unlimited user comment fields. libvorbis writes 'libvorbis'
+ whatever vendor is set to in encode */
+ char **user_comments;
+ int *comment_lengths;
+ int comments;
+ char *vendor;
+
+} vorbis_comment;
+
+
+/* libvorbis encodes in two abstraction layers; first we perform DSP
+ and produce a packet (see docs/analysis.txt). The packet is then
+ coded into a framed OggSquish bitstream by the second layer (see
+ docs/framing.txt). Decode is the reverse process; we sync/frame
+ the bitstream and extract individual packets, then decode the
+ packet back into PCM audio.
+
+ The extra framing/packetizing is used in streaming formats, such as
+ files. Over the net (such as with UDP), the framing and
+ packetization aren't necessary as they're provided by the transport
+ and the streaming layer is not used */
+
+/* Vorbis PRIMITIVES: general ***************************************/
+
+extern void vorbis_info_init(vorbis_info *vi);
+extern void vorbis_info_clear(vorbis_info *vi);
+extern void vorbis_comment_init(vorbis_comment *vc);
+extern void vorbis_comment_add(vorbis_comment *vc, char *comment);
+extern void vorbis_comment_add_tag(vorbis_comment *vc,
+ char *tag, char *contents);
+extern char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count);
+extern int vorbis_comment_query_count(vorbis_comment *vc, char *tag);
+extern void vorbis_comment_clear(vorbis_comment *vc);
+
+extern int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb);
+extern int vorbis_block_clear(vorbis_block *vb);
+extern void vorbis_dsp_clear(vorbis_dsp_state *v);
+
+/* Vorbis PRIMITIVES: analysis/DSP layer ****************************/
+
+extern int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi);
+extern int vorbis_analysis_headerout(vorbis_dsp_state *v,
+ vorbis_comment *vc,
+ ogg_packet *op,
+ ogg_packet *op_comm,
+ ogg_packet *op_code);
+extern float **vorbis_analysis_buffer(vorbis_dsp_state *v,int vals);
+extern int vorbis_analysis_wrote(vorbis_dsp_state *v,int vals);
+extern int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb);
+extern int vorbis_analysis(vorbis_block *vb,ogg_packet *op);
+
+/* Vorbis PRIMITIVES: synthesis layer *******************************/
+extern int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,
+ ogg_packet *op);
+
+extern int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi);
+extern int vorbis_synthesis(vorbis_block *vb,ogg_packet *op);
+extern int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb);
+extern int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm);
+extern int vorbis_synthesis_read(vorbis_dsp_state *v,int samples);
+
+/* Vorbis ERRORS and return codes ***********************************/
+
+#define OV_FALSE -1
+#define OV_EOF -2
+#define OV_HOLE -3
+
+#define OV_EREAD -128
+#define OV_EFAULT -129
+#define OV_EIMPL -130
+#define OV_EINVAL -131
+#define OV_ENOTVORBIS -132
+#define OV_EBADHEADER -133
+#define OV_EVERSION -134
+#define OV_ENOTAUDIO -135
+#define OV_EBADPACKET -136
+#define OV_EBADLINK -137
+#define OV_ENOSEEK -138
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
diff --git a/lib/analysis.c b/lib/analysis.c
new file mode 100644
index 00000000..a6af547a
--- /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.34.2.1 2000/10/14 03:14:06 xiphmont Exp $
+
+ ********************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <ogg/ogg.h>
+#include "vorbis/codec.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,ret;
+ 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((ret=_mapping_P[type]->forward(vb,vd->mode[mode])))
+ return(ret);
+
+ /* set up the packet wrapper */
+
+ op->packet=oggpack_get_buffer(&vb->opb);
+ op->bytes=oggpack_bytes(&vb->opb);
+ op->b_o_s=0;
+ op->e_o_s=vb->eofflag;
+ op->granulepos=vb->granulepos;
+ 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,float *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/block.c b/lib/block.c
new file mode 100644
index 00000000..fe49609a
--- /dev/null
+++ b/lib/block.c
@@ -0,0 +1,729 @@
+/********************************************************************
+ * *
+ * 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: PCM data vector blocking, windowing and dis/reassembly
+ last mod: $Id: block.c,v 1.39.2.1 2000/10/14 03:14:06 xiphmont Exp $
+
+ Handle windowing, overlap-add, etc of the PCM vectors. This is made
+ more amusing by Vorbis' current two allowed block sizes.
+
+ Vorbis manipulates the dynamic range of the incoming PCM data
+ envelope to minimise time-domain energy leakage from percussive and
+ plosive waveforms being quantized in the MDCT domain.
+
+ ********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ogg/ogg.h>
+#include "vorbis/codec.h"
+
+#include "window.h"
+#include "envelope.h"
+#include "mdct.h"
+#include "lpc.h"
+#include "registry.h"
+#include "sharedbook.h"
+#include "bookinternal.h"
+#include "misc.h"
+#include "os.h"
+
+static int ilog2(unsigned int v){
+ int ret=0;
+ while(v>1){
+ ret++;
+ v>>=1;
+ }
+ return(ret);
+}
+
+/* pcm accumulator examples (not exhaustive):
+
+ <-------------- lW ---------------->
+ <--------------- W ---------------->
+: .....|..... _______________ |
+: .''' | '''_--- | |\ |
+:.....''' |_____--- '''......| | \_______|
+:.................|__________________|_______|__|______|
+ |<------ Sl ------>| > Sr < |endW
+ |beginSl |endSl | |endSr
+ |beginW |endlW |beginSr
+
+
+ |< lW >|
+ <--------------- W ---------------->
+ | | .. ______________ |
+ | | ' `/ | ---_ |
+ |___.'___/`. | ---_____|
+ |_______|__|_______|_________________|
+ | >|Sl|< |<------ Sr ----->|endW
+ | | |endSl |beginSr |endSr
+ |beginW | |endlW
+ mult[0] |beginSl mult[n]
+
+ <-------------- lW ----------------->
+ |<--W-->|
+: .............. ___ | |
+: .''' |`/ \ | |
+:.....''' |/`....\|...|
+:.........................|___|___|___|
+ |Sl |Sr |endW
+ | | |endSr
+ | |beginSr
+ | |endSl
+ |beginSl
+ |beginW
+*/
+
+/* block abstraction setup *********************************************/
+
+#ifndef WORD_ALIGN
+#define WORD_ALIGN 8
+#endif
+
+int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){
+ memset(vb,0,sizeof(vorbis_block));
+ vb->vd=v;
+ vb->localalloc=0;
+ vb->localstore=NULL;
+ if(v->analysisp)
+ oggpack_writeinit(&vb->opb);
+
+ return(0);
+}
+
+void *_vorbis_block_alloc(vorbis_block *vb,long bytes){
+ bytes=(bytes+(WORD_ALIGN-1)) & ~(WORD_ALIGN-1);
+ if(bytes+vb->localtop>vb->localalloc){
+ /* can't just realloc... there are outstanding pointers */
+ if(vb->localstore){
+ struct alloc_chain *link=malloc(sizeof(struct alloc_chain));
+ vb->totaluse+=vb->localtop;
+ link->next=vb->reap;
+ link->ptr=vb->localstore;
+ vb->reap=link;
+ }
+ /* highly conservative */
+ vb->localalloc=bytes;
+ vb->localstore=malloc(vb->localalloc);
+ vb->localtop=0;
+ }
+ {
+ void *ret=(void *)(((char *)vb->localstore)+vb->localtop);
+ vb->localtop+=bytes;
+ return ret;
+ }
+}
+
+/* reap the chain, pull the ripcord */
+void _vorbis_block_ripcord(vorbis_block *vb){
+ /* reap the chain */
+ struct alloc_chain *reap=vb->reap;
+ while(reap){
+ struct alloc_chain *next=reap->next;
+ free(reap->ptr);
+ memset(reap,0,sizeof(struct alloc_chain));
+ free(reap);
+ reap=next;
+ }
+ /* consolidate storage */
+ if(vb->totaluse){
+ vb->localstore=realloc(vb->localstore,vb->totaluse+vb->localalloc);
+ vb->localalloc+=vb->totaluse;
+ vb->totaluse=0;
+ }
+
+ /* pull the ripcord */
+ vb->localtop=0;
+ vb->reap=NULL;
+}
+
+int vorbis_block_clear(vorbis_block *vb){
+ if(vb->vd)
+ if(vb->vd->analysisp)
+ oggpack_writeclear(&vb->opb);
+ _vorbis_block_ripcord(vb);
+ if(vb->localstore)free(vb->localstore);
+
+ memset(vb,0,sizeof(vorbis_block));
+ return(0);
+}
+
+/* Analysis side code, but directly related to blocking. Thus it's
+ here and not in analysis.c (which is for analysis transforms only).
+ The init is here because some of it is shared */
+
+static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){
+ int i;
+ memset(v,0,sizeof(vorbis_dsp_state));
+
+ v->vi=vi;
+ v->modebits=ilog2(vi->modes);
+
+ v->transform[0]=calloc(VI_TRANSFORMB,sizeof(vorbis_look_transform *));
+ v->transform[1]=calloc(VI_TRANSFORMB,sizeof(vorbis_look_transform *));
+
+ /* MDCT is tranform 0 */
+
+ v->transform[0][0]=calloc(1,sizeof(mdct_lookup));
+ v->transform[1][0]=calloc(1,sizeof(mdct_lookup));
+ mdct_init(v->transform[0][0],vi->blocksizes[0]);
+ mdct_init(v->transform[1][0],vi->blocksizes[1]);
+
+ v->window[0][0][0]=calloc(VI_WINDOWB,sizeof(float *));
+ v->window[0][0][1]=v->window[0][0][0];
+ v->window[0][1][0]=v->window[0][0][0];
+ v->window[0][1][1]=v->window[0][0][0];
+ v->window[1][0][0]=calloc(VI_WINDOWB,sizeof(float *));
+ v->window[1][0][1]=calloc(VI_WINDOWB,sizeof(float *));
+ v->window[1][1][0]=calloc(VI_WINDOWB,sizeof(float *));
+ v->window[1][1][1]=calloc(VI_WINDOWB,sizeof(float *));
+
+ for(i=0;i<VI_WINDOWB;i++){
+ v->window[0][0][0][i]=
+ _vorbis_window(i,vi->blocksizes[0],vi->blocksizes[0]/2,vi->blocksizes[0]/2);
+ v->window[1][0][0][i]=
+ _vorbis_window(i,vi->blocksizes[1],vi->blocksizes[0]/2,vi->blocksizes[0]/2);
+ v->window[1][0][1][i]=
+ _vorbis_window(i,vi->blocksizes[1],vi->blocksizes[0]/2,vi->blocksizes[1]/2);
+ v->window[1][1][0][i]=
+ _vorbis_window(i,vi->blocksizes[1],vi->blocksizes[1]/2,vi->blocksizes[0]/2);
+ v->window[1][1][1][i]=
+ _vorbis_window(i,vi->blocksizes[1],vi->blocksizes[1]/2,vi->blocksizes[1]/2);
+ }
+
+ if(encp){ /* encode/decode differ here */
+ /* finish the codebooks */
+ v->fullbooks=calloc(vi->books,sizeof(codebook));
+ for(i=0;i<vi->books;i++)
+ vorbis_book_init_encode(v->fullbooks+i,vi->book_param[i]);
+ v->analysisp=1;
+ }else{
+ /* finish the codebooks */
+ v->fullbooks=calloc(vi->books,sizeof(codebook));
+ for(i=0;i<vi->books;i++)
+ vorbis_book_init_decode(v->fullbooks+i,vi->book_param[i]);
+ }
+
+ /* initialize the storage vectors to a decent size greater than the
+ minimum */
+
+ v->pcm_storage=8192; /* we'll assume later that we have
+ a minimum of twice the blocksize of
+ accumulated samples in analysis */
+ v->pcm=malloc(vi->channels*sizeof(float *));
+ v->pcmret=malloc(vi->channels*sizeof(float *));
+ {
+ int i;
+ for(i=0;i<vi->channels;i++)
+ v->pcm[i]=calloc(v->pcm_storage,sizeof(float));
+ }
+
+ /* all 1 (large block) or 0 (small block) */
+ /* explicitly set for the sake of clarity */
+ v->lW=0; /* previous window size */
+ v->W=0; /* current window size */
+
+ /* all vector indexes */
+ v->centerW=vi->blocksizes[1]/2;
+
+ v->pcm_current=v->centerW;
+
+ /* initialize all the mapping/backend lookups */
+ v->mode=calloc(vi->modes,sizeof(vorbis_look_mapping *));
+ for(i=0;i<vi->modes;i++){
+ int mapnum=vi->mode_param[i]->mapping;
+ int maptype=vi->map_type[mapnum];
+ v->mode[i]=_mapping_P[maptype]->look(v,vi->mode_param[i],
+ vi->map_param[mapnum]);
+ }
+
+ return(0);
+}
+
+/* arbitrary settings and spec-mandated numbers get filled in here */
+int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi){
+ _vds_shared_init(v,vi,1);
+
+ /* Initialize the envelope state storage */
+ v->ve=calloc(1,sizeof(envelope_lookup));
+ _ve_envelope_init(v->ve,vi);
+
+ return(0);
+}
+
+void vorbis_dsp_clear(vorbis_dsp_state *v){
+ int i,j,k;
+ if(v){
+ vorbis_info *vi=v->vi;
+
+ if(v->window[0][0][0]){
+ for(i=0;i<VI_WINDOWB;i++)
+ if(v->window[0][0][0][i])free(v->window[0][0][0][i]);
+ free(v->window[0][0][0]);
+
+ for(j=0;j<2;j++)
+ for(k=0;k<2;k++){
+ for(i=0;i<VI_WINDOWB;i++)
+ if(v->window[1][j][k][i])free(v->window[1][j][k][i]);
+ free(v->window[1][j][k]);
+ }
+ }
+
+ if(v->pcm){
+ for(i=0;i<vi->channels;i++)
+ if(v->pcm[i])free(v->pcm[i]);
+ free(v->pcm);
+ if(v->pcmret)free(v->pcmret);
+ }
+
+ if(v->ve){
+ _ve_envelope_clear(v->ve);
+ free(v->ve);
+ }
+
+ if(v->transform[0]){
+ mdct_clear(v->transform[0][0]);
+ free(v->transform[0][0]);
+ free(v->transform[0]);
+ }
+ if(v->transform[1]){
+ mdct_clear(v->transform[1][0]);
+ free(v->transform[1][0]);
+ free(v->transform[1]);
+ }
+
+ /* free mode lookups; these are actually vorbis_look_mapping structs */
+ if(vi){
+ for(i=0;i<vi->modes;i++){
+ int mapnum=vi->mode_param[i]->mapping;
+ int maptype=vi->map_type[mapnum];
+ _mapping_P[maptype]->free_look(v->mode[i]);
+ }
+ /* free codebooks */
+ for(i=0;i<vi->books;i++)
+ vorbis_book_clear(v->fullbooks+i);
+ }
+
+ if(v->mode)free(v->mode);
+ if(v->fullbooks)free(v->fullbooks);
+
+ /* free header, header1, header2 */
+ if(v->header)free(v->header);
+ if(v->header1)free(v->header1);
+ if(v->header2)free(v->header2);
+
+ memset(v,0,sizeof(vorbis_dsp_state));
+ }
+}
+
+float **vorbis_analysis_buffer(vorbis_dsp_state *v, int vals){
+ int i;
+ vorbis_info *vi=v->vi;
+
+ /* free header, header1, header2 */
+ if(v->header)free(v->header);v->header=NULL;
+ if(v->header1)free(v->header1);v->header1=NULL;
+ if(v->header2)free(v->header2);v->header2=NULL;
+
+ /* Do we have enough storage space for the requested buffer? If not,
+ expand the PCM (and envelope) storage */
+
+ if(v->pcm_current+vals>=v->pcm_storage){
+ v->pcm_storage=v->pcm_current+vals*2;
+
+ for(i=0;i<vi->channels;i++){
+ v->pcm[i]=realloc(v->pcm[i],v->pcm_storage*sizeof(float));
+ }
+ }
+
+ for(i=0;i<vi->channels;i++)
+ v->pcmret[i]=v->pcm[i]+v->pcm_current;
+
+ return(v->pcmret);
+}
+
+static void _preextrapolate_helper(vorbis_dsp_state *v){
+ int i;
+ int order=32;
+ float *lpc=alloca(order*sizeof(float));
+ float *work=alloca(v->pcm_current*sizeof(float));
+ long j;
+ v->preextrapolate=1;
+
+ if(v->pcm_current-v->centerW>order*2){ /* safety */
+ for(i=0;i<v->vi->channels;i++){
+
+ /* need to run the extrapolation in reverse! */
+ for(j=0;j<v->pcm_current;j++)
+ work[j]=v->pcm[i][v->pcm_current-j-1];
+
+ /* prime as above */
+ vorbis_lpc_from_data(work,lpc,v->pcm_current-v->centerW,order);
+
+ /* run the predictor filter */
+ vorbis_lpc_predict(lpc,work+v->pcm_current-v->centerW-order,
+ order,
+ work+v->pcm_current-v->centerW,
+ v->centerW);
+ for(j=0;j<v->pcm_current;j++)
+ v->pcm[i][v->pcm_current-j-1]=work[j];
+ }
+ }
+}
+
+
+/* call with val<=0 to set eof */
+
+int vorbis_analysis_wrote(vorbis_dsp_state *v, int vals){
+ vorbis_info *vi=v->vi;
+ if(vals<=0){
+ int order=32;
+ int i;
+ float *lpc=alloca(order*sizeof(float));
+
+ /* if it wasn't done earlier (very short sample) */
+ if(!v->preextrapolate)
+ _preextrapolate_helper(v);
+
+ /* We're encoding the end of the stream. Just make sure we have
+ [at least] a full block of zeroes at the end. */
+ /* actually, we don't want zeroes; that could drop a large
+ amplitude off a cliff, creating spread spectrum noise that will
+ suck to encode. Extrapolate for the sake of cleanliness. */
+
+ vorbis_analysis_buffer(v,v->vi->blocksizes[1]*2);
+ v->eofflag=v->pcm_current;
+ v->pcm_current+=v->vi->blocksizes[1]*2;
+
+ for(i=0;i<vi->channels;i++){
+ if(v->eofflag>order*2){
+ /* extrapolate with LPC to fill in */
+ long n;
+
+ /* make a predictor filter */
+ n=v->eofflag;
+ if(n>v->vi->blocksizes[1])n=v->vi->blocksizes[1];
+ vorbis_lpc_from_data(v->pcm[i]+v->eofflag-n,lpc,n,order);
+
+ /* run the predictor filter */
+ vorbis_lpc_predict(lpc,v->pcm[i]+v->eofflag-order,order,
+ v->pcm[i]+v->eofflag,v->pcm_current-v->eofflag);
+ }else{
+ /* not enough data to extrapolate (unlikely to happen due to
+ guarding the overlap, but bulletproof in case that
+ assumtion goes away). zeroes will do. */
+ memset(v->pcm[i]+v->eofflag,0,
+ (v->pcm_current-v->eofflag)*sizeof(float));
+
+ }
+ }
+ }else{
+
+ if(v->pcm_current+vals>v->pcm_storage)
+ return(OV_EINVAL);
+
+ v->pcm_current+=vals;
+
+ /* we may want to reverse extrapolate the beginning of a stream
+ too... in case we're beginning on a cliff! */
+ /* clumsy, but simple. It only runs once, so simple is good. */
+ if(!v->preextrapolate && v->pcm_current-v->centerW>v->vi->blocksizes[1])
+ _preextrapolate_helper(v);
+
+ }
+ return(0);
+}
+
+/* do the deltas, envelope shaping, pre-echo and determine the size of
+ the next block on which to continue analysis */
+int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){
+ int i;
+ vorbis_info *vi=v->vi;
+ long beginW=v->centerW-vi->blocksizes[v->W]/2,centerNext;
+
+ /* check to see if we're started... */
+ if(!v->preextrapolate)return(0);
+
+ /* check to see if we're done... */
+ if(v->eofflag==-1)return(0);
+
+ /* By our invariant, we have lW, W and centerW set. Search for
+ the next boundary so we can determine nW (the next window size)
+ which lets us compute the shape of the current block's window */
+
+ if(vi->blocksizes[0]<vi->blocksizes[1]){
+ long largebound;
+ long bp;
+
+ if(v->W)
+ /* min boundary; nW large, next small */
+ largebound=v->centerW+vi->blocksizes[1]*3/4+vi->blocksizes[0]/4;
+ else
+ /* min boundary; nW large, next small */
+ largebound=v->centerW+vi->blocksizes[1]*3/4+vi->blocksizes[0]*3/4;
+
+ bp=_ve_envelope_search(v,largebound);
+ if(bp==-1)return(0); /* not enough data currently to search for a
+ full long block */
+ v->nW=bp;
+
+ }else
+ v->nW=0;
+
+ centerNext=v->centerW+vi->blocksizes[v->W]/4+vi->blocksizes[v->nW]/4;
+
+ {
+ /* center of next block + next block maximum right side. */
+
+ long blockbound=centerNext+vi->blocksizes[v->nW]/2;
+ if(v->pcm_current<blockbound)return(0); /* not enough data yet;
+ although this check is
+ less strict that the
+ _ve_envelope_search,
+ the search is not run
+ if we only use one
+ block size */
+ }
+
+ /* fill in the block. Note that for a short window, lW and nW are *short*
+ regardless of actual settings in the stream */
+
+ _vorbis_block_ripcord(vb);
+ if(v->W){
+ vb->lW=v->lW;
+ vb->W=v->W;
+ vb->nW=v->nW;
+ }else{
+ vb->lW=0;
+ vb->W=v->W;
+ vb->nW=0;
+ }
+ vb->vd=v;
+ vb->sequence=v->sequence;
+ vb->granulepos=v->granulepos;
+ vb->pcmend=vi->blocksizes[v->W];
+
+ /* copy the vectors; this uses the local storage in vb */
+ {
+ vb->pcm=_vorbis_block_alloc(vb,sizeof(float *)*vi->channels);
+ for(i=0;i<vi->channels;i++){
+ vb->pcm[i]=_vorbis_block_alloc(vb,vb->pcmend*sizeof(float));
+ memcpy(vb->pcm[i],v->pcm[i]+beginW,vi->blocksizes[v->W]*sizeof(float));
+ }
+ }
+
+ /* handle eof detection: eof==0 means that we've not yet received EOF
+ eof>0 marks the last 'real' sample in pcm[]
+ eof<0 'no more to do'; doesn't get here */
+
+ if(v->eofflag){
+ if(v->centerW>=v->eofflag){
+ v->eofflag=-1;
+ vb->eofflag=1;
+ return(1);
+ }
+ }
+
+ /* advance storage vectors and clean up */
+ {
+ int new_centerNext=vi->blocksizes[1]/2;
+ int movementW=centerNext-new_centerNext;
+
+ _ve_envelope_shift(v->ve,movementW);
+ v->pcm_current-=movementW;
+
+ for(i=0;i<vi->channels;i++)
+ memmove(v->pcm[i],v->pcm[i]+movementW,
+ v->pcm_current*sizeof(float));
+
+
+ v->lW=v->W;
+ v->W=v->nW;
+ v->centerW=new_centerNext;
+
+ v->sequence++;
+
+ if(v->eofflag){
+ v->eofflag-=movementW;
+ /* do not add padding to end of stream! */
+ if(v->centerW>=v->eofflag){
+ v->granulepos+=movementW-(v->centerW-v->eofflag);
+ }else{
+ v->granulepos+=movementW;
+ }
+ }else{
+ v->granulepos+=movementW;
+ }
+ }
+
+ /* done */
+ return(1);
+}
+
+int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){
+ _vds_shared_init(v,vi,0);
+
+ /* Adjust centerW to allow an easier mechanism for determining output */
+ v->pcm_returned=v->centerW;
+ v->centerW-= vi->blocksizes[v->W]/4+vi->blocksizes[v->lW]/4;
+ v->granulepos=-1;
+ v->sequence=-1;
+
+ return(0);
+}
+
+/* Unlike in analysis, the window is only partially applied for each
+ block. The time domain envelope is not yet handled at the point of
+ calling (as it relies on the previous block). */
+
+int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){
+ vorbis_info *vi=v->vi;
+
+ /* Shift out any PCM that we returned previously */
+ /* centerW is currently the center of the last block added */
+ if(v->pcm_returned && v->centerW>vi->blocksizes[1]/2){
+
+ /* don't shift too much; we need to have a minimum PCM buffer of
+ 1/2 long block */
+
+ int shiftPCM=v->centerW-vi->blocksizes[1]/2;
+ shiftPCM=(v->pcm_returned<shiftPCM?v->pcm_returned:shiftPCM);
+
+ v->pcm_current-=shiftPCM;
+ v->centerW-=shiftPCM;
+ v->pcm_returned-=shiftPCM;
+
+ if(shiftPCM){
+ int i;
+ for(i=0;i<vi->channels;i++)
+ memmove(v->pcm[i],v->pcm[i]+shiftPCM,
+ v->pcm_current*sizeof(float));
+ }
+ }
+
+ v->lW=v->W;
+ v->W=vb->W;
+ v->nW=-1;
+
+ v->glue_bits+=vb->glue_bits;
+ v->time_bits+=vb->time_bits;
+ v->floor_bits+=vb->floor_bits;
+ v->res_bits+=vb->res_bits;
+
+ if(v->sequence+1 != vb->sequence)v->granulepos=-1; /* out of sequence;
+ lose count */
+
+ v->sequence=vb->sequence;
+
+ {
+ int sizeW=vi->blocksizes[v->W];
+ int centerW=v->centerW+vi->blocksizes[v->lW]/4+sizeW/4;
+ int beginW=centerW-sizeW/2;
+ int endW=beginW+sizeW;
+ int beginSl;
+ int endSl;
+ int i,j;
+
+ /* Do we have enough PCM/mult storage for the block? */
+ if(endW>v->pcm_storage){
+ /* expand the storage */
+ v->pcm_storage=endW+vi->blocksizes[1];
+
+ for(i=0;i<vi->channels;i++)
+ v->pcm[i]=realloc(v->pcm[i],v->pcm_storage*sizeof(float));
+ }
+
+ /* overlap/add PCM */
+
+ switch(v->W){
+ case 0:
+ beginSl=0;
+ endSl=vi->blocksizes[0]/2;
+ break;
+ case 1:
+ beginSl=vi->blocksizes[1]/4-vi->blocksizes[v->lW]/4;
+ endSl=beginSl+vi->blocksizes[v->lW]/2;
+ break;
+ }
+
+ for(j=0;j<vi->channels;j++){
+ float *pcm=v->pcm[j]+beginW;
+ float *p=vb->pcm[j];
+
+ /* the overlap/add section */
+ for(i=beginSl;i<endSl;i++)
+ pcm[i]+=p[i];
+ /* the remaining section */
+ for(;i<sizeW;i++)
+ pcm[i]=p[i];
+ }
+
+ /* track the frame number... This is for convenience, but also
+ making sure our last packet doesn't end with added padding. If
+ the last packet is partial, the number of samples we'll have to
+ return will be past the vb->granulepos.
+
+ This is not foolproof! It will be confused if we begin
+ decoding at the last page after a seek or hole. In that case,
+ we don't have a starting point to judge where the last frame
+ is. For this reason, vorbisfile will always try to make sure
+ it reads the last two marked pages in proper sequence */
+
+ if(v->granulepos==-1)
+ v->granulepos=vb->granulepos;
+ else{
+ v->granulepos+=(centerW-v->centerW);
+ if(vb->granulepos!=-1 && v->granulepos!=vb->granulepos){
+ if(v->granulepos>vb->granulepos && vb->eofflag){
+ /* partial last frame. Strip the padding off */
+ centerW-=(v->granulepos-vb->granulepos);
+ }/* else{ Shouldn't happen *unless* the bitstream is out of
+ spec. Either way, believe the bitstream } */
+ v->granulepos=vb->granulepos;
+ }
+ }
+
+ /* Update, cleanup */
+
+ v->centerW=centerW;
+ v->pcm_current=endW;
+
+ if(vb->eofflag)v->eofflag=1;
+ }
+
+ return(0);
+}
+
+/* pcm==NULL indicates we just want the pending samples, no more */
+int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm){
+ vorbis_info *vi=v->vi;
+ if(v->pcm_returned<v->centerW){
+ if(pcm){
+ int i;
+ for(i=0;i<vi->channels;i++)
+ v->pcmret[i]=v->pcm[i]+v->pcm_returned;
+ *pcm=v->pcmret;
+ }
+ return(v->centerW-v->pcm_returned);
+ }
+ return(0);
+}
+
+int vorbis_synthesis_read(vorbis_dsp_state *v,int bytes){
+ if(bytes && v->pcm_returned+bytes>v->centerW)return(OV_EINVAL);
+ v->pcm_returned+=bytes;
+ return(0);
+}
+
diff --git a/lib/info.c b/lib/info.c
new file mode 100644
index 00000000..0c0fb98d
--- /dev/null
+++ b/lib/info.c
@@ -0,0 +1,558 @@
+/********************************************************************
+ * *
+ * 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.31.2.1 2000/10/14 03:14:06 xiphmont Exp $
+
+ ********************************************************************/
+
+/* general handling of the header and the vorbis_info structure (and
+ substructures) */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <ogg/ogg.h>
+#include "vorbis/codec.h"
+#include "vorbis/backends.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)+1; /* +1 for the = we append */
+ char *fulltag = alloca(taglen+ 1);
+
+ 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;
+ else
+ found++;
+ }
+ }
+ return NULL; /* didn't find anything */
+}
+
+int vorbis_comment_query_count(vorbis_comment *vc, char *tag){
+ int i,count=0;
+ int taglen = strlen(tag)+1; /* +1 for the = we append */
+ char *fulltag = alloca(taglen+1);
+ strcpy(fulltag,tag);
+ strcat(fulltag, "=");
+
+ for(i=0;i<vc->comments;i++){
+ if(!tagcompare(vc->user_comments[i], fulltag, taglen))
+ count++;
+ }
+
+ return count;
+}
+
+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(OV_EVERSION);
+
+ 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(OV_EBADHEADER);
+}
+
+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(OV_EBADHEADER);
+}
+
+/* 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(OV_EBADHEADER);
+}
+
+/* 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(OV_ENOTVORBIS);
+ }
+ switch(packtype){
+ case 0x01: /* least significant *bit* is read first */
+ if(!op->b_o_s){
+ /* Not the initial packet */
+ return(OV_EBADHEADER);
+ }
+ if(vi->rate!=0){
+ /* previously initialized info header */
+ return(OV_EBADHEADER);
+ }
+
+ 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(OV_EBADHEADER);
+ }
+
+ 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(OV_EBADHEADER);
+ }
+
+ return(_vorbis_unpack_books(vi,&opb));
+
+ default:
+ /* Not a valid vorbis header type */
+ return(OV_EBADHEADER);
+ break;
+ }
+ }
+ }
+ return(OV_EBADHEADER);
+}
+
+/* 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->granulepos=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->granulepos=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->granulepos=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(OV_EIMPL);
+}
+
diff --git a/lib/synthesis.c b/lib/synthesis.c
new file mode 100644
index 00000000..de23bc8d
--- /dev/null
+++ b/lib/synthesis.c
@@ -0,0 +1,74 @@
+/********************************************************************
+ * *
+ * 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 synthesis
+ last mod: $Id: synthesis.c,v 1.18.2.1 2000/10/14 03:14:07 xiphmont Exp $
+
+ ********************************************************************/
+
+#include <stdio.h>
+#include <ogg/ogg.h>
+#include "vorbis/codec.h"
+#include "registry.h"
+#include "misc.h"
+#include "os.h"
+
+int vorbis_synthesis(vorbis_block *vb,ogg_packet *op){
+ vorbis_dsp_state *vd=vb->vd;
+ vorbis_info *vi=vd->vi;
+ oggpack_buffer *opb=&vb->opb;
+ int type,mode,i;
+
+ /* first things first. Make sure decode is ready */
+ _vorbis_block_ripcord(vb);
+ oggpack_readinit(opb,op->packet,op->bytes);
+
+ /* Check the packet type */
+ if(oggpack_read(opb,1)!=0){
+ /* Oops. This is not an audio data packet */
+ return(OV_ENOTAUDIO);
+ }
+
+ /* read our mode and pre/post windowsize */
+ mode=oggpack_read(opb,vd->modebits);
+ if(mode==-1)return(OV_EBADPACKET);
+
+ vb->mode=mode;
+ vb->W=vi->mode_param[mode]->blockflag;
+ if(vb->W){
+ vb->lW=oggpack_read(opb,1);
+ vb->nW=oggpack_read(opb,1);
+ if(vb->nW==-1) return(OV_EBADPACKET);
+ }else{
+ vb->lW=0;
+ vb->nW=0;
+ }
+
+ /* more setup */
+ vb->granulepos=op->granulepos;
+ vb->sequence=op->packetno-3; /* first block is third packet */
+ vb->eofflag=op->e_o_s;
+
+ /* alloc pcm passback storage */
+ vb->pcmend=vi->blocksizes[vb->W];
+ vb->pcm=_vorbis_block_alloc(vb,sizeof(float *)*vi->channels);
+ for(i=0;i<vi->channels;i++)
+ vb->pcm[i]=_vorbis_block_alloc(vb,vb->pcmend*sizeof(float));
+
+ /* unpack_header enforces range checking */
+ type=vi->map_type[vi->mode_param[mode]->mapping];
+
+ return(_mapping_P[type]->inverse(vb,vd->mode[mode]));
+}
+
+
diff --git a/lib/vorbis-errors.txt b/lib/vorbis-errors.txt
new file mode 100644
index 00000000..1350ed03
--- /dev/null
+++ b/lib/vorbis-errors.txt
@@ -0,0 +1,83 @@
+All 'failure' style returns are <0; this either indicates a generic
+'false' value (eg, ready? T or F) or an error condition. Code can
+safely just test for < 0, or look at the specific return code for more
+detail.
+
+---------- (internal calls) ------------
+
+
+long _get_next_page()
+ OV_FALSE, OV_EOF, OV_EREAD
+
+long _get_prev_page()
+ OV_EREAD, OV_FAULT
+
+int _bisect_forward_serialno()
+ OV_EREAD
+
+int _fetch_headers()
+ OV_EREAD, OV_ENOTVORBIS, OV_EVERSION, OV_EBADHEADER
+
+int _open_seekable()
+ OV_EREAD, OV_ENOTVORBIS, OV_EVERSION, OV_EBADHEADER, OV_FAULT
+ + ov_raw_seek
+
+int _open_nonseekable()
+ OV_EREAD, OV_ENOTVORBIS, OV_EVERSION, OV_EBADHEADER
+
+int _process_packet()
+ OV_EOF, OV_HOLE, OV_EBADLINK
+
+---------- public calls ------------
+
+int vorbis_analysis_headerout()
+ OV_EIMPL
+
+int vorbis_analysis_wrote()
+ OV_EINVAL
+
+int vorbis_synthesis_headerin()
+ OV_ENOTVORBIS, OV_EVERSION, OV_EBADHEADER
+
+int vorbis_synthesis()
+ OV_ENOTAUDIO, OV_EBADPACKET
+
+int vorbis_synthesis_read()
+ OV_EINVAL
+
+int ov_open_callbacks()
+ OV_EREAD, OV_ENOTVORBIS, OV_EVERSION, OV_EBADHEADER, OV_FAULT
+
+int ov_open()
+ OV_EREAD, OV_ENOTVORBIS, OV_EVERSION, OV_EBADHEADER, OV_FAULT
+
+long ov_bitrate()
+ OV_EINVAL, OV_FALSE
+
+long ov_bitrate_instant()
+ OV_FALSE
+
+ogg_int64_t ov_raw_total()
+ OV_EINVAL
+
+ogg_int64_t ov_pcm_total()
+ OV_EINVAL
+
+double ov_time_total()
+ OV_EINVAL
+
+int ov_raw_seek()
+ OV_ENOSEEK, OV_EINVAL, OV_BADLINK
+
+int ov_pcm_seek_page()
+ OV_ENOSEEK, OV_EINVAL, OV_EREAD, OV_BADLINK, OV_FAULT
+
+int ov_pcm_seek()
+ OV_ENOSEEK, OV_EINVAL, OV_EREAD, OV_BADLINK, OV_FAULT
+
+int ov_time_seek()
+ OV_ENOSEEK, OV_EINVAL, OV_EREAD, OV_BADLINK, OV_FAULT
+
+int ov_time_seek_page()
+ OV_ENOSEEK, OV_EINVAL, OV_EREAD, OV_BADLINK, OV_FAULT
+
diff --git a/lib/vorbisfile.c b/lib/vorbisfile.c
new file mode 100644
index 00000000..425ea433
--- /dev/null
+++ b/lib/vorbisfile.c
@@ -0,0 +1,1223 @@
+/********************************************************************
+ * *
+ * 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.30.2.1 2000/10/14 03:14:07 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);
+ if(bytes>0)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: <0) did not find a page (OV_FALSE, OV_EOF, OV_EREAD)
+ 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(OV_FALSE);
+ 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(OV_FALSE);
+ {
+ long ret=_get_data(vf);
+ if(ret==0)return(OV_EOF);
+ if(ret<0)return(OV_EREAD);
+ }
+ }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. */
+/* returns offset or OV_EREAD, OV_FAULT */
+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==OV_EREAD)return(OV_EREAD);
+ if(ret<0){
+ 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<0)
+ /* this shouldn't be possible */
+ return(OV_EFAULT);
+
+ 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 int _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==OV_EREAD)return(OV_EREAD);
+ 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(ret==OV_EREAD)return(OV_EREAD);
+
+ if(searched>=end || ret<0){
+ vf->links=m+1;
+ vf->offsets=malloc((m+2)*sizeof(ogg_int64_t));
+ vf->offsets[m+1]=searched;
+ }else{
+ ret=_bisect_forward_serialno(vf,next,vf->offset,
+ end,ogg_page_serialno(&og),m+1);
+ if(ret==OV_EREAD)return(OV_EREAD);
+ }
+
+ vf->offsets[m]=begin;
+ return(0);
+}
+
+/* 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_ptr){
+ ogg_page og;
+ ogg_packet op;
+ int i,ret=0;
+
+ if(!og_ptr){
+ ret=_get_next_page(vf,&og,CHUNKSIZE);
+ if(ret==OV_EREAD)return(OV_EREAD);
+ if(ret<0)return OV_ENOTVORBIS;
+ og_ptr=&og;
+ }
+
+ if(serialno)*serialno=ogg_page_serialno(og_ptr);
+ ogg_stream_init(&vf->os,ogg_page_serialno(og_ptr));
+
+ /* 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_ptr);
+ while(i<3){
+ int result=ogg_stream_packetout(&vf->os,&op);
+ if(result==0)break;
+ if(result==-1){
+ ret=OV_EBADHEADER;
+ goto bail_header;
+ }
+ if((ret=vorbis_synthesis_headerin(vi,vc,&op))){
+ goto bail_header;
+ }
+ i++;
+ }
+ if(i<3)
+ if(_get_next_page(vf,og_ptr,1)<0){
+ ret=OV_EBADHEADER;
+ goto bail_header;
+ }
+ }
+ return 0;
+
+ bail_header:
+ vorbis_info_clear(vi);
+ vorbis_comment_clear(vc);
+ ogg_stream_clear(&vf->os);
+ return ret;
+}
+
+/* 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) */
+
+/* this is void and does not propogate errors up because we want to be
+ able to open and use damaged bitstreams as well as we can. Just
+ watch out for missing information for links in the OggVorbis_File
+ struct */
+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(ogg_int64_t));
+ vf->pcmlengths=malloc(vf->links*sizeof(ogg_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,NULL)<0){
+ 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<0){
+ /* this should not be possible, actually */
+ vorbis_info_clear(vf->vi+i);
+ vorbis_comment_clear(vf->vc+i);
+ break;
+ }
+ if(ogg_page_granulepos(&og)!=-1){
+ vf->serialnos[i]=ogg_page_serialno(&og);
+ vf->pcmlengths[i]=ogg_page_granulepos(&og);
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void _make_decode_ready(OggVorbis_File *vf){
+ if(vf->decode_ready)return;
+ if(vf->seekable){
+ vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link);
+ }else{
+ vorbis_synthesis_init(&vf->vd,vf->vi);
+ }
+ vorbis_block_init(&vf->vd,&vf->vb);
+ vf->decode_ready=1;
+ return;
+}
+
+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,NULL);
+ dataoffset=vf->offset;
+ ogg_stream_clear(&vf->os);
+ if(ret<0)return(ret);
+
+ /* 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);
+ if(end<0){
+ ogg_stream_clear(&vf->os);
+ return(end);
+ }
+
+ /* more 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 */
+ if(_bisect_forward_serialno(vf,0,0,end+1,serialno,0)<0){
+ ogg_stream_clear(&vf->os);
+ return(OV_EREAD);
+ }
+
+ }else{
+
+ /* Only one logical bitstream */
+ if(_bisect_forward_serialno(vf,0,end,end+1,serialno,0)){
+ ogg_stream_clear(&vf->os);
+ return(OV_EREAD);
+ }
+
+ }
+
+ _prefetch_all_headers(vf,&initial_i,&initial_c,dataoffset);
+ return(ov_raw_seek(vf,0));
+
+}
+
+static int _open_nonseekable(OggVorbis_File *vf){
+ int ret;
+ /* 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((ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,NULL))<0)
+ return(ret);
+ _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: <0) error, OV_HOLE (lost packet) or OV_EOF
+ 0) need more data (only if readp==0)
+ 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);
+ ogg_int64_t granulepos;
+
+ if(result==-1)return(OV_HOLE); /* hole in the data. */
+ if(result>0){
+ /* got a packet. process it */
+ granulepos=op.granulepos;
+ 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(granulepos!=-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
+ granulepos declares the last frame in the stream, and the
+ last packet of the last page may be a partial frame.
+ So, we need a previous granulepos from an in-sequence page
+ to have a reference point. Thus the !op.e_o_s clause
+ above */
+
+ samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
+
+ granulepos-=samples;
+ for(i=0;i<link;i++)
+ granulepos+=vf->pcmlengths[i];
+ vf->pcm_offset=granulepos;
+ }
+ return(1);
+ }
+ }
+ }
+
+ if(!readp)return(0);
+ if(_get_next_page(vf,&og,-1)<0)return(OV_EOF); /* 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(OV_EBADLINK); /* 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,&og);
+ 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,ogg_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 *, ogg_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(OV_EINVAL);
+ if(!vf->seekable && i!=0)return(ov_bitrate(vf,0));
+ if(i<0){
+ ogg_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(OV_FALSE);
+ }
+ }
+ }
+}
+
+/* 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(OV_FALSE);
+ 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(ov_serialnumber(vf,vf->links-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)
+*/
+ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i){
+ if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
+ 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)
+*/
+ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){
+ if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
+ if(i<0){
+ ogg_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(OV_EINVAL);
+ 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){
+ int flag=0;
+ if(!vf->seekable)return(OV_ENOSEEK); /* don't dump machine if we can't seek */
+ if(pos<0 || pos>vf->offsets[vf->links])return(OV_EINVAL);
+
+ /* 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 'granulepos' field set, and that's how the
+ helper updates the offset */
+
+ while(!flag){
+ switch(_process_packet(vf,1)){
+ case 0:case OV_EOF:
+ /* 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 OV_HOLE:
+ break;
+ case OV_EBADLINK:
+ goto seek_error;
+ default:
+ /* all OK */
+ flag=1;
+ break;
+ }
+ }
+
+ while(1){
+ /* don't have to check each time through for the updated granule;
+ it's always the last complete packet on a page */
+ switch(_process_packet(vf,0)){
+ case 0:case OV_EOF:
+ /* the offset is set unless it's a bogus bitstream with no
+ offset information but that's not our fault. We still run
+ gracefully, we're just missing the offset */
+ return(0);
+ case OV_EBADLINK:
+ 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 OV_EBADLINK;
+}
+
+/* Page granularity seek (faster than sample granularity because we
+ don't do the last bit of decode to find a specific sample).
+
+ Seek to the last [granule marked] page preceeding the specified pos
+ location, such that decoding past the returned point will quickly
+ arrive at the requested position. */
+int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){
+ int link=-1;
+ long ret;
+ ogg_int64_t total=ov_pcm_total(vf,-1);
+
+ if(!vf->seekable)return(OV_ENOSEEK);
+ if(pos<0 || pos>total)return(OV_EINVAL);
+
+ /* 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) */
+ {
+ ogg_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;
+
+ if(end-begin<CHUNKSIZE){
+ bisect=begin;
+ }else{
+ bisect=(end+begin)/2;
+ }
+
+ _seek_helper(vf,bisect);
+ ret=_get_next_page(vf,&og,end-bisect);
+ switch(ret){
+ case OV_FALSE: case OV_EOF:
+ end=bisect;
+ break;
+ case OV_EREAD:
+ goto seek_error;
+ default:
+ {
+ ogg_int64_t granulepos=ogg_page_granulepos(&og);
+ if(granulepos<target){
+ best=ret; /* raw offset of packet with granulepos */
+ begin=vf->offset; /* raw offset of next packet */
+ }else{
+ end=bisect;
+ }
+ }
+ }
+ }
+
+ /* found our page. seek to it (call raw_seek). */
+
+ if((ret=ov_raw_seek(vf,best)))goto seek_error;
+ }
+
+ /* verify result */
+ if(vf->pcm_offset>=pos || pos>ov_pcm_total(vf,-1)){
+ ret=OV_EFAULT;
+ goto seek_error;
+ }
+ return(0);
+
+ seek_error:
+ /* dump machine so we're in a known state */
+ vf->pcm_offset=-1;
+ _decode_clear(vf);
+ return ret;
+}
+
+/* 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,ogg_int64_t pos){
+ int ret=ov_pcm_seek_page(vf,pos);
+ if(ret<0)return(ret);
+
+ /* discard samples until we reach the desired position. Crossing a
+ logical bitstream boundary with abandon is OK. */
+ while(vf->pcm_offset<pos){
+ float **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 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;
+ ogg_int64_t pcm_total=ov_pcm_total(vf,-1);
+ double time_total=ov_time_total(vf,-1);
+
+ if(!vf->seekable)return(OV_ENOSEEK);
+ if(seconds<0 || seconds>time_total)return(OV_EINVAL);
+
+ /* 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 */
+ {
+ ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate;
+ return(ov_pcm_seek(vf,target));
+ }
+}
+
+/* page-granularity version of ov_time_seek
+ returns zero on success, nonzero on failure */
+int ov_time_seek_page(OggVorbis_File *vf,double seconds){
+ /* translate time to PCM position and call ov_pcm_seek */
+
+ int link=-1;
+ ogg_int64_t pcm_total=ov_pcm_total(vf,-1);
+ double time_total=ov_time_total(vf,-1);
+
+ if(!vf->seekable)return(OV_ENOSEEK);
+ if(seconds<0 || seconds>time_total)return(OV_EINVAL);
+
+ /* 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 */
+ {
+ ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate;
+ return(ov_pcm_seek_page(vf,target));
+ }
+}
+
+/* tell the current stream offset cursor. Note that seek followed by
+ tell will likely not give the set offset due to caching */
+ogg_int64_t ov_raw_tell(OggVorbis_File *vf){
+ return(vf->offset);
+}
+
+/* return PCM offset (sample) of next PCM sample to be read */
+ogg_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;
+ ogg_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() {
+ ogg_int32_t pattern = 0xfeedface; /* deadbeef */
+ unsigned char *bytewise = (unsigned char *)&pattern;
+ if (bytewise[0] == 0xfe) return 1;
+ 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 (OV_HOLE)
+ 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){
+ float **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 */
+ float *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++) {
+ float *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:case OV_EOF:
+ return(0);
+ case OV_HOLE:
+ return(OV_HOLE);
+ case OV_EBADLINK:
+ return(OV_EBADLINK);
+ }
+ }
+}
+
+
+
+