michael@0: /******************************************************************** michael@0: * * michael@0: * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * michael@0: * * michael@0: * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * michael@0: * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * michael@0: * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * michael@0: * * michael@0: * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * michael@0: * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * michael@0: * * michael@0: ******************************************************************** michael@0: michael@0: function: PCM data vector blocking, windowing and dis/reassembly michael@0: michael@0: ********************************************************************/ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "ivorbiscodec.h" michael@0: #include "codec_internal.h" michael@0: michael@0: #include "window.h" michael@0: #include "registry.h" michael@0: #include "misc.h" michael@0: michael@0: static int ilog(unsigned int v){ michael@0: int ret=0; michael@0: if(v)--v; michael@0: while(v){ michael@0: ret++; michael@0: v>>=1; michael@0: } michael@0: return(ret); michael@0: } michael@0: michael@0: /* pcm accumulator examples (not exhaustive): michael@0: michael@0: <-------------- lW ----------------> michael@0: <--------------- W ----------------> michael@0: : .....|..... _______________ | michael@0: : .''' | '''_--- | |\ | michael@0: :.....''' |_____--- '''......| | \_______| michael@0: :.................|__________________|_______|__|______| michael@0: |<------ Sl ------>| > Sr < |endW michael@0: |beginSl |endSl | |endSr michael@0: |beginW |endlW |beginSr michael@0: michael@0: michael@0: |< lW >| michael@0: <--------------- W ----------------> michael@0: | | .. ______________ | michael@0: | | ' `/ | ---_ | michael@0: |___.'___/`. | ---_____| michael@0: |_______|__|_______|_________________| michael@0: | >|Sl|< |<------ Sr ----->|endW michael@0: | | |endSl |beginSr |endSr michael@0: |beginW | |endlW michael@0: mult[0] |beginSl mult[n] michael@0: michael@0: <-------------- lW -----------------> michael@0: |<--W-->| michael@0: : .............. ___ | | michael@0: : .''' |`/ \ | | michael@0: :.....''' |/`....\|...| michael@0: :.........................|___|___|___| michael@0: |Sl |Sr |endW michael@0: | | |endSr michael@0: | |beginSr michael@0: | |endSl michael@0: |beginSl michael@0: |beginW michael@0: */ michael@0: michael@0: /* block abstraction setup *********************************************/ michael@0: michael@0: #ifndef WORD_ALIGN michael@0: #define WORD_ALIGN 8 michael@0: #endif michael@0: michael@0: int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){ michael@0: memset(vb,0,sizeof(*vb)); michael@0: vb->vd=v; michael@0: vb->localalloc=0; michael@0: vb->localstore=NULL; michael@0: michael@0: return(0); michael@0: } michael@0: michael@0: void *_vorbis_block_alloc(vorbis_block *vb,long bytes){ michael@0: bytes=(bytes+(WORD_ALIGN-1)) & ~(WORD_ALIGN-1); michael@0: if(bytes+vb->localtop>vb->localalloc){ michael@0: /* can't just _ogg_realloc... there are outstanding pointers */ michael@0: if(vb->localstore){ michael@0: struct alloc_chain *link=(struct alloc_chain *)_ogg_malloc(sizeof(*link)); michael@0: vb->totaluse+=vb->localtop; michael@0: link->next=vb->reap; michael@0: link->ptr=vb->localstore; michael@0: vb->reap=link; michael@0: } michael@0: /* highly conservative */ michael@0: vb->localalloc=bytes; michael@0: vb->localstore=_ogg_malloc(vb->localalloc); michael@0: vb->localtop=0; michael@0: } michael@0: { michael@0: void *ret=(void *)(((char *)vb->localstore)+vb->localtop); michael@0: vb->localtop+=bytes; michael@0: return ret; michael@0: } michael@0: } michael@0: michael@0: /* reap the chain, pull the ripcord */ michael@0: void _vorbis_block_ripcord(vorbis_block *vb){ michael@0: /* reap the chain */ michael@0: struct alloc_chain *reap=vb->reap; michael@0: while(reap){ michael@0: struct alloc_chain *next=reap->next; michael@0: _ogg_free(reap->ptr); michael@0: memset(reap,0,sizeof(*reap)); michael@0: _ogg_free(reap); michael@0: reap=next; michael@0: } michael@0: /* consolidate storage */ michael@0: if(vb->totaluse){ michael@0: vb->localstore=_ogg_realloc(vb->localstore,vb->totaluse+vb->localalloc); michael@0: vb->localalloc+=vb->totaluse; michael@0: vb->totaluse=0; michael@0: } michael@0: michael@0: /* pull the ripcord */ michael@0: vb->localtop=0; michael@0: vb->reap=NULL; michael@0: } michael@0: michael@0: int vorbis_block_clear(vorbis_block *vb){ michael@0: _vorbis_block_ripcord(vb); michael@0: if(vb->localstore)_ogg_free(vb->localstore); michael@0: michael@0: memset(vb,0,sizeof(*vb)); michael@0: return(0); michael@0: } michael@0: michael@0: static int _vds_init(vorbis_dsp_state *v,vorbis_info *vi){ michael@0: int i; michael@0: codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; michael@0: private_state *b=NULL; michael@0: michael@0: if(ci==NULL) return 1; michael@0: michael@0: memset(v,0,sizeof(*v)); michael@0: b=(private_state *)(v->backend_state=_ogg_calloc(1,sizeof(*b))); michael@0: michael@0: v->vi=vi; michael@0: b->modebits=ilog(ci->modes); michael@0: michael@0: /* Vorbis I uses only window type 0 */ michael@0: b->window[0]=_vorbis_window(0,ci->blocksizes[0]/2); michael@0: b->window[1]=_vorbis_window(0,ci->blocksizes[1]/2); michael@0: michael@0: /* finish the codebooks */ michael@0: if(!ci->fullbooks){ michael@0: ci->fullbooks=(codebook *)_ogg_calloc(ci->books,sizeof(*ci->fullbooks)); michael@0: for(i=0;ibooks;i++){ michael@0: if(ci->book_param[i]==NULL) michael@0: goto abort_books; michael@0: if(vorbis_book_init_decode(ci->fullbooks+i,ci->book_param[i])) michael@0: goto abort_books; michael@0: /* decode codebooks are now standalone after init */ michael@0: vorbis_staticbook_destroy(ci->book_param[i]); michael@0: ci->book_param[i]=NULL; michael@0: } michael@0: } michael@0: michael@0: v->pcm_storage=ci->blocksizes[1]; michael@0: v->pcm=(ogg_int32_t **)_ogg_malloc(vi->channels*sizeof(*v->pcm)); michael@0: v->pcmret=(ogg_int32_t **)_ogg_malloc(vi->channels*sizeof(*v->pcmret)); michael@0: for(i=0;ichannels;i++) michael@0: v->pcm[i]=(ogg_int32_t *)_ogg_calloc(v->pcm_storage,sizeof(*v->pcm[i])); michael@0: michael@0: /* all 1 (large block) or 0 (small block) */ michael@0: /* explicitly set for the sake of clarity */ michael@0: v->lW=0; /* previous window size */ michael@0: v->W=0; /* current window size */ michael@0: michael@0: /* initialize all the mapping/backend lookups */ michael@0: b->mode=(vorbis_look_mapping **)_ogg_calloc(ci->modes,sizeof(*b->mode)); michael@0: for(i=0;imodes;i++){ michael@0: int mapnum=ci->mode_param[i]->mapping; michael@0: int maptype=ci->map_type[mapnum]; michael@0: b->mode[i]=_mapping_P[maptype]->look(v,ci->mode_param[i], michael@0: ci->map_param[mapnum]); michael@0: } michael@0: return 0; michael@0: abort_books: michael@0: for(i=0;ibooks;i++){ michael@0: if(ci->book_param[i]!=NULL){ michael@0: vorbis_staticbook_destroy(ci->book_param[i]); michael@0: ci->book_param[i]=NULL; michael@0: } michael@0: } michael@0: vorbis_dsp_clear(v); michael@0: return -1; michael@0: } michael@0: michael@0: int vorbis_synthesis_restart(vorbis_dsp_state *v){ michael@0: vorbis_info *vi=v->vi; michael@0: codec_setup_info *ci; michael@0: michael@0: if(!v->backend_state)return -1; michael@0: if(!vi)return -1; michael@0: ci=vi->codec_setup; michael@0: if(!ci)return -1; michael@0: michael@0: v->centerW=ci->blocksizes[1]/2; michael@0: v->pcm_current=v->centerW; michael@0: michael@0: v->pcm_returned=-1; michael@0: v->granulepos=-1; michael@0: v->sequence=-1; michael@0: ((private_state *)(v->backend_state))->sample_count=-1; michael@0: michael@0: return(0); michael@0: } michael@0: michael@0: int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){ michael@0: if(_vds_init(v,vi))return 1; michael@0: vorbis_synthesis_restart(v); michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: void vorbis_dsp_clear(vorbis_dsp_state *v){ michael@0: int i; michael@0: if(v){ michael@0: vorbis_info *vi=v->vi; michael@0: codec_setup_info *ci=(codec_setup_info *)(vi?vi->codec_setup:NULL); michael@0: private_state *b=(private_state *)v->backend_state; michael@0: michael@0: if(v->pcm){ michael@0: for(i=0;ichannels;i++) michael@0: if(v->pcm[i])_ogg_free(v->pcm[i]); michael@0: _ogg_free(v->pcm); michael@0: if(v->pcmret)_ogg_free(v->pcmret); michael@0: } michael@0: michael@0: /* free mode lookups; these are actually vorbis_look_mapping structs */ michael@0: if(ci){ michael@0: for(i=0;imodes;i++){ michael@0: int mapnum=ci->mode_param[i]->mapping; michael@0: int maptype=ci->map_type[mapnum]; michael@0: if(b && b->mode)_mapping_P[maptype]->free_look(b->mode[i]); michael@0: } michael@0: } michael@0: michael@0: if(b){ michael@0: if(b->mode)_ogg_free(b->mode); michael@0: _ogg_free(b); michael@0: } michael@0: michael@0: memset(v,0,sizeof(*v)); michael@0: } michael@0: } michael@0: michael@0: /* Unlike in analysis, the window is only partially applied for each michael@0: block. The time domain envelope is not yet handled at the point of michael@0: calling (as it relies on the previous block). */ michael@0: michael@0: int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ michael@0: vorbis_info *vi=v->vi; michael@0: codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; michael@0: private_state *b=v->backend_state; michael@0: int i,j; michael@0: michael@0: if(v->pcm_current>v->pcm_returned && v->pcm_returned!=-1)return(OV_EINVAL); michael@0: michael@0: v->lW=v->W; michael@0: v->W=vb->W; michael@0: v->nW=-1; michael@0: michael@0: if((v->sequence==-1)|| michael@0: (v->sequence+1 != vb->sequence)){ michael@0: v->granulepos=-1; /* out of sequence; lose count */ michael@0: b->sample_count=-1; michael@0: } michael@0: michael@0: v->sequence=vb->sequence; michael@0: michael@0: if(vb->pcm){ /* no pcm to process if vorbis_synthesis_trackonly michael@0: was called on block */ michael@0: int n=ci->blocksizes[v->W]/2; michael@0: int n0=ci->blocksizes[0]/2; michael@0: int n1=ci->blocksizes[1]/2; michael@0: michael@0: int thisCenter; michael@0: int prevCenter; michael@0: michael@0: if(v->centerW){ michael@0: thisCenter=n1; michael@0: prevCenter=0; michael@0: }else{ michael@0: thisCenter=0; michael@0: prevCenter=n1; michael@0: } michael@0: michael@0: /* v->pcm is now used like a two-stage double buffer. We don't want michael@0: to have to constantly shift *or* adjust memory usage. Don't michael@0: accept a new block until the old is shifted out */ michael@0: michael@0: /* overlap/add PCM */ michael@0: michael@0: for(j=0;jchannels;j++){ michael@0: /* the overlap/add section */ michael@0: if(v->lW){ michael@0: if(v->W){ michael@0: /* large/large */ michael@0: ogg_int32_t *pcm=v->pcm[j]+prevCenter; michael@0: ogg_int32_t *p=vb->pcm[j]; michael@0: for(i=0;ipcm[j]+prevCenter+n1/2-n0/2; michael@0: ogg_int32_t *p=vb->pcm[j]; michael@0: for(i=0;iW){ michael@0: /* small/large */ michael@0: ogg_int32_t *pcm=v->pcm[j]+prevCenter; michael@0: ogg_int32_t *p=vb->pcm[j]+n1/2-n0/2; michael@0: for(i=0;ipcm[j]+prevCenter; michael@0: ogg_int32_t *p=vb->pcm[j]; michael@0: for(i=0;ipcm[j]+thisCenter; michael@0: ogg_int32_t *p=vb->pcm[j]+n; michael@0: for(i=0;icenterW) michael@0: v->centerW=0; michael@0: else michael@0: v->centerW=n1; michael@0: michael@0: /* deal with initial packet state; we do this using the explicit michael@0: pcm_returned==-1 flag otherwise we're sensitive to first block michael@0: being short or long */ michael@0: michael@0: if(v->pcm_returned==-1){ michael@0: v->pcm_returned=thisCenter; michael@0: v->pcm_current=thisCenter; michael@0: }else{ michael@0: v->pcm_returned=prevCenter; michael@0: v->pcm_current=prevCenter+ michael@0: ci->blocksizes[v->lW]/4+ michael@0: ci->blocksizes[v->W]/4; michael@0: } michael@0: michael@0: } michael@0: michael@0: /* track the frame number... This is for convenience, but also michael@0: making sure our last packet doesn't end with added padding. If michael@0: the last packet is partial, the number of samples we'll have to michael@0: return will be past the vb->granulepos. michael@0: michael@0: This is not foolproof! It will be confused if we begin michael@0: decoding at the last page after a seek or hole. In that case, michael@0: we don't have a starting point to judge where the last frame michael@0: is. For this reason, vorbisfile will always try to make sure michael@0: it reads the last two marked pages in proper sequence */ michael@0: michael@0: if(b->sample_count==-1){ michael@0: b->sample_count=0; michael@0: }else{ michael@0: b->sample_count+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; michael@0: } michael@0: michael@0: if(v->granulepos==-1){ michael@0: if(vb->granulepos!=-1){ /* only set if we have a position to set to */ michael@0: michael@0: v->granulepos=vb->granulepos; michael@0: michael@0: /* is this a short page? */ michael@0: if(b->sample_count>v->granulepos){ michael@0: /* corner case; if this is both the first and last audio page, michael@0: then spec says the end is cut, not beginning */ michael@0: long extra=b->sample_count-vb->granulepos; michael@0: michael@0: /* we use ogg_int64_t for granule positions because a michael@0: uint64 isn't universally available. Unfortunately, michael@0: that means granposes can be 'negative' and result in michael@0: extra being negative */ michael@0: if(extra<0) michael@0: extra=0; michael@0: michael@0: if(vb->eofflag){ michael@0: /* trim the end */ michael@0: /* no preceeding granulepos; assume we started at zero (we'd michael@0: have to in a short single-page stream) */ michael@0: /* granulepos could be -1 due to a seek, but that would result michael@0: in a long coun`t, not short count */ michael@0: michael@0: /* Guard against corrupt/malicious frames that set EOP and michael@0: a backdated granpos; don't rewind more samples than we michael@0: actually have */ michael@0: if(extra > v->pcm_current - v->pcm_returned) michael@0: extra = v->pcm_current - v->pcm_returned; michael@0: michael@0: v->pcm_current-=extra; michael@0: }else{ michael@0: /* trim the beginning */ michael@0: v->pcm_returned+=extra; michael@0: if(v->pcm_returned>v->pcm_current) michael@0: v->pcm_returned=v->pcm_current; michael@0: } michael@0: michael@0: } michael@0: michael@0: } michael@0: }else{ michael@0: v->granulepos+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; michael@0: if(vb->granulepos!=-1 && v->granulepos!=vb->granulepos){ michael@0: michael@0: if(v->granulepos>vb->granulepos){ michael@0: long extra=v->granulepos-vb->granulepos; michael@0: michael@0: if(extra) michael@0: if(vb->eofflag){ michael@0: /* partial last frame. Strip the extra samples off */ michael@0: michael@0: /* Guard against corrupt/malicious frames that set EOP and michael@0: a backdated granpos; don't rewind more samples than we michael@0: actually have */ michael@0: if(extra > v->pcm_current - v->pcm_returned) michael@0: extra = v->pcm_current - v->pcm_returned; michael@0: michael@0: /* we use ogg_int64_t for granule positions because a michael@0: uint64 isn't universally available. Unfortunately, michael@0: that means granposes can be 'negative' and result in michael@0: extra being negative */ michael@0: if(extra<0) michael@0: extra=0; michael@0: michael@0: v->pcm_current-=extra; michael@0: michael@0: } /* else {Shouldn't happen *unless* the bitstream is out of michael@0: spec. Either way, believe the bitstream } */ michael@0: } /* else {Shouldn't happen *unless* the bitstream is out of michael@0: spec. Either way, believe the bitstream } */ michael@0: v->granulepos=vb->granulepos; michael@0: } michael@0: } michael@0: michael@0: /* Update, cleanup */ michael@0: michael@0: if(vb->eofflag)v->eofflag=1; michael@0: return(0); michael@0: } michael@0: michael@0: /* pcm==NULL indicates we just want the pending samples, no more */ michael@0: int vorbis_synthesis_pcmout(vorbis_dsp_state *v,ogg_int32_t ***pcm){ michael@0: vorbis_info *vi=v->vi; michael@0: if(v->pcm_returned>-1 && v->pcm_returnedpcm_current){ michael@0: if(pcm){ michael@0: int i; michael@0: for(i=0;ichannels;i++) michael@0: v->pcmret[i]=v->pcm[i]+v->pcm_returned; michael@0: *pcm=v->pcmret; michael@0: } michael@0: return(v->pcm_current-v->pcm_returned); michael@0: } michael@0: return(0); michael@0: } michael@0: michael@0: int vorbis_synthesis_read(vorbis_dsp_state *v,int bytes){ michael@0: if(bytes && v->pcm_returned+bytes>v->pcm_current)return(OV_EINVAL); michael@0: v->pcm_returned+=bytes; michael@0: return(0); michael@0: } michael@0: