Mercurial > hg > sv-dependency-builds
comparison src/libvorbis-1.3.3/vq/vqgen.c @ 1:05aa0afa9217
Bring in flac, ogg, vorbis
author | Chris Cannam |
---|---|
date | Tue, 19 Mar 2013 17:37:49 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:c7265573341e | 1:05aa0afa9217 |
---|---|
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. */ | |
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 | |
71 float *_weight_null(vqgen *v,float *a){ | |
72 return a; | |
73 } | |
74 | |
75 /* *must* be beefed up. */ | |
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 | |
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 | |
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 | |
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. */ | |
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 | |
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 | |
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; | |
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 | |
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 | |
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 |