| /******************************************************************** |
| * * |
| * THIS FILE IS PART OF THE OggVorbis 'TREMOR' 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 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * |
| * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * |
| * * |
| ******************************************************************** |
| |
| function: PCM data vector blocking, windowing and dis/reassembly |
| |
| ********************************************************************/ |
| |
| #include "config-tremor.h" |
| #include <stdio.h> |
| #include <string.h> |
| #include "ogg.h" |
| #include "ivorbiscodec.h" |
| #include "codec_internal.h" |
| |
| #include "window.h" |
| #include "registry.h" |
| #include "misc.h" |
| |
| static int ilog(unsigned int v){ |
| int ret=0; |
| if(v)--v; |
| while(v){ |
| ret++; |
| v>>=1; |
| } |
| return(ret); |
| } |
| |
| /* pcm accumulator examples (not exhaustive): |
| |
| <-------------- lW ----------------> |
| <--------------- W ----------------> |
| : .....|..... _______________ | |
| : .''' | '''_--- | |\ | |
| :.....''' |_____--- '''......| | \_______| |
| :.................|__________________|_______|__|______| |
| |<------ Sl ------>| > Sr < |endW |
| |beginSl |endSl | |endSr |
| |beginW |endlW |beginSr |
| |
| |
| |< lW >| |
| <--------------- W ----------------> |
| | | .. ______________ | |
| | | ' `/ | ---_ | |
| |___.'___/`. | ---_____| |
| |_______|__|_______|_________________| |
| | >|Sl|< |<------ Sr ----->|endW |
| | | |endSl |beginSr |endSr |
| |beginW | |endlW |
| mult[0] |beginSl mult[n] |
| |
| <-------------- lW -----------------> |
| |<--W-->| |
| : .............. ___ | | |
| : .''' |`/ \ | | |
| :.....''' |/`....\|...| |
| :.........................|___|___|___| |
| |Sl |Sr |endW |
| | | |endSr |
| | |beginSr |
| | |endSl |
| |beginSl |
| |beginW |
| */ |
| |
| /* block abstraction setup *********************************************/ |
| |
| #ifndef WORD_ALIGN |
| #define WORD_ALIGN 8 |
| #endif |
| |
| int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){ |
| memset(vb,0,sizeof(*vb)); |
| vb->vd=v; |
| vb->localalloc=0; |
| vb->localstore=NULL; |
| |
| 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=(struct alloc_chain *)_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; |
| } |
| { |
| void *ret=(void *)(((char *)vb->localstore)+vb->localtop); |
| vb->localtop+=bytes; |
| return ret; |
| } |
| } |
| |
| /* 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; |
| } |
| |
| /* pull the ripcord */ |
| vb->localtop=0; |
| vb->reap=NULL; |
| } |
| |
| int vorbis_block_clear(vorbis_block *vb){ |
| _vorbis_block_ripcord(vb); |
| if(vb->localstore)_ogg_free(vb->localstore); |
| |
| memset(vb,0,sizeof(*vb)); |
| return(0); |
| } |
| |
| static int _vds_init(vorbis_dsp_state *v,vorbis_info *vi){ |
| int i; |
| codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; |
| private_state *b=NULL; |
| |
| memset(v,0,sizeof(*v)); |
| b=(private_state *)(v->backend_state=_ogg_calloc(1,sizeof(*b))); |
| |
| v->vi=vi; |
| b->modebits=ilog(ci->modes); |
| |
| /* Vorbis I uses only window type 0 */ |
| b->window[0]=_vorbis_window(0,ci->blocksizes[0]/2); |
| b->window[1]=_vorbis_window(0,ci->blocksizes[1]/2); |
| |
| /* finish the codebooks */ |
| if(!ci->fullbooks){ |
| ci->fullbooks=(codebook *)_ogg_calloc(ci->books,sizeof(*ci->fullbooks)); |
| for(i=0;i<ci->books;i++){ |
| vorbis_book_init_decode(ci->fullbooks+i,ci->book_param[i]); |
| /* decode codebooks are now standalone after init */ |
| vorbis_staticbook_destroy(ci->book_param[i]); |
| ci->book_param[i]=NULL; |
| } |
| } |
| |
| v->pcm_storage=ci->blocksizes[1]; |
| v->pcm=(ogg_int32_t **)_ogg_malloc(vi->channels*sizeof(*v->pcm)); |
| v->pcmret=(ogg_int32_t **)_ogg_malloc(vi->channels*sizeof(*v->pcmret)); |
| |
| for(i=0;i<vi->channels;i++) |
| v->pcm[i]=(ogg_int32_t *)_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 */ |
| |
| /* initialize all the mapping/backend lookups */ |
| b->mode=(vorbis_look_mapping **)_ogg_calloc(ci->modes,sizeof(*b->mode)); |
| for(i=0;i<ci->modes;i++){ |
| int mapnum=ci->mode_param[i]->mapping; |
| int maptype=ci->map_type[mapnum]; |
| b->mode[i]=_mapping_P[maptype]->look(v,ci->mode_param[i], |
| ci->map_param[mapnum]); |
| } |
| return(0); |
| } |
| |
| int vorbis_synthesis_restart(vorbis_dsp_state *v){ |
| vorbis_info *vi=v->vi; |
| codec_setup_info *ci; |
| |
| if(!v->backend_state)return -1; |
| if(!vi)return -1; |
| ci=vi->codec_setup; |
| if(!ci)return -1; |
| |
| v->centerW=ci->blocksizes[1]/2; |
| v->pcm_current=v->centerW; |
| |
| v->pcm_returned=-1; |
| v->granulepos=-1; |
| v->sequence=-1; |
| ((private_state *)(v->backend_state))->sample_count=-1; |
| |
| return(0); |
| } |
| |
| int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){ |
| _vds_init(v,vi); |
| vorbis_synthesis_restart(v); |
| |
| return(0); |
| } |
| |
| void vorbis_dsp_clear(vorbis_dsp_state *v){ |
| int i; |
| if(v){ |
| vorbis_info *vi=v->vi; |
| codec_setup_info *ci=(codec_setup_info *)(vi?vi->codec_setup:NULL); |
| private_state *b=(private_state *)v->backend_state; |
| |
| if(v->pcm){ |
| for(i=0;i<vi->channels;i++) |
| if(v->pcm[i])_ogg_free(v->pcm[i]); |
| _ogg_free(v->pcm); |
| if(v->pcmret)_ogg_free(v->pcmret); |
| } |
| |
| /* free mode lookups; these are actually vorbis_look_mapping structs */ |
| if(ci){ |
| for(i=0;i<ci->modes;i++){ |
| int mapnum=ci->mode_param[i]->mapping; |
| int maptype=ci->map_type[mapnum]; |
| if(b && b->mode)_mapping_P[maptype]->free_look(b->mode[i]); |
| } |
| } |
| |
| if(b){ |
| if(b->mode)_ogg_free(b->mode); |
| _ogg_free(b); |
| } |
| |
| memset(v,0,sizeof(*v)); |
| } |
| } |
| |
| /* 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) |
| ICODE_ATTR_TREMOR_NOT_MDCT; |
| int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ |
| vorbis_info *vi=v->vi; |
| codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; |
| private_state *b=v->backend_state; |
| int j; |
| |
| if(v->pcm_current>v->pcm_returned && v->pcm_returned!=-1)return(OV_EINVAL); |
| |
| v->lW=v->W; |
| v->W=vb->W; |
| v->nW=-1; |
| |
| if((v->sequence==-1)|| |
| (v->sequence+1 != vb->sequence)){ |
| v->granulepos=-1; /* out of sequence; lose count */ |
| b->sample_count=-1; |
| } |
| |
| v->sequence=vb->sequence; |
| |
| if(vb->pcm){ /* no pcm to process if vorbis_synthesis_trackonly |
| was called on block */ |
| int n=ci->blocksizes[v->W]/2; |
| int n0=ci->blocksizes[0]/2; |
| int n1=ci->blocksizes[1]/2; |
| |
| int thisCenter; |
| int prevCenter; |
| |
| if(v->centerW){ |
| thisCenter=n1; |
| prevCenter=0; |
| }else{ |
| thisCenter=0; |
| prevCenter=n1; |
| } |
| |
| /* 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 */ |
| |
| /* overlap/add PCM */ |
| |
| for(j=0;j<vi->channels;j++){ |
| /* the overlap/add section */ |
| if(v->lW){ |
| if(v->W){ |
| /* large/large */ |
| ogg_int32_t *pcm=v->pcm[j]+prevCenter; |
| ogg_int32_t *p=vb->pcm[j]; |
| vect_add(pcm, p, n1); |
| }else{ |
| /* large/small */ |
| ogg_int32_t *pcm=v->pcm[j]+prevCenter+n1/2-n0/2; |
| ogg_int32_t *p=vb->pcm[j]; |
| vect_add(pcm, p, n0); |
| } |
| }else{ |
| if(v->W){ |
| /* small/large */ |
| ogg_int32_t *pcm=v->pcm[j]+prevCenter; |
| ogg_int32_t *p=vb->pcm[j]+n1/2-n0/2; |
| vect_add(pcm, p, n0); |
| vect_copy(&pcm[n0], &p[n0], n1/2-n0/2); |
| }else{ |
| /* small/small */ |
| ogg_int32_t *pcm=v->pcm[j]+prevCenter; |
| ogg_int32_t *p=vb->pcm[j]; |
| vect_add(pcm, p, n0); |
| } |
| } |
| |
| /* the copy section */ |
| { |
| ogg_int32_t *pcm=v->pcm[j]+thisCenter; |
| ogg_int32_t *p=vb->pcm[j]+n; |
| vect_copy(pcm, p, n); |
| } |
| } |
| |
| 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; |
| } |
| |
| } |
| |
| /* 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. |
| |
| 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 */ |
| if(vb->eofflag){ |
| /* trim the end */ |
| /* no preceeding 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 coun`t, not short count */ |
| |
| v->pcm_current-=(b->sample_count-v->granulepos); |
| }else{ |
| /* trim the beginning */ |
| v->pcm_returned+=(b->sample_count-v->granulepos); |
| if(v->pcm_returned>v->pcm_current) |
| v->pcm_returned=v->pcm_current; |
| } |
| |
| } |
| |
| } |
| }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 */ |
| v->pcm_current-=extra; |
| } /* 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 */ |
| |
| if(vb->eofflag)v->eofflag=1; |
| return(0); |
| } |
| |
| /* pcm==NULL indicates we just want the pending samples, no more */ |
| int vorbis_synthesis_pcmout(vorbis_dsp_state *v,ogg_int32_t ***pcm) ICODE_ATTR; |
| int vorbis_synthesis_pcmout(vorbis_dsp_state *v,ogg_int32_t ***pcm){ |
| vorbis_info *vi=v->vi; |
| if(v->pcm_returned>-1 && v->pcm_returned<v->pcm_current){ |
| if(pcm){ |
| int i; |
| for(i=0;i<vi->channels;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_read(vorbis_dsp_state *v,int bytes){ |
| if(bytes && v->pcm_returned+bytes>v->pcm_current)return(OV_EINVAL); |
| v->pcm_returned+=bytes; |
| return(0); |
| } |
| |