From 938f4699b2390c9a035e15736d06a3bf2c5ff40c Mon Sep 17 00:00:00 2001 From: Monty Date: Mon, 18 Aug 2003 05:34:01 +0000 Subject: [PATCH] Vorbisfile API addition for game and mod coders; Do a 'free' sample rate conversion from source rate to half source rate by calling ov_halfrate() after ov_open(); Although this is solid for immediately desired usage, I want to clean it up a bit before advertising it's existence, so no doc addition yet. Does not break binary API. Monty svn path=/trunk/vorbis/; revision=5222 --- include/vorbis/codec.h | 5 +++- include/vorbis/vorbisfile.h | 5 +++- lib/block.c | 59 ++++++++++++++++++++++++--------------------- lib/codec_internal.h | 7 +++--- lib/mapping0.c | 3 ++- lib/synthesis.c | 17 ++++++++++++- lib/vorbisfile.c | 30 ++++++++++++++++++++++- lib/window.c | 49 +++++++++++++++---------------------- lib/window.h | 4 +-- 9 files changed, 112 insertions(+), 67 deletions(-) diff --git a/include/vorbis/codec.h b/include/vorbis/codec.h index ed98b12..6061369 100644 --- a/include/vorbis/codec.h +++ b/include/vorbis/codec.h @@ -11,7 +11,7 @@ ******************************************************************** function: libvorbis codec headers - last mod: $Id: codec.h,v 1.43 2003/03/04 21:23:37 xiphmont Exp $ + last mod: $Id: codec.h,v 1.44 2003/08/18 05:34:01 xiphmont Exp $ ********************************************************************/ @@ -212,6 +212,9 @@ extern int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm); extern int vorbis_synthesis_read(vorbis_dsp_state *v,int samples); extern long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op); +extern int vorbis_synthesis_halfrate(vorbis_info *v,int flag); +extern int vorbis_synthesis_halfrate_p(vorbis_info *v); + /* Vorbis ERRORS and return codes ***********************************/ #define OV_FALSE -1 diff --git a/include/vorbis/vorbisfile.h b/include/vorbis/vorbisfile.h index cb3028d..71ca86f 100644 --- a/include/vorbis/vorbisfile.h +++ b/include/vorbis/vorbisfile.h @@ -11,7 +11,7 @@ ******************************************************************** function: stdio-based convenience library for opening/seeking/decoding - last mod: $Id: vorbisfile.h,v 1.19 2003/03/06 22:03:42 xiphmont Exp $ + last mod: $Id: vorbisfile.h,v 1.20 2003/08/18 05:34:01 xiphmont Exp $ ********************************************************************/ @@ -131,6 +131,9 @@ extern long ov_read(OggVorbis_File *vf,char *buffer,int length, int bigendianp,int word,int sgned,int *bitstream); extern int ov_crosslap(OggVorbis_File *vf1,OggVorbis_File *vf2); +extern int ov_halfrate(OggVorbis_File *vf,int flag); +extern int ov_halfrate_p(OggVorbis_File *vf); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/lib/block.c b/lib/block.c index 9f379a3..921a9d2 100644 --- a/lib/block.c +++ b/lib/block.c @@ -5,13 +5,13 @@ * 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-2002 * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2003 * * by the XIPHOPHORUS Company http://www.xiph.org/ * * * ******************************************************************** function: PCM data vector blocking, windowing and dis/reassembly - last mod: $Id: block.c,v 1.72 2003/03/06 22:05:26 xiphmont Exp $ + last mod: $Id: block.c,v 1.73 2003/08/18 05:34:01 xiphmont Exp $ Handle windowing, overlap-add, etc of the PCM vectors. This is made more amusing by Vorbis' current two allowed block sizes. @@ -168,6 +168,7 @@ 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=ci->halfrate_flag; memset(v,0,sizeof(*v)); b=v->backend_state=_ogg_calloc(1,sizeof(*b)); @@ -182,12 +183,12 @@ static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){ 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]); - mdct_init(b->transform[1][0],ci->blocksizes[1]); + 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]=_vorbis_window_get(0,ci->blocksizes[0]/2); - b->window[1]=_vorbis_window_get(0,ci->blocksizes[1]/2); + b->window[0]=ilog2(ci->blocksizes[0])-6; + b->window[1]=ilog2(ci->blocksizes[1])-6; if(encp){ /* encode/decode differ here */ @@ -636,14 +637,16 @@ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ 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]/2; - v->pcm_current=v->centerW; + v->centerW=ci->blocksizes[1]>>(hs+1); + v->pcm_current=v->centerW>>hs; v->pcm_returned=-1; v->granulepos=-1; @@ -669,6 +672,7 @@ 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); @@ -688,10 +692,10 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ 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 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; @@ -712,21 +716,19 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ 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;jchannels;j++){ /* the overlap/add section */ if(v->lW){ if(v->W){ /* large/large */ - float *w=b->window[1]; + float *w=_vorbis_window_get(b->window[1]-hs); float *pcm=v->pcm[j]+prevCenter; float *p=vb->pcm[j]; for(i=0;iwindow[0]; + 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;iW){ /* small/large */ - float *w=b->window[0]; + 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;iwindow[0]; + float *w=_vorbis_window_get(b->window[0]-hs); float *pcm=v->pcm[j]+prevCenter; float *p=vb->pcm[j]; for(i=0;ipcm_returned=prevCenter; v->pcm_current=prevCenter+ - ci->blocksizes[v->lW]/4+ - ci->blocksizes[v->W]/4; + ((ci->blocksizes[v->lW]/4+ + ci->blocksizes[v->W]/4)>>hs); } } @@ -815,10 +817,10 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ /* 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); + v->pcm_current-=(b->sample_count-v->granulepos)>>hs; }else{ /* trim the beginning */ - v->pcm_returned+=(b->sample_count-v->granulepos); + v->pcm_returned+=(b->sample_count-v->granulepos)>>hs; if(v->pcm_returned>v->pcm_current) v->pcm_returned=v->pcm_current; } @@ -836,7 +838,7 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ if(extra) if(vb->eofflag){ /* partial last frame. Strip the extra samples off */ - v->pcm_current-=extra; + 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 @@ -855,6 +857,7 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ /* 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_returnedpcm_current){ if(pcm){ int i; @@ -881,10 +884,11 @@ int vorbis_synthesis_read(vorbis_dsp_state *v,int n){ 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]/2; - int n0=ci->blocksizes[0]/2; - int n1=ci->blocksizes[1]/2; + 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; @@ -916,6 +920,7 @@ int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm){ v->centerW=0; } + /* solidify buffer into contiguous space */ if((v->lW^v->W)==1){ /* long/short or short/long */ for(j=0;jchannels;j++){ @@ -953,6 +958,6 @@ int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm){ float *vorbis_window(vorbis_dsp_state *v,int W){ private_state *b=v->backend_state; - return b->window[W]; + return _vorbis_window_get(b->window[W]); } diff --git a/lib/codec_internal.h b/lib/codec_internal.h index 392df91..8559771 100644 --- a/lib/codec_internal.h +++ b/lib/codec_internal.h @@ -11,7 +11,7 @@ ******************************************************************** function: libvorbis codec headers - last mod: $Id: codec_internal.h,v 1.16 2002/10/11 11:14:41 xiphmont Exp $ + last mod: $Id: codec_internal.h,v 1.17 2003/08/18 05:34:01 xiphmont Exp $ ********************************************************************/ @@ -58,7 +58,7 @@ typedef void vorbis_info_mapping; typedef struct private_state { /* local lookup storage */ envelope_lookup *ve; /* envelope lookup */ - float *window[2]; + int window[2]; vorbis_look_transform **transform[2]; /* block, type */ drft_lookup fft_look[2]; @@ -79,7 +79,6 @@ typedef struct private_state { bitrate_manager_state bms; ogg_int64_t sample_count; - } private_state; /* codec_setup_info contains all the setup information specific to the @@ -125,7 +124,7 @@ typedef struct codec_setup_info { highlevel_encode_setup hi; /* used only by vorbisenc.c. It's a highly redundant structure, but improves clarity of program flow. */ - + int halfrate_flag; /* painless downsample for decode */ } codec_setup_info; extern vorbis_look_psy_global *_vp_global_look(vorbis_info *vi); diff --git a/lib/mapping0.c b/lib/mapping0.c index f832003..e38ca5f 100644 --- a/lib/mapping0.c +++ b/lib/mapping0.c @@ -11,7 +11,7 @@ ******************************************************************** function: channel mapping 0 implementation - last mod: $Id: mapping0.c,v 1.57 2003/03/02 11:45:17 xiphmont Exp $ + last mod: $Id: mapping0.c,v 1.58 2003/08/18 05:34:01 xiphmont Exp $ ********************************************************************/ @@ -654,6 +654,7 @@ static int mapping0_inverse(vorbis_block *vb,vorbis_info_mapping *l){ codec_setup_info *ci=vi->codec_setup; private_state *b=vd->backend_state; vorbis_info_mapping0 *info=(vorbis_info_mapping0 *)l; + int hs=ci->halfrate_flag; int i,j; long n=vb->pcmend=ci->blocksizes[vb->W]; diff --git a/lib/synthesis.c b/lib/synthesis.c index ec3f5bf..d645c5a 100644 --- a/lib/synthesis.c +++ b/lib/synthesis.c @@ -11,7 +11,7 @@ ******************************************************************** function: single-block PCM synthesis - last mod: $Id: synthesis.c,v 1.29 2002/10/11 11:14:41 xiphmont Exp $ + last mod: $Id: synthesis.c,v 1.30 2003/08/18 05:34:01 xiphmont Exp $ ********************************************************************/ @@ -152,4 +152,19 @@ long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){ return(ci->blocksizes[ci->mode_param[mode]->blockflag]); } +int vorbis_synthesis_halfrate(vorbis_info *vi,int flag){ + /* set / clear half-sample-rate mode */ + codec_setup_info *ci=vi->codec_setup; + + /* right now, our MDCT can't handle < 64 sample windows. */ + if(ci->blocksizes[0]<=64 && flag)return -1; + ci->halfrate_flag=(flag?1:0); + return 0; +} + +int vorbis_synthesis_halfrate_p(vorbis_info *vi){ + codec_setup_info *ci=vi->codec_setup; + return ci->halfrate_flag; +} + diff --git a/lib/vorbisfile.c b/lib/vorbisfile.c index 983e80c..1345322 100644 --- a/lib/vorbisfile.c +++ b/lib/vorbisfile.c @@ -11,7 +11,7 @@ ******************************************************************** function: stdio-based convenience library for opening/seeking/decoding - last mod: $Id: vorbisfile.c,v 1.69 2003/03/11 23:52:02 xiphmont Exp $ + last mod: $Id: vorbisfile.c,v 1.70 2003/08/18 05:34:01 xiphmont Exp $ ********************************************************************/ @@ -735,7 +735,35 @@ int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){ return ov_open_callbacks((void *)f, vf, initial, ibytes, callbacks); } + +/* cheap hack for game usage where downsampling is desirable; there's + no need for SRC as we can just do it cheaply in libvorbis. */ + +int ov_halfrate(OggVorbis_File *vf,int flag){ + int i; + if(vf->vi==NULL)return OV_EINVAL; + if(!vf->seekable)return OV_EINVAL; + if(vf->ready_state>=STREAMSET) + _decode_clear(vf); /* clear out stream state; later on libvorbis + will be able to swap this on the fly, but + for now dumping the decode machine is needed + to reinit the MDCT lookups. 1.1 libvorbis + is planned to be able to switch on the fly */ + for(i=0;ilinks;i++){ + if(vorbis_synthesis_halfrate(vf->vi+i,flag)){ + ov_halfrate(vf,0); + return OV_EINVAL; + } + } + return 0; +} + +int ov_halfrate_p(OggVorbis_File *vf){ + if(vf->vi==NULL)return OV_EINVAL; + return vorbis_synthesis_halfrate_p(vf->vi); +} + /* Only partially open the vorbis file; test for Vorbisness, and load the headers for the first chain. Do not seek (although test for seekability). Use ov_test_open to finish opening the file, else diff --git a/lib/window.c b/lib/window.c index 2e8ac24..b08196b 100644 --- a/lib/window.c +++ b/lib/window.c @@ -11,7 +11,7 @@ ******************************************************************** function: window functions - last mod: $Id: window.c,v 1.21 2003/03/06 22:05:26 xiphmont Exp $ + last mod: $Id: window.c,v 1.22 2003/08/18 05:34:01 xiphmont Exp $ ********************************************************************/ @@ -2084,35 +2084,26 @@ static float vwin8192[4096] = { 1.0000000000F, 1.0000000000F, 1.0000000000F, 1.0000000000F, }; -float *_vorbis_window_get(int type, int left){ - switch(type){ - case 0: - /* The 'vorbis window' (window 0) is sin(sin(x)*sin(x)*pi*.5) */ - switch(left){ - case 32: - return(vwin64); - case 64: - return(vwin128); - case 128: - return(vwin256); - case 256: - return(vwin512); - case 512: - return(vwin1024); - case 1024: - return(vwin2048); - case 2048: - return(vwin4096); - case 4096: - return(vwin8192); - } - default: - return(NULL); - } +static float *vwin[8] = { + vwin64, + vwin128, + vwin256, + vwin512, + vwin1024, + vwin2048, + vwin4096, + vwin8192, +}; + +float *_vorbis_window_get(int n){ + return vwin[n]; } -void _vorbis_apply_window(float *d,float *window[2],long *blocksizes, +void _vorbis_apply_window(float *d,int *winno,long *blocksizes, int lW,int W,int nW){ + float *windowLW=vwin[winno[lW]]; + float *windowNW=vwin[winno[nW]]; + lW=(W?lW:0); nW=(W?nW:0); @@ -2133,10 +2124,10 @@ void _vorbis_apply_window(float *d,float *window[2],long *blocksizes, d[i]=0.f; for(p=0;i