summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMonty <xiphmont@xiph.org>2000-04-06 15:59:38 +0000
committerMonty <xiphmont@xiph.org>2000-04-06 15:59:38 +0000
commit16fdbfc3ef0c99e387c452dd66c7fd0891698443 (patch)
tree81e24e27ec09ba6bad7486698a5d64ea559c3edc
parent08309f2d62179920fe6c51d4383b93e9bc26b1fa (diff)
downloadlibvorbis-git-16fdbfc3ef0c99e387c452dd66c7fd0891698443.tar.gz
Incremental update, nearly there.
Fixed a sorting compare function bug in lsp.c and psy.c as spotted by Vakor. Monty svn path=/branches/new_acoustics_pending_merge_20000328/vorbis/; revision=309
-rw-r--r--examples/Makefile.in80
-rw-r--r--examples/seeking_example.c2
-rw-r--r--include/vorbis/backends.h5
-rw-r--r--include/vorbis/codebook.h4
-rw-r--r--include/vorbis/codec.h4
-rw-r--r--include/vorbis/modes.h15
-rw-r--r--lib/block.c4
-rw-r--r--lib/codebook.c11
-rw-r--r--lib/floor0.c252
-rw-r--r--lib/framing.c1583
-rw-r--r--lib/lpc.c131
-rw-r--r--lib/lpc.h49
-rw-r--r--lib/lsp.c175
-rw-r--r--lib/mapping0.c3
-rw-r--r--lib/os.h34
-rw-r--r--lib/psy.c28
-rw-r--r--lib/psytune.c17
-rw-r--r--lib/res0.c10
-rw-r--r--lib/sharedbook.c33
-rw-r--r--lib/sharedbook.h6
-rw-r--r--lib/vorbisfile.c1060
-rw-r--r--vq/build.c69
-rw-r--r--vq/cascade.c6
-rw-r--r--vq/lspdata.c117
-rw-r--r--vq/metrics.c287
-rw-r--r--vq/partition.c88
-rw-r--r--vq/residuedata.c6
-rw-r--r--vq/train.c9
-rw-r--r--vq/vqgen.c15
-rw-r--r--vq/vqsplit.c3
30 files changed, 3827 insertions, 279 deletions
diff --git a/examples/Makefile.in b/examples/Makefile.in
new file mode 100644
index 00000000..e732f2dd
--- /dev/null
+++ b/examples/Makefile.in
@@ -0,0 +1,80 @@
+# vorbis makefile configured for use with gcc on any platform
+
+# $Id: Makefile.in,v 1.5.4.1 2000/04/06 15:59:35 xiphmont Exp $
+
+###############################################################################
+# #
+# To build a production vorbis (preferrably using gmake), just type 'make'. #
+# To build with debugging or profiling information, use 'make debug' or #
+# 'make profile' respectively. 'make clean' is a good idea between builds #
+# with different target names, or before a final build. #
+# #
+###############################################################################
+
+
+# DO NOT EDIT BELOW! ##########################################################
+# (unless, of course, you know what you are doing :) ##########################
+
+@SET_MAKE@
+FLAGS=-I. -I../include @TYPESIZES@ @CFLAGS@
+OPT=@OPT@ $(FLAGS)
+DEBUG=@DEBUG@ $(FLAGS)
+PROFILE=@PROFILE@ $(FLAGS)
+CC=@CC@
+LD=@CC@
+LDFLAGS=@LDFLAGS@ $(FLAGS)
+AR=@AR@
+RANLIB=@RANLIB@
+LIBS=@LIBS@ -lm
+
+HFILES = ../include/vorbis/codec.h ../include/vorbis/vorbisfile.h \
+ ../include/vorbis/internal.h ../include/vorbis/backends.h \
+ ../include/vorbis/codebook.h
+OFILES = encoder_example.o decoder_example.o chaining_example.o \
+ seeking_test.o
+BINFILES = encoder_example decoder_example chaining_example \
+ seeking_test
+
+all:
+ $(MAKE) target CFLAGS="$(OPT)"
+
+debug:
+ $(MAKE) target CFLAGS="$(DEBUG)"
+
+profile:
+ $(MAKE) target CFLAGS="$(PROFILE)"
+
+target: $(BINFILES)
+
+encoder_example.o: ../include/vorbis/modes.h
+
+encoder_example: $(OFILES) ../lib/libvorbis.a
+ $(CC) $(CFLAGS) $(LDFLAGS) encoder_example.o ../lib/libvorbis.a -o \
+ encoder_example -lm
+
+decoder_example: $(OFILES) ../lib/libvorbis.a
+ $(CC) $(CFLAGS) $(LDFLAGS) decoder_example.o ../lib/libvorbis.a -o \
+ decoder_example -lm
+
+chaining_example: $(OFILES) ../lib/libvorbis.a ../lib/vorbisfile.a
+ $(CC) $(CFLAGS) $(LDFLAGS) chaining_example.o \
+ ../lib/vorbisfile.a ../lib/libvorbis.a \
+ -o chaining_example -lm
+
+seeking_test: $(OFILES) ../lib/libvorbis.a ../lib/vorbisfile.a
+ $(CC) $(CFLAGS) $(LDFLAGS) seeking_test.o \
+ ../lib/vorbisfile.a ../lib/libvorbis.a \
+ -o seeking_test -lm
+
+$(OFILES): $(HFILES)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f *.o *.a test* *~ *.out ogg config.* \
+ encoder_example decoder_example chaining_example
+
+distclean: clean
+ -rm -f Makefile
+
diff --git a/examples/seeking_example.c b/examples/seeking_example.c
index b9a2264e..8c76e8d9 100644
--- a/examples/seeking_example.c
+++ b/examples/seeking_example.c
@@ -12,7 +12,7 @@
********************************************************************
function: illustrate seeking, and test it too
- last mod: $Id: seeking_example.c,v 1.1 2000/04/03 09:51:07 xiphmont Exp $
+ last mod: $Id: seeking_example.c,v 1.1.2.1 2000/04/06 15:59:36 xiphmont Exp $
********************************************************************/
diff --git a/include/vorbis/backends.h b/include/vorbis/backends.h
index e8b7d857..8176a84d 100644
--- a/include/vorbis/backends.h
+++ b/include/vorbis/backends.h
@@ -13,7 +13,7 @@
function: libvorbis backend and mapping structures; needed for
static mode headers
- last mod: $Id: backends.h,v 1.7.4.1 2000/03/30 01:46:47 xiphmont Exp $
+ last mod: $Id: backends.h,v 1.7.4.2 2000/04/06 15:59:36 xiphmont Exp $
********************************************************************/
@@ -66,11 +66,12 @@ typedef struct{
int order;
long rate;
long barkmap;
+ unsigned char subcurve[27];
int ampbits;
int ampdB;
- int stages; /* <= 16 */
+ int numbooks; /* <= 16 */
int books[16];
} vorbis_info_floor0;
diff --git a/include/vorbis/codebook.h b/include/vorbis/codebook.h
index 6b7893fa..48dc40fc 100644
--- a/include/vorbis/codebook.h
+++ b/include/vorbis/codebook.h
@@ -12,7 +12,7 @@
********************************************************************
function: codebook types
- last mod: $Id: codebook.h,v 1.4.4.2 2000/04/01 12:51:31 xiphmont Exp $
+ last mod: $Id: codebook.h,v 1.4.4.3 2000/04/06 15:59:36 xiphmont Exp $
********************************************************************/
@@ -96,8 +96,6 @@ typedef struct codebook{
} codebook;
-#define VQ_FEXP_BIAS 20 /* bias toward values smaller than 1. */
-
#endif
diff --git a/include/vorbis/codec.h b/include/vorbis/codec.h
index 20cd4525..1bb5d245 100644
--- a/include/vorbis/codec.h
+++ b/include/vorbis/codec.h
@@ -12,7 +12,7 @@
********************************************************************
function: libvorbis codec headers
- last mod: $Id: codec.h,v 1.10.2.2 2000/03/31 00:23:02 xiphmont Exp $
+ last mod: $Id: codec.h,v 1.10.2.3 2000/04/06 15:59:36 xiphmont Exp $
********************************************************************/
@@ -367,7 +367,7 @@ extern int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op);
extern int ogg_stream_init(ogg_stream_state *os,int serialno);
extern int ogg_stream_clear(ogg_stream_state *os);
-extern int ogg_stream_reset(ogg_stream_state *os,long expected_pageno);
+extern int ogg_stream_reset(ogg_stream_state *os);
extern int ogg_stream_destroy(ogg_stream_state *os);
extern int ogg_stream_eof(ogg_stream_state *os);
diff --git a/include/vorbis/modes.h b/include/vorbis/modes.h
index 0ebe49c5..0145d5a3 100644
--- a/include/vorbis/modes.h
+++ b/include/vorbis/modes.h
@@ -12,7 +12,7 @@
********************************************************************
function: predefined encoding modes
- last mod: $Id: modes.h,v 1.9.2.3 2000/04/04 07:08:44 xiphmont Exp $
+ last mod: $Id: modes.h,v 1.9.2.4 2000/04/06 15:59:36 xiphmont Exp $
********************************************************************/
@@ -42,7 +42,9 @@
/* A farily high quality setting mix */
static vorbis_info_psy _psy_set0={
- 3,1,1,
+ 3, /* iterations to fit */
+ 1, /* athp */
+ 1, /* decayp */
1,16,4.,
@@ -76,8 +78,13 @@ static vorbis_info_psy _psy_set0={
/* with GNUisms, this could be short and readable. Oh well */
static vorbis_info_time0 _time_set0={0};
-static vorbis_info_floor0 _floor_set0={20, 44100, 64, 12,140, 1, {0} };
-static vorbis_info_floor0 _floor_set1={32, 44100, 256, 12,140, 1, {1} };
+static vorbis_info_floor0 _floor_set0={20, 44100, 64,
+ /* yes, the first number is different than the ATH */
+ {31,31,31,17,15,14,13,13,13,13,12,10,8,6,3,1,0,0,2,4,17,24,27,23,31,86,126},
+ 12,150, 1, {0} };
+static vorbis_info_floor0 _floor_set1={32, 44100, 256,
+ {31,31,31,17,15,14,13,13,13,13,12,10,8,6,3,1,0,0,2,4,17,24,27,23,31,86,126},
+ 12,150, 1, {1} };
static vorbis_info_residue0 _residue_set0={0, 128, 8,1,0,{0},{}};
static vorbis_info_residue0 _residue_set1={0,1024, 8,1,0,{0},{}};
static vorbis_info_mapping0 _mapping_set0={1, {0,0}, {0}, {0}, {0}, {0}};
diff --git a/lib/block.c b/lib/block.c
index 01da5344..0464b9b1 100644
--- a/lib/block.c
+++ b/lib/block.c
@@ -12,7 +12,7 @@
********************************************************************
function: PCM data vector blocking, windowing and dis/reassembly
- last mod: $Id: block.c,v 1.27.4.1 2000/04/04 07:08:44 xiphmont Exp $
+ last mod: $Id: block.c,v 1.27.4.2 2000/04/06 15:59:36 xiphmont Exp $
Handle windowing, overlap-add, etc of the PCM vectors. This is made
more amusing by Vorbis' current two allowed block sizes.
@@ -118,7 +118,7 @@ void *_vorbis_block_alloc(vorbis_block *vb,long bytes){
vb->localtop=0;
}
{
- void *ret=vb->localstore+vb->localtop;
+ void *ret=(void *)(((char *)vb->localstore)+vb->localtop);
vb->localtop+=bytes;
return ret;
}
diff --git a/lib/codebook.c b/lib/codebook.c
index 0b458ca8..8ef16846 100644
--- a/lib/codebook.c
+++ b/lib/codebook.c
@@ -12,11 +12,12 @@
********************************************************************
function: basic codebook pack/unpack/code/decode operations
- last mod: $Id: codebook.c,v 1.11.4.3 2000/04/04 07:08:44 xiphmont Exp $
+ last mod: $Id: codebook.c,v 1.11.4.4 2000/04/06 15:59:36 xiphmont Exp $
********************************************************************/
#include <stdlib.h>
+#include <string.h>
#include <math.h>
#include "vorbis/codec.h"
#include "vorbis/codebook.h"
@@ -79,8 +80,8 @@ int vorbis_staticbook_pack(const static_codebook *c,oggpack_buffer *opb){
_oggpack_write(opb,1,1);
/* values that define the dequantization */
- _oggpack_write(opb,c->q_min,24);
- _oggpack_write(opb,c->q_delta,24);
+ _oggpack_write(opb,c->q_min,32);
+ _oggpack_write(opb,c->q_delta,32);
_oggpack_write(opb,c->q_quant-1,4);
_oggpack_write(opb,c->q_sequencep,1);
_oggpack_write(opb,c->q_log,1);
@@ -170,8 +171,8 @@ int vorbis_staticbook_unpack(oggpack_buffer *opb,static_codebook *s){
if(_oggpack_read(opb,1)){
/* values that define the dequantization */
- s->q_min=_oggpack_read(opb,24);
- s->q_delta=_oggpack_read(opb,24);
+ s->q_min=_oggpack_read(opb,32);
+ s->q_delta=_oggpack_read(opb,32);
s->q_quant=_oggpack_read(opb,4)+1;
s->q_sequencep=_oggpack_read(opb,1);
s->q_log=_oggpack_read(opb,1);
diff --git a/lib/floor0.c b/lib/floor0.c
index 68a84758..47a1c972 100644
--- a/lib/floor0.c
+++ b/lib/floor0.c
@@ -12,7 +12,7 @@
********************************************************************
function: floor backend 0 implementation
- last mod: $Id: floor0.c,v 1.11.2.1.2.1 2000/04/01 12:51:32 xiphmont Exp $
+ last mod: $Id: floor0.c,v 1.11.2.1.2.2 2000/04/06 15:59:36 xiphmont Exp $
********************************************************************/
@@ -29,8 +29,11 @@
typedef struct {
long n;
- long m;
-
+ int ln;
+ int m;
+ int *linearmap;
+ double *subcurve;
+
vorbis_info_floor0 *vi;
lpc_lookup lpclook;
} vorbis_look_floor0;
@@ -45,6 +48,8 @@ static void free_info(vorbis_info_floor *i){
static void free_look(vorbis_look_floor *i){
vorbis_look_floor0 *look=(vorbis_look_floor0 *)i;
if(i){
+ if(look->linearmap)free(look->linearmap);
+ if(look->subcurve)free(look->subcurve);
lpc_clear(&look->lpclook);
memset(look,0,sizeof(vorbis_look_floor0));
free(look);
@@ -59,8 +64,10 @@ static void pack (vorbis_info_floor *i,oggpack_buffer *opb){
_oggpack_write(opb,info->barkmap,16);
_oggpack_write(opb,info->ampbits,6);
_oggpack_write(opb,info->ampdB,8);
- _oggpack_write(opb,info->stages-1,4);
- for(j=0;j<info->stages;j++)
+ for(j=0;j<27;j++)
+ _oggpack_write(opb,(int)info->subcurve[j],8);
+ _oggpack_write(opb,info->numbooks-1,4);
+ for(j=0;j<info->numbooks;j++)
_oggpack_write(opb,info->books[j],8);
}
@@ -72,14 +79,16 @@ static vorbis_info_floor *unpack (vorbis_info *vi,oggpack_buffer *opb){
info->barkmap=_oggpack_read(opb,16);
info->ampbits=_oggpack_read(opb,6);
info->ampdB=_oggpack_read(opb,8);
- info->stages=_oggpack_read(opb,4)+1;
+ for(j=0;j<27;j++)
+ info->subcurve[j]=_oggpack_read(opb,8);
+ info->numbooks=_oggpack_read(opb,4)+1;
if(info->order<1)goto err_out;
if(info->rate<1)goto err_out;
if(info->barkmap<1)goto err_out;
- if(info->stages<1)goto err_out;
+ if(info->numbooks<1)goto err_out;
- for(j=0;j<info->stages;j++){
+ for(j=0;j<info->numbooks;j++){
info->books[j]=_oggpack_read(opb,8);
if(info->books[j]<0 || info->books[j]>=vi->books)goto err_out;
}
@@ -89,21 +98,159 @@ static vorbis_info_floor *unpack (vorbis_info *vi,oggpack_buffer *opb){
return(NULL);
}
+/* initialize Bark scale and normalization lookups. We could do this
+ with static tables, but Vorbis allows a number of possible
+ combinations, so it's best to do it computationally.
+
+ The below is authoritative in terms of defining scale mapping.
+ Note that the scale depends on the sampling rate as well as the
+ linear block and mapping sizes */
+
static vorbis_look_floor *look (vorbis_dsp_state *vd,vorbis_info_mode *mi,
vorbis_info_floor *i){
+ int j;
+ double scale;
vorbis_info *vi=vd->vi;
vorbis_info_floor0 *info=(vorbis_info_floor0 *)i;
vorbis_look_floor0 *look=malloc(sizeof(vorbis_look_floor0));
look->m=info->order;
look->n=vi->blocksizes[mi->blockflag]/2;
+ look->ln=info->barkmap;
look->vi=info;
- lpc_init(&look->lpclook,look->n,info->barkmap,info->rate,look->m);
+ lpc_init(&look->lpclook,look->ln,look->m);
+
+ /* we choose a scaling constant so that:
+ floor(bark(rate/2-1)*C)=mapped-1
+ floor(bark(rate/2)*C)=mapped */
+ scale=look->ln/toBARK(info->rate/2.);
+
+ /* the mapping from a linear scale to a smaller bark scale is
+ straightforward. We do *not* make sure that the linear mapping
+ does not skip bark-scale bins; the decoder simply skips them and
+ the encoder may do what it wishes in filling them. They're
+ necessary in some mapping combinations to keep the scale spacing
+ accurate */
+ look->linearmap=malloc(look->n*sizeof(int));
+ for(j=0;j<look->n;j++){
+ int val=floor( toBARK((info->rate/2.)/look->n*j)
+ *scale); /* bark numbers represent band edges */
+ if(val>look->ln)val=look->ln; /* guard against the approximation */
+ look->linearmap[j]=val;
+ }
+ look->subcurve=malloc(look->ln*sizeof(double));
+ {
+ double max=toBARK(info->rate/2.);
+ for(j=0;j<look->ln;j++){
+ double bark=max/look->ln*j;
+ int f=floor(bark);
+ double del=bark-f;
+
+ if(f>=27){
+ f=26;
+ del=1.;
+ }
+
+ look->subcurve[j]=info->subcurve[f]*(1.-del)+info->subcurve[f+1]*del;
+ }
+ }
return look;
}
#include <stdio.h>
+
+/* less efficient than the decode side (written for clarity). We're
+ not bottlenecked here anyway */
+static double _curve_to_lpc(double *curve,double *lpc,vorbis_look_floor0 *l,
+ long frameno){
+ /* map the input curve to a bark-scale curve for encoding */
+
+ int mapped=l->ln;
+ double *work=alloca(sizeof(double)*mapped);
+ int i,j,last=0;
+
+ memset(work,0,sizeof(double)*mapped);
+
+ /* Only the decode side is behavior-specced; for now in the encoder,
+ we select the maximum value of each band as representative (this
+ helps make sure peaks don't go out of range. In error terms,
+ selecting min would make more sense, but the codebook is trained
+ numerically, so we don't actually lose. We'd still want to
+ use the original curve for error and noise estimation */
+
+ for(i=0;i<l->n;i++){
+ int bark=l->linearmap[i];
+ if(work[bark]<curve[i])work[bark]=curve[i];
+ if(bark>last+1){
+ /* If the bark scale is climbing rapidly, some bins may end up
+ going unused. This isn't a waste actually; it keeps the
+ scale resolution even so that the LPC generator has an easy
+ time. However, if we leave the bins empty we lose energy.
+ So, fill 'em in. The decoder does not do anything with he
+ unused bins, so we can fill them anyway we like to end up
+ with a better spectral curve */
+
+ /* we'll always have a bin zero, so we don't need to guard init */
+ long span=bark-last;
+ for(j=1;j<span;j++){
+ double del=(double)j/span;
+ work[j+last]=work[bark]*del+work[last]*(1.-del);
+ }
+ }
+ last=bark;
+ }
+ for(i=0;i<l->ln;i++)work[i]-=l->subcurve[i];
+
+#if 0
+ { /******************/
+ FILE *of;
+ char buffer[80];
+ int i;
+
+ sprintf(buffer,"mask_%d.m",frameno);
+ of=fopen(buffer,"w");
+ for(i=0;i<mapped;i++)
+ fprintf(of,"%g\n",work[i]);
+ fclose(of);
+ }
+#endif
+
+ return vorbis_lpc_from_curve(work,lpc,&(l->lpclook));
+}
+
+/* generate the whole freq response curve of an LPC IIR filter */
+
+static void _lpc_to_curve(double *curve,double *lpc,double amp,
+ vorbis_look_floor0 *l,char *name,long frameno){
+ double *lcurve=alloca(sizeof(double)*(l->ln*2));
+ int i;
+
+ if(amp==0){
+ memset(curve,0,sizeof(double)*l->n);
+ return;
+ }
+ vorbis_lpc_to_curve(lcurve,lpc,amp,&(l->lpclook));
+
+#if 0
+ { /******************/
+ FILE *of;
+ char buffer[80];
+ int i;
+
+ sprintf(buffer,"%s_%d.m",name,frameno);
+ of=fopen(buffer,"w");
+ for(i=0;i<l->ln;i++)
+ fprintf(of,"%g\n",lcurve[i]);
+ fclose(of);
+ }
+#endif
+
+ for(i=0;i<l->ln;i++)lcurve[i]+=l->subcurve[i];
+ for(i=0;i<l->n;i++)curve[i]=lcurve[l->linearmap[i]];
+
+}
+
static int forward(vorbis_block *vb,vorbis_look_floor *i,
double *in,double *out){
long j,k,stage;
@@ -112,20 +259,20 @@ static int forward(vorbis_block *vb,vorbis_look_floor *i,
double amp;
long bits=0;
- /* our floor comes in on a [-Inf...0] dB scale. The curve has to be
- positive, so we offset it. */
- for(j=0;j<look->n;j++)in[j]+=info->ampdB;
-
+ /* our floor comes in on a linear scale; go to a [-Inf...0] dB
+ scale. The curve has to be positive, so we offset it. */
+ for(j=0;j<look->n;j++)in[j]=todB(in[j])+info->ampdB;
+
/* use 'out' as temp storage */
/* Convert our floor to a set of lpc coefficients */
- amp=sqrt(vorbis_curve_to_lpc(in,out,&look->lpclook));
-
+ amp=sqrt(_curve_to_lpc(in,out,look,vb->sequence));
+
/* amp is in the range (0. to ampdB]. Encode that range using
ampbits bits */
{
long maxval=(1<<info->ampbits)-1;
-
+
long val=amp/info->ampdB*maxval+1;
if(val<0)val=0; /* likely */
@@ -139,11 +286,9 @@ static int forward(vorbis_block *vb,vorbis_look_floor *i,
}
if(amp>0){
- double *work=alloca(sizeof(double)*look->m);
-
+
/* LSP <-> LPC is orthogonal and LSP quantizes more stably */
vorbis_lpc_to_lsp(out,out,look->m);
- memcpy(work,out,sizeof(double)*look->m);
#ifdef TRAIN
{
@@ -159,37 +304,35 @@ static int forward(vorbis_block *vb,vorbis_look_floor *i,
}
#endif
+#if 0
+ { /******************/
+ vorbis_lsp_to_lpc(out,in,look->m);
+ _lpc_to_curve(in,in,amp,look,"prefloor",vb->sequence);
+ }
+#endif
+
/* code the spectral envelope, and keep track of the actual
quantized values; we don't want creeping error as each block is
nailed to the last quantized value of the previous block. */
-
- /* first stage is a bit different because quantization error must be
- handled carefully */
- for(stage=0;stage<info->stages;stage++){
- codebook *b=vb->vd->fullbooks+info->books[stage];
-
- if(stage==0){
- double last=0.;
- for(j=0;j<look->m;){
- for(k=0;k<b->dim;k++)out[j+k]-=last;
- bits+=vorbis_book_encodev(b,out+j,&vb->opb);
- for(k=0;k<b->dim;k++,j++){
- out[j]+=last;
- work[j]-=out[j];
- }
- last=out[j-1];
- }
- }else{
- memcpy(out,work,sizeof(double)*look->m);
- for(j=0;j<look->m;){
- bits+=vorbis_book_encodev(b,out+j,&vb->opb);
- for(k=0;k<b->dim;k++,j++)work[j]-=out[j];
- }
+
+ /* the spec supports using one of a number of codebooks. Right
+ now, encode using this lib supports only one */
+ _oggpack_write(&vb->opb,0,_ilog(info->numbooks));
+
+ {
+ codebook *b=vb->vd->fullbooks+info->books[0];
+ double last=0.;
+ for(j=0;j<look->m;){
+ for(k=0;k<b->dim;k++)out[j+k]-=last;
+ bits+=vorbis_book_encodev(b,out+j,&vb->opb);
+ for(k=0;k<b->dim;k++,j++)out[j]+=last;
+ last=out[j-1];
}
}
+
/* take the coefficients back to a spectral envelope curve */
vorbis_lsp_to_lpc(out,out,look->m);
- vorbis_lpc_to_curve(out,out,amp,&look->lpclook);
+ _lpc_to_curve(out,out,amp,look,"floor",vb->sequence);
for(j=0;j<look->n;j++)out[j]= fromdB(out[j]-info->ampdB);
return(1);
}
@@ -207,25 +350,22 @@ static int inverse(vorbis_block *vb,vorbis_look_floor *i,double *out){
if(ampraw>0){
long maxval=(1<<info->ampbits)-1;
double amp=(ampraw-.5)/maxval*info->ampdB;
+ int booknum=_oggpack_read(&vb->opb,_ilog(info->numbooks));
+ codebook *b=vb->vd->fullbooks+info->books[booknum];
+ double last=0.;
memset(out,0,sizeof(double)*look->m);
- for(stage=0;stage<info->stages;stage++){
- codebook *b=vb->vd->fullbooks+info->books[stage];
- for(j=0;j<look->m;j+=b->dim)
- vorbis_book_decodev(b,out+j,&vb->opb);
- if(stage==0){
- double last=0.;
- for(j=0;j<look->m;){
- for(k=0;k<b->dim;k++,j++)out[j]+=last;
- last=out[j-1];
- }
- }
+
+ for(j=0;j<look->m;j+=b->dim)
+ vorbis_book_decodev(b,out+j,&vb->opb);
+ for(j=0;j<look->m;){
+ for(k=0;k<b->dim;k++,j++)out[j]+=last;
+ last=out[j-1];
}
-
/* take the coefficients back to a spectral envelope curve */
vorbis_lsp_to_lpc(out,out,look->m);
- vorbis_lpc_to_curve(out,out,amp,&look->lpclook);
+ _lpc_to_curve(out,out,amp,look,"",0);
for(j=0;j<look->n;j++)out[j]= fromdB(out[j]-info->ampdB);
return(1);
diff --git a/lib/framing.c b/lib/framing.c
new file mode 100644
index 00000000..3fb06477
--- /dev/null
+++ b/lib/framing.c
@@ -0,0 +1,1583 @@
+/********************************************************************
+ * *
+ * 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.14.4.1 2000/04/06 15:59:36 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"
+
+/* 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 u_int32_t crc_lookup[256];
+static int crc_ready=0;
+
+static u_int32_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){
+ u_int32_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;
+
+ /* 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 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){
+ int 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;
+ }
+
+ if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */
+ os->body_fill > 4096 || /* 'page nominal size' case */
+ os->lacing_fill>=255 || /* 'segment table full' case */
+ (os->lacing_fill&&!os->b_o_s)){ /* 'initial header page' case */
+
+ int vals=0,bytes=0;
+ int maxvals=(os->lacing_fill>255?255:os->lacing_fill);
+ long acc=0;
+ int64_t pcm_pos=os->pcm_vals[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) */
+ {
+ 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);
+
+ /* 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;
+
+ /* set pointers in the ogg_page struct */
+ og->header=os->header;
+ og->header_len=os->header_fill=vals+27;
+ og->body=os->body_data;
+ og->body_len=bytes;
+
+ /* calculate the checksum */
+
+ _os_checksum(og);
+
+ return(1);
+ }
+
+ /* 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_decode_buffer(). The app either copies into the
+ buffer, or passes it directly to read(), etc. We then call
+ ogg_decode_wrote() to tell how many bytes we just added.
+
+ Pages are returned (pointers into the buffer in ogg_sync_state)
+ by ogg_decode_stream(). The page is then submitted to
+ ogg_decode_page() along with the appropriate
+ ogg_stream_state* (ie, matching serialno). We then get raw
+ packets out calling ogg_stream_packet() 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",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 at pos %ld: %x!=%lx!\n\n",
+ j,op->packet[j],(j+no)&0xff);
+ exit(1);
+ }
+}
+
+void check_page(unsigned char *data,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 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){
+ 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);
+}
+
+void test_pack(int *pl, 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,0);
+ ogg_stream_reset(&os_de,0);
+ 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 */
+ int packets[]={17, -1};
+ int head1[] = {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};
+ int *headret[]={head1,NULL};
+
+ fprintf(stderr,"testing single page encoding... ");
+ test_pack(packets,headret);
+ }
+
+ {
+ /* 17, 254, 255, 256, 500, 510, 600 byte, pad */
+ int packets[]={17, 254, 255, 256, 500, 510, 600, -1};
+ int head1[] = {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};
+ int head2[] = {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};
+ int *headret[]={head1,head2,NULL};
+
+ fprintf(stderr,"testing basic page encoding... ");
+ test_pack(packets,headret);
+ }
+
+ {
+ /* nil packets; beginning,middle,end */
+ int packets[]={0,17, 254, 255, 0, 256, 0, 500, 510, 600, 0, -1};
+
+ int head1[] = {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};
+ int head2[] = {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};
+ int *headret[]={head1,head2,NULL};
+
+ fprintf(stderr,"testing basic nil packets... ");
+ test_pack(packets,headret);
+ }
+
+ {
+ /* large initial packet */
+ int packets[]={4345,259,255,-1};
+
+ int head1[] = {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};
+
+ int head2[] = {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};
+ int *headret[]={head1,head2,NULL};
+
+ fprintf(stderr,"testing initial-packet lacing > 4k... ");
+ test_pack(packets,headret);
+ }
+
+ {
+ /* continuing packet test */
+ int packets[]={0,4345,259,255,-1};
+
+ int head1[] = {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};
+
+ int head2[] = {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};
+
+ int head3[] = {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};
+ int *headret[]={head1,head2,head3,NULL};
+
+ fprintf(stderr,"testing single packet page span... ");
+ test_pack(packets,headret);
+ }
+
+ /* page with the 255 segment limit */
+ {
+
+ 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};
+
+ int head1[] = {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};
+
+ int head2[] = {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};
+
+ int head3[] = {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};
+ int *headret[]={head1,head2,head3,NULL};
+
+ fprintf(stderr,"testing max packet segments... ");
+ test_pack(packets,headret);
+ }
+
+ {
+ /* packet that overspans over an entire page */
+
+ int packets[]={0,100,9000,259,255,-1};
+
+ int head1[] = {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};
+
+ int head2[] = {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};
+
+ int head3[] = {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};
+
+ int head4[] = {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};
+ int *headret[]={head1,head2,head3,head4,NULL};
+
+ fprintf(stderr,"testing very large packets... ");
+ test_pack(packets,headret);
+ }
+
+ {
+ /* term only page. why not? */
+
+ int packets[]={0,100,4080,-1};
+
+ int head1[] = {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};
+
+ int head2[] = {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};
+
+ int head3[] = {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};
+
+ int *headret[]={head1,head2,head3,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,0);
+
+ 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,0);
+ 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,0);
+ 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/lpc.c b/lib/lpc.c
index ced6b298..d7166530 100644
--- a/lib/lpc.c
+++ b/lib/lpc.c
@@ -12,7 +12,7 @@
********************************************************************
function: LPC low level routines
- last mod: $Id: lpc.c,v 1.18.2.1 2000/03/29 03:49:28 xiphmont Exp $
+ last mod: $Id: lpc.c,v 1.18.2.1.2.1 2000/04/06 15:59:36 xiphmont Exp $
********************************************************************/
@@ -114,7 +114,7 @@ double vorbis_lpc_from_data(double *data,double *lpc,int n,int m){
/* Input : n element envelope spectral curve
Output: m lpc coefficients, excitation energy */
-double vorbis_lpc_from_spectrum(double *curve,double *lpc,lpc_lookup *l){
+double vorbis_lpc_from_curve(double *curve,double *lpc,lpc_lookup *l){
int n=l->ln;
int m=l->m;
double *work=alloca(sizeof(double)*(n+n));
@@ -143,125 +143,23 @@ double vorbis_lpc_from_spectrum(double *curve,double *lpc,lpc_lookup *l){
return(vorbis_lpc_from_data(work,lpc,n,m));
}
-/* initialize Bark scale and normalization lookups. We could do this
- with static tables, but Vorbis allows a number of possible
- combinations, so it's best to do it computationally.
-
- The below is authoritative in terms of defining scale mapping.
- Note that the scale depends on the sampling rate as well as the
- linear block and mapping sizes */
-
-void lpc_init(lpc_lookup *l,int n, long mapped, long rate, int m){
- int i;
- double scale;
+void lpc_init(lpc_lookup *l,long mapped, int m){
memset(l,0,sizeof(lpc_lookup));
- l->n=n;
l->ln=mapped;
l->m=m;
- l->linearmap=malloc(n*sizeof(int));
- l->barknorm=malloc(mapped*sizeof(double));
-
- /* we choose a scaling constant so that:
- floor(bark(rate/2-1)*C)=mapped-1
- floor(bark(rate/2)*C)=mapped */
-
- scale=mapped/toBARK(rate/2.);
-
- /* the mapping from a linear scale to a smaller bark scale is
- straightforward. We do *not* make sure that the linear mapping
- does not skip bark-scale bins; the decoder simply skips them and
- the encoder may do what it wishes in filling them. They're
- necessary in some mapping combinations to keep the scale spacing
- accurate */
- {
- int last=-1;
- for(i=0;i<n;i++){
- int val=floor( toBARK((rate/2.)/n*i) *scale); /* bark numbers
- represent
- band edges */
- if(val>=mapped)val=mapped; /* guard against the approximation */
- l->linearmap[i]=val;
- last=val;
- }
- }
-
- /* 'Normalization' is just making sure that power isn't lost in the
- log scale by virtue of compressing the scale in higher
- frequencies. We figure the weight of bands in proportion to
- their linear/bark width ratio below, again, authoritatively. We
- use computed width (not the number of actual bins above) for
- smoothness in the scale; they should agree closely */
-
- /* keep it 0. to 1., else the dynamic range starts spreading through
- all the squaring... */
-
- for(i=0;i<mapped;i++)
- l->barknorm[i]=(fromBARK((i+1)/scale)-fromBARK(i/scale));
- for(i=0;i<mapped;i++)
- l->barknorm[i]/=l->barknorm[mapped-1];
-
- /* we cheat decoding the LPC spectrum via FFTs */
-
+ /* we cheat decoding the LPC spectrum via FFTs */
drft_init(&l->fft,mapped*2);
}
void lpc_clear(lpc_lookup *l){
if(l){
- if(l->barknorm)free(l->barknorm);
- if(l->linearmap)free(l->linearmap);
drft_clear(&l->fft);
}
}
-
-/* less efficient than the decode side (written for clarity). We're
- not bottlenecked here anyway */
-double vorbis_curve_to_lpc(double *curve,double *lpc,lpc_lookup *l){
- /* map the input curve to a bark-scale curve for encoding */
-
- int mapped=l->ln;
- double *work=alloca(sizeof(double)*mapped);
- int i,j,last=0;
-
- memset(work,0,sizeof(double)*mapped);
-
- /* Only the decode side is behavior-specced; for now in the encoder,
- we select the maximum value of each band as representative (this
- helps make sure peaks don't go out of range. In error terms,
- selecting min would make more sense, but the codebook is trained
- numerically, so we don't actually lose. We'd still want to
- use the original curve for error and noise estimation */
-
- for(i=0;i<l->n;i++){
- int bark=l->linearmap[i];
- if(work[bark]<curve[i])work[bark]=curve[i];
- if(bark>last+1){
- /* If the bark scale is climbing rapidly, some bins may end up
- going unused. This isn't a waste actually; it keeps the
- scale resolution even so that the LPC generator has an easy
- time. However, if we leave the bins empty we lose energy.
- So, fill 'em in. The decoder does not do anything with he
- unused bins, so we can fill them anyway we like to end up
- with a better spectral curve */
-
- /* we'll always have a bin zero, so we don't need to guard init */
- long span=bark-last;
- for(j=1;j<span;j++){
- double del=(double)j/span;
- work[j+last]=work[bark]*del+work[last]*(1.-del);
- }
- }
- last=bark;
- }
- /*for(i=0;i<mapped;i++)work[i]*=l->barknorm[i];*/
-
- return vorbis_lpc_from_spectrum(work,lpc,l);
-}
-
-
/* One can do this the long way by generating the transfer function in
the time domain and taking the forward FFT of the result. The
results from direct calculation are cleaner and faster.
@@ -269,8 +167,8 @@ double vorbis_curve_to_lpc(double *curve,double *lpc,lpc_lookup *l){
This version does a linear curve generation and then later
interpolates the log curve from the linear curve. */
-void _vlpc_de_helper(double *curve,double *lpc,double amp,
- lpc_lookup *l){
+void vorbis_lpc_to_curve(double *curve,double *lpc,double amp,
+ lpc_lookup *l){
int i;
memset(curve,0,sizeof(double)*l->ln*2);
if(amp==0)return;
@@ -294,23 +192,6 @@ void _vlpc_de_helper(double *curve,double *lpc,double amp,
}
}
-/* generate the whole freq response curve of an LPC IIR filter */
-
-void vorbis_lpc_to_curve(double *curve,double *lpc,double amp,lpc_lookup *l){
- double *lcurve=alloca(sizeof(double)*(l->ln*2));
- int i;
-
- if(amp==0){
- memset(curve,0,sizeof(double)*l->n);
- return;
- }
- _vlpc_de_helper(lcurve,lpc,amp,l);
-
- /*for(i=0;i<l->ln;i++)lcurve[i]/=l->barknorm[i];*/
- for(i=0;i<l->n;i++)curve[i]=lcurve[l->linearmap[i]];
-
-}
-
/* subtract or add an lpc filter to data. Vorbis doesn't actually use this. */
void vorbis_lpc_residue(double *coeff,double *prime,int m,
diff --git a/lib/lpc.h b/lib/lpc.h
new file mode 100644
index 00000000..b4654a77
--- /dev/null
+++ b/lib/lpc.h
@@ -0,0 +1,49 @@
+/********************************************************************
+ * *
+ * 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: LPC low level routines
+ last mod: $Id: lpc.h,v 1.9.4.1 2000/04/06 15:59:37 xiphmont Exp $
+
+ ********************************************************************/
+
+#ifndef _V_LPC_H_
+#define _V_LPC_H_
+
+#include "vorbis/codec.h"
+#include "smallft.h"
+
+typedef struct lpclook{
+ /* en/decode lookups */
+ drft_lookup fft;
+
+ int ln;
+ int m;
+
+} lpc_lookup;
+
+extern void lpc_init(lpc_lookup *l,long mapped, int m);
+extern void lpc_clear(lpc_lookup *l);
+
+/* simple linear scale LPC code */
+extern double vorbis_lpc_from_data(double *data,double *lpc,int n,int m);
+extern double vorbis_lpc_from_curve(double *curve,double *lpc,lpc_lookup *l);
+extern void vorbis_lpc_to_curve(double *curve,double *lpc,double amp,
+ lpc_lookup *l);
+
+/* standard lpc stuff */
+extern void vorbis_lpc_residue(double *coeff,double *prime,int m,
+ double *data,long n);
+extern void vorbis_lpc_predict(double *coeff,double *prime,int m,
+ double *data,long n);
+
+#endif
diff --git a/lib/lsp.c b/lib/lsp.c
new file mode 100644
index 00000000..9f905bf1
--- /dev/null
+++ b/lib/lsp.c
@@ -0,0 +1,175 @@
+/********************************************************************
+ * *
+ * 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: LSP (also called LSF) conversion routines
+ last mod: $Id: lsp.c,v 1.4.4.1 2000/04/06 15:59:37 xiphmont Exp $
+
+ The LSP generation code is taken (with minimal modification) from
+ "On the Computation of the LSP Frequencies" by Joseph Rothweiler
+ <rothwlr@altavista.net>, available at:
+
+ http://www2.xtdl.com/~rothwlr/lsfpaper/lsfpage.html
+
+ ********************************************************************/
+
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include "lsp.h"
+#include "os.h"
+
+void vorbis_lsp_to_lpc(double *lsp,double *lpc,int m){
+ int i,j,m2=m/2;
+ double *O=alloca(sizeof(double)*m2);
+ double *E=alloca(sizeof(double)*m2);
+ double A;
+ double *Ae=alloca(sizeof(double)*(m2+1));
+ double *Ao=alloca(sizeof(double)*(m2+1));
+ double B;
+ double *Be=alloca(sizeof(double)*(m2));
+ double *Bo=alloca(sizeof(double)*(m2));
+ double temp;
+
+ /* even/odd roots setup */
+ for(i=0;i<m2;i++){
+ O[i]=-2.*cos(lsp[i*2]);
+ E[i]=-2.*cos(lsp[i*2+1]);
+ }
+
+ /* set up impulse response */
+ for(j=0;j<m2;j++){
+ Ae[j]=0.;
+ Ao[j]=1.;
+ Be[j]=0.;
+ Bo[j]=1.;
+ }
+ Ao[j]=1.;
+ Ae[j]=1.;
+
+ /* run impulse response */
+ for(i=1;i<m+1;i++){
+ A=B=0.;
+ for(j=0;j<m2;j++){
+ temp=O[j]*Ao[j]+Ae[j];
+ Ae[j]=Ao[j];
+ Ao[j]=A;
+ A+=temp;
+
+ temp=E[j]*Bo[j]+Be[j];
+ Be[j]=Bo[j];
+ Bo[j]=B;
+ B+=temp;
+ }
+ lpc[i-1]=(A+Ao[j]+B-Ae[j])/2;
+ Ao[j]=A;
+ Ae[j]=B;
+ }
+}
+
+static void kw(double *r,int n) {
+ double *s=alloca(sizeof(double)*(n/2+1));
+ double *c=alloca(sizeof(double)*(n+1));
+ int i, j, k;
+
+ s[0] = 1.0;
+ s[1] = -2.0;
+ s[2] = 2.0;
+ for(i=3;i<=n/2;i++) s[i] = s[i-2];
+
+ for(k=0;k<=n;k++) {
+ c[k] = r[k];
+ j = 1;
+ for(i=k+2;i<=n;i+=2) {
+ c[k] += s[j]*r[i];
+ s[j] -= s[j-1];
+ j++;
+ }
+ }
+ for(k=0;k<=n;k++) r[k] = c[k];
+}
+
+
+static int comp(const void *a,const void *b){
+ if(*(double *)a<*(double *)b)
+ return(-1);
+ else
+ return(1);
+}
+
+/* CACM algorithm 283. */
+static void cacm283(double *a,int ord,double *r){
+ int i, k;
+ double val, p, delta, error;
+ double rooti;
+
+ for(i=0; i<ord;i++) r[i] = 2.0 * (i+0.5) / ord - 1.0;
+
+ for(error=1 ; error > 1.e-12; ) {
+ error = 0;
+ for( i=0; i<ord; i++) { /* Update each point. */
+ rooti = r[i];
+ val = a[ord];
+ p = a[ord];
+ for(k=ord-1; k>= 0; k--) {
+ val = val * rooti + a[k];
+ if (k != i) p *= rooti - r[k];
+ }
+ delta = val/p;
+ r[i] -= delta;
+ error += delta*delta;
+ }
+ }
+
+ /* Replaced the original bubble sort with a real sort. With your
+ help, we can eliminate the bubble sort in our lifetime. --Monty */
+
+ qsort(r,ord,sizeof(double),comp);
+
+}
+
+/* Convert lpc coefficients to lsp coefficients */
+void vorbis_lpc_to_lsp(double *lpc,double *lsp,int m){
+ int order2=m/2;
+ double *g1=alloca(sizeof(double)*(order2+1));
+ double *g2=alloca(sizeof(double)*(order2+1));
+ double *g1r=alloca(sizeof(double)*(order2+1));
+ double *g2r=alloca(sizeof(double)*(order2+1));
+ int i;
+
+ /* Compute the lengths of the x polynomials. */
+ /* Compute the first half of K & R F1 & F2 polynomials. */
+ /* Compute half of the symmetric and antisymmetric polynomials. */
+ /* Remove the roots at +1 and -1. */
+
+ g1[order2] = 1.0;
+ for(i=0;i<order2;i++) g1[order2-i-1] = lpc[i]+lpc[m-i-1];
+ g2[order2] = 1.0;
+ for(i=0;i<order2;i++) g2[order2-i-1] = lpc[i]-lpc[m-i-1];
+
+ for(i=0; i<order2;i++) g1[order2-i-1] -= g1[order2-i];
+ for(i=0; i<order2;i++) g2[order2-i-1] += g2[order2-i];
+
+ /* Convert into polynomials in cos(alpha) */
+ kw(g1,order2);
+ kw(g2,order2);
+
+ /* Find the roots of the 2 even polynomials.*/
+
+ cacm283(g1,order2,g1r);
+ cacm283(g2,order2,g2r);
+
+ for(i=0;i<m;i+=2){
+ lsp[i] = acos(g1r[i/2]*.5);
+ lsp[i+1] = acos(g2r[i/2]*.5);
+ }
+}
diff --git a/lib/mapping0.c b/lib/mapping0.c
index 8b32530b..02cad2e1 100644
--- a/lib/mapping0.c
+++ b/lib/mapping0.c
@@ -12,7 +12,7 @@
********************************************************************
function: channel mapping 0 implementation
- last mod: $Id: mapping0.c,v 1.11.2.2.2.2 2000/04/01 12:51:32 xiphmont Exp $
+ last mod: $Id: mapping0.c,v 1.11.2.2.2.3 2000/04/06 15:59:37 xiphmont Exp $
********************************************************************/
@@ -195,6 +195,7 @@ static vorbis_info_mapping *unpack(vorbis_info *vi,oggpack_buffer *opb){
#include "psy.h"
#include "bitwise.h"
#include "spectrum.h"
+#include "scales.h"
/* no time mapping implementation for now */
static int forward(vorbis_block *vb,vorbis_look_mapping *l){
diff --git a/lib/os.h b/lib/os.h
new file mode 100644
index 00000000..994445f4
--- /dev/null
+++ b/lib/os.h
@@ -0,0 +1,34 @@
+/********************************************************************
+ * *
+ * 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: #ifdef jail to whip a few platforms into the UNIX ideal.
+ last mod: $Id: os.h,v 1.2.4.1 2000/04/06 15:59:37 xiphmont Exp $
+
+ ********************************************************************/
+
+#ifndef _V_IFDEFJAIL_H_
+#define _V_IFDEFJAIL_H_
+
+#ifndef M_PI
+#define M_PI (3.1415926539)
+#endif
+
+#ifdef _WIN32
+#define alloca(x) (_alloca(x))
+/* not strictly correct, but Vorbis doesn't care */
+#define rint(x) (floor((x)+0.5))
+#endif
+
+#endif
+
+
diff --git a/lib/psy.c b/lib/psy.c
index bffd0cec..ff9305b0 100644
--- a/lib/psy.c
+++ b/lib/psy.c
@@ -12,18 +12,20 @@
********************************************************************
function: psychoacoustics not including preecho
- last mod: $Id: psy.c,v 1.16.2.2.2.6 2000/04/02 01:21:22 xiphmont Exp $
+ last mod: $Id: psy.c,v 1.16.2.2.2.7 2000/04/06 15:59:37 xiphmont Exp $
********************************************************************/
#include <stdlib.h>
#include <math.h>
#include <string.h>
-#include <stdio.h>
#include "vorbis/codec.h"
#include "masking.h"
#include "psy.h"
+#include "os.h"
+#include "lpc.h"
+#include "smallft.h"
#include "scales.h"
/* the beginnings of real psychoacoustic infrastructure. This is
@@ -315,10 +317,9 @@ static double seed_peaks(double *floor,double **curve,
double ret=0.;
/* make this attenuation adjustable */
- int choice=rint((amp-specmax+specatt)/10.)-2;
+ int choice=rint((todB(amp)-specmax+specatt)/10.)-2;
if(choice<0)choice=0;
if(choice>8)choice=8;
- amp=fromdB(amp);
for(i=0;i<EHMER_MAX;i++){
if(prevx<n){
@@ -326,7 +327,6 @@ static double seed_peaks(double *floor,double **curve,
ix=x*_eights[i];
nextx=(ix<n?post[ix]:n);
if(lin){
-
/* Currently uses a n+n = +3dB additivity */
lin*=amp;
lin*=lin;
@@ -399,7 +399,7 @@ static void _vp_tone_tone_iter(vorbis_look_psy *p,double *f, double *flr,
if(o<0)o=0;
if(o>10)o=10;
- acc+=seed_peaks(flr,p->curves[o],todB(work[i]),
+ acc+=seed_peaks(flr,p->curves[o],work[i],
specmax,p->pre,p->post,i,n,vi->max_curve_dB);
}
}
@@ -415,8 +415,12 @@ static void _vp_tone_tone_iter(vorbis_look_psy *p,double *f, double *flr,
}
+/* stability doesn't matter */
static int comp(const void *a,const void *b){
- return(fabs(**(double **)a)<fabs(**(double **)b));
+ if(fabs(**(double **)a)<fabs(**(double **)b))
+ return(-1);
+ else
+ return(1);
}
/* this applies the floor and (optionally) tries to preserve noise
@@ -432,9 +436,9 @@ void _vp_apply_floor(vorbis_look_psy *p,double *f,
/* subtract the floor */
for(j=0;j<p->n;j++){
if(flr[j]<=0)
- work[j]=0;
+ work[j]=0.;
else{
- double val=rint(f[j]/flr[j]);
+ double val=f[j]/flr[j];
if(fabs(val)<1.)val=0.;
work[j]=val;
}
@@ -470,7 +474,7 @@ void _vp_apply_floor(vorbis_look_psy *p,double *f,
/* sort the zeroed values; add back the largest first, stop when
we violate the desired result above (which may be
immediately) */
- if(z &&current_SL*thresh<original_SL){
+ if(z && current_SL*thresh<original_SL){
qsort(index,z,sizeof(double *),&comp);
for(j=0;j<z;j++){
@@ -482,14 +486,13 @@ void _vp_apply_floor(vorbis_look_psy *p,double *f,
if(f[p]>0)
work[p]=1.;
else
- work[p]=-1.;
+ work[p]=-1.;
current_SL=val;
}else
break;
}
}
}
- fprintf(stderr,"added %d noise coeffs back\n",addcount);
}
memcpy(f,work,p->n*sizeof(double));
}
@@ -518,3 +521,4 @@ void _vp_tone_tone_mask(vorbis_look_psy *p,double *f, double *flr,
}
}
}
+
diff --git a/lib/psytune.c b/lib/psytune.c
index 51f9afe0..b515df39 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.1.2.2.2.4 2000/04/01 12:51:32 xiphmont Exp $
+ last mod: $Id: psytune.c,v 1.1.2.2.2.5 2000/04/06 15:59:37 xiphmont Exp $
********************************************************************/
@@ -23,6 +23,7 @@
#include <math.h>
#include "vorbis/codec.h"
+#include "os.h"
#include "psy.h"
#include "mdct.h"
#include "window.h"
@@ -30,7 +31,7 @@
#include "lpc.h"
static vorbis_info_psy _psy_set0={
- 3,1,1,
+ 1,1,1,
1,16,4.,
@@ -166,7 +167,7 @@ int main(int argc,char *argv[]){
maskwindow=_vorbis_window(0,framesize,framesize/2,framesize/2);
mdct_init(&m_look,framesize);
_vp_psy_init(&p_look,&_psy_set0,framesize/2,44100);
- lpc_init(&lpc_look,framesize/2,256,44100,order);
+ lpc_init(&lpc_look,framesize/2,order);
for(i=0;i<11;i++)
for(j=0;j<9;j++)
@@ -216,8 +217,8 @@ int main(int argc,char *argv[]){
_vp_tone_tone_mask(&p_look,mask,floor,decay[i]);
- analysis("mask",frameno,floor,framesize/2,1,0);
- analysis("lmask",frameno,floor,framesize/2,0,0);
+ analysis("mask",frameno,floor,framesize/2,1,1);
+ analysis("lmask",frameno,floor,framesize/2,0,1);
analysis("decay",frameno,decay[i],framesize/2,1,1);
analysis("ldecay",frameno,decay[i],framesize/2,0,1);
@@ -230,17 +231,17 @@ int main(int argc,char *argv[]){
analysis("lmdct",frameno,pcm[i],framesize/2,0,1);
/* floor */
- {
+ /*{
double amp;
- for(j=0;j<framesize/2;j++)floor[j]=todB(floor[j]+DYNAMIC_RANGE_dB);
+ for(j=0;j<framesize/2;j++)floor[j]=todB(floor[j])+DYNAMIC_RANGE_dB;
amp=sqrt(vorbis_curve_to_lpc(floor,lpc,&lpc_look));
fprintf(stderr,"amp=%g\n",amp);
vorbis_lpc_to_curve(floor,lpc,amp,&lpc_look);
for(j=0;j<framesize/2;j++)floor[j]=fromdB(floor[j]-DYNAMIC_RANGE_dB);
analysis("floor",frameno,floor,framesize/2,1,1);
- }
+ }*/
_vp_apply_floor(&p_look,pcm[i],floor);
diff --git a/lib/res0.c b/lib/res0.c
index 49217b04..81345e01 100644
--- a/lib/res0.c
+++ b/lib/res0.c
@@ -12,7 +12,7 @@
********************************************************************
function: residue backend 0 implementation
- last mod: $Id: res0.c,v 1.8.4.2 2000/04/02 01:21:22 xiphmont Exp $
+ last mod: $Id: res0.c,v 1.8.4.3 2000/04/06 15:59:37 xiphmont Exp $
********************************************************************/
@@ -308,9 +308,11 @@ int inverse(vorbis_block *vb,vorbis_look_residue *vl,double **in,int ch){
for(i=info->begin,l=0;i<info->end;){
/* fetch the partition word for each channel */
- for(j=0;j<ch;j++)
- partword[j]=look->decodemap[vorbis_book_decode(look->phrasebook,
- &vb->opb)];
+ for(j=0;j<ch;j++){
+ int temp=vorbis_book_decode(look->phrasebook,&vb->opb);
+ partword[j]=look->decodemap[temp];
+ if(partword[j]==NULL)exit(1);
+ }
/* now we decode interleaved residual values for the partitions */
for(k=0;k<partitions_per_word;k++,l++,i+=samples_per_partition)
diff --git a/lib/sharedbook.c b/lib/sharedbook.c
index 48579a5e..2d8e206c 100644
--- a/lib/sharedbook.c
+++ b/lib/sharedbook.c
@@ -12,7 +12,7 @@
********************************************************************
function: basic shared codebook operations
- last mod: $Id: sharedbook.c,v 1.1.2.2 2000/04/04 07:08:44 xiphmont Exp $
+ last mod: $Id: sharedbook.c,v 1.1.2.3 2000/04/06 15:59:37 xiphmont Exp $
********************************************************************/
@@ -34,31 +34,36 @@ int _ilog(unsigned int v){
return(ret);
}
-/* 24 bit float (not IEEE; nonnormalized mantissa +
- biased exponent ): neeeeemm mmmmmmmm mmmmmmmm
+/* 32 bit float (not IEEE; nonnormalized mantissa +
+ biased exponent) : neeeeeee eeemmmmm mmmmmmmm mmmmmmmm
Why not IEEE? It's just not that important here. */
-long _float24_pack(double val){
+#define VQ_FEXP 10
+#define VQ_FMAN 21
+#define VQ_FEXP_BIAS 768 /* bias toward values smaller than 1. */
+
+/* doesn't currently guard under/overflow */
+long _float32_pack(double val){
int sign=0;
long exp;
long mant;
if(val<0){
- sign=0x800000;
+ sign=0x80000000;
val= -val;
}
exp= floor(log(val)/log(2));
- mant=rint(ldexp(val,17-exp));
- exp=(exp+VQ_FEXP_BIAS)<<18;
+ mant=rint(ldexp(val,(VQ_FMAN-1)-exp));
+ exp=(exp+VQ_FEXP_BIAS)<<VQ_FMAN;
return(sign|exp|mant);
}
-double _float24_unpack(long val){
- double mant=val&0x3ffff;
- double sign=val&0x800000;
- double exp =(val&0x7c0000)>>18;
+double _float32_unpack(long val){
+ double mant=val&0x1fffff;
+ double sign=val&0x80000000;
+ double exp =(val&0x7fe00000)>>VQ_FMAN;
if(sign)mant= -mant;
- return(ldexp(mant,exp-17-VQ_FEXP_BIAS));
+ return(ldexp(mant,exp-(VQ_FMAN-1)-VQ_FEXP_BIAS));
}
/* given a list of word lengths, generate a list of codewords. Works
@@ -169,8 +174,8 @@ decode_aux *_make_decode_tree(codebook *c){
double *_book_unquantize(const static_codebook *b){
long j,k;
if(b->quantlist){
- double mindel=_float24_unpack(b->q_min);
- double delta=_float24_unpack(b->q_delta);
+ double mindel=_float32_unpack(b->q_min);
+ double delta=_float32_unpack(b->q_delta);
double *r=malloc(sizeof(double)*b->entries*b->dim);
for(j=0;j<b->entries;j++){
diff --git a/lib/sharedbook.h b/lib/sharedbook.h
index 567cab3e..d986aad6 100644
--- a/lib/sharedbook.h
+++ b/lib/sharedbook.h
@@ -12,7 +12,7 @@
********************************************************************
function: basic shared codebook operations
- last mod: $Id: sharedbook.h,v 1.1.2.2 2000/04/04 07:08:44 xiphmont Exp $
+ last mod: $Id: sharedbook.h,v 1.1.2.3 2000/04/06 15:59:37 xiphmont Exp $
********************************************************************/
@@ -28,8 +28,8 @@ extern void vorbis_book_clear(codebook *b);
extern double *_book_unquantize(const static_codebook *b);
extern double *_book_logdist(const static_codebook *b,double *vals);
-extern double _float24_unpack(long val);
-extern long _float24_pack(double val);
+extern double _float32_unpack(long val);
+extern long _float32_pack(double val);
extern int _best(codebook *book, double *a, int step);
extern int _logbest(codebook *book, double *a, int step);
extern int _ilog(unsigned int v);
diff --git a/lib/vorbisfile.c b/lib/vorbisfile.c
new file mode 100644
index 00000000..773b5168
--- /dev/null
+++ b/lib/vorbisfile.c
@@ -0,0 +1,1060 @@
+/********************************************************************
+ * *
+ * 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.15.4.1 2000/04/06 15:59:37 xiphmont Exp $
+
+ ********************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.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,4096);
+ long bytes=fread(buffer,1,4096,vf->f);
+ 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){
+ fseek(vf->f,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(long));
+ 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(long));
+ 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;
+ fseek(vf->f,0,SEEK_END);
+ vf->offset=vf->end=ftell(vf->f);
+
+ /* 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);
+ ov_raw_seek(vf,0);
+
+ return(0);
+}
+
+static int _open_nonseekable(OggVorbis_File *vf){
+ /* we cannot seek. Set up a 'single' (current) logical bitstream entry */
+ vf->links=1;
+ vf->vi=malloc(sizeof(vorbis_info));
+ vf->vc=malloc(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->pcm_offset=-1;
+ vf->decode_ready=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. alert the toplevel */
+ 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 */
+ vorbis_synthesis_blockin(&vf->vd,&vf->vb);
+
+ /* update the pcm offset. */
+ if(frameno!=-1){
+ int link=(vf->seekable?vf->current_link:0);
+ double **dummy;
+ 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* */
+
+ samples=vorbis_synthesis_pcmout(&vf->vd,&dummy);
+
+ 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 */
+
+ /* 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->f)fclose(vf->f);
+ memset(vf,0,sizeof(OggVorbis_File));
+ }
+#ifdef DEBUG_LEAKS
+ _VDBG_dump();
+#endif
+ return(0);
+}
+
+/* inspects the OggVorbis file and finds/documents all the logical
+ bitstreams contained in it. Tries to be tolerant of logical
+ bitstream sections that are truncated/woogie.
+
+ return: -1) error
+ 0) OK
+*/
+
+int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){
+ long offset=fseek(f,0,SEEK_CUR);
+ int ret;
+
+ memset(vf,0,sizeof(OggVorbis_File));
+ vf->f=f;
+
+ /* 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->f=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);
+ }
+ }
+ }
+}
+
+/* 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)
+*/
+long 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 */
+ _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 */
+ _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 */
+ _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 */
+ _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 */
+long 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;
+ }
+}
+
+/* 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;
+
+ 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 */
+ {
+ if(word==1){
+ int off=(sgned?0:128);
+ for(j=0;j<samples;j++)
+ for(i=0;i<channels;i++){
+ int val=rint(pcm[i][j]*128.);
+ if(val>127)val=127;
+ if(val<-128)val=-128;
+ *buffer++=val+off;
+ }
+ }else{
+ int off=(sgned?0:32768);
+
+ if(bigendianp){
+ for(j=0;j<samples;j++)
+ for(i=0;i<channels;i++){
+ int val=rint(pcm[i][j]*32768.);
+ if(val>32767)val=32767;
+ if(val<-32768)val=-32768;
+ val+=off;
+ *buffer++=(val>>8);
+ *buffer++=(val&0xff);
+ }
+ }else{
+ for(j=0;j<samples;j++)
+ for(i=0;i<channels;i++){
+ int val=rint(pcm[i][j]*32768.);
+ if(val>32767)val=32767;
+ 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;
+ }
+ }
+}
+
+
+
+
diff --git a/vq/build.c b/vq/build.c
index ec3125d8..85b34aa5 100644
--- a/vq/build.c
+++ b/vq/build.c
@@ -12,7 +12,7 @@
********************************************************************
function: utility main for building codebooks from training sets
- last mod: $Id: build.c,v 1.12.4.1 2000/04/04 07:08:45 xiphmont Exp $
+ last mod: $Id: build.c,v 1.12.4.2 2000/04/06 15:59:37 xiphmont Exp $
********************************************************************/
@@ -135,8 +135,8 @@ int main(int argc,char *argv[]){
/* quant */
line=rline(in,out);
- if(sscanf(line,"%ld %ld %d %d",&q.min,&q.delta,
- &q.quant,&q.sequencep)!=4){
+ if(sscanf(line,"%d %ld %ld %d %d %lf %lf",&q.log,&q.min,&q.delta,
+ &q.quant,&q.sequencep,&q.encodebias,&q.entropy)!=7){
fprintf(stderr,"Syntax error reading book file\n");
exit(1);
}
@@ -268,24 +268,51 @@ int main(int argc,char *argv[]){
}
fprintf(out,"};\n\n");
- /* tie it all together */
-
- fprintf(out,"static encode_aux _vq_aux_%s = {\n",name);
- fprintf(out,"\t_vq_ptr0_%s,\n",name);
- fprintf(out,"\t_vq_ptr1_%s,\n",name);
- fprintf(out,"\t_vq_p_%s,\n",name);
- fprintf(out,"\t_vq_q_%s,\n",name);
- fprintf(out,"\t%ld, %ld\n};\n\n",c.encode_tree->aux,c.encode_tree->aux);
-
- fprintf(out,"static static_codebook _vq_book_%s = {\n",name);
-XXX
- fprintf(out,"\t%ld, %ld, %ld, %ld, %d, %d,\n",
- c.dim,c.entries,q.min,q.delta,q.quant,q.sequencep);
- fprintf(out,"\t_vq_quantlist_%s,\n",name);
- fprintf(out,"\t_vq_lengthlist_%s,\n",name);
- fprintf(out,"\t&_vq_aux_%s,\n",name);
- fprintf(out,"};\n\n");
-
+ /* zero quant values? Negative log quant values? */
+ {
+ int zero=0;
+ int neg=0;
+ for(j=0;j<c.entries*dim;j++){
+ if(c.quantlist[j]==0){
+ if(q.log)
+ zero=1;
+ else{
+ fprintf(stderr,"INTERNAL ERROR: Non log scale quantization has \n"
+ "quantized entry values == 0 (< min)\n");
+ exit(1);
+ }
+ }
+ if(c.quantlist[j]<0){
+ if(q.log)
+ neg=1;
+ else{
+ fprintf(stderr,"INTERNAL ERROR: Non log scale quantization has \n"
+ "quantized entry values < 0 (< min)\n");
+ exit(1);
+ }
+ }
+ }
+
+
+ /* tie it all together */
+
+ fprintf(out,"static encode_aux _vq_aux_%s = {\n",name);
+ fprintf(out,"\t_vq_ptr0_%s,\n",name);
+ fprintf(out,"\t_vq_ptr1_%s,\n",name);
+ fprintf(out,"\t_vq_p_%s,\n",name);
+ fprintf(out,"\t_vq_q_%s,\n",name);
+ fprintf(out,"\t%ld, %ld\n};\n\n",c.encode_tree->aux,c.encode_tree->aux);
+
+ fprintf(out,"static static_codebook _vq_book_%s = {\n",name);
+
+ fprintf(out,"\t%ld, %ld, %d, %ld, %ld, %d, %d, %d, %d, %g, %g,\n",
+ c.dim,c.entries,q.log,q.min,q.delta,q.quant,q.sequencep,
+ zero,neg,q.encodebias,q.entropy);
+ fprintf(out,"\t_vq_quantlist_%s,\n",name);
+ fprintf(out,"\t_vq_lengthlist_%s,\n",name);
+ fprintf(out,"\t&_vq_aux_%s,\n",name);
+ fprintf(out,"};\n\n");
+ }
fprintf(out,"\n#endif\n");
fclose(out);
exit(0);
diff --git a/vq/cascade.c b/vq/cascade.c
index 686164b6..62904362 100644
--- a/vq/cascade.c
+++ b/vq/cascade.c
@@ -12,7 +12,7 @@
********************************************************************
function: function call to do simple data cascading
- last mod: $Id: cascade.c,v 1.5.4.1 2000/04/04 07:08:45 xiphmont Exp $
+ last mod: $Id: cascade.c,v 1.5.4.2 2000/04/06 15:59:38 xiphmont Exp $
********************************************************************/
@@ -21,6 +21,8 @@
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
+#include "vorbis/codebook.h"
+#include "../lib/sharedbook.h"
#include "bookutil.h"
/* set up metrics */
@@ -55,7 +57,7 @@ void process_vector(codebook **bs,double *a){
while(*bs){
codebook *b=*bs;
- int entry=(book->c->q_log?_logbest(book,a,step):_best(book,a,step));
+ int entry=(b->c->q_log?_logbest(b,a,1):_best(b,a,1));
double *e=b->valuelist+b->c->dim*entry;
for(i=0;i<b->c->dim;i++)work[i]-=e[i];
diff --git a/vq/lspdata.c b/vq/lspdata.c
new file mode 100644
index 00000000..33765c56
--- /dev/null
+++ b/vq/lspdata.c
@@ -0,0 +1,117 @@
+/********************************************************************
+ * *
+ * 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: metrics and quantization code for LSP VQ codebooks
+ last mod: $Id: lspdata.c,v 1.11.4.1 2000/04/06 15:59:38 xiphmont Exp $
+
+ ********************************************************************/
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+#include "vqgen.h"
+#include "vqext.h"
+
+char *vqext_booktype="LSPdata";
+quant_meta q={0,0,0,1, 0,0,0}; /* set sequence data */
+int vqext_aux=1;
+
+void vqext_quantize(vqgen *v,quant_meta *q){
+ vqgen_quantize(v,q);
+}
+
+/* the custom weighting was of questionable value; keep it simple
+ until we know something else is better */
+
+double global_maxdel=M_PI;
+double *weight=NULL;
+#if 1
+/* LSP training metric. We weight error proportional to distance
+ *between* LSP vector values. The idea of this metric is not to set
+ final cells, but get the midpoint spacing into a form conducive to
+ what we want, which is weighting toward preserving narrower
+ features. */
+
+#define FUDGE (global_maxdel-weight[i])
+
+double *vqext_weight(vqgen *v,double *p){
+ int i;
+ int el=v->elements;
+ double lastp=0.;
+ for(i=0;i<el;i++){
+ double predist=(p[i]-lastp);
+ double postdist=(p[i+1]-p[i]);
+ weight[i]=(predist<postdist?predist:postdist);
+ lastp=p[i];
+ }
+ return p;
+}
+#else
+#define FUDGE 1.
+double *vqext_weight(vqgen *v,double *p){
+ return p;
+}
+#endif
+
+ /* candidate,actual */
+double vqext_metric(vqgen *v,double *e, double *p){
+ int i;
+ int el=v->elements;
+ double acc=0.;
+ for(i=0;i<el;i++){
+ double val=(p[i]-e[i])*FUDGE;
+ acc+=val*val;
+ }
+ return sqrt(acc/v->elements);
+}
+
+/* Data files are line-vectors, starting with zero. If we want to
+ train on a subvector starting in the middle, we need to adjust the
+ data as if it was starting at zero. we also need to add the 'aux'
+ value, which is an extra point at the end so we have leading and
+ trailing space */
+
+/* assume vqext_aux==1 */
+void vqext_addpoint_adj(vqgen *v,double *b,int start,int dim,int cols){
+ double *a=alloca(sizeof(double)*(dim+1)); /* +aux */
+ double base=0;
+ int i;
+
+ if(start>0)base=b[start-1];
+ for(i=0;i<dim;i++)a[i]=b[i+start]-base;
+ if(start+dim+1>cols) /* +aux */
+ a[i]=a[i-1];
+ else
+ a[i]=b[i+start]-base;
+
+ vqgen_addpoint(v,a,a+dim);
+}
+
+/* we just need to calc the global_maxdel from the training set */
+void vqext_preprocess(vqgen *v){
+ long j,k;
+
+ global_maxdel=0.;
+ for(j=0;j<v->points;j++){
+ double last=0.;
+ for(k=0;k<v->elements+v->aux;k++){
+ double p=_point(v,j)[k];
+ if(p-last>global_maxdel)global_maxdel=p-last;
+ last=p;
+ }
+ }
+
+ global_maxdel*=1.1;
+ weight=malloc(sizeof(double)*v->elements);
+}
+
diff --git a/vq/metrics.c b/vq/metrics.c
new file mode 100644
index 00000000..034b139c
--- /dev/null
+++ b/vq/metrics.c
@@ -0,0 +1,287 @@
+/********************************************************************
+ * *
+ * 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: function calls to collect codebook metrics
+ last mod: $Id: metrics.c,v 1.6.4.1 2000/04/06 15:59:38 xiphmont Exp $
+
+ ********************************************************************/
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+#include "vorbis/codebook.h"
+#include "../lib/sharedbook.h"
+#include "bookutil.h"
+
+/* set up metrics */
+
+double meanamplitude_acc=0.;
+double meanamplitudesq_acc=0.;
+double meanerror_acc=0.;
+double meanerrorsq_acc=0.;
+double meandev_acc=0.;
+
+double *histogram=NULL;
+double *histogram_error=NULL;
+double *histogram_errorsq=NULL;
+double *histogram_distance=NULL;
+double *histogram_hi=NULL;
+double *histogram_lo=NULL;
+
+double count=0.;
+
+int books=0;
+int dim=-1;
+double *work;
+
+static double *_now(codebook *c, int i){
+ return c->valuelist+i*c->c->dim;
+}
+
+int histerrsort(const void *a, const void *b){
+ double av=histogram_distance[*((long *)a)];
+ double bv=histogram_distance[*((long *)b)];
+ if(av<bv)return(-1);
+ return(1);
+}
+
+void process_preprocess(codebook **bs,char *basename){
+ while(bs[books]){
+ codebook *b=bs[books];
+ if(dim==-1){
+ dim=b->c->dim;
+ work=malloc(sizeof(double)*dim);
+ }else{
+ if(dim!=b->c->dim){
+ fprintf(stderr,"Each codebook in a cascade must have the same dimensional order\n");
+ exit(1);
+ }
+ }
+ books++;
+ }
+
+ if(books){
+ const static_codebook *b=bs[books-1]->c;
+ histogram=calloc(b->entries,sizeof(double));
+ histogram_distance=calloc(b->entries,sizeof(double));
+ histogram_errorsq=calloc(b->entries*dim,sizeof(double));
+ histogram_error=calloc(b->entries*dim,sizeof(double));
+ histogram_hi=calloc(b->entries*dim,sizeof(double));
+ histogram_lo=calloc(b->entries*dim,sizeof(double));
+ }else{
+ fprintf(stderr,"Specify at least one codebook\n");
+ exit(1);
+ }
+}
+
+static double _dist(int el,double *a, double *b){
+ int i;
+ double acc=0.;
+ for(i=0;i<el;i++){
+ double val=(a[i]-b[i]);
+ acc+=val*val;
+ }
+ return acc;
+}
+
+void cell_spacing(codebook **b){
+ int i,j,k;
+ for(i=0;i<books;i++){
+ double min,max,mean=0.,meansq=0.;
+ codebook *c=b[i];
+
+ fprintf(stderr,"\nCell spacing for book %d:\n",i);
+
+ /* minimum, maximum, mean, ms cell spacing */
+ for(j=0;j<c->c->entries;j++){
+ double localmin=-1.;
+ for(k=0;k<c->c->entries;k++){
+ double this=_dist(c->c->dim,_now(c,j),_now(c,k));
+ if(j!=k &&
+ (localmin==-1 || this<localmin))
+ localmin=this;
+ }
+
+ if(j==0 || localmin<min)min=localmin;
+ if(j==0 || localmin>max)max=localmin;
+ mean+=sqrt(localmin);
+ meansq+=localmin;
+ }
+
+ fprintf(stderr,"\tminimum cell spacing (closest side): %g\n",sqrt(min));
+ fprintf(stderr,"\tmaximum cell spacing (closest side): %g\n",sqrt(max));
+ fprintf(stderr,"\tmean closest side spacing: %g\n",mean/c->c->entries);
+ fprintf(stderr,"\tmean sq closest side spacing: %g\n",sqrt(meansq/c->c->entries));
+ }
+}
+
+void process_postprocess(codebook **b,char *basename){
+ int i,j,k;
+ char *buffer=alloca(strlen(basename)+80);
+ codebook *bb=b[books-1];
+
+ fprintf(stderr,"Done. Processed %ld data points for %ld entries:\n",
+ (long)count,bb->c->entries);
+ fprintf(stderr,"\tglobal mean amplitude: %g\n",
+ meanamplitude_acc/(count*dim));
+ fprintf(stderr,"\tglobal mean squared amplitude: %g\n",
+ sqrt(meanamplitudesq_acc/(count*dim)));
+
+ fprintf(stderr,"\tglobal mean error: %g\n",
+ meanerror_acc/(count*dim));
+ fprintf(stderr,"\tglobal mean squared error: %g\n",
+ sqrt(meanerrorsq_acc/(count*dim)));
+ fprintf(stderr,"\tglobal mean deviation: %g\n",
+ meandev_acc/(count*dim));
+
+ cell_spacing(b);
+
+ {
+ FILE *out;
+
+ sprintf(buffer,"%s-mse.m",basename);
+ out=fopen(buffer,"w");
+ if(!out){
+ fprintf(stderr,"Could not open file %s for writing\n",buffer);
+ exit(1);
+ }
+
+ for(i=0;i<bb->c->entries;i++){
+ for(k=0;k<bb->c->dim;k++){
+ fprintf(out,"%ld, %g, %g\n",
+ i*bb->c->dim+k,(bb->valuelist+i*bb->c->dim)[k],
+ sqrt((histogram_errorsq+i*bb->c->dim)[k]/histogram[i]));
+ }
+ }
+ fclose(out);
+
+ sprintf(buffer,"%s-me.m",basename);
+ out=fopen(buffer,"w");
+ if(!out){
+ fprintf(stderr,"Could not open file %s for writing\n",buffer);
+ exit(1);
+ }
+
+ for(i=0;i<bb->c->entries;i++){
+ for(k=0;k<bb->c->dim;k++){
+ fprintf(out,"%ld, %g, %g\n",
+ i*bb->c->dim+k,(bb->valuelist+i*bb->c->dim)[k],
+ (histogram_error+i*bb->c->dim)[k]/histogram[i]);
+ }
+ }
+ fclose(out);
+
+ sprintf(buffer,"%s-worst.m",basename);
+ out=fopen(buffer,"w");
+ if(!out){
+ fprintf(stderr,"Could not open file %s for writing\n",buffer);
+ exit(1);
+ }
+
+ for(i=0;i<bb->c->entries;i++){
+ for(k=0;k<bb->c->dim;k++){
+ fprintf(out,"%ld, %g, %g, %g\n",
+ i*bb->c->dim+k,(bb->valuelist+i*bb->c->dim)[k],
+ (bb->valuelist+i*bb->c->dim)[k]+(histogram_lo+i*bb->c->dim)[k],
+ (bb->valuelist+i*bb->c->dim)[k]+(histogram_hi+i*bb->c->dim)[k]);
+ }
+ }
+ fclose(out);
+ }
+
+ {
+ FILE *out;
+ long *index=alloca(sizeof(long)*bb->c->entries);
+ sprintf(buffer,"%s-distance.m",basename);
+ out=fopen(buffer,"w");
+ if(!out){
+ fprintf(stderr,"Could not open file %s for writing\n",buffer);
+ exit(1);
+ }
+ for(j=0;j<bb->c->entries;j++){
+ if(histogram[j])histogram_distance[j]/=histogram[j];
+ index[j]=j;
+ }
+
+ qsort(index,bb->c->entries,sizeof(long),histerrsort);
+
+ for(j=0;j<bb->c->entries;j++)
+ for(k=0;k<histogram[index[j]];k++)
+ fprintf(out,"%g,\n",histogram_distance[index[j]]);
+ fclose(out);
+
+ }
+}
+
+void process_vector(codebook **bs,double *a){
+ int bi;
+ int i;
+ double amplitude=0.;
+ double distance=0.;
+ double base=0.;
+ int entry;
+ double *e;
+ memcpy(work,a,sizeof(double)*dim);
+
+ for(bi=0;bi<books;bi++){
+ codebook *b=bs[bi];
+ entry=(b->c->q_log?_logbest(b,work,1):_best(b,work,1));
+ e=b->valuelist+b->c->dim*entry;
+ for(i=0;i<b->c->dim;i++)work[i]-=e[i];
+ }
+
+ for(i=0;i<dim;i++){
+ double error=work[i];
+ if(bs[0]->c->q_sequencep){
+ amplitude=a[i]-base;
+ base=a[i];
+ }else
+ amplitude=a[i];
+
+ meanamplitude_acc+=fabs(amplitude);
+ meanamplitudesq_acc+=amplitude*amplitude;
+ meanerror_acc+=fabs(error);
+ meanerrorsq_acc+=error*error;
+
+ if(amplitude)
+ meandev_acc+=fabs(error/amplitude);
+ else
+ meandev_acc+=fabs(error); /* yeah, yeah */
+
+ histogram_errorsq[entry*dim+i]+=error*error;
+ histogram_error[entry*dim+i]+=fabs(error);
+ if(histogram[entry]==0 || histogram_hi[entry*dim+i]<error)
+ histogram_hi[entry*dim+i]=error;
+ if(histogram[entry]==0 || histogram_lo[entry*dim+i]>error)
+ histogram_lo[entry*dim+i]=error;
+ distance+=error*error;
+ }
+
+ histogram[entry]++;
+ histogram_distance[entry]+=sqrt(distance);
+
+ if((long)(count++)%100)spinnit("working.... lines: ",count);
+}
+
+void process_usage(void){
+ fprintf(stderr,
+ "usage: vqmetrics <codebook>.vqh datafile.vqd [datafile.vqd]...\n\n"
+ " data can be taken on stdin. Output goes to output files:\n"
+ " basename-me.m: gnuplot: mean error by entry value\n"
+ " basename-mse.m: gnuplot: mean square error by entry value\n"
+ " basename-worst.m: gnuplot: worst error by entry value\n"
+ " basename-distance.m: gnuplot file showing distance probability\n"
+ "\n");
+
+}
diff --git a/vq/partition.c b/vq/partition.c
new file mode 100644
index 00000000..00c21bdf
--- /dev/null
+++ b/vq/partition.c
@@ -0,0 +1,88 @@
+/********************************************************************
+ * *
+ * 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: function call to do data partitioning
+ last mod: $Id: partition.c,v 1.3.4.1 2000/04/06 15:59:38 xiphmont Exp $
+
+ ********************************************************************/
+
+/* make n files, one for each cell. partition the input data into the
+ N cells */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "vorbis/codebook.h"
+#include "../lib/sharedbook.h"
+#include "bookutil.h"
+
+/* open all the partitioning files */
+FILE **out;
+long count;
+
+void process_preprocess(codebook **b,char *basename){
+ int i;
+ char *buffer=alloca(strlen(basename)+80);
+ codebook *b0=*b;
+
+ if(!b0){
+ fprintf(stderr,"Specify at least one codebook, binky.\n");
+ exit(1);
+ }
+ if(b[1]){
+ fprintf(stderr,"Ignoring all but first codebook\n");
+ exit(1);
+ }
+
+ out=malloc(sizeof(FILE *)*b0->c->entries);
+ for(i=0;i<b0->c->entries;i++){
+ sprintf(buffer,"%s-%dp.vqd",basename,i);
+ out[i]=fopen(buffer,"w");
+ if(out[i]==NULL){
+ fprintf(stderr,"Could not open file %s\n",buffer);
+ exit(1);
+ }
+ }
+}
+
+void process_postprocess(codebook **b,char *basename){
+ codebook *b0=*b;
+ int i;
+ for(i=0;i<b0->c->entries;i++)
+ fclose(out[i]);
+ fprintf(stderr,"Done. \n");
+}
+
+/* just redirect this entry to the appropriate partition file */
+void process_vector(codebook **b,double *a){
+ codebook *b0=*b;
+ int entry=(b0->c->q_log?_logbest(b0,a,1):_best(b0,a,1));
+ int i;
+
+ for(i=0;i<b0->c->dim;i++)
+ fprintf(out[entry],"%f, ",a[i]);
+ fprintf(out[entry],"\n");
+ if(count++%100)spinnit("working.... lines: ",count);
+}
+
+void process_usage(void){
+ fprintf(stderr,
+ "usage: vqpartition codebook.vqh datafile.vqd [datafile.vqd]...\n\n"
+ " data can be taken on stdin. partitioning data is written to\n"
+
+ " files named <basename>-<n>p.vqh.\n\n");
+
+}
+
+
+
+
diff --git a/vq/residuedata.c b/vq/residuedata.c
index da26170d..19720ff9 100644
--- a/vq/residuedata.c
+++ b/vq/residuedata.c
@@ -12,7 +12,7 @@
********************************************************************
function: metrics and quantization code for residue VQ codebooks
- last mod: $Id: residuedata.c,v 1.2.4.1 2000/04/04 07:08:45 xiphmont Exp $
+ last mod: $Id: residuedata.c,v 1.2.4.2 2000/04/06 15:59:38 xiphmont Exp $
********************************************************************/
@@ -88,8 +88,8 @@ void vqext_quantize(vqgen *v,quant_meta *q){
information (as we know granularity beforehand and don't need to
maximize it) */
- q->min=_float24_pack(0.);
- q->delta=_float24_pack(scalequant);
+ q->min=_float32_pack(0.);
+ q->delta=_float32_pack(scalequant);
q->quant=_ilog(max);
if(quant_save){
diff --git a/vq/train.c b/vq/train.c
index 5db8751b..b3ca402b 100644
--- a/vq/train.c
+++ b/vq/train.c
@@ -12,7 +12,7 @@
********************************************************************
function: utility main for training codebooks
- last mod: $Id: train.c,v 1.16.4.1 2000/04/04 07:08:45 xiphmont Exp $
+ last mod: $Id: train.c,v 1.16.4.2 2000/04/06 15:59:38 xiphmont Exp $
********************************************************************/
@@ -137,8 +137,8 @@ int main(int argc,char *argv[]){
/* quant setup */
line=rline(in,out,1);
- if(sscanf(line,"%ld %ld %d %d",&q.min,&q.delta,
- &q.quant,&q.sequencep)!=4){
+ if(sscanf(line,"%d %ld %ld %d %d %lf %lf",&q.log,&q.min,&q.delta,
+ &q.quant,&q.sequencep,&q.encodebias,&q.entropy)!=7){
fprintf(stderr,"Syntax error reading book file\n");
exit(1);
}
@@ -315,7 +315,8 @@ int main(int argc,char *argv[]){
fprintf(out,"# OggVorbis VQ codebook trainer, intermediate file\n");
fprintf(out,"%s\n",vqext_booktype);
fprintf(out,"%d %d %d\n",entries,dim,vqext_aux);
- fprintf(out,"%ld %ld %d %d\n",q.min,q.delta,q.quant,q.sequencep);
+ fprintf(out,"%d %ld %ld %d %d %g %g\n",
+ q.log,q.min,q.delta,q.quant,q.sequencep,q.encodebias,q.entropy);
/* quantized entries */
fprintf(out,"# quantized entries---\n");
diff --git a/vq/vqgen.c b/vq/vqgen.c
index ed036446..3c9832de 100644
--- a/vq/vqgen.c
+++ b/vq/vqgen.c
@@ -12,7 +12,7 @@
********************************************************************
function: train a VQ codebook
- last mod: $Id: vqgen.c,v 1.30.4.1 2000/04/04 07:08:45 xiphmont Exp $
+ last mod: $Id: vqgen.c,v 1.30.4.2 2000/04/06 15:59:38 xiphmont Exp $
********************************************************************/
@@ -201,8 +201,11 @@ void vqgen_quantize(vqgen *v,quant_meta *q){
delta=(maxdel-mindel)/((1<<q->quant)-1.5);
- q->min=_float24_pack(mindel);
- q->delta=_float24_pack(delta);
+ q->min=_float32_pack(mindel);
+ q->delta=_float32_pack(delta);
+
+ mindel=_float32_unpack(q->min);
+ delta=_float32_unpack(q->delta);
for(j=0;j<v->entries;j++){
double last=0;
@@ -231,8 +234,8 @@ void vqgen_quantize(vqgen *v,quant_meta *q){
scales; we just make sure they're properly offset. */
void vqgen_unquantize(vqgen *v,quant_meta *q){
long j,k;
- double mindel=_float24_unpack(q->min);
- double delta=_float24_unpack(q->delta);
+ double mindel=_float32_unpack(q->min);
+ double delta=_float32_unpack(q->delta);
for(j=0;j<v->entries;j++){
double last=0.;
@@ -243,8 +246,8 @@ void vqgen_unquantize(vqgen *v,quant_meta *q){
if(q->sequencep)last=now;
if(q->log)now+=q->encodebias;
if(_now(v,j)[k]<0)now= -now;
- _now(v,j)[k]=now;
}
+ _now(v,j)[k]=now;
}
}
}
diff --git a/vq/vqsplit.c b/vq/vqsplit.c
index f76884c6..4706d551 100644
--- a/vq/vqsplit.c
+++ b/vq/vqsplit.c
@@ -12,7 +12,7 @@
********************************************************************
function: build a VQ codebook and the encoding decision 'tree'
- last mod: $Id: vqsplit.c,v 1.18.4.1 2000/04/04 07:08:45 xiphmont Exp $
+ last mod: $Id: vqsplit.c,v 1.18.4.2 2000/04/06 15:59:38 xiphmont Exp $
********************************************************************/
@@ -538,6 +538,7 @@ void vqsp_book(vqgen *v, codebook *b, long *quantlist){
long *probability=malloc(c->entries*sizeof(long));
for(i=0;i<c->entries;i++)probability[i]=1; /* trivial guard */
b->valuelist=v->entrylist; /* temporary for vqenc_entry; replaced later */
+ b->dim=c->dim;
/* sigh. A necessary hack */
for(i=0;i<t->aux;i++)t->p[i]*=c->dim;