| /******************************************************************** |
| * * |
| * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * |
| * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * |
| * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * |
| * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * |
| * * |
| * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * |
| * by the Xiph.Org Foundation http://www.xiph.org/ * |
| * * |
| ******************************************************************** |
| |
| function: PCM data vector blocking, windowing and dis/reassembly |
| last mod: $Id: block.c 16330 2009-07-24 01:58:50Z xiphmont $ |
| |
| Handle windowing, overlap-add, etc of the PCM vectors. This is made |
| more amusing by Vorbis' current two allowed block sizes. |
| |
| ********************************************************************/ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ogg/ogg.h> |
| #include "vorbis/codec.h" |
| #include "codec_internal.h" |
| |
| #include "window.h" |
| #include "mdct.h" |
| #include "lpc.h" |
| #include "registry.h" |
| #include "misc.h" |
| |
| static int ilog2(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){ |
| 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;i<PACKETBLOBS;i++){ |
| if(i==PACKETBLOBS/2){ |
| vbi->packetblob[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; |
| } |
| { |
| 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){ |
| int i; |
| vorbis_block_internal *vbi=vb->internal; |
| |
| _vorbis_block_ripcord(vb); |
| if(vb->localstore)_ogg_free(vb->localstore); |
| |
| if(vbi){ |
| for(i=0;i<PACKETBLOBS;i++){ |
| oggpack_writeclear(vbi->packetblob[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) 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=ilog2(ci->modes); |
| |
| 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 */ |
| b->window[0]=ilog2(ci->blocksizes[0])-6; |
| b->window[1]=ilog2(ci->blocksizes[1])-6; |
| |
| 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;i<ci->books;i++) |
| vorbis_book_init_encode(ci->fullbooks+i,ci->book_param[i]); |
| } |
| |
| b->psy=_ogg_calloc(ci->psys,sizeof(*b->psy)); |
| for(i=0;i<ci->psys;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;i<ci->books;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;i<vi->channels;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 */ |
| v->centerW=ci->blocksizes[1]/2; |
| |
| v->pcm_current=v->centerW; |
| |
| /* 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;i<ci->floors;i++) |
| b->flr[i]=_floor_P[ci->floor_type[i]]-> |
| look(v,ci->floor_param[i]); |
| |
| for(i=0;i<ci->residues;i++) |
| b->residue[i]=_residue_P[ci->residue_type[i]]-> |
| look(v,ci->residue_param[i]); |
| |
| return 0; |
| abort_books: |
| for(i=0;i<ci->books;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){ |
| 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_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;i<ci->floors;i++) |
| _floor_P[ci->floor_type[i]]-> |
| free_look(b->flr[i]); |
| _ogg_free(b->flr); |
| } |
| if(b->residue){ |
| if(ci) |
| for(i=0;i<ci->residues;i++) |
| _residue_P[ci->residue_type[i]]-> |
| free_look(b->residue[i]); |
| _ogg_free(b->residue); |
| } |
| if(b->psy){ |
| if(ci) |
| for(i=0;i<ci->psys;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->pcm){ |
| if(vi) |
| 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); |
| } |
| |
| 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); |
| } |
| |
| memset(v,0,sizeof(*v)); |
| } |
| } |
| |
| 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; |
| |
| for(i=0;i<vi->channels;i++){ |
| v->pcm[i]=_ogg_realloc(v->pcm[i],v->pcm_storage*sizeof(*v->pcm[i])); |
| } |
| } |
| |
| for(i=0;i<vi->channels;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;i<v->vi->channels;i++){ |
| /* need to run the extrapolation in reverse! */ |
| for(j=0;j<v->pcm_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;j<v->pcm_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 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. */ |
| |
| vorbis_analysis_buffer(v,ci->blocksizes[1]*3); |
| v->eofflag=v->pcm_current; |
| v->pcm_current+=ci->blocksizes[1]*3; |
| |
| for(i=0;i<vi->channels;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(OV_EINVAL); |
| |
| v->pcm_current+=vals; |
| |
| /* 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); |
| |
| } |
| 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; |
| 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); |
| |
| /* 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 */ |
| |
| /* 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(v->eofflag==0)return(0); /* not enough data currently to search for a |
| full long block */ |
| v->nW=0; |
| }else{ |
| |
| if(ci->blocksizes[0]==ci->blocksizes[1]) |
| v->nW=0; |
| else |
| v->nW=bp; |
| } |
| } |
| |
| centerNext=v->centerW+ci->blocksizes[v->W]/4+ci->blocksizes[v->nW]/4; |
| |
| { |
| /* center of next block + next block maximum right side. */ |
| |
| long blockbound=centerNext+ci->blocksizes[v->nW]/2; |
| if(v->pcm_current<blockbound)return(0); /* not enough data yet; |
| although this check is |
| less strict that the |
| _ve_envelope_search, |
| the search is not run |
| if we only use one |
| block size */ |
| |
| |
| } |
| |
| /* fill in the block. Note that for a short window, lW and nW are *short* |
| regardless of actual settings in the stream */ |
| |
| _vorbis_block_ripcord(vb); |
| vb->lW=v->lW; |
| vb->W=v->W; |
| vb->nW=v->nW; |
| |
| 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,"|");*/ |
| |
| }else{ |
| vbi->blocktype=BLOCKTYPE_PADDING; |
| /*fprintf(stderr,".");*/ |
| |
| } |
| } |
| |
| 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;i<vi->channels;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])); |
| */ |
| |
| } |
| |
| /* handle eof detection: eof==0 means that we've not yet received EOF |
| eof>0 marks the last 'real' sample in pcm[] |
| eof<0 'no more to do'; doesn't get here */ |
| |
| if(v->eofflag){ |
| if(v->centerW>=v->eofflag){ |
| v->eofflag=-1; |
| vb->eofflag=1; |
| return(1); |
| } |
| } |
| |
| /* advance storage vectors and clean up */ |
| { |
| int new_centerNext=ci->blocksizes[1]/2; |
| int movementW=centerNext-new_centerNext; |
| |
| if(movementW>0){ |
| |
| _ve_envelope_shift(b->ve,movementW); |
| v->pcm_current-=movementW; |
| |
| for(i=0;i<vi->channels;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; |
| |
| 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_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); |
| } |
| |
| 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; |
| |
| if(!vb)return(OV_EINVAL); |
| 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]>>(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; |
| } |
| |
| /* 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;j<vi->channels;j++){ |
| /* the overlap/add section */ |
| if(v->lW){ |
| if(v->W){ |
| /* large/large */ |
| float *w=_vorbis_window_get(b->window[1]-hs); |
| float *pcm=v->pcm[j]+prevCenter; |
| float *p=vb->pcm[j]; |
| for(i=0;i<n1;i++) |
| pcm[i]=pcm[i]*w[n1-i-1] + p[i]*w[i]; |
| }else{ |
| /* large/small */ |
| float *w=_vorbis_window_get(b->window[0]-hs); |
| float *pcm=v->pcm[j]+prevCenter+n1/2-n0/2; |
| float *p=vb->pcm[j]; |
| for(i=0;i<n0;i++) |
| pcm[i]=pcm[i]*w[n0-i-1] +p[i]*w[i]; |
| } |
| }else{ |
| if(v->W){ |
| /* small/large */ |
| 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;i<n0;i++) |
| pcm[i]=pcm[i]*w[n0-i-1] +p[i]*w[i]; |
| for(;i<n1/2+n0/2;i++) |
| pcm[i]=p[i]; |
| }else{ |
| /* small/small */ |
| float *w=_vorbis_window_get(b->window[0]-hs); |
| float *pcm=v->pcm[j]+prevCenter; |
| float *p=vb->pcm[j]; |
| for(i=0;i<n0;i++) |
| pcm[i]=pcm[i]*w[n0-i-1] +p[i]*w[i]; |
| } |
| } |
| |
| /* the copy section */ |
| { |
| float *pcm=v->pcm[j]+thisCenter; |
| float *p=vb->pcm[j]+n; |
| for(i=0;i<n;i++) |
| pcm[i]=p[i]; |
| } |
| } |
| |
| 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); |
| } |
| |
| } |
| |
| /* 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 count, not short count */ |
| |
| v->pcm_current-=(b->sample_count-v->granulepos)>>hs; |
| }else{ |
| /* trim the beginning */ |
| v->pcm_returned+=(b->sample_count-v->granulepos)>>hs; |
| 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>>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 */ |
| |
| 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,float ***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 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;j<vi->channels;j++){ |
| float *p=v->pcm[j]; |
| for(i=0;i<n1;i++){ |
| float temp=p[i]; |
| p[i]=p[i+n1]; |
| p[i+n1]=temp; |
| } |
| } |
| |
| v->pcm_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;j<vi->channels;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;j<vi->channels;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;i<vi->channels;i++) |
| v->pcmret[i]=v->pcm[i]+v->pcm_returned; |
| *pcm=v->pcmret; |
| } |
| |
| return(n1+n-v->pcm_returned); |
| |
| } |
| |
| 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); |
| } |