123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- /********************************************************************
- * *
- * THIS FILE IS PART OF THE Ogg Vorbis SOFTWARE CODEC SOURCE CODE. *
- * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
- * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE. *
- * PLEASE READ THESE TERMS DISTRIBUTING. *
- * *
- * THE OggSQUISH SOURCE CODE IS (C) COPYRIGHT 1994-2000 *
- * by Monty <monty@xiph.org> and The XIPHOPHORUS Company *
- * http://www.xiph.org/ *
- * *
- ********************************************************************
- function: utility main for building thresh/pigeonhole encode hints
- last mod: $Id: latticehint.c,v 1.2.2.1 2000/08/31 09:00:02 xiphmont Exp $
- ********************************************************************/
- #include <stdlib.h>
- #include <stdio.h>
- #include <math.h>
- #include <string.h>
- #include <errno.h>
- #include "vorbis/codebook.h"
- #include "../lib/sharedbook.h"
- #include "../lib/scales.h"
- #include "bookutil.h"
- #include "vqgen.h"
- #include "vqsplit.h"
- /* The purpose of this util is to build encode hints for lattice
- codebooks so that brute forcing each codebook entry isn't needed.
- Threshhold hints are for books in which each scalar in the vector
- is independant (eg, residue) and pigeonhole lookups provide a
- minimum error fit for words where the scalars are interdependant
- (each affecting the fit of the next in sequence) as in an LSP
- sequential book (or can be used along with a sparse threshhold map,
- like a splitting tree that need not be trained)
- If the input book is non-sequential, a threshhold hint is built.
- If the input book is sequential, a pigeonholing hist is built.
- If the book is sparse, a pigeonholing hint is built, possibly in addition
- to the threshhold hint
- command line:
- latticehint book.vqh
- latticehint produces book.vqh on stdout */
- static int longsort(const void *a, const void *b){
- return(**((long **)a)-**((long **)b));
- }
- static int addtosearch(int entry,long **tempstack,long *tempcount,int add){
- long *ptr=tempstack[entry];
- long i=tempcount[entry];
- if(ptr){
- while(i--)
- if(*ptr++==add)return(0);
- tempstack[entry]=realloc(tempstack[entry],
- (tempcount[entry]+1)*sizeof(long));
- }else{
- tempstack[entry]=malloc(sizeof(long));
- }
- tempstack[entry][tempcount[entry]++]=add;
- return(1);
- }
- static void setvals(int dim,encode_aux_pigeonhole *p,
- long *temptrack,float *tempmin,float *tempmax,
- int seqp){
- int i;
- float last=0.;
- for(i=0;i<dim;i++){
- tempmin[i]=(temptrack[i])*p->del+p->min+last;
- tempmax[i]=tempmin[i]+p->del;
- if(seqp)last=tempmin[i];
- }
- }
- /* note that things are currently set up such that input fits that
- quantize outside the pigeonmap are dropped and brute-forced. So we
- can ignore the <0 and >=n boundary cases in min/max error */
- static float minerror(int dim,float *a,encode_aux_pigeonhole *p,
- long *temptrack,float *tempmin,float *tempmax){
- int i;
- float err=0.;
- for(i=0;i<dim;i++){
- float eval=0.;
- if(a[i]<tempmin[i]){
- eval=tempmin[i]-a[i];
- }else if(a[i]>tempmax[i]){
- eval=a[i]-tempmax[i];
- }
- err+=eval*eval;
- }
- return(err);
- }
- static float maxerror(int dim,float *a,encode_aux_pigeonhole *p,
- long *temptrack,float *tempmin,float *tempmax){
- int i;
- float err=0.,eval;
- for(i=0;i<dim;i++){
- if(a[i]<tempmin[i]){
- eval=tempmax[i]-a[i];
- }else if(a[i]>tempmax[i]){
- eval=a[i]-tempmin[i];
- }else{
- float t1=a[i]-tempmin[i];
- eval=tempmax[i]-a[i];
- if(t1>eval)eval=t1;
- }
- err+=eval*eval;
- }
- return(err);
- }
- int main(int argc,char *argv[]){
- codebook *b;
- static_codebook *c;
- int entries=-1,dim=-1;
- float min,del;
- char *name;
- long i,j;
- long dB=0;
- if(argv[1]==NULL){
- fprintf(stderr,"Need a lattice book on the command line.\n");
- exit(1);
- }
- if(argv[2])dB=1;
- {
- char *ptr;
- char *filename=strdup(argv[1]);
- b=codebook_load(filename);
- c=(static_codebook *)(b->c);
-
- ptr=strrchr(filename,'.');
- if(ptr){
- *ptr='\0';
- name=strdup(filename);
- }else{
- name=strdup(filename);
- }
- }
- if(c->maptype!=1){
- fprintf(stderr,"Provided book is not a latticebook.\n");
- exit(1);
- }
- entries=b->entries;
- dim=b->dim;
- min=_float32_unpack(c->q_min);
- del=_float32_unpack(c->q_delta);
- /* Do we want to gen a threshold hint? */
- if(c->q_sequencep==0){
- /* yes. Discard any preexisting threshhold hint */
- long quantvals=_book_maptype1_quantvals(c);
- long **quantsort=alloca(quantvals*sizeof(long *));
- encode_aux_threshmatch *t=calloc(1,sizeof(encode_aux_threshmatch));
- c->thresh_tree=t;
- fprintf(stderr,"Adding threshold hint to %s...\n",name);
- /* simplest possible threshold hint only */
- t->quantthresh=calloc(quantvals-1,sizeof(float));
- t->quantmap=calloc(quantvals,sizeof(int));
- t->threshvals=quantvals;
- t->quantvals=quantvals;
- /* the quantvals may not be in order; sort em first */
- for(i=0;i<quantvals;i++)quantsort[i]=c->quantlist+i;
- qsort(quantsort,quantvals,sizeof(long *),longsort);
- /* ok, gen the map and thresholds */
- for(i=0;i<quantvals;i++)t->quantmap[i]=quantsort[i]-c->quantlist;
- for(i=0;i<quantvals-1;i++){
- float v1=*(quantsort[i])*del+min;
- float v2=*(quantsort[i+1])*del+min;
- if(dB){
- if(fabs(v1)<.01)v1=(v1+v2)*.5;
- if(fabs(v2)<.01)v2=(v1+v2)*.5;
- t->quantthresh[i]=fromdB((todB(v1)+todB(v2))*.5);
- if(v1<0 || v2<0)t->quantthresh[i]*=-1;
- }else{
- t->quantthresh[i]=(v1+v2)*.5;
- }
- }
- }
- /* Do we want to gen a pigeonhole hint? */
- for(i=0;i<entries;i++)if(c->lengthlist[i]==0)break;
- if(c->q_sequencep || i<entries){
- long **tempstack;
- long *tempcount;
- long *temptrack;
- float *tempmin;
- float *tempmax;
- long totalstack=0;
- long pigeons;
- long subpigeons;
- long quantvals=_book_maptype1_quantvals(c);
- int changep=1,factor;
- encode_aux_pigeonhole *p=calloc(1,sizeof(encode_aux_pigeonhole));
- c->pigeon_tree=p;
- fprintf(stderr,"Adding pigeonhole hint to %s...\n",name);
-
- /* the idea is that we quantize uniformly, even in a nonuniform
- lattice, so that quantization of one scalar has a predictable
- result on the next sequential scalar in a greedy matching
- algorithm. We generate a lookup based on the quantization of
- the vector (pigeonmap groups quantized entries together) and
- list the entries that could possible be the best fit for any
- given member of that pigeonhole. The encode process then has a
- much smaller list to brute force */
- /* find our pigeonhole-specific quantization values, fill in the
- quant value->pigeonhole map */
- factor=3;
- p->del=del;
- p->min=min;
- p->quantvals=quantvals;
- {
- int max=0;
- for(i=0;i<quantvals;i++)if(max<c->quantlist[i])max=c->quantlist[i];
- p->mapentries=max;
- }
- p->pigeonmap=malloc(p->mapentries*sizeof(long));
- p->quantvals=(quantvals+factor-1)/factor;
- /* pigeonhole roughly on the boundaries of the quantvals; the
- exact pigeonhole grouping is an optimization issue, not a
- correctness issue */
- for(i=0;i<p->mapentries;i++){
- float thisval=del*i+min; /* middle of the quant zone */
- int quant=0;
- float err=fabs(c->quantlist[0]*del+min-thisval);
- for(j=1;j<quantvals;j++){
- float thiserr=fabs(c->quantlist[j]*del+min-thisval);
- if(thiserr<err){
- quant=j/factor;
- err=thiserr;
- }
- }
- p->pigeonmap[i]=quant;
- }
-
- /* pigeonmap complete. Now do the grungy business of finding the
- entries that could possibly be the best fit for a value appearing
- in the pigeonhole. The trick that allows the below to work is the
- uniform quantization; even though the scalars may be 'sequential'
- (each a delta from the last), the uniform quantization means that
- the error variance is *not* dependant. Given a pigeonhole and an
- entry, we can find the minimum and maximum possible errors
- (relative to the entry) for any point that could appear in the
- pigeonhole */
-
- /* must iterate over both pigeonholes and entries */
- /* temporarily (in order to avoid thinking hard), we grow each
- pigeonhole seperately, the build a stack of 'em later */
- pigeons=1;
- subpigeons=1;
- for(i=0;i<dim;i++)subpigeons*=p->mapentries;
- for(i=0;i<dim;i++)pigeons*=p->quantvals;
- temptrack=calloc(dim,sizeof(long));
- tempmin=calloc(dim,sizeof(float));
- tempmax=calloc(dim,sizeof(float));
- tempstack=calloc(pigeons,sizeof(long *));
- tempcount=calloc(pigeons,sizeof(long));
- while(1){
- float errorpost=-1;
- char buffer[80];
- /* map our current pigeonhole to a 'big pigeonhole' so we know
- what list we're after */
- int entry=0;
- for(i=dim-1;i>=0;i--)entry=entry*p->quantvals+p->pigeonmap[temptrack[i]];
- setvals(dim,p,temptrack,tempmin,tempmax,c->q_sequencep);
- sprintf(buffer,"Building pigeonhole search list [%ld]...",totalstack);
- /* Search all entries to find the one with the minimum possible
- maximum error. Record that error */
- for(i=0;i<entries;i++){
- if(c->lengthlist[i]>0){
- float this=maxerror(dim,b->valuelist+i*dim,p,
- temptrack,tempmin,tempmax);
- if(errorpost==-1 || this<errorpost)errorpost=this;
- spinnit(buffer,subpigeons);
- }
- }
- /* Our search list will contain all entries with a minimum
- possible error <= our errorpost */
- for(i=0;i<entries;i++)
- if(c->lengthlist[i]>0){
- spinnit(buffer,subpigeons);
- if(minerror(dim,b->valuelist+i*dim,p,
- temptrack,tempmin,tempmax)<errorpost)
- totalstack+=addtosearch(entry,tempstack,tempcount,i);
- }
- for(i=0;i<dim;i++){
- temptrack[i]++;
- if(temptrack[i]<p->mapentries)break;
- temptrack[i]=0;
- }
- if(i==dim)break;
- subpigeons--;
- }
- fprintf(stderr,"\r "
- "\rTotal search list size (all entries): %ld\n",totalstack);
- /* pare the index of lists for improbable quantizations (where
- improbable is determined by c->lengthlist; we assume that
- pigeonholing is in sync with the codeword cells, which it is */
- /*for(i=0;i<entries;i++){
- float probability= 1./(1<<c->lengthlist[i]);
- if(c->lengthlist[i]==0 || probability*entries<cutoff){
- totalstack-=tempcount[i];
- tempcount[i]=0;
- }
- }*/
- /* pare the list of shortlists; merge contained and similar lists
- together */
- p->fitmap=malloc(pigeons*sizeof(long));
- for(i=0;i<pigeons;i++)p->fitmap[i]=-1;
- while(changep){
- char buffer[80];
- changep=0;
- for(i=0;i<pigeons;i++){
- if(p->fitmap[i]<0 && tempcount[i]){
- for(j=i+1;j<pigeons;j++){
- if(p->fitmap[j]<0 && tempcount[j]){
- /* is one list a superset, or are they sufficiently similar? */
- int amiss=0,bmiss=0,ii,jj;
- for(ii=0;ii<tempcount[i];ii++){
- for(jj=0;jj<tempcount[j];jj++)
- if(tempstack[i][ii]==tempstack[j][jj])break;
- if(jj==tempcount[j])amiss++;
- }
- for(jj=0;jj<tempcount[j];jj++){
- for(ii=0;ii<tempcount[i];ii++)
- if(tempstack[i][ii]==tempstack[j][jj])break;
- if(ii==tempcount[i])bmiss++;
- }
- if(amiss==0 ||
- bmiss==0 ||
- (amiss*2<tempcount[i] && bmiss*2<tempcount[j] &&
- tempcount[i]+bmiss<entries/30)){
- /*superset/similar Add all of one to the other. */
- for(jj=0;jj<tempcount[j];jj++)
- totalstack+=addtosearch(i,tempstack,tempcount,
- tempstack[j][jj]);
- totalstack-=tempcount[j];
- p->fitmap[j]=i;
- changep=1;
- }
- }
- }
- sprintf(buffer,"Consolidating [%ld total, %s]... ",totalstack,
- changep?"reit":"nochange");
- spinnit(buffer,pigeons-i);
- }
- }
- }
- /* repack the temp stack in final form */
- fprintf(stderr,"\r "
- "\rFinal total list size: %ld\n",totalstack);
-
- p->fittotal=totalstack;
- p->fitlist=malloc((totalstack+1)*sizeof(long));
- p->fitlength=malloc(pigeons*sizeof(long));
- {
- long usage=0;
- for(i=0;i<pigeons;i++){
- if(p->fitmap[i]==-1){
- if(tempcount[i])
- memcpy(p->fitlist+usage,tempstack[i],tempcount[i]*sizeof(long));
- p->fitmap[i]=usage;
- p->fitlength[i]=tempcount[i];
- usage+=tempcount[i];
- if(usage>totalstack){
- fprintf(stderr,"Internal error; usage>totalstack\n");
- exit(1);
- }
- }else{
- p->fitlength[i]=p->fitlength[p->fitmap[i]];
- p->fitmap[i]=p->fitmap[p->fitmap[i]];
- }
- }
- }
- }
- write_codebook(stdout,name,c);
- fprintf(stderr,"\r "
- "\nDone.\n");
- exit(0);
- }
|