Use a different method for HRTF mixing

This new method mixes sources normally into a 14-channel buffer with the
channels placed all around the listener. HRTF is then applied to the channels
given their positions and written to a 2-channel buffer, which gets written out
to the device.

This method has the benefit that HRTF processing becomes more scalable. The
costly HRTF filters are applied to the 14-channel buffer after the mix is done,
turning it into a post-process with a fixed overhead. Mixing sources is done
with normal non-HRTF methods, so increasing the number of playing sources only
incurs normal mixing costs.

Another benefit is that it improves B-Format playback since the soundfield gets
mixed into speakers covering all three dimensions, which then get filtered
based on their locations.

The main downside to this is that the spatial resolution of the HRTF dataset
does not play a big role anymore. However, the hope is that with ambisonics-
based panning, the perceptual position of panned sounds will still be good. It
is also an option to increase the number of virtual channels for systems that
can handle it, or maybe even decrease it for weaker systems.
This commit is contained in:
Chris Robinson 2014-11-22 04:20:17 -08:00
parent 38383671d7
commit a27e5e1652
15 changed files with 198 additions and 558 deletions

View File

@ -1712,6 +1712,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
enum DevFmtType oldType; enum DevFmtType oldType;
ALCuint oldFreq; ALCuint oldFreq;
FPUCtl oldMode; FPUCtl oldMode;
size_t size;
// Check for attributes // Check for attributes
if(device->Type == Loopback) if(device->Type == Loopback)
@ -1885,6 +1886,9 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
if((device->Flags&DEVICE_RUNNING)) if((device->Flags&DEVICE_RUNNING))
return ALC_NO_ERROR; return ALC_NO_ERROR;
al_free(device->DryBuffer);
device->DryBuffer = NULL;
UpdateClockBase(device); UpdateClockBase(device);
if(device->Type != Loopback) if(device->Type != Loopback)
@ -1992,11 +1996,15 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
aluInitPanning(device); aluInitPanning(device);
al_free(device->DryBuffer); /* With HRTF enabled, the channels are virtual and get positioned around
device->DryBuffer = al_calloc(16, sizeof(device->DryBuffer[0]) * device->NumChannels); * the virtual listener. Two extra channels are allocated for the actual
* HRTF-filtered output.
*/
size = sizeof(device->DryBuffer[0]) * (device->NumChannels + (device->Hrtf ? 2 : 0));
device->DryBuffer = al_calloc(16, size);
if(!device->DryBuffer) if(!device->DryBuffer)
{ {
ERR("Failed to allocate "SZFMT" bytes for mix buffer\n", sizeof(device->DryBuffer[0]) * device->NumChannels); ERR("Failed to allocate "SZFMT" bytes for mix buffer\n", size);
return ALC_INVALID_DEVICE; return ALC_INVALID_DEVICE;
} }

163
Alc/ALu.c
View File

@ -36,6 +36,8 @@
#include "hrtf.h" #include "hrtf.h"
#include "static_assert.h" #include "static_assert.h"
#include "mixer_defs.h"
#include "backends/base.h" #include "backends/base.h"
#include "midi/base.h" #include "midi/base.h"
@ -83,6 +85,21 @@ extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
extern inline ALfloat cubic(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat mu); extern inline ALfloat cubic(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat mu);
static inline HrtfMixerFunc SelectHrtfMixer(void)
{
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return MixHrtf_SSE;
#endif
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return MixHrtf_Neon;
#endif
return MixHrtf_C;
}
static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector) static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
{ {
outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1]; outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
@ -492,37 +509,6 @@ ALvoid CalcNonAttnSourceParams(ALvoice *voice, const ALsource *ALSource, const A
voice->IsHrtf = AL_FALSE; voice->IsHrtf = AL_FALSE;
} }
else if(Device->Hrtf)
{
for(c = 0;c < num_channels;c++)
{
if(chans[c].channel == LFE)
{
/* Skip LFE */
voice->Direct.Mix.Hrtf.Params[c].Delay[0] = 0;
voice->Direct.Mix.Hrtf.Params[c].Delay[1] = 0;
for(i = 0;i < HRIR_LENGTH;i++)
{
voice->Direct.Mix.Hrtf.Params[c].Coeffs[i][0] = 0.0f;
voice->Direct.Mix.Hrtf.Params[c].Coeffs[i][1] = 0.0f;
}
}
else
{
/* Get the static HRIR coefficients and delays for this
* channel. */
GetLerpedHrtfCoeffs(Device->Hrtf,
chans[c].elevation, chans[c].angle, 1.0f, DryGain,
voice->Direct.Mix.Hrtf.Params[c].Coeffs,
voice->Direct.Mix.Hrtf.Params[c].Delay);
}
}
voice->Direct.Counter = 0;
voice->Direct.Moving = AL_TRUE;
voice->Direct.Mix.Hrtf.IrSize = GetHrtfIrSize(Device->Hrtf);
voice->IsHrtf = AL_TRUE;
}
else else
{ {
for(c = 0;c < num_channels;c++) for(c = 0;c < num_channels;c++)
@ -929,70 +915,6 @@ ALvoid CalcSourceParams(ALvoice *voice, const ALsource *ALSource, const ALCconte
BufferListItem = BufferListItem->next; BufferListItem = BufferListItem->next;
} }
if(Device->Hrtf)
{
/* Use a binaural HRTF algorithm for stereo headphone playback */
ALfloat delta, ev = 0.0f, az = 0.0f;
ALfloat radius = ALSource->Radius;
ALfloat dirfact = 1.0f;
if(Distance > FLT_EPSILON)
{
ALfloat invlen = 1.0f/Distance;
Position[0] *= invlen;
Position[1] *= invlen;
Position[2] *= invlen;
/* Calculate elevation and azimuth only when the source is not at
* the listener. This prevents +0 and -0 Z from producing
* inconsistent panning. Also, clamp Y in case FP precision errors
* cause it to land outside of -1..+1. */
ev = asinf(clampf(Position[1], -1.0f, 1.0f));
az = atan2f(Position[0], -Position[2]*ZScale);
}
if(radius > Distance)
dirfact *= Distance / radius;
/* Check to see if the HRIR is already moving. */
if(voice->Direct.Moving)
{
/* Calculate the normalized HRTF transition factor (delta). */
delta = CalcHrtfDelta(voice->Direct.Mix.Hrtf.Gain, DryGain,
voice->Direct.Mix.Hrtf.Dir, Position);
/* If the delta is large enough, get the moving HRIR target
* coefficients, target delays, steppping values, and counter. */
if(delta > 0.001f)
{
ALuint counter = GetMovingHrtfCoeffs(Device->Hrtf,
ev, az, dirfact, DryGain, delta, voice->Direct.Counter,
voice->Direct.Mix.Hrtf.Params[0].Coeffs, voice->Direct.Mix.Hrtf.Params[0].Delay,
voice->Direct.Mix.Hrtf.Params[0].CoeffStep, voice->Direct.Mix.Hrtf.Params[0].DelayStep
);
voice->Direct.Counter = counter;
voice->Direct.Mix.Hrtf.Gain = DryGain;
voice->Direct.Mix.Hrtf.Dir[0] = Position[0];
voice->Direct.Mix.Hrtf.Dir[1] = Position[1];
voice->Direct.Mix.Hrtf.Dir[2] = Position[2];
}
}
else
{
/* Get the initial (static) HRIR coefficients and delays. */
GetLerpedHrtfCoeffs(Device->Hrtf, ev, az, dirfact, DryGain,
voice->Direct.Mix.Hrtf.Params[0].Coeffs,
voice->Direct.Mix.Hrtf.Params[0].Delay);
voice->Direct.Counter = 0;
voice->Direct.Moving = AL_TRUE;
voice->Direct.Mix.Hrtf.Gain = DryGain;
voice->Direct.Mix.Hrtf.Dir[0] = Position[0];
voice->Direct.Mix.Hrtf.Dir[1] = Position[1];
voice->Direct.Mix.Hrtf.Dir[2] = Position[2];
}
voice->Direct.Mix.Hrtf.IrSize = GetHrtfIrSize(Device->Hrtf);
voice->IsHrtf = AL_TRUE;
}
else
{ {
MixGains *gains = voice->Direct.Mix.Gains[0]; MixGains *gains = voice->Direct.Mix.Gains[0];
ALfloat radius = ALSource->Radius; ALfloat radius = ALSource->Radius;
@ -1125,14 +1047,24 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
while(size > 0) while(size > 0)
{ {
ALuint outchanoffset = 0;
ALuint outchancount = device->NumChannels;
IncrementRef(&device->MixCount); IncrementRef(&device->MixCount);
SamplesToDo = minu(size, BUFFERSIZE); SamplesToDo = minu(size, BUFFERSIZE);
for(c = 0;c < device->NumChannels;c++) for(c = 0;c < device->NumChannels;c++)
memset(device->DryBuffer[c], 0, SamplesToDo*sizeof(ALfloat)); memset(device->DryBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
if(device->Hrtf)
{
outchanoffset = device->NumChannels;
outchancount = 2;
for(c = 0;c < outchancount;c++)
memset(device->DryBuffer[outchanoffset+c], 0, SamplesToDo*sizeof(ALfloat));
}
V0(device->Backend,lock)(); V0(device->Backend,lock)();
V(device->Synth,process)(SamplesToDo, device->DryBuffer); V(device->Synth,process)(SamplesToDo, &device->DryBuffer[outchanoffset]);
ctx = ATOMIC_LOAD(&device->ContextList); ctx = ATOMIC_LOAD(&device->ContextList);
while(ctx) while(ctx)
@ -1212,7 +1144,16 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
device->SamplesDone %= device->Frequency; device->SamplesDone %= device->Frequency;
V0(device->Backend,unlock)(); V0(device->Backend,unlock)();
if(device->Bs2b) if(device->Hrtf)
{
HrtfMixerFunc HrtfMix = SelectHrtfMixer();
ALuint irsize = GetHrtfIrSize(device->Hrtf);
for(c = 0;c < device->NumChannels;c++)
HrtfMix(&device->DryBuffer[outchanoffset], device->DryBuffer[c], device->Hrtf_Offset, irsize,
&device->Hrtf_Params[c], &device->Hrtf_State[c], SamplesToDo);
device->Hrtf_Offset += SamplesToDo;
}
else if(device->Bs2b)
{ {
/* Apply binaural/crossfeed filter */ /* Apply binaural/crossfeed filter */
for(i = 0;i < SamplesToDo;i++) for(i = 0;i < SamplesToDo;i++)
@ -1231,32 +1172,32 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
switch(device->FmtType) switch(device->FmtType)
{ {
case DevFmtByte: case DevFmtByte:
Write_ALbyte(device->DryBuffer, buffer, SamplesToDo, device->NumChannels); Write_ALbyte(&device->DryBuffer[outchanoffset], buffer, SamplesToDo, outchancount);
buffer = (char*)buffer + SamplesToDo*device->NumChannels*sizeof(ALbyte); buffer = (char*)buffer + SamplesToDo*outchancount*sizeof(ALbyte);
break; break;
case DevFmtUByte: case DevFmtUByte:
Write_ALubyte(device->DryBuffer, buffer, SamplesToDo, device->NumChannels); Write_ALubyte(&device->DryBuffer[outchanoffset], buffer, SamplesToDo, outchancount);
buffer = (char*)buffer + SamplesToDo*device->NumChannels*sizeof(ALubyte); buffer = (char*)buffer + SamplesToDo*outchancount*sizeof(ALubyte);
break; break;
case DevFmtShort: case DevFmtShort:
Write_ALshort(device->DryBuffer, buffer, SamplesToDo, device->NumChannels); Write_ALshort(&device->DryBuffer[outchanoffset], buffer, SamplesToDo, outchancount);
buffer = (char*)buffer + SamplesToDo*device->NumChannels*sizeof(ALshort); buffer = (char*)buffer + SamplesToDo*outchancount*sizeof(ALshort);
break; break;
case DevFmtUShort: case DevFmtUShort:
Write_ALushort(device->DryBuffer, buffer, SamplesToDo, device->NumChannels); Write_ALushort(&device->DryBuffer[outchanoffset], buffer, SamplesToDo, outchancount);
buffer = (char*)buffer + SamplesToDo*device->NumChannels*sizeof(ALushort); buffer = (char*)buffer + SamplesToDo*outchancount*sizeof(ALushort);
break; break;
case DevFmtInt: case DevFmtInt:
Write_ALint(device->DryBuffer, buffer, SamplesToDo, device->NumChannels); Write_ALint(&device->DryBuffer[outchanoffset], buffer, SamplesToDo, outchancount);
buffer = (char*)buffer + SamplesToDo*device->NumChannels*sizeof(ALint); buffer = (char*)buffer + SamplesToDo*outchancount*sizeof(ALint);
break; break;
case DevFmtUInt: case DevFmtUInt:
Write_ALuint(device->DryBuffer, buffer, SamplesToDo, device->NumChannels); Write_ALuint(&device->DryBuffer[outchanoffset], buffer, SamplesToDo, outchancount);
buffer = (char*)buffer + SamplesToDo*device->NumChannels*sizeof(ALuint); buffer = (char*)buffer + SamplesToDo*outchancount*sizeof(ALuint);
break; break;
case DevFmtFloat: case DevFmtFloat:
Write_ALfloat(device->DryBuffer, buffer, SamplesToDo, device->NumChannels); Write_ALfloat(&device->DryBuffer[outchanoffset], buffer, SamplesToDo, outchancount);
buffer = (char*)buffer + SamplesToDo*device->NumChannels*sizeof(ALfloat); buffer = (char*)buffer + SamplesToDo*outchancount*sizeof(ALfloat);
break; break;
} }
} }

View File

@ -58,10 +58,6 @@ struct Hrtf {
static const ALchar magicMarker00[8] = "MinPHR00"; static const ALchar magicMarker00[8] = "MinPHR00";
static const ALchar magicMarker01[8] = "MinPHR01"; static const ALchar magicMarker01[8] = "MinPHR01";
/* First value for pass-through coefficients (remaining are 0), used for omni-
* directional sounds. */
static const ALfloat PassthruCoeff = 32767.0f * 0.707106781187f/*sqrt(0.5)*/;
static struct Hrtf *LoadedHrtfs = NULL; static struct Hrtf *LoadedHrtfs = NULL;
/* Calculate the elevation indices given the polar elevation in radians. /* Calculate the elevation indices given the polar elevation in radians.
@ -88,45 +84,12 @@ static void CalcAzIndices(ALuint azcount, ALfloat az, ALuint *azidx, ALfloat *az
*azmu = az - floorf(az); *azmu = az - floorf(az);
} }
/* Calculates the normalized HRTF transition factor (delta) from the changes
* in gain and listener to source angle between updates. The result is a
* normalized delta factor that can be used to calculate moving HRIR stepping
* values.
*/
ALfloat CalcHrtfDelta(ALfloat oldGain, ALfloat newGain, const ALfloat olddir[3], const ALfloat newdir[3])
{
ALfloat gainChange, angleChange, change;
// Calculate the normalized dB gain change.
newGain = maxf(newGain, 0.0001f);
oldGain = maxf(oldGain, 0.0001f);
gainChange = fabsf(log10f(newGain / oldGain) / log10f(0.0001f));
// Calculate the angle change only when there is enough gain to notice it.
angleChange = 0.0f;
if(gainChange > 0.0001f || newGain > 0.0001f)
{
// No angle change when the directions are equal or degenerate (when
// both have zero length).
if(newdir[0] != olddir[0] || newdir[1] != olddir[1] || newdir[2] != olddir[2])
{
ALfloat dotp = olddir[0]*newdir[0] + olddir[1]*newdir[1] + olddir[2]*newdir[2];
angleChange = acosf(clampf(dotp, -1.0f, 1.0f)) / F_PI;
}
}
// Use the largest of the two changes for the delta factor, and apply a
// significance shaping function to it.
change = maxf(angleChange * 25.0f, gainChange) * 2.0f;
return minf(change, 1.0f);
}
/* Calculates static HRIR coefficients and delays for the given polar /* Calculates static HRIR coefficients and delays for the given polar
* elevation and azimuth in radians. Linear interpolation is used to * elevation and azimuth in radians. Linear interpolation is used to
* increase the apparent resolution of the HRIR data set. The coefficients * increase the apparent resolution of the HRIR data set. The coefficients
* are also normalized and attenuated by the specified gain. * are also normalized and attenuated by the specified gain.
*/ */
void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays) void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat (*coeffs)[2], ALuint *delays)
{ {
ALuint evidx[2], lidx[4], ridx[4]; ALuint evidx[2], lidx[4], ridx[4];
ALfloat mu[3], blend[4]; ALfloat mu[3], blend[4];
@ -158,12 +121,12 @@ void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azi
blend[3] = ( mu[1]) * ( mu[2]); blend[3] = ( mu[1]) * ( mu[2]);
/* Calculate the HRIR delays using linear interpolation. */ /* Calculate the HRIR delays using linear interpolation. */
delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] + delays[0] = fastf2u(Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] +
Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) * Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3] +
dirfact + 0.5f) << HRTFDELAY_BITS; 0.5f);
delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] + delays[1] = fastf2u(Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] +
Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) * Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3] +
dirfact + 0.5f) << HRTFDELAY_BITS; 0.5f);
/* Calculate the sample offsets for the HRIR indices. */ /* Calculate the sample offsets for the HRIR indices. */
lidx[0] *= Hrtf->irSize; lidx[0] *= Hrtf->irSize;
@ -175,183 +138,22 @@ void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azi
ridx[2] *= Hrtf->irSize; ridx[2] *= Hrtf->irSize;
ridx[3] *= Hrtf->irSize; ridx[3] *= Hrtf->irSize;
/* Calculate the normalized and attenuated HRIR coefficients using linear for(i = 0;i < Hrtf->irSize;i++)
* interpolation when there is enough gain to warrant it. Zero the
* coefficients if gain is too low.
*/
if(gain > 0.0001f)
{ {
ALfloat c; ALfloat c;
gain *= 1.0f/32767.0f;
i = 0;
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] + c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]); Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain; coeffs[i][0] = c * (1.0f/32767.0f);
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] + c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]); Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain; coeffs[i][1] = c * (1.0f/32767.0f);
for(i = 1;i < Hrtf->irSize;i++)
{
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
coeffs[i][0] = lerp(0.0f, c, dirfact) * gain;
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
coeffs[i][1] = lerp(0.0f, c, dirfact) * gain;
}
} }
else
{
for(i = 0;i < Hrtf->irSize;i++)
{
coeffs[i][0] = 0.0f;
coeffs[i][1] = 0.0f;
}
}
}
/* Calculates the moving HRIR target coefficients, target delays, and
* stepping values for the given polar elevation and azimuth in radians.
* Linear interpolation is used to increase the apparent resolution of the
* HRIR data set. The coefficients are also normalized and attenuated by the
* specified gain. Stepping resolution and count is determined using the
* given delta factor between 0.0 and 1.0.
*/
ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep)
{
ALuint evidx[2], lidx[4], ridx[4];
ALfloat mu[3], blend[4];
ALfloat left, right;
ALfloat step;
ALuint i;
/* Claculate elevation indices and interpolation factor. */
CalcEvIndices(Hrtf->evCount, elevation, evidx, &mu[2]);
for(i = 0;i < 2;i++)
{
ALuint azcount = Hrtf->azCount[evidx[i]];
ALuint evoffset = Hrtf->evOffset[evidx[i]];
ALuint azidx[2];
/* Calculate azimuth indices and interpolation factor for this elevation. */
CalcAzIndices(azcount, azimuth, azidx, &mu[i]);
/* Calculate a set of linear HRIR indices for left and right channels. */
lidx[i*2 + 0] = evoffset + azidx[0];
lidx[i*2 + 1] = evoffset + azidx[1];
ridx[i*2 + 0] = evoffset + ((azcount-azidx[0]) % azcount);
ridx[i*2 + 1] = evoffset + ((azcount-azidx[1]) % azcount);
}
// Calculate the stepping parameters.
delta = maxf(floorf(delta*(Hrtf->sampleRate*0.015f) + 0.5f), 1.0f);
step = 1.0f / delta;
/* Calculate 4 blending weights for 2D bilinear interpolation. */
blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]);
blend[1] = ( mu[0]) * (1.0f-mu[2]);
blend[2] = (1.0f-mu[1]) * ( mu[2]);
blend[3] = ( mu[1]) * ( mu[2]);
/* Calculate the HRIR delays using linear interpolation. Then calculate
* the delay stepping values using the target and previous running
* delays.
*/
left = (ALfloat)(delays[0] - (delayStep[0] * counter));
right = (ALfloat)(delays[1] - (delayStep[1] * counter));
delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] +
Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) *
dirfact + 0.5f) << HRTFDELAY_BITS;
delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] +
Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) *
dirfact + 0.5f) << HRTFDELAY_BITS;
delayStep[0] = fastf2i(step * (delays[0] - left));
delayStep[1] = fastf2i(step * (delays[1] - right));
/* Calculate the sample offsets for the HRIR indices. */
lidx[0] *= Hrtf->irSize;
lidx[1] *= Hrtf->irSize;
lidx[2] *= Hrtf->irSize;
lidx[3] *= Hrtf->irSize;
ridx[0] *= Hrtf->irSize;
ridx[1] *= Hrtf->irSize;
ridx[2] *= Hrtf->irSize;
ridx[3] *= Hrtf->irSize;
/* Calculate the normalized and attenuated target HRIR coefficients using
* linear interpolation when there is enough gain to warrant it. Zero
* the target coefficients if gain is too low. Then calculate the
* coefficient stepping values using the target and previous running
* coefficients.
*/
if(gain > 0.0001f)
{
ALfloat c;
gain *= 1.0f/32767.0f;
i = 0;
left = coeffs[i][0] - (coeffStep[i][0] * counter);
right = coeffs[i][1] - (coeffStep[i][1] * counter);
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain;
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain;
coeffStep[i][0] = step * (coeffs[i][0] - left);
coeffStep[i][1] = step * (coeffs[i][1] - right);
for(i = 1;i < Hrtf->irSize;i++)
{
left = coeffs[i][0] - (coeffStep[i][0] * counter);
right = coeffs[i][1] - (coeffStep[i][1] * counter);
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
coeffs[i][0] = lerp(0.0f, c, dirfact) * gain;
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
coeffs[i][1] = lerp(0.0f, c, dirfact) * gain;
coeffStep[i][0] = step * (coeffs[i][0] - left);
coeffStep[i][1] = step * (coeffs[i][1] - right);
}
}
else
{
for(i = 0;i < Hrtf->irSize;i++)
{
left = coeffs[i][0] - (coeffStep[i][0] * counter);
right = coeffs[i][1] - (coeffStep[i][1] * counter);
coeffs[i][0] = 0.0f;
coeffs[i][1] = 0.0f;
coeffStep[i][0] = step * -left;
coeffStep[i][1] = step * -right;
}
}
/* The stepping count is the number of samples necessary for the HRIR to
* complete its transition. The mixer will only apply stepping for this
* many samples.
*/
return fastf2u(delta);
} }
static struct Hrtf *LoadHrtf00(FILE *f, ALuint deviceRate) static struct Hrtf *LoadHrtf00(FILE *f, ALuint deviceRate)
{ {
const ALubyte maxDelay = SRC_HISTORY_LENGTH-1; const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1;
struct Hrtf *Hrtf = NULL; struct Hrtf *Hrtf = NULL;
ALboolean failed = AL_FALSE; ALboolean failed = AL_FALSE;
ALuint rate = 0, irCount = 0; ALuint rate = 0, irCount = 0;
@ -518,7 +320,7 @@ static struct Hrtf *LoadHrtf00(FILE *f, ALuint deviceRate)
static struct Hrtf *LoadHrtf01(FILE *f, ALuint deviceRate) static struct Hrtf *LoadHrtf01(FILE *f, ALuint deviceRate)
{ {
const ALubyte maxDelay = SRC_HISTORY_LENGTH-1; const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1;
struct Hrtf *Hrtf = NULL; struct Hrtf *Hrtf = NULL;
ALboolean failed = AL_FALSE; ALboolean failed = AL_FALSE;
ALuint rate = 0, irCount = 0; ALuint rate = 0, irCount = 0;

View File

@ -21,8 +21,6 @@ ALCboolean FindHrtfFormat(enum DevFmtChannels *chans, ALCuint *srate);
void FreeHrtfs(void); void FreeHrtfs(void);
ALuint GetHrtfIrSize(const struct Hrtf *Hrtf); ALuint GetHrtfIrSize(const struct Hrtf *Hrtf);
ALfloat CalcHrtfDelta(ALfloat oldGain, ALfloat newGain, const ALfloat olddir[3], const ALfloat newdir[3]); void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat (*coeffs)[2], ALuint *delays);
void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays);
ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep);
#endif /* ALC_HRTF_H */ #endif /* ALC_HRTF_H */

View File

@ -853,8 +853,8 @@ static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict
return; return;
if(state != AL_PLAYING) if(state != AL_PLAYING)
{ {
fluid_synth_write_float(self->Synth, SamplesToDo, DryBuffer[FrontLeft], 0, 1, fluid_synth_write_float(self->Synth, SamplesToDo, DryBuffer[0], 0, 1,
DryBuffer[FrontRight], 0, 1); DryBuffer[1], 0, 1);
return; return;
} }
@ -882,8 +882,8 @@ static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict
if(tonext > 0) if(tonext > 0)
{ {
ALuint todo = minu(tonext, SamplesToDo-total); ALuint todo = minu(tonext, SamplesToDo-total);
fluid_synth_write_float(self->Synth, todo, DryBuffer[FrontLeft], total, 1, fluid_synth_write_float(self->Synth, todo, DryBuffer[0], total, 1,
DryBuffer[FrontRight], total, 1); DryBuffer[1], total, 1);
total += todo; total += todo;
tonext -= todo; tonext -= todo;
} }

View File

@ -41,20 +41,6 @@
extern inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size); extern inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size);
static inline HrtfMixerFunc SelectHrtfMixer(void)
{
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return MixHrtf_SSE;
#endif
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return MixHrtf_Neon;
#endif
return MixHrtf_C;
}
static inline MixerFunc SelectMixer(void) static inline MixerFunc SelectMixer(void)
{ {
#ifdef HAVE_SSE #ifdef HAVE_SSE
@ -179,7 +165,6 @@ static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter
ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint SamplesToDo)
{ {
MixerFunc Mix; MixerFunc Mix;
HrtfMixerFunc HrtfMix;
ResamplerFunc Resample; ResamplerFunc Resample;
ALbufferlistitem *BufferListItem; ALbufferlistitem *BufferListItem;
ALuint DataPosInt, DataPosFrac; ALuint DataPosInt, DataPosFrac;
@ -218,7 +203,6 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
} }
Mix = SelectMixer(); Mix = SelectMixer();
HrtfMix = SelectHrtfMixer();
Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ? Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ?
Resample_copy32_C : SelectResampler(Resampler)); Resample_copy32_C : SelectResampler(Resampler));
@ -431,13 +415,8 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
Device->FilteredData, ResampledData, DstBufferSize, Device->FilteredData, ResampledData, DstBufferSize,
parms->Filters[chan].ActiveType parms->Filters[chan].ActiveType
); );
if(!voice->IsHrtf) Mix(samples, Device->NumChannels, parms->OutBuffer, parms->Mix.Gains[chan],
Mix(samples, Device->NumChannels, parms->OutBuffer, parms->Mix.Gains[chan], parms->Counter, OutPos, DstBufferSize);
parms->Counter, OutPos, DstBufferSize);
else
HrtfMix(parms->OutBuffer, samples, parms->Counter, voice->Offset,
OutPos, parms->Mix.Hrtf.IrSize, &parms->Mix.Hrtf.Params[chan],
&parms->Mix.Hrtf.State[chan], DstBufferSize);
} }
/* Only the first channel for B-Format buffers (W channel) goes to /* Only the first channel for B-Format buffers (W channel) goes to

View File

@ -59,23 +59,6 @@ void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const
} }
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2],
ALfloat left, ALfloat right)
{
ALuint c;
for(c = 0;c < IrSize;c++)
{
const ALuint off = (Offset+c)&HRIR_MASK;
Values[off][0] += Coeffs[c][0] * left;
Values[off][1] += Coeffs[c][1] * right;
Coeffs[c][0] += CoeffStep[c][0];
Coeffs[c][1] += CoeffStep[c][1];
}
}
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize, const ALuint IrSize,
ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Coeffs)[2],

View File

@ -20,17 +20,15 @@ const ALfloat *Resample_cubic32_C(const ALfloat *src, ALuint frac, ALuint increm
/* C mixers */ /* C mixers */
void MixHrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, void MixHrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, ALuint Offset, const ALuint IrSize, const struct HrtfParams *hrtfparams,
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate, struct HrtfState *hrtfstate, ALuint BufferSize);
ALuint BufferSize);
void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize); struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
/* SSE mixers */ /* SSE mixers */
void MixHrtf_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, void MixHrtf_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, ALuint Offset, const ALuint IrSize, const struct HrtfParams *hrtfparams,
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate, struct HrtfState *hrtfstate, ALuint BufferSize);
ALuint BufferSize);
void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize); struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
@ -56,9 +54,8 @@ const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint inc
/* Neon mixers */ /* Neon mixers */
void MixHrtf_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, void MixHrtf_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, ALuint Offset, const ALuint IrSize, const struct HrtfParams *hrtfparams,
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate, struct HrtfState *hrtfstate, ALuint BufferSize);
ALuint BufferSize);
void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize); struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);

View File

@ -14,11 +14,6 @@
#define MixHrtf MERGE(MixHrtf_,SUFFIX) #define MixHrtf MERGE(MixHrtf_,SUFFIX)
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint irSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2],
ALfloat left, ALfloat right);
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint irSize, const ALuint irSize,
ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Coeffs)[2],
@ -26,7 +21,7 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
void MixHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, void MixHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, ALuint Offset, const ALuint IrSize,
const HrtfParams *hrtfparams, HrtfState *hrtfstate, ALuint BufferSize) const HrtfParams *hrtfparams, HrtfState *hrtfstate, ALuint BufferSize)
{ {
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2]; alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
@ -37,52 +32,25 @@ void MixHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
for(c = 0;c < IrSize;c++) for(c = 0;c < IrSize;c++)
{ {
Coeffs[c][0] = hrtfparams->Coeffs[c][0] - (hrtfparams->CoeffStep[c][0]*Counter); Coeffs[c][0] = hrtfparams->Coeffs[c][0];
Coeffs[c][1] = hrtfparams->Coeffs[c][1] - (hrtfparams->CoeffStep[c][1]*Counter); Coeffs[c][1] = hrtfparams->Coeffs[c][1];
} }
Delay[0] = hrtfparams->Delay[0] - (hrtfparams->DelayStep[0]*Counter); Delay[0] = hrtfparams->Delay[0];
Delay[1] = hrtfparams->Delay[1] - (hrtfparams->DelayStep[1]*Counter); Delay[1] = hrtfparams->Delay[1];
for(pos = 0;pos < BufferSize && pos < Counter;pos++) for(pos = 0;pos < BufferSize;pos++)
{ {
hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos]; hrtfstate->History[Offset&HRTF_HISTORY_MASK] = data[pos];
left = lerp(hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK], left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK];
hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK], right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK];
(Delay[0]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
right = lerp(hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK],
hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK],
(Delay[1]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
Delay[0] += hrtfparams->DelayStep[0];
Delay[1] += hrtfparams->DelayStep[1];
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
Offset++;
ApplyCoeffsStep(Offset, hrtfstate->Values, IrSize, Coeffs, hrtfparams->CoeffStep, left, right);
OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
OutPos++;
}
Delay[0] >>= HRTFDELAY_BITS;
Delay[1] >>= HRTFDELAY_BITS;
for(;pos < BufferSize;pos++)
{
hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos];
left = hrtfstate->History[(Offset-Delay[0])&SRC_HISTORY_MASK];
right = hrtfstate->History[(Offset-Delay[1])&SRC_HISTORY_MASK];
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f; hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f; hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
Offset++; Offset++;
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right); ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right);
OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0]; OutBuffer[0][pos] += hrtfstate->Values[Offset&HRIR_MASK][0];
OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1]; OutBuffer[1][pos] += hrtfstate->Values[Offset&HRIR_MASK][1];
OutPos++;
} }
} }

View File

@ -9,38 +9,6 @@
#include "hrtf.h" #include "hrtf.h"
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2],
ALfloat left, ALfloat right)
{
ALuint c;
float32x4_t leftright4;
{
float32x2_t leftright2 = vdup_n_f32(0.0);
leftright2 = vset_lane_f32(left, leftright2, 0);
leftright2 = vset_lane_f32(right, leftright2, 1);
leftright4 = vcombine_f32(leftright2, leftright2);
}
for(c = 0;c < IrSize;c += 2)
{
const ALuint o0 = (Offset+c)&HRIR_MASK;
const ALuint o1 = (o0+1)&HRIR_MASK;
float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
vld1_f32((float32_t*)&Values[o1][0]));
float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
float32x4_t deltas = vld1q_f32(&CoeffStep[c][0]);
vals = vmlaq_f32(vals, coefs, leftright4);
coefs = vaddq_f32(coefs, deltas);
vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
vst1q_f32(&Coeffs[c][0], coefs);
}
}
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize, const ALuint IrSize,
ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Coeffs)[2],

View File

@ -19,68 +19,6 @@
#include "mixer_defs.h" #include "mixer_defs.h"
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2],
ALfloat left, ALfloat right)
{
const __m128 lrlr = _mm_setr_ps(left, right, left, right);
__m128 coeffs, deltas, imp0, imp1;
__m128 vals = _mm_setzero_ps();
ALuint i;
if((Offset&1))
{
const ALuint o0 = Offset&HRIR_MASK;
const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[0][0]);
deltas = _mm_load_ps(&CoeffStep[0][0]);
vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
imp0 = _mm_mul_ps(lrlr, coeffs);
coeffs = _mm_add_ps(coeffs, deltas);
vals = _mm_add_ps(imp0, vals);
_mm_store_ps(&Coeffs[0][0], coeffs);
_mm_storel_pi((__m64*)&Values[o0][0], vals);
for(i = 1;i < IrSize-1;i += 2)
{
const ALuint o2 = (Offset+i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i+1][0]);
deltas = _mm_load_ps(&CoeffStep[i+1][0]);
vals = _mm_load_ps(&Values[o2][0]);
imp1 = _mm_mul_ps(lrlr, coeffs);
coeffs = _mm_add_ps(coeffs, deltas);
imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
vals = _mm_add_ps(imp0, vals);
_mm_store_ps(&Coeffs[i+1][0], coeffs);
_mm_store_ps(&Values[o2][0], vals);
imp0 = imp1;
}
vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
imp0 = _mm_movehl_ps(imp0, imp0);
vals = _mm_add_ps(imp0, vals);
_mm_storel_pi((__m64*)&Values[o1][0], vals);
}
else
{
for(i = 0;i < IrSize;i += 2)
{
const ALuint o = (Offset + i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i][0]);
deltas = _mm_load_ps(&CoeffStep[i][0]);
vals = _mm_load_ps(&Values[o][0]);
imp0 = _mm_mul_ps(lrlr, coeffs);
coeffs = _mm_add_ps(coeffs, deltas);
vals = _mm_add_ps(imp0, vals);
_mm_store_ps(&Coeffs[i][0], coeffs);
_mm_store_ps(&Values[o][0], vals);
}
}
}
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize, const ALuint IrSize,
ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Coeffs)[2],

View File

@ -124,6 +124,18 @@ DECL_CONST static inline const char *GetLabelFromChannel(enum Channel channel)
case BackCenter: return "back-center"; case BackCenter: return "back-center";
case SideLeft: return "side-left"; case SideLeft: return "side-left";
case SideRight: return "side-right"; case SideRight: return "side-right";
case TopCenter: return "top-center";
case BottomCenter: return "bottom-center";
case TopFrontLeft: return "top-front-left";
case TopFrontRight: return "top-front-right";
case TopBackLeft: return "top-back-left";
case TopBackRight: return "top-back-right";
case BottomFrontLeft: return "bottom-front-left";
case BottomFrontRight: return "bottom-front-right";
case BottomBackLeft: return "bottom-back-left";
case BottomBackRight: return "bottom-back-right";
case InvalidChannel: break; case InvalidChannel: break;
} }
return "(unknown)"; return "(unknown)";
@ -322,6 +334,21 @@ ALvoid aluInitPanning(ALCdevice *device)
{ BackRight, { { 0.224752f, -0.295009f, -0.170325f, 0.0f, 0.0f, 0.0f, 0.0f, 0.105349f, 0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065799f }, { 0.224752f, -0.225790f, -0.130361f, 0.0f } } }, { BackRight, { { 0.224752f, -0.295009f, -0.170325f, 0.0f, 0.0f, 0.0f, 0.0f, 0.105349f, 0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065799f }, { 0.224752f, -0.225790f, -0.130361f, 0.0f } } },
{ SideLeft, { { 0.224739f, 0.000002f, 0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, 0.000002f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065795f }, { 0.224739f, 0.000000f, 0.260717f, 0.0f } } }, { SideLeft, { { 0.224739f, 0.000002f, 0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, 0.000002f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065795f }, { 0.224739f, 0.000000f, 0.260717f, 0.0f } } },
{ SideRight, { { 0.224739f, 0.000002f, -0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, -0.000002f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.065795f }, { 0.224739f, 0.000000f, -0.260717f, 0.0f } } }, { SideRight, { { 0.224739f, 0.000002f, -0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, -0.000002f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.065795f }, { 0.224739f, 0.000000f, -0.260717f, 0.0f } } },
}, CubeDiamond[14] = {
{ SideLeft, { { 0.130137f, -0.000013f, 0.207620f, 0.000001f, -0.081895f, 0.000001f, 0.000002f, -0.144357f, -0.000015f, -0.000002f, 0.000001f, -0.032339f, -0.000000f, 0.000001f, 0.000005f, -0.057583f }, { 0.130137f, -0.000009f, 0.139199f, 0.000001f } } },
{ FrontCenter, { { 0.130131f, 0.207613f, -0.000007f, 0.000001f, -0.081898f, 0.000001f, -0.000000f, 0.144354f, -0.000010f, -0.000002f, -0.032343f, 0.000006f, -0.000000f, -0.000001f, 0.057583f, -0.000004f }, { 0.130131f, 0.139195f, -0.000005f, 0.000001f } } },
{ SideRight, { { 0.130140f, 0.000001f, -0.207624f, -0.000001f, -0.081900f, 0.000005f, 0.000001f, -0.144357f, -0.000002f, 0.000002f, -0.000004f, 0.032342f, -0.000000f, -0.000005f, -0.000002f, 0.057580f }, { 0.130140f, 0.000000f, -0.139202f, -0.000000f } } },
{ BackCenter, { { 0.130142f, -0.207624f, 0.000006f, -0.000010f, -0.081897f, 0.000011f, 0.000005f, 0.144354f, -0.000006f, 0.000004f, 0.032340f, 0.000000f, -0.000002f, -0.000005f, -0.057579f, 0.000003f }, { 0.130142f, -0.139202f, 0.000004f, -0.000007f } } },
{ TopCenter, { { 0.072579f, 0.000006f, 0.000001f, 0.123524f, 0.126630f, 0.000008f, 0.000001f, -0.000002f, 0.000001f, 0.070290f, 0.000005f, 0.000001f, -0.000002f, 0.000001f, -0.000001f, 0.000002f }, { 0.072579f, 0.000004f, 0.000001f, 0.082817f } } },
{ BottomCenter, { { 0.072577f, -0.000008f, -0.000004f, -0.123522f, 0.126628f, 0.000012f, 0.000005f, -0.000001f, 0.000002f, -0.070291f, -0.000008f, -0.000002f, 0.000001f, -0.000002f, 0.000000f, -0.000001f }, { 0.072577f, -0.000006f, -0.000003f, -0.082815f } } },
{ TopFrontLeft, { { 0.176238f, 0.156939f, 0.156950f, 0.149964f, 0.006362f, 0.101960f, 0.101968f, -0.000003f, 0.117690f, -0.020696f, 0.018718f, 0.018723f, 0.000002f, 0.038069f, -0.025485f, 0.025484f }, { 0.176238f, 0.105220f, 0.105227f, 0.100544f } } },
{ TopFrontRight, { { 0.176240f, 0.156940f, -0.156956f, 0.149960f, 0.006356f, 0.101955f, -0.101970f, -0.000007f, -0.117691f, -0.020700f, 0.018719f, -0.018722f, -0.000003f, -0.038066f, -0.025485f, -0.025485f }, { 0.176240f, 0.105220f, -0.105231f, 0.100541f } } },
{ TopBackLeft, { { 0.176245f, -0.156957f, 0.156942f, 0.149971f, 0.006355f, -0.101977f, 0.101961f, 0.000003f, -0.117684f, -0.020701f, -0.018724f, 0.018716f, 0.000001f, -0.038069f, 0.025482f, 0.025479f }, { 0.176245f, -0.105232f, 0.105222f, 0.100548f } } },
{ TopBackRight, { { 0.176238f, -0.156944f, -0.156942f, 0.149972f, 0.006363f, -0.101976f, -0.101964f, -0.000005f, 0.117687f, -0.020701f, -0.018726f, -0.018716f, 0.000000f, 0.038072f, 0.025486f, -0.025481f }, { 0.176238f, -0.105224f, -0.105222f, 0.100549f } } },
{ BottomFrontLeft, { { 0.176248f, 0.156943f, 0.156950f, -0.149981f, 0.006371f, -0.101969f, -0.101967f, -0.000008f, 0.117685f, 0.020695f, 0.018723f, 0.018718f, 0.000000f, -0.038067f, -0.025486f, 0.025479f }, { 0.176248f, 0.105223f, 0.105227f, -0.100555f } } },
{ BottomFrontRight, { { 0.176236f, 0.156947f, -0.156945f, -0.149961f, 0.006353f, -0.101971f, 0.101964f, -0.000002f, -0.117689f, 0.020704f, 0.018723f, -0.018718f, -0.000001f, 0.038071f, -0.025484f, -0.025482f }, { 0.176236f, 0.105225f, -0.105224f, -0.100542f } } },
{ BottomBackLeft, { { 0.176236f, -0.156936f, 0.156952f, -0.149964f, 0.006358f, 0.101962f, -0.101976f, -0.000005f, -0.117687f, 0.020707f, -0.018720f, 0.018725f, 0.000001f, 0.038071f, 0.025483f, 0.025485f }, { 0.176236f, -0.105218f, 0.105229f, -0.100543f } } },
{ BottomBackRight, { { 0.176256f, -0.156956f, -0.156954f, -0.149974f, 0.006366f, 0.101960f, 0.101964f, 0.000003f, 0.117692f, 0.020690f, -0.018718f, -0.018722f, 0.000001f, -0.038064f, 0.025483f, -0.025484f }, { 0.176256f, -0.105232f, -0.105230f, -0.100550f } } },
}; };
const ChannelMap *chanmap = NULL; const ChannelMap *chanmap = NULL;
size_t count = 0; size_t count = 0;
@ -329,6 +356,43 @@ ALvoid aluInitPanning(ALCdevice *device)
memset(device->Channel, 0, sizeof(device->Channel)); memset(device->Channel, 0, sizeof(device->Channel));
device->NumChannels = 0; device->NumChannels = 0;
if(device->Hrtf)
{
static const struct {
enum Channel channel;
ALfloat elevation;
ALfloat angle;
} VirtualChans[14] = {
{ FrontCenter, DEG2RAD( 0.0f), DEG2RAD( 0.0f) },
{ BackCenter, DEG2RAD( 0.0f), DEG2RAD(-180.0f) },
{ SideLeft, DEG2RAD( 0.0f), DEG2RAD( -90.0f) },
{ SideRight, DEG2RAD( 0.0f), DEG2RAD( 90.0f) },
{ TopFrontLeft, DEG2RAD( 45.0f), DEG2RAD( -45.0f) },
{ TopFrontRight, DEG2RAD( 45.0f), DEG2RAD( 45.0f) },
{ TopBackLeft, DEG2RAD( 45.0f), DEG2RAD(-135.0f) },
{ TopBackRight, DEG2RAD( 45.0f), DEG2RAD( 135.0f) },
{ BottomFrontLeft, DEG2RAD(-45.0f), DEG2RAD( -45.0f) },
{ BottomFrontRight, DEG2RAD(-45.0f), DEG2RAD( 45.0f) },
{ BottomBackLeft, DEG2RAD(-45.0f), DEG2RAD(-135.0f) },
{ BottomBackRight, DEG2RAD(-45.0f), DEG2RAD( 135.0f) },
{ TopCenter, DEG2RAD( 90.0f), DEG2RAD( 0.0f) },
{ BottomCenter, DEG2RAD(-90.0f), DEG2RAD( 0.0f) },
};
ALuint i;
count = COUNTOF(CubeDiamond);
chanmap = CubeDiamond;
for(i = 0;i < count;i++)
device->ChannelName[i] = VirtualChans[i].channel;
SetChannelMap(device, chanmap, count);
for(i = 0;i < count;i++)
GetLerpedHrtfCoeffs(device->Hrtf, VirtualChans[i].elevation, VirtualChans[i].angle,
device->Hrtf_Params[i].Coeffs, device->Hrtf_Params[i].Delay);
return;
}
if(LoadChannelSetup(device)) if(LoadChannelSetup(device))
return; return;

View File

@ -37,6 +37,8 @@
#include "vector.h" #include "vector.h"
#include "alstring.h" #include "alstring.h"
#include "hrtf.h"
#ifndef ALC_SOFT_HRTF #ifndef ALC_SOFT_HRTF
#define ALC_SOFT_HRTF 1 #define ALC_SOFT_HRTF 1
#define ALC_HRTF_SOFT 0x1992 #define ALC_HRTF_SOFT 0x1992
@ -528,6 +530,17 @@ enum Channel {
SideLeft, SideLeft,
SideRight, SideRight,
TopCenter,
BottomCenter,
TopFrontLeft,
TopFrontRight,
TopBackLeft,
TopBackRight,
BottomFrontLeft,
BottomFrontRight,
BottomBackLeft,
BottomBackRight,
InvalidChannel InvalidChannel
}; };
@ -557,7 +570,7 @@ enum DevFmtChannels {
DevFmtChannelsDefault = DevFmtStereo DevFmtChannelsDefault = DevFmtStereo
}; };
#define MAX_OUTPUT_CHANNELS (8) #define MAX_OUTPUT_CHANNELS (14)
ALuint BytesFromDevFmt(enum DevFmtType type) DECL_CONST; ALuint BytesFromDevFmt(enum DevFmtType type) DECL_CONST;
ALuint ChannelsFromDevFmt(enum DevFmtChannels chans) DECL_CONST; ALuint ChannelsFromDevFmt(enum DevFmtChannels chans) DECL_CONST;
@ -593,6 +606,21 @@ typedef struct ChannelConfig {
} ChannelConfig; } ChannelConfig;
#define HRTF_HISTORY_BITS (6)
#define HRTF_HISTORY_LENGTH (1<<HRTF_HISTORY_BITS)
#define HRTF_HISTORY_MASK (HRTF_HISTORY_LENGTH-1)
typedef struct HrtfState {
alignas(16) ALfloat History[HRTF_HISTORY_LENGTH];
alignas(16) ALfloat Values[HRIR_LENGTH][2];
} HrtfState;
typedef struct HrtfParams {
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
ALuint Delay[2];
} HrtfParams;
/* Size for temporary storage of buffer data, in ALfloats. Larger values need /* Size for temporary storage of buffer data, in ALfloats. Larger values need
* more memory, while smaller values may need more iterations. The value needs * more memory, while smaller values may need more iterations. The value needs
* to be a sensible size, however, as it constrains the max stepping value used * to be a sensible size, however, as it constrains the max stepping value used
@ -652,6 +680,9 @@ struct ALCdevice_struct
/* HRTF filter tables */ /* HRTF filter tables */
const struct Hrtf *Hrtf; const struct Hrtf *Hrtf;
HrtfState Hrtf_State[MAX_OUTPUT_CHANNELS];
HrtfParams Hrtf_Params[MAX_OUTPUT_CHANNELS];
ALuint Hrtf_Offset;
// Stereo-to-binaural filter // Stereo-to-binaural filter
struct bs2b *Bs2b; struct bs2b *Bs2b;

View File

@ -30,10 +30,6 @@
#define RAD2DEG(x) ((ALfloat)(x) * (180.0f/F_PI)) #define RAD2DEG(x) ((ALfloat)(x) * (180.0f/F_PI))
#define SRC_HISTORY_BITS (6)
#define SRC_HISTORY_LENGTH (1<<SRC_HISTORY_BITS)
#define SRC_HISTORY_MASK (SRC_HISTORY_LENGTH-1)
#define MAX_PITCH (10) #define MAX_PITCH (10)
@ -53,19 +49,6 @@ enum ActiveFilters {
}; };
typedef struct HrtfState {
alignas(16) ALfloat History[SRC_HISTORY_LENGTH];
alignas(16) ALfloat Values[HRIR_LENGTH][2];
} HrtfState;
typedef struct HrtfParams {
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
alignas(16) ALfloat CoeffStep[HRIR_LENGTH][2];
ALuint Delay[2];
ALint DelayStep[2];
} HrtfParams;
typedef struct MixGains { typedef struct MixGains {
ALfloat Current; ALfloat Current;
ALfloat Step; ALfloat Step;
@ -88,14 +71,6 @@ typedef struct DirectParams {
} Filters[MAX_INPUT_CHANNELS]; } Filters[MAX_INPUT_CHANNELS];
union { union {
struct {
HrtfParams Params[MAX_INPUT_CHANNELS];
HrtfState State[MAX_INPUT_CHANNELS];
ALuint IrSize;
ALfloat Gain;
ALfloat Dir[3];
} Hrtf;
MixGains Gains[MAX_INPUT_CHANNELS][MAX_OUTPUT_CHANNELS]; MixGains Gains[MAX_INPUT_CHANNELS][MAX_OUTPUT_CHANNELS];
} Mix; } Mix;
} DirectParams; } DirectParams;
@ -125,8 +100,7 @@ typedef void (*MixerFunc)(const ALfloat *data, ALuint OutChans,
ALfloat (*restrict OutBuffer)[BUFFERSIZE], struct MixGains *Gains, ALfloat (*restrict OutBuffer)[BUFFERSIZE], struct MixGains *Gains,
ALuint Counter, ALuint OutPos, ALuint BufferSize); ALuint Counter, ALuint OutPos, ALuint BufferSize);
typedef void (*HrtfMixerFunc)(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, typedef void (*HrtfMixerFunc)(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
ALuint Counter, ALuint Offset, ALuint OutPos, ALuint Offset, const ALuint IrSize, const HrtfParams *hrtfparams,
const ALuint IrSize, const HrtfParams *hrtfparams,
HrtfState *hrtfstate, ALuint BufferSize); HrtfState *hrtfstate, ALuint BufferSize);

View File

@ -2597,23 +2597,12 @@ ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state)
voice->Source = Source; voice->Source = Source;
} }
voice->Direct.Moving = AL_FALSE; voice->Direct.Moving = AL_FALSE;
voice->Direct.Counter = 0; voice->Direct.Counter = 0;
for(i = 0;i < MAX_INPUT_CHANNELS;i++)
{
ALsizei j;
for(j = 0;j < SRC_HISTORY_LENGTH;j++)
voice->Direct.Mix.Hrtf.State[i].History[j] = 0.0f;
for(j = 0;j < HRIR_LENGTH;j++)
{
voice->Direct.Mix.Hrtf.State[i].Values[j][0] = 0.0f;
voice->Direct.Mix.Hrtf.State[i].Values[j][1] = 0.0f;
}
}
for(i = 0;i < (ALsizei)device->NumAuxSends;i++) for(i = 0;i < (ALsizei)device->NumAuxSends;i++)
{ {
voice->Send[i].Counter = 0;
voice->Send[i].Moving = AL_FALSE; voice->Send[i].Moving = AL_FALSE;
voice->Send[i].Counter = 0;
} }
if(BufferList->buffer->FmtChannels == FmtMono) if(BufferList->buffer->FmtChannels == FmtMono)