cannam@86: /******************************************************************** cannam@86: * * cannam@86: * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * cannam@86: * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * cannam@86: * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * cannam@86: * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * cannam@86: * * cannam@86: * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * cannam@86: * by the Xiph.Org Foundation http://www.xiph.org/ * cannam@86: * * cannam@86: ******************************************************************** cannam@86: cannam@86: function: PCM data vector blocking, windowing and dis/reassembly cannam@86: last mod: $Id: block.c 17561 2010-10-23 10:34:24Z xiphmont $ cannam@86: cannam@86: Handle windowing, overlap-add, etc of the PCM vectors. This is made cannam@86: more amusing by Vorbis' current two allowed block sizes. cannam@86: cannam@86: ********************************************************************/ cannam@86: cannam@86: #include cannam@86: #include cannam@86: #include cannam@86: #include cannam@86: #include "vorbis/codec.h" cannam@86: #include "codec_internal.h" cannam@86: cannam@86: #include "window.h" cannam@86: #include "mdct.h" cannam@86: #include "lpc.h" cannam@86: #include "registry.h" cannam@86: #include "misc.h" cannam@86: cannam@86: static int ilog2(unsigned int v){ cannam@86: int ret=0; cannam@86: if(v)--v; cannam@86: while(v){ cannam@86: ret++; cannam@86: v>>=1; cannam@86: } cannam@86: return(ret); cannam@86: } cannam@86: cannam@86: /* pcm accumulator examples (not exhaustive): cannam@86: cannam@86: <-------------- lW ----------------> cannam@86: <--------------- W ----------------> cannam@86: : .....|..... _______________ | cannam@86: : .''' | '''_--- | |\ | cannam@86: :.....''' |_____--- '''......| | \_______| cannam@86: :.................|__________________|_______|__|______| cannam@86: |<------ Sl ------>| > Sr < |endW cannam@86: |beginSl |endSl | |endSr cannam@86: |beginW |endlW |beginSr cannam@86: cannam@86: cannam@86: |< lW >| cannam@86: <--------------- W ----------------> cannam@86: | | .. ______________ | cannam@86: | | ' `/ | ---_ | cannam@86: |___.'___/`. | ---_____| cannam@86: |_______|__|_______|_________________| cannam@86: | >|Sl|< |<------ Sr ----->|endW cannam@86: | | |endSl |beginSr |endSr cannam@86: |beginW | |endlW cannam@86: mult[0] |beginSl mult[n] cannam@86: cannam@86: <-------------- lW -----------------> cannam@86: |<--W-->| cannam@86: : .............. ___ | | cannam@86: : .''' |`/ \ | | cannam@86: :.....''' |/`....\|...| cannam@86: :.........................|___|___|___| cannam@86: |Sl |Sr |endW cannam@86: | | |endSr cannam@86: | |beginSr cannam@86: | |endSl cannam@86: |beginSl cannam@86: |beginW cannam@86: */ cannam@86: cannam@86: /* block abstraction setup *********************************************/ cannam@86: cannam@86: #ifndef WORD_ALIGN cannam@86: #define WORD_ALIGN 8 cannam@86: #endif cannam@86: cannam@86: int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){ cannam@86: int i; cannam@86: memset(vb,0,sizeof(*vb)); cannam@86: vb->vd=v; cannam@86: vb->localalloc=0; cannam@86: vb->localstore=NULL; cannam@86: if(v->analysisp){ cannam@86: vorbis_block_internal *vbi= cannam@86: vb->internal=_ogg_calloc(1,sizeof(vorbis_block_internal)); cannam@86: vbi->ampmax=-9999; cannam@86: cannam@86: for(i=0;ipacketblob[i]=&vb->opb; cannam@86: }else{ cannam@86: vbi->packetblob[i]= cannam@86: _ogg_calloc(1,sizeof(oggpack_buffer)); cannam@86: } cannam@86: oggpack_writeinit(vbi->packetblob[i]); cannam@86: } cannam@86: } cannam@86: cannam@86: return(0); cannam@86: } cannam@86: cannam@86: void *_vorbis_block_alloc(vorbis_block *vb,long bytes){ cannam@86: bytes=(bytes+(WORD_ALIGN-1)) & ~(WORD_ALIGN-1); cannam@86: if(bytes+vb->localtop>vb->localalloc){ cannam@86: /* can't just _ogg_realloc... there are outstanding pointers */ cannam@86: if(vb->localstore){ cannam@86: struct alloc_chain *link=_ogg_malloc(sizeof(*link)); cannam@86: vb->totaluse+=vb->localtop; cannam@86: link->next=vb->reap; cannam@86: link->ptr=vb->localstore; cannam@86: vb->reap=link; cannam@86: } cannam@86: /* highly conservative */ cannam@86: vb->localalloc=bytes; cannam@86: vb->localstore=_ogg_malloc(vb->localalloc); cannam@86: vb->localtop=0; cannam@86: } cannam@86: { cannam@86: void *ret=(void *)(((char *)vb->localstore)+vb->localtop); cannam@86: vb->localtop+=bytes; cannam@86: return ret; cannam@86: } cannam@86: } cannam@86: cannam@86: /* reap the chain, pull the ripcord */ cannam@86: void _vorbis_block_ripcord(vorbis_block *vb){ cannam@86: /* reap the chain */ cannam@86: struct alloc_chain *reap=vb->reap; cannam@86: while(reap){ cannam@86: struct alloc_chain *next=reap->next; cannam@86: _ogg_free(reap->ptr); cannam@86: memset(reap,0,sizeof(*reap)); cannam@86: _ogg_free(reap); cannam@86: reap=next; cannam@86: } cannam@86: /* consolidate storage */ cannam@86: if(vb->totaluse){ cannam@86: vb->localstore=_ogg_realloc(vb->localstore,vb->totaluse+vb->localalloc); cannam@86: vb->localalloc+=vb->totaluse; cannam@86: vb->totaluse=0; cannam@86: } cannam@86: cannam@86: /* pull the ripcord */ cannam@86: vb->localtop=0; cannam@86: vb->reap=NULL; cannam@86: } cannam@86: cannam@86: int vorbis_block_clear(vorbis_block *vb){ cannam@86: int i; cannam@86: vorbis_block_internal *vbi=vb->internal; cannam@86: cannam@86: _vorbis_block_ripcord(vb); cannam@86: if(vb->localstore)_ogg_free(vb->localstore); cannam@86: cannam@86: if(vbi){ cannam@86: for(i=0;ipacketblob[i]); cannam@86: if(i!=PACKETBLOBS/2)_ogg_free(vbi->packetblob[i]); cannam@86: } cannam@86: _ogg_free(vbi); cannam@86: } cannam@86: memset(vb,0,sizeof(*vb)); cannam@86: return(0); cannam@86: } cannam@86: cannam@86: /* Analysis side code, but directly related to blocking. Thus it's cannam@86: here and not in analysis.c (which is for analysis transforms only). cannam@86: The init is here because some of it is shared */ cannam@86: cannam@86: static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){ cannam@86: int i; cannam@86: codec_setup_info *ci=vi->codec_setup; cannam@86: private_state *b=NULL; cannam@86: int hs; cannam@86: cannam@86: if(ci==NULL) return 1; cannam@86: hs=ci->halfrate_flag; cannam@86: cannam@86: memset(v,0,sizeof(*v)); cannam@86: b=v->backend_state=_ogg_calloc(1,sizeof(*b)); cannam@86: cannam@86: v->vi=vi; cannam@86: b->modebits=ilog2(ci->modes); cannam@86: cannam@86: b->transform[0]=_ogg_calloc(VI_TRANSFORMB,sizeof(*b->transform[0])); cannam@86: b->transform[1]=_ogg_calloc(VI_TRANSFORMB,sizeof(*b->transform[1])); cannam@86: cannam@86: /* MDCT is tranform 0 */ cannam@86: cannam@86: b->transform[0][0]=_ogg_calloc(1,sizeof(mdct_lookup)); cannam@86: b->transform[1][0]=_ogg_calloc(1,sizeof(mdct_lookup)); cannam@86: mdct_init(b->transform[0][0],ci->blocksizes[0]>>hs); cannam@86: mdct_init(b->transform[1][0],ci->blocksizes[1]>>hs); cannam@86: cannam@86: /* Vorbis I uses only window type 0 */ cannam@86: b->window[0]=ilog2(ci->blocksizes[0])-6; cannam@86: b->window[1]=ilog2(ci->blocksizes[1])-6; cannam@86: cannam@86: if(encp){ /* encode/decode differ here */ cannam@86: cannam@86: /* analysis always needs an fft */ cannam@86: drft_init(&b->fft_look[0],ci->blocksizes[0]); cannam@86: drft_init(&b->fft_look[1],ci->blocksizes[1]); cannam@86: cannam@86: /* finish the codebooks */ cannam@86: if(!ci->fullbooks){ cannam@86: ci->fullbooks=_ogg_calloc(ci->books,sizeof(*ci->fullbooks)); cannam@86: for(i=0;ibooks;i++) cannam@86: vorbis_book_init_encode(ci->fullbooks+i,ci->book_param[i]); cannam@86: } cannam@86: cannam@86: b->psy=_ogg_calloc(ci->psys,sizeof(*b->psy)); cannam@86: for(i=0;ipsys;i++){ cannam@86: _vp_psy_init(b->psy+i, cannam@86: ci->psy_param[i], cannam@86: &ci->psy_g_param, cannam@86: ci->blocksizes[ci->psy_param[i]->blockflag]/2, cannam@86: vi->rate); cannam@86: } cannam@86: cannam@86: v->analysisp=1; cannam@86: }else{ cannam@86: /* finish the codebooks */ cannam@86: if(!ci->fullbooks){ cannam@86: ci->fullbooks=_ogg_calloc(ci->books,sizeof(*ci->fullbooks)); cannam@86: for(i=0;ibooks;i++){ cannam@86: if(ci->book_param[i]==NULL) cannam@86: goto abort_books; cannam@86: if(vorbis_book_init_decode(ci->fullbooks+i,ci->book_param[i])) cannam@86: goto abort_books; cannam@86: /* decode codebooks are now standalone after init */ cannam@86: vorbis_staticbook_destroy(ci->book_param[i]); cannam@86: ci->book_param[i]=NULL; cannam@86: } cannam@86: } cannam@86: } cannam@86: cannam@86: /* initialize the storage vectors. blocksize[1] is small for encode, cannam@86: but the correct size for decode */ cannam@86: v->pcm_storage=ci->blocksizes[1]; cannam@86: v->pcm=_ogg_malloc(vi->channels*sizeof(*v->pcm)); cannam@86: v->pcmret=_ogg_malloc(vi->channels*sizeof(*v->pcmret)); cannam@86: { cannam@86: int i; cannam@86: for(i=0;ichannels;i++) cannam@86: v->pcm[i]=_ogg_calloc(v->pcm_storage,sizeof(*v->pcm[i])); cannam@86: } cannam@86: cannam@86: /* all 1 (large block) or 0 (small block) */ cannam@86: /* explicitly set for the sake of clarity */ cannam@86: v->lW=0; /* previous window size */ cannam@86: v->W=0; /* current window size */ cannam@86: cannam@86: /* all vector indexes */ cannam@86: v->centerW=ci->blocksizes[1]/2; cannam@86: cannam@86: v->pcm_current=v->centerW; cannam@86: cannam@86: /* initialize all the backend lookups */ cannam@86: b->flr=_ogg_calloc(ci->floors,sizeof(*b->flr)); cannam@86: b->residue=_ogg_calloc(ci->residues,sizeof(*b->residue)); cannam@86: cannam@86: for(i=0;ifloors;i++) cannam@86: b->flr[i]=_floor_P[ci->floor_type[i]]-> cannam@86: look(v,ci->floor_param[i]); cannam@86: cannam@86: for(i=0;iresidues;i++) cannam@86: b->residue[i]=_residue_P[ci->residue_type[i]]-> cannam@86: look(v,ci->residue_param[i]); cannam@86: cannam@86: return 0; cannam@86: abort_books: cannam@86: for(i=0;ibooks;i++){ cannam@86: if(ci->book_param[i]!=NULL){ cannam@86: vorbis_staticbook_destroy(ci->book_param[i]); cannam@86: ci->book_param[i]=NULL; cannam@86: } cannam@86: } cannam@86: vorbis_dsp_clear(v); cannam@86: return -1; cannam@86: } cannam@86: cannam@86: /* arbitrary settings and spec-mandated numbers get filled in here */ cannam@86: int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi){ cannam@86: private_state *b=NULL; cannam@86: cannam@86: if(_vds_shared_init(v,vi,1))return 1; cannam@86: b=v->backend_state; cannam@86: b->psy_g_look=_vp_global_look(vi); cannam@86: cannam@86: /* Initialize the envelope state storage */ cannam@86: b->ve=_ogg_calloc(1,sizeof(*b->ve)); cannam@86: _ve_envelope_init(b->ve,vi); cannam@86: cannam@86: vorbis_bitrate_init(vi,&b->bms); cannam@86: cannam@86: /* compressed audio packets start after the headers cannam@86: with sequence number 3 */ cannam@86: v->sequence=3; cannam@86: cannam@86: return(0); cannam@86: } cannam@86: cannam@86: void vorbis_dsp_clear(vorbis_dsp_state *v){ cannam@86: int i; cannam@86: if(v){ cannam@86: vorbis_info *vi=v->vi; cannam@86: codec_setup_info *ci=(vi?vi->codec_setup:NULL); cannam@86: private_state *b=v->backend_state; cannam@86: cannam@86: if(b){ cannam@86: cannam@86: if(b->ve){ cannam@86: _ve_envelope_clear(b->ve); cannam@86: _ogg_free(b->ve); cannam@86: } cannam@86: cannam@86: if(b->transform[0]){ cannam@86: mdct_clear(b->transform[0][0]); cannam@86: _ogg_free(b->transform[0][0]); cannam@86: _ogg_free(b->transform[0]); cannam@86: } cannam@86: if(b->transform[1]){ cannam@86: mdct_clear(b->transform[1][0]); cannam@86: _ogg_free(b->transform[1][0]); cannam@86: _ogg_free(b->transform[1]); cannam@86: } cannam@86: cannam@86: if(b->flr){ cannam@86: if(ci) cannam@86: for(i=0;ifloors;i++) cannam@86: _floor_P[ci->floor_type[i]]-> cannam@86: free_look(b->flr[i]); cannam@86: _ogg_free(b->flr); cannam@86: } cannam@86: if(b->residue){ cannam@86: if(ci) cannam@86: for(i=0;iresidues;i++) cannam@86: _residue_P[ci->residue_type[i]]-> cannam@86: free_look(b->residue[i]); cannam@86: _ogg_free(b->residue); cannam@86: } cannam@86: if(b->psy){ cannam@86: if(ci) cannam@86: for(i=0;ipsys;i++) cannam@86: _vp_psy_clear(b->psy+i); cannam@86: _ogg_free(b->psy); cannam@86: } cannam@86: cannam@86: if(b->psy_g_look)_vp_global_free(b->psy_g_look); cannam@86: vorbis_bitrate_clear(&b->bms); cannam@86: cannam@86: drft_clear(&b->fft_look[0]); cannam@86: drft_clear(&b->fft_look[1]); cannam@86: cannam@86: } cannam@86: cannam@86: if(v->pcm){ cannam@86: if(vi) cannam@86: for(i=0;ichannels;i++) cannam@86: if(v->pcm[i])_ogg_free(v->pcm[i]); cannam@86: _ogg_free(v->pcm); cannam@86: if(v->pcmret)_ogg_free(v->pcmret); cannam@86: } cannam@86: cannam@86: if(b){ cannam@86: /* free header, header1, header2 */ cannam@86: if(b->header)_ogg_free(b->header); cannam@86: if(b->header1)_ogg_free(b->header1); cannam@86: if(b->header2)_ogg_free(b->header2); cannam@86: _ogg_free(b); cannam@86: } cannam@86: cannam@86: memset(v,0,sizeof(*v)); cannam@86: } cannam@86: } cannam@86: cannam@86: float **vorbis_analysis_buffer(vorbis_dsp_state *v, int vals){ cannam@86: int i; cannam@86: vorbis_info *vi=v->vi; cannam@86: private_state *b=v->backend_state; cannam@86: cannam@86: /* free header, header1, header2 */ cannam@86: if(b->header)_ogg_free(b->header);b->header=NULL; cannam@86: if(b->header1)_ogg_free(b->header1);b->header1=NULL; cannam@86: if(b->header2)_ogg_free(b->header2);b->header2=NULL; cannam@86: cannam@86: /* Do we have enough storage space for the requested buffer? If not, cannam@86: expand the PCM (and envelope) storage */ cannam@86: cannam@86: if(v->pcm_current+vals>=v->pcm_storage){ cannam@86: v->pcm_storage=v->pcm_current+vals*2; cannam@86: cannam@86: for(i=0;ichannels;i++){ cannam@86: v->pcm[i]=_ogg_realloc(v->pcm[i],v->pcm_storage*sizeof(*v->pcm[i])); cannam@86: } cannam@86: } cannam@86: cannam@86: for(i=0;ichannels;i++) cannam@86: v->pcmret[i]=v->pcm[i]+v->pcm_current; cannam@86: cannam@86: return(v->pcmret); cannam@86: } cannam@86: cannam@86: static void _preextrapolate_helper(vorbis_dsp_state *v){ cannam@86: int i; cannam@86: int order=16; cannam@86: float *lpc=alloca(order*sizeof(*lpc)); cannam@86: float *work=alloca(v->pcm_current*sizeof(*work)); cannam@86: long j; cannam@86: v->preextrapolate=1; cannam@86: cannam@86: if(v->pcm_current-v->centerW>order*2){ /* safety */ cannam@86: for(i=0;ivi->channels;i++){ cannam@86: /* need to run the extrapolation in reverse! */ cannam@86: for(j=0;jpcm_current;j++) cannam@86: work[j]=v->pcm[i][v->pcm_current-j-1]; cannam@86: cannam@86: /* prime as above */ cannam@86: vorbis_lpc_from_data(work,lpc,v->pcm_current-v->centerW,order); cannam@86: cannam@86: #if 0 cannam@86: if(v->vi->channels==2){ cannam@86: if(i==0) cannam@86: _analysis_output("predataL",0,work,v->pcm_current-v->centerW,0,0,0); cannam@86: else cannam@86: _analysis_output("predataR",0,work,v->pcm_current-v->centerW,0,0,0); cannam@86: }else{ cannam@86: _analysis_output("predata",0,work,v->pcm_current-v->centerW,0,0,0); cannam@86: } cannam@86: #endif cannam@86: cannam@86: /* run the predictor filter */ cannam@86: vorbis_lpc_predict(lpc,work+v->pcm_current-v->centerW-order, cannam@86: order, cannam@86: work+v->pcm_current-v->centerW, cannam@86: v->centerW); cannam@86: cannam@86: for(j=0;jpcm_current;j++) cannam@86: v->pcm[i][v->pcm_current-j-1]=work[j]; cannam@86: cannam@86: } cannam@86: } cannam@86: } cannam@86: cannam@86: cannam@86: /* call with val<=0 to set eof */ cannam@86: cannam@86: int vorbis_analysis_wrote(vorbis_dsp_state *v, int vals){ cannam@86: vorbis_info *vi=v->vi; cannam@86: codec_setup_info *ci=vi->codec_setup; cannam@86: cannam@86: if(vals<=0){ cannam@86: int order=32; cannam@86: int i; cannam@86: float *lpc=alloca(order*sizeof(*lpc)); cannam@86: cannam@86: /* if it wasn't done earlier (very short sample) */ cannam@86: if(!v->preextrapolate) cannam@86: _preextrapolate_helper(v); cannam@86: cannam@86: /* We're encoding the end of the stream. Just make sure we have cannam@86: [at least] a few full blocks of zeroes at the end. */ cannam@86: /* actually, we don't want zeroes; that could drop a large cannam@86: amplitude off a cliff, creating spread spectrum noise that will cannam@86: suck to encode. Extrapolate for the sake of cleanliness. */ cannam@86: cannam@86: vorbis_analysis_buffer(v,ci->blocksizes[1]*3); cannam@86: v->eofflag=v->pcm_current; cannam@86: v->pcm_current+=ci->blocksizes[1]*3; cannam@86: cannam@86: for(i=0;ichannels;i++){ cannam@86: if(v->eofflag>order*2){ cannam@86: /* extrapolate with LPC to fill in */ cannam@86: long n; cannam@86: cannam@86: /* make a predictor filter */ cannam@86: n=v->eofflag; cannam@86: if(n>ci->blocksizes[1])n=ci->blocksizes[1]; cannam@86: vorbis_lpc_from_data(v->pcm[i]+v->eofflag-n,lpc,n,order); cannam@86: cannam@86: /* run the predictor filter */ cannam@86: vorbis_lpc_predict(lpc,v->pcm[i]+v->eofflag-order,order, cannam@86: v->pcm[i]+v->eofflag,v->pcm_current-v->eofflag); cannam@86: }else{ cannam@86: /* not enough data to extrapolate (unlikely to happen due to cannam@86: guarding the overlap, but bulletproof in case that cannam@86: assumtion goes away). zeroes will do. */ cannam@86: memset(v->pcm[i]+v->eofflag,0, cannam@86: (v->pcm_current-v->eofflag)*sizeof(*v->pcm[i])); cannam@86: cannam@86: } cannam@86: } cannam@86: }else{ cannam@86: cannam@86: if(v->pcm_current+vals>v->pcm_storage) cannam@86: return(OV_EINVAL); cannam@86: cannam@86: v->pcm_current+=vals; cannam@86: cannam@86: /* we may want to reverse extrapolate the beginning of a stream cannam@86: too... in case we're beginning on a cliff! */ cannam@86: /* clumsy, but simple. It only runs once, so simple is good. */ cannam@86: if(!v->preextrapolate && v->pcm_current-v->centerW>ci->blocksizes[1]) cannam@86: _preextrapolate_helper(v); cannam@86: cannam@86: } cannam@86: return(0); cannam@86: } cannam@86: cannam@86: /* do the deltas, envelope shaping, pre-echo and determine the size of cannam@86: the next block on which to continue analysis */ cannam@86: int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ cannam@86: int i; cannam@86: vorbis_info *vi=v->vi; cannam@86: codec_setup_info *ci=vi->codec_setup; cannam@86: private_state *b=v->backend_state; cannam@86: vorbis_look_psy_global *g=b->psy_g_look; cannam@86: long beginW=v->centerW-ci->blocksizes[v->W]/2,centerNext; cannam@86: vorbis_block_internal *vbi=(vorbis_block_internal *)vb->internal; cannam@86: cannam@86: /* check to see if we're started... */ cannam@86: if(!v->preextrapolate)return(0); cannam@86: cannam@86: /* check to see if we're done... */ cannam@86: if(v->eofflag==-1)return(0); cannam@86: cannam@86: /* By our invariant, we have lW, W and centerW set. Search for cannam@86: the next boundary so we can determine nW (the next window size) cannam@86: which lets us compute the shape of the current block's window */ cannam@86: cannam@86: /* we do an envelope search even on a single blocksize; we may still cannam@86: be throwing more bits at impulses, and envelope search handles cannam@86: marking impulses too. */ cannam@86: { cannam@86: long bp=_ve_envelope_search(v); cannam@86: if(bp==-1){ cannam@86: cannam@86: if(v->eofflag==0)return(0); /* not enough data currently to search for a cannam@86: full long block */ cannam@86: v->nW=0; cannam@86: }else{ cannam@86: cannam@86: if(ci->blocksizes[0]==ci->blocksizes[1]) cannam@86: v->nW=0; cannam@86: else cannam@86: v->nW=bp; cannam@86: } cannam@86: } cannam@86: cannam@86: centerNext=v->centerW+ci->blocksizes[v->W]/4+ci->blocksizes[v->nW]/4; cannam@86: cannam@86: { cannam@86: /* center of next block + next block maximum right side. */ cannam@86: cannam@86: long blockbound=centerNext+ci->blocksizes[v->nW]/2; cannam@86: if(v->pcm_currentlW=v->lW; cannam@86: vb->W=v->W; cannam@86: vb->nW=v->nW; cannam@86: cannam@86: if(v->W){ cannam@86: if(!v->lW || !v->nW){ cannam@86: vbi->blocktype=BLOCKTYPE_TRANSITION; cannam@86: /*fprintf(stderr,"-");*/ cannam@86: }else{ cannam@86: vbi->blocktype=BLOCKTYPE_LONG; cannam@86: /*fprintf(stderr,"_");*/ cannam@86: } cannam@86: }else{ cannam@86: if(_ve_envelope_mark(v)){ cannam@86: vbi->blocktype=BLOCKTYPE_IMPULSE; cannam@86: /*fprintf(stderr,"|");*/ cannam@86: cannam@86: }else{ cannam@86: vbi->blocktype=BLOCKTYPE_PADDING; cannam@86: /*fprintf(stderr,".");*/ cannam@86: cannam@86: } cannam@86: } cannam@86: cannam@86: vb->vd=v; cannam@86: vb->sequence=v->sequence++; cannam@86: vb->granulepos=v->granulepos; cannam@86: vb->pcmend=ci->blocksizes[v->W]; cannam@86: cannam@86: /* copy the vectors; this uses the local storage in vb */ cannam@86: cannam@86: /* this tracks 'strongest peak' for later psychoacoustics */ cannam@86: /* moved to the global psy state; clean this mess up */ cannam@86: if(vbi->ampmax>g->ampmax)g->ampmax=vbi->ampmax; cannam@86: g->ampmax=_vp_ampmax_decay(g->ampmax,v); cannam@86: vbi->ampmax=g->ampmax; cannam@86: cannam@86: vb->pcm=_vorbis_block_alloc(vb,sizeof(*vb->pcm)*vi->channels); cannam@86: vbi->pcmdelay=_vorbis_block_alloc(vb,sizeof(*vbi->pcmdelay)*vi->channels); cannam@86: for(i=0;ichannels;i++){ cannam@86: vbi->pcmdelay[i]= cannam@86: _vorbis_block_alloc(vb,(vb->pcmend+beginW)*sizeof(*vbi->pcmdelay[i])); cannam@86: memcpy(vbi->pcmdelay[i],v->pcm[i],(vb->pcmend+beginW)*sizeof(*vbi->pcmdelay[i])); cannam@86: vb->pcm[i]=vbi->pcmdelay[i]+beginW; cannam@86: cannam@86: /* before we added the delay cannam@86: vb->pcm[i]=_vorbis_block_alloc(vb,vb->pcmend*sizeof(*vb->pcm[i])); cannam@86: memcpy(vb->pcm[i],v->pcm[i]+beginW,ci->blocksizes[v->W]*sizeof(*vb->pcm[i])); cannam@86: */ cannam@86: cannam@86: } cannam@86: cannam@86: /* handle eof detection: eof==0 means that we've not yet received EOF cannam@86: eof>0 marks the last 'real' sample in pcm[] cannam@86: eof<0 'no more to do'; doesn't get here */ cannam@86: cannam@86: if(v->eofflag){ cannam@86: if(v->centerW>=v->eofflag){ cannam@86: v->eofflag=-1; cannam@86: vb->eofflag=1; cannam@86: return(1); cannam@86: } cannam@86: } cannam@86: cannam@86: /* advance storage vectors and clean up */ cannam@86: { cannam@86: int new_centerNext=ci->blocksizes[1]/2; cannam@86: int movementW=centerNext-new_centerNext; cannam@86: cannam@86: if(movementW>0){ cannam@86: cannam@86: _ve_envelope_shift(b->ve,movementW); cannam@86: v->pcm_current-=movementW; cannam@86: cannam@86: for(i=0;ichannels;i++) cannam@86: memmove(v->pcm[i],v->pcm[i]+movementW, cannam@86: v->pcm_current*sizeof(*v->pcm[i])); cannam@86: cannam@86: cannam@86: v->lW=v->W; cannam@86: v->W=v->nW; cannam@86: v->centerW=new_centerNext; cannam@86: cannam@86: if(v->eofflag){ cannam@86: v->eofflag-=movementW; cannam@86: if(v->eofflag<=0)v->eofflag=-1; cannam@86: /* do not add padding to end of stream! */ cannam@86: if(v->centerW>=v->eofflag){ cannam@86: v->granulepos+=movementW-(v->centerW-v->eofflag); cannam@86: }else{ cannam@86: v->granulepos+=movementW; cannam@86: } cannam@86: }else{ cannam@86: v->granulepos+=movementW; cannam@86: } cannam@86: } cannam@86: } cannam@86: cannam@86: /* done */ cannam@86: return(1); cannam@86: } cannam@86: cannam@86: int vorbis_synthesis_restart(vorbis_dsp_state *v){ cannam@86: vorbis_info *vi=v->vi; cannam@86: codec_setup_info *ci; cannam@86: int hs; cannam@86: cannam@86: if(!v->backend_state)return -1; cannam@86: if(!vi)return -1; cannam@86: ci=vi->codec_setup; cannam@86: if(!ci)return -1; cannam@86: hs=ci->halfrate_flag; cannam@86: cannam@86: v->centerW=ci->blocksizes[1]>>(hs+1); cannam@86: v->pcm_current=v->centerW>>hs; cannam@86: cannam@86: v->pcm_returned=-1; cannam@86: v->granulepos=-1; cannam@86: v->sequence=-1; cannam@86: v->eofflag=0; cannam@86: ((private_state *)(v->backend_state))->sample_count=-1; cannam@86: cannam@86: return(0); cannam@86: } cannam@86: cannam@86: int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){ cannam@86: if(_vds_shared_init(v,vi,0)){ cannam@86: vorbis_dsp_clear(v); cannam@86: return 1; cannam@86: } cannam@86: vorbis_synthesis_restart(v); cannam@86: return 0; cannam@86: } cannam@86: cannam@86: /* Unlike in analysis, the window is only partially applied for each cannam@86: block. The time domain envelope is not yet handled at the point of cannam@86: calling (as it relies on the previous block). */ cannam@86: cannam@86: int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ cannam@86: vorbis_info *vi=v->vi; cannam@86: codec_setup_info *ci=vi->codec_setup; cannam@86: private_state *b=v->backend_state; cannam@86: int hs=ci->halfrate_flag; cannam@86: int i,j; cannam@86: cannam@86: if(!vb)return(OV_EINVAL); cannam@86: if(v->pcm_current>v->pcm_returned && v->pcm_returned!=-1)return(OV_EINVAL); cannam@86: cannam@86: v->lW=v->W; cannam@86: v->W=vb->W; cannam@86: v->nW=-1; cannam@86: cannam@86: if((v->sequence==-1)|| cannam@86: (v->sequence+1 != vb->sequence)){ cannam@86: v->granulepos=-1; /* out of sequence; lose count */ cannam@86: b->sample_count=-1; cannam@86: } cannam@86: cannam@86: v->sequence=vb->sequence; cannam@86: cannam@86: if(vb->pcm){ /* no pcm to process if vorbis_synthesis_trackonly cannam@86: was called on block */ cannam@86: int n=ci->blocksizes[v->W]>>(hs+1); cannam@86: int n0=ci->blocksizes[0]>>(hs+1); cannam@86: int n1=ci->blocksizes[1]>>(hs+1); cannam@86: cannam@86: int thisCenter; cannam@86: int prevCenter; cannam@86: cannam@86: v->glue_bits+=vb->glue_bits; cannam@86: v->time_bits+=vb->time_bits; cannam@86: v->floor_bits+=vb->floor_bits; cannam@86: v->res_bits+=vb->res_bits; cannam@86: cannam@86: if(v->centerW){ cannam@86: thisCenter=n1; cannam@86: prevCenter=0; cannam@86: }else{ cannam@86: thisCenter=0; cannam@86: prevCenter=n1; cannam@86: } cannam@86: cannam@86: /* v->pcm is now used like a two-stage double buffer. We don't want cannam@86: to have to constantly shift *or* adjust memory usage. Don't cannam@86: accept a new block until the old is shifted out */ cannam@86: cannam@86: for(j=0;jchannels;j++){ cannam@86: /* the overlap/add section */ cannam@86: if(v->lW){ cannam@86: if(v->W){ cannam@86: /* large/large */ cannam@86: float *w=_vorbis_window_get(b->window[1]-hs); cannam@86: float *pcm=v->pcm[j]+prevCenter; cannam@86: float *p=vb->pcm[j]; cannam@86: for(i=0;iwindow[0]-hs); cannam@86: float *pcm=v->pcm[j]+prevCenter+n1/2-n0/2; cannam@86: float *p=vb->pcm[j]; cannam@86: for(i=0;iW){ cannam@86: /* small/large */ cannam@86: float *w=_vorbis_window_get(b->window[0]-hs); cannam@86: float *pcm=v->pcm[j]+prevCenter; cannam@86: float *p=vb->pcm[j]+n1/2-n0/2; cannam@86: for(i=0;iwindow[0]-hs); cannam@86: float *pcm=v->pcm[j]+prevCenter; cannam@86: float *p=vb->pcm[j]; cannam@86: for(i=0;ipcm[j]+thisCenter; cannam@86: float *p=vb->pcm[j]+n; cannam@86: for(i=0;icenterW) cannam@86: v->centerW=0; cannam@86: else cannam@86: v->centerW=n1; cannam@86: cannam@86: /* deal with initial packet state; we do this using the explicit cannam@86: pcm_returned==-1 flag otherwise we're sensitive to first block cannam@86: being short or long */ cannam@86: cannam@86: if(v->pcm_returned==-1){ cannam@86: v->pcm_returned=thisCenter; cannam@86: v->pcm_current=thisCenter; cannam@86: }else{ cannam@86: v->pcm_returned=prevCenter; cannam@86: v->pcm_current=prevCenter+ cannam@86: ((ci->blocksizes[v->lW]/4+ cannam@86: ci->blocksizes[v->W]/4)>>hs); cannam@86: } cannam@86: cannam@86: } cannam@86: cannam@86: /* track the frame number... This is for convenience, but also cannam@86: making sure our last packet doesn't end with added padding. If cannam@86: the last packet is partial, the number of samples we'll have to cannam@86: return will be past the vb->granulepos. cannam@86: cannam@86: This is not foolproof! It will be confused if we begin cannam@86: decoding at the last page after a seek or hole. In that case, cannam@86: we don't have a starting point to judge where the last frame cannam@86: is. For this reason, vorbisfile will always try to make sure cannam@86: it reads the last two marked pages in proper sequence */ cannam@86: cannam@86: if(b->sample_count==-1){ cannam@86: b->sample_count=0; cannam@86: }else{ cannam@86: b->sample_count+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; cannam@86: } cannam@86: cannam@86: if(v->granulepos==-1){ cannam@86: if(vb->granulepos!=-1){ /* only set if we have a position to set to */ cannam@86: cannam@86: v->granulepos=vb->granulepos; cannam@86: cannam@86: /* is this a short page? */ cannam@86: if(b->sample_count>v->granulepos){ cannam@86: /* corner case; if this is both the first and last audio page, cannam@86: then spec says the end is cut, not beginning */ cannam@86: long extra=b->sample_count-vb->granulepos; cannam@86: cannam@86: /* we use ogg_int64_t for granule positions because a cannam@86: uint64 isn't universally available. Unfortunately, cannam@86: that means granposes can be 'negative' and result in cannam@86: extra being negative */ cannam@86: if(extra<0) cannam@86: extra=0; cannam@86: cannam@86: if(vb->eofflag){ cannam@86: /* trim the end */ cannam@86: /* no preceding granulepos; assume we started at zero (we'd cannam@86: have to in a short single-page stream) */ cannam@86: /* granulepos could be -1 due to a seek, but that would result cannam@86: in a long count, not short count */ cannam@86: cannam@86: /* Guard against corrupt/malicious frames that set EOP and cannam@86: a backdated granpos; don't rewind more samples than we cannam@86: actually have */ cannam@86: if(extra > (v->pcm_current - v->pcm_returned)<pcm_current - v->pcm_returned)<pcm_current-=extra>>hs; cannam@86: }else{ cannam@86: /* trim the beginning */ cannam@86: v->pcm_returned+=extra>>hs; cannam@86: if(v->pcm_returned>v->pcm_current) cannam@86: v->pcm_returned=v->pcm_current; cannam@86: } cannam@86: cannam@86: } cannam@86: cannam@86: } cannam@86: }else{ cannam@86: v->granulepos+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; cannam@86: if(vb->granulepos!=-1 && v->granulepos!=vb->granulepos){ cannam@86: cannam@86: if(v->granulepos>vb->granulepos){ cannam@86: long extra=v->granulepos-vb->granulepos; cannam@86: cannam@86: if(extra) cannam@86: if(vb->eofflag){ cannam@86: /* partial last frame. Strip the extra samples off */ cannam@86: cannam@86: /* Guard against corrupt/malicious frames that set EOP and cannam@86: a backdated granpos; don't rewind more samples than we cannam@86: actually have */ cannam@86: if(extra > (v->pcm_current - v->pcm_returned)<pcm_current - v->pcm_returned)<pcm_current-=extra>>hs; cannam@86: } /* else {Shouldn't happen *unless* the bitstream is out of cannam@86: spec. Either way, believe the bitstream } */ cannam@86: } /* else {Shouldn't happen *unless* the bitstream is out of cannam@86: spec. Either way, believe the bitstream } */ cannam@86: v->granulepos=vb->granulepos; cannam@86: } cannam@86: } cannam@86: cannam@86: /* Update, cleanup */ cannam@86: cannam@86: if(vb->eofflag)v->eofflag=1; cannam@86: return(0); cannam@86: cannam@86: } cannam@86: cannam@86: /* pcm==NULL indicates we just want the pending samples, no more */ cannam@86: int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm){ cannam@86: vorbis_info *vi=v->vi; cannam@86: cannam@86: if(v->pcm_returned>-1 && v->pcm_returnedpcm_current){ cannam@86: if(pcm){ cannam@86: int i; cannam@86: for(i=0;ichannels;i++) cannam@86: v->pcmret[i]=v->pcm[i]+v->pcm_returned; cannam@86: *pcm=v->pcmret; cannam@86: } cannam@86: return(v->pcm_current-v->pcm_returned); cannam@86: } cannam@86: return(0); cannam@86: } cannam@86: cannam@86: int vorbis_synthesis_read(vorbis_dsp_state *v,int n){ cannam@86: if(n && v->pcm_returned+n>v->pcm_current)return(OV_EINVAL); cannam@86: v->pcm_returned+=n; cannam@86: return(0); cannam@86: } cannam@86: cannam@86: /* intended for use with a specific vorbisfile feature; we want access cannam@86: to the [usually synthetic/postextrapolated] buffer and lapping at cannam@86: the end of a decode cycle, specifically, a half-short-block worth. cannam@86: This funtion works like pcmout above, except it will also expose cannam@86: this implicit buffer data not normally decoded. */ cannam@86: int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm){ cannam@86: vorbis_info *vi=v->vi; cannam@86: codec_setup_info *ci=vi->codec_setup; cannam@86: int hs=ci->halfrate_flag; cannam@86: cannam@86: int n=ci->blocksizes[v->W]>>(hs+1); cannam@86: int n0=ci->blocksizes[0]>>(hs+1); cannam@86: int n1=ci->blocksizes[1]>>(hs+1); cannam@86: int i,j; cannam@86: cannam@86: if(v->pcm_returned<0)return 0; cannam@86: cannam@86: /* our returned data ends at pcm_returned; because the synthesis pcm cannam@86: buffer is a two-fragment ring, that means our data block may be cannam@86: fragmented by buffering, wrapping or a short block not filling cannam@86: out a buffer. To simplify things, we unfragment if it's at all cannam@86: possibly needed. Otherwise, we'd need to call lapout more than cannam@86: once as well as hold additional dsp state. Opt for cannam@86: simplicity. */ cannam@86: cannam@86: /* centerW was advanced by blockin; it would be the center of the cannam@86: *next* block */ cannam@86: if(v->centerW==n1){ cannam@86: /* the data buffer wraps; swap the halves */ cannam@86: /* slow, sure, small */ cannam@86: for(j=0;jchannels;j++){ cannam@86: float *p=v->pcm[j]; cannam@86: for(i=0;ipcm_current-=n1; cannam@86: v->pcm_returned-=n1; cannam@86: v->centerW=0; cannam@86: } cannam@86: cannam@86: /* solidify buffer into contiguous space */ cannam@86: if((v->lW^v->W)==1){ cannam@86: /* long/short or short/long */ cannam@86: for(j=0;jchannels;j++){ cannam@86: float *s=v->pcm[j]; cannam@86: float *d=v->pcm[j]+(n1-n0)/2; cannam@86: for(i=(n1+n0)/2-1;i>=0;--i) cannam@86: d[i]=s[i]; cannam@86: } cannam@86: v->pcm_returned+=(n1-n0)/2; cannam@86: v->pcm_current+=(n1-n0)/2; cannam@86: }else{ cannam@86: if(v->lW==0){ cannam@86: /* short/short */ cannam@86: for(j=0;jchannels;j++){ cannam@86: float *s=v->pcm[j]; cannam@86: float *d=v->pcm[j]+n1-n0; cannam@86: for(i=n0-1;i>=0;--i) cannam@86: d[i]=s[i]; cannam@86: } cannam@86: v->pcm_returned+=n1-n0; cannam@86: v->pcm_current+=n1-n0; cannam@86: } cannam@86: } cannam@86: cannam@86: if(pcm){ cannam@86: int i; cannam@86: for(i=0;ichannels;i++) cannam@86: v->pcmret[i]=v->pcm[i]+v->pcm_returned; cannam@86: *pcm=v->pcmret; cannam@86: } cannam@86: cannam@86: return(n1+n-v->pcm_returned); cannam@86: cannam@86: } cannam@86: cannam@86: float *vorbis_window(vorbis_dsp_state *v,int W){ cannam@86: vorbis_info *vi=v->vi; cannam@86: codec_setup_info *ci=vi->codec_setup; cannam@86: int hs=ci->halfrate_flag; cannam@86: private_state *b=v->backend_state; cannam@86: cannam@86: if(b->window[W]-1<0)return NULL; cannam@86: return _vorbis_window_get(b->window[W]-hs); cannam@86: }