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.25 1999/12/30 07:27:04 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
34 /* Codebook generation happens in two steps:
36 1) Train the codebook with data collected from the encoder: We use
37 one of a few error metrics (which represent the distance between a
38 given data point and a candidate point in the training set) to
39 divide the training set up into cells representing roughly equal
40 probability of occurring.
42 2) Generate the codebook and auxiliary data from the trained data set
45 /* Codebook training ****************************************************
47 * The basic idea here is that a VQ codebook is like an m-dimensional
48 * foam with n bubbles. The bubbles compete for space/volume and are
49 * 'pressurized' [biased] according to some metric. The basic alg
50 * iterates through allowing the bubbles to compete for space until
51 * they converge (if the damping is dome properly) on a steady-state
52 * solution. Individual input points, collected from libvorbis, are
53 * used to train the algorithm monte-carlo style. */
55 /* internal helpers *****************************************************/
56 #define vN(data,i) (data+v->elements*i)
58 /* default metric; squared 'distance' from desired value. */
59 double _dist_sq(vqgen *v,double *a, double *b){
64 double val=(a[i]-b[i]);
70 double *_weight_null(vqgen *v,double *a){
74 /* *must* be beefed up. */
75 void _vqgen_seed(vqgen *v){
77 for(i=0;i<v->entries;i++)
78 memcpy(_now(v,i),_point(v,i),sizeof(double)*v->elements);
81 /* External calls *******************************************************/
83 /* We have two forms of quantization; in the first, each vector
84 element in the codebook entry is orthogonal. Residues would use this
85 quantization for example.
87 In the second, we have a sequence of monotonically increasing
88 values that we wish to quantize as deltas (to save space). We
89 still need to quantize so that absolute values are accurate. For
90 example, LSP quantizes all absolute values, but the book encodes
91 distance between values because each successive value is larger
92 than the preceeding value. Thus the desired quantibits apply to
93 the encoded (delta) values, not abs positions. This requires minor
94 additional encode-side trickery. */
96 /* 24 bit float (not IEEE; nonnormalized mantissa +
97 biased exponent ): neeeeemm mmmmmmmm mmmmmmmm */
99 #define VQ_FEXP_BIAS 20 /* bias toward values smaller than 1. */
100 long float24_pack(double val){
108 exp= floor(log(val)/log(2));
109 mant=rint(ldexp(val,17-exp));
110 exp=(exp+VQ_FEXP_BIAS)<<18;
112 return(sign|exp|mant);
115 double float24_unpack(long val){
116 double mant=val&0x3ffff;
117 double sign=val&0x800000;
118 double exp =(val&0x7c0000)>>18;
120 return(ldexp(mant,exp-17-VQ_FEXP_BIAS));
123 void vqgen_quantize(vqgen *v,quant_meta *q){
129 double maxquant=((1<<q->quant)-1);
133 mindel=maxdel=_now(v,0)[0];
135 for(j=0;j<v->entries;j++){
137 for(k=0;k<v->elements;k++){
138 if(mindel>_now(v,j)[k]-last)mindel=_now(v,j)[k]-last;
139 if(maxdel<_now(v,j)[k]-last)maxdel=_now(v,j)[k]-last;
140 if(q->sequencep)last=_now(v,j)[k];
145 /* first find the basic delta amount from the maximum span to be
146 encoded. Loosen the delta slightly to allow for additional error
147 during sequence quantization */
149 delta=(maxdel-mindel)/((1<<q->quant)-1.5);
151 q->min=float24_pack(mindel);
152 q->delta=float24_pack(delta);
154 for(j=0;j<v->entries;j++){
156 for(k=0;k<v->elements;k++){
157 double val=_now(v,j)[k];
158 double now=rint((val-last-mindel)/delta);
162 /* be paranoid; this should be impossible */
163 fprintf(stderr,"fault; quantized value<0\n");
168 /* be paranoid; this should be impossible */
169 fprintf(stderr,"fault; quantized value>max\n");
172 if(q->sequencep)last=(now*delta)+mindel+last;
177 /* much easier :-) */
178 void vqgen_unquantize(vqgen *v,quant_meta *q){
180 double mindel=float24_unpack(q->min);
181 double delta=float24_unpack(q->delta);
183 for(j=0;j<v->entries;j++){
185 for(k=0;k<v->elements;k++){
186 double now=_now(v,j)[k]*delta+last+mindel;
188 if(q->sequencep)last=now;
194 void vqgen_init(vqgen *v,int elements,int aux,int entries,
195 double (*metric)(vqgen *,double *, double *),
196 double *(*weight)(vqgen *,double *)){
197 memset(v,0,sizeof(vqgen));
199 v->elements=elements;
202 v->pointlist=malloc(v->allocated*(v->elements+v->aux)*sizeof(double));
205 v->entrylist=malloc(v->entries*v->elements*sizeof(double));
206 v->assigned=malloc(v->entries*sizeof(long));
207 v->bias=calloc(v->entries,sizeof(double));
209 v->metric_func=metric;
211 v->metric_func=_dist_sq;
213 v->weight_func=weight;
215 v->weight_func=_weight_null;
219 void vqgen_addpoint(vqgen *v, double *p,double *a){
220 if(v->points>=v->allocated){
222 v->pointlist=realloc(v->pointlist,v->allocated*(v->elements+v->aux)*
226 memcpy(_point(v,v->points),p,sizeof(double)*v->elements);
227 if(v->aux)memcpy(_point(v,v->points)+v->elements,a,sizeof(double)*v->aux);
229 if(v->points==v->entries)_vqgen_seed(v);
232 int directdsort(const void *a, const void *b){
233 double av=*((double *)a);
234 double bv=*((double *)b);
239 double vqgen_iterate(vqgen *v){
241 double fdesired=(double)v->points/v->entries;
242 long desired=fdesired;
243 long desired2=desired*2;
246 double *new=malloc(sizeof(double)*v->entries*v->elements);
247 long *nearcount=malloc(v->entries*sizeof(long));
248 double *nearbias=malloc(v->entries*desired2*sizeof(double));
255 sprintf(buff,"cells%d.m",v->it);
256 cells=fopen(buff,"w");
257 sprintf(buff,"assig%d.m",v->it);
258 assig=fopen(buff,"w");
259 sprintf(buff,"bias%d.m",v->it);
260 bias=fopen(buff,"w");
263 fprintf(stderr,"Pass #%d... ",v->it);
266 fprintf(stderr,"generation requires at least two entries\n");
270 /* fill in nearest points for entries */
271 /*memset(v->bias,0,sizeof(double)*v->entries);*/
272 memset(nearcount,0,sizeof(long)*v->entries);
273 memset(v->assigned,0,sizeof(long)*v->entries);
274 for(i=0;i<v->points;i++){
275 double *ppt=v->weight_func(v,_point(v,i));
276 double firstmetric=v->metric_func(v,_now(v,0),ppt)+v->bias[0];
277 double secondmetric=v->metric_func(v,_now(v,1),ppt)+v->bias[1];
280 if(firstmetric>secondmetric){
281 double temp=firstmetric;
282 firstmetric=secondmetric;
288 for(j=2;j<v->entries;j++){
289 double thismetric=v->metric_func(v,_now(v,j),ppt)+v->bias[j];
290 if(thismetric<secondmetric){
291 if(thismetric<firstmetric){
292 secondmetric=firstmetric;
293 secondentry=firstentry;
294 firstmetric=thismetric;
297 secondmetric=thismetric;
304 meterror+=sqrt(_dist_sq(v,_now(v,j),ppt));
305 /* set up midpoints for next iter */
307 for(k=0;k<v->elements;k++)
308 vN(new,j)[k]+=ppt[k];
310 for(k=0;k<v->elements;k++)
315 fprintf(cells,"%g %g\n%g %g\n\n",
316 _now(v,j)[0],_now(v,j)[1],
320 for(j=0;j<v->entries;j++){
323 double *nearbiasptr=nearbias+desired2*j;
326 /* 'thismetric' is to be the bias value necessary in the current
327 arrangement for entry j to capture point i */
329 /* use the secondary entry as the threshhold */
330 thismetric=secondmetric-v->metric_func(v,_now(v,j),ppt);
332 /* use the primary entry as the threshhold */
333 thismetric=firstmetric-v->metric_func(v,_now(v,j),ppt);
336 /* a cute two-stage delayed sorting hack */
338 nearbiasptr[k]=thismetric;
341 qsort(nearbiasptr,desired,sizeof(double),directdsort);
343 }else if(thismetric>nearbiasptr[desired-1]){
344 nearbiasptr[k]=thismetric;
347 qsort(nearbiasptr,desired2,sizeof(double),directdsort);
355 /* inflate/deflate */
356 for(i=0;i<v->entries;i++){
357 double *nearbiasptr=nearbias+desired2*i;
359 /* due to the delayed sorting, we likely need to finish it off....*/
360 if(nearcount[i]>desired)
361 qsort(nearbiasptr,nearcount[i],sizeof(double),directdsort);
363 v->bias[i]=nearbiasptr[desired-1];
366 /* assign midpoints */
368 for(j=0;j<v->entries;j++){
369 asserror+=fabs(v->assigned[j]-fdesired);
371 for(k=0;k<v->elements;k++)
372 _now(v,j)[k]=vN(new,j)[k]/v->assigned[j];
374 fprintf(assig,"%ld\n",v->assigned[j]);
375 fprintf(bias,"%g\n",v->bias[j]);
379 asserror/=(v->entries*fdesired);
380 fprintf(stderr,": dist %g(%g) metric error=%g \n",
381 asserror,fdesired,meterror/v->points/v->elements);