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-1999 *
9 * by 1999 Monty <monty@xiph.org> and The XIPHOPHORUS Company *
10 * http://www.xiph.org/ *
12 ********************************************************************
14 function: build a VQ codebook
15 author: Monty <xiphmont@mit.edu>
16 modifications by: Monty
17 last modification date: Nov 18 1999
19 ********************************************************************/
21 /* This code is *not* part of libvorbis. It is used to generate
22 trained codebooks offline and then spit the results into a
23 pregenerated codebook that is compiled into libvorbis. It is an
24 expensive (but good) algorithm. Run it on big iron. */
31 /************************************************************************
32 * The basic idea here is that a VQ codebook is like an m-dimensional
33 * foam with n bubbles. The bubbles compete for space/volume and are
34 * 'pressurized' [biased] according to some metric. The basic alg
35 * iterates through allowing the bubbles to compete for space until
36 * they converge (if the damping is dome properly) on a steady-state
39 * We use the ratio of local to average 'error' as the metric to bias a
40 * variable-length word codebook, and probability of occurrence within
41 * that bubble as the metric to bias fixed length word
42 * codebooks. Individual input points, collected from libvorbis, are
43 * used to train the algorithm monte-carlo style. */
64 double (*metric_func) (struct vqgen *v,double *a,double *b);
67 /* internal helpers *****************************************************/
68 double *_point(vqgen *v,long ptr){
69 return v->pointlist+(v->elements*ptr);
72 double *_now(vqgen *v,long ptr){
73 return v->entry_now+(v->elements*ptr);
76 double *_next(vqgen *v,long ptr){
77 return v->entry_next+(v->elements*ptr);
80 double _dist_sq(vqgen *v,double *a, double *b){
85 double val=(a[i]-b[i]);
91 void vqgen_init(vqgen *v,int elements,int entries,
92 double (*metric)(vqgen *,double *, double *),
94 memset(v,0,sizeof(vqgen));
99 v->pointlist=malloc(v->allocated*v->elements*sizeof(double));
100 v->first=malloc(v->allocated*sizeof(long));
101 v->second=malloc(v->allocated*sizeof(long));
104 v->entry_now=malloc(v->entries*v->elements*sizeof(double));
105 v->entry_next=malloc(v->entries*v->elements*sizeof(double));
106 v->assigned=malloc(v->entries*sizeof(long));
107 v->metric=malloc(v->entries*sizeof(double));
108 v->bias=calloc(v->entries,sizeof(double));
110 v->metric_func=metric;
112 v->metric_func=_dist_sq;
115 /* *must* be beefed up. Perhaps a Floyd-Steinberg like scattering? */
116 void _vqgen_seed(vqgen *v){
117 memcpy(v->entry_now,v->pointlist,sizeof(double)*v->entries*v->elements);
120 void vqgen_addpoint(vqgen *v, double *p){
121 if(v->points>=v->allocated){
123 v->pointlist=realloc(v->pointlist,v->allocated*v->elements*sizeof(double));
124 v->first=realloc(v->first,v->allocated*sizeof(long));
125 v->second=realloc(v->second,v->allocated*sizeof(long));
128 memcpy(_point(v,v->points),p,sizeof(double)*v->elements);
130 if(v->points==v->entries)_vqgen_seed(v);
134 int sort_t_compare(const void *ap,const void *bp){
137 double val=sort_t_vals[a]-sort_t_vals[b];
143 void vqgen_iterate(vqgen *v,int biasp){
144 static int iteration=0;
155 sprintf(name,"space%d.m",iteration);
156 graph=fopen(name,"w");
158 sprintf(name,"err%d.m",iteration);
161 sprintf(name,"bias%d.m",iteration);
162 bias=fopen(name,"w");
164 sprintf(name,"as%d.m",iteration);
169 memset(v->entry_next,0,sizeof(double)*v->elements*v->entries);
170 memset(v->assigned,0,sizeof(long)*v->entries);
171 memset(v->metric,0,sizeof(double)*v->entries);
173 if(v->entries<2)exit(1);
175 /* assign all the points, accumulate metric */
176 for(i=0;i<v->points;i++){
177 double firstmetric=v->metric_func(v,_now(v,0),_point(v,i))+v->bias[0];
178 double secondmetric=v->metric_func(v,_now(v,1),_point(v,i))+v->bias[1];
182 if(firstmetric>secondmetric){
183 double tempmetric=firstmetric;
184 firstmetric=secondmetric;
185 secondmetric=tempmetric;
190 for(j=2;j<v->entries;j++){
191 double thismetric=v->metric_func(v,_now(v,j),_point(v,i))+v->bias[j];
192 if(thismetric<secondmetric){
193 if(thismetric<firstmetric){
194 secondmetric=firstmetric;
195 secondentry=firstentry;
196 firstmetric=thismetric;
199 secondmetric=thismetric;
205 v->first[i]=firstentry;
206 v->second[i]=secondentry;
207 v->metric[firstentry]+=firstmetric-v->bias[firstentry];
208 v->assigned[firstentry]++;
211 double *no=_now(v,firstentry);
212 double *ne=_next(v,firstentry);
213 double *p=_point(v,i);
214 for(j=0;j<v->elements;j++)
216 fprintf(graph,"%g %g\n%g %g\n\n",p[0],p[1],no[0],no[1]);
221 for(i=0;i<v->entries;i++){
222 double *next=_next(v,i);
223 double *now=_now(v,i);
225 for(j=0;j<v->elements;j++)
226 next[j]/=v->assigned[i];
228 for(j=0;j<v->elements;j++)
233 /* average global metric */
234 for(i=0;i<v->entries;i++){
235 avmetric+=v->metric[i];
237 avmetric/=v->entries;
238 fprintf(stderr,"%ld... ",iteration);
240 /* positive/negative 'pressure' */
242 /* Another round of n*m. Above we had to index by point. Now, we
243 have to index by entry */
244 long *index=alloca(v->points*sizeof(long));
245 double *vals =alloca(v->points*sizeof(double));
246 double *met =alloca(v->points*sizeof(double));
247 for(i=0;i<v->entries;i++){
249 /* sort all n-thousand points by effective metric */
251 for(j=0;j<v->points;j++){
255 /* use the secondary entry as the threshhold */
256 threshentry=v->second[j];
258 /* use the primary entry as the threshhold */
259 threshentry=v->first[j];
262 /* bias value right at the threshhold to grab this point */
263 met[j]=v->metric_func(v,_now(v,i),_point(v,j));
265 v->metric_func(v,_now(v,threshentry),_point(v,j))+
266 v->bias[threshentry]-
267 v->metric_func(v,_now(v,i),_point(v,j));
271 qsort(index,v->points,sizeof(long),sort_t_compare);
274 /* find the desired number of points and level of bias. We
275 nominally want all entries to represent the same number of
276 points, but we may also have a metric range restriction */
277 int desired=(v->points+v->entries-1)/v->entries;
279 if(v->errspread>=1.){
281 for(j=0;j<desired;j++){
283 if(acc*v->errspread>avmetric)break;
287 v->bias[i]=vals[index[j-1]];
291 memset(v->bias,0,sizeof(double)*v->entries);
294 /* dump state, report metric */
295 for(i=0;i<v->entries;i++){
296 for(j=0;j<v->assigned[i];j++)
297 fprintf(err,"%g\n",v->metric[i]/v->assigned[i]);
298 fprintf(bias,"%g\n",v->bias[i]);
299 fprintf(as,"%ld\n",v->assigned[i]);
302 fprintf(stderr,"average metric: %g\n",avmetric/v->elements/v->points*v->entries);
308 memcpy(v->entry_now,v->entry_next,sizeof(double)*v->entries*v->elements);
312 int main(int argc,char *argv[]){
313 FILE *in=fopen(argv[1],"r");
318 vqgen_init(&v,4,24,_dist_sq,1.);
320 while(fgets(buffer,160,in)){
322 if(sscanf(buffer,"%lf %lf %lf %lf",
324 vqgen_addpoint(&v,a);