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.27 2000/01/05 15:05:01 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(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;
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));
261 sprintf(buff,"cells%d.m",v->it);
262 cells=fopen(buff,"w");
263 sprintf(buff,"assig%d.m",v->it);
264 assig=fopen(buff,"w");
265 sprintf(buff,"bias%d.m",v->it);
266 bias=fopen(buff,"w");
271 fprintf(stderr,"generation requires at least two entries\n");
275 /* fill in nearest points for entries */
276 /*memset(v->bias,0,sizeof(double)*v->entries);*/
277 memset(nearcount,0,sizeof(long)*v->entries);
278 memset(v->assigned,0,sizeof(long)*v->entries);
279 for(i=0;i<v->points;i++){
280 double *ppt=v->weight_func(v,_point(v,i));
281 double firstmetric=v->metric_func(v,_now(v,0),ppt)+v->bias[0];
282 double secondmetric=v->metric_func(v,_now(v,1),ppt)+v->bias[1];
286 spinnit("working... ",v->points-i);
288 if(firstmetric>secondmetric){
289 double temp=firstmetric;
290 firstmetric=secondmetric;
296 for(j=2;j<v->entries;j++){
297 double thismetric=v->metric_func(v,_now(v,j),ppt)+v->bias[j];
298 if(thismetric<secondmetric){
299 if(thismetric<firstmetric){
300 secondmetric=firstmetric;
301 secondentry=firstentry;
302 firstmetric=thismetric;
305 secondmetric=thismetric;
314 fprintf(cells,"%g %g\n%g %g\n\n",
315 _now(v,j)[0],_now(v,j)[1],
320 meterror+=firstmetric-v->bias[firstentry];
321 /* set up midpoints for next iter */
323 for(k=0;k<v->elements;k++)
324 vN(new,j)[k]+=ppt[k];
326 for(k=0;k<v->elements;k++)
329 for(j=0;j<v->entries;j++){
332 double *nearbiasptr=nearbias+desired2*j;
335 /* 'thismetric' is to be the bias value necessary in the current
336 arrangement for entry j to capture point i */
338 /* use the secondary entry as the threshhold */
339 thismetric=secondmetric-v->metric_func(v,_now(v,j),ppt);
341 /* use the primary entry as the threshhold */
342 thismetric=firstmetric-v->metric_func(v,_now(v,j),ppt);
345 /* a cute two-stage delayed sorting hack */
347 nearbiasptr[k]=thismetric;
350 qsort(nearbiasptr,desired,sizeof(double),directdsort);
352 }else if(thismetric>nearbiasptr[desired-1]){
353 nearbiasptr[k]=thismetric;
356 qsort(nearbiasptr,desired2,sizeof(double),directdsort);
364 /* inflate/deflate */
365 for(i=0;i<v->entries;i++){
366 double *nearbiasptr=nearbias+desired2*i;
368 spinnit("working... ",v->entries-i);
370 /* due to the delayed sorting, we likely need to finish it off....*/
371 if(nearcount[i]>desired)
372 qsort(nearbiasptr,nearcount[i],sizeof(double),directdsort);
374 v->bias[i]=nearbiasptr[desired-1];
377 /* assign midpoints */
379 for(j=0;j<v->entries;j++){
381 fprintf(assig,"%ld\n",v->assigned[j]);
382 fprintf(bias,"%g\n",v->bias[j]);
384 asserror+=fabs(v->assigned[j]-fdesired);
386 for(k=0;k<v->elements;k++)
387 _now(v,j)[k]=vN(new,j)[k]/v->assigned[j];
390 asserror/=(v->entries*fdesired);
392 fprintf(stderr,"Pass #%d... ",v->it);
393 fprintf(stderr,": dist %g(%g) metric error=%g \n",
394 asserror,fdesired,meterror/v->points);