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-2000 *
9 * by Monty <monty@xiph.org> and The XIPHOPHORUS Company *
10 * http://www.xiph.org/ *
12 ********************************************************************
14 function: train a VQ codebook
15 last mod: $Id: vqgen.c,v 1.26 2000/01/05 10:14:57 xiphmont Exp $
17 ********************************************************************/
19 /* This code is *not* part of libvorbis. It is used to generate
20 trained codebooks offline and then spit the results into a
21 pregenerated codebook that is compiled into libvorbis. It is an
22 expensive (but good) algorithm. Run it on big iron. */
24 /* There are so many optimizations to explore in *both* stages that
25 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 void spinnit(char *s,int n){
87 static long lasttime=0;
89 struct timeval thistime;
91 gettimeofday(&thistime,NULL);
92 test=thistime.tv_sec*10+thistime.tv_usec/100000;
96 fprintf(stderr,"%s%d ",s,n);
101 fprintf(stderr,"| \r");
104 fprintf(stderr,"/ \r");
107 fprintf(stderr,"- \r");
110 fprintf(stderr,"\\ \r");
117 /* We have two forms of quantization; in the first, each vector
118 element in the codebook entry is orthogonal. Residues would use this
119 quantization for example.
121 In the second, we have a sequence of monotonically increasing
122 values that we wish to quantize as deltas (to save space). We
123 still need to quantize so that absolute values are accurate. For
124 example, LSP quantizes all absolute values, but the book encodes
125 distance between values because each successive value is larger
126 than the preceeding value. Thus the desired quantibits apply to
127 the encoded (delta) values, not abs positions. This requires minor
128 additional encode-side trickery. */
130 void vqgen_quantize(vqgen *v,quant_meta *q){
136 double maxquant=((1<<q->quant)-1);
140 mindel=maxdel=_now(v,0)[0];
142 for(j=0;j<v->entries;j++){
144 for(k=0;k<v->elements;k++){
145 if(mindel>_now(v,j)[k]-last)mindel=_now(v,j)[k]-last;
146 if(maxdel<_now(v,j)[k]-last)maxdel=_now(v,j)[k]-last;
147 if(q->sequencep)last=_now(v,j)[k];
152 /* first find the basic delta amount from the maximum span to be
153 encoded. Loosen the delta slightly to allow for additional error
154 during sequence quantization */
156 delta=(maxdel-mindel)/((1<<q->quant)-1.5);
158 q->min=float24_pack(mindel);
159 q->delta=float24_pack(delta);
161 for(j=0;j<v->entries;j++){
163 for(k=0;k<v->elements;k++){
164 double val=_now(v,j)[k];
165 double now=rint((val-last-mindel)/delta);
169 /* be paranoid; this should be impossible */
170 fprintf(stderr,"fault; quantized value<0\n");
175 /* be paranoid; this should be impossible */
176 fprintf(stderr,"fault; quantized value>max\n");
179 if(q->sequencep)last=(now*delta)+mindel+last;
184 /* much easier :-) */
185 void vqgen_unquantize(vqgen *v,quant_meta *q){
187 double mindel=float24_unpack(q->min);
188 double delta=float24_unpack(q->delta);
190 for(j=0;j<v->entries;j++){
192 for(k=0;k<v->elements;k++){
193 double now=_now(v,j)[k]*delta+last+mindel;
195 if(q->sequencep)last=now;
201 void vqgen_init(vqgen *v,int elements,int aux,int entries,
202 double (*metric)(vqgen *,double *, double *),
203 double *(*weight)(vqgen *,double *)){
204 memset(v,0,sizeof(vqgen));
206 v->elements=elements;
209 v->pointlist=malloc(v->allocated*(v->elements+v->aux)*sizeof(double));
212 v->entrylist=malloc(v->entries*v->elements*sizeof(double));
213 v->assigned=malloc(v->entries*sizeof(long));
214 v->bias=calloc(v->entries,sizeof(double));
216 v->metric_func=metric;
218 v->metric_func=_dist_sq;
220 v->weight_func=weight;
222 v->weight_func=_weight_null;
226 void vqgen_addpoint(vqgen *v, double *p,double *a){
227 if(v->points>=v->allocated){
229 v->pointlist=realloc(v->pointlist,v->allocated*(v->elements+v->aux)*
233 memcpy(_point(v,v->points),p,sizeof(double)*v->elements);
234 if(v->aux)memcpy(_point(v,v->points)+v->elements,a,sizeof(double)*v->aux);
236 if(v->points==v->entries)_vqgen_seed(v);
239 int directdsort(const void *a, const void *b){
240 double av=*((double *)a);
241 double bv=*((double *)b);
246 double vqgen_iterate(vqgen *v){
248 double fdesired=(double)v->points/v->entries;
249 long desired=fdesired;
250 long desired2=desired*2;
253 double *new=malloc(sizeof(double)*v->entries*v->elements);
254 long *nearcount=malloc(v->entries*sizeof(long));
255 double *nearbias=malloc(v->entries*desired2*sizeof(double));
258 fprintf(stderr,"generation requires at least two entries\n");
262 /* fill in nearest points for entries */
263 /*memset(v->bias,0,sizeof(double)*v->entries);*/
264 memset(nearcount,0,sizeof(long)*v->entries);
265 memset(v->assigned,0,sizeof(long)*v->entries);
266 for(i=0;i<v->points;i++){
267 double *ppt=v->weight_func(v,_point(v,i));
268 double firstmetric=v->metric_func(v,_now(v,0),ppt)+v->bias[0];
269 double secondmetric=v->metric_func(v,_now(v,1),ppt)+v->bias[1];
273 spinnit("working... ",v->points-i);
275 if(firstmetric>secondmetric){
276 double temp=firstmetric;
277 firstmetric=secondmetric;
283 for(j=2;j<v->entries;j++){
284 double thismetric=v->metric_func(v,_now(v,j),ppt)+v->bias[j];
285 if(thismetric<secondmetric){
286 if(thismetric<firstmetric){
287 secondmetric=firstmetric;
288 secondentry=firstentry;
289 firstmetric=thismetric;
292 secondmetric=thismetric;
299 meterror+=sqrt(_dist_sq(v,_now(v,j),ppt)/v->elements);
300 /* set up midpoints for next iter */
302 for(k=0;k<v->elements;k++)
303 vN(new,j)[k]+=ppt[k];
305 for(k=0;k<v->elements;k++)
308 for(j=0;j<v->entries;j++){
311 double *nearbiasptr=nearbias+desired2*j;
314 /* 'thismetric' is to be the bias value necessary in the current
315 arrangement for entry j to capture point i */
317 /* use the secondary entry as the threshhold */
318 thismetric=secondmetric-v->metric_func(v,_now(v,j),ppt);
320 /* use the primary entry as the threshhold */
321 thismetric=firstmetric-v->metric_func(v,_now(v,j),ppt);
324 /* a cute two-stage delayed sorting hack */
326 nearbiasptr[k]=thismetric;
329 qsort(nearbiasptr,desired,sizeof(double),directdsort);
331 }else if(thismetric>nearbiasptr[desired-1]){
332 nearbiasptr[k]=thismetric;
335 qsort(nearbiasptr,desired2,sizeof(double),directdsort);
343 /* inflate/deflate */
344 for(i=0;i<v->entries;i++){
345 double *nearbiasptr=nearbias+desired2*i;
347 spinnit("working... ",v->entries-i);
349 /* due to the delayed sorting, we likely need to finish it off....*/
350 if(nearcount[i]>desired)
351 qsort(nearbiasptr,nearcount[i],sizeof(double),directdsort);
353 v->bias[i]=nearbiasptr[desired-1];
356 /* assign midpoints */
358 for(j=0;j<v->entries;j++){
359 asserror+=fabs(v->assigned[j]-fdesired);
361 for(k=0;k<v->elements;k++)
362 _now(v,j)[k]=vN(new,j)[k]/v->assigned[j];
365 asserror/=(v->entries*fdesired);
367 fprintf(stderr,"Pass #%d... ",v->it);
368 fprintf(stderr,": dist %g(%g) metric error=%g \n",
369 asserror,fdesired,meterror/v->points);