Commit 91b4f011 authored by Monty's avatar Monty
Browse files

Experimental addition to the vorbisfile API that required a few
modifications elsewhere:

added 'ov_crosslap()' such that decode of a second clip can be primed
witht he MDCT overlap of a previous clip; this entirely eliminates any
click on sample boundaries that should otherwise match, but have a
small step error due to encoding being lossy.  It will also smooth
transitions in general purpose loops.  More detailed docs to come
after more testing.

Monty

svn path=/trunk/vorbis/; revision=4387
parent 60cb982d
......@@ -11,7 +11,7 @@
********************************************************************
function: PCM data vector blocking, windowing and dis/reassembly
last mod: $Id: block.c,v 1.68 2002/10/11 11:14:41 xiphmont Exp $
last mod: $Id: block.c,v 1.69 2003/03/02 11:45:17 xiphmont Exp $
Handle windowing, overlap-add, etc of the PCM vectors. This is made
more amusing by Vorbis' current two allowed block sizes.
......@@ -673,7 +673,7 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){
v->sequence=vb->sequence;
if(vb->pcm){ /* not pcm to process if vorbis_synthesis_trackonly
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;
......@@ -691,8 +691,8 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){
thisCenter=n1;
prevCenter=0;
}else{
thisCenter=0;
prevCenter=n1;
thisCenter=0;
prevCenter=n1;
}
/* v->pcm is now used like a two-stage double buffer. We don't want
......@@ -706,32 +706,36 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){
if(v->lW){
if(v->W){
/* large/large */
float *w=b->window[1];
float *pcm=v->pcm[j]+prevCenter;
float *p=vb->pcm[j];
for(i=0;i<n1;i++)
pcm[i]+=p[i];
pcm[i]=pcm[i]*w[n1-i-1] + p[i]*w[i];
}else{
/* large/small */
float *w=b->window[0];
float *pcm=v->pcm[j]+prevCenter+n1/2-n0/2;
float *p=vb->pcm[j];
for(i=0;i<n0;i++)
pcm[i]+=p[i];
pcm[i]=pcm[i]*w[n0-i-1] +p[i]*w[i];
}
}else{
if(v->W){
/* small/large */
float *w=b->window[0];
float *pcm=v->pcm[j]+prevCenter;
float *p=vb->pcm[j]+n1/2-n0/2;
for(i=0;i<n0;i++)
pcm[i]+=p[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=b->window[0];
float *pcm=v->pcm[j]+prevCenter;
float *p=vb->pcm[j];
for(i=0;i<n0;i++)
pcm[i]+=p[i];
pcm[i]=pcm[i]*w[n0-i-1] +p[i]*w[i];
}
}
......@@ -796,7 +800,7 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){
/* 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 */
in a long count, not short count */
v->pcm_current-=(b->sample_count-v->granulepos);
}else{
......@@ -856,3 +860,97 @@ int vorbis_synthesis_read(vorbis_dsp_state *v,int 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 n=ci->blocksizes[v->W]/2;
int n0=ci->blocksizes[0]/2;
int n1=ci->blocksizes[1]/2;
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;
}
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);
}
void vorbis_splice(float *d,float *s,
vorbis_dsp_state *v,int W){
vorbis_info *vi=v->vi;
codec_setup_info *ci=vi->codec_setup;
private_state *b=v->backend_state;
int n=ci->blocksizes[W]/2;
float *wd=b->window[W];
float *ws=b->window[W]+n;
int i;
for(i=0;i<n;i++)
d[i]=d[i]*(*wd++) + s[i]*(*--ws);
}
......@@ -11,7 +11,7 @@
********************************************************************
function: channel mapping 0 implementation
last mod: $Id: mapping0.c,v 1.56 2002/10/17 06:06:59 xiphmont Exp $
last mod: $Id: mapping0.c,v 1.57 2003/03/02 11:45:17 xiphmont Exp $
********************************************************************/
......@@ -747,17 +747,6 @@ static int mapping0_inverse(vorbis_block *vb,vorbis_info_mapping *l){
mdct_backward(b->transform[vb->W][0],pcm,pcm);
}
/* window the data */
for(i=0;i<vi->channels;i++){
float *pcm=vb->pcm[i];
if(nonzero[i])
_vorbis_apply_window(pcm,b->window,ci->blocksizes,vb->lW,vb->W,vb->nW);
else
for(j=0;j<n;j++)
pcm[j]=0.f;
}
/* all done! */
return(0);
}
......
......@@ -11,7 +11,7 @@
********************************************************************
function: stdio-based convenience library for opening/seeking/decoding
last mod: $Id: vorbisfile.c,v 1.64 2002/10/26 13:37:03 msmith Exp $
last mod: $Id: vorbisfile.c,v 1.65 2003/03/02 11:45:17 xiphmont Exp $
********************************************************************/
......@@ -457,7 +457,8 @@ static void _decode_clear(OggVorbis_File *vf){
static int _fetch_and_process_packet(OggVorbis_File *vf,
ogg_packet *op_in,
int readp){
int readp,
int spanp){
ogg_page og;
/* handle one packet. Try to fetch it from current stream state */
......@@ -549,6 +550,8 @@ static int _fetch_and_process_packet(OggVorbis_File *vf,
/* has our decoding just traversed a bitstream boundary? */
if(vf->ready_state==INITSET){
if(vf->current_serialno!=ogg_page_serialno(&og)){
if(!spanp)return(OV_EOF);
_decode_clear(vf);
if(!vf->seekable){
......@@ -924,7 +927,7 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
decoding as immediately after the seek position as possible.
So, a hack. We use two stream states; a local scratch state and
a the shared vf->os stream state. We use the local state to
the shared vf->os stream state. We use the local state to
scan, and the shared state as a buffer for later decode.
Unfortuantely, on the last page we still advance to last packet
......@@ -1263,16 +1266,15 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
/* discard samples until we reach the desired position. Crossing a
logical bitstream boundary with abandon is OK. */
while(vf->pcm_offset<pos){
float **pcm;
ogg_int64_t target=pos-vf->pcm_offset;
long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
long samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
if(samples>target)samples=target;
vorbis_synthesis_read(&vf->vd,samples);
vf->pcm_offset+=samples;
if(samples<target)
if(_fetch_and_process_packet(vf,NULL,1)<=0)
if(_fetch_and_process_packet(vf,NULL,1,1)<=0)
vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */
}
return 0;
......@@ -1459,14 +1461,14 @@ long ov_read(OggVorbis_File *vf,char *buffer,int length,
if(vf->ready_state<OPENED)return(OV_EINVAL);
while(1){
if(vf->ready_state>=STREAMSET){
if(vf->ready_state==INITSET){
samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
if(samples)break;
}
/* suck in another packet */
{
int ret=_fetch_and_process_packet(vf,NULL,1);
int ret=_fetch_and_process_packet(vf,NULL,1,1);
if(ret==OV_EOF)return(0);
if(ret<=0)return(ret);
}
......@@ -1597,7 +1599,7 @@ long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int length,
if(vf->ready_state<OPENED)return(OV_EINVAL);
while(1){
if(vf->ready_state>=STREAMSET){
if(vf->ready_state==INITSET){
float **pcm;
long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
if(samples){
......@@ -1613,7 +1615,7 @@ long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int length,
/* suck in another packet */
{
int ret=_fetch_and_process_packet(vf,NULL,1);
int ret=_fetch_and_process_packet(vf,NULL,1,1);
if(ret==OV_EOF)return(0);
if(ret<=0)return(ret);
}
......@@ -1621,3 +1623,127 @@ long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int length,
}
}
extern void vorbis_splice(float *d,float *s,
vorbis_dsp_state *v,int W);
/* this sets up crosslapping of a sample by using trailing data from
sample 1 and lapping it into the windowing buffer of the second */
int ov_crosslap(OggVorbis_File *vf1, OggVorbis_File *vf2){
vorbis_info *vi1,*vi2;
vorbis_dsp_state *vd1=&vf1->vd;
vorbis_dsp_state *vd2=&vf2->vd;
float **lappcm;
float **pcm;
vorbis_dsp_state *winstate;
int lapsize,lapcount=0,i,j;
/* first enforce some level of sanity... */
/* we need to know that sample rate & channels match. To do that,
we need to verify the streams are actually initialized; the call
is not just to be used for end-to-beginning lapping */
if(vf1->ready_state<OPENED)return(OV_EINVAL);
if(vf2->ready_state<OPENED)return(OV_EINVAL);
/* make sure vf1 is INITSET */
while(1){
if(vf1->ready_state==INITSET)break;
/* suck in another packet */
{
int ret=_fetch_and_process_packet(vf1,NULL,1,0);
if(ret<0)return(ret);
}
}
/* make sure vf2 is INITSET and that we have a primed buffer; if
we're crosslapping at a stream section boundary, this also makes
sure we're sanity checking against the right stream information */
while(1){
if(vf2->ready_state==INITSET)
if(vorbis_synthesis_pcmout(vd2,NULL))break;
/* suck in another packet */
{
int ret=_fetch_and_process_packet(vf2,NULL,1,0);
if(ret<0)return(ret);
}
}
/* sanity-check settings */
vi1=ov_info(vf1,-1);
vi2=ov_info(vf2,-1);
if(vi1->channels != vi2->channels ||
vi1->rate != vi2->rate) return OV_EINVAL;
/* begin by grabbing enough data for lapping from vf1; this may be
in the form of unreturned, already-decoded pcm, remaining PCM we
will need to decode, or synthetic postextrapolation from last
packets. */
lappcm=alloca(sizeof(*lappcm)*vi1->channels);
lapsize=vorbis_info_blocksize(vi1,0);
if(vorbis_info_blocksize(vi2,0)<lapsize){
lapsize=vorbis_info_blocksize(vi2,0)/2;
winstate=vd2;
}else{
lapsize/=2;
winstate=vd1;
}
for(i=0;i<vi1->channels;i++)
lappcm[i]=alloca(sizeof(**lappcm)*lapsize);
/* try first to decode the lapping data */
while(lapcount<lapsize){
int samples=vorbis_synthesis_pcmout(vd1,&pcm);
if(samples){
if(samples>lapsize-lapcount)samples=lapsize-lapcount;
for(i=0;i<vi1->channels;i++)
memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples);
lapcount+=samples;
vorbis_synthesis_read(vd1,samples);
}else{
/* suck in another packet */
int ret=_fetch_and_process_packet(vf1,NULL,1,0); /* do *not* span */
if(ret==OV_EOF)break;
}
}
if(lapcount<lapsize){
/* failed to get lapping data from normal decode; pry it from the
postextrapolation buffering, or the second half of the MDCT
from the last packet */
int samples=vorbis_synthesis_lapout(&vf1->vd,&pcm);
if(samples>lapsize-lapcount)samples=lapsize-lapcount;
for(i=0;i<vi1->channels;i++)
memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples);
lapcount+=samples;
if(lapcount<lapsize){
fprintf(stderr,"GAR undersized lapping.\n");
exit(1);
}
}
/* have a lapping buffer from vf1; now to splice it into the lapping
buffer of vf2 */
/* consolidate and expose the buffer. */
if(vorbis_synthesis_lapout(vd2,&pcm)<lapsize){
fprintf(stderr,"vf2 undersized lapping.\n");
exit(1);
}
/* splice */
for(j=0;j<vi1->channels;j++){
float *s=lappcm[j];
float *d=pcm[j];
vorbis_splice(d,s,winstate,0);
}
/* done */
return(0);
}
......@@ -11,7 +11,7 @@
********************************************************************
function: window functions
last mod: $Id: window.c,v 1.18 2002/10/16 02:43:48 xiphmont Exp $
last mod: $Id: window.c,v 1.19 2003/03/02 11:45:17 xiphmont Exp $
********************************************************************/
......@@ -77,3 +77,4 @@ void _vorbis_apply_window(float *d,float *window[2],long *blocksizes,
d[i]=0.f;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment