/******************************************************************** * * * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2010 * * by the Xiph.Org Foundation http://www.xiph.org/ * * * ******************************************************************** function: utility functions for loading .vqh and .vqd files last mod: $Id: bookutil.c 16959 2010-03-10 16:03:11Z xiphmont $ ********************************************************************/ #include <stdlib.h> #include <stdio.h> #include <math.h> #include <string.h> #include <errno.h> #include "bookutil.h" int _best(codebook *book, float *a, int step){ int dim=book->dim; int i,j,o; int minval=book->minval; int del=book->delta; int qv=book->quantvals; int ze=(qv>>1); int index=0; /* assumes integer/centered encoder codebook maptype 1 no more than dim 8 */ if(del!=1){ for(i=0,o=step*(dim-1);i<dim;i++,o-=step){ int v = ((int)rint(a[o])-minval+(del>>1))/del; int m = (v<ze ? ((ze-v)<<1)-1 : ((v-ze)<<1)); index = index*qv+ (m<0?0:(m>=qv?qv-1:m)); } }else{ for(i=0,o=step*(dim-1);i<dim;i++,o-=step){ int v = (int)rint(a[o])-minval; int m = (v<ze ? ((ze-v)<<1)-1 : ((v-ze)<<1)); index = index*qv+ (m<0?0:(m>=qv?qv-1:m)); } } if(book->c->lengthlist[index]<=0){ const static_codebook *c=book->c; int best=-1; /* assumes integer/centered encoder codebook maptype 1 no more than dim 8 */ int e[8]={0,0,0,0,0,0,0,0}; int maxval = book->minval + book->delta*(book->quantvals-1); for(i=0;i<book->entries;i++){ if(c->lengthlist[i]>0){ float this=0; for(j=0;j<dim;j++){ float val=(e[j]-a[j*step]); this+=val*val; } if(best==-1 || this<best){ best=this; index=i; } } /* assumes the value patterning created by the tools in vq/ */ j=0; while(e[j]>=maxval) e[j++]=0; if(e[j]>=0) e[j]+=book->delta; e[j]= -e[j]; } } return index; } /* A few little utils for reading files */ /* read a line. Use global, persistent buffering */ static char *linebuffer=NULL; static int lbufsize=0; char *get_line(FILE *in){ long sofar=0; if(feof(in))return NULL; while(1){ int gotline=0; while(!gotline){ if(sofar+1>=lbufsize){ if(!lbufsize){ lbufsize=1024; linebuffer=_ogg_malloc(lbufsize); }else{ lbufsize*=2; linebuffer=_ogg_realloc(linebuffer,lbufsize); } } { long c=fgetc(in); switch(c){ case EOF: if(sofar==0)return(NULL); /* fallthrough correct */ case '\n': linebuffer[sofar]='\0'; gotline=1; break; default: linebuffer[sofar++]=c; linebuffer[sofar]='\0'; break; } } } if(linebuffer[0]=='#'){ sofar=0; }else{ return(linebuffer); } } } /* read the next numerical value from the given file */ static char *value_line_buff=NULL; int get_line_value(FILE *in,float *value){ char *next; if(!value_line_buff)return(-1); *value=strtod(value_line_buff, &next); if(next==value_line_buff){ value_line_buff=NULL; return(-1); }else{ value_line_buff=next; while(*value_line_buff>44)value_line_buff++; if(*value_line_buff==44)value_line_buff++; return(0); } } int get_next_value(FILE *in,float *value){ while(1){ if(get_line_value(in,value)){ value_line_buff=get_line(in); if(!value_line_buff)return(-1); }else{ return(0); } } } int get_next_ivalue(FILE *in,long *ivalue){ float value; int ret=get_next_value(in,&value); *ivalue=value; return(ret); } static float sequence_base=0.f; static int v_sofar=0; void reset_next_value(void){ value_line_buff=NULL; sequence_base=0.f; v_sofar=0; } char *setup_line(FILE *in){ reset_next_value(); value_line_buff=get_line(in); return(value_line_buff); } int get_vector(codebook *b,FILE *in,int start, int n,float *a){ int i; const static_codebook *c=b->c; while(1){ if(v_sofar==n || get_line_value(in,a)){ reset_next_value(); if(get_next_value(in,a)) break; for(i=0;i<start;i++){ sequence_base=*a; get_line_value(in,a); } } for(i=1;i<c->dim;i++) if(get_line_value(in,a+i)) break; if(i==c->dim){ float temp=a[c->dim-1]; for(i=0;i<c->dim;i++)a[i]-=sequence_base; if(c->q_sequencep)sequence_base=temp; v_sofar++; return(0); } sequence_base=0.f; } return(-1); } /* read lines fromt he beginning until we find one containing the specified string */ char *find_seek_to(FILE *in,char *s){ rewind(in); while(1){ char *line=get_line(in); if(line){ if(strstr(line,s)) return(line); }else return(NULL); } } /* this reads the format as written by vqbuild/latticebuild; innocent (legal) tweaking of the file that would not affect its valid header-ness will break this routine */ codebook *codebook_load(char *filename){ codebook *b=_ogg_calloc(1,sizeof(codebook)); static_codebook *c=(static_codebook *)(b->c=_ogg_calloc(1,sizeof(static_codebook))); int quant_to_read=0; FILE *in=fopen(filename,"r"); char *line; long i; if(in==NULL){ fprintf(stderr,"Couldn't open codebook %s\n",filename); exit(1); } /* find the codebook struct */ find_seek_to(in,"static const static_codebook "); /* get the major important values */ line=get_line(in); if(sscanf(line,"%ld, %ld,", &(c->dim),&(c->entries))!=2){ fprintf(stderr,"1: syntax in %s in line:\t %s",filename,line); exit(1); } line=get_line(in); line=get_line(in); if(sscanf(line,"%d, %ld, %ld, %d, %d,", &(c->maptype),&(c->q_min),&(c->q_delta),&(c->q_quant), &(c->q_sequencep))!=5){ fprintf(stderr,"1: syntax in %s in line:\t %s",filename,line); exit(1); } switch(c->maptype){ case 0: quant_to_read=0; break; case 1: quant_to_read=_book_maptype1_quantvals(c); break; case 2: quant_to_read=c->entries*c->dim; break; } /* load the quantized entries */ find_seek_to(in,"static const long _vq_quantlist_"); reset_next_value(); c->quantlist=_ogg_malloc(sizeof(long)*quant_to_read); for(i=0;i<quant_to_read;i++) if(get_next_ivalue(in,c->quantlist+i)){ fprintf(stderr,"out of data while reading codebook %s\n",filename); exit(1); } /* load the lengthlist */ find_seek_to(in,"_lengthlist"); reset_next_value(); c->lengthlist=_ogg_malloc(sizeof(long)*c->entries); for(i=0;i<c->entries;i++) if(get_next_ivalue(in,c->lengthlist+i)){ fprintf(stderr,"out of data while reading codebook %s\n",filename); exit(1); } /* got it all */ fclose(in); vorbis_book_init_encode(b,c); b->valuelist=_book_unquantize(c,c->entries,NULL); return(b); } void spinnit(char *s,int n){ static int p=0; static long lasttime=0; long test; struct timeval thistime; gettimeofday(&thistime,NULL); test=thistime.tv_sec*10+thistime.tv_usec/100000; if(lasttime!=test){ lasttime=test; fprintf(stderr,"%s%d ",s,n); p++;if(p>3)p=0; switch(p){ case 0: fprintf(stderr,"| \r"); break; case 1: fprintf(stderr,"/ \r"); break; case 2: fprintf(stderr,"- \r"); break; case 3: fprintf(stderr,"\\ \r"); break; } fflush(stderr); } } void build_tree_from_lengths(int vals, long *hist, long *lengths){ int i,j; long *membership=_ogg_malloc(vals*sizeof(long)); long *histsave=alloca(vals*sizeof(long)); memcpy(histsave,hist,vals*sizeof(long)); for(i=0;i<vals;i++)membership[i]=i; /* find codeword lengths */ /* much more elegant means exist. Brute force n^2, minimum thought */ for(i=vals;i>1;i--){ int first=-1,second=-1; long least=-1; spinnit("building... ",i); /* find the two nodes to join */ for(j=0;j<vals;j++) if(least==-1 || hist[j]<=least){ least=hist[j]; first=membership[j]; } least=-1; for(j=0;j<vals;j++) if((least==-1 || hist[j]<=least) && membership[j]!=first){ least=hist[j]; second=membership[j]; } if(first==-1 || second==-1){ fprintf(stderr,"huffman fault; no free branch\n"); exit(1); } /* join them */ least=hist[first]+hist[second]; for(j=0;j<vals;j++) if(membership[j]==first || membership[j]==second){ membership[j]=first; hist[j]=least; lengths[j]++; } } for(i=0;i<vals-1;i++) if(membership[i]!=membership[i+1]){ fprintf(stderr,"huffman fault; failed to build single tree\n"); exit(1); } /* for sanity check purposes: how many bits would it have taken to encode the training set? */ { long bitsum=0; long samples=0; for(i=0;i<vals;i++){ bitsum+=(histsave[i]-1)*lengths[i]; samples+=histsave[i]-1; } if(samples){ fprintf(stderr,"\rTotal samples in training set: %ld \n",samples); fprintf(stderr,"\rTotal bits used to represent training set: %ld\n", bitsum); } } free(membership); } /* wrap build_tree_from_lengths to allow zero entries in the histogram */ void build_tree_from_lengths0(int vals, long *hist, long *lengths){ /* pack the 'sparse' hit list into a dense list, then unpack the lengths after the build */ int upper=0,i; long *lengthlist=_ogg_calloc(vals,sizeof(long)); long *newhist=alloca(vals*sizeof(long)); for(i=0;i<vals;i++) if(hist[i]>0) newhist[upper++]=hist[i]; if(upper != vals){ fprintf(stderr,"\rEliminating %d unused entries; %d entries remain\n", vals-upper,upper); } build_tree_from_lengths(upper,newhist,lengthlist); upper=0; for(i=0;i<vals;i++) if(hist[i]>0) lengths[i]=lengthlist[upper++]; else lengths[i]=0; free(lengthlist); } void write_codebook(FILE *out,char *name,const static_codebook *c){ int i,j,k; /* save the book in C header form */ /* first, the static vectors, then the book structure to tie it together. */ /* quantlist */ if(c->quantlist){ long vals=(c->maptype==1?_book_maptype1_quantvals(c):c->entries*c->dim); fprintf(out,"static const long _vq_quantlist_%s[] = {\n",name); for(j=0;j<vals;j++){ fprintf(out,"\t%ld,\n",c->quantlist[j]); } fprintf(out,"};\n\n"); } /* lengthlist */ fprintf(out,"static const long _vq_lengthlist_%s[] = {\n",name); for(j=0;j<c->entries;){ fprintf(out,"\t"); for(k=0;k<16 && j<c->entries;k++,j++) fprintf(out,"%2ld,",c->lengthlist[j]); fprintf(out,"\n"); } fprintf(out,"};\n\n"); /* tie it all together */ fprintf(out,"static const static_codebook %s = {\n",name); fprintf(out,"\t%ld, %ld,\n",c->dim,c->entries); fprintf(out,"\t(long *)_vq_lengthlist_%s,\n",name); fprintf(out,"\t%d, %ld, %ld, %d, %d,\n", c->maptype,c->q_min,c->q_delta,c->q_quant,c->q_sequencep); if(c->quantlist) fprintf(out,"\t(long *)_vq_quantlist_%s,\n",name); else fprintf(out,"\tNULL,\n"); fprintf(out,"\t0\n};\n\n"); }