X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=lib%2Fblock.c;h=db245b3e69668fb2be8c123e49596c1c94c39f03;hb=a9eb99a5bd6f2d7da02d6cd13a428baf3a1bf48c;hp=48db4bd9422e45023fc32005e09755f8aff52dc6;hpb=f5d723f55d89497dd9be23fd7723fab8248cd843;p=platform%2Fupstream%2Flibvorbis.git diff --git a/lib/block.c b/lib/block.c index 48db4bd..db245b3 100644 --- a/lib/block.c +++ b/lib/block.c @@ -1,36 +1,34 @@ /******************************************************************** * * - * 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. * + * 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 OggSQUISH SOURCE CODE IS (C) COPYRIGHT 1994-1999 * - * by 1999 Monty and The XIPHOPHORUS Company * - * http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015 * + * by the Xiph.Org Foundation http://www.xiph.org/ * * * ******************************************************************** function: PCM data vector blocking, windowing and dis/reassembly - author: Monty - modifications by: Monty - last modification date: Aug 05 1999 Handle windowing, overlap-add, etc of the PCM vectors. This is made more amusing by Vorbis' current two allowed block sizes. - - Vorbis manipulates the dynamic range of the incoming PCM data - envelope to minimise time-domain energy leakage from percussive and - plosive waveforms being quantized in the MDCT domain. ********************************************************************/ +#include #include #include -#include "codec.h" +#include +#include "vorbis/codec.h" +#include "codec_internal.h" + #include "window.h" -#include "envelope.h" #include "mdct.h" +#include "lpc.h" +#include "registry.h" +#include "misc.h" /* pcm accumulator examples (not exhaustive): @@ -41,327 +39,598 @@ :.....''' |_____--- '''......| | \_______| :.................|__________________|_______|__|______| |<------ Sl ------>| > Sr < |endW - |beginSl |endSl | |endSr + |beginSl |endSl | |endSr |beginW |endlW |beginSr - |< lW >| + |< lW >| <--------------- W ----------------> | | .. ______________ | | | ' `/ | ---_ | - |___.'___/`. | ---_____| + |___.'___/`. | ---_____| |_______|__|_______|_________________| | >|Sl|< |<------ Sr ----->|endW | | |endSl |beginSr |endSr - |beginW | |endlW + |beginW | |endlW mult[0] |beginSl mult[n] <-------------- lW -----------------> - |<--W-->| -: .............. ___ | | -: .''' |`/ \ | | -:.....''' |/`....\|...| -:.........................|___|___|___| - |Sl |Sr |endW + |<--W-->| +: .............. ___ | | +: .''' |`/ \ | | +:.....''' |/`....\|...| +:.........................|___|___|___| + |Sl |Sr |endW | | |endSr | |beginSr | |endSl - |beginSl - |beginW + |beginSl + |beginW */ -static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi){ - memset(v,0,sizeof(vorbis_dsp_state)); - - memcpy(&v->vi,vi,sizeof(vorbis_info)); - _ve_envelope_init(&v->ve,vi->envelopesa); - mdct_init(&v->vm[0],vi->smallblock); - mdct_init(&v->vm[1],vi->largeblock); - - v->samples_per_envelope_step=vi->envelopesa; - v->block_size[0]=vi->smallblock; - v->block_size[1]=vi->largeblock; - - v->window[0][0][0]=_vorbis_window(v->block_size[0], - v->block_size[0]/2,v->block_size[0]/2); - v->window[0][0][1]=v->window[0][0][0]; - v->window[0][1][0]=v->window[0][0][0]; - v->window[0][1][1]=v->window[0][0][0]; - - v->window[1][0][0]=_vorbis_window(v->block_size[1], - v->block_size[0]/2,v->block_size[0]/2); - v->window[1][0][1]=_vorbis_window(v->block_size[1], - v->block_size[0]/2,v->block_size[1]/2); - v->window[1][1][0]=_vorbis_window(v->block_size[1], - v->block_size[1]/2,v->block_size[0]/2); - v->window[1][1][1]=_vorbis_window(v->block_size[1], - v->block_size[1]/2,v->block_size[1]/2); - - /* initialize the storage vectors to a decent size greater than the - minimum */ - - v->pcm_storage=8192; /* we'll assume later that we have - a minimum of twice the blocksize of - accumulated samples in analysis */ - v->pcm_channels=v->vi.channels=vi->channels; - v->pcm=malloc(vi->channels*sizeof(double *)); - v->pcmret=malloc(vi->channels*sizeof(double *)); +/* block abstraction setup *********************************************/ + +#ifndef WORD_ALIGN +#define WORD_ALIGN 8 +#endif + +int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){ + int i; + memset(vb,0,sizeof(*vb)); + vb->vd=v; + vb->localalloc=0; + vb->localstore=NULL; + if(v->analysisp){ + vorbis_block_internal *vbi= + vb->internal=_ogg_calloc(1,sizeof(vorbis_block_internal)); + vbi->ampmax=-9999; + + for(i=0;ipacketblob[i]=&vb->opb; + }else{ + vbi->packetblob[i]= + _ogg_calloc(1,sizeof(oggpack_buffer)); + } + oggpack_writeinit(vbi->packetblob[i]); + } + } + + return(0); +} + +void *_vorbis_block_alloc(vorbis_block *vb,long bytes){ + bytes=(bytes+(WORD_ALIGN-1)) & ~(WORD_ALIGN-1); + if(bytes+vb->localtop>vb->localalloc){ + /* can't just _ogg_realloc... there are outstanding pointers */ + if(vb->localstore){ + struct alloc_chain *link=_ogg_malloc(sizeof(*link)); + vb->totaluse+=vb->localtop; + link->next=vb->reap; + link->ptr=vb->localstore; + vb->reap=link; + } + /* highly conservative */ + vb->localalloc=bytes; + vb->localstore=_ogg_malloc(vb->localalloc); + vb->localtop=0; + } { - int i; - for(i=0;ichannels;i++) - v->pcm[i]=calloc(v->pcm_storage,sizeof(double)); + void *ret=(void *)(((char *)vb->localstore)+vb->localtop); + vb->localtop+=bytes; + return ret; } +} - /* Initialize the envelope multiplier storage */ +/* reap the chain, pull the ripcord */ +void _vorbis_block_ripcord(vorbis_block *vb){ + /* reap the chain */ + struct alloc_chain *reap=vb->reap; + while(reap){ + struct alloc_chain *next=reap->next; + _ogg_free(reap->ptr); + memset(reap,0,sizeof(*reap)); + _ogg_free(reap); + reap=next; + } + /* consolidate storage */ + if(vb->totaluse){ + vb->localstore=_ogg_realloc(vb->localstore,vb->totaluse+vb->localalloc); + vb->localalloc+=vb->totaluse; + vb->totaluse=0; + } - if(vi->envelopech){ - v->envelope_storage=v->pcm_storage/v->samples_per_envelope_step; - v->envelope_channels=vi->envelopech; - v->multipliers=calloc(v->envelope_channels,sizeof(double *)); - { - int i; - for(i=0;ienvelope_channels;i++){ - v->multipliers[i]=calloc(v->envelope_storage,sizeof(double)); + /* pull the ripcord */ + vb->localtop=0; + vb->reap=NULL; +} + +int vorbis_block_clear(vorbis_block *vb){ + int i; + vorbis_block_internal *vbi=vb->internal; + + _vorbis_block_ripcord(vb); + if(vb->localstore)_ogg_free(vb->localstore); + + if(vbi){ + for(i=0;ipacketblob[i]); + if(i!=PACKETBLOBS/2)_ogg_free(vbi->packetblob[i]); + } + _ogg_free(vbi); + } + memset(vb,0,sizeof(*vb)); + return(0); +} + +/* Analysis side code, but directly related to blocking. Thus it's + here and not in analysis.c (which is for analysis transforms only). + The init is here because some of it is shared */ + +static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){ + int i; + codec_setup_info *ci=vi->codec_setup; + private_state *b=NULL; + int hs; + + if(ci==NULL|| + ci->modes<=0|| + ci->blocksizes[0]<64|| + ci->blocksizes[1]blocksizes[0]){ + return 1; + } + hs=ci->halfrate_flag; + + memset(v,0,sizeof(*v)); + b=v->backend_state=_ogg_calloc(1,sizeof(*b)); + + v->vi=vi; + b->modebits=ov_ilog(ci->modes-1); + + b->transform[0]=_ogg_calloc(VI_TRANSFORMB,sizeof(*b->transform[0])); + b->transform[1]=_ogg_calloc(VI_TRANSFORMB,sizeof(*b->transform[1])); + + /* MDCT is tranform 0 */ + + b->transform[0][0]=_ogg_calloc(1,sizeof(mdct_lookup)); + b->transform[1][0]=_ogg_calloc(1,sizeof(mdct_lookup)); + mdct_init(b->transform[0][0],ci->blocksizes[0]>>hs); + mdct_init(b->transform[1][0],ci->blocksizes[1]>>hs); + + /* Vorbis I uses only window type 0 */ + /* note that the correct computation below is technically: + b->window[0]=ov_ilog(ci->blocksizes[0]-1)-6; + b->window[1]=ov_ilog(ci->blocksizes[1]-1)-6; + but since blocksizes are always powers of two, + the below is equivalent. + */ + b->window[0]=ov_ilog(ci->blocksizes[0])-7; + b->window[1]=ov_ilog(ci->blocksizes[1])-7; + + if(encp){ /* encode/decode differ here */ + + /* analysis always needs an fft */ + drft_init(&b->fft_look[0],ci->blocksizes[0]); + drft_init(&b->fft_look[1],ci->blocksizes[1]); + + /* finish the codebooks */ + if(!ci->fullbooks){ + ci->fullbooks=_ogg_calloc(ci->books,sizeof(*ci->fullbooks)); + for(i=0;ibooks;i++) + vorbis_book_init_encode(ci->fullbooks+i,ci->book_param[i]); + } + + b->psy=_ogg_calloc(ci->psys,sizeof(*b->psy)); + for(i=0;ipsys;i++){ + _vp_psy_init(b->psy+i, + ci->psy_param[i], + &ci->psy_g_param, + ci->blocksizes[ci->psy_param[i]->blockflag]/2, + vi->rate); + } + + v->analysisp=1; + }else{ + /* finish the codebooks */ + if(!ci->fullbooks){ + ci->fullbooks=_ogg_calloc(ci->books,sizeof(*ci->fullbooks)); + for(i=0;ibooks;i++){ + if(ci->book_param[i]==NULL) + goto abort_books; + if(vorbis_book_init_decode(ci->fullbooks+i,ci->book_param[i])) + goto abort_books; + /* decode codebooks are now standalone after init */ + vorbis_staticbook_destroy(ci->book_param[i]); + ci->book_param[i]=NULL; } } } + /* initialize the storage vectors. blocksize[1] is small for encode, + but the correct size for decode */ + v->pcm_storage=ci->blocksizes[1]; + v->pcm=_ogg_malloc(vi->channels*sizeof(*v->pcm)); + v->pcmret=_ogg_malloc(vi->channels*sizeof(*v->pcmret)); + { + int i; + for(i=0;ichannels;i++) + v->pcm[i]=_ogg_calloc(v->pcm_storage,sizeof(*v->pcm[i])); + } + /* all 1 (large block) or 0 (small block) */ /* explicitly set for the sake of clarity */ v->lW=0; /* previous window size */ v->W=0; /* current window size */ - /* all vector indexes; multiples of samples_per_envelope_step */ - v->centerW=v->block_size[1]/2; + /* all vector indexes */ + v->centerW=ci->blocksizes[1]/2; v->pcm_current=v->centerW; - v->envelope_current=v->centerW/v->samples_per_envelope_step; - return(0); + + /* initialize all the backend lookups */ + b->flr=_ogg_calloc(ci->floors,sizeof(*b->flr)); + b->residue=_ogg_calloc(ci->residues,sizeof(*b->residue)); + + for(i=0;ifloors;i++) + b->flr[i]=_floor_P[ci->floor_type[i]]-> + look(v,ci->floor_param[i]); + + for(i=0;iresidues;i++) + b->residue[i]=_residue_P[ci->residue_type[i]]-> + look(v,ci->residue_param[i]); + + return 0; + abort_books: + for(i=0;ibooks;i++){ + if(ci->book_param[i]!=NULL){ + vorbis_staticbook_destroy(ci->book_param[i]); + ci->book_param[i]=NULL; + } + } + vorbis_dsp_clear(v); + return -1; } /* arbitrary settings and spec-mandated numbers get filled in here */ int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi){ - _vds_shared_init(v,vi); - drft_init(&v->vf[0],vi->smallblock); /* only used in analysis */ - drft_init(&v->vf[1],vi->largeblock); /* only used in analysis */ + private_state *b=NULL; + + if(_vds_shared_init(v,vi,1))return 1; + b=v->backend_state; + b->psy_g_look=_vp_global_look(vi); + + /* Initialize the envelope state storage */ + b->ve=_ogg_calloc(1,sizeof(*b->ve)); + _ve_envelope_init(b->ve,vi); + + vorbis_bitrate_init(vi,&b->bms); + + /* compressed audio packets start after the headers + with sequence number 3 */ + v->sequence=3; return(0); } -void vorbis_analysis_clear(vorbis_dsp_state *v){ - int i,j,k; +void vorbis_dsp_clear(vorbis_dsp_state *v){ + int i; if(v){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=(vi?vi->codec_setup:NULL); + private_state *b=v->backend_state; + + if(b){ + + if(b->ve){ + _ve_envelope_clear(b->ve); + _ogg_free(b->ve); + } + + if(b->transform[0]){ + mdct_clear(b->transform[0][0]); + _ogg_free(b->transform[0][0]); + _ogg_free(b->transform[0]); + } + if(b->transform[1]){ + mdct_clear(b->transform[1][0]); + _ogg_free(b->transform[1][0]); + _ogg_free(b->transform[1]); + } + + if(b->flr){ + if(ci) + for(i=0;ifloors;i++) + _floor_P[ci->floor_type[i]]-> + free_look(b->flr[i]); + _ogg_free(b->flr); + } + if(b->residue){ + if(ci) + for(i=0;iresidues;i++) + _residue_P[ci->residue_type[i]]-> + free_look(b->residue[i]); + _ogg_free(b->residue); + } + if(b->psy){ + if(ci) + for(i=0;ipsys;i++) + _vp_psy_clear(b->psy+i); + _ogg_free(b->psy); + } + + if(b->psy_g_look)_vp_global_free(b->psy_g_look); + vorbis_bitrate_clear(&b->bms); + + drft_clear(&b->fft_look[0]); + drft_clear(&b->fft_look[1]); + + } - if(v->window[0][0][0])free(v->window[0][0][0]); - for(j=0;j<2;j++) - for(k=0;k<2;k++) - if(v->window[1][j][k])free(v->window[1][j][k]); if(v->pcm){ - for(i=0;ipcm_channels;i++) - if(v->pcm[i])free(v->pcm[i]); - free(v->pcm); - free(v->pcmret); + if(vi) + for(i=0;ichannels;i++) + if(v->pcm[i])_ogg_free(v->pcm[i]); + _ogg_free(v->pcm); + if(v->pcmret)_ogg_free(v->pcmret); } - if(v->multipliers){ - for(i=0;ienvelope_channels;i++) - if(v->multipliers[i])free(v->multipliers[i]); - free(v->multipliers); + + if(b){ + /* free header, header1, header2 */ + if(b->header)_ogg_free(b->header); + if(b->header1)_ogg_free(b->header1); + if(b->header2)_ogg_free(b->header2); + _ogg_free(b); } - _ve_envelope_clear(&v->ve); - drft_clear(&v->vf[0]); - drft_clear(&v->vf[1]); - mdct_clear(&v->vm[0]); - mdct_clear(&v->vm[1]); - memset(v,0,sizeof(vorbis_dsp_state)); + + memset(v,0,sizeof(*v)); } } -double **vorbis_analysis_buffer(vorbis_dsp_state *v, int vals){ +float **vorbis_analysis_buffer(vorbis_dsp_state *v, int vals){ int i; + vorbis_info *vi=v->vi; + private_state *b=v->backend_state; + + /* free header, header1, header2 */ + if(b->header)_ogg_free(b->header);b->header=NULL; + if(b->header1)_ogg_free(b->header1);b->header1=NULL; + if(b->header2)_ogg_free(b->header2);b->header2=NULL; /* Do we have enough storage space for the requested buffer? If not, expand the PCM (and envelope) storage */ - + if(v->pcm_current+vals>=v->pcm_storage){ v->pcm_storage=v->pcm_current+vals*2; - v->envelope_storage=v->pcm_storage/v->samples_per_envelope_step; - - for(i=0;ipcm_channels;i++){ - v->pcm[i]=realloc(v->pcm[i],v->pcm_storage*sizeof(double)); - } - for(i=0;ienvelope_channels;i++){ - v->multipliers[i]=realloc(v->multipliers[i], - v->envelope_storage*sizeof(double)); + + for(i=0;ichannels;i++){ + v->pcm[i]=_ogg_realloc(v->pcm[i],v->pcm_storage*sizeof(*v->pcm[i])); } } - for(i=0;ipcm_channels;i++) + for(i=0;ichannels;i++) v->pcmret[i]=v->pcm[i]+v->pcm_current; - + return(v->pcmret); } +static void _preextrapolate_helper(vorbis_dsp_state *v){ + int i; + int order=16; + float *lpc=alloca(order*sizeof(*lpc)); + float *work=alloca(v->pcm_current*sizeof(*work)); + long j; + v->preextrapolate=1; + + if(v->pcm_current-v->centerW>order*2){ /* safety */ + for(i=0;ivi->channels;i++){ + /* need to run the extrapolation in reverse! */ + for(j=0;jpcm_current;j++) + work[j]=v->pcm[i][v->pcm_current-j-1]; + + /* prime as above */ + vorbis_lpc_from_data(work,lpc,v->pcm_current-v->centerW,order); + +#if 0 + if(v->vi->channels==2){ + if(i==0) + _analysis_output("predataL",0,work,v->pcm_current-v->centerW,0,0,0); + else + _analysis_output("predataR",0,work,v->pcm_current-v->centerW,0,0,0); + }else{ + _analysis_output("predata",0,work,v->pcm_current-v->centerW,0,0,0); + } +#endif + + /* run the predictor filter */ + vorbis_lpc_predict(lpc,work+v->pcm_current-v->centerW-order, + order, + work+v->pcm_current-v->centerW, + v->centerW); + + for(j=0;jpcm_current;j++) + v->pcm[i][v->pcm_current-j-1]=work[j]; + + } + } +} + + /* call with val<=0 to set eof */ int vorbis_analysis_wrote(vorbis_dsp_state *v, int vals){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + if(vals<=0){ + int order=32; + int i; + float *lpc=alloca(order*sizeof(*lpc)); + + /* if it wasn't done earlier (very short sample) */ + if(!v->preextrapolate) + _preextrapolate_helper(v); + /* We're encoding the end of the stream. Just make sure we have - [at least] a full block of zeroes at the end. */ + [at least] a few full blocks of zeroes at the end. */ + /* actually, we don't want zeroes; that could drop a large + amplitude off a cliff, creating spread spectrum noise that will + suck to encode. Extrapolate for the sake of cleanliness. */ - int i; - vorbis_analysis_buffer(v,v->block_size[1]*2); + vorbis_analysis_buffer(v,ci->blocksizes[1]*3); v->eofflag=v->pcm_current; - v->pcm_current+=v->block_size[1]*2; - for(i=0;ipcm_channels;i++) - memset(v->pcm[i]+v->eofflag,0, - (v->pcm_current-v->eofflag)*sizeof(double)); + v->pcm_current+=ci->blocksizes[1]*3; + + for(i=0;ichannels;i++){ + if(v->eofflag>order*2){ + /* extrapolate with LPC to fill in */ + long n; + + /* make a predictor filter */ + n=v->eofflag; + if(n>ci->blocksizes[1])n=ci->blocksizes[1]; + vorbis_lpc_from_data(v->pcm[i]+v->eofflag-n,lpc,n,order); + + /* run the predictor filter */ + vorbis_lpc_predict(lpc,v->pcm[i]+v->eofflag-order,order, + v->pcm[i]+v->eofflag,v->pcm_current-v->eofflag); + }else{ + /* not enough data to extrapolate (unlikely to happen due to + guarding the overlap, but bulletproof in case that + assumtion goes away). zeroes will do. */ + memset(v->pcm[i]+v->eofflag,0, + (v->pcm_current-v->eofflag)*sizeof(*v->pcm[i])); + + } + } }else{ - + if(v->pcm_current+vals>v->pcm_storage) - return(-1); + return(OV_EINVAL); v->pcm_current+=vals; - } - return(0); -} -int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){ - int i; - vb->pcm_storage=v->block_size[1]; - vb->pcm_channels=v->pcm_channels; - vb->mult_storage=v->block_size[1]/v->samples_per_envelope_step; - vb->mult_channels=v->envelope_channels; - - vb->pcm=malloc(vb->pcm_channels*sizeof(double *)); - for(i=0;ipcm_channels;i++) - vb->pcm[i]=malloc(vb->pcm_storage*sizeof(double)); - - vb->mult=malloc(vb->mult_channels*sizeof(double *)); - for(i=0;imult_channels;i++) - vb->mult[i]=malloc(vb->mult_storage*sizeof(double)); - return(0); -} + /* we may want to reverse extrapolate the beginning of a stream + too... in case we're beginning on a cliff! */ + /* clumsy, but simple. It only runs once, so simple is good. */ + if(!v->preextrapolate && v->pcm_current-v->centerW>ci->blocksizes[1]) + _preextrapolate_helper(v); -int vorbis_block_clear(vorbis_block *vb){ - int i; - if(vb->pcm){ - for(i=0;ipcm_channels;i++) - free(vb->pcm[i]); - free(vb->pcm); - } - if(vb->mult){ - for(i=0;imult_channels;i++) - free(vb->mult[i]); - free(vb->mult); } - memset(vb,0,sizeof(vorbis_block)); return(0); } /* do the deltas, envelope shaping, pre-echo and determine the size of the next block on which to continue analysis */ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ - int i,j; - long beginW=v->centerW-v->block_size[v->W]/2,centerNext; - long beginM=beginW/v->samples_per_envelope_step; + int i; + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + private_state *b=v->backend_state; + vorbis_look_psy_global *g=b->psy_g_look; + long beginW=v->centerW-ci->blocksizes[v->W]/2,centerNext; + vorbis_block_internal *vbi=(vorbis_block_internal *)vb->internal; + + /* check to see if we're started... */ + if(!v->preextrapolate)return(0); /* check to see if we're done... */ if(v->eofflag==-1)return(0); - /* if we have any unfilled envelope blocks for which we have PCM - data, fill them up in before proceeding. */ - - if(v->pcm_current/v->samples_per_envelope_step>v->envelope_current){ - /* This generates the multipliers, but does not sparsify the vector. - That's done by block before coding */ - _ve_envelope_multipliers(v); - } - /* By our invariant, we have lW, W and centerW set. Search for the next boundary so we can determine nW (the next window size) which lets us compute the shape of the current block's window */ - /* overconserve for now; any block with a non-placeholder multiplier - should be minimal size. We can be greedy and only look at nW size */ - - if(v->W) - /* this is a long window; we start the search forward of centerW - because that's the fastest we could react anyway */ - i=v->centerW+v->block_size[1]/4-v->block_size[0]/4; - else - /* short window. Search from centerW */ - i=v->centerW; - i/=v->samples_per_envelope_step; - - for(;ienvelope_current;i++){ - for(j=0;jenvelope_channels;j++) - if(v->multipliers[j][i-1]*v->vi.preecho_thresh< - v->multipliers[j][i])break; - if(jenvelope_channels)break; - } - - if(ienvelope_current){ - /* Ooo, we hit a multiplier. Is it beyond the boundary to make the - upcoming block large ? */ - long largebound; - if(v->W) - largebound=v->centerW+v->block_size[1]; - else - largebound=v->centerW+v->block_size[0]/4+v->block_size[1]*3/4; - largebound/=v->samples_per_envelope_step; + /* we do an envelope search even on a single blocksize; we may still + be throwing more bits at impulses, and envelope search handles + marking impulses too. */ + { + long bp=_ve_envelope_search(v); + if(bp==-1){ - if(i>=largebound) - v->nW=1; - else + if(v->eofflag==0)return(0); /* not enough data currently to search for a + full long block */ v->nW=0; + }else{ - }else{ - /* Assume maximum; if the block is incomplete given current - buffered data, this will be detected below */ - v->nW=1; + if(ci->blocksizes[0]==ci->blocksizes[1]) + v->nW=0; + else + v->nW=bp; + } } - /* Do we actually have enough data *now* for the next block? The - reason to check is that if we had no multipliers, that could - simply been due to running out of data. In that case, we don;t - know the size of the next block for sure and we need that now to - figure out the window shape of this block */ - - centerNext=v->centerW+v->block_size[v->W]/4+v->block_size[v->nW]/4; + centerNext=v->centerW+ci->blocksizes[v->W]/4+ci->blocksizes[v->nW]/4; { - long blockbound=centerNext+v->block_size[v->nW]/2; - if(v->pcm_currentblocksizes[v->nW]/2; + if(v->pcm_currentlW=v->lW; vb->W=v->W; vb->nW=v->nW; - vb->vd=v; - vb->pcmend=v->block_size[v->W]; - vb->multend=vb->pcmend / v->samples_per_envelope_step; - - if(v->pcm_channels!=vb->pcm_channels || - v->block_size[1]!=vb->pcm_storage || - v->envelope_channels!=vb->mult_channels){ + if(v->W){ + if(!v->lW || !v->nW){ + vbi->blocktype=BLOCKTYPE_TRANSITION; + /*fprintf(stderr,"-");*/ + }else{ + vbi->blocktype=BLOCKTYPE_LONG; + /*fprintf(stderr,"_");*/ + } + }else{ + if(_ve_envelope_mark(v)){ + vbi->blocktype=BLOCKTYPE_IMPULSE; + /*fprintf(stderr,"|");*/ - /* Storage not initialized or initilized for some other codec - instance with different settings */ + }else{ + vbi->blocktype=BLOCKTYPE_PADDING; + /*fprintf(stderr,".");*/ - vorbis_block_clear(vb); - vorbis_block_init(v,vb); + } } - /* copy the vectors */ - for(i=0;ipcm_channels;i++) - memcpy(vb->pcm[i],v->pcm[i]+beginW,v->block_size[v->W]*sizeof(double)); - for(i=0;ienvelope_channels;i++) - memcpy(vb->mult[i],v->multipliers[i]+beginM,v->block_size[v->W]/ - v->samples_per_envelope_step*sizeof(double)); + vb->vd=v; + vb->sequence=v->sequence++; + vb->granulepos=v->granulepos; + vb->pcmend=ci->blocksizes[v->W]; + + /* copy the vectors; this uses the local storage in vb */ + + /* this tracks 'strongest peak' for later psychoacoustics */ + /* moved to the global psy state; clean this mess up */ + if(vbi->ampmax>g->ampmax)g->ampmax=vbi->ampmax; + g->ampmax=_vp_ampmax_decay(g->ampmax,v); + vbi->ampmax=g->ampmax; + + vb->pcm=_vorbis_block_alloc(vb,sizeof(*vb->pcm)*vi->channels); + vbi->pcmdelay=_vorbis_block_alloc(vb,sizeof(*vbi->pcmdelay)*vi->channels); + for(i=0;ichannels;i++){ + vbi->pcmdelay[i]= + _vorbis_block_alloc(vb,(vb->pcmend+beginW)*sizeof(*vbi->pcmdelay[i])); + memcpy(vbi->pcmdelay[i],v->pcm[i],(vb->pcmend+beginW)*sizeof(*vbi->pcmdelay[i])); + vb->pcm[i]=vbi->pcmdelay[i]+beginW; + + /* before we added the delay + vb->pcm[i]=_vorbis_block_alloc(vb,vb->pcmend*sizeof(*vb->pcm[i])); + memcpy(vb->pcm[i],v->pcm[i]+beginW,ci->blocksizes[v->W]*sizeof(*vb->pcm[i])); + */ - vb->frameno=v->frame; + } /* handle eof detection: eof==0 means that we've not yet received EOF eof>0 marks the last 'real' sample in pcm[] @@ -371,155 +640,407 @@ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ if(v->centerW>=v->eofflag){ v->eofflag=-1; vb->eofflag=1; + return(1); } } /* advance storage vectors and clean up */ { - int new_centerNext=v->block_size[1]/2; + int new_centerNext=ci->blocksizes[1]/2; int movementW=centerNext-new_centerNext; - int movementM=movementW/v->samples_per_envelope_step; - - /* the multipliers and pcm stay synced up because the blocksizes - must be multiples of samples_per_envelope_step (minimum - multiple is 2) */ - - for(i=0;ipcm_channels;i++) - memmove(v->pcm[i],v->pcm[i]+movementW, - (v->pcm_current-movementW)*sizeof(double)); - - for(i=0;ienvelope_channels;i++){ - memmove(v->multipliers[i],v->multipliers[i]+movementM, - (v->envelope_current-movementM)*sizeof(double)); - } - v->pcm_current-=movementW; - v->envelope_current-=movementM; + if(movementW>0){ + + _ve_envelope_shift(b->ve,movementW); + v->pcm_current-=movementW; + + for(i=0;ichannels;i++) + memmove(v->pcm[i],v->pcm[i]+movementW, + v->pcm_current*sizeof(*v->pcm[i])); - v->lW=v->W; - v->W=v->nW; - v->centerW=new_centerNext; - v->frame++; - v->samples+=movementW; + v->lW=v->W; + v->W=v->nW; + v->centerW=new_centerNext; - if(v->eofflag) - v->eofflag-=movementW; + if(v->eofflag){ + v->eofflag-=movementW; + if(v->eofflag<=0)v->eofflag=-1; + /* do not add padding to end of stream! */ + if(v->centerW>=v->eofflag){ + v->granulepos+=movementW-(v->centerW-v->eofflag); + }else{ + v->granulepos+=movementW; + } + }else{ + v->granulepos+=movementW; + } + } } /* done */ return(1); } -int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){ - int temp=vi->envelopech; - vi->envelopech=0; /* we don't need multiplier buffering in syn */ - _vds_shared_init(v,vi); - vi->envelopech=temp; - - /* Adjust centerW to allow an easier mechanism for determining output */ - v->pcm_returned=v->centerW; - v->centerW-= v->block_size[v->W]/4+v->block_size[v->lW]/4; +int vorbis_synthesis_restart(vorbis_dsp_state *v){ + vorbis_info *vi=v->vi; + codec_setup_info *ci; + int hs; + + if(!v->backend_state)return -1; + if(!vi)return -1; + ci=vi->codec_setup; + if(!ci)return -1; + hs=ci->halfrate_flag; + + v->centerW=ci->blocksizes[1]>>(hs+1); + v->pcm_current=v->centerW>>hs; + + v->pcm_returned=-1; + v->granulepos=-1; + v->sequence=-1; + v->eofflag=0; + ((private_state *)(v->backend_state))->sample_count=-1; + return(0); } -/* Unike in analysis, the window is only partially applied. Envelope - is previously applied (the whole envelope, if any, is shipped in - each block) */ +int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){ + if(_vds_shared_init(v,vi,0)){ + vorbis_dsp_clear(v); + return 1; + } + vorbis_synthesis_restart(v); + return 0; +} + +/* Unlike in analysis, the window is only partially applied for each + block. The time domain envelope is not yet handled at the point of + calling (as it relies on the previous block). */ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + private_state *b=v->backend_state; + int hs=ci->halfrate_flag; + int i,j; - /* Shift out any PCM that we returned previously */ + if(!vb)return(OV_EINVAL); + if(v->pcm_current>v->pcm_returned && v->pcm_returned!=-1)return(OV_EINVAL); - if(v->pcm_returned && v->centerW>v->block_size[1]/2){ + v->lW=v->W; + v->W=vb->W; + v->nW=-1; - /* don't shift too much; we need to have a minimum PCM buffer of - 1/2 long block */ + if((v->sequence==-1)|| + (v->sequence+1 != vb->sequence)){ + v->granulepos=-1; /* out of sequence; lose count */ + b->sample_count=-1; + } - int shift=v->centerW-v->block_size[1]/2; - shift=(v->pcm_returnedpcm_returned:shift); + v->sequence=vb->sequence; - v->pcm_current-=shift; - v->centerW-=shift; - v->pcm_returned-=shift; - - if(shift){ - int i; - for(i=0;ipcm_channels;i++) - memmove(v->pcm[i],v->pcm[i]+shift, - v->pcm_current*sizeof(double)); + if(vb->pcm){ /* no pcm to process if vorbis_synthesis_trackonly + was called on block */ + int n=ci->blocksizes[v->W]>>(hs+1); + int n0=ci->blocksizes[0]>>(hs+1); + int n1=ci->blocksizes[1]>>(hs+1); + + int thisCenter; + int prevCenter; + + v->glue_bits+=vb->glue_bits; + v->time_bits+=vb->time_bits; + v->floor_bits+=vb->floor_bits; + v->res_bits+=vb->res_bits; + + if(v->centerW){ + thisCenter=n1; + prevCenter=0; + }else{ + thisCenter=0; + prevCenter=n1; } - } - { - int sizeW=v->block_size[vb->W]; - int centerW=v->centerW+v->block_size[vb->lW]/4+sizeW/4; - int beginW=centerW-sizeW/2; - int endW=beginW+sizeW; - int beginSl; - int endSl; - int i,j; - double *window; - - /* Do we have enough PCM storage for the block? */ - if(endW>v->pcm_storage){ - /* expand the PCM storage */ - - v->pcm_storage=endW+v->block_size[1]; - - for(i=0;ipcm_channels;i++) - v->pcm[i]=realloc(v->pcm[i],v->pcm_storage*sizeof(double)); + /* v->pcm is now used like a two-stage double buffer. We don't want + to have to constantly shift *or* adjust memory usage. Don't + accept a new block until the old is shifted out */ + + for(j=0;jchannels;j++){ + /* the overlap/add section */ + if(v->lW){ + if(v->W){ + /* large/large */ + const float *w=_vorbis_window_get(b->window[1]-hs); + float *pcm=v->pcm[j]+prevCenter; + float *p=vb->pcm[j]; + for(i=0;iwindow[0]-hs); + float *pcm=v->pcm[j]+prevCenter+n1/2-n0/2; + float *p=vb->pcm[j]; + for(i=0;iW){ + /* small/large */ + const float *w=_vorbis_window_get(b->window[0]-hs); + float *pcm=v->pcm[j]+prevCenter; + float *p=vb->pcm[j]+n1/2-n0/2; + for(i=0;iwindow[0]-hs); + float *pcm=v->pcm[j]+prevCenter; + float *p=vb->pcm[j]; + for(i=0;ipcm[j]+thisCenter; + float *p=vb->pcm[j]+n; + for(i=0;iW){ - case 0: - beginSl=0; - endSl=v->block_size[0]/2; - break; - case 1: - beginSl=v->block_size[1]/4-v->block_size[vb->lW]/4; - endSl=beginSl+v->block_size[vb->lW]/2; - break; + if(v->centerW) + v->centerW=0; + else + v->centerW=n1; + + /* deal with initial packet state; we do this using the explicit + pcm_returned==-1 flag otherwise we're sensitive to first block + being short or long */ + + if(v->pcm_returned==-1){ + v->pcm_returned=thisCenter; + v->pcm_current=thisCenter; + }else{ + v->pcm_returned=prevCenter; + v->pcm_current=prevCenter+ + ((ci->blocksizes[v->lW]/4+ + ci->blocksizes[v->W]/4)>>hs); } - window=v->window[vb->W][0][vb->lW]+v->block_size[vb->W]/2; + } + + /* track the frame number... This is for convenience, but also + making sure our last packet doesn't end with added padding. If + the last packet is partial, the number of samples we'll have to + return will be past the vb->granulepos. - for(j=0;jpcm_channels;j++){ - double *pcm=v->pcm[j]+beginW; + This is not foolproof! It will be confused if we begin + decoding at the last page after a seek or hole. In that case, + we don't have a starting point to judge where the last frame + is. For this reason, vorbisfile will always try to make sure + it reads the last two marked pages in proper sequence */ + + if(b->sample_count==-1){ + b->sample_count=0; + }else{ + b->sample_count+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; + } + + if(v->granulepos==-1){ + if(vb->granulepos!=-1){ /* only set if we have a position to set to */ + + v->granulepos=vb->granulepos; + + /* is this a short page? */ + if(b->sample_count>v->granulepos){ + /* corner case; if this is both the first and last audio page, + then spec says the end is cut, not beginning */ + long extra=b->sample_count-vb->granulepos; + + /* we use ogg_int64_t for granule positions because a + uint64 isn't universally available. Unfortunately, + that means granposes can be 'negative' and result in + extra being negative */ + if(extra<0) + extra=0; + + if(vb->eofflag){ + /* trim the end */ + /* no preceding granulepos; assume we started at zero (we'd + have to in a short single-page stream) */ + /* granulepos could be -1 due to a seek, but that would result + in a long count, not short count */ + + /* Guard against corrupt/malicious frames that set EOP and + a backdated granpos; don't rewind more samples than we + actually have */ + if(extra > (v->pcm_current - v->pcm_returned)<pcm_current - v->pcm_returned)<pcm_current-=extra>>hs; + }else{ + /* trim the beginning */ + v->pcm_returned+=extra>>hs; + if(v->pcm_returned>v->pcm_current) + v->pcm_returned=v->pcm_current; + } + + } - /* the add section */ - for(i=beginSl;ipcm[j][i]; - /* the remaining section */ - for(;ipcm[j][i]; } + }else{ + v->granulepos+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; + if(vb->granulepos!=-1 && v->granulepos!=vb->granulepos){ + + if(v->granulepos>vb->granulepos){ + long extra=v->granulepos-vb->granulepos; + + if(extra) + if(vb->eofflag){ + /* partial last frame. Strip the extra samples off */ + + /* Guard against corrupt/malicious frames that set EOP and + a backdated granpos; don't rewind more samples than we + actually have */ + if(extra > (v->pcm_current - v->pcm_returned)<pcm_current - v->pcm_returned)<pcm_current-=extra>>hs; + } /* else {Shouldn't happen *unless* the bitstream is out of + spec. Either way, believe the bitstream } */ + } /* else {Shouldn't happen *unless* the bitstream is out of + spec. Either way, believe the bitstream } */ + v->granulepos=vb->granulepos; + } + } + + /* Update, cleanup */ - /* Update, cleanup */ + if(vb->eofflag)v->eofflag=1; + return(0); + +} - v->centerW=centerW; - v->pcm_current=endW; +/* pcm==NULL indicates we just want the pending samples, no more */ +int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm){ + vorbis_info *vi=v->vi; - if(vb->eofflag)v->eofflag=1; + if(v->pcm_returned>-1 && v->pcm_returnedpcm_current){ + if(pcm){ + int i; + for(i=0;ichannels;i++) + v->pcmret[i]=v->pcm[i]+v->pcm_returned; + *pcm=v->pcmret; + } + return(v->pcm_current-v->pcm_returned); } return(0); } -int vorbis_synthesis_pcmout(vorbis_dsp_state *v,double ***pcm){ - if(v->pcm_returnedcenterW){ +int vorbis_synthesis_read(vorbis_dsp_state *v,int n){ + if(n && v->pcm_returned+n>v->pcm_current)return(OV_EINVAL); + v->pcm_returned+=n; + return(0); +} + +/* intended for use with a specific vorbisfile feature; we want access + to the [usually synthetic/postextrapolated] buffer and lapping at + the end of a decode cycle, specifically, a half-short-block worth. + This funtion works like pcmout above, except it will also expose + this implicit buffer data not normally decoded. */ +int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + int hs=ci->halfrate_flag; + + int n=ci->blocksizes[v->W]>>(hs+1); + int n0=ci->blocksizes[0]>>(hs+1); + int n1=ci->blocksizes[1]>>(hs+1); + int i,j; + + if(v->pcm_returned<0)return 0; + + /* our returned data ends at pcm_returned; because the synthesis pcm + buffer is a two-fragment ring, that means our data block may be + fragmented by buffering, wrapping or a short block not filling + out a buffer. To simplify things, we unfragment if it's at all + possibly needed. Otherwise, we'd need to call lapout more than + once as well as hold additional dsp state. Opt for + simplicity. */ + + /* centerW was advanced by blockin; it would be the center of the + *next* block */ + if(v->centerW==n1){ + /* the data buffer wraps; swap the halves */ + /* slow, sure, small */ + for(j=0;jchannels;j++){ + float *p=v->pcm[j]; + for(i=0;ipcm_current-=n1; + v->pcm_returned-=n1; + v->centerW=0; + } + + /* solidify buffer into contiguous space */ + if((v->lW^v->W)==1){ + /* long/short or short/long */ + for(j=0;jchannels;j++){ + float *s=v->pcm[j]; + float *d=v->pcm[j]+(n1-n0)/2; + for(i=(n1+n0)/2-1;i>=0;--i) + d[i]=s[i]; + } + v->pcm_returned+=(n1-n0)/2; + v->pcm_current+=(n1-n0)/2; + }else{ + if(v->lW==0){ + /* short/short */ + for(j=0;jchannels;j++){ + float *s=v->pcm[j]; + float *d=v->pcm[j]+n1-n0; + for(i=n0-1;i>=0;--i) + d[i]=s[i]; + } + v->pcm_returned+=n1-n0; + v->pcm_current+=n1-n0; + } + } + + if(pcm){ int i; - for(i=0;ipcm_channels;i++) + for(i=0;ichannels;i++) v->pcmret[i]=v->pcm[i]+v->pcm_returned; *pcm=v->pcmret; - return(v->centerW-v->pcm_returned); } - return(0); -} -int vorbis_synthesis_read(vorbis_dsp_state *v,int bytes){ - if(bytes && v->pcm_returned+bytes>v->centerW)return(-1); - v->pcm_returned+=bytes; - return(0); + return(n1+n-v->pcm_returned); + } +const float *vorbis_window(vorbis_dsp_state *v,int W){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + int hs=ci->halfrate_flag; + private_state *b=v->backend_state; + + if(b->window[W]-1<0)return NULL; + return _vorbis_window_get(b->window[W]-hs); +}