diff options
author | Chris Robinson <[email protected]> | 2014-11-22 04:20:17 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2014-11-22 04:20:17 -0800 |
commit | a27e5e16523e1f6f166410e9992fc40886064eca (patch) | |
tree | a53e47af1b8afb1afa6c497247c9c462889d9067 /Alc/ALu.c | |
parent | 38383671d7d2a4f143f0ac84b48e8a02b91a1ba2 (diff) |
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.
Diffstat (limited to 'Alc/ALu.c')
-rw-r--r-- | Alc/ALu.c | 163 |
1 files changed, 52 insertions, 111 deletions
@@ -36,6 +36,8 @@ #include "hrtf.h" #include "static_assert.h" +#include "mixer_defs.h" + #include "backends/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); +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) { 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; } - 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 { for(c = 0;c < num_channels;c++) @@ -929,70 +915,6 @@ ALvoid CalcSourceParams(ALvoice *voice, const ALsource *ALSource, const ALCconte 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]; ALfloat radius = ALSource->Radius; @@ -1125,14 +1047,24 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) while(size > 0) { + ALuint outchanoffset = 0; + ALuint outchancount = device->NumChannels; + IncrementRef(&device->MixCount); SamplesToDo = minu(size, BUFFERSIZE); for(c = 0;c < device->NumChannels;c++) 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)(); - V(device->Synth,process)(SamplesToDo, device->DryBuffer); + V(device->Synth,process)(SamplesToDo, &device->DryBuffer[outchanoffset]); ctx = ATOMIC_LOAD(&device->ContextList); while(ctx) @@ -1212,7 +1144,16 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) device->SamplesDone %= device->Frequency; 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 */ for(i = 0;i < SamplesToDo;i++) @@ -1231,32 +1172,32 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) switch(device->FmtType) { case DevFmtByte: - Write_ALbyte(device->DryBuffer, buffer, SamplesToDo, device->NumChannels); - buffer = (char*)buffer + SamplesToDo*device->NumChannels*sizeof(ALbyte); + Write_ALbyte(&device->DryBuffer[outchanoffset], buffer, SamplesToDo, outchancount); + buffer = (char*)buffer + SamplesToDo*outchancount*sizeof(ALbyte); break; case DevFmtUByte: - Write_ALubyte(device->DryBuffer, buffer, SamplesToDo, device->NumChannels); - buffer = (char*)buffer + SamplesToDo*device->NumChannels*sizeof(ALubyte); + Write_ALubyte(&device->DryBuffer[outchanoffset], buffer, SamplesToDo, outchancount); + buffer = (char*)buffer + SamplesToDo*outchancount*sizeof(ALubyte); break; case DevFmtShort: - Write_ALshort(device->DryBuffer, buffer, SamplesToDo, device->NumChannels); - buffer = (char*)buffer + SamplesToDo*device->NumChannels*sizeof(ALshort); + Write_ALshort(&device->DryBuffer[outchanoffset], buffer, SamplesToDo, outchancount); + buffer = (char*)buffer + SamplesToDo*outchancount*sizeof(ALshort); break; case DevFmtUShort: - Write_ALushort(device->DryBuffer, buffer, SamplesToDo, device->NumChannels); - buffer = (char*)buffer + SamplesToDo*device->NumChannels*sizeof(ALushort); + Write_ALushort(&device->DryBuffer[outchanoffset], buffer, SamplesToDo, outchancount); + buffer = (char*)buffer + SamplesToDo*outchancount*sizeof(ALushort); break; case DevFmtInt: - Write_ALint(device->DryBuffer, buffer, SamplesToDo, device->NumChannels); - buffer = (char*)buffer + SamplesToDo*device->NumChannels*sizeof(ALint); + Write_ALint(&device->DryBuffer[outchanoffset], buffer, SamplesToDo, outchancount); + buffer = (char*)buffer + SamplesToDo*outchancount*sizeof(ALint); break; case DevFmtUInt: - Write_ALuint(device->DryBuffer, buffer, SamplesToDo, device->NumChannels); - buffer = (char*)buffer + SamplesToDo*device->NumChannels*sizeof(ALuint); + Write_ALuint(&device->DryBuffer[outchanoffset], buffer, SamplesToDo, outchancount); + buffer = (char*)buffer + SamplesToDo*outchancount*sizeof(ALuint); break; case DevFmtFloat: - Write_ALfloat(device->DryBuffer, buffer, SamplesToDo, device->NumChannels); - buffer = (char*)buffer + SamplesToDo*device->NumChannels*sizeof(ALfloat); + Write_ALfloat(&device->DryBuffer[outchanoffset], buffer, SamplesToDo, outchancount); + buffer = (char*)buffer + SamplesToDo*outchancount*sizeof(ALfloat); break; } } |