1 /********************************************************************
3 * THIS FILE IS PART OF THE Ogg Vorbis SOFTWARE CODEC SOURCE CODE. *
4 * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
5 * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE. *
6 * PLEASE READ THESE TERMS DISTRIBUTING. *
8 * THE OggSQUISH SOURCE CODE IS (C) COPYRIGHT 1994-1999 *
9 * by 1999 Monty <monty@xiph.org> and The XIPHOPHORUS Company *
10 * http://www.xiph.org/ *
12 ********************************************************************
14 function: build a VQ codebook
15 author: Monty <xiphmont@mit.edu>
16 modifications by: Monty
17 last modification date: Dec 27 1999
19 ********************************************************************/
21 /* This code is *not* part of libvorbis. It is used to generate
22 trained codebooks offline and then spit the results into a
23 pregenerated codebook that is compiled into libvorbis. It is an
24 expensive (but good) algorithm. Run it on big iron. */
26 /* There are so many optimizations to explore in *both* stages that
27 considering the undertaking is almost withering. For now, we brute
36 /* Codebook generation happens in two steps:
38 1) Train the codebook with data collected from the encoder: We use
39 one of a few error metrics (which represent the distance between a
40 given data point and a candidate point in the training set) to
41 divide the training set up into cells representing roughly equal
42 probability of occurring.
44 2) Generate the codebook and auxiliary data from the trained data set
47 /* Codebook training ****************************************************
49 * The basic idea here is that a VQ codebook is like an m-dimensional
50 * foam with n bubbles. The bubbles compete for space/volume and are
51 * 'pressurized' [biased] according to some metric. The basic alg
52 * iterates through allowing the bubbles to compete for space until
53 * they converge (if the damping is dome properly) on a steady-state
54 * solution. Individual input points, collected from libvorbis, are
55 * used to train the algorithm monte-carlo style. */
57 /* internal helpers *****************************************************/
58 #define vN(data,i) (data+v->elements*i)
60 /* default metric; squared 'distance' from desired value. */
61 double _dist_sq(vqgen *v,double *a, double *b){
66 double val=(a[i]-b[i]);
72 double *_weight_null(vqgen *v,double *a){
76 /* *must* be beefed up. */
77 void _vqgen_seed(vqgen *v){
79 for(i=0;i<v->entries;i++)
80 memcpy(_now(v,i),_point(v,i),sizeof(double)*v->elements);
83 /* External calls *******************************************************/
85 /* We have two forms of quantization; in the first, each vector
86 element in the codebook entry is orthogonal. Residues would use this
87 quantization for example.
89 In the second, we have a sequence of monotonically increasing
90 values that we wish to quantize as deltas (to save space). We
91 still need to quantize so that absolute values are accurate. For
92 example, LSP quantizes all absolute values, but the book encodes
93 distance between values because each successive value is larger
94 than the preceeding value. Thus the desired quantibits apply to
95 the encoded (delta) values, not abs positions. This requires minor
96 additional encode-side trickery. */
98 /* 24 bit float (not IEEE; nonnormalized mantissa +
99 biased exponent ): neeeeemm mmmmmmmm mmmmmmmm */
101 #define VQ_FEXP_BIAS 20 /* bias toward values smaller than 1. */
102 long float24_pack(double val){
110 exp= floor(log(val)/log(2));
111 mant=rint(ldexp(val,17-exp));
112 exp=(exp+VQ_FEXP_BIAS)<<18;
114 return(sign|exp|mant);
117 double float24_unpack(long val){
118 double mant=val&0x3ffff;
119 double sign=val&0x800000;
120 double exp =(val&0x7c0000)>>18;
122 return(ldexp(mant,exp-17-VQ_FEXP_BIAS));
125 void vqgen_quantize(vqgen *v,quant_meta *q){
131 double maxquant=((1<<q->quant)-1);
135 mindel=maxdel=_now(v,0)[0];
137 for(j=0;j<v->entries;j++){
139 for(k=0;k<v->elements;k++){
140 if(mindel>_now(v,j)[k]-last)mindel=_now(v,j)[k]-last;
141 if(maxdel<_now(v,j)[k]-last)maxdel=_now(v,j)[k]-last;
142 if(q->sequencep)last=_now(v,j)[k];
147 /* first find the basic delta amount from the maximum span to be
148 encoded. Loosen the delta slightly to allow for additional error
149 during sequence quantization */
151 delta=(maxdel-mindel)/((1<<q->quant)-1.5);
153 q->min=float24_pack(mindel);
154 q->delta=float24_pack(delta);
156 for(j=0;j<v->entries;j++){
158 for(k=0;k<v->elements;k++){
159 double val=_now(v,j)[k];
160 double now=rint((val-last-mindel)/delta);
164 /* be paranoid; this should be impossible */
165 fprintf(stderr,"fault; quantized value<0\n");
170 /* be paranoid; this should be impossible */
171 fprintf(stderr,"fault; quantized value>max\n");
174 if(q->sequencep)last=(now*delta)+mindel+last;
179 /* much easier :-) */
180 void vqgen_unquantize(vqgen *v,quant_meta *q){
182 double mindel=float24_unpack(q->min);
183 double delta=float24_unpack(q->delta);
185 for(j=0;j<v->entries;j++){
187 for(k=0;k<v->elements;k++){
188 double now=_now(v,j)[k]*delta+last+mindel;
190 if(q->sequencep)last=now;
196 void vqgen_init(vqgen *v,int elements,int aux,int entries,
197 double (*metric)(vqgen *,double *, double *),
198 double *(*weight)(vqgen *,double *)){
199 memset(v,0,sizeof(vqgen));
201 v->elements=elements;
204 v->pointlist=malloc(v->allocated*(v->elements+v->aux)*sizeof(double));
207 v->entrylist=malloc(v->entries*v->elements*sizeof(double));
208 v->assigned=malloc(v->entries*sizeof(long));
209 v->bias=calloc(v->entries,sizeof(double));
211 v->metric_func=metric;
213 v->metric_func=_dist_sq;
215 v->weight_func=weight;
217 v->weight_func=_weight_null;
221 void vqgen_addpoint(vqgen *v, double *p,double *a){
222 if(v->points>=v->allocated){
224 v->pointlist=realloc(v->pointlist,v->allocated*(v->elements+v->aux)*
228 memcpy(_point(v,v->points),p,sizeof(double)*v->elements);
229 if(v->aux)memcpy(_point(v,v->points)+v->elements,a,sizeof(double)*v->aux);
231 if(v->points==v->entries)_vqgen_seed(v);
234 int directdsort(const void *a, const void *b){
235 double av=*((double *)a);
236 double bv=*((double *)b);
241 double vqgen_iterate(vqgen *v){
243 double fdesired=(double)v->points/v->entries;
244 long desired=fdesired;
245 long desired2=desired*2;
248 double *new=malloc(sizeof(double)*v->entries*v->elements);
249 long *nearcount=malloc(v->entries*sizeof(long));
250 double *nearbias=malloc(v->entries*desired2*sizeof(double));
257 sprintf(buff,"cells%d.m",v->it);
258 cells=fopen(buff,"w");
259 sprintf(buff,"assig%d.m",v->it);
260 assig=fopen(buff,"w");
261 sprintf(buff,"bias%d.m",v->it);
262 bias=fopen(buff,"w");
265 fprintf(stderr,"Pass #%d... ",v->it);
268 fprintf(stderr,"generation requires at least two entries\n");
272 /* fill in nearest points for entries */
273 /*memset(v->bias,0,sizeof(double)*v->entries);*/
274 memset(nearcount,0,sizeof(long)*v->entries);
275 memset(v->assigned,0,sizeof(long)*v->entries);
276 for(i=0;i<v->points;i++){
277 double *ppt=v->weight_func(v,_point(v,i));
278 double firstmetric=v->metric_func(v,_now(v,0),ppt)+v->bias[0];
279 double secondmetric=v->metric_func(v,_now(v,1),ppt)+v->bias[1];
282 if(firstmetric>secondmetric){
283 double temp=firstmetric;
284 firstmetric=secondmetric;
290 for(j=2;j<v->entries;j++){
291 double thismetric=v->metric_func(v,_now(v,j),ppt)+v->bias[j];
292 if(thismetric<secondmetric){
293 if(thismetric<firstmetric){
294 secondmetric=firstmetric;
295 secondentry=firstentry;
296 firstmetric=thismetric;
299 secondmetric=thismetric;
306 meterror+=sqrt(_dist_sq(v,_now(v,j),ppt));
307 /* set up midpoints for next iter */
309 for(k=0;k<v->elements;k++)
310 vN(new,j)[k]+=ppt[k];
312 for(k=0;k<v->elements;k++)
317 fprintf(cells,"%g %g\n%g %g\n\n",
318 _now(v,j)[0],_now(v,j)[1],
322 for(j=0;j<v->entries;j++){
325 double *nearbiasptr=nearbias+desired2*j;
328 /* 'thismetric' is to be the bias value necessary in the current
329 arrangement for entry j to capture point i */
331 /* use the secondary entry as the threshhold */
332 thismetric=secondmetric-v->metric_func(v,_now(v,j),ppt);
334 /* use the primary entry as the threshhold */
335 thismetric=firstmetric-v->metric_func(v,_now(v,j),ppt);
338 /* a cute two-stage delayed sorting hack */
340 nearbiasptr[k]=thismetric;
343 qsort(nearbiasptr,desired,sizeof(double),directdsort);
345 }else if(thismetric>nearbiasptr[desired-1]){
346 nearbiasptr[k]=thismetric;
349 qsort(nearbiasptr,desired2,sizeof(double),directdsort);
357 /* inflate/deflate */
358 for(i=0;i<v->entries;i++){
359 double *nearbiasptr=nearbias+desired2*i;
361 /* due to the delayed sorting, we likely need to finish it off....*/
362 if(nearcount[i]>desired)
363 qsort(nearbiasptr,nearcount[i],sizeof(double),directdsort);
365 v->bias[i]=nearbiasptr[desired-1];
368 /* assign midpoints */
370 for(j=0;j<v->entries;j++){
371 asserror+=fabs(v->assigned[j]-fdesired);
373 for(k=0;k<v->elements;k++)
374 _now(v,j)[k]=vN(new,j)[k]/v->assigned[j];
376 fprintf(assig,"%ld\n",v->assigned[j]);
377 fprintf(bias,"%g\n",v->bias[j]);
381 asserror/=(v->entries*fdesired);
382 fprintf(stderr,": dist %g(%g) metric error=%g \n",
383 asserror,fdesired,meterror/v->points/v->elements);