1 /********************************************************************
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. *
8 * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 *
9 * by the XIPHOPHORUS Company http://www.xiph.org/ *
11 ********************************************************************
13 function: utility main for building thresh/pigeonhole encode hints
14 last mod: $Id: latticehint.c,v 1.12 2001/12/20 01:00:39 segher Exp $
16 ********************************************************************/
23 #include "../lib/scales.h"
28 /* The purpose of this util is to build encode hints for lattice
29 codebooks so that brute forcing each codebook entry isn't needed.
30 Threshhold hints are for books in which each scalar in the vector
31 is independant (eg, residue) and pigeonhole lookups provide a
32 minimum error fit for words where the scalars are interdependant
33 (each affecting the fit of the next in sequence) as in an LSP
34 sequential book (or can be used along with a sparse threshhold map,
35 like a splitting tree that need not be trained)
37 If the input book is non-sequential, a threshhold hint is built.
38 If the input book is sequential, a pigeonholing hist is built.
39 If the book is sparse, a pigeonholing hint is built, possibly in addition
40 to the threshhold hint
43 latticehint book.vqh [threshlist]
45 latticehint produces book.vqh on stdout */
47 static int longsort(const void *a, const void *b){
48 return(**((long **)a)-**((long **)b));
51 static int addtosearch(int entry,long **tempstack,long *tempcount,int add){
52 long *ptr=tempstack[entry];
53 long i=tempcount[entry];
57 if(*ptr++==add)return(0);
58 tempstack[entry]=_ogg_realloc(tempstack[entry],
59 (tempcount[entry]+1)*sizeof(long));
61 tempstack[entry]=_ogg_malloc(sizeof(long));
64 tempstack[entry][tempcount[entry]++]=add;
68 static void setvals(int dim,encode_aux_pigeonhole *p,
69 long *temptrack,float *tempmin,float *tempmax,
74 tempmin[i]=(temptrack[i])*p->del+p->min+last;
75 tempmax[i]=tempmin[i]+p->del;
76 if(seqp)last=tempmin[i];
80 /* note that things are currently set up such that input fits that
81 quantize outside the pigeonmap are dropped and brute-forced. So we
82 can ignore the <0 and >=n boundary cases in min/max error */
84 static float minerror(int dim,float *a,encode_aux_pigeonhole *p,
85 long *temptrack,float *tempmin,float *tempmax){
92 }else if(a[i]>tempmax[i]){
100 static float maxerror(int dim,float *a,encode_aux_pigeonhole *p,
101 long *temptrack,float *tempmin,float *tempmax){
106 eval=tempmax[i]-a[i];
107 }else if(a[i]>tempmax[i]){
108 eval=a[i]-tempmin[i];
110 float t1=a[i]-tempmin[i];
111 eval=tempmax[i]-a[i];
119 int main(int argc,char *argv[]){
122 int entries=-1,dim=-1;
130 fprintf(stderr,"Need a lattice book on the command line.\n");
136 char *filename=strdup(argv[1]);
138 b=codebook_load(filename);
139 c=(static_codebook *)(b->c);
141 ptr=strrchr(filename,'.');
144 name=strdup(filename);
146 name=strdup(filename);
151 fprintf(stderr,"Provided book is not a latticebook.\n");
157 min=_float32_unpack(c->q_min);
158 del=_float32_unpack(c->q_delta);
160 /* Do we want to gen a threshold hint? */
161 if(c->q_sequencep==0){
162 /* yes. Discard any preexisting threshhold hint */
163 long quantvals=_book_maptype1_quantvals(c);
164 long **quantsort=alloca(quantvals*sizeof(long *));
165 encode_aux_threshmatch *t=_ogg_calloc(1,sizeof(encode_aux_threshmatch));
168 fprintf(stderr,"Adding threshold hint to %s...\n",name);
170 /* partial/complete suggestions */
172 char *ptr=strdup(argv[2]);
173 suggestions=alloca(sizeof(float)*quantvals);
175 for(suggcount=0;ptr && suggcount<quantvals;suggcount++){
176 char *ptr2=strchr(ptr,',');
177 if(ptr2)*ptr2++='\0';
178 suggestions[suggcount]=atof(ptr);
183 /* simplest possible threshold hint only */
184 t->quantthresh=_ogg_calloc(quantvals-1,sizeof(float));
185 t->quantmap=_ogg_calloc(quantvals,sizeof(int));
186 t->threshvals=quantvals;
187 t->quantvals=quantvals;
189 /* the quantvals may not be in order; sort em first */
190 for(i=0;i<quantvals;i++)quantsort[i]=c->quantlist+i;
191 qsort(quantsort,quantvals,sizeof(long *),longsort);
193 /* ok, gen the map and thresholds */
194 for(i=0;i<quantvals;i++)t->quantmap[i]=quantsort[i]-c->quantlist;
195 for(i=0;i<quantvals-1;i++){
196 float v1=*(quantsort[i])*del+min;
197 float v2=*(quantsort[i+1])*del+min;
199 for(j=0;j<suggcount;j++)
200 if(v1<suggestions[j] && suggestions[j]<v2){
201 t->quantthresh[i]=suggestions[j];
206 t->quantthresh[i]=(v1+v2)*.5;
211 /* Do we want to gen a pigeonhole hint? */
213 for(i=0;i<entries;i++)if(c->lengthlist[i]==0)break;
214 if(c->q_sequencep || i<entries){
223 long quantvals=_book_maptype1_quantvals(c);
224 int changep=1,factor;
226 encode_aux_pigeonhole *p=_ogg_calloc(1,sizeof(encode_aux_pigeonhole));
229 fprintf(stderr,"Adding pigeonhole hint to %s...\n",name);
231 /* the idea is that we quantize uniformly, even in a nonuniform
232 lattice, so that quantization of one scalar has a predictable
233 result on the next sequential scalar in a greedy matching
234 algorithm. We generate a lookup based on the quantization of
235 the vector (pigeonmap groups quantized entries together) and
236 list the entries that could possible be the best fit for any
237 given member of that pigeonhole. The encode process then has a
238 much smaller list to brute force */
240 /* find our pigeonhole-specific quantization values, fill in the
241 quant value->pigeonhole map */
245 p->quantvals=quantvals;
248 for(i=0;i<quantvals;i++)if(max<c->quantlist[i])max=c->quantlist[i];
251 p->pigeonmap=_ogg_malloc(p->mapentries*sizeof(long));
252 p->quantvals=(quantvals+factor-1)/factor;
254 /* pigeonhole roughly on the boundaries of the quantvals; the
255 exact pigeonhole grouping is an optimization issue, not a
257 for(i=0;i<p->mapentries;i++){
258 float thisval=del*i+min; /* middle of the quant zone */
260 float err=fabs(c->quantlist[0]*del+min-thisval);
261 for(j=1;j<quantvals;j++){
262 float thiserr=fabs(c->quantlist[j]*del+min-thisval);
268 p->pigeonmap[i]=quant;
271 /* pigeonmap complete. Now do the grungy business of finding the
272 entries that could possibly be the best fit for a value appearing
273 in the pigeonhole. The trick that allows the below to work is the
274 uniform quantization; even though the scalars may be 'sequential'
275 (each a delta from the last), the uniform quantization means that
276 the error variance is *not* dependant. Given a pigeonhole and an
277 entry, we can find the minimum and maximum possible errors
278 (relative to the entry) for any point that could appear in the
281 /* must iterate over both pigeonholes and entries */
282 /* temporarily (in order to avoid thinking hard), we grow each
283 pigeonhole seperately, the build a stack of 'em later */
286 for(i=0;i<dim;i++)subpigeons*=p->mapentries;
287 for(i=0;i<dim;i++)pigeons*=p->quantvals;
288 temptrack=_ogg_calloc(dim,sizeof(long));
289 tempmin=_ogg_calloc(dim,sizeof(float));
290 tempmax=_ogg_calloc(dim,sizeof(float));
291 tempstack=_ogg_calloc(pigeons,sizeof(long *));
292 tempcount=_ogg_calloc(pigeons,sizeof(long));
298 /* map our current pigeonhole to a 'big pigeonhole' so we know
299 what list we're after */
301 for(i=dim-1;i>=0;i--)entry=entry*p->quantvals+p->pigeonmap[temptrack[i]];
302 setvals(dim,p,temptrack,tempmin,tempmax,c->q_sequencep);
303 sprintf(buffer,"Building pigeonhole search list [%ld]...",totalstack);
306 /* Search all entries to find the one with the minimum possible
307 maximum error. Record that error */
308 for(i=0;i<entries;i++){
309 if(c->lengthlist[i]>0){
310 float this=maxerror(dim,b->valuelist+i*dim,p,
311 temptrack,tempmin,tempmax);
312 if(errorpost==-1 || this<errorpost)errorpost=this;
313 spinnit(buffer,subpigeons);
317 /* Our search list will contain all entries with a minimum
318 possible error <= our errorpost */
319 for(i=0;i<entries;i++)
320 if(c->lengthlist[i]>0){
321 spinnit(buffer,subpigeons);
322 if(minerror(dim,b->valuelist+i*dim,p,
323 temptrack,tempmin,tempmax)<errorpost)
324 totalstack+=addtosearch(entry,tempstack,tempcount,i);
329 if(temptrack[i]<p->mapentries)break;
337 "\rTotal search list size (all entries): %ld\n",totalstack);
339 /* pare the index of lists for improbable quantizations (where
340 improbable is determined by c->lengthlist; we assume that
341 pigeonholing is in sync with the codeword cells, which it is */
342 /*for(i=0;i<entries;i++){
343 float probability= 1.f/(1<<c->lengthlist[i]);
344 if(c->lengthlist[i]==0 || probability*entries<cutoff){
345 totalstack-=tempcount[i];
350 /* pare the list of shortlists; merge contained and similar lists
352 p->fitmap=_ogg_malloc(pigeons*sizeof(long));
353 for(i=0;i<pigeons;i++)p->fitmap[i]=-1;
358 for(i=0;i<pigeons;i++){
359 if(p->fitmap[i]<0 && tempcount[i]){
360 for(j=i+1;j<pigeons;j++){
361 if(p->fitmap[j]<0 && tempcount[j]){
362 /* is one list a superset, or are they sufficiently similar? */
363 int amiss=0,bmiss=0,ii,jj;
364 for(ii=0;ii<tempcount[i];ii++){
365 for(jj=0;jj<tempcount[j];jj++)
366 if(tempstack[i][ii]==tempstack[j][jj])break;
367 if(jj==tempcount[j])amiss++;
369 for(jj=0;jj<tempcount[j];jj++){
370 for(ii=0;ii<tempcount[i];ii++)
371 if(tempstack[i][ii]==tempstack[j][jj])break;
372 if(ii==tempcount[i])bmiss++;
376 (amiss*2<tempcount[i] && bmiss*2<tempcount[j] &&
377 tempcount[i]+bmiss<entries/30)){
379 /*superset/similar Add all of one to the other. */
380 for(jj=0;jj<tempcount[j];jj++)
381 totalstack+=addtosearch(i,tempstack,tempcount,
383 totalstack-=tempcount[j];
389 sprintf(buffer,"Consolidating [%ld total, %s]... ",totalstack,
390 changep?"reit":"nochange");
391 spinnit(buffer,pigeons-i);
396 /* repack the temp stack in final form */
398 "\rFinal total list size: %ld\n",totalstack);
401 p->fittotal=totalstack;
402 p->fitlist=_ogg_malloc((totalstack+1)*sizeof(long));
403 p->fitlength=_ogg_malloc(pigeons*sizeof(long));
406 for(i=0;i<pigeons;i++){
407 if(p->fitmap[i]==-1){
409 memcpy(p->fitlist+usage,tempstack[i],tempcount[i]*sizeof(long));
411 p->fitlength[i]=tempcount[i];
413 if(usage>totalstack){
414 fprintf(stderr,"Internal error; usage>totalstack\n");
418 p->fitlength[i]=p->fitlength[p->fitmap[i]];
419 p->fitmap[i]=p->fitmap[p->fitmap[i]];
426 write_codebook(stdout,name,c);