• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /********************************************************************
2  *                                                                  *
3  * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
4  * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
5  * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
6  * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
7  *                                                                  *
8  * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001             *
9  * by the Xiph.Org Foundation http://www.xiph.org/                  *
10  *                                                                  *
11  ********************************************************************
12 
13  function: train a VQ codebook
14  last mod: $Id: vqgen.c 16037 2009-05-26 21:10:58Z xiphmont $
15 
16  ********************************************************************/
17 
18 /* This code is *not* part of libvorbis.  It is used to generate
19    trained codebooks offline and then spit the results into a
20    pregenerated codebook that is compiled into libvorbis.  It is an
21    expensive (but good) algorithm.  Run it on big iron. */
22 
23 /* There are so many optimizations to explore in *both* stages that
24    considering the undertaking is almost withering.  For now, we brute
25    force it all */
26 
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <math.h>
30 #include <string.h>
31 
32 #include "vqgen.h"
33 #include "bookutil.h"
34 
35 /* Codebook generation happens in two steps:
36 
37    1) Train the codebook with data collected from the encoder: We use
38    one of a few error metrics (which represent the distance between a
39    given data point and a candidate point in the training set) to
40    divide the training set up into cells representing roughly equal
41    probability of occurring.
42 
43    2) Generate the codebook and auxiliary data from the trained data set
44 */
45 
46 /* Codebook training ****************************************************
47  *
48  * The basic idea here is that a VQ codebook is like an m-dimensional
49  * foam with n bubbles.  The bubbles compete for space/volume and are
50  * 'pressurized' [biased] according to some metric.  The basic alg
51  * iterates through allowing the bubbles to compete for space until
52  * they converge (if the damping is dome properly) on a steady-state
53  * solution. Individual input points, collected from libvorbis, are
54  * used to train the algorithm monte-carlo style.  */
55 
56 /* internal helpers *****************************************************/
57 #define vN(data,i) (data+v->elements*i)
58 
59 /* default metric; squared 'distance' from desired value. */
_dist(vqgen * v,float * a,float * b)60 float _dist(vqgen *v,float *a, float *b){
61   int i;
62   int el=v->elements;
63   float acc=0.f;
64   for(i=0;i<el;i++){
65     float val=(a[i]-b[i]);
66     acc+=val*val;
67   }
68   return sqrt(acc);
69 }
70 
_weight_null(vqgen * v,float * a)71 float *_weight_null(vqgen *v,float *a){
72   return a;
73 }
74 
75 /* *must* be beefed up. */
_vqgen_seed(vqgen * v)76 void _vqgen_seed(vqgen *v){
77   long i;
78   for(i=0;i<v->entries;i++)
79     memcpy(_now(v,i),_point(v,i),sizeof(float)*v->elements);
80   v->seeded=1;
81 }
82 
directdsort(const void * a,const void * b)83 int directdsort(const void *a, const void *b){
84   float av=*((float *)a);
85   float bv=*((float *)b);
86   return (av<bv)-(av>bv);
87 }
88 
vqgen_cellmetric(vqgen * v)89 void vqgen_cellmetric(vqgen *v){
90   int j,k;
91   float min=-1.f,max=-1.f,mean=0.f,acc=0.f;
92   long dup=0,unused=0;
93  #ifdef NOISY
94   int i;
95    char buff[80];
96    float spacings[v->entries];
97    int count=0;
98    FILE *cells;
99    sprintf(buff,"cellspace%d.m",v->it);
100    cells=fopen(buff,"w");
101 #endif
102 
103   /* minimum, maximum, cell spacing */
104   for(j=0;j<v->entries;j++){
105     float localmin=-1.;
106 
107     for(k=0;k<v->entries;k++){
108       if(j!=k){
109         float this=_dist(v,_now(v,j),_now(v,k));
110         if(this>0){
111           if(v->assigned[k] && (localmin==-1 || this<localmin))
112             localmin=this;
113         }else{
114           if(k<j){
115             dup++;
116             break;
117           }
118         }
119       }
120     }
121     if(k<v->entries)continue;
122 
123     if(v->assigned[j]==0){
124       unused++;
125       continue;
126     }
127 
128     localmin=v->max[j]+localmin/2; /* this gives us rough diameter */
129     if(min==-1 || localmin<min)min=localmin;
130     if(max==-1 || localmin>max)max=localmin;
131     mean+=localmin;
132     acc++;
133 #ifdef NOISY
134     spacings[count++]=localmin;
135 #endif
136   }
137 
138   fprintf(stderr,"cell diameter: %.03g::%.03g::%.03g (%ld unused/%ld dup)\n",
139           min,mean/acc,max,unused,dup);
140 
141 #ifdef NOISY
142   qsort(spacings,count,sizeof(float),directdsort);
143   for(i=0;i<count;i++)
144     fprintf(cells,"%g\n",spacings[i]);
145   fclose(cells);
146 #endif
147 
148 }
149 
150 /* External calls *******************************************************/
151 
152 /* We have two forms of quantization; in the first, each vector
153    element in the codebook entry is orthogonal.  Residues would use this
154    quantization for example.
155 
156    In the second, we have a sequence of monotonically increasing
157    values that we wish to quantize as deltas (to save space).  We
158    still need to quantize so that absolute values are accurate. For
159    example, LSP quantizes all absolute values, but the book encodes
160    distance between values because each successive value is larger
161    than the preceeding value.  Thus the desired quantibits apply to
162    the encoded (delta) values, not abs positions. This requires minor
163    additional encode-side trickery. */
164 
vqgen_quantize(vqgen * v,quant_meta * q)165 void vqgen_quantize(vqgen *v,quant_meta *q){
166 
167   float maxdel;
168   float mindel;
169 
170   float delta;
171   float maxquant=((1<<q->quant)-1);
172 
173   int j,k;
174 
175   mindel=maxdel=_now(v,0)[0];
176 
177   for(j=0;j<v->entries;j++){
178     float last=0.f;
179     for(k=0;k<v->elements;k++){
180       if(mindel>_now(v,j)[k]-last)mindel=_now(v,j)[k]-last;
181       if(maxdel<_now(v,j)[k]-last)maxdel=_now(v,j)[k]-last;
182       if(q->sequencep)last=_now(v,j)[k];
183     }
184   }
185 
186 
187   /* first find the basic delta amount from the maximum span to be
188      encoded.  Loosen the delta slightly to allow for additional error
189      during sequence quantization */
190 
191   delta=(maxdel-mindel)/((1<<q->quant)-1.5f);
192 
193   q->min=_float32_pack(mindel);
194   q->delta=_float32_pack(delta);
195 
196   mindel=_float32_unpack(q->min);
197   delta=_float32_unpack(q->delta);
198 
199   for(j=0;j<v->entries;j++){
200     float last=0;
201     for(k=0;k<v->elements;k++){
202       float val=_now(v,j)[k];
203       float now=rint((val-last-mindel)/delta);
204 
205       _now(v,j)[k]=now;
206       if(now<0){
207         /* be paranoid; this should be impossible */
208         fprintf(stderr,"fault; quantized value<0\n");
209         exit(1);
210       }
211 
212       if(now>maxquant){
213         /* be paranoid; this should be impossible */
214         fprintf(stderr,"fault; quantized value>max\n");
215         exit(1);
216       }
217       if(q->sequencep)last=(now*delta)+mindel+last;
218     }
219   }
220 }
221 
222 /* much easier :-).  Unlike in the codebook, we don't un-log log
223    scales; we just make sure they're properly offset. */
vqgen_unquantize(vqgen * v,quant_meta * q)224 void vqgen_unquantize(vqgen *v,quant_meta *q){
225   long j,k;
226   float mindel=_float32_unpack(q->min);
227   float delta=_float32_unpack(q->delta);
228 
229   for(j=0;j<v->entries;j++){
230     float last=0.f;
231     for(k=0;k<v->elements;k++){
232       float now=_now(v,j)[k];
233       now=fabs(now)*delta+last+mindel;
234       if(q->sequencep)last=now;
235       _now(v,j)[k]=now;
236     }
237   }
238 }
239 
vqgen_init(vqgen * v,int elements,int aux,int entries,float mindist,float (* metric)(vqgen *,float *,float *),float * (* weight)(vqgen *,float *),int centroid)240 void vqgen_init(vqgen *v,int elements,int aux,int entries,float mindist,
241                 float  (*metric)(vqgen *,float *, float *),
242                 float *(*weight)(vqgen *,float *),int centroid){
243   memset(v,0,sizeof(vqgen));
244 
245   v->centroid=centroid;
246   v->elements=elements;
247   v->aux=aux;
248   v->mindist=mindist;
249   v->allocated=32768;
250   v->pointlist=_ogg_malloc(v->allocated*(v->elements+v->aux)*sizeof(float));
251 
252   v->entries=entries;
253   v->entrylist=_ogg_malloc(v->entries*v->elements*sizeof(float));
254   v->assigned=_ogg_malloc(v->entries*sizeof(long));
255   v->bias=_ogg_calloc(v->entries,sizeof(float));
256   v->max=_ogg_calloc(v->entries,sizeof(float));
257   if(metric)
258     v->metric_func=metric;
259   else
260     v->metric_func=_dist;
261   if(weight)
262     v->weight_func=weight;
263   else
264     v->weight_func=_weight_null;
265 
266   v->asciipoints=tmpfile();
267 
268 }
269 
vqgen_addpoint(vqgen * v,float * p,float * a)270 void vqgen_addpoint(vqgen *v, float *p,float *a){
271   int k;
272   for(k=0;k<v->elements;k++)
273     fprintf(v->asciipoints,"%.12g\n",p[k]);
274   for(k=0;k<v->aux;k++)
275     fprintf(v->asciipoints,"%.12g\n",a[k]);
276 
277   if(v->points>=v->allocated){
278     v->allocated*=2;
279     v->pointlist=_ogg_realloc(v->pointlist,v->allocated*(v->elements+v->aux)*
280                          sizeof(float));
281   }
282 
283   memcpy(_point(v,v->points),p,sizeof(float)*v->elements);
284   if(v->aux)memcpy(_point(v,v->points)+v->elements,a,sizeof(float)*v->aux);
285 
286   /* quantize to the density mesh if it's selected */
287   if(v->mindist>0.f){
288     /* quantize to the mesh */
289     for(k=0;k<v->elements+v->aux;k++)
290       _point(v,v->points)[k]=
291         rint(_point(v,v->points)[k]/v->mindist)*v->mindist;
292   }
293   v->points++;
294   if(!(v->points&0xff))spinnit("loading... ",v->points);
295 }
296 
297 /* yes, not threadsafe.  These utils aren't */
298 static int sortit=0;
299 static int sortsize=0;
meshcomp(const void * a,const void * b)300 static int meshcomp(const void *a,const void *b){
301   if(((sortit++)&0xfff)==0)spinnit("sorting mesh...",sortit);
302   return(memcmp(a,b,sortsize));
303 }
304 
vqgen_sortmesh(vqgen * v)305 void vqgen_sortmesh(vqgen *v){
306   sortit=0;
307   if(v->mindist>0.f){
308     long i,march=1;
309 
310     /* sort to make uniqueness detection trivial */
311     sortsize=(v->elements+v->aux)*sizeof(float);
312     qsort(v->pointlist,v->points,sortsize,meshcomp);
313 
314     /* now march through and eliminate dupes */
315     for(i=1;i<v->points;i++){
316       if(memcmp(_point(v,i),_point(v,i-1),sortsize)){
317         /* a new, unique entry.  march it down */
318         if(i>march)memcpy(_point(v,march),_point(v,i),sortsize);
319         march++;
320       }
321       spinnit("eliminating density... ",v->points-i);
322     }
323 
324     /* we're done */
325     fprintf(stderr,"\r%ld training points remining out of %ld"
326             " after density mesh (%ld%%)\n",march,v->points,march*100/v->points);
327     v->points=march;
328 
329   }
330   v->sorted=1;
331 }
332 
vqgen_iterate(vqgen * v,int biasp)333 float vqgen_iterate(vqgen *v,int biasp){
334   long   i,j,k;
335 
336   float fdesired;
337   long  desired;
338   long  desired2;
339 
340   float asserror=0.f;
341   float meterror=0.f;
342   float *new;
343   float *new2;
344   long   *nearcount;
345   float *nearbias;
346  #ifdef NOISY
347    char buff[80];
348    FILE *assig;
349    FILE *bias;
350    FILE *cells;
351    sprintf(buff,"cells%d.m",v->it);
352    cells=fopen(buff,"w");
353    sprintf(buff,"assig%d.m",v->it);
354    assig=fopen(buff,"w");
355    sprintf(buff,"bias%d.m",v->it);
356    bias=fopen(buff,"w");
357  #endif
358 
359 
360   if(v->entries<2){
361     fprintf(stderr,"generation requires at least two entries\n");
362     exit(1);
363   }
364 
365   if(!v->sorted)vqgen_sortmesh(v);
366   if(!v->seeded)_vqgen_seed(v);
367 
368   fdesired=(float)v->points/v->entries;
369   desired=fdesired;
370   desired2=desired*2;
371   new=_ogg_malloc(sizeof(float)*v->entries*v->elements);
372   new2=_ogg_malloc(sizeof(float)*v->entries*v->elements);
373   nearcount=_ogg_malloc(v->entries*sizeof(long));
374   nearbias=_ogg_malloc(v->entries*desired2*sizeof(float));
375 
376   /* fill in nearest points for entry biasing */
377   /*memset(v->bias,0,sizeof(float)*v->entries);*/
378   memset(nearcount,0,sizeof(long)*v->entries);
379   memset(v->assigned,0,sizeof(long)*v->entries);
380   if(biasp){
381     for(i=0;i<v->points;i++){
382       float *ppt=v->weight_func(v,_point(v,i));
383       float firstmetric=v->metric_func(v,_now(v,0),ppt)+v->bias[0];
384       float secondmetric=v->metric_func(v,_now(v,1),ppt)+v->bias[1];
385       long   firstentry=0;
386       long   secondentry=1;
387 
388       if(!(i&0xff))spinnit("biasing... ",v->points+v->points+v->entries-i);
389 
390       if(firstmetric>secondmetric){
391         float temp=firstmetric;
392         firstmetric=secondmetric;
393         secondmetric=temp;
394         firstentry=1;
395         secondentry=0;
396       }
397 
398       for(j=2;j<v->entries;j++){
399         float thismetric=v->metric_func(v,_now(v,j),ppt)+v->bias[j];
400         if(thismetric<secondmetric){
401           if(thismetric<firstmetric){
402             secondmetric=firstmetric;
403             secondentry=firstentry;
404             firstmetric=thismetric;
405             firstentry=j;
406           }else{
407             secondmetric=thismetric;
408             secondentry=j;
409           }
410         }
411       }
412 
413       j=firstentry;
414       for(j=0;j<v->entries;j++){
415 
416         float thismetric,localmetric;
417         float *nearbiasptr=nearbias+desired2*j;
418         long k=nearcount[j];
419 
420         localmetric=v->metric_func(v,_now(v,j),ppt);
421         /* 'thismetric' is to be the bias value necessary in the current
422            arrangement for entry j to capture point i */
423         if(firstentry==j){
424           /* use the secondary entry as the threshhold */
425           thismetric=secondmetric-localmetric;
426         }else{
427           /* use the primary entry as the threshhold */
428           thismetric=firstmetric-localmetric;
429         }
430 
431         /* support the idea of 'minimum distance'... if we want the
432            cells in a codebook to be roughly some minimum size (as with
433            the low resolution residue books) */
434 
435         /* a cute two-stage delayed sorting hack */
436         if(k<desired){
437           nearbiasptr[k]=thismetric;
438           k++;
439           if(k==desired){
440             spinnit("biasing... ",v->points+v->points+v->entries-i);
441             qsort(nearbiasptr,desired,sizeof(float),directdsort);
442           }
443 
444         }else if(thismetric>nearbiasptr[desired-1]){
445           nearbiasptr[k]=thismetric;
446           k++;
447           if(k==desired2){
448             spinnit("biasing... ",v->points+v->points+v->entries-i);
449             qsort(nearbiasptr,desired2,sizeof(float),directdsort);
450             k=desired;
451           }
452         }
453         nearcount[j]=k;
454       }
455     }
456 
457     /* inflate/deflate */
458 
459     for(i=0;i<v->entries;i++){
460       float *nearbiasptr=nearbias+desired2*i;
461 
462       spinnit("biasing... ",v->points+v->entries-i);
463 
464       /* due to the delayed sorting, we likely need to finish it off....*/
465       if(nearcount[i]>desired)
466         qsort(nearbiasptr,nearcount[i],sizeof(float),directdsort);
467 
468       v->bias[i]=nearbiasptr[desired-1];
469 
470     }
471   }else{
472     memset(v->bias,0,v->entries*sizeof(float));
473   }
474 
475   /* Now assign with new bias and find new midpoints */
476   for(i=0;i<v->points;i++){
477     float *ppt=v->weight_func(v,_point(v,i));
478     float firstmetric=v->metric_func(v,_now(v,0),ppt)+v->bias[0];
479     long   firstentry=0;
480 
481     if(!(i&0xff))spinnit("centering... ",v->points-i);
482 
483     for(j=0;j<v->entries;j++){
484       float thismetric=v->metric_func(v,_now(v,j),ppt)+v->bias[j];
485       if(thismetric<firstmetric){
486         firstmetric=thismetric;
487         firstentry=j;
488       }
489     }
490 
491     j=firstentry;
492 
493 #ifdef NOISY
494     fprintf(cells,"%g %g\n%g %g\n\n",
495           _now(v,j)[0],_now(v,j)[1],
496           ppt[0],ppt[1]);
497 #endif
498 
499     firstmetric-=v->bias[j];
500     meterror+=firstmetric;
501 
502     if(v->centroid==0){
503       /* set up midpoints for next iter */
504       if(v->assigned[j]++){
505         for(k=0;k<v->elements;k++)
506           vN(new,j)[k]+=ppt[k];
507         if(firstmetric>v->max[j])v->max[j]=firstmetric;
508       }else{
509         for(k=0;k<v->elements;k++)
510           vN(new,j)[k]=ppt[k];
511         v->max[j]=firstmetric;
512       }
513     }else{
514       /* centroid */
515       if(v->assigned[j]++){
516         for(k=0;k<v->elements;k++){
517           if(vN(new,j)[k]>ppt[k])vN(new,j)[k]=ppt[k];
518           if(vN(new2,j)[k]<ppt[k])vN(new2,j)[k]=ppt[k];
519         }
520         if(firstmetric>v->max[firstentry])v->max[j]=firstmetric;
521       }else{
522         for(k=0;k<v->elements;k++){
523           vN(new,j)[k]=ppt[k];
524           vN(new2,j)[k]=ppt[k];
525         }
526         v->max[firstentry]=firstmetric;
527       }
528     }
529   }
530 
531   /* assign midpoints */
532 
533   for(j=0;j<v->entries;j++){
534 #ifdef NOISY
535     fprintf(assig,"%ld\n",v->assigned[j]);
536     fprintf(bias,"%g\n",v->bias[j]);
537 #endif
538     asserror+=fabs(v->assigned[j]-fdesired);
539     if(v->assigned[j]){
540       if(v->centroid==0){
541         for(k=0;k<v->elements;k++)
542           _now(v,j)[k]=vN(new,j)[k]/v->assigned[j];
543       }else{
544         for(k=0;k<v->elements;k++)
545           _now(v,j)[k]=(vN(new,j)[k]+vN(new2,j)[k])/2.f;
546       }
547     }
548   }
549 
550   asserror/=(v->entries*fdesired);
551 
552   fprintf(stderr,"Pass #%d... ",v->it);
553   fprintf(stderr,": dist %g(%g) metric error=%g \n",
554           asserror,fdesired,meterror/v->points);
555   v->it++;
556 
557   free(new);
558   free(nearcount);
559   free(nearbias);
560 #ifdef NOISY
561   fclose(assig);
562   fclose(bias);
563   fclose(cells);
564 #endif
565   return(asserror);
566 }
567 
568