450 lines
12 KiB
C
Raw Normal View History

2007-11-13 18:02:18 -08:00
#ifndef _ALU_H_
#define _ALU_H_
#include <limits.h>
2009-11-19 09:24:35 -08:00
#include <math.h>
2009-05-16 23:26:39 -07:00
#ifdef HAVE_FLOAT_H
#include <float.h>
#endif
#ifdef HAVE_IEEEFP_H
#include <ieeefp.h>
#endif
2009-05-16 23:26:39 -07:00
2018-12-22 16:01:14 -08:00
#include <cmath>
2018-12-01 01:12:05 -08:00
#include <array>
#include "alMain.h"
#include "alBuffer.h"
2014-04-19 02:11:04 -07:00
#include "hrtf.h"
2018-12-25 11:09:41 -08:00
#include "logging.h"
#include "math_defs.h"
#include "filters/biquad.h"
#include "filters/nfc.h"
#include "almalloc.h"
#include "alnumeric.h"
enum class DistanceModel;
#define MAX_PITCH 255
#define MAX_SENDS 16
/* Maximum number of samples to pad on either end of a buffer for resampling.
* Note that both the beginning and end need padding!
*/
#define MAX_RESAMPLE_PADDING 24
struct BSincTable;
struct ALsource;
2017-03-09 07:23:12 -08:00
struct ALbufferlistitem;
2014-08-21 03:24:48 -07:00
struct ALvoice;
struct ALeffectslot;
#define DITHER_RNG_SEED 22222
enum SpatializeMode {
SpatializeOff = AL_FALSE,
SpatializeOn = AL_TRUE,
SpatializeAuto = AL_AUTO_SOFT
};
enum Resampler {
PointResampler,
LinearResampler,
FIR4Resampler,
2017-08-25 05:52:19 -07:00
BSinc12Resampler,
BSinc24Resampler,
2017-04-21 04:15:08 -07:00
ResamplerMax = BSinc24Resampler
};
extern Resampler ResamplerDefault;
/* The number of distinct scale and phase intervals within the bsinc filter
* table.
*/
#define BSINC_SCALE_BITS 4
#define BSINC_SCALE_COUNT (1<<BSINC_SCALE_BITS)
#define BSINC_PHASE_BITS 4
#define BSINC_PHASE_COUNT (1<<BSINC_PHASE_BITS)
/* Interpolator state. Kind of a misnomer since the interpolator itself is
* stateless. This just keeps it from having to recompute scale-related
* mappings for every sample.
*/
struct BsincState {
ALfloat sf; /* Scale interpolation factor. */
2017-08-15 04:15:50 -07:00
ALsizei m; /* Coefficient count. */
ALsizei l; /* Left coefficient offset. */
/* Filter coefficients, followed by the scale, phase, and scale-phase
* delta coefficients. Starting at phase index 0, each subsequent phase
* index follows contiguously.
*/
const ALfloat *filter;
};
union InterpState {
2017-02-13 11:29:32 -08:00
BsincState bsinc;
};
2017-02-13 11:29:32 -08:00
using ResamplerFunc = const ALfloat*(*)(const InterpState *state,
const ALfloat *RESTRICT src, ALsizei frac, ALint increment,
ALfloat *RESTRICT dst, ALsizei dstlen);
void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table);
2018-12-10 21:30:22 -08:00
extern const BSincTable bsinc12;
extern const BSincTable bsinc24;
enum {
AF_None = 0,
AF_LowPass = 1,
AF_HighPass = 2,
AF_BandPass = AF_LowPass | AF_HighPass
};
2018-12-22 16:01:14 -08:00
struct MixHrtfParams {
const HrirArray<ALfloat> *Coeffs;
ALsizei Delay[2];
ALfloat Gain;
ALfloat GainStep;
2018-12-22 16:01:14 -08:00
};
2017-03-09 07:23:12 -08:00
2018-12-22 16:01:14 -08:00
struct DirectParams {
2018-04-04 18:07:46 -07:00
BiquadFilter LowPass;
BiquadFilter HighPass;
NfcFilter NFCtrlFilter;
struct {
HrtfParams Old;
HrtfParams Target;
HrtfState State;
} Hrtf;
struct {
ALfloat Current[MAX_OUTPUT_CHANNELS];
ALfloat Target[MAX_OUTPUT_CHANNELS];
} Gains;
2018-12-22 16:01:14 -08:00
};
2018-12-22 16:01:14 -08:00
struct SendParams {
2018-04-04 18:07:46 -07:00
BiquadFilter LowPass;
BiquadFilter HighPass;
struct {
ALfloat Current[MAX_OUTPUT_CHANNELS];
ALfloat Target[MAX_OUTPUT_CHANNELS];
} Gains;
2018-12-22 16:01:14 -08:00
};
struct ALvoicePropsBase {
ALfloat Pitch;
ALfloat Gain;
ALfloat OuterGain;
ALfloat MinGain;
ALfloat MaxGain;
ALfloat InnerAngle;
ALfloat OuterAngle;
ALfloat RefDistance;
ALfloat MaxDistance;
2017-05-05 03:19:50 -07:00
ALfloat RolloffFactor;
std::array<ALfloat,3> Position;
std::array<ALfloat,3> Velocity;
std::array<ALfloat,3> Direction;
std::array<ALfloat,3> OrientAt;
std::array<ALfloat,3> OrientUp;
ALboolean HeadRelative;
DistanceModel mDistanceModel;
Resampler mResampler;
ALboolean DirectChannels;
SpatializeMode mSpatializeMode;
ALboolean DryGainHFAuto;
ALboolean WetGainAuto;
ALboolean WetGainHFAuto;
ALfloat OuterGainHF;
ALfloat AirAbsorptionFactor;
ALfloat RoomRolloffFactor;
ALfloat DopplerFactor;
std::array<ALfloat,2> StereoPan;
ALfloat Radius;
/** Direct filter and auxiliary send info. */
struct {
ALfloat Gain;
ALfloat GainHF;
ALfloat HFReference;
ALfloat GainLF;
ALfloat LFReference;
} Direct;
struct SendData {
ALeffectslot *Slot;
ALfloat Gain;
ALfloat GainHF;
ALfloat HFReference;
ALfloat GainLF;
ALfloat LFReference;
} Send[MAX_SENDS];
};
struct ALvoiceProps : public ALvoicePropsBase {
std::atomic<ALvoiceProps*> next{nullptr};
DEF_NEWDEL(ALvoiceProps)
};
#define VOICE_IS_STATIC (1<<0)
#define VOICE_IS_FADING (1<<1) /* Fading sources use gain stepping for smooth transitions. */
#define VOICE_HAS_HRTF (1<<2)
#define VOICE_HAS_NFC (1<<3)
struct ALvoice {
std::atomic<ALvoiceProps*> Update{nullptr};
2017-03-09 07:23:12 -08:00
std::atomic<ALuint> SourceID{0u};
std::atomic<bool> Playing{false};
2017-03-09 07:23:12 -08:00
ALvoicePropsBase Props;
2017-03-09 07:23:12 -08:00
/**
* Source offset in samples, relative to the currently playing buffer, NOT
* the whole queue, and the fractional (fixed-point) offset to the next
* sample.
*/
2018-11-26 14:48:26 -08:00
std::atomic<ALuint> position;
std::atomic<ALsizei> position_fraction;
2017-03-09 07:23:12 -08:00
/* Current buffer queue item being played. */
2018-11-26 14:48:26 -08:00
std::atomic<ALbufferlistitem*> current_buffer;
/* Buffer queue item to loop to at end of queue (will be NULL for non-
* looping voices).
*/
2018-11-26 14:48:26 -08:00
std::atomic<ALbufferlistitem*> loop_buffer;
2017-03-09 07:23:12 -08:00
/**
* Number of channels and bytes-per-sample for the attached source's
* buffer(s).
*/
ALsizei NumChannels;
ALsizei SampleSize;
/** Current target parameters used for mixing. */
ALint Step;
ResamplerFunc Resampler;
ALuint Flags;
2017-03-09 07:23:12 -08:00
ALuint Offset; /* Number of output samples mixed since starting. */
alignas(16) std::array<std::array<ALfloat,MAX_RESAMPLE_PADDING>,MAX_INPUT_CHANNELS> PrevSamples;
2017-03-09 07:23:12 -08:00
InterpState ResampleState;
struct {
int FilterType;
2017-03-09 07:23:12 -08:00
DirectParams Params[MAX_INPUT_CHANNELS];
ALfloat (*Buffer)[BUFFERSIZE];
ALsizei Channels;
ALsizei ChannelsPerOrder[MAX_AMBI_ORDER+1];
2017-03-09 07:23:12 -08:00
} Direct;
2018-12-02 13:35:07 -08:00
struct SendData {
int FilterType;
2017-03-09 07:23:12 -08:00
SendParams Params[MAX_INPUT_CHANNELS];
ALfloat (*Buffer)[BUFFERSIZE];
ALsizei Channels;
};
al::FlexArray<SendData> Send;
ALvoice(size_t numsends) : Send{numsends} { }
ALvoice(const ALvoice&) = delete;
ALvoice& operator=(const ALvoice&) = delete;
static constexpr size_t Sizeof(size_t numsends) noexcept
{
return maxz(sizeof(ALvoice),
al::FlexArray<SendData>::Sizeof(numsends, offsetof(ALvoice, Send)));
}
};
2017-03-09 07:23:12 -08:00
2018-11-27 21:19:30 -08:00
void DeinitVoice(ALvoice *voice) noexcept;
2019-01-12 21:08:34 -08:00
using MixerFunc = void(*)(const ALfloat *data, const ALsizei OutChans,
ALfloat (*OutBuffer)[BUFFERSIZE], ALfloat *CurrentGains, const ALfloat *TargetGains,
const ALsizei Counter, const ALsizei OutPos, const ALsizei BufferSize);
using RowMixerFunc = void(*)(ALfloat *OutBuffer, const ALfloat *gains,
2019-01-12 21:08:34 -08:00
const ALfloat (*data)[BUFFERSIZE], const ALsizei InChans, const ALsizei InPos,
const ALsizei BufferSize);
using HrtfMixerFunc = void(*)(ALfloat *RESTRICT LeftOut, ALfloat *RESTRICT RightOut,
const ALfloat *data, ALsizei Offset, const ALsizei OutPos, const ALsizei IrSize,
MixHrtfParams *hrtfparams, HrtfState *hrtfstate, const ALsizei BufferSize);
using HrtfMixerBlendFunc = void(*)(ALfloat *RESTRICT LeftOut, ALfloat *RESTRICT RightOut,
const ALfloat *data, ALsizei Offset, const ALsizei OutPos, const ALsizei IrSize,
const HrtfParams *oldparams, MixHrtfParams *newparams, HrtfState *hrtfstate,
const ALsizei BufferSize);
using HrtfDirectMixerFunc = void(*)(ALfloat *RESTRICT LeftOut, ALfloat *RESTRICT RightOut,
const ALfloat (*data)[BUFFERSIZE], DirectHrtfState *State, const ALsizei NumChans,
const ALsizei BufferSize);
2010-11-26 17:47:43 -08:00
2018-12-08 04:10:59 -08:00
#define GAIN_MIX_MAX (1000.0f) /* +60dB */
#define GAIN_SILENCE_THRESHOLD (0.00001f) /* -100dB */
2013-10-06 17:25:47 -07:00
#define SPEEDOFSOUNDMETRESPERSEC (343.3f)
#define AIRABSORBGAINHF (0.99426f) /* -0.05dB */
/* Target gain for the reverb decay feedback reaching the decay time. */
#define REVERB_DECAY_GAIN (0.001f) /* -60 dB */
#define FRACTIONBITS (12)
#define FRACTIONONE (1<<FRACTIONBITS)
#define FRACTIONMASK (FRACTIONONE-1)
inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu) noexcept
2018-12-22 16:01:14 -08:00
{ return val1 + (val2-val1)*mu; }
inline ALfloat cubic(ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat mu) noexcept
{
ALfloat mu2 = mu*mu, mu3 = mu2*mu;
ALfloat a0 = -0.5f*mu3 + mu2 + -0.5f*mu;
ALfloat a1 = 1.5f*mu3 + -2.5f*mu2 + 1.0f;
ALfloat a2 = -1.5f*mu3 + 2.0f*mu2 + 0.5f*mu;
ALfloat a3 = 0.5f*mu3 + -0.5f*mu2;
return val1*a0 + val2*a1 + val3*a2 + val4*a3;
}
enum HrtfRequestMode {
Hrtf_Default = 0,
Hrtf_Enable = 1,
Hrtf_Disable = 2,
};
void aluInit(void);
2015-09-27 20:55:39 -07:00
void aluInitMixer(void);
2018-12-22 16:01:14 -08:00
ResamplerFunc SelectResampler(Resampler resampler);
2016-09-06 13:21:11 -07:00
/* aluInitRenderer
*
* Set up the appropriate panning method and mixing method given the device
* properties.
*/
2018-12-22 16:01:14 -08:00
void aluInitRenderer(ALCdevice *device, ALint hrtf_id, HrtfRequestMode hrtf_appreq, HrtfRequestMode hrtf_userreq);
2018-12-22 16:01:14 -08:00
void aluInitEffectPanning(ALeffectslot *slot);
void aluSelectPostProcess(ALCdevice *device);
/**
* Calculates ambisonic encoder coefficients using the X, Y, and Z direction
* components, which must represent a normalized (unit length) vector, and the
* spread is the angular width of the sound (0...tau).
*
* NOTE: The components use ambisonic coordinates. As a result:
*
* Ambisonic Y = OpenAL -X
* Ambisonic Z = OpenAL Y
* Ambisonic X = OpenAL -Z
*
* The components are ordered such that OpenAL's X, Y, and Z are the first,
* second, and third parameters respectively -- simply negate X and Z.
*/
void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread,
ALfloat (&coeffs)[MAX_AMBI_CHANNELS]);
Use an ambisonics-based panning method For mono sources, third-order ambisonics is utilized to generate panning gains. The general idea is that a panned mono sound can be encoded into b-format ambisonics as: w[i] = sample[i] * 0.7071; x[i] = sample[i] * dir[0]; y[i] = sample[i] * dir[1]; ... and subsequently rendered using: output[chan][i] = w[i] * w_coeffs[chan] + x[i] * x_coeffs[chan] + y[i] * y_coeffs[chan] + ...; By reordering the math, channel gains can be generated by doing: gain[chan] = 0.7071 * w_coeffs[chan] + dir[0] * x_coeffs[chan] + dir[1] * y_coeffs[chan] + ...; which then get applied as normal: output[chan][i] = sample[i] * gain[chan]; One of the reasons to use ambisonics for panning is that it provides arguably better reproduction for sounds emanating from between two speakers. As well, this makes it easier to pan in all 3 dimensions, with for instance a "3D7.1" or 8-channel cube speaker configuration by simply providing the necessary coefficients (this will need some work since some methods still use angle-based panpot, particularly multi-channel sources). Unfortunately, the math to reliably generate the coefficients for a given speaker configuration is too costly to do at run-time. They have to be pre- generated based on a pre-specified speaker arangement, which means the config options for tweaking speaker angles are no longer supportable. Eventually I hope to provide config options for custom coefficients, which can either be generated and written in manually, or via alsoft-config from user-specified speaker positions. The current default set of coefficients were generated using the MATLAB scripts (compatible with GNU Octave) from the excellent Ambisonic Decoder Toolbox, at https://bitbucket.org/ambidecodertoolbox/adt/
2014-09-30 07:33:13 -07:00
/**
* CalcDirectionCoeffs
Use an ambisonics-based panning method For mono sources, third-order ambisonics is utilized to generate panning gains. The general idea is that a panned mono sound can be encoded into b-format ambisonics as: w[i] = sample[i] * 0.7071; x[i] = sample[i] * dir[0]; y[i] = sample[i] * dir[1]; ... and subsequently rendered using: output[chan][i] = w[i] * w_coeffs[chan] + x[i] * x_coeffs[chan] + y[i] * y_coeffs[chan] + ...; By reordering the math, channel gains can be generated by doing: gain[chan] = 0.7071 * w_coeffs[chan] + dir[0] * x_coeffs[chan] + dir[1] * y_coeffs[chan] + ...; which then get applied as normal: output[chan][i] = sample[i] * gain[chan]; One of the reasons to use ambisonics for panning is that it provides arguably better reproduction for sounds emanating from between two speakers. As well, this makes it easier to pan in all 3 dimensions, with for instance a "3D7.1" or 8-channel cube speaker configuration by simply providing the necessary coefficients (this will need some work since some methods still use angle-based panpot, particularly multi-channel sources). Unfortunately, the math to reliably generate the coefficients for a given speaker configuration is too costly to do at run-time. They have to be pre- generated based on a pre-specified speaker arangement, which means the config options for tweaking speaker angles are no longer supportable. Eventually I hope to provide config options for custom coefficients, which can either be generated and written in manually, or via alsoft-config from user-specified speaker positions. The current default set of coefficients were generated using the MATLAB scripts (compatible with GNU Octave) from the excellent Ambisonic Decoder Toolbox, at https://bitbucket.org/ambidecodertoolbox/adt/
2014-09-30 07:33:13 -07:00
*
* Calculates ambisonic coefficients based on an OpenAL direction vector. The
* vector must be normalized (unit length), and the spread is the angular width
* of the sound (0...tau).
Use an ambisonics-based panning method For mono sources, third-order ambisonics is utilized to generate panning gains. The general idea is that a panned mono sound can be encoded into b-format ambisonics as: w[i] = sample[i] * 0.7071; x[i] = sample[i] * dir[0]; y[i] = sample[i] * dir[1]; ... and subsequently rendered using: output[chan][i] = w[i] * w_coeffs[chan] + x[i] * x_coeffs[chan] + y[i] * y_coeffs[chan] + ...; By reordering the math, channel gains can be generated by doing: gain[chan] = 0.7071 * w_coeffs[chan] + dir[0] * x_coeffs[chan] + dir[1] * y_coeffs[chan] + ...; which then get applied as normal: output[chan][i] = sample[i] * gain[chan]; One of the reasons to use ambisonics for panning is that it provides arguably better reproduction for sounds emanating from between two speakers. As well, this makes it easier to pan in all 3 dimensions, with for instance a "3D7.1" or 8-channel cube speaker configuration by simply providing the necessary coefficients (this will need some work since some methods still use angle-based panpot, particularly multi-channel sources). Unfortunately, the math to reliably generate the coefficients for a given speaker configuration is too costly to do at run-time. They have to be pre- generated based on a pre-specified speaker arangement, which means the config options for tweaking speaker angles are no longer supportable. Eventually I hope to provide config options for custom coefficients, which can either be generated and written in manually, or via alsoft-config from user-specified speaker positions. The current default set of coefficients were generated using the MATLAB scripts (compatible with GNU Octave) from the excellent Ambisonic Decoder Toolbox, at https://bitbucket.org/ambidecodertoolbox/adt/
2014-09-30 07:33:13 -07:00
*/
inline void CalcDirectionCoeffs(const ALfloat (&dir)[3], ALfloat spread, ALfloat (&coeffs)[MAX_AMBI_CHANNELS])
{
/* Convert from OpenAL coords to Ambisonics. */
CalcAmbiCoeffs(-dir[0], dir[1], -dir[2], spread, coeffs);
}
Use an ambisonics-based panning method For mono sources, third-order ambisonics is utilized to generate panning gains. The general idea is that a panned mono sound can be encoded into b-format ambisonics as: w[i] = sample[i] * 0.7071; x[i] = sample[i] * dir[0]; y[i] = sample[i] * dir[1]; ... and subsequently rendered using: output[chan][i] = w[i] * w_coeffs[chan] + x[i] * x_coeffs[chan] + y[i] * y_coeffs[chan] + ...; By reordering the math, channel gains can be generated by doing: gain[chan] = 0.7071 * w_coeffs[chan] + dir[0] * x_coeffs[chan] + dir[1] * y_coeffs[chan] + ...; which then get applied as normal: output[chan][i] = sample[i] * gain[chan]; One of the reasons to use ambisonics for panning is that it provides arguably better reproduction for sounds emanating from between two speakers. As well, this makes it easier to pan in all 3 dimensions, with for instance a "3D7.1" or 8-channel cube speaker configuration by simply providing the necessary coefficients (this will need some work since some methods still use angle-based panpot, particularly multi-channel sources). Unfortunately, the math to reliably generate the coefficients for a given speaker configuration is too costly to do at run-time. They have to be pre- generated based on a pre-specified speaker arangement, which means the config options for tweaking speaker angles are no longer supportable. Eventually I hope to provide config options for custom coefficients, which can either be generated and written in manually, or via alsoft-config from user-specified speaker positions. The current default set of coefficients were generated using the MATLAB scripts (compatible with GNU Octave) from the excellent Ambisonic Decoder Toolbox, at https://bitbucket.org/ambidecodertoolbox/adt/
2014-09-30 07:33:13 -07:00
/**
* CalcAngleCoeffs
*
2016-04-15 18:14:19 -07:00
* Calculates ambisonic coefficients based on azimuth and elevation. The
* azimuth and elevation parameters are in radians, going right and up
* respectively.
*/
inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat (&coeffs)[MAX_AMBI_CHANNELS])
{
2018-12-22 16:01:14 -08:00
ALfloat x = -std::sin(azimuth) * std::cos(elevation);
ALfloat y = std::sin(elevation);
ALfloat z = std::cos(azimuth) * std::cos(elevation);
CalcAmbiCoeffs(x, y, z, spread, coeffs);
}
/**
* ScaleAzimuthFront
*
* Scales the given azimuth toward the side (+/- pi/2 radians) for positions in
* front.
*/
inline float ScaleAzimuthFront(float azimuth, float scale)
{
2018-12-22 16:01:14 -08:00
ALfloat sign = std::copysign(1.0f, azimuth);
if(!(std::fabs(azimuth) > al::MathDefs<float>::Pi()*0.5f))
return minf(std::fabs(azimuth) * scale, al::MathDefs<float>::Pi()*0.5f) * sign;
return azimuth;
}
2018-12-08 14:22:20 -08:00
void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat*RESTRICT coeffs, ALfloat ingain, ALfloat (&gains)[MAX_OUTPUT_CHANNELS]);
2018-09-19 21:09:19 -07:00
/**
2018-09-19 22:18:46 -07:00
* ComputePanGains
*
* Computes panning gains using the given channel decoder coefficients and the
2018-09-19 22:18:46 -07:00
* pre-calculated direction or angle coefficients. For B-Format sources, the
* coeffs are a 'slice' of a transform matrix for the input channel, used to
* scale and orient the sound samples.
*/
2018-12-08 14:22:20 -08:00
inline void ComputePanGains(const MixParams *dry, const ALfloat*RESTRICT coeffs, ALfloat ingain, ALfloat (&gains)[MAX_OUTPUT_CHANNELS])
{
ComputePanningGainsBF(dry->AmbiMap.data(), dry->NumChannels, coeffs, ingain, gains);
}
void ComputePanGains(const ALeffectslot *slot, const ALfloat*RESTRICT coeffs, ALfloat ingain, ALfloat (&gains)[MAX_OUTPUT_CHANNELS]);
ALboolean MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const ALsizei SamplesToDo);
void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples);
2018-12-30 21:38:42 -08:00
/* Caller must lock the device state, and the mixer must not be running. */
void aluHandleDisconnect(ALCdevice *device, const char *msg, ...) DECL_FORMAT(printf, 2, 3);
2007-11-13 18:02:18 -08:00
2017-12-17 21:48:07 -08:00
extern MixerFunc MixSamples;
2018-01-16 12:18:59 -08:00
extern RowMixerFunc MixRowSamples;
2017-12-17 21:48:07 -08:00
extern const ALfloat ConeScale;
extern const ALfloat ZScale;
extern const ALboolean OverrideReverbSpeedOfSound;
2007-11-13 18:02:18 -08:00
#endif