michael@0: /******************************************************************** michael@0: * * michael@0: * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * michael@0: * * michael@0: * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * michael@0: * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * michael@0: * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * michael@0: * * michael@0: * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * michael@0: * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * michael@0: * * michael@0: ******************************************************************** michael@0: michael@0: function: channel mapping 0 implementation michael@0: michael@0: ********************************************************************/ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "ivorbiscodec.h" michael@0: #include "mdct.h" michael@0: #include "codec_internal.h" michael@0: #include "codebook.h" michael@0: #include "window.h" michael@0: #include "registry.h" michael@0: #include "misc.h" michael@0: michael@0: /* simplistic, wasteful way of doing this (unique lookup for each michael@0: mode/submapping); there should be a central repository for michael@0: identical lookups. That will require minor work, so I'm putting it michael@0: off as low priority. michael@0: michael@0: Why a lookup for each backend in a given mode? Because the michael@0: blocksize is set by the mode, and low backend lookups may require michael@0: parameters from other areas of the mode/mapping */ michael@0: michael@0: typedef struct { michael@0: vorbis_info_mode *mode; michael@0: vorbis_info_mapping0 *map; michael@0: michael@0: vorbis_look_floor **floor_look; michael@0: michael@0: vorbis_look_residue **residue_look; michael@0: michael@0: vorbis_func_floor **floor_func; michael@0: vorbis_func_residue **residue_func; michael@0: michael@0: int ch; michael@0: long lastframe; /* if a different mode is called, we need to michael@0: invalidate decay */ michael@0: } vorbis_look_mapping0; michael@0: michael@0: static void mapping0_free_info(vorbis_info_mapping *i){ michael@0: vorbis_info_mapping0 *info=(vorbis_info_mapping0 *)i; michael@0: if(info){ michael@0: memset(info,0,sizeof(*info)); michael@0: _ogg_free(info); michael@0: } michael@0: } michael@0: michael@0: static void mapping0_free_look(vorbis_look_mapping *look){ michael@0: int i; michael@0: vorbis_look_mapping0 *l=(vorbis_look_mapping0 *)look; michael@0: if(l){ michael@0: michael@0: for(i=0;imap->submaps;i++){ michael@0: l->floor_func[i]->free_look(l->floor_look[i]); michael@0: l->residue_func[i]->free_look(l->residue_look[i]); michael@0: } michael@0: michael@0: _ogg_free(l->floor_func); michael@0: _ogg_free(l->residue_func); michael@0: _ogg_free(l->floor_look); michael@0: _ogg_free(l->residue_look); michael@0: memset(l,0,sizeof(*l)); michael@0: _ogg_free(l); michael@0: } michael@0: } michael@0: michael@0: static vorbis_look_mapping *mapping0_look(vorbis_dsp_state *vd,vorbis_info_mode *vm, michael@0: vorbis_info_mapping *m){ michael@0: int i; michael@0: vorbis_info *vi=vd->vi; michael@0: codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; michael@0: vorbis_look_mapping0 *look=(vorbis_look_mapping0 *)_ogg_calloc(1,sizeof(*look)); michael@0: vorbis_info_mapping0 *info=look->map=(vorbis_info_mapping0 *)m; michael@0: look->mode=vm; michael@0: michael@0: look->floor_look=(vorbis_look_floor **)_ogg_calloc(info->submaps,sizeof(*look->floor_look)); michael@0: michael@0: look->residue_look=(vorbis_look_residue **)_ogg_calloc(info->submaps,sizeof(*look->residue_look)); michael@0: michael@0: look->floor_func=(vorbis_func_floor **)_ogg_calloc(info->submaps,sizeof(*look->floor_func)); michael@0: look->residue_func=(vorbis_func_residue **)_ogg_calloc(info->submaps,sizeof(*look->residue_func)); michael@0: michael@0: for(i=0;isubmaps;i++){ michael@0: int floornum=info->floorsubmap[i]; michael@0: int resnum=info->residuesubmap[i]; michael@0: michael@0: look->floor_func[i]=_floor_P[ci->floor_type[floornum]]; michael@0: look->floor_look[i]=look->floor_func[i]-> michael@0: look(vd,vm,ci->floor_param[floornum]); michael@0: look->residue_func[i]=_residue_P[ci->residue_type[resnum]]; michael@0: look->residue_look[i]=look->residue_func[i]-> michael@0: look(vd,vm,ci->residue_param[resnum]); michael@0: michael@0: } michael@0: michael@0: look->ch=vi->channels; michael@0: michael@0: return(look); michael@0: } michael@0: michael@0: static int ilog(unsigned int v){ michael@0: int ret=0; michael@0: if(v)--v; michael@0: while(v){ michael@0: ret++; michael@0: v>>=1; michael@0: } michael@0: return(ret); michael@0: } michael@0: michael@0: /* also responsible for range checking */ michael@0: static vorbis_info_mapping *mapping0_unpack(vorbis_info *vi,oggpack_buffer *opb){ michael@0: int i,b; michael@0: vorbis_info_mapping0 *info=(vorbis_info_mapping0 *)_ogg_calloc(1,sizeof(*info)); michael@0: codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; michael@0: memset(info,0,sizeof(*info)); michael@0: michael@0: b=oggpack_read(opb,1); michael@0: if(b<0)goto err_out; michael@0: if(b){ michael@0: info->submaps=oggpack_read(opb,4)+1; michael@0: if(info->submaps<=0)goto err_out; michael@0: }else michael@0: info->submaps=1; michael@0: michael@0: b=oggpack_read(opb,1); michael@0: if(b<0)goto err_out; michael@0: if(b){ michael@0: info->coupling_steps=oggpack_read(opb,8)+1; michael@0: if(info->coupling_steps<=0)goto err_out; michael@0: for(i=0;icoupling_steps;i++){ michael@0: int testM=info->coupling_mag[i]=oggpack_read(opb,ilog(vi->channels)); michael@0: int testA=info->coupling_ang[i]=oggpack_read(opb,ilog(vi->channels)); michael@0: michael@0: if(testM<0 || michael@0: testA<0 || michael@0: testM==testA || michael@0: testM>=vi->channels || michael@0: testA>=vi->channels) goto err_out; michael@0: } michael@0: michael@0: } michael@0: michael@0: if(oggpack_read(opb,2)!=0)goto err_out; /* 2,3:reserved */ michael@0: michael@0: if(info->submaps>1){ michael@0: for(i=0;ichannels;i++){ michael@0: info->chmuxlist[i]=oggpack_read(opb,4); michael@0: if(info->chmuxlist[i]>=info->submaps || info->chmuxlist[i]<0)goto err_out; michael@0: } michael@0: } michael@0: for(i=0;isubmaps;i++){ michael@0: int temp=oggpack_read(opb,8); michael@0: if(temp>=ci->times)goto err_out; michael@0: info->floorsubmap[i]=oggpack_read(opb,8); michael@0: if(info->floorsubmap[i]>=ci->floors || info->floorsubmap[i]<0)goto err_out; michael@0: info->residuesubmap[i]=oggpack_read(opb,8); michael@0: if(info->residuesubmap[i]>=ci->residues || info->residuesubmap[i]<0) michael@0: goto err_out; michael@0: } michael@0: michael@0: return info; michael@0: michael@0: err_out: michael@0: mapping0_free_info(info); michael@0: return(NULL); michael@0: } michael@0: michael@0: static int seq=0; michael@0: static int mapping0_inverse(vorbis_block *vb,vorbis_look_mapping *l){ michael@0: vorbis_dsp_state *vd=vb->vd; michael@0: vorbis_info *vi=vd->vi; michael@0: codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; michael@0: private_state *b=(private_state *)vd->backend_state; michael@0: vorbis_look_mapping0 *look=(vorbis_look_mapping0 *)l; michael@0: vorbis_info_mapping0 *info=look->map; michael@0: michael@0: int i,j; michael@0: long n=vb->pcmend=ci->blocksizes[vb->W]; michael@0: michael@0: ogg_int32_t **pcmbundle=(ogg_int32_t **)alloca(sizeof(*pcmbundle)*vi->channels); michael@0: int *zerobundle=(int *)alloca(sizeof(*zerobundle)*vi->channels); michael@0: michael@0: int *nonzero =(int *)alloca(sizeof(*nonzero)*vi->channels); michael@0: void **floormemo=(void **)alloca(sizeof(*floormemo)*vi->channels); michael@0: michael@0: /* time domain information decode (note that applying the michael@0: information would have to happen later; we'll probably add a michael@0: function entry to the harness for that later */ michael@0: /* NOT IMPLEMENTED */ michael@0: michael@0: /* recover the spectral envelope; store it in the PCM vector for now */ michael@0: for(i=0;ichannels;i++){ michael@0: int submap=info->chmuxlist[i]; michael@0: floormemo[i]=look->floor_func[submap]-> michael@0: inverse1(vb,look->floor_look[submap]); michael@0: if(floormemo[i]) michael@0: nonzero[i]=1; michael@0: else michael@0: nonzero[i]=0; michael@0: memset(vb->pcm[i],0,sizeof(*vb->pcm[i])*n/2); michael@0: } michael@0: michael@0: /* channel coupling can 'dirty' the nonzero listing */ michael@0: for(i=0;icoupling_steps;i++){ michael@0: if(nonzero[info->coupling_mag[i]] || michael@0: nonzero[info->coupling_ang[i]]){ michael@0: nonzero[info->coupling_mag[i]]=1; michael@0: nonzero[info->coupling_ang[i]]=1; michael@0: } michael@0: } michael@0: michael@0: /* recover the residue into our working vectors */ michael@0: for(i=0;isubmaps;i++){ michael@0: int ch_in_bundle=0; michael@0: for(j=0;jchannels;j++){ michael@0: if(info->chmuxlist[j]==i){ michael@0: if(nonzero[j]) michael@0: zerobundle[ch_in_bundle]=1; michael@0: else michael@0: zerobundle[ch_in_bundle]=0; michael@0: pcmbundle[ch_in_bundle++]=vb->pcm[j]; michael@0: } michael@0: } michael@0: michael@0: look->residue_func[i]->inverse(vb,look->residue_look[i], michael@0: pcmbundle,zerobundle,ch_in_bundle); michael@0: } michael@0: michael@0: //for(j=0;jchannels;j++) michael@0: //_analysis_output("coupled",seq+j,vb->pcm[j],-8,n/2,0,0); michael@0: michael@0: michael@0: /* channel coupling */ michael@0: for(i=info->coupling_steps-1;i>=0;i--){ michael@0: ogg_int32_t *pcmM=vb->pcm[info->coupling_mag[i]]; michael@0: ogg_int32_t *pcmA=vb->pcm[info->coupling_ang[i]]; michael@0: michael@0: for(j=0;j0) michael@0: if(ang>0){ michael@0: pcmM[j]=mag; michael@0: pcmA[j]=mag-ang; michael@0: }else{ michael@0: pcmA[j]=mag; michael@0: pcmM[j]=mag+ang; michael@0: } michael@0: else michael@0: if(ang>0){ michael@0: pcmM[j]=mag; michael@0: pcmA[j]=mag+ang; michael@0: }else{ michael@0: pcmA[j]=mag; michael@0: pcmM[j]=mag-ang; michael@0: } michael@0: } michael@0: } michael@0: michael@0: //for(j=0;jchannels;j++) michael@0: //_analysis_output("residue",seq+j,vb->pcm[j],-8,n/2,0,0); michael@0: michael@0: /* compute and apply spectral envelope */ michael@0: for(i=0;ichannels;i++){ michael@0: ogg_int32_t *pcm=vb->pcm[i]; michael@0: int submap=info->chmuxlist[i]; michael@0: look->floor_func[submap]-> michael@0: inverse2(vb,look->floor_look[submap],floormemo[i],pcm); michael@0: } michael@0: michael@0: //for(j=0;jchannels;j++) michael@0: //_analysis_output("mdct",seq+j,vb->pcm[j],-24,n/2,0,1); michael@0: michael@0: /* transform the PCM data; takes PCM vector, vb; modifies PCM vector */ michael@0: /* only MDCT right now.... */ michael@0: for(i=0;ichannels;i++){ michael@0: ogg_int32_t *pcm=vb->pcm[i]; michael@0: mdct_backward(n,pcm,pcm); michael@0: } michael@0: michael@0: //for(j=0;jchannels;j++) michael@0: //_analysis_output("imdct",seq+j,vb->pcm[j],-24,n,0,0); michael@0: michael@0: /* window the data */ michael@0: for(i=0;ichannels;i++){ michael@0: ogg_int32_t *pcm=vb->pcm[i]; michael@0: if(nonzero[i]) michael@0: _vorbis_apply_window(pcm,b->window,ci->blocksizes,vb->lW,vb->W,vb->nW); michael@0: else michael@0: for(j=0;jchannels;j++) michael@0: //_analysis_output("window",seq+j,vb->pcm[j],-24,n,0,0); michael@0: michael@0: seq+=vi->channels; michael@0: /* all done! */ michael@0: return(0); michael@0: } michael@0: michael@0: /* export hooks */ michael@0: vorbis_func_mapping mapping0_exportbundle={ michael@0: &mapping0_unpack, michael@0: &mapping0_look, michael@0: &mapping0_free_info, michael@0: &mapping0_free_look, michael@0: &mapping0_inverse michael@0: };