Sweden-Number/libs/faudio/src/FAudioFX_reverb.c

1970 lines
49 KiB
C

/* FAudio - XAudio Reimplementation for FNA
*
* Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
*
*/
#include "FAudioFX.h"
#include "FAudio_internal.h"
/* #define DISABLE_SUBNORMALS */
#ifdef DISABLE_SUBNORMALS
#include <math.h> /* ONLY USE THIS FOR fpclassify/_fpclass! */
/* VS2010 doesn't define fpclassify (which is C99), so here it is. */
#if defined(_MSC_VER) && !defined(fpclassify)
#define IS_SUBNORMAL(a) (_fpclass(a) & (_FPCLASS_ND | _FPCLASS_PD))
#else
#define IS_SUBNORMAL(a) (fpclassify(a) == FP_SUBNORMAL)
#endif
#endif /* DISABLE_SUBNORMALS */
/* Utility Functions */
static inline float DbGainToFactor(float gain)
{
return (float) FAudio_pow(10, gain / 20.0f);
}
static inline uint32_t MsToSamples(float msec, int32_t sampleRate)
{
return (uint32_t) ((sampleRate * msec) / 1000.0f);
}
#ifndef DISABLE_SUBNORMALS
#define Undenormalize(a) ((a))
#else /* DISABLE_SUBNORMALS */
static inline float Undenormalize(float sample_in)
{
if (IS_SUBNORMAL(sample_in))
{
return 0.0f;
}
return sample_in;
}
#endif /* DISABLE_SUBNORMALS */
/* Component - Delay */
#define DSP_DELAY_MAX_DELAY_MS 300
typedef struct DspDelay
{
int32_t sampleRate;
uint32_t capacity; /* In samples */
uint32_t delay; /* In samples */
uint32_t read_idx;
uint32_t write_idx;
float *buffer;
} DspDelay;
static inline void DspDelay_Initialize(
DspDelay *filter,
int32_t sampleRate,
float delay_ms,
FAudioMallocFunc pMalloc
) {
FAudio_assert(delay_ms >= 0 && delay_ms <= DSP_DELAY_MAX_DELAY_MS);
filter->sampleRate = sampleRate;
filter->capacity = MsToSamples(DSP_DELAY_MAX_DELAY_MS, sampleRate);
filter->delay = MsToSamples(delay_ms, sampleRate);
filter->read_idx = 0;
filter->write_idx = filter->delay;
filter->buffer = (float*) pMalloc(filter->capacity * sizeof(float));
FAudio_zero(filter->buffer, filter->capacity * sizeof(float));
}
static inline void DspDelay_Change(DspDelay *filter, float delay_ms)
{
FAudio_assert(delay_ms >= 0 && delay_ms <= DSP_DELAY_MAX_DELAY_MS);
/* Length */
filter->delay = MsToSamples(delay_ms, filter->sampleRate);
filter->read_idx = (filter->write_idx - filter->delay + filter->capacity) % filter->capacity;
}
static inline float DspDelay_Read(DspDelay *filter)
{
float delay_out;
FAudio_assert(filter->read_idx < filter->capacity);
delay_out = filter->buffer[filter->read_idx];
filter->read_idx = (filter->read_idx + 1) % filter->capacity;
return delay_out;
}
static inline void DspDelay_Write(DspDelay *filter, float sample)
{
FAudio_assert(filter->write_idx < filter->capacity);
filter->buffer[filter->write_idx] = sample;
filter->write_idx = (filter->write_idx + 1) % filter->capacity;
}
static inline float DspDelay_Process(DspDelay *filter, float sample_in)
{
float delay_out = DspDelay_Read(filter);
DspDelay_Write(filter, sample_in);
return delay_out;
}
static inline float DspDelay_Tap(DspDelay *filter, uint32_t delay)
{
FAudio_assert(delay <= filter->delay);
return filter->buffer[(filter->write_idx - delay + filter->capacity) % filter->capacity];
}
static inline void DspDelay_Reset(DspDelay *filter)
{
filter->read_idx = 0;
filter->write_idx = filter->delay;
FAudio_zero(filter->buffer, filter->capacity * sizeof(float));
}
static inline void DspDelay_Destroy(DspDelay *filter, FAudioFreeFunc pFree)
{
pFree(filter->buffer);
}
static inline float DspComb_FeedbackFromRT60(DspDelay *delay, float rt60_ms)
{
float exponent = (
(-3.0f * delay->delay * 1000.0f) /
(delay->sampleRate * rt60_ms)
);
return (float) FAudio_pow(10.0f, exponent);
}
/* Component - Bi-Quad Filter */
typedef enum DspBiQuadType
{
DSP_BIQUAD_LOWSHELVING,
DSP_BIQUAD_HIGHSHELVING
} DspBiQuadType;
typedef struct DspBiQuad
{
int32_t sampleRate;
float a0, a1, a2;
float b1, b2;
float c0, d0;
float delay0, delay1;
} DspBiQuad;
static inline void DspBiQuad_Change(
DspBiQuad *filter,
DspBiQuadType type,
float frequency,
float q,
float gain
) {
const float TWOPI = 6.283185307179586476925286766559005;
float theta_c = (TWOPI * frequency) / (float) filter->sampleRate;
float mu = DbGainToFactor(gain);
float beta = (type == DSP_BIQUAD_LOWSHELVING) ?
4.0f / (1 + mu) :
(1 + mu) / 4.0f;
float delta = beta * (float) FAudio_tan(theta_c * 0.5f);
float gamma = (1 - delta) / (1 + delta);
if (type == DSP_BIQUAD_LOWSHELVING)
{
filter->a0 = (1 - gamma) * 0.5f;
filter->a1 = filter->a0;
}
else
{
filter->a0 = (1 + gamma) * 0.5f;
filter->a1 = -filter->a0;
}
filter->a2 = 0.0f;
filter->b1 = -gamma;
filter->b2 = 0.0f;
filter->c0 = mu - 1.0f;
filter->d0 = 1.0f;
}
static inline void DspBiQuad_Initialize(
DspBiQuad *filter,
int32_t sampleRate,
DspBiQuadType type,
float frequency, /* Corner frequency */
float q, /* Only used by low/high-pass filters */
float gain /* Only used by low/high-shelving filters */
) {
filter->sampleRate = sampleRate;
filter->delay0 = 0.0f;
filter->delay1 = 0.0f;
DspBiQuad_Change(filter, type, frequency, q, gain);
}
static inline float DspBiQuad_Process(DspBiQuad *filter, float sample_in)
{
/* Direct Form II Transposed:
* - Less delay registers than Direct Form I
* - More numerically stable than Direct Form II
*/
float result = (filter->a0 * sample_in) + filter->delay0;
filter->delay0 = (filter->a1 * sample_in) - (filter->b1 * result) + filter->delay1;
filter->delay1 = (filter->a2 * sample_in) - (filter->b2 * result);
return Undenormalize(
(result * filter->c0) +
(sample_in * filter->d0)
);
}
static inline void DspBiQuad_Reset(DspBiQuad *filter)
{
filter->delay0 = 0.0f;
filter->delay1 = 0.0f;
}
static inline void DspBiQuad_Destroy(DspBiQuad *filter)
{
}
/* Component - Comb Filter with Integrated Low/High Shelving Filters */
typedef struct DspCombShelving
{
DspDelay comb_delay;
float comb_feedback_gain;
DspBiQuad low_shelving;
DspBiQuad high_shelving;
} DspCombShelving;
static inline void DspCombShelving_Initialize(
DspCombShelving *filter,
int32_t sampleRate,
float delay_ms,
float rt60_ms,
float low_frequency,
float low_gain,
float high_frequency,
float high_gain,
FAudioMallocFunc pMalloc
) {
DspDelay_Initialize(&filter->comb_delay, sampleRate, delay_ms, pMalloc);
filter->comb_feedback_gain = DspComb_FeedbackFromRT60(
&filter->comb_delay,
rt60_ms
);
DspBiQuad_Initialize(
&filter->low_shelving,
sampleRate,
DSP_BIQUAD_LOWSHELVING,
low_frequency,
0.0f,
low_gain
);
DspBiQuad_Initialize(
&filter->high_shelving,
sampleRate,
DSP_BIQUAD_HIGHSHELVING,
high_frequency,
0.0f,
high_gain
);
}
static inline float DspCombShelving_Process(
DspCombShelving *filter,
float sample_in
) {
float delay_out, feedback, to_buf;
delay_out = DspDelay_Read(&filter->comb_delay);
/* Apply shelving filters */
feedback = DspBiQuad_Process(&filter->high_shelving, delay_out);
feedback = DspBiQuad_Process(&filter->low_shelving, feedback);
/* Apply comb filter */
to_buf = Undenormalize(sample_in + (filter->comb_feedback_gain * feedback));
DspDelay_Write(&filter->comb_delay, to_buf);
return delay_out;
}
static inline void DspCombShelving_Reset(DspCombShelving *filter)
{
DspDelay_Reset(&filter->comb_delay);
DspBiQuad_Reset(&filter->low_shelving);
DspBiQuad_Reset(&filter->high_shelving);
}
static inline void DspCombShelving_Destroy(
DspCombShelving *filter,
FAudioFreeFunc pFree
) {
DspDelay_Destroy(&filter->comb_delay, pFree);
DspBiQuad_Destroy(&filter->low_shelving);
DspBiQuad_Destroy(&filter->high_shelving);
}
/* Component - Delaying All-Pass Filter */
typedef struct DspAllPass
{
DspDelay delay;
float feedback_gain;
} DspAllPass;
static inline void DspAllPass_Initialize(
DspAllPass *filter,
int32_t sampleRate,
float delay_ms,
float gain,
FAudioMallocFunc pMalloc
) {
DspDelay_Initialize(&filter->delay, sampleRate, delay_ms, pMalloc);
filter->feedback_gain = gain;
}
static inline void DspAllPass_Change(DspAllPass *filter, float delay_ms, float gain)
{
DspDelay_Change(&filter->delay, delay_ms);
filter->feedback_gain = gain;
}
static inline float DspAllPass_Process(DspAllPass *filter, float sample_in)
{
float delay_out, to_buf;
delay_out = DspDelay_Read(&filter->delay);
to_buf = Undenormalize(sample_in + (filter->feedback_gain * delay_out));
DspDelay_Write(&filter->delay, to_buf);
return Undenormalize(delay_out - (filter->feedback_gain * to_buf));
}
static inline void DspAllPass_Reset(DspAllPass *filter)
{
DspDelay_Reset(&filter->delay);
}
static inline void DspAllPass_Destroy(DspAllPass *filter, FAudioFreeFunc pFree)
{
DspDelay_Destroy(&filter->delay, pFree);
}
/*
Reverb network - loosely based on the reverberator from
"Designing Audio Effect Plug-Ins in C++" by Will Pirkle and
the classic classic Schroeder-Moorer reverberator with modifications
to fit the XAudio2FX parameters.
In +--------+ +----+ +------------+ +-----+
----|--->PreDelay---->APF1---+--->Sub LeftCh |----->| | Left Out
| +--------+ +----+ | +------------+ | Wet |-------->
| | +------------+ | |
| |---|Sub RightCh |----->| Dry |
| +------------+ | | Right Out
| | Mix |-------->
+----------------------------------------------->| |
+-----+
Sub routine per channel :
In +-----+ +-----+ * cg
---+->|Delay|--+---|Comb1|------+
| +-----+ | +-----+ |
| | |
| | +-----+ * cg |
| +--->Comb2|------+
| | +-----+ | +-----+
| | +---->| SUM |--------+
| | +-----+ * cg | +-----+ |
| +--->... |------+ |
| * g0 | +-----+ | |
| | | |
| +--->-----+ * cg | |
| |Comb8|------+ |
| +-----+ |
v |
+-----+ g1 +----+ +----+ +----+ +----+ |
| SUM |<------|APF4|<--|APF3|<--|APF2|<--|APF1|<-----+
+-----+ +----+ +----+ +----+ +----+
|
|
| +-------------+ Out
+----------->|RoomFilter |------------------------>
+-------------+
Parameters:
float WetDryMix; 0 - 100 (0 = fully dry, 100 = fully wet)
uint32_t ReflectionsDelay; 0 - 300 ms
uint8_t ReverbDelay; 0 - 85 ms
uint8_t RearDelay; 0 - 5 ms
uint8_t PositionLeft; 0 - 30
uint8_t PositionRight; 0 - 30
uint8_t PositionMatrixLeft; 0 - 30
uint8_t PositionMatrixRight; 0 - 30
uint8_t EarlyDiffusion; 0 - 15
uint8_t LateDiffusion; 0 - 15
uint8_t LowEQGain; 0 - 12 (formula dB = LowEQGain - 8)
uint8_t LowEQCutoff; 0 - 9 (formula Hz = 50 + (LowEQCutoff * 50))
uint8_t HighEQGain; 0 - 8 (formula dB = HighEQGain - 8)
uint8_t HighEQCutoff; 0 - 14 (formula Hz = 1000 + (HighEqCutoff * 500))
float RoomFilterFreq; 20 - 20000Hz
float RoomFilterMain; -100 - 0dB
float RoomFilterHF; -100 - 0dB
float ReflectionsGain; -100 - 20dB
float ReverbGain; -100 - 20dB
float DecayTime; 0.1 - .... ms
float Density; 0 - 100 %
float RoomSize; 1 - 100 feet (NOT USED YET)
*/
#define REVERB_COUNT_COMB 8
#define REVERB_COUNT_APF_IN 1
#define REVERB_COUNT_APF_OUT 4
static float COMB_DELAYS[REVERB_COUNT_COMB] =
{
25.31f,
26.94f,
28.96f,
30.75f,
32.24f,
33.80f,
35.31f,
36.67f
};
static float APF_IN_DELAYS[REVERB_COUNT_APF_IN] =
{
13.28f,
/* 28.13f */
};
static float APF_OUT_DELAYS[REVERB_COUNT_APF_OUT] =
{
5.10f,
12.61f,
10.0f,
7.73f
};
typedef enum FAudio_ChannelPositionFlags
{
Position_Left = 0x1,
Position_Right = 0x2,
Position_Center = 0x4,
Position_Rear = 0x8,
} FAudio_ChannelPositionFlags;
static FAudio_ChannelPositionFlags FAudio_GetChannelPositionFlags(int32_t total_channels, int32_t channel)
{
switch (total_channels)
{
case 1:
return Position_Center;
case 2:
return (channel == 0) ? Position_Left : Position_Right;
case 4:
switch (channel)
{
case 0:
return Position_Left;
case 1:
return Position_Right;
case 2:
return Position_Left | Position_Rear;
case 3:
return Position_Right | Position_Rear;
}
case 5:
switch (channel)
{
case 0:
return Position_Left;
case 1:
return Position_Right;
case 2:
return Position_Center;
case 3:
return Position_Left | Position_Rear;
case 4:
return Position_Right | Position_Rear;
}
default:
FAudio_assert(0 && "Unsupported channel count");
break;
}
/* shouldn't happen, but default to left speaker */
return Position_Left;
}
float FAudio_GetStereoSpreadDelayMS(int32_t total_channels, int32_t channel)
{
FAudio_ChannelPositionFlags flags = FAudio_GetChannelPositionFlags(total_channels, channel);
return (flags & Position_Right) ? 0.5216f : 0.0f;
}
typedef struct DspReverbChannel
{
DspDelay reverb_delay;
DspCombShelving lpf_comb[REVERB_COUNT_COMB];
DspAllPass apf_out[REVERB_COUNT_APF_OUT];
DspBiQuad room_high_shelf;
float early_gain;
float gain;
} DspReverbChannel;
typedef struct DspReverb
{
DspDelay early_delay;
DspAllPass apf_in[REVERB_COUNT_APF_IN];
int32_t in_channels;
int32_t out_channels;
int32_t reverb_channels;
DspReverbChannel channel[5];
float early_gain;
float reverb_gain;
float room_gain;
float wet_ratio;
float dry_ratio;
} DspReverb;
static inline void DspReverb_Create(
DspReverb *reverb,
int32_t sampleRate,
int32_t in_channels,
int32_t out_channels,
FAudioMallocFunc pMalloc
) {
int32_t i, c;
FAudio_assert(in_channels == 1 || in_channels == 2 || in_channels == 6);
FAudio_assert(out_channels == 1 || out_channels == 2 || out_channels == 6);
FAudio_zero(reverb, sizeof(DspReverb));
DspDelay_Initialize(&reverb->early_delay, sampleRate, 10, pMalloc);
for (i = 0; i < REVERB_COUNT_APF_IN; i += 1)
{
DspAllPass_Initialize(
&reverb->apf_in[i],
sampleRate,
APF_IN_DELAYS[i],
0.5f,
pMalloc
);
}
if (out_channels == 6)
{
reverb->reverb_channels = (in_channels == 6) ? 5 : 4;
}
else
{
reverb->reverb_channels = out_channels;
}
for (c = 0; c < reverb->reverb_channels; c += 1)
{
DspDelay_Initialize(
&reverb->channel[c].reverb_delay,
sampleRate,
10,
pMalloc
);
for (i = 0; i < REVERB_COUNT_COMB; i += 1)
{
DspCombShelving_Initialize(
&reverb->channel[c].lpf_comb[i],
sampleRate,
COMB_DELAYS[i] + FAudio_GetStereoSpreadDelayMS(reverb->reverb_channels, c),
500,
500,
-6,
5000,
-6,
pMalloc
);
}
for (i = 0; i < REVERB_COUNT_APF_OUT; i += 1)
{
DspAllPass_Initialize(
&reverb->channel[c].apf_out[i],
sampleRate,
APF_OUT_DELAYS[i] + FAudio_GetStereoSpreadDelayMS(reverb->reverb_channels, c),
0.5f,
pMalloc
);
}
DspBiQuad_Initialize(
&reverb->channel[c].room_high_shelf,
sampleRate,
DSP_BIQUAD_HIGHSHELVING,
5000,
0,
-10
);
reverb->channel[c].gain = 1.0f;
}
reverb->early_gain = 1.0f;
reverb->reverb_gain = 1.0f;
reverb->dry_ratio = 0.0f;
reverb->wet_ratio = 1.0f;
reverb->in_channels = in_channels;
reverb->out_channels = out_channels;
}
static inline void DspReverb_Destroy(DspReverb *reverb, FAudioFreeFunc pFree)
{
int32_t i, c;
DspDelay_Destroy(&reverb->early_delay, pFree);
for (i = 0; i < REVERB_COUNT_APF_IN; i += 1)
{
DspAllPass_Destroy(&reverb->apf_in[i], pFree);
}
for (c = 0; c < reverb->reverb_channels; c += 1)
{
DspDelay_Destroy(&reverb->channel[c].reverb_delay, pFree);
for (i = 0; i < REVERB_COUNT_COMB; i += 1)
{
DspCombShelving_Destroy(
&reverb->channel[c].lpf_comb[i],
pFree
);
}
DspBiQuad_Destroy(&reverb->channel[c].room_high_shelf);
for (i = 0; i < REVERB_COUNT_APF_OUT; i += 1)
{
DspAllPass_Destroy(
&reverb->channel[c].apf_out[i],
pFree
);
}
}
}
static inline void DspReverb_SetParameters(
DspReverb *reverb,
FAudioFXReverbParameters *params
) {
float early_diffusion, late_diffusion;
DspCombShelving *comb;
int32_t i, c;
/* Pre-Delay */
DspDelay_Change(&reverb->early_delay, (float) params->ReflectionsDelay);
/* Early Reflections - Diffusion */
early_diffusion = 0.6f - ((params->EarlyDiffusion / 15.0f) * 0.2f);
for (i = 0; i < REVERB_COUNT_APF_IN; i += 1)
{
DspAllPass_Change(
&reverb->apf_in[i],
APF_IN_DELAYS[i],
early_diffusion
);
}
/* Reverberation */
for (c = 0; c < reverb->reverb_channels; c += 1)
{
float channel_delay =
(FAudio_GetChannelPositionFlags(reverb->reverb_channels, c) & Position_Rear) ?
params->RearDelay :
0.0f;
DspDelay_Change(
&reverb->channel[c].reverb_delay,
(float) params->ReverbDelay + channel_delay
);
for (i = 0; i < REVERB_COUNT_COMB; i += 1)
{
comb = &reverb->channel[c].lpf_comb[i];
/* Set decay time of comb filter */
DspDelay_Change(
&comb->comb_delay,
COMB_DELAYS[i] + FAudio_GetStereoSpreadDelayMS(reverb->reverb_channels, c)
);
comb->comb_feedback_gain = DspComb_FeedbackFromRT60(
&comb->comb_delay,
FAudio_max(params->DecayTime, FAUDIOFX_REVERB_MIN_DECAY_TIME) * 1000.0f
);
/* High/Low shelving */
DspBiQuad_Change(
&comb->low_shelving,
DSP_BIQUAD_LOWSHELVING,
50.0f + params->LowEQCutoff * 50.0f,
0.0f,
params->LowEQGain - 8.0f
);
DspBiQuad_Change(
&comb->high_shelving,
DSP_BIQUAD_HIGHSHELVING,
1000 + params->HighEQCutoff * 500.0f,
0.0f,
params->HighEQGain - 8.0f
);
}
}
/* Gain */
reverb->early_gain = DbGainToFactor(params->ReflectionsGain);
reverb->reverb_gain = DbGainToFactor(params->ReverbGain);
reverb->room_gain = DbGainToFactor(params->RoomFilterMain);
/* Late Diffusion */
late_diffusion = 0.6f - ((params->LateDiffusion / 15.0f) * 0.2f);
for (c = 0; c < reverb->reverb_channels; c += 1)
{
FAudio_ChannelPositionFlags position = FAudio_GetChannelPositionFlags(reverb->reverb_channels, c);
float gain;
for (i = 0; i < REVERB_COUNT_APF_OUT; i += 1)
{
DspAllPass_Change(
&reverb->channel[c].apf_out[i],
APF_OUT_DELAYS[i] + FAudio_GetStereoSpreadDelayMS(reverb->reverb_channels, c),
late_diffusion
);
}
DspBiQuad_Change(
&reverb->channel[c].room_high_shelf,
DSP_BIQUAD_HIGHSHELVING,
params->RoomFilterFreq,
0.0f,
params->RoomFilterMain + params->RoomFilterHF
);
if (position & Position_Left)
{
gain = params->PositionMatrixLeft;
}
else if (position & Position_Right)
{
gain = params->PositionMatrixRight;
}
else /*if (position & Position_Center) */
{
gain = (params->PositionMatrixLeft + params->PositionMatrixRight) / 2.0f;
}
reverb->channel[c].gain = 1.5f - (gain / 27.0f) * 0.5f;
if (position & Position_Rear)
{
/* Rear-channel Attenuation */
reverb->channel[c].gain *= 0.75f;
}
if (position & Position_Left)
{
gain = params->PositionLeft;
}
else if (position & Position_Right)
{
gain = params->PositionRight;
}
else /*if (position & Position_Center) */
{
gain = (params->PositionLeft + params->PositionRight) / 2.0f;
}
reverb->channel[c].early_gain = 1.2f - (gain / 6.0f) * 0.2f;
reverb->channel[c].early_gain = (
reverb->channel[c].early_gain *
reverb->early_gain
);
}
/* Wet/Dry Mix (100 = fully wet, 0 = fully dry) */
reverb->wet_ratio = params->WetDryMix / 100.0f;
reverb->dry_ratio = 1.0f - reverb->wet_ratio;
}
static inline void DspReverb_SetParameters9(
DspReverb *reverb,
FAudioFXReverbParameters9 *params
) {
FAudioFXReverbParameters oldParams;
oldParams.WetDryMix = params->WetDryMix;
oldParams.ReflectionsDelay = params->ReflectionsDelay;
oldParams.ReverbDelay = params->ReverbDelay;
oldParams.RearDelay = params->RearDelay;
oldParams.PositionLeft = params->PositionLeft;
oldParams.PositionRight = params->PositionRight;
oldParams.PositionMatrixLeft = params->PositionMatrixLeft;
oldParams.PositionMatrixRight = params->PositionMatrixRight;
oldParams.EarlyDiffusion = params->EarlyDiffusion;
oldParams.LateDiffusion = params->LateDiffusion;
oldParams.LowEQGain = params->LowEQGain;
oldParams.LowEQCutoff = params->LowEQCutoff;
oldParams.HighEQGain = params->HighEQGain;
oldParams.HighEQCutoff = params->HighEQCutoff;
oldParams.RoomFilterFreq = params->RoomFilterFreq;
oldParams.RoomFilterMain = params->RoomFilterMain;
oldParams.RoomFilterHF = params->RoomFilterHF;
oldParams.ReflectionsGain = params->ReflectionsGain;
oldParams.ReverbGain = params->ReverbGain;
oldParams.DecayTime = params->DecayTime;
oldParams.Density = params->Density;
oldParams.RoomSize = params->RoomSize;
DspReverb_SetParameters(reverb, &oldParams);
}
static inline float DspReverb_INTERNAL_ProcessEarly(
DspReverb *reverb,
float sample_in
) {
float early;
int32_t i;
/* Pre-Delay */
early = DspDelay_Process(&reverb->early_delay, sample_in);
/* Early Reflections */
for (i = 0; i < REVERB_COUNT_APF_IN; i += 1)
{
early = DspAllPass_Process(&reverb->apf_in[i], early);
}
return early;
}
static inline float DspReverb_INTERNAL_ProcessChannel(
DspReverb *reverb,
DspReverbChannel *channel,
float sample_in
) {
float revdelay, early_late, sample_out;
int32_t i;
revdelay = DspDelay_Process(&channel->reverb_delay, sample_in);
sample_out = 0.0f;
for (i = 0; i < REVERB_COUNT_COMB; i += 1)
{
sample_out += DspCombShelving_Process(
&channel->lpf_comb[i],
revdelay
);
}
sample_out /= (float) REVERB_COUNT_COMB;
/* Output Diffusion */
for (i = 0; i < REVERB_COUNT_APF_OUT; i += 1)
{
sample_out = DspAllPass_Process(
&channel->apf_out[i],
sample_out
);
}
/* Combine early reflections and reverberation */
early_late = (
(sample_in * channel->early_gain) +
(sample_out * reverb->reverb_gain)
);
/* Room filter */
sample_out = DspBiQuad_Process(
&channel->room_high_shelf,
early_late * reverb->room_gain
);
/* PositionMatrixLeft/Right */
return sample_out * channel->gain;
}
/* Reverb Process Functions */
static inline float DspReverb_INTERNAL_Process_1_to_1(
DspReverb *reverb,
float *restrict samples_in,
float *restrict samples_out,
size_t sample_count
) {
const float *in_end = samples_in + sample_count;
float in, early, late, out;
float squared_sum = 0.0f;
while (samples_in < in_end)
{
/* Input */
in = *samples_in++;
/* Early Reflections */
early = DspReverb_INTERNAL_ProcessEarly(reverb, in);
/* Reverberation */
late = DspReverb_INTERNAL_ProcessChannel(
reverb,
&reverb->channel[0],
early
);
/* Wet/Dry Mix */
out = (late * reverb->wet_ratio) + (in * reverb->dry_ratio);
squared_sum += out * out;
/* Output */
*samples_out++ = out;
}
return squared_sum;
}
static inline float DspReverb_INTERNAL_Process_1_to_5p1(
DspReverb *reverb,
float *restrict samples_in,
float *restrict samples_out,
size_t sample_count
) {
const float *in_end = samples_in + sample_count;
float in, in_ratio, early, late[4];
float squared_sum = 0.0f;
int32_t c;
while (samples_in < in_end)
{
/* Input */
in = *samples_in++;
in_ratio = in * reverb->dry_ratio;
/* Early Reflections */
early = DspReverb_INTERNAL_ProcessEarly(reverb, in);
/* Reverberation with Wet/Dry Mix */
for (c = 0; c < 4; c += 1)
{
late[c] = (DspReverb_INTERNAL_ProcessChannel(
reverb,
&reverb->channel[c],
early
) * reverb->wet_ratio) + in_ratio;
squared_sum += late[c] * late[c];
}
/* Output */
*samples_out++ = late[0]; /* Front Left */
*samples_out++ = late[1]; /* Front Right */
*samples_out++ = 0.0f; /* Center */
*samples_out++ = 0.0f; /* LFE */
*samples_out++ = late[2]; /* Rear Left */
*samples_out++ = late[3]; /* Rear Right */
}
return squared_sum;
}
static inline float DspReverb_INTERNAL_Process_2_to_2(
DspReverb *reverb,
float *restrict samples_in,
float *restrict samples_out,
size_t sample_count
) {
const float *in_end = samples_in + sample_count;
float in, in_ratio, early, late[2];
float squared_sum = 0;
while (samples_in < in_end)
{
/* Input - Combine 2 channels into 1 */
in = (samples_in[0] + samples_in[1]) / 2.0f;
in_ratio = in * reverb->dry_ratio;
samples_in += 2;
/* Early Reflections */
early = DspReverb_INTERNAL_ProcessEarly(reverb, in);
/* Reverberation with Wet/Dry Mix */
late[0] = DspReverb_INTERNAL_ProcessChannel(
reverb,
&reverb->channel[0],
early
);
late[1] = (DspReverb_INTERNAL_ProcessChannel(
reverb,
&reverb->channel[1],
early
) * reverb->wet_ratio) + in_ratio;
squared_sum += (late[0] * late[0]) + (late[1] * late[1]);
/* Output */
*samples_out++ = late[0];
*samples_out++ = late[1];
}
return squared_sum;
}
static inline float DspReverb_INTERNAL_Process_2_to_5p1(
DspReverb *reverb,
float *restrict samples_in,
float *restrict samples_out,
size_t sample_count
) {
const float *in_end = samples_in + sample_count;
float in, in_ratio, early, late[4];
float squared_sum = 0;
int32_t c;
while (samples_in < in_end)
{
/* Input - Combine 2 channels into 1 */
in = (samples_in[0] + samples_in[1]) / 2.0f;
in_ratio = in * reverb->dry_ratio;
samples_in += 2;
/* Early Reflections */
early = DspReverb_INTERNAL_ProcessEarly(reverb, in);
/* Reverberation with Wet/Dry Mix */
for (c = 0; c < 4; c += 1)
{
late[c] = (DspReverb_INTERNAL_ProcessChannel(
reverb,
&reverb->channel[c],
early
) * reverb->wet_ratio) + in_ratio;
squared_sum += late[c] * late[c];
}
/* Output */
*samples_out++ = late[0]; /* Front Left */
*samples_out++ = late[1]; /* Front Right */
*samples_out++ = 0.0f; /* Center */
*samples_out++ = 0.0f; /* LFE */
*samples_out++ = late[2]; /* Rear Left */
*samples_out++ = late[3]; /* Rear Right */
}
return squared_sum;
}
static inline float DspReverb_INTERNAL_Process_5p1_to_5p1(
DspReverb *reverb,
float *restrict samples_in,
float *restrict samples_out,
size_t sample_count
) {
const float *in_end = samples_in + sample_count;
float in, in_ratio, early, late[5];
float squared_sum = 0;
int32_t c;
while (samples_in < in_end)
{
/* Input - Combine non-LFE channels into 1 */
in = (samples_in[0] + samples_in[1] + samples_in[2] +
samples_in[4] + samples_in[5]) / 5.0f;
in_ratio = in * reverb->dry_ratio;
/* Early Reflections */
early = DspReverb_INTERNAL_ProcessEarly(reverb, in);
/* Reverberation with Wet/Dry Mix */
for (c = 0; c < 5; c += 1)
{
late[c] = (DspReverb_INTERNAL_ProcessChannel(
reverb,
&reverb->channel[c],
early
) * reverb->wet_ratio) + in_ratio;
squared_sum += late[c] * late[c];
}
/* Output */
*samples_out++ = late[0]; /* Front Left */
*samples_out++ = late[1]; /* Front Right */
*samples_out++ = late[2]; /* Center */
*samples_out++ = samples_in[3]; /* LFE, pass through */
*samples_out++ = late[3]; /* Rear Left */
*samples_out++ = late[4]; /* Rear Right */
samples_in += 6;
}
return squared_sum;
}
#undef OUTPUT_SAMPLE
/* Reverb FAPO Implementation */
const FAudioGUID FAudioFX_CLSID_AudioReverb = /* 2.7 */
{
0x6A93130E,
0xCB4E,
0x4CE1,
{
0xA9,
0xCF,
0xE7,
0x58,
0x80,
0x0B,
0xB1,
0x79
}
};
static FAPORegistrationProperties ReverbProperties =
{
/* .clsid = */ {0},
/*.FriendlyName = */
{
'R', 'e', 'v', 'e', 'r', 'b', '\0'
},
/*.CopyrightInfo = */ {
'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')',
'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0'
},
/*.MajorVersion = */ 0,
/*.MinorVersion = */ 0,
/*.Flags = */ (
FAPO_FLAG_FRAMERATE_MUST_MATCH |
FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH |
FAPO_FLAG_BUFFERCOUNT_MUST_MATCH |
FAPO_FLAG_INPLACE_SUPPORTED
),
/*.MinInputBufferCount = */ 1,
/*.MaxInputBufferCount = */ 1,
/*.MinOutputBufferCount = */ 1,
/*.MaxOutputBufferCount = */ 1
};
typedef struct FAudioFXReverb
{
FAPOBase base;
uint16_t inChannels;
uint16_t outChannels;
uint32_t sampleRate;
uint16_t inBlockAlign;
uint16_t outBlockAlign;
uint8_t apiVersion;
DspReverb reverb;
} FAudioFXReverb;
static inline int8_t IsFloatFormat(const FAudioWaveFormatEx *format)
{
if (format->wFormatTag == FAUDIO_FORMAT_IEEE_FLOAT)
{
/* Plain ol' WaveFormatEx */
return 1;
}
if (format->wFormatTag == FAUDIO_FORMAT_EXTENSIBLE)
{
/* WaveFormatExtensible, match GUID */
#define MAKE_SUBFORMAT_GUID(guid, fmt) \
static FAudioGUID KSDATAFORMAT_SUBTYPE_##guid = \
{ \
(uint16_t) (fmt), 0x0000, 0x0010, \
{ \
0x80, 0x00, 0x00, 0xaa, \
0x00, 0x38, 0x9b, 0x71 \
} \
}
MAKE_SUBFORMAT_GUID(IEEE_FLOAT, 3);
#undef MAKE_SUBFORMAT_GUID
if (FAudio_memcmp(
&((FAudioWaveFormatExtensible*) format)->SubFormat,
&KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
sizeof(FAudioGUID)
) == 0) {
return 1;
}
}
return 0;
}
uint32_t FAudioFXReverb_IsInputFormatSupported(
FAPOBase *fapo,
const FAudioWaveFormatEx *pOutputFormat,
const FAudioWaveFormatEx *pRequestedInputFormat,
FAudioWaveFormatEx **ppSupportedInputFormat
) {
uint32_t result = 0;
#define SET_SUPPORTED_FIELD(field, value) \
result = 1; \
if (ppSupportedInputFormat && *ppSupportedInputFormat) \
{ \
(*ppSupportedInputFormat)->field = (value); \
}
/* Sample Rate */
if (pOutputFormat->nSamplesPerSec != pRequestedInputFormat->nSamplesPerSec)
{
SET_SUPPORTED_FIELD(nSamplesPerSec, pOutputFormat->nSamplesPerSec);
}
/* Data Type */
if (!IsFloatFormat(pRequestedInputFormat))
{
SET_SUPPORTED_FIELD(wFormatTag, FAUDIO_FORMAT_IEEE_FLOAT);
}
/* Input/Output Channel Count */
if (pOutputFormat->nChannels == 1 || pOutputFormat->nChannels == 2)
{
if (pRequestedInputFormat->nChannels != pOutputFormat->nChannels)
{
SET_SUPPORTED_FIELD(nChannels, pOutputFormat->nChannels);
}
}
else if (pOutputFormat->nChannels == 6)
{
if ( pRequestedInputFormat->nChannels != 1 &&
pRequestedInputFormat->nChannels != 2 &&
pRequestedInputFormat->nChannels != 6 )
{
SET_SUPPORTED_FIELD(nChannels, 1);
}
}
else
{
SET_SUPPORTED_FIELD(nChannels, 1);
}
#undef SET_SUPPORTED_FIELD
return result;
}
uint32_t FAudioFXReverb_IsOutputFormatSupported(
FAPOBase *fapo,
const FAudioWaveFormatEx *pInputFormat,
const FAudioWaveFormatEx *pRequestedOutputFormat,
FAudioWaveFormatEx **ppSupportedOutputFormat
) {
uint32_t result = 0;
#define SET_SUPPORTED_FIELD(field, value) \
result = 1; \
if (ppSupportedOutputFormat && *ppSupportedOutputFormat) \
{ \
(*ppSupportedOutputFormat)->field = (value); \
}
/* Sample Rate */
if (pInputFormat->nSamplesPerSec != pRequestedOutputFormat->nSamplesPerSec)
{
SET_SUPPORTED_FIELD(nSamplesPerSec, pInputFormat->nSamplesPerSec);
}
/* Data Type */
if (!IsFloatFormat(pRequestedOutputFormat))
{
SET_SUPPORTED_FIELD(wFormatTag, FAUDIO_FORMAT_IEEE_FLOAT);
}
/* Input/Output Channel Count */
if (pInputFormat->nChannels == 1 || pInputFormat->nChannels == 2)
{
if ( pRequestedOutputFormat->nChannels != pInputFormat->nChannels &&
pRequestedOutputFormat->nChannels != 6)
{
SET_SUPPORTED_FIELD(nChannels, pInputFormat->nChannels);
}
}
else if (pInputFormat->nChannels == 6)
{
if (pRequestedOutputFormat->nChannels != 6)
{
SET_SUPPORTED_FIELD(nChannels, pInputFormat->nChannels);
}
}
else
{
SET_SUPPORTED_FIELD(nChannels, 1);
}
#undef SET_SUPPORTED_FIELD
return result;
}
uint32_t FAudioFXReverb_Initialize(
FAudioFXReverb *fapo,
const void* pData,
uint32_t DataByteSize
) {
#define INITPARAMS(offset) \
FAudio_memcpy( \
fapo->base.m_pParameterBlocks + DataByteSize * offset, \
pData, \
DataByteSize \
);
INITPARAMS(0)
INITPARAMS(1)
INITPARAMS(2)
#undef INITPARAMS
return 0;
}
uint32_t FAudioFXReverb_LockForProcess(
FAudioFXReverb *fapo,
uint32_t InputLockedParameterCount,
const FAPOLockForProcessBufferParameters *pInputLockedParameters,
uint32_t OutputLockedParameterCount,
const FAPOLockForProcessBufferParameters *pOutputLockedParameters
) {
/* Reverb specific validation */
if (!IsFloatFormat(pInputLockedParameters->pFormat))
{
return FAPO_E_FORMAT_UNSUPPORTED;
}
if ( pInputLockedParameters->pFormat->nSamplesPerSec < FAUDIOFX_REVERB_MIN_FRAMERATE ||
pInputLockedParameters->pFormat->nSamplesPerSec > FAUDIOFX_REVERB_MAX_FRAMERATE )
{
return FAPO_E_FORMAT_UNSUPPORTED;
}
if (!( (pInputLockedParameters->pFormat->nChannels == 1 &&
(pOutputLockedParameters->pFormat->nChannels == 1 ||
pOutputLockedParameters->pFormat->nChannels == 6)) ||
(pInputLockedParameters->pFormat->nChannels == 2 &&
(pOutputLockedParameters->pFormat->nChannels == 2 ||
pOutputLockedParameters->pFormat->nChannels == 6)) ||
(pInputLockedParameters->pFormat->nChannels == 6 &&
pOutputLockedParameters->pFormat->nChannels == 6)))
{
return FAPO_E_FORMAT_UNSUPPORTED;
}
/* Save the things we care about */
fapo->inChannels = pInputLockedParameters->pFormat->nChannels;
fapo->outChannels = pOutputLockedParameters->pFormat->nChannels;
fapo->sampleRate = pOutputLockedParameters->pFormat->nSamplesPerSec;
fapo->inBlockAlign = pInputLockedParameters->pFormat->nBlockAlign;
fapo->outBlockAlign = pOutputLockedParameters->pFormat->nBlockAlign;
/* Create the network */
DspReverb_Create(
&fapo->reverb,
fapo->sampleRate,
fapo->inChannels,
fapo->outChannels,
fapo->base.pMalloc
);
/* Call parent to do basic validation */
return FAPOBase_LockForProcess(
&fapo->base,
InputLockedParameterCount,
pInputLockedParameters,
OutputLockedParameterCount,
pOutputLockedParameters
);
}
static inline void FAudioFXReverb_CopyBuffer(
FAudioFXReverb *fapo,
float *restrict buffer_in,
float *restrict buffer_out,
size_t frames_in
) {
/* In-place processing? */
if (buffer_in == buffer_out)
{
return;
}
/* equal channel count */
if (fapo->inBlockAlign == fapo->outBlockAlign)
{
FAudio_memcpy(
buffer_out,
buffer_in,
fapo->inBlockAlign * frames_in
);
return;
}
/* 1 -> 5.1 */
if (fapo->inChannels == 1 && fapo->outChannels == 6)
{
const float *in_end = buffer_in + frames_in;
while (buffer_in < in_end)
{
*buffer_out++ = *buffer_in;
*buffer_out++ = *buffer_in++;
*buffer_out++ = 0.0f;
*buffer_out++ = 0.0f;
*buffer_out++ = 0.0f;
*buffer_out++ = 0.0f;
}
return;
}
/* 2 -> 5.1 */
if (fapo->inChannels == 2 && fapo->outChannels == 6)
{
const float *in_end = buffer_in + (frames_in * 2);
while (buffer_in < in_end)
{
*buffer_out++ = *buffer_in++;
*buffer_out++ = *buffer_in++;
*buffer_out++ = 0.0f;
*buffer_out++ = 0.0f;
*buffer_out++ = 0.0f;
*buffer_out++ = 0.0f;
}
return;
}
FAudio_assert(0 && "Unsupported channel combination");
FAudio_zero(buffer_out, fapo->outBlockAlign * frames_in);
}
void FAudioFXReverb_Process(
FAudioFXReverb *fapo,
uint32_t InputProcessParameterCount,
const FAPOProcessBufferParameters* pInputProcessParameters,
uint32_t OutputProcessParameterCount,
FAPOProcessBufferParameters* pOutputProcessParameters,
int32_t IsEnabled
) {
FAudioFXReverbParameters *params;
uint8_t update_params = FAPOBase_ParametersChanged(&fapo->base);
float total;
/* Handle disabled filter */
if (IsEnabled == 0)
{
pOutputProcessParameters->BufferFlags = pInputProcessParameters->BufferFlags;
if (pOutputProcessParameters->BufferFlags != FAPO_BUFFER_SILENT)
{
FAudioFXReverb_CopyBuffer(
fapo,
(float*) pInputProcessParameters->pBuffer,
(float*) pOutputProcessParameters->pBuffer,
pInputProcessParameters->ValidFrameCount
);
}
return;
}
/* XAudio2 passes a 'silent' buffer when no input buffer is available to play the effect tail */
if (pInputProcessParameters->BufferFlags == FAPO_BUFFER_SILENT)
{
/* Make sure input data is usable. FIXME: Is this required? */
FAudio_zero(
pInputProcessParameters->pBuffer,
pInputProcessParameters->ValidFrameCount * fapo->inBlockAlign
);
}
params = (FAudioFXReverbParameters*) FAPOBase_BeginProcess(&fapo->base);
/* Update parameters */
if (update_params)
{
if (fapo->apiVersion == 9)
{
DspReverb_SetParameters9(
&fapo->reverb,
(FAudioFXReverbParameters9*) params
);
}
else
{
DspReverb_SetParameters(&fapo->reverb, params);
}
}
/* Run reverb effect */
#define PROCESS(pin, pout) \
DspReverb_INTERNAL_Process_##pin##_to_##pout( \
&fapo->reverb, \
(float*) pInputProcessParameters->pBuffer, \
(float*) pOutputProcessParameters->pBuffer, \
pInputProcessParameters->ValidFrameCount * fapo->inChannels \
)
switch (fapo->reverb.out_channels)
{
case 1:
total = PROCESS(1, 1);
break;
case 2:
total = PROCESS(2, 2);
break;
default: /* 5.1 */
if (fapo->reverb.in_channels == 1)
{
total = PROCESS(1, 5p1);
}
else if (fapo->reverb.in_channels == 2)
{
total = PROCESS(2, 5p1);
}
else /* 5.1 */
{
total = PROCESS(5p1, 5p1);
}
break;
}
#undef PROCESS
/* Set BufferFlags to silent so PLAY_TAILS knows when to stop */
pOutputProcessParameters->BufferFlags = (total < 0.0000001f) ?
FAPO_BUFFER_SILENT :
FAPO_BUFFER_VALID;
FAPOBase_EndProcess(&fapo->base);
}
void FAudioFXReverb_Reset(FAudioFXReverb *fapo)
{
int32_t i, c;
FAPOBase_Reset(&fapo->base);
/* Reset the cached state of the reverb filter */
DspDelay_Reset(&fapo->reverb.early_delay);
for (i = 0; i < REVERB_COUNT_APF_IN; i += 1)
{
DspAllPass_Reset(&fapo->reverb.apf_in[i]);
}
for (c = 0; c < fapo->reverb.reverb_channels; c += 1)
{
DspDelay_Reset(&fapo->reverb.channel[c].reverb_delay);
for (i = 0; i < REVERB_COUNT_COMB; i += 1)
{
DspCombShelving_Reset(&fapo->reverb.channel[c].lpf_comb[i]);
}
DspBiQuad_Reset(&fapo->reverb.channel[c].room_high_shelf);
for (i = 0; i < REVERB_COUNT_APF_OUT; i += 1)
{
DspAllPass_Reset(&fapo->reverb.channel[c].apf_out[i]);
}
}
}
void FAudioFXReverb_Free(void* fapo)
{
FAudioFXReverb *reverb = (FAudioFXReverb*) fapo;
DspReverb_Destroy(&reverb->reverb, reverb->base.pFree);
reverb->base.pFree(reverb->base.m_pParameterBlocks);
reverb->base.pFree(fapo);
}
/* Public API (Version 7) */
uint32_t FAudioCreateReverb(FAPO** ppApo, uint32_t Flags)
{
return FAudioCreateReverbWithCustomAllocatorEXT(
ppApo,
Flags,
FAudio_malloc,
FAudio_free,
FAudio_realloc
);
}
uint32_t FAudioCreateReverbWithCustomAllocatorEXT(
FAPO** ppApo,
uint32_t Flags,
FAudioMallocFunc customMalloc,
FAudioFreeFunc customFree,
FAudioReallocFunc customRealloc
) {
const FAudioFXReverbParameters fxdefault =
{
FAUDIOFX_REVERB_DEFAULT_WET_DRY_MIX,
FAUDIOFX_REVERB_DEFAULT_REFLECTIONS_DELAY,
FAUDIOFX_REVERB_DEFAULT_REVERB_DELAY,
FAUDIOFX_REVERB_DEFAULT_REAR_DELAY,
FAUDIOFX_REVERB_DEFAULT_POSITION,
FAUDIOFX_REVERB_DEFAULT_POSITION,
FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX,
FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX,
FAUDIOFX_REVERB_DEFAULT_EARLY_DIFFUSION,
FAUDIOFX_REVERB_DEFAULT_LATE_DIFFUSION,
FAUDIOFX_REVERB_DEFAULT_LOW_EQ_GAIN,
FAUDIOFX_REVERB_DEFAULT_LOW_EQ_CUTOFF,
FAUDIOFX_REVERB_DEFAULT_HIGH_EQ_GAIN,
FAUDIOFX_REVERB_DEFAULT_HIGH_EQ_CUTOFF,
FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_FREQ,
FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_MAIN,
FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_HF,
FAUDIOFX_REVERB_DEFAULT_REFLECTIONS_GAIN,
FAUDIOFX_REVERB_DEFAULT_REVERB_GAIN,
FAUDIOFX_REVERB_DEFAULT_DECAY_TIME,
FAUDIOFX_REVERB_DEFAULT_DENSITY,
FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE
};
/* Allocate... */
FAudioFXReverb *result = (FAudioFXReverb*) customMalloc(sizeof(FAudioFXReverb));
uint8_t *params = (uint8_t*) customMalloc(
sizeof(FAudioFXReverbParameters) * 3
);
result->apiVersion = 7;
#define INITPARAMS(offset) \
FAudio_memcpy( \
params + sizeof(FAudioFXReverbParameters) * offset, \
&fxdefault, \
sizeof(FAudioFXReverbParameters) \
);
INITPARAMS(0)
INITPARAMS(1)
INITPARAMS(2)
#undef INITPARAMS
/* Initialize... */
FAudio_memcpy(
&ReverbProperties.clsid,
&FAudioFX_CLSID_AudioReverb,
sizeof(FAudioGUID)
);
CreateFAPOBaseWithCustomAllocatorEXT(
&result->base,
&ReverbProperties,
params,
sizeof(FAudioFXReverbParameters),
0,
customMalloc,
customFree,
customRealloc
);
result->inChannels = 0;
result->outChannels = 0;
result->sampleRate = 0;
FAudio_zero(&result->reverb, sizeof(DspReverb));
/* Function table... */
#define ASSIGN_VT(name) \
result->base.base.name = (name##Func) FAudioFXReverb_##name;
ASSIGN_VT(LockForProcess);
ASSIGN_VT(IsInputFormatSupported);
ASSIGN_VT(IsOutputFormatSupported);
ASSIGN_VT(Initialize);
ASSIGN_VT(Reset);
ASSIGN_VT(Process);
result->base.Destructor = FAudioFXReverb_Free;
#undef ASSIGN_VT
/* Finally. */
*ppApo = &result->base.base;
return 0;
}
void ReverbConvertI3DL2ToNative(
const FAudioFXReverbI3DL2Parameters *pI3DL2,
FAudioFXReverbParameters *pNative
) {
float reflectionsDelay;
float reverbDelay;
pNative->RearDelay = FAUDIOFX_REVERB_DEFAULT_REAR_DELAY;
pNative->PositionLeft = FAUDIOFX_REVERB_DEFAULT_POSITION;
pNative->PositionRight = FAUDIOFX_REVERB_DEFAULT_POSITION;
pNative->PositionMatrixLeft = FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
pNative->PositionMatrixRight = FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
pNative->RoomSize = FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE;
pNative->LowEQCutoff = 4;
pNative->HighEQCutoff = 6;
pNative->RoomFilterMain = (float) pI3DL2->Room / 100.0f;
pNative->RoomFilterHF = (float) pI3DL2->RoomHF / 100.0f;
if (pI3DL2->DecayHFRatio >= 1.0f)
{
int32_t index = (int32_t) (-4.0 * FAudio_log10(pI3DL2->DecayHFRatio));
if (index < -8)
{
index = -8;
}
pNative->LowEQGain = (uint8_t) ((index < 0) ? index + 8 : 8);
pNative->HighEQGain = 8;
pNative->DecayTime = pI3DL2->DecayTime * pI3DL2->DecayHFRatio;
}
else
{
int32_t index = (int32_t) (4.0 * FAudio_log10(pI3DL2->DecayHFRatio));
if (index < -8)
{
index = -8;
}
pNative->LowEQGain = 8;
pNative->HighEQGain = (uint8_t) ((index < 0) ? index + 8 : 8);
pNative->DecayTime = pI3DL2->DecayTime;
}
reflectionsDelay = pI3DL2->ReflectionsDelay * 1000.0f;
if (reflectionsDelay >= FAUDIOFX_REVERB_MAX_REFLECTIONS_DELAY)
{
reflectionsDelay = (float) (FAUDIOFX_REVERB_MAX_REFLECTIONS_DELAY - 1);
}
else if (reflectionsDelay <= 1)
{
reflectionsDelay = 1;
}
pNative->ReflectionsDelay = (uint32_t) reflectionsDelay;
reverbDelay = pI3DL2->ReverbDelay * 1000.0f;
if (reverbDelay >= FAUDIOFX_REVERB_MAX_REVERB_DELAY)
{
reverbDelay = (float) (FAUDIOFX_REVERB_MAX_REVERB_DELAY - 1);
}
pNative->ReverbDelay = (uint8_t) reverbDelay;
pNative->ReflectionsGain = pI3DL2->Reflections / 100.0f;
pNative->ReverbGain = pI3DL2->Reverb / 100.0f;
pNative->EarlyDiffusion = (uint8_t) (15.0f * pI3DL2->Diffusion / 100.0f);
pNative->LateDiffusion = pNative->EarlyDiffusion;
pNative->Density = pI3DL2->Density;
pNative->RoomFilterFreq = pI3DL2->HFReference;
pNative->WetDryMix = pI3DL2->WetDryMix;
}
/* Public API (Version 9) */
uint32_t FAudioCreateReverb9(FAPO** ppApo, uint32_t Flags)
{
return FAudioCreateReverb9WithCustomAllocatorEXT(
ppApo,
Flags,
FAudio_malloc,
FAudio_free,
FAudio_realloc
);
}
uint32_t FAudioCreateReverb9WithCustomAllocatorEXT(
FAPO** ppApo,
uint32_t Flags,
FAudioMallocFunc customMalloc,
FAudioFreeFunc customFree,
FAudioReallocFunc customRealloc
) {
const FAudioFXReverbParameters9 fxdefault =
{
FAUDIOFX_REVERB_DEFAULT_WET_DRY_MIX,
FAUDIOFX_REVERB_DEFAULT_REFLECTIONS_DELAY,
FAUDIOFX_REVERB_DEFAULT_REVERB_DELAY,
FAUDIOFX_REVERB_DEFAULT_REAR_DELAY, /* FIXME: 7POINT1? */
FAUDIOFX_REVERB_DEFAULT_7POINT1_SIDE_DELAY,
FAUDIOFX_REVERB_DEFAULT_POSITION,
FAUDIOFX_REVERB_DEFAULT_POSITION,
FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX,
FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX,
FAUDIOFX_REVERB_DEFAULT_EARLY_DIFFUSION,
FAUDIOFX_REVERB_DEFAULT_LATE_DIFFUSION,
FAUDIOFX_REVERB_DEFAULT_LOW_EQ_GAIN,
FAUDIOFX_REVERB_DEFAULT_LOW_EQ_CUTOFF,
FAUDIOFX_REVERB_DEFAULT_HIGH_EQ_GAIN,
FAUDIOFX_REVERB_DEFAULT_HIGH_EQ_CUTOFF,
FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_FREQ,
FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_MAIN,
FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_HF,
FAUDIOFX_REVERB_DEFAULT_REFLECTIONS_GAIN,
FAUDIOFX_REVERB_DEFAULT_REVERB_GAIN,
FAUDIOFX_REVERB_DEFAULT_DECAY_TIME,
FAUDIOFX_REVERB_DEFAULT_DENSITY,
FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE
};
/* Allocate... */
FAudioFXReverb *result = (FAudioFXReverb*) customMalloc(sizeof(FAudioFXReverb));
uint8_t *params = (uint8_t*) customMalloc(
sizeof(FAudioFXReverbParameters9) * 3
);
result->apiVersion = 9;
#define INITPARAMS(offset) \
FAudio_memcpy( \
params + sizeof(FAudioFXReverbParameters9) * offset, \
&fxdefault, \
sizeof(FAudioFXReverbParameters9) \
);
INITPARAMS(0)
INITPARAMS(1)
INITPARAMS(2)
#undef INITPARAMS
/* Initialize... */
FAudio_memcpy(
&ReverbProperties.clsid,
&FAudioFX_CLSID_AudioReverb,
sizeof(FAudioGUID)
);
CreateFAPOBaseWithCustomAllocatorEXT(
&result->base,
&ReverbProperties,
params,
sizeof(FAudioFXReverbParameters9),
0,
customMalloc,
customFree,
customRealloc
);
result->inChannels = 0;
result->outChannels = 0;
result->sampleRate = 0;
FAudio_zero(&result->reverb, sizeof(DspReverb));
/* Function table... */
#define ASSIGN_VT(name) \
result->base.base.name = (name##Func) FAudioFXReverb_##name;
ASSIGN_VT(LockForProcess);
ASSIGN_VT(IsInputFormatSupported);
ASSIGN_VT(IsOutputFormatSupported);
ASSIGN_VT(Initialize);
ASSIGN_VT(Reset);
ASSIGN_VT(Process);
result->base.Destructor = FAudioFXReverb_Free;
#undef ASSIGN_VT
/* Finally. */
*ppApo = &result->base.base;
return 0;
}
void ReverbConvertI3DL2ToNative9(
const FAudioFXReverbI3DL2Parameters *pI3DL2,
FAudioFXReverbParameters9 *pNative,
int32_t sevenDotOneReverb
) {
float reflectionsDelay;
float reverbDelay;
if (sevenDotOneReverb)
{
pNative->RearDelay = FAUDIOFX_REVERB_DEFAULT_7POINT1_REAR_DELAY;
}
else
{
pNative->RearDelay = FAUDIOFX_REVERB_DEFAULT_REAR_DELAY;
}
pNative->SideDelay = FAUDIOFX_REVERB_DEFAULT_7POINT1_SIDE_DELAY;
pNative->PositionLeft = FAUDIOFX_REVERB_DEFAULT_POSITION;
pNative->PositionRight = FAUDIOFX_REVERB_DEFAULT_POSITION;
pNative->PositionMatrixLeft = FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
pNative->PositionMatrixRight = FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
pNative->RoomSize = FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE;
pNative->LowEQCutoff = 4;
pNative->HighEQCutoff = 6;
pNative->RoomFilterMain = (float) pI3DL2->Room / 100.0f;
pNative->RoomFilterHF = (float) pI3DL2->RoomHF / 100.0f;
if (pI3DL2->DecayHFRatio >= 1.0f)
{
int32_t index = (int32_t) (-4.0 * FAudio_log10(pI3DL2->DecayHFRatio));
if (index < -8)
{
index = -8;
}
pNative->LowEQGain = (uint8_t) ((index < 0) ? index + 8 : 8);
pNative->HighEQGain = 8;
pNative->DecayTime = pI3DL2->DecayTime * pI3DL2->DecayHFRatio;
}
else
{
int32_t index = (int32_t) (4.0 * FAudio_log10(pI3DL2->DecayHFRatio));
if (index < -8)
{
index = -8;
}
pNative->LowEQGain = 8;
pNative->HighEQGain = (uint8_t) ((index < 0) ? index + 8 : 8);
pNative->DecayTime = pI3DL2->DecayTime;
}
reflectionsDelay = pI3DL2->ReflectionsDelay * 1000.0f;
if (reflectionsDelay >= FAUDIOFX_REVERB_MAX_REFLECTIONS_DELAY)
{
reflectionsDelay = (float) (FAUDIOFX_REVERB_MAX_REFLECTIONS_DELAY - 1);
}
else if (reflectionsDelay <= 1)
{
reflectionsDelay = 1;
}
pNative->ReflectionsDelay = (uint32_t) reflectionsDelay;
reverbDelay = pI3DL2->ReverbDelay * 1000.0f;
if (reverbDelay >= FAUDIOFX_REVERB_MAX_REVERB_DELAY)
{
reverbDelay = (float) (FAUDIOFX_REVERB_MAX_REVERB_DELAY - 1);
}
pNative->ReverbDelay = (uint8_t) reverbDelay;
pNative->ReflectionsGain = pI3DL2->Reflections / 100.0f;
pNative->ReverbGain = pI3DL2->Reverb / 100.0f;
pNative->EarlyDiffusion = (uint8_t) (15.0f * pI3DL2->Diffusion / 100.0f);
pNative->LateDiffusion = pNative->EarlyDiffusion;
pNative->Density = pI3DL2->Density;
pNative->RoomFilterFreq = pI3DL2->HFReference;
pNative->WetDryMix = pI3DL2->WetDryMix;
}
/* vim: set noexpandtab shiftwidth=8 tabstop=8: */