| /************************************************************************ |
| * Copyright (C) 2002-2009, Xiph.org Foundation |
| * Copyright (C) 2010, Robin Watts for Pinknoise Productions Ltd |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the names of the Xiph.org Foundation nor Pinknoise |
| * Productions Ltd nor the names of its contributors may be used to |
| * endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| ************************************************************************ |
| |
| function: PCM data vector blocking, windowing and dis/reassembly |
| |
| ************************************************************************/ |
| |
| #include <stdlib.h> |
| #include "ogg.h" |
| #include "mdct.h" |
| #include "ivorbiscodec.h" |
| #include "codec_internal.h" |
| #include "misc.h" |
| #include "window_lookup.h" |
| |
| int vorbis_dsp_restart(vorbis_dsp_state *v){ |
| if(!v)return -1; |
| { |
| vorbis_info *vi=v->vi; |
| codec_setup_info *ci; |
| |
| if(!vi)return -1; |
| ci=vi->codec_setup; |
| if(!ci)return -1; |
| |
| v->out_end=-1; |
| v->out_begin=-1; |
| |
| v->granulepos=-1; |
| v->sequence=-1; |
| v->sample_count=-1; |
| } |
| return 0; |
| } |
| |
| int vorbis_dsp_init(vorbis_dsp_state *v,vorbis_info *vi){ |
| int i; |
| |
| codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; |
| |
| v->vi=vi; |
| |
| v->work=(ogg_int32_t **)_ogg_malloc(vi->channels*sizeof(*v->work)); |
| v->mdctright=(ogg_int32_t **)_ogg_malloc(vi->channels*sizeof(*v->mdctright)); |
| for(i=0;i<vi->channels;i++){ |
| v->work[i]=(ogg_int32_t *)_ogg_calloc(1,(ci->blocksizes[1]>>1)* |
| sizeof(*v->work[i])); |
| v->mdctright[i]=(ogg_int32_t *)_ogg_calloc(1,(ci->blocksizes[1]>>2)* |
| sizeof(*v->mdctright[i])); |
| } |
| |
| v->lW=0; /* previous window size */ |
| v->W=0; /* current window size */ |
| |
| vorbis_dsp_restart(v); |
| return 0; |
| } |
| |
| vorbis_dsp_state *vorbis_dsp_create(vorbis_info *vi){ |
| vorbis_dsp_state *v=_ogg_calloc(1,sizeof(*v)); |
| vorbis_dsp_init(v,vi); |
| return v; |
| } |
| |
| void vorbis_dsp_clear(vorbis_dsp_state *v){ |
| int i; |
| if(v){ |
| vorbis_info *vi=v->vi; |
| |
| if(v->work){ |
| for(i=0;i<vi->channels;i++) |
| if(v->work[i])_ogg_free(v->work[i]); |
| _ogg_free(v->work); |
| } |
| if(v->mdctright){ |
| for(i=0;i<vi->channels;i++) |
| if(v->mdctright[i])_ogg_free(v->mdctright[i]); |
| _ogg_free(v->mdctright); |
| } |
| } |
| } |
| |
| void vorbis_dsp_destroy(vorbis_dsp_state *v){ |
| vorbis_dsp_clear(v); |
| _ogg_free(v); |
| } |
| |
| static LOOKUP_T *_vorbis_window(int left){ |
| 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; |
| #ifndef LIMIT_TO_64kHz |
| case 4096: |
| return vwin8192; |
| #endif |
| default: |
| return(0); |
| } |
| } |
| |
| /* pcm==0 indicates we just want the pending samples, no more */ |
| int vorbis_dsp_pcmout(vorbis_dsp_state *v,ogg_int16_t *pcm,int samples){ |
| vorbis_info *vi=v->vi; |
| codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; |
| if(v->out_begin>-1 && v->out_begin<v->out_end){ |
| int n=v->out_end-v->out_begin; |
| if(pcm){ |
| int i; |
| if(n>samples)n=samples; |
| for(i=0;i<vi->channels;i++) |
| mdct_unroll_lap(ci->blocksizes[0],ci->blocksizes[1], |
| v->lW,v->W,v->work[i],v->mdctright[i], |
| _vorbis_window(ci->blocksizes[0]>>1), |
| _vorbis_window(ci->blocksizes[1]>>1), |
| pcm+i,vi->channels, |
| v->out_begin,v->out_begin+n); |
| } |
| return(n); |
| } |
| return(0); |
| } |
| |
| int vorbis_dsp_read(vorbis_dsp_state *v,int s){ |
| if(s && v->out_begin+s>v->out_end)return(OV_EINVAL); |
| v->out_begin+=s; |
| return(0); |
| } |
| |
| long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){ |
| codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; |
| oggpack_buffer opb; |
| int mode; |
| int modebits=0; |
| int v=ci->modes; |
| |
| oggpack_readinit(&opb,op->packet); |
| |
| /* Check the packet type */ |
| if(oggpack_read(&opb,1)!=0){ |
| /* Oops. This is not an audio data packet */ |
| return(OV_ENOTAUDIO); |
| } |
| |
| while(v>1){ |
| modebits++; |
| v>>=1; |
| } |
| |
| /* read our mode and pre/post windowsize */ |
| mode=oggpack_read(&opb,modebits); |
| if(mode==-1)return(OV_EBADPACKET); |
| return(ci->blocksizes[ci->mode_param[mode].blockflag]); |
| } |
| |
| |
| static int ilog(ogg_uint32_t v){ |
| int ret=0; |
| if(v)--v; |
| while(v){ |
| ret++; |
| v>>=1; |
| } |
| return(ret); |
| } |
| |
| int vorbis_dsp_synthesis(vorbis_dsp_state *vd,ogg_packet *op,int decodep){ |
| vorbis_info *vi=vd->vi; |
| codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; |
| int mode,i; |
| |
| oggpack_readinit(&vd->opb,op->packet); |
| |
| /* Check the packet type */ |
| if(oggpack_read(&vd->opb,1)!=0){ |
| /* Oops. This is not an audio data packet */ |
| return OV_ENOTAUDIO ; |
| } |
| |
| /* read our mode and pre/post windowsize */ |
| mode=oggpack_read(&vd->opb,ilog(ci->modes)); |
| if(mode==-1 || mode>=ci->modes) return OV_EBADPACKET; |
| |
| /* shift information we still need from last window */ |
| vd->lW=vd->W; |
| vd->W=ci->mode_param[mode].blockflag; |
| for(i=0;i<vi->channels;i++) |
| mdct_shift_right(ci->blocksizes[vd->lW],vd->work[i],vd->mdctright[i]); |
| |
| if(vd->W){ |
| int temp; |
| oggpack_read(&vd->opb,1); |
| temp=oggpack_read(&vd->opb,1); |
| if(temp==-1) return OV_EBADPACKET; |
| } |
| |
| /* packet decode and portions of synthesis that rely on only this block */ |
| if(decodep){ |
| mapping_inverse(vd,ci->map_param+ci->mode_param[mode].mapping); |
| |
| if(vd->out_begin==-1){ |
| vd->out_begin=0; |
| vd->out_end=0; |
| }else{ |
| vd->out_begin=0; |
| vd->out_end=ci->blocksizes[vd->lW]/4+ci->blocksizes[vd->W]/4; |
| } |
| } |
| |
| /* track the frame number... This is for convenience, but also |
| making sure our last packet doesn't end with added padding. |
| |
| 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 we're out of sequence, dump granpos tracking until we sync back up */ |
| if(vd->sequence==-1 || vd->sequence+1 != op->packetno-3){ |
| /* out of sequence; lose count */ |
| vd->granulepos=-1; |
| vd->sample_count=-1; |
| } |
| |
| vd->sequence=op->packetno; |
| vd->sequence=vd->sequence-3; |
| |
| if(vd->sample_count==-1){ |
| vd->sample_count=0; |
| }else{ |
| vd->sample_count+= |
| ci->blocksizes[vd->lW]/4+ci->blocksizes[vd->W]/4; |
| } |
| |
| if(vd->granulepos==-1){ |
| if(op->granulepos!=-1){ /* only set if we have a |
| position to set to */ |
| |
| vd->granulepos=op->granulepos; |
| |
| /* is this a short page? */ |
| if(vd->sample_count>vd->granulepos){ |
| /* corner case; if this is both the first and last audio page, |
| then spec says the end is cut, not beginning */ |
| if(op->e_o_s){ |
| /* trim the end */ |
| /* no preceeding granulepos; assume we started at zero (we'd |
| have to in a short single-page stream) */ |
| /* granulepos could be -1 due to a seek, but that would result |
| in a long coun t, not short count */ |
| |
| vd->out_end-=(int)(vd->sample_count-vd->granulepos); |
| }else{ |
| /* trim the beginning */ |
| vd->out_begin+=(int)(vd->sample_count-vd->granulepos); |
| if(vd->out_begin>vd->out_end) |
| vd->out_begin=vd->out_end; |
| } |
| |
| } |
| |
| } |
| }else{ |
| vd->granulepos+= |
| ci->blocksizes[vd->lW]/4+ci->blocksizes[vd->W]/4; |
| if(op->granulepos!=-1 && vd->granulepos!=op->granulepos){ |
| |
| if(vd->granulepos>op->granulepos){ |
| long extra=(long)(vd->granulepos-op->granulepos); |
| |
| if(extra) |
| if(op->e_o_s){ |
| /* partial last frame. Strip the extra samples off */ |
| vd->out_end-=extra; |
| } /* else {Shouldn't happen *unless* the bitstream is out of |
| spec. Either way, believe the bitstream } */ |
| } /* else {Shouldn't happen *unless* the bitstream is out of |
| spec. Either way, believe the bitstream } */ |
| vd->granulepos=op->granulepos; |
| } |
| } |
| |
| return(0); |
| } |