/********************************************************************
* *
- * 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 <monty@xiph.org> 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 <xiphmont@mit.edu>
- 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "codec.h"
+#include <ogg/ogg.h>
+#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):
:.....''' |_____--- '''......| | \_______|
:.................|__________________|_______|__|______|
|<------ 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;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;
+ }
{
- int i;
- for(i=0;i<vi->channels;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;i<v->envelope_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;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||
+ ci->modes<=0||
+ ci->blocksizes[0]<64||
+ ci->blocksizes[1]<ci->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;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; 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;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){
- _vds_shared_init(v,vi);
+ private_state *b=NULL;
- /* Yes, wasteful to have four lookups. This will get collapsed once
- things crystallize */
- lpc_init(&v->vl[0],vi->smallblock/2,vi->smallblock/2,
- vi->floororder,vi->flooroctaves,1);
- lpc_init(&v->vl[1],vi->largeblock/2,vi->largeblock/2,
- vi->floororder,vi->flooroctaves,1);
+ if(_vds_shared_init(v,vi,1))return 1;
+ b=v->backend_state;
+ b->psy_g_look=_vp_global_look(vi);
- lpc_init(&v->vbal[0],vi->smallblock/2,256,
- vi->balanceorder,vi->balanceoctaves,1);
- lpc_init(&v->vbal[1],vi->largeblock/2,256,
- vi->balanceorder,vi->balanceoctaves,1);
+ /* 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;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->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;i<v->pcm_channels;i++)
- if(v->pcm[i])free(v->pcm[i]);
- free(v->pcm);
- free(v->pcmret);
+ 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(v->multipliers){
- for(i=0;i<v->envelope_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);
- 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;i<v->pcm_channels;i++){
- v->pcm[i]=realloc(v->pcm[i],v->pcm_storage*sizeof(double));
- }
- for(i=0;i<v->envelope_channels;i++){
- v->multipliers[i]=realloc(v->multipliers[i],
- v->envelope_storage*sizeof(double));
+
+ 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<v->pcm_channels;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 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;i<v->pcm_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;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(-1);
+ return(OV_EINVAL);
v->pcm_current+=vals;
- }
- return(0);
-}
-int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){
- int i;
- memset(vb,0,sizeof(vorbis_block));
- 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->floor_channels=v->vi.floorch;
- vb->floor_storage=v->vi.floororder;
-
- vb->pcm=malloc(vb->pcm_channels*sizeof(double *));
- for(i=0;i<vb->pcm_channels;i++)
- vb->pcm[i]=malloc(vb->pcm_storage*sizeof(double));
-
- vb->mult=malloc(vb->mult_channels*sizeof(double *));
- for(i=0;i<vb->mult_channels;i++)
- vb->mult[i]=malloc(vb->mult_storage*sizeof(double));
-
- vb->lsp=malloc(vb->floor_channels*sizeof(double *));
- vb->lpc=malloc(vb->floor_channels*sizeof(double *));
- vb->amp=malloc(vb->floor_channels*sizeof(double));
- for(i=0;i<vb->floor_channels;i++){
- vb->lsp[i]=malloc(vb->floor_storage*sizeof(double));
- vb->lpc[i]=malloc(vb->floor_storage*sizeof(double));
- }
+ /* 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);
-}
-
-int vorbis_block_clear(vorbis_block *vb){
- int i;
- if(vb->pcm){
- for(i=0;i<vb->pcm_channels;i++)
- free(vb->pcm[i]);
- free(vb->pcm);
}
- if(vb->mult){
- for(i=0;i<vb->mult_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->vi.smallblock<v->vi.largeblock){
-
- 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(;i<v->envelope_current;i++){
- for(j=0;j<v->envelope_channels;j++)
- if(v->multipliers[j][i-1]*v->vi.preecho_thresh<
- v->multipliers[j][i])break;
- if(j<v->envelope_channels)break;
- }
-
- if(i<v->envelope_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;
-
- if(i>=largebound)
- v->nW=1;
- else
- v->nW=0;
-
+ /* 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{
- /* 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;
}
- }else
- v->nW=1;
- v->nW=1;
-
- /* 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_current<blockbound)return(0); /* not enough data yet */
+ /* 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 */
+ /* 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;
- vb->vd=v;
- vb->pcmend=v->block_size[v->W];
- vb->multend=vb->pcmend / v->samples_per_envelope_step;
-
- if(vb->floor_channels!=v->vi.floorch ||
- vb->floor_storage!=v->vi.floororder ||
- 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;i<v->pcm_channels;i++)
- memcpy(vb->pcm[i],v->pcm[i]+beginW,v->block_size[v->W]*sizeof(double));
- for(i=0;i<v->envelope_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;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]));
+ */
- 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[]
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;i<v->pcm_channels;i++)
- memmove(v->pcm[i],v->pcm[i]+movementW,
- (v->pcm_current-movementW)*sizeof(double));
-
- for(i=0;i<v->envelope_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){
- v->lW=v->W;
- v->W=v->nW;
- v->centerW=new_centerNext;
+ _ve_envelope_shift(b->ve,movementW);
+ v->pcm_current-=movementW;
- v->frame++;
- v->samples+=movementW;
+ for(i=0;i<vi->channels;i++)
+ memmove(v->pcm[i],v->pcm[i]+movementW,
+ v->pcm_current*sizeof(*v->pcm[i]));
- if(v->eofflag)
- v->eofflag-=movementW;
+
+ 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_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;
-
- /* Yes, wasteful to have four lookups. This will get collapsed once
- things crystallize */
- lpc_init(&v->vl[0],vi->smallblock/2,vi->smallblock/2,
- vi->floororder,vi->flooroctaves,0);
- lpc_init(&v->vl[1],vi->largeblock/2,vi->largeblock/2,
- vi->floororder,vi->flooroctaves,0);
- lpc_init(&v->vbal[0],vi->smallblock/2,256,
- vi->balanceorder,vi->balanceoctaves,0);
- lpc_init(&v->vbal[1],vi->largeblock/2,256,
- vi->balanceorder,vi->balanceoctaves,0);
-
-
- /* 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_returned<shift?v->pcm_returned:shift);
+ v->sequence=vb->sequence;
- v->pcm_current-=shift;
- v->centerW-=shift;
- v->pcm_returned-=shift;
-
- if(shift){
- int i;
- for(i=0;i<v->pcm_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;i<v->pcm_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;j<vi->channels;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;i<n1;i++)
+ pcm[i]=pcm[i]*w[n1-i-1] + p[i]*w[i];
+ }else{
+ /* large/small */
+ const 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 */
+ 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;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 */
+ const 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];
+ }
}
- /* Overlap/add */
- switch(vb->W){
- 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;
+ }
- for(j=0;j<v->pcm_channels;j++){
- double *pcm=v->pcm[j]+beginW;
+ /* 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 */
+ 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)<<hs)
+ extra = (v->pcm_current - v->pcm_returned)<<hs;
+
+ v->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;i<endSl;i++)
- pcm[i]=pcm[i]*window[i]+vb->pcm[j][i];
- /* the remaining section */
- for(;i<sizeW;i++)
- pcm[i]=vb->pcm[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)<<hs)
+ extra = (v->pcm_current - v->pcm_returned)<<hs;
+
+ /* 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;
+
+ 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 */
+ /* Update, cleanup */
- v->centerW=centerW;
- v->pcm_current=endW;
+ if(vb->eofflag)v->eofflag=1;
+ return(0);
+
+}
- if(vb->eofflag)v->eofflag=1;
+/* 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_pcmout(vorbis_dsp_state *v,double ***pcm){
- if(v->pcm_returned<v->centerW){
+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<v->pcm_channels;i++)
+ for(i=0;i<vi->channels;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);
+}