aboutsummaryrefslogtreecommitdiffstats
path: root/Alc
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2017-12-18 13:41:12 -0800
committerChris Robinson <[email protected]>2017-12-18 13:41:12 -0800
commit661bd054aa94a5ce99433cf6fd4b8f99ed77f87e (patch)
treeba9307a3e94e8ec57d61d0077f13036a5a1c40df /Alc
parenteee4aca40b65b77f2f25858089c1be6f0759c780 (diff)
Use a single delay line for chorus feedback on a fixed tap
The outputs themselves use a variale-delay tap, but using a separate fixed- delay tap on the feedback helps improve the perceived "wobble" with sustained notes. This also applies to the flanger effect.
Diffstat (limited to 'Alc')
-rw-r--r--Alc/effects/chorus.c105
-rw-r--r--Alc/effects/flanger.c105
2 files changed, 118 insertions, 92 deletions
diff --git a/Alc/effects/chorus.c b/Alc/effects/chorus.c
index e8f2c797..fc49d170 100644
--- a/Alc/effects/chorus.c
+++ b/Alc/effects/chorus.c
@@ -38,7 +38,7 @@ enum ChorusWaveForm {
typedef struct ALchorusState {
DERIVE_FROM_TYPE(ALeffectState);
- ALfloat *SampleBuffer[2];
+ ALfloat *SampleBuffer;
ALsizei BufferLength;
ALsizei offset;
@@ -72,8 +72,7 @@ static void ALchorusState_Construct(ALchorusState *state)
SET_VTABLE2(ALchorusState, ALeffectState, state);
state->BufferLength = 0;
- state->SampleBuffer[0] = NULL;
- state->SampleBuffer[1] = NULL;
+ state->SampleBuffer = NULL;
state->offset = 0;
state->lfo_offset = 0;
state->lfo_range = 1;
@@ -82,9 +81,8 @@ static void ALchorusState_Construct(ALchorusState *state)
static ALvoid ALchorusState_Destruct(ALchorusState *state)
{
- al_free(state->SampleBuffer[0]);
- state->SampleBuffer[0] = NULL;
- state->SampleBuffer[1] = NULL;
+ al_free(state->SampleBuffer);
+ state->SampleBuffer = NULL;
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
@@ -99,21 +97,17 @@ static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Dev
if(maxlen != state->BufferLength)
{
- void *temp = al_calloc(16, maxlen * sizeof(ALfloat) * 2);
+ void *temp = al_calloc(16, maxlen * sizeof(ALfloat));
if(!temp) return AL_FALSE;
- al_free(state->SampleBuffer[0]);
- state->SampleBuffer[0] = temp;
- state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
+ al_free(state->SampleBuffer);
+ state->SampleBuffer = temp;
state->BufferLength = maxlen;
}
for(it = 0;it < state->BufferLength;it++)
- {
- state->SampleBuffer[0][it] = 0.0f;
- state->SampleBuffer[1][it] = 0.0f;
- }
+ state->SampleBuffer[it] = 0.0f;
return AL_TRUE;
}
@@ -144,7 +138,7 @@ static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Conte
/* Offset the delay so that the center point remains the same with the LFO
* ranging from 0...2 instead of -1...+1.
*/
- state->delay = fastf2i(delay - state->depth + 0.5f);
+ state->delay = fastf2i(delay-state->depth + 0.5f);
state->feedback = props->Chorus.Feedback;
@@ -218,49 +212,68 @@ static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, c
{
const ALsizei bufmask = state->BufferLength-1;
const ALfloat feedback = state->feedback;
+ const ALsizei avgdelay = (state->delay+fastf2i(state->depth) + (FRACTIONONE>>1)) >>
+ FRACTIONBITS;
+ ALfloat *restrict delaybuf = state->SampleBuffer;
ALsizei i, c;
ALsizei base;
for(base = 0;base < SamplesToDo;)
{
const ALsizei todo = mini(256, SamplesToDo-base);
- ALfloat temps[256];
+ ALint moddelays[2][256];
+ ALfloat temps[2][256];
+ ALsizei offset;
- for(c = 0;c < 2;c++)
+ if(state->waveform == CWF_Triangle)
{
- ALfloat *restrict sampbuf = state->SampleBuffer[c];
- ALint disp_offset = state->lfo_disp*c;
- ALint moddelays[256];
- ALsizei offset;
-
- if(state->waveform == CWF_Triangle)
- GetTriangleDelays(moddelays, (state->lfo_offset+disp_offset)%state->lfo_range,
- state->lfo_range, state->lfo_scale, state->depth, state->delay,
- todo);
- else /*if(state->waveform == CWF_Sinusoid)*/
- GetSinusoidDelays(moddelays, (state->lfo_offset+disp_offset)%state->lfo_range,
- state->lfo_range, state->lfo_scale, state->depth, state->delay,
- todo);
-
- offset = state->offset;
- for(i = 0;i < todo;i++)
- {
- ALint delay = moddelays[i] >> FRACTIONBITS;
- ALfloat mu = (moddelays[i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
-
- sampbuf[offset&bufmask] = SamplesIn[0][base+i];
- temps[i] = sampbuf[(offset-delay) & bufmask]*(1.0f-mu) +
- sampbuf[(offset-(delay+1)) & bufmask]*mu;
- sampbuf[offset&bufmask] += temps[i] * feedback;
- offset++;
- }
-
- MixSamples(temps, NumChannels, SamplesOut, state->Gain[c], state->Gain[c],
- 0, base, todo);
+ GetTriangleDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale,
+ state->depth, state->delay, todo);
+ GetTriangleDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range,
+ state->lfo_range, state->lfo_scale, state->depth, state->delay,
+ todo);
+ }
+ else /*if(state->waveform == CWF_Sinusoid)*/
+ {
+ GetSinusoidDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale,
+ state->depth, state->delay, todo);
+ GetSinusoidDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range,
+ state->lfo_range, state->lfo_scale, state->depth, state->delay,
+ todo);
+ }
+
+ offset = state->offset;
+ for(i = 0;i < todo;i++)
+ {
+ ALint delay;
+ ALfloat mu;
+
+ // Feed the buffer's input first (necessary for delays < 1).
+ delaybuf[offset&bufmask] = SamplesIn[0][base+i];
+
+ // Tap for the left output.
+ delay = moddelays[0][i] >> FRACTIONBITS;
+ mu = (moddelays[0][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
+ temps[0][i] = delaybuf[(offset-delay) & bufmask]*(1.0f-mu) +
+ delaybuf[(offset-(delay+1)) & bufmask]*mu;
+
+ // Tap for the right output.
+ delay = moddelays[1][i] >> FRACTIONBITS;
+ mu = (moddelays[1][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
+ temps[1][i] = delaybuf[(offset-delay) & bufmask]*(1.0f-mu) +
+ delaybuf[(offset-(delay+1)) & bufmask]*mu;
+
+ // Accumulate feedback from the average delay of the taps.
+ delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback;
+ offset++;
}
state->offset += todo;
state->lfo_offset = (state->lfo_offset+todo) % state->lfo_range;
+ for(c = 0;c < 2;c++)
+ MixSamples(temps[c], NumChannels, SamplesOut, state->Gain[c], state->Gain[c],
+ 0, base, todo);
+
base += todo;
}
}
diff --git a/Alc/effects/flanger.c b/Alc/effects/flanger.c
index a749f7ba..bbd16059 100644
--- a/Alc/effects/flanger.c
+++ b/Alc/effects/flanger.c
@@ -38,7 +38,7 @@ enum FlangerWaveForm {
typedef struct ALflangerState {
DERIVE_FROM_TYPE(ALeffectState);
- ALfloat *SampleBuffer[2];
+ ALfloat *SampleBuffer;
ALsizei BufferLength;
ALsizei offset;
@@ -72,8 +72,7 @@ static void ALflangerState_Construct(ALflangerState *state)
SET_VTABLE2(ALflangerState, ALeffectState, state);
state->BufferLength = 0;
- state->SampleBuffer[0] = NULL;
- state->SampleBuffer[1] = NULL;
+ state->SampleBuffer = NULL;
state->offset = 0;
state->lfo_offset = 0;
state->lfo_range = 1;
@@ -82,9 +81,8 @@ static void ALflangerState_Construct(ALflangerState *state)
static ALvoid ALflangerState_Destruct(ALflangerState *state)
{
- al_free(state->SampleBuffer[0]);
- state->SampleBuffer[0] = NULL;
- state->SampleBuffer[1] = NULL;
+ al_free(state->SampleBuffer);
+ state->SampleBuffer = NULL;
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
@@ -99,21 +97,17 @@ static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *D
if(maxlen != state->BufferLength)
{
- void *temp = al_calloc(16, maxlen * sizeof(ALfloat) * 2);
+ void *temp = al_calloc(16, maxlen * sizeof(ALfloat));
if(!temp) return AL_FALSE;
- al_free(state->SampleBuffer[0]);
- state->SampleBuffer[0] = temp;
- state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
+ al_free(state->SampleBuffer);
+ state->SampleBuffer = temp;
state->BufferLength = maxlen;
}
for(it = 0;it < state->BufferLength;it++)
- {
- state->SampleBuffer[0][it] = 0.0f;
- state->SampleBuffer[1][it] = 0.0f;
- }
+ state->SampleBuffer[it] = 0.0f;
return AL_TRUE;
}
@@ -144,7 +138,7 @@ static ALvoid ALflangerState_update(ALflangerState *state, const ALCcontext *con
/* Offset the delay so that the center point remains the same with the LFO
* ranging from 0...2 instead of -1...+1.
*/
- state->delay = fastf2i(delay - state->depth + 0.5f);
+ state->delay = fastf2i(delay-state->depth + 0.5f);
state->feedback = props->Flanger.Feedback;
@@ -217,49 +211,68 @@ static ALvoid ALflangerState_process(ALflangerState *state, ALsizei SamplesToDo,
{
const ALsizei bufmask = state->BufferLength-1;
const ALfloat feedback = state->feedback;
+ const ALsizei avgdelay = (state->delay+fastf2i(state->depth) + (FRACTIONONE>>1)) >>
+ FRACTIONBITS;
+ ALfloat *restrict delaybuf = state->SampleBuffer;
ALsizei i, c;
ALsizei base;
for(base = 0;base < SamplesToDo;)
{
const ALsizei todo = mini(256, SamplesToDo-base);
- ALfloat temps[256];
+ ALint moddelays[2][256];
+ ALfloat temps[2][256];
+ ALsizei offset;
- for(c = 0;c < 2;c++)
+ if(state->waveform == FWF_Triangle)
{
- ALfloat *restrict sampbuf = state->SampleBuffer[c];
- ALint disp_offset = state->lfo_disp*c;
- ALint moddelays[256];
- ALsizei offset;
-
- if(state->waveform == FWF_Triangle)
- GetTriangleDelays(moddelays, (state->lfo_offset+disp_offset)%state->lfo_range,
- state->lfo_range, state->lfo_scale, state->depth, state->delay,
- todo);
- else /*if(state->waveform == FWF_Sinusoid)*/
- GetSinusoidDelays(moddelays, (state->lfo_offset+disp_offset)%state->lfo_range,
- state->lfo_range, state->lfo_scale, state->depth, state->delay,
- todo);
-
- offset = state->offset;
- for(i = 0;i < todo;i++)
- {
- ALint delay = moddelays[i] >> FRACTIONBITS;
- ALfloat mu = (moddelays[i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
-
- sampbuf[offset&bufmask] = SamplesIn[0][base+i];
- temps[i] = sampbuf[(offset-delay) & bufmask]*(1.0f-mu) +
- sampbuf[(offset-(delay+1)) & bufmask]*mu;
- sampbuf[offset&bufmask] += temps[i] * feedback;
- offset++;
- }
-
- MixSamples(temps, NumChannels, SamplesOut, state->Gain[c], state->Gain[c],
- 0, base, todo);
+ GetTriangleDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale,
+ state->depth, state->delay, todo);
+ GetTriangleDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range,
+ state->lfo_range, state->lfo_scale, state->depth, state->delay,
+ todo);
+ }
+ else /*if(state->waveform == FWF_Sinusoid)*/
+ {
+ GetSinusoidDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale,
+ state->depth, state->delay, todo);
+ GetSinusoidDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range,
+ state->lfo_range, state->lfo_scale, state->depth, state->delay,
+ todo);
+ }
+
+ offset = state->offset;
+ for(i = 0;i < todo;i++)
+ {
+ ALint delay;
+ ALfloat mu;
+
+ // Feed the buffer's input first (necessary for delays < 1).
+ delaybuf[offset&bufmask] = SamplesIn[0][base+i];
+
+ // Tap for the left output.
+ delay = moddelays[0][i] >> FRACTIONBITS;
+ mu = (moddelays[0][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
+ temps[0][i] = delaybuf[(offset-delay) & bufmask]*(1.0f-mu) +
+ delaybuf[(offset-(delay+1)) & bufmask]*mu;
+
+ // Tap for the right output.
+ delay = moddelays[1][i] >> FRACTIONBITS;
+ mu = (moddelays[1][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
+ temps[1][i] = delaybuf[(offset-delay) & bufmask]*(1.0f-mu) +
+ delaybuf[(offset-(delay+1)) & bufmask]*mu;
+
+ // Accumulate feedback from the average delay.
+ delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback;
+ offset++;
}
state->offset += todo;
state->lfo_offset = (state->lfo_offset+todo) % state->lfo_range;
+ for(c = 0;c < 2;c++)
+ MixSamples(temps[c], NumChannels, SamplesOut, state->Gain[c], state->Gain[c],
+ 0, base, todo);
+
base += todo;
}
}