mirror of
https://github.com/love2d/megasource.git
synced 2026-08-20 04:30:45 +02:00
Update OpenAL Soft to commit 414b56edec5441211dc924fef365c54267c04f1c
This commit is contained in:
@@ -24,32 +24,40 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
|
||||
enum ChorusWaveForm {
|
||||
CWF_Triangle = AL_CHORUS_WAVEFORM_TRIANGLE,
|
||||
CWF_Sinusoid = AL_CHORUS_WAVEFORM_SINUSOID
|
||||
static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
|
||||
static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
|
||||
|
||||
enum WaveForm {
|
||||
WF_Sinusoid,
|
||||
WF_Triangle
|
||||
};
|
||||
|
||||
typedef struct ALchorusState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
ALfloat *SampleBuffer[2];
|
||||
ALfloat *SampleBuffer;
|
||||
ALsizei BufferLength;
|
||||
ALsizei offset;
|
||||
|
||||
ALsizei lfo_offset;
|
||||
ALsizei lfo_range;
|
||||
ALfloat lfo_scale;
|
||||
ALint lfo_disp;
|
||||
|
||||
/* Gains for left and right sides */
|
||||
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
|
||||
struct {
|
||||
ALfloat Current[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat Target[MAX_OUTPUT_CHANNELS];
|
||||
} Gains[2];
|
||||
|
||||
/* effect parameters */
|
||||
enum ChorusWaveForm waveform;
|
||||
enum WaveForm waveform;
|
||||
ALint delay;
|
||||
ALfloat depth;
|
||||
ALfloat feedback;
|
||||
@@ -57,7 +65,7 @@ typedef struct ALchorusState {
|
||||
|
||||
static ALvoid ALchorusState_Destruct(ALchorusState *state);
|
||||
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device);
|
||||
static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
|
||||
|
||||
@@ -70,54 +78,51 @@ 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;
|
||||
state->waveform = CWF_Triangle;
|
||||
state->waveform = WF_Triangle;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device)
|
||||
{
|
||||
const ALfloat max_delay = maxf(AL_CHORUS_MAX_DELAY, AL_FLANGER_MAX_DELAY);
|
||||
ALsizei maxlen;
|
||||
ALsizei it;
|
||||
|
||||
maxlen = fastf2i(AL_CHORUS_MAX_DELAY * 2.0f * Device->Frequency) + 1;
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
maxlen = NextPowerOf2(fastf2i(max_delay*2.0f*Device->Frequency) + 1);
|
||||
if(maxlen <= 0) return AL_FALSE;
|
||||
|
||||
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;
|
||||
}
|
||||
memset(state->SampleBuffer, 0, state->BufferLength*sizeof(ALfloat));
|
||||
memset(state->Gains, 0, sizeof(state->Gains));
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
{
|
||||
ALfloat frequency = (ALfloat)Device->Frequency;
|
||||
const ALsizei mindelay = MAX_RESAMPLE_PADDING << FRACTIONBITS;
|
||||
const ALCdevice *device = Context->Device;
|
||||
ALfloat frequency = (ALfloat)device->Frequency;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
ALfloat rate;
|
||||
ALint phase;
|
||||
@@ -125,50 +130,61 @@ static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device
|
||||
switch(props->Chorus.Waveform)
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM_TRIANGLE:
|
||||
state->waveform = CWF_Triangle;
|
||||
state->waveform = WF_Triangle;
|
||||
break;
|
||||
case AL_CHORUS_WAVEFORM_SINUSOID:
|
||||
state->waveform = CWF_Sinusoid;
|
||||
state->waveform = WF_Sinusoid;
|
||||
break;
|
||||
}
|
||||
|
||||
/* The LFO depth is scaled to be relative to the sample delay. Clamp the
|
||||
* delay and depth to allow enough padding for resampling.
|
||||
*/
|
||||
state->delay = maxi(fastf2i(props->Chorus.Delay*frequency*FRACTIONONE + 0.5f),
|
||||
mindelay);
|
||||
state->depth = minf(props->Chorus.Depth * state->delay,
|
||||
(ALfloat)(state->delay - mindelay));
|
||||
|
||||
state->feedback = props->Chorus.Feedback;
|
||||
state->delay = fastf2i(props->Chorus.Delay * frequency);
|
||||
/* The LFO depth is scaled to be relative to the sample delay. */
|
||||
state->depth = props->Chorus.Depth * state->delay;
|
||||
|
||||
/* Gains for left and right sides */
|
||||
CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[0]);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target);
|
||||
CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[1]);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target);
|
||||
|
||||
phase = props->Chorus.Phase;
|
||||
rate = props->Chorus.Rate;
|
||||
if(!(rate > 0.0f))
|
||||
{
|
||||
state->lfo_scale = 0.0f;
|
||||
state->lfo_offset = 0;
|
||||
state->lfo_range = 1;
|
||||
state->lfo_scale = 0.0f;
|
||||
state->lfo_disp = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Calculate LFO coefficient */
|
||||
state->lfo_range = fastf2i(frequency/rate + 0.5f);
|
||||
/* Calculate LFO coefficient (number of samples per cycle). Limit the
|
||||
* max range to avoid overflow when calculating the displacement.
|
||||
*/
|
||||
ALsizei lfo_range = mini(fastf2i(frequency/rate + 0.5f), INT_MAX/360 - 180);
|
||||
|
||||
state->lfo_offset = fastf2i((ALfloat)state->lfo_offset/state->lfo_range*
|
||||
lfo_range + 0.5f) % lfo_range;
|
||||
state->lfo_range = lfo_range;
|
||||
switch(state->waveform)
|
||||
{
|
||||
case CWF_Triangle:
|
||||
case WF_Triangle:
|
||||
state->lfo_scale = 4.0f / state->lfo_range;
|
||||
break;
|
||||
case CWF_Sinusoid:
|
||||
case WF_Sinusoid:
|
||||
state->lfo_scale = F_TAU / state->lfo_range;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculate lfo phase displacement */
|
||||
if(phase >= 0)
|
||||
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
|
||||
else
|
||||
state->lfo_disp = fastf2i(state->lfo_range * ((360+phase)/360.0f));
|
||||
if(phase < 0) phase = 360 + phase;
|
||||
state->lfo_disp = (state->lfo_range*phase + 180) / 360;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,67 +215,68 @@ static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsi
|
||||
|
||||
static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALfloat *restrict leftbuf = state->SampleBuffer[0];
|
||||
ALfloat *restrict rightbuf = state->SampleBuffer[1];
|
||||
const ALsizei bufmask = state->BufferLength-1;
|
||||
const ALfloat feedback = state->feedback;
|
||||
const ALsizei avgdelay = (state->delay + (FRACTIONONE>>1)) >> FRACTIONBITS;
|
||||
ALfloat *restrict delaybuf = state->SampleBuffer;
|
||||
ALsizei offset = state->offset;
|
||||
ALsizei i, c;
|
||||
ALsizei base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
const ALsizei todo = mini(128, SamplesToDo-base);
|
||||
ALfloat temps[128][2];
|
||||
ALint moddelays[2][128];
|
||||
const ALsizei todo = mini(256, SamplesToDo-base);
|
||||
ALint moddelays[2][256];
|
||||
alignas(16) ALfloat temps[2][256];
|
||||
|
||||
switch(state->waveform)
|
||||
if(state->waveform == WF_Sinusoid)
|
||||
{
|
||||
case CWF_Triangle:
|
||||
GetTriangleDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
|
||||
state->lfo_scale, state->depth, state->delay, todo);
|
||||
GetTriangleDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
break;
|
||||
case CWF_Sinusoid:
|
||||
GetSinusoidDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
|
||||
state->lfo_scale, state->depth, state->delay, todo);
|
||||
GetSinusoidDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
break;
|
||||
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);
|
||||
}
|
||||
else /*if(state->waveform == WF_Triangle)*/
|
||||
{
|
||||
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);
|
||||
}
|
||||
state->lfo_offset = (state->lfo_offset+todo) % state->lfo_range;
|
||||
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
leftbuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback;
|
||||
leftbuf[offset&bufmask] += temps[i][0];
|
||||
ALint delay;
|
||||
ALfloat mu;
|
||||
|
||||
rightbuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback;
|
||||
rightbuf[offset&bufmask] += temps[i][1];
|
||||
// Feed the buffer's input first (necessary for delays < 1).
|
||||
delaybuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
|
||||
// Tap for the left output.
|
||||
delay = offset - (moddelays[0][i]>>FRACTIONBITS);
|
||||
mu = (moddelays[0][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
|
||||
temps[0][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
|
||||
delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
|
||||
mu);
|
||||
|
||||
// Tap for the right output.
|
||||
delay = offset - (moddelays[1][i]>>FRACTIONBITS);
|
||||
mu = (moddelays[1][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
|
||||
temps[1][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
|
||||
delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
|
||||
mu);
|
||||
|
||||
// Accumulate feedback from the average delay of the taps.
|
||||
delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback;
|
||||
offset++;
|
||||
}
|
||||
|
||||
for(c = 0;c < NumChannels;c++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][c];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[c][i+base] += temps[i][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][c];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[c][i+base] += temps[i][1] * gain;
|
||||
}
|
||||
}
|
||||
for(c = 0;c < 2;c++)
|
||||
MixSamples(temps[c], NumChannels, SamplesOut, state->Gains[c].Current,
|
||||
state->Gains[c].Target, SamplesToDo-base, base, todo);
|
||||
|
||||
base += todo;
|
||||
}
|
||||
@@ -268,11 +285,11 @@ static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, c
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALchorusStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALchorusStateFactory;
|
||||
typedef struct ChorusStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} ChorusStateFactory;
|
||||
|
||||
static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(factory))
|
||||
static ALeffectState *ChorusStateFactory_create(ChorusStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALchorusState *state;
|
||||
|
||||
@@ -282,14 +299,14 @@ static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(f
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALchorusStateFactory);
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(ChorusStateFactory);
|
||||
|
||||
|
||||
ALeffectStateFactory *ALchorusStateFactory_getFactory(void)
|
||||
EffectStateFactory *ChorusStateFactory_getFactory(void)
|
||||
{
|
||||
static ALchorusStateFactory ChorusFactory = { { GET_VTABLE2(ALchorusStateFactory, ALeffectStateFactory) } };
|
||||
static ChorusStateFactory ChorusFactory = { { GET_VTABLE2(ChorusStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &ChorusFactory);
|
||||
return STATIC_CAST(EffectStateFactory, &ChorusFactory);
|
||||
}
|
||||
|
||||
|
||||
@@ -300,24 +317,22 @@ void ALchorus_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALi
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM:
|
||||
if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid chorus waveform");
|
||||
props->Chorus.Waveform = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_PHASE:
|
||||
if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus phase out of range");
|
||||
props->Chorus.Phase = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALchorus_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALchorus_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
{ ALchorus_setParami(effect, context, param, vals[0]); }
|
||||
void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
@@ -325,36 +340,34 @@ void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALf
|
||||
{
|
||||
case AL_CHORUS_RATE:
|
||||
if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus rate out of range");
|
||||
props->Chorus.Rate = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DEPTH:
|
||||
if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus depth out of range");
|
||||
props->Chorus.Depth = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_FEEDBACK:
|
||||
if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus feedback out of range");
|
||||
props->Chorus.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DELAY:
|
||||
if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus delay out of range");
|
||||
props->Chorus.Delay = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALchorus_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALchorus_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
{ ALchorus_setParamf(effect, context, param, vals[0]); }
|
||||
|
||||
void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
@@ -370,13 +383,11 @@ void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum para
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALchorus_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALchorus_getParami(effect, context, param, vals);
|
||||
}
|
||||
{ ALchorus_getParami(effect, context, param, vals); }
|
||||
void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
@@ -399,12 +410,146 @@ void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum para
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALchorus_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALchorus_getParamf(effect, context, param, vals);
|
||||
}
|
||||
{ ALchorus_getParamf(effect, context, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALchorus);
|
||||
|
||||
|
||||
/* Flanger is basically a chorus with a really short delay. They can both use
|
||||
* the same processing functions, so piggyback flanger on the chorus functions.
|
||||
*/
|
||||
typedef struct FlangerStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} FlangerStateFactory;
|
||||
|
||||
ALeffectState *FlangerStateFactory_create(FlangerStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALchorusState *state;
|
||||
|
||||
NEW_OBJ0(state, ALchorusState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(FlangerStateFactory);
|
||||
|
||||
EffectStateFactory *FlangerStateFactory_getFactory(void)
|
||||
{
|
||||
static FlangerStateFactory FlangerFactory = { { GET_VTABLE2(FlangerStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(EffectStateFactory, &FlangerFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_WAVEFORM:
|
||||
if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid flanger waveform");
|
||||
props->Chorus.Waveform = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_PHASE:
|
||||
if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger phase out of range");
|
||||
props->Chorus.Phase = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{ ALflanger_setParami(effect, context, param, vals[0]); }
|
||||
void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_RATE:
|
||||
if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger rate out of range");
|
||||
props->Chorus.Rate = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DEPTH:
|
||||
if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger depth out of range");
|
||||
props->Chorus.Depth = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_FEEDBACK:
|
||||
if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger feedback out of range");
|
||||
props->Chorus.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DELAY:
|
||||
if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger delay out of range");
|
||||
props->Chorus.Delay = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{ ALflanger_setParamf(effect, context, param, vals[0]); }
|
||||
|
||||
void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_WAVEFORM:
|
||||
*val = props->Chorus.Waveform;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_PHASE:
|
||||
*val = props->Chorus.Phase;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{ ALflanger_getParami(effect, context, param, vals); }
|
||||
void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_RATE:
|
||||
*val = props->Chorus.Rate;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DEPTH:
|
||||
*val = props->Chorus.Depth;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_FEEDBACK:
|
||||
*val = props->Chorus.Feedback;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DELAY:
|
||||
*val = props->Chorus.Delay;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{ ALflanger_getParamf(effect, context, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALflanger);
|
||||
|
||||
@@ -42,7 +42,7 @@ typedef struct ALcompressorState {
|
||||
|
||||
static ALvoid ALcompressorState_Destruct(ALcompressorState *state);
|
||||
static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device);
|
||||
static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
|
||||
|
||||
@@ -76,8 +76,9 @@ static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdev
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALuint i;
|
||||
|
||||
state->Enabled = props->Compressor.OnOff;
|
||||
@@ -85,7 +86,7 @@ static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < 4;i++)
|
||||
ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i],
|
||||
ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Gain[i]);
|
||||
}
|
||||
|
||||
@@ -178,11 +179,11 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei Sample
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALcompressorStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALcompressorStateFactory;
|
||||
typedef struct CompressorStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} CompressorStateFactory;
|
||||
|
||||
static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *UNUSED(factory))
|
||||
static ALeffectState *CompressorStateFactory_create(CompressorStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALcompressorState *state;
|
||||
|
||||
@@ -192,13 +193,13 @@ static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALcompressorStateFactory);
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(CompressorStateFactory);
|
||||
|
||||
ALeffectStateFactory *ALcompressorStateFactory_getFactory(void)
|
||||
EffectStateFactory *CompressorStateFactory_getFactory(void)
|
||||
{
|
||||
static ALcompressorStateFactory CompressorFactory = { { GET_VTABLE2(ALcompressorStateFactory, ALeffectStateFactory) } };
|
||||
static CompressorStateFactory CompressorFactory = { { GET_VTABLE2(CompressorStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &CompressorFactory);
|
||||
return STATIC_CAST(EffectStateFactory, &CompressorFactory);
|
||||
}
|
||||
|
||||
|
||||
@@ -209,24 +210,21 @@ void ALcompressor_setParami(ALeffect *effect, ALCcontext *context, ALenum param,
|
||||
{
|
||||
case AL_COMPRESSOR_ONOFF:
|
||||
if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Compressor state out of range");
|
||||
props->Compressor.OnOff = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
|
||||
param);
|
||||
}
|
||||
}
|
||||
void ALcompressor_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALcompressor_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALcompressor_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALcompressor_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
{ ALcompressor_setParami(effect, context, param, vals[0]); }
|
||||
void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
|
||||
void ALcompressor_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
|
||||
|
||||
void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
@@ -236,19 +234,17 @@ void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum
|
||||
case AL_COMPRESSOR_ONOFF:
|
||||
*val = props->Compressor.OnOff;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
|
||||
param);
|
||||
}
|
||||
}
|
||||
void ALcompressor_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALcompressor_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALcompressor_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALcompressor_getParamf(effect, context, param, vals);
|
||||
}
|
||||
{ ALcompressor_getParami(effect, context, param, vals); }
|
||||
void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
|
||||
void ALcompressor_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALcompressor);
|
||||
|
||||
@@ -23,21 +23,22 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
|
||||
typedef struct ALdedicatedState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
ALfloat gains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
} ALdedicatedState;
|
||||
|
||||
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state);
|
||||
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *device);
|
||||
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
|
||||
|
||||
@@ -46,13 +47,8 @@ DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
|
||||
|
||||
static void ALdedicatedState_Construct(ALdedicatedState *state)
|
||||
{
|
||||
ALsizei s;
|
||||
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALdedicatedState, ALeffectState, state);
|
||||
|
||||
for(s = 0;s < MAX_OUTPUT_CHANNELS;s++)
|
||||
state->gains[s] = 0.0f;
|
||||
}
|
||||
|
||||
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
|
||||
@@ -60,40 +56,44 @@ static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device))
|
||||
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *UNUSED(device))
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
state->CurrentGains[i] = 0.0f;
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat Gain;
|
||||
ALuint i;
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
state->gains[i] = 0.0f;
|
||||
state->TargetGains[i] = 0.0f;
|
||||
|
||||
Gain = Slot->Params.Gain * props->Dedicated.Gain;
|
||||
if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
|
||||
Gain = slot->Params.Gain * props->Dedicated.Gain;
|
||||
if(slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
|
||||
{
|
||||
int idx;
|
||||
if((idx=GetChannelIdxByName(device->RealOut, LFE)) != -1)
|
||||
if((idx=GetChannelIdxByName(&device->RealOut, LFE)) != -1)
|
||||
{
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
|
||||
state->gains[idx] = Gain;
|
||||
state->TargetGains[idx] = Gain;
|
||||
}
|
||||
}
|
||||
else if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
|
||||
else if(slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
|
||||
{
|
||||
int idx;
|
||||
/* Dialog goes to the front-center speaker if it exists, otherwise it
|
||||
* plays from the front-center location. */
|
||||
if((idx=GetChannelIdxByName(device->RealOut, FrontCenter)) != -1)
|
||||
if((idx=GetChannelIdxByName(&device->RealOut, FrontCenter)) != -1)
|
||||
{
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
|
||||
state->gains[idx] = Gain;
|
||||
state->TargetGains[idx] = Gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -102,34 +102,23 @@ static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->Dry.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->Dry.NumChannels;
|
||||
ComputePanningGains(device->Dry, coeffs, Gain, state->gains);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, Gain, state->TargetGains);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALsizei i, c;
|
||||
|
||||
SamplesIn = ASSUME_ALIGNED(SamplesIn, 16);
|
||||
SamplesOut = ASSUME_ALIGNED(SamplesOut, 16);
|
||||
for(c = 0;c < NumChannels;c++)
|
||||
{
|
||||
const ALfloat gain = state->gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
SamplesOut[c][i] += SamplesIn[0][i] * gain;
|
||||
}
|
||||
MixSamples(SamplesIn[0], NumChannels, SamplesOut, state->CurrentGains,
|
||||
state->TargetGains, SamplesToDo, 0, SamplesToDo);
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALdedicatedStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALdedicatedStateFactory;
|
||||
typedef struct DedicatedStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} DedicatedStateFactory;
|
||||
|
||||
ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(factory))
|
||||
ALeffectState *DedicatedStateFactory_create(DedicatedStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALdedicatedState *state;
|
||||
|
||||
@@ -139,23 +128,21 @@ ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(fa
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdedicatedStateFactory);
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(DedicatedStateFactory);
|
||||
|
||||
|
||||
ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void)
|
||||
EffectStateFactory *DedicatedStateFactory_getFactory(void)
|
||||
{
|
||||
static ALdedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(ALdedicatedStateFactory, ALeffectStateFactory) } };
|
||||
static DedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(DedicatedStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &DedicatedFactory);
|
||||
return STATIC_CAST(EffectStateFactory, &DedicatedFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALdedicated_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALdedicated_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
|
||||
void ALdedicated_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
|
||||
void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
@@ -163,25 +150,21 @@ void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param,
|
||||
{
|
||||
case AL_DEDICATED_GAIN:
|
||||
if(!(val >= 0.0f && isfinite(val)))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Dedicated gain out of range");
|
||||
props->Dedicated.Gain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALdedicated_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALdedicated_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
{ ALdedicated_setParamf(effect, context, param, vals[0]); }
|
||||
|
||||
void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALdedicated_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALdedicated_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
|
||||
void ALdedicated_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
|
||||
void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
@@ -192,12 +175,10 @@ void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum p
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALdedicated_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALdedicated_getParamf(effect, context, param, vals);
|
||||
}
|
||||
{ ALdedicated_getParamf(effect, context, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALdedicated);
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
|
||||
typedef struct ALdistortionState {
|
||||
@@ -37,15 +37,17 @@ typedef struct ALdistortionState {
|
||||
ALfloat Gain[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Effect parameters */
|
||||
ALfilterState lowpass;
|
||||
ALfilterState bandpass;
|
||||
BiquadState lowpass;
|
||||
BiquadState bandpass;
|
||||
ALfloat attenuation;
|
||||
ALfloat edge_coeff;
|
||||
|
||||
ALfloat Buffer[2][BUFFERSIZE];
|
||||
} ALdistortionState;
|
||||
|
||||
static ALvoid ALdistortionState_Destruct(ALdistortionState *state);
|
||||
static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *device);
|
||||
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
|
||||
|
||||
@@ -57,8 +59,8 @@ static void ALdistortionState_Construct(ALdistortionState *state)
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALdistortionState, ALeffectState, state);
|
||||
|
||||
ALfilterState_clear(&state->lowpass);
|
||||
ALfilterState_clear(&state->bandpass);
|
||||
BiquadState_clear(&state->lowpass);
|
||||
BiquadState_clear(&state->bandpass);
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
|
||||
@@ -71,16 +73,15 @@ static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state)
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
ALfloat frequency = (ALfloat)Device->Frequency;
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat frequency = (ALfloat)device->Frequency;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
ALfloat bandwidth;
|
||||
ALfloat cutoff;
|
||||
ALfloat edge;
|
||||
|
||||
/* Store distorted signal attenuation settings. */
|
||||
state->attenuation = props->Distortion.Gain;
|
||||
|
||||
/* Store waveshaper edge settings. */
|
||||
edge = sinf(props->Distortion.Edge * (F_PI_2));
|
||||
edge = minf(edge, 0.99f);
|
||||
@@ -92,98 +93,94 @@ static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice
|
||||
/* Multiply sampling frequency by the amount of oversampling done during
|
||||
* processing.
|
||||
*/
|
||||
ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f,
|
||||
BiquadState_setParams(&state->lowpass, BiquadType_LowPass, 1.0f,
|
||||
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
|
||||
);
|
||||
|
||||
cutoff = props->Distortion.EQCenter;
|
||||
/* Convert bandwidth in Hz to octaves. */
|
||||
bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
|
||||
ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f,
|
||||
BiquadState_setParams(&state->bandpass, BiquadType_BandPass, 1.0f,
|
||||
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
|
||||
);
|
||||
|
||||
ComputeAmbientGains(Device->Dry, Slot->Params.Gain, state->Gain);
|
||||
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain * props->Distortion.Gain,
|
||||
state->Gain);
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALfloat (*restrict buffer)[BUFFERSIZE] = state->Buffer;
|
||||
const ALfloat fc = state->edge_coeff;
|
||||
ALsizei it, kt;
|
||||
ALsizei base;
|
||||
ALsizei i, k;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
float buffer[2][64 * 4];
|
||||
ALsizei td = mini(64, SamplesToDo-base);
|
||||
|
||||
/* Perform 4x oversampling to avoid aliasing. Oversampling greatly
|
||||
* improves distortion quality and allows to implement lowpass and
|
||||
* bandpass filters using high frequencies, at which classic IIR
|
||||
* filters became unstable.
|
||||
*/
|
||||
ALsizei todo = mini(BUFFERSIZE, (SamplesToDo-base) * 4);
|
||||
|
||||
/* Fill oversample buffer using zero stuffing. */
|
||||
for(it = 0;it < td;it++)
|
||||
{
|
||||
/* Multiply the sample by the amount of oversampling to maintain
|
||||
* the signal's power.
|
||||
*/
|
||||
buffer[0][it*4 + 0] = SamplesIn[0][it+base] * 4.0f;
|
||||
buffer[0][it*4 + 1] = 0.0f;
|
||||
buffer[0][it*4 + 2] = 0.0f;
|
||||
buffer[0][it*4 + 3] = 0.0f;
|
||||
}
|
||||
/* Fill oversample buffer using zero stuffing. Multiply the sample by
|
||||
* the amount of oversampling to maintain the signal's power.
|
||||
*/
|
||||
for(i = 0;i < todo;i++)
|
||||
buffer[0][i] = !(i&3) ? SamplesIn[0][(i>>2)+base] * 4.0f : 0.0f;
|
||||
|
||||
/* First step, do lowpass filtering of original signal. Additionally
|
||||
* perform buffer interpolation and lowpass cutoff for oversampling
|
||||
* (which is fortunately first step of distortion). So combine three
|
||||
* operations into the one.
|
||||
*/
|
||||
ALfilterState_process(&state->lowpass, buffer[1], buffer[0], td*4);
|
||||
BiquadState_process(&state->lowpass, buffer[1], buffer[0], todo);
|
||||
|
||||
/* Second step, do distortion using waveshaper function to emulate
|
||||
* signal processing during tube overdriving. Three steps of
|
||||
* waveshaping are intended to modify waveform without boost/clipping/
|
||||
* attenuation process.
|
||||
*/
|
||||
for(it = 0;it < td*4;it++)
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALfloat smp = buffer[1][it];
|
||||
ALfloat smp = buffer[1][i];
|
||||
|
||||
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
|
||||
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
|
||||
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
|
||||
|
||||
buffer[0][it] = smp;
|
||||
buffer[0][i] = smp;
|
||||
}
|
||||
|
||||
/* Third step, do bandpass filtering of distorted signal. */
|
||||
ALfilterState_process(&state->bandpass, buffer[1], buffer[0], td*4);
|
||||
BiquadState_process(&state->bandpass, buffer[1], buffer[0], todo);
|
||||
|
||||
for(kt = 0;kt < NumChannels;kt++)
|
||||
todo >>= 2;
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
{
|
||||
/* Fourth step, final, do attenuation and perform decimation,
|
||||
* store only one sample out of 4.
|
||||
* storing only one sample out of four.
|
||||
*/
|
||||
ALfloat gain = state->Gain[kt] * state->attenuation;
|
||||
ALfloat gain = state->Gain[k];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][base+it] += gain * buffer[1][it*4];
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[k][base+i] += gain * buffer[1][i*4];
|
||||
}
|
||||
|
||||
base += td;
|
||||
base += todo;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALdistortionStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALdistortionStateFactory;
|
||||
typedef struct DistortionStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} DistortionStateFactory;
|
||||
|
||||
static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *UNUSED(factory))
|
||||
static ALeffectState *DistortionStateFactory_create(DistortionStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALdistortionState *state;
|
||||
|
||||
@@ -193,23 +190,21 @@ static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdistortionStateFactory);
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(DistortionStateFactory);
|
||||
|
||||
|
||||
ALeffectStateFactory *ALdistortionStateFactory_getFactory(void)
|
||||
EffectStateFactory *DistortionStateFactory_getFactory(void)
|
||||
{
|
||||
static ALdistortionStateFactory DistortionFactory = { { GET_VTABLE2(ALdistortionStateFactory, ALeffectStateFactory) } };
|
||||
static DistortionStateFactory DistortionFactory = { { GET_VTABLE2(DistortionStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &DistortionFactory);
|
||||
return STATIC_CAST(EffectStateFactory, &DistortionFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALdistortion_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALdistortion_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
|
||||
void ALdistortion_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
|
||||
void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
@@ -217,49 +212,46 @@ void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param,
|
||||
{
|
||||
case AL_DISTORTION_EDGE:
|
||||
if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion edge out of range");
|
||||
props->Distortion.Edge = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_GAIN:
|
||||
if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion gain out of range");
|
||||
props->Distortion.Gain = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_LOWPASS_CUTOFF:
|
||||
if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion low-pass cutoff out of range");
|
||||
props->Distortion.LowpassCutoff = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQCENTER:
|
||||
if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ center out of range");
|
||||
props->Distortion.EQCenter = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQBANDWIDTH:
|
||||
if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ bandwidth out of range");
|
||||
props->Distortion.EQBandwidth = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
|
||||
param);
|
||||
}
|
||||
}
|
||||
void ALdistortion_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALdistortion_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
{ ALdistortion_setParamf(effect, context, param, vals[0]); }
|
||||
|
||||
void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALdistortion_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALdistortion_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
|
||||
void ALdistortion_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
|
||||
void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
@@ -286,12 +278,11 @@ void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
|
||||
param);
|
||||
}
|
||||
}
|
||||
void ALdistortion_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALdistortion_getParamf(effect, context, param, vals);
|
||||
}
|
||||
{ ALdistortion_getParamf(effect, context, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALdistortion);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
|
||||
typedef struct ALechoState {
|
||||
@@ -42,17 +43,21 @@ typedef struct ALechoState {
|
||||
ALsizei delay;
|
||||
} Tap[2];
|
||||
ALsizei Offset;
|
||||
|
||||
/* The panning gains for the two taps */
|
||||
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
|
||||
struct {
|
||||
ALfloat Current[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat Target[MAX_OUTPUT_CHANNELS];
|
||||
} Gains[2];
|
||||
|
||||
ALfloat FeedGain;
|
||||
|
||||
ALfilterState Filter;
|
||||
BiquadState Filter;
|
||||
} ALechoState;
|
||||
|
||||
static ALvoid ALechoState_Destruct(ALechoState *state);
|
||||
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device);
|
||||
static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALechoState)
|
||||
|
||||
@@ -71,7 +76,7 @@ static void ALechoState_Construct(ALechoState *state)
|
||||
state->Tap[1].delay = 0;
|
||||
state->Offset = 0;
|
||||
|
||||
ALfilterState_clear(&state->Filter);
|
||||
BiquadState_clear(&state->Filter);
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_Destruct(ALechoState *state)
|
||||
@@ -83,13 +88,14 @@ static ALvoid ALechoState_Destruct(ALechoState *state)
|
||||
|
||||
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
|
||||
{
|
||||
ALsizei maxlen, i;
|
||||
ALsizei maxlen;
|
||||
|
||||
// Use the next power of 2 for the buffer length, so the tap offsets can be
|
||||
// wrapped using a mask instead of a modulo
|
||||
maxlen = fastf2i(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
|
||||
maxlen += fastf2i(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
maxlen = fastf2i(AL_ECHO_MAX_DELAY*Device->Frequency + 0.5f) +
|
||||
fastf2i(AL_ECHO_MAX_LRDELAY*Device->Frequency + 0.5f);
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
if(maxlen <= 0) return AL_FALSE;
|
||||
|
||||
if(maxlen != state->BufferLength)
|
||||
{
|
||||
@@ -100,20 +106,22 @@ static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
|
||||
state->SampleBuffer = temp;
|
||||
state->BufferLength = maxlen;
|
||||
}
|
||||
for(i = 0;i < state->BufferLength;i++)
|
||||
state->SampleBuffer[i] = 0.0f;
|
||||
|
||||
memset(state->SampleBuffer, 0, state->BufferLength*sizeof(ALfloat));
|
||||
memset(state->Gains, 0, sizeof(state->Gains));
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
ALuint frequency = Device->Frequency;
|
||||
const ALCdevice *device = context->Device;
|
||||
ALuint frequency = device->Frequency;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
ALfloat gain, lrpan, spread;
|
||||
ALfloat gainhf, lrpan, spread;
|
||||
|
||||
state->Tap[0].delay = fastf2i(props->Echo.Delay * frequency) + 1;
|
||||
state->Tap[1].delay = fastf2i(props->Echo.LRDelay * frequency);
|
||||
state->Tap[0].delay = maxi(fastf2i(props->Echo.Delay*frequency + 0.5f), 1);
|
||||
state->Tap[1].delay = fastf2i(props->Echo.LRDelay*frequency + 0.5f);
|
||||
state->Tap[1].delay += state->Tap[0].delay;
|
||||
|
||||
spread = props->Echo.Spread;
|
||||
@@ -126,20 +134,18 @@ static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, co
|
||||
|
||||
state->FeedGain = props->Echo.Feedback;
|
||||
|
||||
gain = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */
|
||||
ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf,
|
||||
gain, LOWPASSFREQREF/frequency,
|
||||
calc_rcpQ_from_slope(gain, 1.0f));
|
||||
|
||||
gain = Slot->Params.Gain;
|
||||
gainhf = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */
|
||||
BiquadState_setParams(&state->Filter, BiquadType_HighShelf,
|
||||
gainhf, LOWPASSFREQREF/frequency,
|
||||
calc_rcpQ_from_slope(gainhf, 1.0f));
|
||||
|
||||
/* First tap panning */
|
||||
CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs);
|
||||
ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[0]);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target);
|
||||
|
||||
/* Second tap panning */
|
||||
CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
|
||||
ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[1]);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[1].Target);
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
@@ -147,10 +153,11 @@ static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const
|
||||
const ALsizei mask = state->BufferLength-1;
|
||||
const ALsizei tap1 = state->Tap[0].delay;
|
||||
const ALsizei tap2 = state->Tap[1].delay;
|
||||
ALfloat *restrict delaybuf = state->SampleBuffer;
|
||||
ALsizei offset = state->Offset;
|
||||
ALfloat x[2], y[2], in, out;
|
||||
ALsizei base, k;
|
||||
ALsizei i;
|
||||
ALsizei base;
|
||||
ALsizei c, i;
|
||||
|
||||
x[0] = state->Filter.x[0];
|
||||
x[1] = state->Filter.x[1];
|
||||
@@ -158,45 +165,36 @@ static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const
|
||||
y[1] = state->Filter.y[1];
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[128][2];
|
||||
alignas(16) ALfloat temps[2][128];
|
||||
ALsizei td = mini(128, SamplesToDo-base);
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
{
|
||||
/* First tap */
|
||||
temps[i][0] = state->SampleBuffer[(offset-tap1) & mask];
|
||||
/* Second tap */
|
||||
temps[i][1] = state->SampleBuffer[(offset-tap2) & mask];
|
||||
/* Feed the delay buffer's input first. */
|
||||
delaybuf[offset&mask] = SamplesIn[0][i+base];
|
||||
|
||||
// Apply damping and feedback gain to the second tap, and mix in the
|
||||
// new sample
|
||||
in = temps[i][1] + SamplesIn[0][i+base];
|
||||
/* First tap */
|
||||
temps[0][i] = delaybuf[(offset-tap1) & mask];
|
||||
/* Second tap */
|
||||
temps[1][i] = delaybuf[(offset-tap2) & mask];
|
||||
|
||||
/* Apply damping to the second tap, then add it to the buffer with
|
||||
* feedback attenuation.
|
||||
*/
|
||||
in = temps[1][i];
|
||||
out = in*state->Filter.b0 +
|
||||
x[0]*state->Filter.b1 + x[1]*state->Filter.b2 -
|
||||
y[0]*state->Filter.a1 - y[1]*state->Filter.a2;
|
||||
x[1] = x[0]; x[0] = in;
|
||||
y[1] = y[0]; y[0] = out;
|
||||
|
||||
state->SampleBuffer[offset&mask] = out * state->FeedGain;
|
||||
delaybuf[offset&mask] += out * state->FeedGain;
|
||||
offset++;
|
||||
}
|
||||
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][k];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][i+base] += temps[i][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][k];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][i+base] += temps[i][1] * gain;
|
||||
}
|
||||
}
|
||||
for(c = 0;c < 2;c++)
|
||||
MixSamples(temps[c], NumChannels, SamplesOut, state->Gains[c].Current,
|
||||
state->Gains[c].Target, SamplesToDo-base, base, td);
|
||||
|
||||
base += td;
|
||||
}
|
||||
@@ -209,11 +207,11 @@ static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALechoStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALechoStateFactory;
|
||||
typedef struct EchoStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} EchoStateFactory;
|
||||
|
||||
ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
|
||||
ALeffectState *EchoStateFactory_create(EchoStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALechoState *state;
|
||||
|
||||
@@ -223,22 +221,20 @@ ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALechoStateFactory);
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(EchoStateFactory);
|
||||
|
||||
ALeffectStateFactory *ALechoStateFactory_getFactory(void)
|
||||
EffectStateFactory *EchoStateFactory_getFactory(void)
|
||||
{
|
||||
static ALechoStateFactory EchoFactory = { { GET_VTABLE2(ALechoStateFactory, ALeffectStateFactory) } };
|
||||
static EchoStateFactory EchoFactory = { { GET_VTABLE2(EchoStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &EchoFactory);
|
||||
return STATIC_CAST(EffectStateFactory, &EchoFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALecho_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALecho_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
|
||||
void ALecho_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
|
||||
void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
@@ -246,49 +242,45 @@ void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALflo
|
||||
{
|
||||
case AL_ECHO_DELAY:
|
||||
if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo delay out of range");
|
||||
props->Echo.Delay = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_LRDELAY:
|
||||
if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo LR delay out of range");
|
||||
props->Echo.LRDelay = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_DAMPING:
|
||||
if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo damping out of range");
|
||||
props->Echo.Damping = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_FEEDBACK:
|
||||
if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo feedback out of range");
|
||||
props->Echo.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_SPREAD:
|
||||
if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo spread out of range");
|
||||
props->Echo.Spread = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALecho_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALecho_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
{ ALecho_setParamf(effect, context, param, vals[0]); }
|
||||
|
||||
void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALecho_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALecho_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
|
||||
void ALecho_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
|
||||
void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
@@ -315,12 +307,10 @@ void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param,
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALecho_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALecho_getParamf(effect, context, param, vals);
|
||||
}
|
||||
{ ALecho_getParamf(effect, context, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALecho);
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
|
||||
/* The document "Effects Extension Guide.pdf" says that low and high *
|
||||
@@ -72,24 +72,24 @@
|
||||
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
|
||||
|
||||
|
||||
/* The maximum number of sample frames per update. */
|
||||
#define MAX_UPDATE_SAMPLES 256
|
||||
|
||||
typedef struct ALequalizerState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
|
||||
struct {
|
||||
/* Effect gains for each channel */
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Effect parameters */
|
||||
ALfilterState filter[4][MAX_EFFECT_CHANNELS];
|
||||
/* Effect parameters */
|
||||
BiquadState filter[4];
|
||||
} Chans[MAX_EFFECT_CHANNELS];
|
||||
|
||||
ALfloat SampleBuffer[4][MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES];
|
||||
ALfloat SampleBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
|
||||
} ALequalizerState;
|
||||
|
||||
static ALvoid ALequalizerState_Destruct(ALequalizerState *state);
|
||||
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *device);
|
||||
static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
|
||||
|
||||
@@ -98,18 +98,8 @@ DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
|
||||
|
||||
static void ALequalizerState_Construct(ALequalizerState *state)
|
||||
{
|
||||
int it, ft;
|
||||
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALequalizerState, ALeffectState, state);
|
||||
|
||||
/* Initialize sample history only on filter creation to avoid */
|
||||
/* sound clicks if filter settings were changed in runtime. */
|
||||
for(it = 0; it < 4; it++)
|
||||
{
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
ALfilterState_clear(&state->filter[it][ft]);
|
||||
}
|
||||
}
|
||||
|
||||
static ALvoid ALequalizerState_Destruct(ALequalizerState *state)
|
||||
@@ -117,107 +107,100 @@ static ALvoid ALequalizerState_Destruct(ALequalizerState *state)
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), ALCdevice *UNUSED(device))
|
||||
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *UNUSED(device))
|
||||
{
|
||||
ALsizei i, j;
|
||||
|
||||
for(i = 0; i < MAX_EFFECT_CHANNELS;i++)
|
||||
{
|
||||
for(j = 0;j < 4;j++)
|
||||
BiquadState_clear(&state->Chans[i].filter[j]);
|
||||
for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
|
||||
state->Chans[i].CurrentGains[j] = 0.0f;
|
||||
}
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat frequency = (ALfloat)device->Frequency;
|
||||
ALfloat gain, freq_mult;
|
||||
ALfloat gain, f0norm;
|
||||
ALuint i;
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Gain[i]);
|
||||
ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Chans[i].TargetGains);
|
||||
|
||||
/* Calculate coefficients for the each type of filter. Note that the shelf
|
||||
* filters' gain is for the reference frequency, which is the centerpoint
|
||||
* of the transition band.
|
||||
*/
|
||||
gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
|
||||
freq_mult = props->Equalizer.LowCutoff/frequency;
|
||||
ALfilterState_setParams(&state->filter[0][0], ALfilterType_LowShelf,
|
||||
gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
f0norm = props->Equalizer.LowCutoff/frequency;
|
||||
BiquadState_setParams(&state->Chans[0].filter[0], BiquadType_LowShelf,
|
||||
gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
);
|
||||
/* Copy the filter coefficients for the other input channels. */
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ALfilterState_copyParams(&state->filter[0][i], &state->filter[0][0]);
|
||||
|
||||
gain = maxf(props->Equalizer.Mid1Gain, 0.0625f);
|
||||
freq_mult = props->Equalizer.Mid1Center/frequency;
|
||||
ALfilterState_setParams(&state->filter[1][0], ALfilterType_Peaking,
|
||||
gain, freq_mult, calc_rcpQ_from_bandwidth(
|
||||
freq_mult, props->Equalizer.Mid1Width
|
||||
f0norm = props->Equalizer.Mid1Center/frequency;
|
||||
BiquadState_setParams(&state->Chans[0].filter[1], BiquadType_Peaking,
|
||||
gain, f0norm, calc_rcpQ_from_bandwidth(
|
||||
f0norm, props->Equalizer.Mid1Width
|
||||
)
|
||||
);
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ALfilterState_copyParams(&state->filter[1][i], &state->filter[1][0]);
|
||||
|
||||
gain = maxf(props->Equalizer.Mid2Gain, 0.0625f);
|
||||
freq_mult = props->Equalizer.Mid2Center/frequency;
|
||||
ALfilterState_setParams(&state->filter[2][0], ALfilterType_Peaking,
|
||||
gain, freq_mult, calc_rcpQ_from_bandwidth(
|
||||
freq_mult, props->Equalizer.Mid2Width
|
||||
f0norm = props->Equalizer.Mid2Center/frequency;
|
||||
BiquadState_setParams(&state->Chans[0].filter[2], BiquadType_Peaking,
|
||||
gain, f0norm, calc_rcpQ_from_bandwidth(
|
||||
f0norm, props->Equalizer.Mid2Width
|
||||
)
|
||||
);
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ALfilterState_copyParams(&state->filter[2][i], &state->filter[2][0]);
|
||||
|
||||
gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
|
||||
freq_mult = props->Equalizer.HighCutoff/frequency;
|
||||
ALfilterState_setParams(&state->filter[3][0], ALfilterType_HighShelf,
|
||||
gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
f0norm = props->Equalizer.HighCutoff/frequency;
|
||||
BiquadState_setParams(&state->Chans[0].filter[3], BiquadType_HighShelf,
|
||||
gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
);
|
||||
|
||||
/* Copy the filter coefficients for the other input channels. */
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ALfilterState_copyParams(&state->filter[3][i], &state->filter[3][0]);
|
||||
{
|
||||
BiquadState_copyParams(&state->Chans[i].filter[0], &state->Chans[0].filter[0]);
|
||||
BiquadState_copyParams(&state->Chans[i].filter[1], &state->Chans[0].filter[1]);
|
||||
BiquadState_copyParams(&state->Chans[i].filter[2], &state->Chans[0].filter[2]);
|
||||
BiquadState_copyParams(&state->Chans[i].filter[3], &state->Chans[0].filter[3]);
|
||||
}
|
||||
}
|
||||
|
||||
static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALfloat (*Samples)[MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES] = state->SampleBuffer;
|
||||
ALsizei it, kt, ft;
|
||||
ALsizei base;
|
||||
ALfloat (*restrict temps)[BUFFERSIZE] = state->SampleBuffer;
|
||||
ALsizei c;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
|
||||
{
|
||||
ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base);
|
||||
BiquadState_process(&state->Chans[c].filter[0], temps[0], SamplesIn[c], SamplesToDo);
|
||||
BiquadState_process(&state->Chans[c].filter[1], temps[1], temps[0], SamplesToDo);
|
||||
BiquadState_process(&state->Chans[c].filter[2], temps[2], temps[1], SamplesToDo);
|
||||
BiquadState_process(&state->Chans[c].filter[3], temps[3], temps[2], SamplesToDo);
|
||||
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
ALfilterState_process(&state->filter[0][ft], Samples[0][ft], &SamplesIn[ft][base], td);
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
ALfilterState_process(&state->filter[1][ft], Samples[1][ft], Samples[0][ft], td);
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
ALfilterState_process(&state->filter[2][ft], Samples[2][ft], Samples[1][ft], td);
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
ALfilterState_process(&state->filter[3][ft], Samples[3][ft], Samples[2][ft], td);
|
||||
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
{
|
||||
for(kt = 0;kt < NumChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[ft][kt];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][base+it] += gain * Samples[3][ft][it];
|
||||
}
|
||||
}
|
||||
|
||||
base += td;
|
||||
MixSamples(temps[3], NumChannels, SamplesOut,
|
||||
state->Chans[c].CurrentGains, state->Chans[c].TargetGains,
|
||||
SamplesToDo, 0, SamplesToDo
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALequalizerStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALequalizerStateFactory;
|
||||
typedef struct EqualizerStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} EqualizerStateFactory;
|
||||
|
||||
ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(factory))
|
||||
ALeffectState *EqualizerStateFactory_create(EqualizerStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALequalizerState *state;
|
||||
|
||||
@@ -227,22 +210,20 @@ ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(fa
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALequalizerStateFactory);
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(EqualizerStateFactory);
|
||||
|
||||
ALeffectStateFactory *ALequalizerStateFactory_getFactory(void)
|
||||
EffectStateFactory *EqualizerStateFactory_getFactory(void)
|
||||
{
|
||||
static ALequalizerStateFactory EqualizerFactory = { { GET_VTABLE2(ALequalizerStateFactory, ALeffectStateFactory) } };
|
||||
static EqualizerStateFactory EqualizerFactory = { { GET_VTABLE2(EqualizerStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &EqualizerFactory);
|
||||
return STATIC_CAST(EffectStateFactory, &EqualizerFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALequalizer_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALequalizer_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
|
||||
void ALequalizer_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
|
||||
void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
@@ -250,79 +231,75 @@ void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param,
|
||||
{
|
||||
case AL_EQUALIZER_LOW_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer low-band gain out of range");
|
||||
props->Equalizer.LowGain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_LOW_CUTOFF:
|
||||
if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer low-band cutoff out of range");
|
||||
props->Equalizer.LowCutoff = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band gain out of range");
|
||||
props->Equalizer.Mid1Gain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_CENTER:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band center out of range");
|
||||
props->Equalizer.Mid1Center = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_WIDTH:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band width out of range");
|
||||
props->Equalizer.Mid1Width = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band gain out of range");
|
||||
props->Equalizer.Mid2Gain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_CENTER:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band center out of range");
|
||||
props->Equalizer.Mid2Center = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_WIDTH:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band width out of range");
|
||||
props->Equalizer.Mid2Width = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band gain out of range");
|
||||
props->Equalizer.HighGain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_CUTOFF:
|
||||
if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band cutoff out of range");
|
||||
props->Equalizer.HighCutoff = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALequalizer_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALequalizer_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
{ ALequalizer_setParamf(effect, context, param, vals[0]); }
|
||||
|
||||
void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALequalizer_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALequalizer_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
|
||||
void ALequalizer_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
|
||||
void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
@@ -369,12 +346,10 @@ void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum p
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALequalizer_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALequalizer_getParamf(effect, context, param, vals);
|
||||
}
|
||||
{ ALequalizer_getParamf(effect, context, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALequalizer);
|
||||
|
||||
@@ -1,408 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2013 by Mike Gorchak
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
enum FlangerWaveForm {
|
||||
FWF_Triangle = AL_FLANGER_WAVEFORM_TRIANGLE,
|
||||
FWF_Sinusoid = AL_FLANGER_WAVEFORM_SINUSOID
|
||||
};
|
||||
|
||||
typedef struct ALflangerState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
ALfloat *SampleBuffer[2];
|
||||
ALsizei BufferLength;
|
||||
ALsizei offset;
|
||||
ALsizei lfo_range;
|
||||
ALfloat lfo_scale;
|
||||
ALint lfo_disp;
|
||||
|
||||
/* Gains for left and right sides */
|
||||
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* effect parameters */
|
||||
enum FlangerWaveForm waveform;
|
||||
ALint delay;
|
||||
ALfloat depth;
|
||||
ALfloat feedback;
|
||||
} ALflangerState;
|
||||
|
||||
static ALvoid ALflangerState_Destruct(ALflangerState *state);
|
||||
static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device);
|
||||
static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALflangerState_process(ALflangerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALflangerState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState);
|
||||
|
||||
|
||||
static void ALflangerState_Construct(ALflangerState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALflangerState, ALeffectState, state);
|
||||
|
||||
state->BufferLength = 0;
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
state->offset = 0;
|
||||
state->lfo_range = 1;
|
||||
state->waveform = FWF_Triangle;
|
||||
}
|
||||
|
||||
static ALvoid ALflangerState_Destruct(ALflangerState *state)
|
||||
{
|
||||
al_free(state->SampleBuffer[0]);
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device)
|
||||
{
|
||||
ALsizei maxlen;
|
||||
ALsizei it;
|
||||
|
||||
maxlen = fastf2i(AL_FLANGER_MAX_DELAY * 2.0f * Device->Frequency) + 1;
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
|
||||
if(maxlen != state->BufferLength)
|
||||
{
|
||||
void *temp = al_calloc(16, maxlen * sizeof(ALfloat) * 2);
|
||||
if(!temp) return AL_FALSE;
|
||||
|
||||
al_free(state->SampleBuffer[0]);
|
||||
state->SampleBuffer[0] = temp;
|
||||
state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
|
||||
|
||||
state->BufferLength = maxlen;
|
||||
}
|
||||
|
||||
for(it = 0;it < state->BufferLength;it++)
|
||||
{
|
||||
state->SampleBuffer[0][it] = 0.0f;
|
||||
state->SampleBuffer[1][it] = 0.0f;
|
||||
}
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
{
|
||||
ALfloat frequency = (ALfloat)Device->Frequency;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
ALfloat rate;
|
||||
ALint phase;
|
||||
|
||||
switch(props->Flanger.Waveform)
|
||||
{
|
||||
case AL_FLANGER_WAVEFORM_TRIANGLE:
|
||||
state->waveform = FWF_Triangle;
|
||||
break;
|
||||
case AL_FLANGER_WAVEFORM_SINUSOID:
|
||||
state->waveform = FWF_Sinusoid;
|
||||
break;
|
||||
}
|
||||
state->feedback = props->Flanger.Feedback;
|
||||
state->delay = fastf2i(props->Flanger.Delay * frequency);
|
||||
/* The LFO depth is scaled to be relative to the sample delay. */
|
||||
state->depth = props->Flanger.Depth * state->delay;
|
||||
|
||||
/* Gains for left and right sides */
|
||||
CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[0]);
|
||||
CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[1]);
|
||||
|
||||
phase = props->Flanger.Phase;
|
||||
rate = props->Flanger.Rate;
|
||||
if(!(rate > 0.0f))
|
||||
{
|
||||
state->lfo_scale = 0.0f;
|
||||
state->lfo_range = 1;
|
||||
state->lfo_disp = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Calculate LFO coefficient */
|
||||
state->lfo_range = fastf2i(frequency/rate + 0.5f);
|
||||
switch(state->waveform)
|
||||
{
|
||||
case FWF_Triangle:
|
||||
state->lfo_scale = 4.0f / state->lfo_range;
|
||||
break;
|
||||
case FWF_Sinusoid:
|
||||
state->lfo_scale = F_TAU / state->lfo_range;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculate lfo phase displacement */
|
||||
if(phase >= 0)
|
||||
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
|
||||
else
|
||||
state->lfo_disp = fastf2i(state->lfo_range * ((360+phase)/360.0f));
|
||||
}
|
||||
}
|
||||
|
||||
static void GetTriangleDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
|
||||
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
|
||||
const ALsizei todo)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
|
||||
offset = (offset+1)%lfo_range;
|
||||
}
|
||||
}
|
||||
|
||||
static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
|
||||
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
|
||||
const ALsizei todo)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
|
||||
offset = (offset+1)%lfo_range;
|
||||
}
|
||||
}
|
||||
|
||||
static ALvoid ALflangerState_process(ALflangerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALfloat *restrict leftbuf = state->SampleBuffer[0];
|
||||
ALfloat *restrict rightbuf = state->SampleBuffer[1];
|
||||
const ALsizei bufmask = state->BufferLength-1;
|
||||
const ALfloat feedback = state->feedback;
|
||||
ALsizei offset = state->offset;
|
||||
ALsizei i, c;
|
||||
ALsizei base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
const ALsizei todo = mini(128, SamplesToDo-base);
|
||||
ALfloat temps[128][2];
|
||||
ALint moddelays[2][128];
|
||||
|
||||
switch(state->waveform)
|
||||
{
|
||||
case FWF_Triangle:
|
||||
GetTriangleDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
|
||||
state->lfo_scale, state->depth, state->delay, todo);
|
||||
GetTriangleDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
break;
|
||||
case FWF_Sinusoid:
|
||||
GetSinusoidDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
|
||||
state->lfo_scale, state->depth, state->delay, todo);
|
||||
GetSinusoidDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
break;
|
||||
}
|
||||
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
leftbuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback;
|
||||
leftbuf[offset&bufmask] += temps[i][0];
|
||||
|
||||
rightbuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback;
|
||||
rightbuf[offset&bufmask] += temps[i][1];
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
for(c = 0;c < NumChannels;c++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][c];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[c][i+base] += temps[i][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][c];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[c][i+base] += temps[i][1] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
base += todo;
|
||||
}
|
||||
|
||||
state->offset = offset;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALflangerStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALflangerStateFactory;
|
||||
|
||||
ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALflangerState *state;
|
||||
|
||||
NEW_OBJ0(state, ALflangerState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALflangerStateFactory);
|
||||
|
||||
ALeffectStateFactory *ALflangerStateFactory_getFactory(void)
|
||||
{
|
||||
static ALflangerStateFactory FlangerFactory = { { GET_VTABLE2(ALflangerStateFactory, ALeffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &FlangerFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_WAVEFORM:
|
||||
if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Flanger.Waveform = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_PHASE:
|
||||
if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Flanger.Phase = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALflanger_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_RATE:
|
||||
if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Flanger.Rate = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DEPTH:
|
||||
if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Flanger.Depth = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_FEEDBACK:
|
||||
if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Flanger.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DELAY:
|
||||
if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Flanger.Delay = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALflanger_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_WAVEFORM:
|
||||
*val = props->Flanger.Waveform;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_PHASE:
|
||||
*val = props->Flanger.Phase;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALflanger_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_RATE:
|
||||
*val = props->Flanger.Rate;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DEPTH:
|
||||
*val = props->Flanger.Depth;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_FEEDBACK:
|
||||
*val = props->Flanger.Feedback;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DELAY:
|
||||
*val = props->Flanger.Delay;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALflanger_getParamf(effect, context, param, vals);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALflanger);
|
||||
@@ -24,28 +24,35 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
|
||||
#define MAX_UPDATE_SAMPLES 128
|
||||
|
||||
typedef struct ALmodulatorState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
void (*Process)(ALfloat*, const ALfloat*, ALsizei, const ALsizei, ALsizei);
|
||||
void (*GetSamples)(ALfloat*, ALsizei, const ALsizei, ALsizei);
|
||||
|
||||
ALsizei index;
|
||||
ALsizei step;
|
||||
|
||||
ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
|
||||
alignas(16) ALfloat ModSamples[MAX_UPDATE_SAMPLES];
|
||||
|
||||
ALfilterState Filter[MAX_EFFECT_CHANNELS];
|
||||
struct {
|
||||
BiquadState Filter;
|
||||
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
} Chans[MAX_EFFECT_CHANNELS];
|
||||
} ALmodulatorState;
|
||||
|
||||
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state);
|
||||
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevice *device);
|
||||
static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState)
|
||||
|
||||
@@ -72,15 +79,15 @@ static inline ALfloat Square(ALsizei index)
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(func) \
|
||||
static void Modulate##func(ALfloat *restrict dst, const ALfloat *restrict src,\
|
||||
ALsizei index, const ALsizei step, ALsizei todo) \
|
||||
static void Modulate##func(ALfloat *restrict dst, ALsizei index, \
|
||||
const ALsizei step, ALsizei todo) \
|
||||
{ \
|
||||
ALsizei i; \
|
||||
for(i = 0;i < todo;i++) \
|
||||
{ \
|
||||
index += step; \
|
||||
index &= WAVEFORM_FRACMASK; \
|
||||
dst[i] = src[i] * func(index); \
|
||||
dst[i] = func(index); \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -93,16 +100,11 @@ DECL_TEMPLATE(Square)
|
||||
|
||||
static void ALmodulatorState_Construct(ALmodulatorState *state)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALmodulatorState, ALeffectState, state);
|
||||
|
||||
state->index = 0;
|
||||
state->step = 1;
|
||||
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ALfilterState_clear(&state->Filter[i]);
|
||||
}
|
||||
|
||||
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state)
|
||||
@@ -110,91 +112,90 @@ static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state)
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), ALCdevice *UNUSED(device))
|
||||
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevice *UNUSED(device))
|
||||
{
|
||||
ALsizei i, j;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
{
|
||||
BiquadState_clear(&state->Chans[i].Filter);
|
||||
for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
|
||||
state->Chans[i].CurrentGains[j] = 0.0f;
|
||||
}
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat cw, a;
|
||||
ALsizei i;
|
||||
|
||||
if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
|
||||
state->Process = ModulateSin;
|
||||
state->GetSamples = ModulateSin;
|
||||
else if(props->Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
|
||||
state->Process = ModulateSaw;
|
||||
state->GetSamples = ModulateSaw;
|
||||
else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
|
||||
state->Process = ModulateSquare;
|
||||
state->GetSamples = ModulateSquare;
|
||||
|
||||
state->step = fastf2i(props->Modulator.Frequency*WAVEFORM_FRACONE /
|
||||
Device->Frequency);
|
||||
if(state->step == 0) state->step = 1;
|
||||
device->Frequency);
|
||||
state->step = clampi(state->step, 1, WAVEFORM_FRACONE-1);
|
||||
|
||||
/* Custom filter coeffs, which match the old version instead of a low-shelf. */
|
||||
cw = cosf(F_TAU * props->Modulator.HighPassCutoff / Device->Frequency);
|
||||
cw = cosf(F_TAU * props->Modulator.HighPassCutoff / device->Frequency);
|
||||
a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
|
||||
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
{
|
||||
state->Filter[i].b0 = a;
|
||||
state->Filter[i].b1 = -a;
|
||||
state->Filter[i].b2 = 0.0f;
|
||||
state->Filter[i].a1 = -a;
|
||||
state->Filter[i].a2 = 0.0f;
|
||||
}
|
||||
state->Chans[0].Filter.b0 = a;
|
||||
state->Chans[0].Filter.b1 = -a;
|
||||
state->Chans[0].Filter.b2 = 0.0f;
|
||||
state->Chans[0].Filter.a1 = -a;
|
||||
state->Chans[0].Filter.a2 = 0.0f;
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
BiquadState_copyParams(&state->Chans[i].Filter, &state->Chans[0].Filter);
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = Device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = Device->FOAOut.NumChannels;
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputeFirstOrderGains(Device->FOAOut, IdentityMatrixf.m[i],
|
||||
Slot->Params.Gain, state->Gain[i]);
|
||||
ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Chans[i].TargetGains);
|
||||
}
|
||||
|
||||
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALfloat *restrict modsamples = ASSUME_ALIGNED(state->ModSamples, 16);
|
||||
const ALsizei step = state->step;
|
||||
ALsizei index = state->index;
|
||||
ALsizei base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[2][128];
|
||||
ALsizei td = mini(128, SamplesToDo-base);
|
||||
ALsizei i, j, k;
|
||||
alignas(16) ALfloat temps[2][MAX_UPDATE_SAMPLES];
|
||||
ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base);
|
||||
ALsizei c, i;
|
||||
|
||||
for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
|
||||
state->GetSamples(modsamples, state->index, step, td);
|
||||
state->index += (step*td) & WAVEFORM_FRACMASK;
|
||||
state->index &= WAVEFORM_FRACMASK;
|
||||
|
||||
for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
|
||||
{
|
||||
ALfilterState_process(&state->Filter[j], temps[0], &SamplesIn[j][base], td);
|
||||
state->Process(temps[1], temps[0], index, step, td);
|
||||
BiquadState_process(&state->Chans[c].Filter, temps[0], &SamplesIn[c][base], td);
|
||||
for(i = 0;i < td;i++)
|
||||
temps[1][i] = temps[0][i] * modsamples[i];
|
||||
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
{
|
||||
ALfloat gain = state->Gain[j][k];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][base+i] += gain * temps[1][i];
|
||||
}
|
||||
MixSamples(temps[1], NumChannels, SamplesOut, state->Chans[c].CurrentGains,
|
||||
state->Chans[c].TargetGains, SamplesToDo-base, base, td);
|
||||
}
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
{
|
||||
index += step;
|
||||
index &= WAVEFORM_FRACMASK;
|
||||
}
|
||||
base += td;
|
||||
}
|
||||
state->index = index;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALmodulatorStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALmodulatorStateFactory;
|
||||
typedef struct ModulatorStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} ModulatorStateFactory;
|
||||
|
||||
static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UNUSED(factory))
|
||||
static ALeffectState *ModulatorStateFactory_create(ModulatorStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALmodulatorState *state;
|
||||
|
||||
@@ -204,13 +205,13 @@ static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UN
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALmodulatorStateFactory);
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(ModulatorStateFactory);
|
||||
|
||||
ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void)
|
||||
EffectStateFactory *ModulatorStateFactory_getFactory(void)
|
||||
{
|
||||
static ALmodulatorStateFactory ModulatorFactory = { { GET_VTABLE2(ALmodulatorStateFactory, ALeffectStateFactory) } };
|
||||
static ModulatorStateFactory ModulatorFactory = { { GET_VTABLE2(ModulatorStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &ModulatorFactory);
|
||||
return STATIC_CAST(EffectStateFactory, &ModulatorFactory);
|
||||
}
|
||||
|
||||
|
||||
@@ -221,24 +222,22 @@ void ALmodulator_setParamf(ALeffect *effect, ALCcontext *context, ALenum param,
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Modulator frequency out of range");
|
||||
props->Modulator.Frequency = val;
|
||||
break;
|
||||
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Modulator high-pass cutoff out of range");
|
||||
props->Modulator.HighPassCutoff = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALmodulator_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALmodulator_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
{ ALmodulator_setParamf(effect, context, param, vals[0]); }
|
||||
void ALmodulator_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
@@ -251,18 +250,16 @@ void ALmodulator_setParami(ALeffect *effect, ALCcontext *context, ALenum param,
|
||||
|
||||
case AL_RING_MODULATOR_WAVEFORM:
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid modulator waveform");
|
||||
props->Modulator.Waveform = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALmodulator_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALmodulator_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
{ ALmodulator_setParami(effect, context, param, vals[0]); }
|
||||
|
||||
void ALmodulator_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
@@ -280,13 +277,11 @@ void ALmodulator_getParami(const ALeffect *effect, ALCcontext *context, ALenum p
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALmodulator_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALmodulator_getParami(effect, context, param, vals);
|
||||
}
|
||||
{ ALmodulator_getParami(effect, context, param, vals); }
|
||||
void ALmodulator_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
@@ -300,12 +295,10 @@ void ALmodulator_getParamf(const ALeffect *effect, ALCcontext *context, ALenum p
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALmodulator_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALmodulator_getParamf(effect, context, param, vals);
|
||||
}
|
||||
{ ALmodulator_getParamf(effect, context, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALmodulator);
|
||||
|
||||
@@ -16,8 +16,8 @@ typedef struct ALnullState {
|
||||
/* Forward-declare "virtual" functions to define the vtable with. */
|
||||
static ALvoid ALnullState_Destruct(ALnullState *state);
|
||||
static ALboolean ALnullState_deviceUpdate(ALnullState *state, ALCdevice *device);
|
||||
static ALvoid ALnullState_update(ALnullState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALnullState_process(ALnullState *state, ALsizei samplesToDo, const ALfloatBUFFERSIZE*restrict samplesIn, ALfloatBUFFERSIZE*restrict samplesOut, ALsizei NumChannels);
|
||||
static ALvoid ALnullState_update(ALnullState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALnullState_process(ALnullState *state, ALsizei samplesToDo, const ALfloat (*restrict samplesIn)[BUFFERSIZE], ALfloat (*restrict samplesOut)[BUFFERSIZE], ALsizei mumChannels);
|
||||
static void *ALnullState_New(size_t size);
|
||||
static void ALnullState_Delete(void *ptr);
|
||||
|
||||
@@ -56,7 +56,7 @@ static ALboolean ALnullState_deviceUpdate(ALnullState* UNUSED(state), ALCdevice*
|
||||
/* This updates the effect state. This is called any time the effect is
|
||||
* (re)loaded into a slot.
|
||||
*/
|
||||
static ALvoid ALnullState_update(ALnullState* UNUSED(state), const ALCdevice* UNUSED(device), const ALeffectslot* UNUSED(slot), const ALeffectProps* UNUSED(props))
|
||||
static ALvoid ALnullState_update(ALnullState* UNUSED(state), const ALCcontext* UNUSED(context), const ALeffectslot* UNUSED(slot), const ALeffectProps* UNUSED(props))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ static ALvoid ALnullState_update(ALnullState* UNUSED(state), const ALCdevice* UN
|
||||
* input to the output buffer. The result should be added to the output buffer,
|
||||
* not replace it.
|
||||
*/
|
||||
static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALsizei UNUSED(samplesToDo), const ALfloatBUFFERSIZE*restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALsizei UNUSED(NumChannels))
|
||||
static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALsizei UNUSED(samplesToDo), const ALfloatBUFFERSIZE*restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALsizei UNUSED(numChannels))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -85,12 +85,12 @@ static void ALnullState_Delete(void *ptr)
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALnullStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALnullStateFactory;
|
||||
typedef struct NullStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} NullStateFactory;
|
||||
|
||||
/* Creates ALeffectState objects of the appropriate type. */
|
||||
ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory))
|
||||
ALeffectState *NullStateFactory_create(NullStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALnullState *state;
|
||||
|
||||
@@ -100,79 +100,79 @@ ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory))
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
/* Define the ALeffectStateFactory vtable for this type. */
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALnullStateFactory);
|
||||
/* Define the EffectStateFactory vtable for this type. */
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(NullStateFactory);
|
||||
|
||||
ALeffectStateFactory *ALnullStateFactory_getFactory(void)
|
||||
EffectStateFactory *NullStateFactory_getFactory(void)
|
||||
{
|
||||
static ALnullStateFactory NullFactory = { { GET_VTABLE2(ALnullStateFactory, ALeffectStateFactory) } };
|
||||
return STATIC_CAST(ALeffectStateFactory, &NullFactory);
|
||||
static NullStateFactory NullFactory = { { GET_VTABLE2(NullStateFactory, EffectStateFactory) } };
|
||||
return STATIC_CAST(EffectStateFactory, &NullFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALnull_setParami(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
void ALnull_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALnull_setParamiv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALint* UNUSED(vals))
|
||||
void ALnull_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint* UNUSED(vals))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer-vector property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALnull_setParamf(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
|
||||
void ALnull_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALnull_setParamfv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat* UNUSED(vals))
|
||||
void ALnull_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat* UNUSED(vals))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect float-vector property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
|
||||
void ALnull_getParami(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(val))
|
||||
void ALnull_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(val))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALnull_getParamiv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(vals))
|
||||
void ALnull_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(vals))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer-vector property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALnull_getParamf(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(val))
|
||||
void ALnull_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(val))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALnull_getParamfv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(vals))
|
||||
void ALnull_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(vals))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect float-vector property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,496 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2018 by Raul Herraiz.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
|
||||
#define STFT_SIZE 1024
|
||||
#define STFT_HALF_SIZE (STFT_SIZE>>1)
|
||||
#define OVERSAMP (1<<2)
|
||||
|
||||
#define STFT_STEP (STFT_SIZE / OVERSAMP)
|
||||
#define FIFO_LATENCY (STFT_STEP * (OVERSAMP-1))
|
||||
|
||||
typedef struct ALcomplex {
|
||||
ALfloat Real;
|
||||
ALfloat Imag;
|
||||
} ALcomplex;
|
||||
|
||||
typedef struct ALphasor {
|
||||
ALfloat Amplitude;
|
||||
ALfloat Phase;
|
||||
} ALphasor;
|
||||
|
||||
typedef struct ALFrequencyDomain {
|
||||
ALfloat Amplitude;
|
||||
ALfloat Frequency;
|
||||
} ALfrequencyDomain;
|
||||
|
||||
typedef struct ALpshifterState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect parameters */
|
||||
ALsizei count;
|
||||
ALfloat PitchShift;
|
||||
ALfloat FreqPerBin;
|
||||
|
||||
/*Effects buffers*/
|
||||
ALfloat InFIFO[STFT_SIZE];
|
||||
ALfloat OutFIFO[STFT_STEP];
|
||||
ALfloat LastPhase[STFT_HALF_SIZE+1];
|
||||
ALfloat SumPhase[STFT_HALF_SIZE+1];
|
||||
ALfloat OutputAccum[STFT_SIZE];
|
||||
|
||||
ALcomplex FFTbuffer[STFT_SIZE];
|
||||
|
||||
ALfrequencyDomain Analysis_buffer[STFT_HALF_SIZE+1];
|
||||
ALfrequencyDomain Syntesis_buffer[STFT_HALF_SIZE+1];
|
||||
|
||||
alignas(16) ALfloat BufferOut[BUFFERSIZE];
|
||||
|
||||
/* Effect gains for each output channel */
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
} ALpshifterState;
|
||||
|
||||
static ALvoid ALpshifterState_Destruct(ALpshifterState *state);
|
||||
static ALboolean ALpshifterState_deviceUpdate(ALpshifterState *state, ALCdevice *device);
|
||||
static ALvoid ALpshifterState_update(ALpshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALpshifterState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALpshifterState);
|
||||
|
||||
|
||||
/* Define a Hann window, used to filter the STFT input and output. */
|
||||
alignas(16) static ALfloat HannWindow[STFT_SIZE];
|
||||
|
||||
static void InitHannWindow(void)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
/* Create lookup table of the Hann window for the desired size, i.e. STFT_SIZE */
|
||||
for(i = 0;i < STFT_SIZE>>1;i++)
|
||||
{
|
||||
ALdouble val = sin(M_PI * (ALdouble)i / (ALdouble)(STFT_SIZE-1));
|
||||
HannWindow[i] = HannWindow[STFT_SIZE-(i+1)] = (ALfloat)(val * val);
|
||||
}
|
||||
}
|
||||
static alonce_flag HannInitOnce = AL_ONCE_FLAG_INIT;
|
||||
|
||||
|
||||
/* Converts ALcomplex to ALphasor */
|
||||
static inline ALphasor rect2polar(ALcomplex number)
|
||||
{
|
||||
ALphasor polar;
|
||||
|
||||
polar.Amplitude = sqrtf(number.Real*number.Real + number.Imag*number.Imag);
|
||||
polar.Phase = atan2f(number.Imag , number.Real);
|
||||
|
||||
return polar;
|
||||
}
|
||||
|
||||
/* Converts ALphasor to ALcomplex */
|
||||
static inline ALcomplex polar2rect(ALphasor number)
|
||||
{
|
||||
ALcomplex cartesian;
|
||||
|
||||
cartesian.Real = number.Amplitude * cosf(number.Phase);
|
||||
cartesian.Imag = number.Amplitude * sinf(number.Phase);
|
||||
|
||||
return cartesian;
|
||||
}
|
||||
|
||||
/* Addition of two complex numbers (ALcomplex format) */
|
||||
static inline ALcomplex complex_add(ALcomplex a, ALcomplex b)
|
||||
{
|
||||
ALcomplex result;
|
||||
|
||||
result.Real = a.Real + b.Real;
|
||||
result.Imag = a.Imag + b.Imag;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Subtraction of two complex numbers (ALcomplex format) */
|
||||
static inline ALcomplex complex_sub(ALcomplex a, ALcomplex b)
|
||||
{
|
||||
ALcomplex result;
|
||||
|
||||
result.Real = a.Real - b.Real;
|
||||
result.Imag = a.Imag - b.Imag;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Multiplication of two complex numbers (ALcomplex format) */
|
||||
static inline ALcomplex complex_mult(ALcomplex a, ALcomplex b)
|
||||
{
|
||||
ALcomplex result;
|
||||
|
||||
result.Real = a.Real*b.Real - a.Imag*b.Imag;
|
||||
result.Imag = a.Imag*b.Real + a.Real*b.Imag;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Iterative implementation of 2-radix FFT (In-place algorithm). Sign = -1 is
|
||||
* FFT and 1 is iFFT (inverse). Fills FFTBuffer[0...FFTSize-1] with the
|
||||
* Discrete Fourier Transform (DFT) of the time domain data stored in
|
||||
* FFTBuffer[0...FFTSize-1]. FFTBuffer is an array of complex numbers
|
||||
* (ALcomplex), FFTSize MUST BE power of two.
|
||||
*/
|
||||
static inline ALvoid FFT(ALcomplex *FFTBuffer, ALsizei FFTSize, ALfloat Sign)
|
||||
{
|
||||
ALsizei i, j, k, mask, step, step2;
|
||||
ALcomplex temp, u, w;
|
||||
ALfloat arg;
|
||||
|
||||
/* Bit-reversal permutation applied to a sequence of FFTSize items */
|
||||
for(i = 1;i < FFTSize-1;i++)
|
||||
{
|
||||
for(mask = 0x1, j = 0;mask < FFTSize;mask <<= 1)
|
||||
{
|
||||
if((i&mask) != 0)
|
||||
j++;
|
||||
j <<= 1;
|
||||
}
|
||||
j >>= 1;
|
||||
|
||||
if(i < j)
|
||||
{
|
||||
temp = FFTBuffer[i];
|
||||
FFTBuffer[i] = FFTBuffer[j];
|
||||
FFTBuffer[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterative form of Danielson–Lanczos lemma */
|
||||
for(i = 1, step = 2;i < FFTSize;i<<=1, step<<=1)
|
||||
{
|
||||
step2 = step >> 1;
|
||||
arg = F_PI / step2;
|
||||
|
||||
w.Real = cosf(arg);
|
||||
w.Imag = sinf(arg) * Sign;
|
||||
|
||||
u.Real = 1.0f;
|
||||
u.Imag = 0.0f;
|
||||
|
||||
for(j = 0;j < step2;j++)
|
||||
{
|
||||
for(k = j;k < FFTSize;k+=step)
|
||||
{
|
||||
temp = complex_mult(FFTBuffer[k+step2], u);
|
||||
FFTBuffer[k+step2] = complex_sub(FFTBuffer[k], temp);
|
||||
FFTBuffer[k] = complex_add(FFTBuffer[k], temp);
|
||||
}
|
||||
|
||||
u = complex_mult(u, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ALpshifterState_Construct(ALpshifterState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALpshifterState, ALeffectState, state);
|
||||
|
||||
alcall_once(&HannInitOnce, InitHannWindow);
|
||||
}
|
||||
|
||||
static ALvoid ALpshifterState_Destruct(ALpshifterState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALpshifterState_deviceUpdate(ALpshifterState *state, ALCdevice *device)
|
||||
{
|
||||
/* (Re-)initializing parameters and clear the buffers. */
|
||||
state->count = FIFO_LATENCY;
|
||||
state->PitchShift = 1.0f;
|
||||
state->FreqPerBin = device->Frequency / (ALfloat)STFT_SIZE;
|
||||
|
||||
memset(state->InFIFO, 0, sizeof(state->InFIFO));
|
||||
memset(state->OutFIFO, 0, sizeof(state->OutFIFO));
|
||||
memset(state->FFTbuffer, 0, sizeof(state->FFTbuffer));
|
||||
memset(state->LastPhase, 0, sizeof(state->LastPhase));
|
||||
memset(state->SumPhase, 0, sizeof(state->SumPhase));
|
||||
memset(state->OutputAccum, 0, sizeof(state->OutputAccum));
|
||||
memset(state->Analysis_buffer, 0, sizeof(state->Analysis_buffer));
|
||||
memset(state->Syntesis_buffer, 0, sizeof(state->Syntesis_buffer));
|
||||
|
||||
memset(state->CurrentGains, 0, sizeof(state->CurrentGains));
|
||||
memset(state->TargetGains, 0, sizeof(state->TargetGains));
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALpshifterState_update(ALpshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
|
||||
state->PitchShift = powf(2.0f,
|
||||
(ALfloat)(props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune) / 1200.0f
|
||||
);
|
||||
|
||||
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
|
||||
}
|
||||
|
||||
static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
/* Pitch shifter engine based on the work of Stephan Bernsee.
|
||||
* http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/
|
||||
*/
|
||||
|
||||
static const ALfloat expected = F_TAU / (ALfloat)OVERSAMP;
|
||||
const ALfloat freq_per_bin = state->FreqPerBin;
|
||||
ALfloat *restrict bufferOut = state->BufferOut;
|
||||
ALsizei count = state->count;
|
||||
ALsizei i, j, k;
|
||||
|
||||
for(i = 0;i < SamplesToDo;)
|
||||
{
|
||||
do {
|
||||
/* Fill FIFO buffer with samples data */
|
||||
state->InFIFO[count] = SamplesIn[0][i];
|
||||
bufferOut[i] = state->OutFIFO[count - FIFO_LATENCY];
|
||||
|
||||
count++;
|
||||
} while(++i < SamplesToDo && count < STFT_SIZE);
|
||||
|
||||
/* Check whether FIFO buffer is filled */
|
||||
if(count < STFT_SIZE) break;
|
||||
count = FIFO_LATENCY;
|
||||
|
||||
/* Real signal windowing and store in FFTbuffer */
|
||||
for(k = 0;k < STFT_SIZE;k++)
|
||||
{
|
||||
state->FFTbuffer[k].Real = state->InFIFO[k] * HannWindow[k];
|
||||
state->FFTbuffer[k].Imag = 0.0f;
|
||||
}
|
||||
|
||||
/* ANALYSIS */
|
||||
/* Apply FFT to FFTbuffer data */
|
||||
FFT(state->FFTbuffer, STFT_SIZE, -1.0f);
|
||||
|
||||
/* Analyze the obtained data. Since the real FFT is symmetric, only
|
||||
* STFT_HALF_SIZE+1 samples are needed.
|
||||
*/
|
||||
for(k = 0;k < STFT_HALF_SIZE+1;k++)
|
||||
{
|
||||
ALphasor component;
|
||||
ALfloat tmp;
|
||||
ALint qpd;
|
||||
|
||||
/* Compute amplitude and phase */
|
||||
component = rect2polar(state->FFTbuffer[k]);
|
||||
|
||||
/* Compute phase difference and subtract expected phase difference */
|
||||
tmp = (component.Phase - state->LastPhase[k]) - (ALfloat)k*expected;
|
||||
|
||||
/* Map delta phase into +/- Pi interval */
|
||||
qpd = fastf2i(tmp / F_PI);
|
||||
tmp -= F_PI * (ALfloat)(qpd + (qpd%2));
|
||||
|
||||
/* Get deviation from bin frequency from the +/- Pi interval */
|
||||
tmp /= expected;
|
||||
|
||||
/* Compute the k-th partials' true frequency, twice the amplitude
|
||||
* for maintain the gain (because half of bins are used) and store
|
||||
* amplitude and true frequency in analysis buffer.
|
||||
*/
|
||||
state->Analysis_buffer[k].Amplitude = 2.0f * component.Amplitude;
|
||||
state->Analysis_buffer[k].Frequency = ((ALfloat)k + tmp) * freq_per_bin;
|
||||
|
||||
/* Store actual phase[k] for the calculations in the next frame*/
|
||||
state->LastPhase[k] = component.Phase;
|
||||
}
|
||||
|
||||
/* PROCESSING */
|
||||
/* pitch shifting */
|
||||
for(k = 0;k < STFT_HALF_SIZE+1;k++)
|
||||
{
|
||||
state->Syntesis_buffer[k].Amplitude = 0.0f;
|
||||
state->Syntesis_buffer[k].Frequency = 0.0f;
|
||||
}
|
||||
|
||||
for(k = 0;k < STFT_HALF_SIZE+1;k++)
|
||||
{
|
||||
j = fastf2i((ALfloat)k * state->PitchShift);
|
||||
if(j >= STFT_HALF_SIZE+1) break;
|
||||
|
||||
state->Syntesis_buffer[j].Amplitude += state->Analysis_buffer[k].Amplitude;
|
||||
state->Syntesis_buffer[j].Frequency = state->Analysis_buffer[k].Frequency *
|
||||
state->PitchShift;
|
||||
}
|
||||
|
||||
/* SYNTHESIS */
|
||||
/* Synthesis the processing data */
|
||||
for(k = 0;k < STFT_HALF_SIZE+1;k++)
|
||||
{
|
||||
ALphasor component;
|
||||
ALfloat tmp;
|
||||
|
||||
/* Compute bin deviation from scaled freq */
|
||||
tmp = state->Syntesis_buffer[k].Frequency/freq_per_bin - (ALfloat)k;
|
||||
|
||||
/* Calculate actual delta phase and accumulate it to get bin phase */
|
||||
state->SumPhase[k] += ((ALfloat)k + tmp) * expected;
|
||||
|
||||
component.Amplitude = state->Syntesis_buffer[k].Amplitude;
|
||||
component.Phase = state->SumPhase[k];
|
||||
|
||||
/* Compute phasor component to cartesian complex number and storage it into FFTbuffer*/
|
||||
state->FFTbuffer[k] = polar2rect(component);
|
||||
}
|
||||
/* zero negative frequencies for recontruct a real signal */
|
||||
for(k = STFT_HALF_SIZE+1;k < STFT_SIZE;k++)
|
||||
{
|
||||
state->FFTbuffer[k].Real = 0.0f;
|
||||
state->FFTbuffer[k].Imag = 0.0f;
|
||||
}
|
||||
|
||||
/* Apply iFFT to buffer data */
|
||||
FFT(state->FFTbuffer, STFT_SIZE, 1.0f);
|
||||
|
||||
/* Windowing and add to output */
|
||||
for(k = 0;k < STFT_SIZE;k++)
|
||||
state->OutputAccum[k] += HannWindow[k] * state->FFTbuffer[k].Real /
|
||||
(0.5f * STFT_HALF_SIZE * OVERSAMP);
|
||||
|
||||
/* Shift accumulator, input & output FIFO */
|
||||
for(k = 0;k < STFT_STEP;k++) state->OutFIFO[k] = state->OutputAccum[k];
|
||||
for(j = 0;k < STFT_SIZE;k++,j++) state->OutputAccum[j] = state->OutputAccum[k];
|
||||
for(;j < STFT_SIZE;j++) state->OutputAccum[j] = 0.0f;
|
||||
for(k = 0;k < FIFO_LATENCY;k++)
|
||||
state->InFIFO[k] = state->InFIFO[k+STFT_STEP];
|
||||
}
|
||||
state->count = count;
|
||||
|
||||
/* Now, mix the processed sound data to the output. */
|
||||
MixSamples(bufferOut, NumChannels, SamplesOut, state->CurrentGains, state->TargetGains,
|
||||
maxi(SamplesToDo, 512), 0, SamplesToDo);
|
||||
}
|
||||
|
||||
typedef struct PshifterStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} PshifterStateFactory;
|
||||
|
||||
static ALeffectState *PshifterStateFactory_create(PshifterStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALpshifterState *state;
|
||||
|
||||
NEW_OBJ0(state, ALpshifterState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(PshifterStateFactory);
|
||||
|
||||
EffectStateFactory *PshifterStateFactory_getFactory(void)
|
||||
{
|
||||
static PshifterStateFactory PshifterFactory = { { GET_VTABLE2(PshifterStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(EffectStateFactory, &PshifterFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALpshifter_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
|
||||
{
|
||||
alSetError( context, AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param );
|
||||
}
|
||||
|
||||
void ALpshifter_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat *UNUSED(vals))
|
||||
{
|
||||
alSetError( context, AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x", param );
|
||||
}
|
||||
|
||||
void ALpshifter_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_PITCH_SHIFTER_COARSE_TUNE:
|
||||
if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,,"Pitch shifter coarse tune out of range");
|
||||
props->Pshifter.CoarseTune = val;
|
||||
break;
|
||||
|
||||
case AL_PITCH_SHIFTER_FINE_TUNE:
|
||||
if(!(val >= AL_PITCH_SHIFTER_MIN_FINE_TUNE && val <= AL_PITCH_SHIFTER_MAX_FINE_TUNE))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,,"Pitch shifter fine tune out of range");
|
||||
props->Pshifter.FineTune = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALpshifter_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALpshifter_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALpshifter_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_PITCH_SHIFTER_COARSE_TUNE:
|
||||
*val = (ALint)props->Pshifter.CoarseTune;
|
||||
break;
|
||||
case AL_PITCH_SHIFTER_FINE_TUNE:
|
||||
*val = (ALint)props->Pshifter.FineTune;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALpshifter_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALpshifter_getParami(effect, context, param, vals);
|
||||
}
|
||||
|
||||
void ALpshifter_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(val))
|
||||
{
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param);
|
||||
}
|
||||
|
||||
void ALpshifter_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(vals))
|
||||
{
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x", param);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALpshifter);
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user