From 81e049bd47cc2423dc6983c47d9f99e70c3135ac Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 23 Mar 2014 06:57:00 -0700 Subject: [PATCH] Step mixing gains per-sample for non-HRTF mixing This fades the dry mixing gains using a logarithmic curve, which should produce a smoother transition than a linear one. It functions similarly to a linear fade except that step = (target - current) / numsteps; ... gain += step; becomes step = powf(target / current, 1.0f / numsteps); ... gain *= step; where 'target' and 'current' are clamped to a lower bound that is greater than 0 (which makes no sense on a logarithmic scale). Consequently, the non-HRTF direct mixers do not do not feed into the click removal and pending click buffers, as this per-sample fading would do an adequate job of stopping clicks and pops caused by extreme gain changes. These buffers should be removed shortly. --- Alc/ALu.c | 127 +++++++++++++++++++++++++++++++++++++++++++------ Alc/mixer.c | 2 +- Alc/mixer_c.c | 30 +++++++----- Alc/mixer_inc.c | 2 +- Alc/mixer_neon.c | 46 ++++++++++++------ Alc/mixer_sse.c | 56 ++++++++++++++++------ OpenAL32/Include/alu.h | 9 +++- 7 files changed, 212 insertions(+), 60 deletions(-) diff --git a/Alc/ALu.c b/Alc/ALu.c index ca0c0be4..9c563023 100644 --- a/Alc/ALu.c +++ b/Alc/ALu.c @@ -291,7 +291,7 @@ ALvoid CalcNonAttnSourceParams(ALactivesource *src, const ALCcontext *ALContext) ALboolean DirectChannels; ALfloat hwidth = 0.0f; ALfloat Pitch; - ALint i, c; + ALint i, j, c; /* Get device properties */ NumSends = Device->NumAuxSends; @@ -420,11 +420,11 @@ ALvoid CalcNonAttnSourceParams(ALactivesource *src, const ALCcontext *ALContext) if(DirectChannels != AL_FALSE) { - ALfloat (*SrcMatrix)[MaxChannels] = src->Direct.Mix.Gains; + ALfloat (*Matrix)[MaxChannels] = src->Direct.Mix.Gains.Target; for(i = 0;i < MAX_INPUT_CHANNELS;i++) { for(c = 0;c < MaxChannels;c++) - SrcMatrix[i][c] = 0.0f; + Matrix[i][c] = 0.0f; } for(c = 0;c < num_channels;c++) { @@ -433,13 +433,47 @@ ALvoid CalcNonAttnSourceParams(ALactivesource *src, const ALCcontext *ALContext) enum Channel chan = Device->Speaker2Chan[i]; if(chan == chans[c].channel) { - SrcMatrix[c][chan] = DryGain; + Matrix[c][chan] = DryGain; break; } } } - src->Direct.Counter = 0; - src->Direct.Moving = AL_TRUE; + + if(src->Direct.Moving) + { + ALfloat (*restrict Current)[MaxChannels] = src->Direct.Mix.Gains.Current; + ALfloat (*restrict Step)[MaxChannels] = src->Direct.Mix.Gains.Step; + for(i = 0;i < MAX_INPUT_CHANNELS;i++) + { + for(j = 0;j < MaxChannels;j++) + { + ALfloat cur = maxf(Current[i][j], GAIN_SILENCE_THRESHOLD); + ALfloat trg = maxf(Matrix[i][j], GAIN_SILENCE_THRESHOLD); + if(fabs(trg - cur) >= GAIN_SILENCE_THRESHOLD) + Step[i][j] = powf(trg/cur, 1.0f/64.0f); + else + Step[i][j] = 1.0f; + Current[i][j] = cur; + } + } + src->Direct.Counter = 64; + } + else + { + ALfloat (*restrict Current)[MaxChannels] = src->Direct.Mix.Gains.Current; + ALfloat (*restrict Step)[MaxChannels] = src->Direct.Mix.Gains.Step; + for(i = 0;i < MAX_INPUT_CHANNELS;i++) + { + for(j = 0;j < MaxChannels;j++) + { + Current[i][j] = Matrix[i][j]; + Step[i][j] = 1.0f; + } + } + src->Direct.Counter = 0; + src->Direct.Moving = AL_TRUE; + } + src->DryMix = SelectDirectMixer(); } else if(Device->Hrtf) @@ -475,11 +509,11 @@ ALvoid CalcNonAttnSourceParams(ALactivesource *src, const ALCcontext *ALContext) } else { - ALfloat (*SrcMatrix)[MaxChannels] = src->Direct.Mix.Gains; + ALfloat (*Matrix)[MaxChannels] = src->Direct.Mix.Gains.Target; for(i = 0;i < MAX_INPUT_CHANNELS;i++) { for(c = 0;c < MaxChannels;c++) - SrcMatrix[i][c] = 0.0f; + Matrix[i][c] = 0.0f; } DryGain *= lerp(1.0f, 1.0f/sqrtf((float)Device->NumChan), hwidth/F_PI); @@ -488,14 +522,48 @@ ALvoid CalcNonAttnSourceParams(ALactivesource *src, const ALCcontext *ALContext) /* Special-case LFE */ if(chans[c].channel == LFE) { - SrcMatrix[c][chans[c].channel] = DryGain; + Matrix[c][chans[c].channel] = DryGain; continue; } ComputeAngleGains(Device, chans[c].angle, hwidth, DryGain, - SrcMatrix[c]); + Matrix[c]); } - src->Direct.Counter = 0; - src->Direct.Moving = AL_TRUE; + + if(src->Direct.Moving) + { + ALfloat (*restrict Current)[MaxChannels] = src->Direct.Mix.Gains.Current; + ALfloat (*restrict Step)[MaxChannels] = src->Direct.Mix.Gains.Step; + for(i = 0;i < MAX_INPUT_CHANNELS;i++) + { + for(j = 0;j < MaxChannels;j++) + { + ALfloat trg = maxf(Matrix[i][j], GAIN_SILENCE_THRESHOLD); + ALfloat cur = maxf(Current[i][j], GAIN_SILENCE_THRESHOLD); + if(fabs(trg - cur) >= GAIN_SILENCE_THRESHOLD) + Step[i][j] = powf(trg/cur, 1.0f/64.0f); + else + Step[i][j] = 1.0f; + Current[i][j] = cur; + } + } + src->Direct.Counter = 64; + } + else + { + ALfloat (*restrict Current)[MaxChannels] = src->Direct.Mix.Gains.Current; + ALfloat (*restrict Step)[MaxChannels] = src->Direct.Mix.Gains.Step; + for(i = 0;i < MAX_INPUT_CHANNELS;i++) + { + for(j = 0;j < MaxChannels;j++) + { + Current[i][j] = Matrix[i][j]; + Step[i][j] = 1.0f; + } + } + src->Direct.Counter = 0; + src->Direct.Moving = AL_TRUE; + } + src->DryMix = SelectDirectMixer(); } for(i = 0;i < NumSends;i++) @@ -919,7 +987,7 @@ ALvoid CalcSourceParams(ALactivesource *src, const ALCcontext *ALContext) } else { - ALfloat (*Matrix)[MaxChannels] = src->Direct.Mix.Gains; + ALfloat (*Matrix)[MaxChannels] = src->Direct.Mix.Gains.Target; ALfloat DirGain = 0.0f; ALfloat AmbientGain; @@ -951,8 +1019,37 @@ ALvoid CalcSourceParams(ALactivesource *src, const ALCcontext *ALContext) Matrix[0][chan] = maxf(Matrix[0][chan], AmbientGain); } - src->Direct.Counter = 0; - src->Direct.Moving = AL_TRUE; + if(src->Direct.Moving) + { + ALfloat (*restrict Current)[MaxChannels] = src->Direct.Mix.Gains.Current; + ALfloat (*restrict Step)[MaxChannels] = src->Direct.Mix.Gains.Step; + for(j = 0;j < MaxChannels;j++) + { + ALfloat cur = maxf(Current[0][j], GAIN_SILENCE_THRESHOLD); + ALfloat trg = maxf(Matrix[0][j], GAIN_SILENCE_THRESHOLD); + if(fabs(trg - cur) >= GAIN_SILENCE_THRESHOLD) + Step[0][j] = powf(trg/cur, 1.0f/64.0f); + else + Step[0][j] = 1.0f; + Current[0][j] = cur; + } + src->Direct.Counter = 64; + } + else + { + ALfloat (*restrict Current)[MaxChannels] = src->Direct.Mix.Gains.Current; + ALfloat (*restrict Step)[MaxChannels] = src->Direct.Mix.Gains.Step; + for(i = 0;i < MAX_INPUT_CHANNELS;i++) + { + for(j = 0;j < MaxChannels;j++) + { + Current[i][j] = Matrix[i][j]; + Step[i][j] = 1.0f; + } + } + src->Direct.Counter = 0; + src->Direct.Moving = AL_TRUE; + } src->DryMix = SelectDirectMixer(); } diff --git a/Alc/mixer.c b/Alc/mixer.c index 8ce7d845..96acb3e8 100644 --- a/Alc/mixer.c +++ b/Alc/mixer.c @@ -411,7 +411,7 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo) Source->BuffersPlayed = BuffersPlayed; Source->position = DataPosInt; Source->position_fraction = DataPosFrac; - src->Direct.Mix.Hrtf.State.Offset += OutPos; + src->Direct.Offset += OutPos; if(State == AL_PLAYING) src->Direct.Counter = maxu(src->Direct.Counter, OutPos) - OutPos; else diff --git a/Alc/mixer_c.c b/Alc/mixer_c.c index 6b21e67a..e83655e6 100644 --- a/Alc/mixer_c.c +++ b/Alc/mixer_c.c @@ -83,27 +83,33 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], void MixDirect_C(DirectParams *params, const ALfloat *restrict data, ALuint srcchan, - ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) + ALuint OutPos, ALuint UNUSED(SamplesToDo), ALuint BufferSize) { ALfloat (*restrict OutBuffer)[BUFFERSIZE] = params->OutBuffer; - ALfloat *restrict ClickRemoval = params->ClickRemoval; - ALfloat *restrict PendingClicks = params->PendingClicks; - ALfloat DrySend; - ALuint pos; + ALuint Counter = maxu(params->Counter, OutPos) - OutPos; + ALfloat DrySend, Step; ALuint c; for(c = 0;c < MaxChannels;c++) { - DrySend = params->Mix.Gains[srcchan][c]; + ALuint pos = 0; + Step = params->Mix.Gains.Step[srcchan][c]; + if(Step != 0.0f && Counter > 0) + { + DrySend = params->Mix.Gains.Current[srcchan][c]; + for(;pos < BufferSize && pos < Counter;pos++) + { + OutBuffer[c][OutPos+pos] += data[pos]*DrySend; + DrySend *= Step; + } + params->Mix.Gains.Current[srcchan][c] = DrySend; + } + + DrySend = params->Mix.Gains.Target[srcchan][c]; if(!(DrySend > GAIN_SILENCE_THRESHOLD)) continue; - - if(OutPos == 0) - ClickRemoval[c] -= data[0]*DrySend; - for(pos = 0;pos < BufferSize;pos++) + for(;pos < BufferSize;pos++) OutBuffer[c][OutPos+pos] += data[pos]*DrySend; - if(OutPos+pos == SamplesToDo) - PendingClicks[c] += data[pos]*DrySend; } } diff --git a/Alc/mixer_inc.c b/Alc/mixer_inc.c index 1cb1967d..d27cb979 100644 --- a/Alc/mixer_inc.c +++ b/Alc/mixer_inc.c @@ -43,8 +43,8 @@ void MixDirect_Hrtf(DirectParams *params, const ALfloat *restrict data, ALuint s const ALuint *restrict TargetDelay = params->Mix.Hrtf.Params.Delay[srcchan]; ALfloat *restrict History = params->Mix.Hrtf.State.History[srcchan]; ALfloat (*restrict Values)[2] = params->Mix.Hrtf.State.Values[srcchan]; - ALuint Offset = params->Mix.Hrtf.State.Offset + OutPos; ALint Counter = maxu(params->Counter, OutPos) - OutPos; + ALuint Offset = params->Offset + OutPos; ALIGN(16) ALfloat Coeffs[HRIR_LENGTH][2]; ALuint Delay[2]; ALfloat left, right; diff --git a/Alc/mixer_neon.c b/Alc/mixer_neon.c index 448eadd3..15703226 100644 --- a/Alc/mixer_neon.c +++ b/Alc/mixer_neon.c @@ -78,27 +78,48 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], void MixDirect_Neon(DirectParams *params, const ALfloat *restrict data, ALuint srcchan, - ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) + ALuint OutPos, ALuint UNUSED(SamplesToDo), ALuint BufferSize) { ALfloat (*restrict OutBuffer)[BUFFERSIZE] = params->OutBuffer; - ALfloat *restrict ClickRemoval = params->ClickRemoval; - ALfloat *restrict PendingClicks = params->PendingClicks; - ALfloat DrySend; + ALuint Counter = maxu(params->Counter, OutPos) - OutPos; + ALfloat DrySend, Step; float32x4_t gain; - ALuint pos; ALuint c; for(c = 0;c < MaxChannels;c++) { - DrySend = params->Mix.Gains[srcchan][c]; + ALuint pos = 0; + Step = params->Mix.Gains.Step[srcchan][c]; + if(Step != 1.0f && Counter > 0) + { + DrySend = params->Mix.Gains.Current[srcchan][c]; + if(BufferSize-pos > 3 && Counter-pos > 3) + { + OutBuffer[c][OutPos+pos ] += data[pos ]*DrySend; + DrySend *= Step; + OutBuffer[c][OutPos+pos+1] += data[pos+1]*DrySend; + DrySend *= Step; + OutBuffer[c][OutPos+pos+2] += data[pos+2]*DrySend; + DrySend *= Step; + OutBuffer[c][OutPos+pos+4] += data[pos+3]*DrySend; + DrySend *= Step; + } + if(!(BufferSize-pos > 3)) + { + for(;pos < BufferSize && pos < Counter;pos++) + { + OutBuffer[c][OutPos+pos] += data[pos]*DrySend; + DrySend *= Step; + } + } + params->Mix.Gains.Current[srcchan][c] = DrySend; + } + + DrySend = params->Mix.Gains.Target[srcchan][c]; if(!(DrySend > GAIN_SILENCE_THRESHOLD)) continue; - - if(OutPos == 0) - ClickRemoval[c] -= data[0]*DrySend; - gain = vdupq_n_f32(DrySend); - for(pos = 0;BufferSize-pos > 3;pos += 4) + for(;BufferSize-pos > 3;pos += 4) { const float32x4_t val4 = vld1q_f32(&data[pos]); float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]); @@ -107,9 +128,6 @@ void MixDirect_Neon(DirectParams *params, const ALfloat *restrict data, ALuint s } for(;pos < BufferSize;pos++) OutBuffer[c][OutPos+pos] += data[pos]*DrySend; - - if(OutPos+pos == SamplesToDo) - PendingClicks[c] += data[pos]*DrySend; } } diff --git a/Alc/mixer_sse.c b/Alc/mixer_sse.c index c9296d3d..52785086 100644 --- a/Alc/mixer_sse.c +++ b/Alc/mixer_sse.c @@ -141,27 +141,56 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], void MixDirect_SSE(DirectParams *params, const ALfloat *restrict data, ALuint srcchan, - ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) + ALuint OutPos, ALuint UNUSED(SamplesToDo), ALuint BufferSize) { ALfloat (*restrict OutBuffer)[BUFFERSIZE] = params->OutBuffer; - ALfloat *restrict ClickRemoval = params->ClickRemoval; - ALfloat *restrict PendingClicks = params->PendingClicks; - ALfloat DrySend; - __m128 gain; - ALuint pos; + ALuint Counter = maxu(params->Counter, OutPos) - OutPos; + ALfloat DrySend, Step; + __m128 gain, step; ALuint c; for(c = 0;c < MaxChannels;c++) { - DrySend = params->Mix.Gains[srcchan][c]; + ALuint pos = 0; + Step = params->Mix.Gains.Step[srcchan][c]; + if(Step != 1.0f && Counter > 0) + { + DrySend = params->Mix.Gains.Current[srcchan][c]; + if(BufferSize-pos > 3 && Counter-pos > 3) + { + gain = _mm_set_ps( + DrySend, + DrySend * Step, + DrySend * Step * Step, + DrySend * Step * Step * Step + ); + step = _mm_set1_ps(Step * Step * Step * Step); + do { + const __m128 val4 = _mm_load_ps(&data[pos]); + __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]); + dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain)); + gain = _mm_mul_ps(gain, step); + _mm_store_ps(&OutBuffer[c][OutPos+pos], dry4); + pos += 4; + } while(BufferSize-pos > 3 && Counter-pos > 3); + DrySend = _mm_cvtss_f32(_mm_shuffle_ps(gain, gain, _MM_SHUFFLE(3, 3, 3, 3))); + } + if(!(BufferSize-pos > 3)) + { + for(;pos < BufferSize && pos < Counter;pos++) + { + OutBuffer[c][OutPos+pos] += data[pos]*DrySend; + DrySend *= Step; + } + } + params->Mix.Gains.Current[srcchan][c] = DrySend; + } + + DrySend = params->Mix.Gains.Target[srcchan][c]; if(!(DrySend > GAIN_SILENCE_THRESHOLD)) continue; - - if(OutPos == 0) - ClickRemoval[c] -= data[0]*DrySend; - gain = _mm_set1_ps(DrySend); - for(pos = 0;BufferSize-pos > 3;pos += 4) + for(;BufferSize-pos > 3;pos += 4) { const __m128 val4 = _mm_load_ps(&data[pos]); __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]); @@ -170,9 +199,6 @@ void MixDirect_SSE(DirectParams *params, const ALfloat *restrict data, ALuint sr } for(;pos < BufferSize;pos++) OutBuffer[c][OutPos+pos] += data[pos]*DrySend; - - if(OutPos+pos == SamplesToDo) - PendingClicks[c] += data[pos]*DrySend; } } diff --git a/OpenAL32/Include/alu.h b/OpenAL32/Include/alu.h index 3ec16565..6202c28a 100644 --- a/OpenAL32/Include/alu.h +++ b/OpenAL32/Include/alu.h @@ -40,7 +40,6 @@ extern "C" { typedef struct HrtfState { ALIGN(16) ALfloat History[MAX_INPUT_CHANNELS][SRC_HISTORY_LENGTH]; ALIGN(16) ALfloat Values[MAX_INPUT_CHANNELS][HRIR_LENGTH][2]; - ALuint Offset; } HrtfState; typedef struct HrtfParams { @@ -67,12 +66,18 @@ typedef struct DirectParams { /* A mixing matrix. First subscript is the channel number of the input * data (regardless of channel configuration) and the second is the * channel target (eg. FrontLeft). Not used with HRTF. */ - ALfloat Gains[MAX_INPUT_CHANNELS][MaxChannels]; + struct { + ALfloat Current[MAX_INPUT_CHANNELS][MaxChannels]; + ALfloat Step[MAX_INPUT_CHANNELS][MaxChannels]; + ALfloat Target[MAX_INPUT_CHANNELS][MaxChannels]; + } Gains; } Mix; /* If not 'moving', gain/coefficients are set directly without fading. */ ALboolean Moving; /* Stepping counter for gain/coefficient fading. */ ALuint Counter; + /* History/coefficient offset. */ + ALuint Offset; ALfilterState LpFilter[MAX_INPUT_CHANNELS]; } DirectParams; -- 2.11.4.GIT