// license:BSD-3-Clause
// copyright-holders:pSXAuthor, R. Belmont
#include "emu.h"
#include "spureverb.h"
//
//
//
//#define use_intrinsics
#ifdef use_intrinsics
#include <mmintrin.h>
#endif
//
//
//
static constexpr int clamp(int v)
{
return (v<-32768) ? -32768 : (v>32767) ? 32767 : v;
}
//
//
//
spu_device::reverb::reverb(const int hz, const int maxdelay)
: yp(0),
max_delay(maxdelay),
sound_hz(hz)
{
for (int c=0; c<2; c++)
{
for (int f=0; f<4; f++) {
y[c][f]=new signed short [maxdelay];
memset(y[c][f], 0, sizeof(signed short) * maxdelay);
}
x[c]=new signed short [maxdelay];
memset(x[c], 0, sizeof(signed short) * maxdelay);
ax[c]=new signed short [maxdelay];
memset(ax[c], 0, sizeof(signed short) * maxdelay);
ay[c]=new signed short [maxdelay];
memset(ay[c], 0, sizeof(signed short) * maxdelay);
}
memset(bx1,0,sizeof(bx1));
memset(by1,0,sizeof(by1));
}
//
//
//
spu_device::reverb::~reverb()
{
for (int c=0; c<2; c++)
{
for (int f=0; f<4; f++)
global_free_array(y[c][f]);
global_free_array(x[c]);
global_free_array(ax[c]);
global_free_array(ay[c]);
}
}
//
//
//
void spu_device::reverb::bandpass(signed short *sp,
const reverb_params *rp,
const unsigned int sz)
{
int band_pole=(int)(rp->band_pole*32767),
band_gain=(int)(rp->band_gain*32767);
// Bandpass
int xp=yp;
for (unsigned int i=0; i<(sz>>2); i++, sp+=2)
{
for (int c=0; c<2; c++)
{
int x1=(xp-1)&(max_delay-1),
bv;
bv=sp[c]+bx1[c][1]+((band_pole*x[c][x1])>>15);
bv=(bv*band_gain)>>15;
x[c][xp]=clamp(bv);
bx1[c][1]=bx1[c][0];
bx1[c][0]=sp[c];
}
xp=(xp+1)&(max_delay-1);
}
}
void spu_device::reverb::comb_allpass1(signed short *sp,
signed short *dp,
const comb_param &comb_delay,
const int comb_gain,
const int allpass_delay,
const int allpass_gain,
const int *rvol,
const unsigned int sz)
{
for (unsigned int i=0; i<(sz>>2); i++, sp+=2, dp+=2)
{
for (int c=0; c<2; c++)
{
// Comb
int v=0;
for (int f=0; f<4; f++)
{
int yck=(yp-comb_delay[c][f])&(max_delay-1);
y[c][f][yp]=clamp(x[c][yck]+((comb_gain*y[c][f][yck])>>15));
v+=y[c][f][yp];
}
v>>=2;
// Allpass
if (allpass_delay)
{
ax[c][yp]=v;
int ypa=(yp-allpass_delay)&(max_delay-1);
v=((allpass_gain*(ay[c][ypa]-x[c][yp]))>>15)+ax[c][ypa];
v=clamp(v);
ay[c][yp]=v;
}
// Output
dp[c]=clamp(((v*rvol[c])>>15)+dp[c]+sp[c]);
}
yp=(yp+1)&(max_delay-1);
}
}
//
//
//
void spu_device::reverb::comb_allpass4(signed short *sp,
signed short *dp,
const comb_param &comb_delay,
const int comb_gain,
const int allpass_delay,
const int allpass_gain,
const int *rvol,
const unsigned int sz)
{
#ifdef use_intrinsics
__m64 cg=_mm_set1_pi16(comb_gain),
ag=_mm_set1_pi16(allpass_gain),
rv[2];
rv[0]=_mm_set1_pi16(rvol[0]);
rv[1]=_mm_set1_pi16(rvol[1]);
for (unsigned int i=0; i<(sz>>4); i++, sp+=2<<2, dp+=2<<2)
{
__m64 dv[2];
for (int c=0; c<2; c++)
{
// Comb
__m64 v=_mm_setzero_si64();
for (int f=0; f<4; f++)
{
int yck=(yp-comb_delay[c][f])&(max_delay-1);
__m64 xv=*(__m64 *)(&x[c][yck]),
yv=*(__m64 *)(&y[c][f][yck]);
yv=_mm_mulhi_pi16(yv,cg);
yv=_mm_adds_pi16(yv,yv);
yv=_mm_adds_pi16(xv,yv);
*((__m64 *)&y[c][f][yp])=yv;
yv=_mm_srai_pi16(yv,2);
v=_mm_adds_pi16(v,yv);
}
// Allpass
if (allpass_delay)
{
*((__m64 *)&ax[c][yp])=v;
int ypa=(yp-allpass_delay)&(max_delay-1);
__m64 ayv=*(__m64 *)&ay[c][ypa],
xv=*(__m64 *)&x[c][yp],
axv=*(__m64 *)&ax[c][ypa];
ayv=_mm_subs_pi16(ayv,xv);
ayv=_mm_mulhi_pi16(ayv,ag);
ayv=_mm_adds_pi16(ayv,ayv);
v=_mm_adds_pi16(ayv,axv);
*((__m64 *)&ay[c][yp])=v;
}
// Output
dv[c]=_mm_mulhi_pi16(v,rv[c]);
dv[c]=_mm_adds_pi16(dv[c],dv[c]);
}
__m64 dv1=_mm_unpacklo_pi16(dv[0],dv[1]),
dv2=_mm_unpackhi_pi16(dv[0],dv[1]),
d1=*(__m64 *)&dp[0],
d2=*(__m64 *)&dp[4],
s1=*(__m64 *)&sp[0],
s2=*(__m64 *)&sp[4];
d1=_mm_adds_pi16(d1,s1);
d2=_mm_adds_pi16(d2,s2);
d1=_mm_adds_pi16(d1,dv1);
d2=_mm_adds_pi16(d2,dv2);
*(__m64 *)&dp[0]=d1;
*(__m64 *)&dp[4]=d2;
yp=(yp+4)&(max_delay-1);
}
_mm_empty();
#endif
}
//
//
//
void spu_device::reverb::comb_allpass(signed short *sp,
signed short *dp,
const reverb_params *rp,
const int wetvol_l,
const int wetvol_r,
const unsigned int _sz)
{
unsigned int sz=_sz;
comb_param comb_delay;
int comb_gain=(int)(rp->comb_gain*32767),
allpass_delay=(int)(((rp->allpass_delay/1000.0f)*sound_hz))&~3,
allpass_gain=(int)(rp->allpass_gain*32767),
rvol[2]={ (signed short)wetvol_l,
(signed short)wetvol_r };
for (int i=0; i<4; i++)
for (int c=0; c<2; c++)
comb_delay[c][i]=(int)(((rp->comb_delay[c][i]/1000.0f)*sound_hz))&~3;
#ifdef use_intrinsics
if (yp&3)
{
unsigned int n=min(sz,(unsigned int)(4-(yp&3))<<2);
comb_allpass1(sp,dp,
(const comb_param &)comb_delay,
comb_gain,
allpass_delay,
allpass_gain,
rvol,
n);
sp+=(n>>1);
dp+=(n>>1);
sz-=n;
}
if (sz>=16)
{
unsigned int n=sz&~15;
comb_allpass4(sp,dp,
(const comb_param &)comb_delay,
comb_gain,
allpass_delay,
allpass_gain,
rvol,
n);
sp+=n>>1;
dp+=n>>1;
sz-=n;
}
if (sz)
{
comb_allpass1(sp,dp,
(const comb_param &)comb_delay,
comb_gain,
allpass_delay,
allpass_gain,
rvol,
sz);
}
#else
comb_allpass1(sp,dp,
(const comb_param &)comb_delay,
comb_gain,
allpass_delay,
allpass_gain,
rvol,
sz);
#endif
}
//
//
//
void spu_device::reverb::process(signed short *output,
signed short *reverb_input,
const reverb_params *rp,
const int wetvol_l,
const int wetvol_r,
const unsigned int sz)
{
signed short *sp=(signed short *)reverb_input,
*dp=(signed short *)output;
if (rp->band_gain>0.0f)
{
// Do reverb processing
bandpass(sp,rp,sz);
comb_allpass(sp,dp,rp,wetvol_l,wetvol_r,sz);
} else
{
// Reverb disabled - just mix the input to the output
for (unsigned int i=0; i<(sz>>2); i++)
{
output[0]=clamp(output[0]+reverb_input[0]);
output[1]=clamp(output[1]+reverb_input[1]);
output+=2;
reverb_input+=2;
}
}
}