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: utility main for building thresh/pigeonhole encode hints
15 last mod: $Id: latticehint.c,v 1.1 2000/07/19 18:10:02 xiphmont Exp $
17 ********************************************************************/
24 #include "vorbis/codebook.h"
25 #include "../lib/sharedbook.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
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]=realloc(tempstack[entry],
59 (tempcount[entry]+1)*sizeof(long));
61 tempstack[entry]=malloc(sizeof(long));
64 tempstack[entry][tempcount[entry]++]=add;
68 static void setvals(int dim,encode_aux_pigeonhole *p,
69 long *temptrack,double *tempmin,double *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 double minerror(int dim,double *a,encode_aux_pigeonhole *p,
85 long *temptrack,double *tempmin,double *tempmax){
92 }else if(a[i]>tempmax[i]){
100 static double maxerror(int dim,double *a,encode_aux_pigeonhole *p,
101 long *temptrack,double *tempmin,double *tempmax){
106 eval=tempmax[i]-a[i];
107 }else if(a[i]>tempmax[i]){
108 eval=a[i]-tempmin[i];
110 double t1=a[i]-tempmin[i];
111 eval=tempmax[i]-a[i];
119 int main(int argc,char *argv[]){
122 int entries=-1,dim=-1;
123 double min,del,cutoff=1.;
128 fprintf(stderr,"Need a lattice book on the command line.\n");
132 if(argv[2])cutoff=atof(argv[2]);
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=calloc(1,sizeof(encode_aux_threshmatch));
168 fprintf(stderr,"Adding threshold hint to %s...\n",name);
170 /* simplest possible threshold hint only */
171 t->quantthresh=calloc(quantvals-1,sizeof(double));
172 t->quantmap=calloc(quantvals,sizeof(int));
173 t->threshvals=quantvals;
174 t->quantvals=quantvals;
176 /* the quantvals may not be in order; sort em first */
177 for(i=0;i<quantvals;i++)quantsort[i]=c->quantlist+i;
178 qsort(quantsort,quantvals,sizeof(long *),longsort);
180 /* ok, gen the map and thresholds */
181 for(i=0;i<quantvals;i++)t->quantmap[i]=quantsort[i]-c->quantlist;
182 for(i=0;i<quantvals-1;i++)
183 t->quantthresh[i]=(*(quantsort[i])+*(quantsort[i+1]))*.5*del+min;
186 /* Do we want to gen a pigeonhole hint? */
187 for(i=0;i<entries;i++)if(c->lengthlist[i]==0)break;
188 if(c->q_sequencep || i<entries){
196 long quantvals=_book_maptype1_quantvals(c);
199 encode_aux_pigeonhole *p=calloc(1,sizeof(encode_aux_pigeonhole));
202 fprintf(stderr,"Adding pigeonhole hint to %s...\n",name);
204 /* the idea is that we quantize uniformly, even in a nonuniform
205 lattice, so that quantization of one scalar has a predictable
206 result on the next sequential scalar in a greedy matching
207 algorithm. We generate a lookup based on the quantization of
208 the vector (pigeonmap groups quantized entries together) and
209 list the entries that could possible be the best fit for any
210 given member of that pigeonhole. The encode process then has a
211 much smaller list to brute force */
213 /* find our pigeonhole-specific quantization values, fill in the
214 quant value->pigeonhole map */
217 p->quantvals=quantvals;
220 for(i=0;i<quantvals;i++)if(max<c->quantlist[i])max=c->quantlist[i];
223 p->pigeonmap=malloc(p->mapentries*sizeof(long));
225 /* pigeonhole roughly on the boundaries of the quantvals; the
226 exact pigeonhole grouping is an optimization issue, not a
228 for(i=0;i<p->mapentries;i++){
229 double thisval=del*(i+.5)+min; /* middle of the quant zone */
231 double err=fabs(c->quantlist[0]*del+min-thisval);
232 for(j=1;j<quantvals;j++){
233 double thiserr=fabs(c->quantlist[j]*del+min-thisval);
239 p->pigeonmap[i]=quant;
242 /* pigeonmap complete. Now do the grungy business of finding the
243 entries that could possibly be the best fit for a value appearing
244 in the pigeonhole. The trick that allows the below to work is the
245 uniform quantization; even though the scalars may be 'sequential'
246 (each a delta from the last), the uniform quantization means that
247 the error variance is *not* dependant. Given a pigeonhole and an
248 entry, we can find the minimum and maximum possible errors
249 (relative to the entry) for any point that could appear in the
252 /* must iterate over both pigeonholes and entries */
253 /* temporarily (in order to avoid thinking hard), we grow each
254 pigeonhole seperately, the build a stack of 'em later */
256 for(i=0;i<dim;i++)subpigeons*=p->mapentries;
257 temptrack=calloc(dim,sizeof(long));
258 tempmin=calloc(dim,sizeof(double));
259 tempmax=calloc(dim,sizeof(double));
260 tempstack=calloc(entries,sizeof(long *));
261 tempcount=calloc(entries,sizeof(long));
267 /* map our current pigeonhole to a 'big pigeonhole' so we know
268 what list we're after */
270 for(i=dim-1;i>=0;i--)entry=entry*quantvals+p->pigeonmap[temptrack[i]];
271 setvals(dim,p,temptrack,tempmin,tempmax,c->q_sequencep);
272 sprintf(buffer,"Building pigeonhole search list [%ld]...",totalstack);
275 /* Search all entries to find the one with the minimum possible
276 maximum error. Record that error */
277 for(i=0;i<entries;i++){
278 if(c->lengthlist[i]>0){
279 double this=maxerror(dim,b->valuelist+i*dim,p,
280 temptrack,tempmin,tempmax);
281 if(errorpost==-1 || this<errorpost)errorpost=this;
282 spinnit(buffer,subpigeons);
286 /* Our search list will contain all entries with a minimum
287 possible error <= our errorpost */
288 for(i=0;i<entries;i++)
289 if(c->lengthlist[i]>0){
290 spinnit(buffer,subpigeons);
291 if(minerror(dim,b->valuelist+i*dim,p,
292 temptrack,tempmin,tempmax)<errorpost)
293 totalstack+=addtosearch(entry,tempstack,tempcount,i);
298 if(temptrack[i]<p->mapentries)break;
306 "\rTotal search list size (all entries): %ld\n",totalstack);
308 /* pare the index of lists for improbable quantizations (where
309 improbable is determined by c->lengthlist; we assume that
310 pigeonholing is in sync with the codeword cells, which it is */
311 for(i=0;i<entries;i++){
312 double probability= 1./(1<<c->lengthlist[i]);
313 if(c->lengthlist[i]==0 || probability*entries<cutoff){
314 totalstack-=tempcount[i];
319 /* pare the list of shortlists; merge contained and similar lists
321 p->fitmap=malloc(entries*sizeof(long));
322 for(i=0;i<entries;i++)p->fitmap[i]=-1;
327 for(i=0;i<entries;i++){
328 if(p->fitmap[i]<0 && tempcount[i]){
329 for(j=i+1;j<entries;j++){
330 if(p->fitmap[j]<0 && tempcount[j]){
331 /* is one list a superset, or are they sufficiently similar? */
332 int amiss=0,bmiss=0,ii,jj;
333 for(ii=0;ii<tempcount[i];ii++){
334 for(jj=0;jj<tempcount[j];jj++)
335 if(tempstack[i][ii]==tempstack[j][jj])break;
336 if(jj==tempcount[j])amiss++;
338 for(jj=0;jj<tempcount[j];jj++){
339 for(ii=0;ii<tempcount[i];ii++)
340 if(tempstack[i][ii]==tempstack[j][jj])break;
341 if(ii==tempcount[i])bmiss++;
345 (amiss*4<tempcount[i] && bmiss*4<tempcount[j] &&
346 tempcount[i]+bmiss<entries/30)){
348 /*superset/similar Add all of one to the other. */
349 for(jj=0;jj<tempcount[j];jj++)
350 totalstack+=addtosearch(i,tempstack,tempcount,
352 totalstack-=tempcount[j];
358 sprintf(buffer,"Consolidating [%ld total, %s]... ",totalstack,
359 changep?"reit":"nochange");
360 spinnit(buffer,entries-i);
365 /* repack the temp stack in final form */
367 p->fittotal=totalstack;
368 p->fitlist=malloc((totalstack+1)*sizeof(long));
369 p->fitlength=malloc(entries*sizeof(long));
372 for(i=0;i<entries;i++){
373 if(p->fitmap[i]==-1){
375 memcpy(p->fitlist+usage,tempstack[i],tempcount[i]*sizeof(long));
377 p->fitlength[i]=tempcount[i];
379 if(usage>totalstack){
380 fprintf(stderr,"Internal error; usage>totalstack\n");
384 p->fitlength[i]=p->fitlength[p->fitmap[i]];
385 p->fitmap[i]=p->fitmap[p->fitmap[i]];
391 write_codebook(stdout,name,c);