|  |  | /* Copyright (C) 2006-2008 CSIRO, Jean-Marc Valin, Xiph.Org Foundation
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |    File: scal.c
 | 
						
						
						
							|  |  |    Shaped comb-allpass filter for channel decorrelation
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |    Redistribution and use in source and binary forms, with or without
 | 
						
						
						
							|  |  |    modification, are permitted provided that the following conditions are
 | 
						
						
						
							|  |  |    met:
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |    1. Redistributions of source code must retain the above copyright notice,
 | 
						
						
						
							|  |  |    this list of conditions and the following disclaimer.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |    2. 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.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |    3. The name of the author may not be used to endorse or promote products
 | 
						
						
						
							|  |  |    derived from this software without specific prior written permission.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 | 
						
						
						
							|  |  | */
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | /*
 | 
						
						
						
							|  |  | The algorithm implemented here is described in:
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | * J.-M. Valin, Perceptually-Motivated Nonlinear Channel Decorrelation For 
 | 
						
						
						
							|  |  |   Stereo Acoustic Echo Cancellation, Accepted for Joint Workshop on 
 | 
						
						
						
							|  |  |   Handsfree Speech Communication and Microphone Arrays (HSCMA), 2008.
 | 
						
						
						
							|  |  |   http://people.xiph.org/~jm/papers/valin_hscma2008.pdf
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | */
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | #ifdef HAVE_CONFIG_H
 | 
						
						
						
							|  |  | #include "config.h"
 | 
						
						
						
							|  |  | #endif
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | #include "speex/speex_echo.h"
 | 
						
						
						
							|  |  | #include "vorbis_psy.h"
 | 
						
						
						
							|  |  | #include "arch.h"
 | 
						
						
						
							|  |  | #include "os_support.h"
 | 
						
						
						
							|  |  | #include "smallft.h"
 | 
						
						
						
							|  |  | #include <math.h>
 | 
						
						
						
							|  |  | #include <stdlib.h>
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | #define ALLPASS_ORDER 20
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | struct SpeexDecorrState_ {
 | 
						
						
						
							|  |  |    int rate;
 | 
						
						
						
							|  |  |    int channels;
 | 
						
						
						
							|  |  |    int frame_size;
 | 
						
						
						
							|  |  | #ifdef VORBIS_PSYCHO
 | 
						
						
						
							|  |  |    VorbisPsy *psy;
 | 
						
						
						
							|  |  |    struct drft_lookup lookup;
 | 
						
						
						
							|  |  |    float *wola_mem;
 | 
						
						
						
							|  |  |    float *curve;
 | 
						
						
						
							|  |  | #endif
 | 
						
						
						
							|  |  |    float *vorbis_win;
 | 
						
						
						
							|  |  |    int    seed;
 | 
						
						
						
							|  |  |    float *y;
 | 
						
						
						
							|  |  |    
 | 
						
						
						
							|  |  |    /* Per-channel stuff */
 | 
						
						
						
							|  |  |    float *buff;
 | 
						
						
						
							|  |  |    float (*ring)[ALLPASS_ORDER];
 | 
						
						
						
							|  |  |    int *ringID;
 | 
						
						
						
							|  |  |    int *order;
 | 
						
						
						
							|  |  |    float *alpha;
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | EXPORT SpeexDecorrState *speex_decorrelate_new(int rate, int channels, int frame_size)
 | 
						
						
						
							|  |  | {
 | 
						
						
						
							|  |  |    int i, ch;
 | 
						
						
						
							|  |  |    SpeexDecorrState *st = speex_alloc(sizeof(SpeexDecorrState));
 | 
						
						
						
							|  |  |    st->rate = rate;
 | 
						
						
						
							|  |  |    st->channels = channels;
 | 
						
						
						
							|  |  |    st->frame_size = frame_size;
 | 
						
						
						
							|  |  | #ifdef VORBIS_PSYCHO
 | 
						
						
						
							|  |  |    st->psy = vorbis_psy_init(rate, 2*frame_size);
 | 
						
						
						
							|  |  |    spx_drft_init(&st->lookup, 2*frame_size);
 | 
						
						
						
							|  |  |    st->wola_mem = speex_alloc(frame_size*sizeof(float));
 | 
						
						
						
							|  |  |    st->curve = speex_alloc(frame_size*sizeof(float));
 | 
						
						
						
							|  |  | #endif
 | 
						
						
						
							|  |  |    st->y = speex_alloc(frame_size*sizeof(float));
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |    st->buff = speex_alloc(channels*2*frame_size*sizeof(float));
 | 
						
						
						
							|  |  |    st->ringID = speex_alloc(channels*sizeof(int));
 | 
						
						
						
							|  |  |    st->order = speex_alloc(channels*sizeof(int));
 | 
						
						
						
							|  |  |    st->alpha = speex_alloc(channels*sizeof(float));
 | 
						
						
						
							|  |  |    st->ring = speex_alloc(channels*ALLPASS_ORDER*sizeof(float));
 | 
						
						
						
							|  |  |    
 | 
						
						
						
							|  |  |    /*FIXME: The +20 is there only as a kludge for ALL_PASS_OLA*/
 | 
						
						
						
							|  |  |    st->vorbis_win = speex_alloc((2*frame_size+20)*sizeof(float));
 | 
						
						
						
							|  |  |    for (i=0;i<2*frame_size;i++)
 | 
						
						
						
							|  |  |       st->vorbis_win[i] = sin(.5*M_PI* sin(M_PI*i/(2*frame_size))*sin(M_PI*i/(2*frame_size)) );
 | 
						
						
						
							|  |  |    st->seed = rand();
 | 
						
						
						
							|  |  |    
 | 
						
						
						
							|  |  |    for (ch=0;ch<channels;ch++)
 | 
						
						
						
							|  |  |    {
 | 
						
						
						
							|  |  |       for (i=0;i<ALLPASS_ORDER;i++)
 | 
						
						
						
							|  |  |          st->ring[ch][i] = 0;
 | 
						
						
						
							|  |  |       st->ringID[ch] = 0;
 | 
						
						
						
							|  |  |       st->alpha[ch] = 0;
 | 
						
						
						
							|  |  |       st->order[ch] = 10;
 | 
						
						
						
							|  |  |    }
 | 
						
						
						
							|  |  |    return st;
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | static float uni_rand(int *seed)
 | 
						
						
						
							|  |  | {
 | 
						
						
						
							|  |  |    const unsigned int jflone = 0x3f800000;
 | 
						
						
						
							|  |  |    const unsigned int jflmsk = 0x007fffff;
 | 
						
						
						
							|  |  |    union {int i; float f;} ran;
 | 
						
						
						
							|  |  |    *seed = 1664525 * *seed + 1013904223;
 | 
						
						
						
							|  |  |    ran.i = jflone | (jflmsk & *seed);
 | 
						
						
						
							|  |  |    ran.f -= 1.5;
 | 
						
						
						
							|  |  |    return 2*ran.f;
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | static unsigned int irand(int *seed)
 | 
						
						
						
							|  |  | {
 | 
						
						
						
							|  |  |    *seed = 1664525 * *seed + 1013904223;
 | 
						
						
						
							|  |  |    return ((unsigned int)*seed)>>16;
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | EXPORT void speex_decorrelate(SpeexDecorrState *st, const spx_int16_t *in, spx_int16_t *out, int strength)
 | 
						
						
						
							|  |  | {
 | 
						
						
						
							|  |  |    int ch;
 | 
						
						
						
							|  |  |    float amount;
 | 
						
						
						
							|  |  |    
 | 
						
						
						
							|  |  |    if (strength<0)
 | 
						
						
						
							|  |  |       strength = 0;
 | 
						
						
						
							|  |  |    if (strength>100)
 | 
						
						
						
							|  |  |       strength = 100;
 | 
						
						
						
							|  |  |    
 | 
						
						
						
							|  |  |    amount = .01*strength;
 | 
						
						
						
							|  |  |    for (ch=0;ch<st->channels;ch++)
 | 
						
						
						
							|  |  |    {
 | 
						
						
						
							|  |  |       int i;
 | 
						
						
						
							|  |  |       //int N=2*st->frame_size;
 | 
						
						
						
							|  |  |       float beta, beta2;
 | 
						
						
						
							|  |  |       float *x;
 | 
						
						
						
							|  |  |       float max_alpha = 0;
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       float *buff;
 | 
						
						
						
							|  |  |       float *ring;
 | 
						
						
						
							|  |  |       int ringID;
 | 
						
						
						
							|  |  |       int order;
 | 
						
						
						
							|  |  |       float alpha;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       buff = st->buff+ch*2*st->frame_size;
 | 
						
						
						
							|  |  |       ring = st->ring[ch];
 | 
						
						
						
							|  |  |       ringID = st->ringID[ch];
 | 
						
						
						
							|  |  |       order = st->order[ch];
 | 
						
						
						
							|  |  |       alpha = st->alpha[ch];
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       for (i=0;i<st->frame_size;i++)
 | 
						
						
						
							|  |  |          buff[i] = buff[i+st->frame_size];
 | 
						
						
						
							|  |  |       for (i=0;i<st->frame_size;i++)
 | 
						
						
						
							|  |  |          buff[i+st->frame_size] = in[i*st->channels+ch];
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       x = buff+st->frame_size;
 | 
						
						
						
							|  |  |       beta = 1.-.3*amount*amount;
 | 
						
						
						
							|  |  |       if (amount>1)
 | 
						
						
						
							|  |  |          beta = 1-sqrt(.4*amount);
 | 
						
						
						
							|  |  |       else
 | 
						
						
						
							|  |  |          beta = 1-0.63246*amount;
 | 
						
						
						
							|  |  |       if (beta<0)
 | 
						
						
						
							|  |  |          beta = 0;
 | 
						
						
						
							|  |  |    
 | 
						
						
						
							|  |  |       beta2 = beta;
 | 
						
						
						
							|  |  |       for (i=0;i<st->frame_size;i++)
 | 
						
						
						
							|  |  |       {
 | 
						
						
						
							|  |  |          st->y[i] = alpha*(x[i-ALLPASS_ORDER+order]-beta*x[i-ALLPASS_ORDER+order-1])*st->vorbis_win[st->frame_size+i+order] 
 | 
						
						
						
							|  |  |                + x[i-ALLPASS_ORDER]*st->vorbis_win[st->frame_size+i] 
 | 
						
						
						
							|  |  |                - alpha*(ring[ringID]
 | 
						
						
						
							|  |  |                - beta*ring[ringID+1>=order?0:ringID+1]);
 | 
						
						
						
							|  |  |          ring[ringID++]=st->y[i];
 | 
						
						
						
							|  |  |          st->y[i] *= st->vorbis_win[st->frame_size+i];
 | 
						
						
						
							|  |  |          if (ringID>=order)
 | 
						
						
						
							|  |  |             ringID=0;
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |       order = order+(irand(&st->seed)%3)-1;
 | 
						
						
						
							|  |  |       if (order < 5)
 | 
						
						
						
							|  |  |          order = 5;
 | 
						
						
						
							|  |  |       if (order > 10)
 | 
						
						
						
							|  |  |          order = 10;
 | 
						
						
						
							|  |  |       /*order = 5+(irand(&st->seed)%6);*/
 | 
						
						
						
							|  |  |       max_alpha = pow(.96+.04*(amount-1),order);
 | 
						
						
						
							|  |  |       if (max_alpha > .98/(1.+beta2))
 | 
						
						
						
							|  |  |          max_alpha = .98/(1.+beta2);
 | 
						
						
						
							|  |  |    
 | 
						
						
						
							|  |  |       alpha = alpha + .4*uni_rand(&st->seed);
 | 
						
						
						
							|  |  |       if (alpha > max_alpha)
 | 
						
						
						
							|  |  |          alpha = max_alpha;
 | 
						
						
						
							|  |  |       if (alpha < -max_alpha)
 | 
						
						
						
							|  |  |          alpha = -max_alpha;
 | 
						
						
						
							|  |  |       for (i=0;i<ALLPASS_ORDER;i++)
 | 
						
						
						
							|  |  |          ring[i] = 0;
 | 
						
						
						
							|  |  |       ringID = 0;
 | 
						
						
						
							|  |  |       for (i=0;i<st->frame_size;i++)
 | 
						
						
						
							|  |  |       {
 | 
						
						
						
							|  |  |          float tmp =  alpha*(x[i-ALLPASS_ORDER+order]-beta*x[i-ALLPASS_ORDER+order-1])*st->vorbis_win[i+order] 
 | 
						
						
						
							|  |  |                + x[i-ALLPASS_ORDER]*st->vorbis_win[i] 
 | 
						
						
						
							|  |  |                - alpha*(ring[ringID]
 | 
						
						
						
							|  |  |                - beta*ring[ringID+1>=order?0:ringID+1]);
 | 
						
						
						
							|  |  |          ring[ringID++]=tmp;
 | 
						
						
						
							|  |  |          tmp *= st->vorbis_win[i];
 | 
						
						
						
							|  |  |          if (ringID>=order)
 | 
						
						
						
							|  |  |             ringID=0;
 | 
						
						
						
							|  |  |          st->y[i] += tmp;
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |    
 | 
						
						
						
							|  |  | #ifdef VORBIS_PSYCHO
 | 
						
						
						
							|  |  |       float frame[N];
 | 
						
						
						
							|  |  |       float scale = 1./N;
 | 
						
						
						
							|  |  |       for (i=0;i<2*st->frame_size;i++)
 | 
						
						
						
							|  |  |          frame[i] = buff[i];
 | 
						
						
						
							|  |  |    //float coef = .5*0.78130;
 | 
						
						
						
							|  |  |       float coef = M_PI*0.075063 * 0.93763 * amount * .8 * 0.707;
 | 
						
						
						
							|  |  |       compute_curve(st->psy, buff, st->curve);
 | 
						
						
						
							|  |  |       for (i=1;i<st->frame_size;i++)
 | 
						
						
						
							|  |  |       {
 | 
						
						
						
							|  |  |          float x1,x2;
 | 
						
						
						
							|  |  |          float gain;
 | 
						
						
						
							|  |  |          do {
 | 
						
						
						
							|  |  |             x1 = uni_rand(&st->seed);
 | 
						
						
						
							|  |  |             x2 = uni_rand(&st->seed);
 | 
						
						
						
							|  |  |          } while (x1*x1+x2*x2 > 1.);
 | 
						
						
						
							|  |  |          gain = coef*sqrt(.1+st->curve[i]);
 | 
						
						
						
							|  |  |          frame[2*i-1] = gain*x1;
 | 
						
						
						
							|  |  |          frame[2*i] = gain*x2;
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |       frame[0] = coef*uni_rand(&st->seed)*sqrt(.1+st->curve[0]);
 | 
						
						
						
							|  |  |       frame[2*st->frame_size-1] = coef*uni_rand(&st->seed)*sqrt(.1+st->curve[st->frame_size-1]);
 | 
						
						
						
							|  |  |       spx_drft_backward(&st->lookup,frame);
 | 
						
						
						
							|  |  |       for (i=0;i<2*st->frame_size;i++)
 | 
						
						
						
							|  |  |          frame[i] *= st->vorbis_win[i];
 | 
						
						
						
							|  |  | #endif
 | 
						
						
						
							|  |  |    
 | 
						
						
						
							|  |  |       for (i=0;i<st->frame_size;i++)
 | 
						
						
						
							|  |  |       {
 | 
						
						
						
							|  |  | #ifdef VORBIS_PSYCHO
 | 
						
						
						
							|  |  |          float tmp = st->y[i] + frame[i] + st->wola_mem[i];
 | 
						
						
						
							|  |  |          st->wola_mem[i] = frame[i+st->frame_size];
 | 
						
						
						
							|  |  | #else
 | 
						
						
						
							|  |  |          float tmp = st->y[i];
 | 
						
						
						
							|  |  | #endif
 | 
						
						
						
							|  |  |          if (tmp>32767)
 | 
						
						
						
							|  |  |             tmp = 32767;
 | 
						
						
						
							|  |  |          if (tmp < -32767)
 | 
						
						
						
							|  |  |             tmp = -32767;
 | 
						
						
						
							|  |  |          out[i*st->channels+ch] = tmp;
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       st->ringID[ch] = ringID;
 | 
						
						
						
							|  |  |       st->order[ch] = order;
 | 
						
						
						
							|  |  |       st->alpha[ch] = alpha;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |    }
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | EXPORT void speex_decorrelate_destroy(SpeexDecorrState *st)
 | 
						
						
						
							|  |  | {
 | 
						
						
						
							|  |  | #ifdef VORBIS_PSYCHO
 | 
						
						
						
							|  |  |    vorbis_psy_destroy(st->psy);
 | 
						
						
						
							|  |  |    speex_free(st->wola_mem);
 | 
						
						
						
							|  |  |    speex_free(st->curve);
 | 
						
						
						
							|  |  | #endif
 | 
						
						
						
							|  |  |    speex_free(st->buff);
 | 
						
						
						
							|  |  |    speex_free(st->ring);
 | 
						
						
						
							|  |  |    speex_free(st->ringID);
 | 
						
						
						
							|  |  |    speex_free(st->alpha);
 | 
						
						
						
							|  |  |    speex_free(st->vorbis_win);
 | 
						
						
						
							|  |  |    speex_free(st->order);
 | 
						
						
						
							|  |  |    speex_free(st->y);
 | 
						
						
						
							|  |  |    speex_free(st);
 | 
						
						
						
							|  |  | }
 |