|
|
/* 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>
|
|
|
|
|
|
#ifndef M_PI
|
|
|
#define M_PI 3.14159265358979323846 /* pi */
|
|
|
#endif
|
|
|
|
|
|
#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;
|
|
|
|
|
|
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);
|
|
|
}
|