From 79bce84900e760e187758e83e4c144cb5179a900 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Sun, 28 Jan 2018 00:03:02 -0800 Subject: Add option to disable "build_version" cmake target --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index b5c65c55..c13c75c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,7 @@ OPTION(ALSOFT_CONFIG "Install alsoft.conf sample configuration file" ON) OPTION(ALSOFT_HRTF_DEFS "Install HRTF definition files" ON) OPTION(ALSOFT_AMBDEC_PRESETS "Install AmbDec preset files" ON) OPTION(ALSOFT_INSTALL "Install headers and libraries" ON) +OPTION(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON) if(DEFINED SHARE_INSTALL_DIR) message(WARNING "SHARE_INSTALL_DIR is deprecated. Use the variables provided by the GNUInstallDirs module instead") @@ -1136,7 +1137,7 @@ SET(BACKENDS "${BACKENDS} Null") FIND_PACKAGE(Git) -IF(GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.git") +IF(ALSOFT_UPDATE_BUILD_VERSION AND GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.git") # Get the current working branch and its latest abbreviated commit hash ADD_CUSTOM_TARGET(build_version ${CMAKE_COMMAND} -D GIT_EXECUTABLE=${GIT_EXECUTABLE} -- cgit v1.2.3 From c093728ced2da0eb5fb01235c62460262b704790 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 28 Nov 2019 10:54:47 -0800 Subject: Move the polyphase resampler to the common lib --- CMakeLists.txt | 2 + common/polyphase_resampler.cpp | 214 +++++++++++++++++++++++++++++++++++++ common/polyphase_resampler.h | 45 ++++++++ utils/makemhr/loaddef.cpp | 6 +- utils/makemhr/loadsofa.cpp | 6 +- utils/makemhr/makemhr.cpp | 237 +---------------------------------------- utils/makemhr/makemhr.h | 12 +-- 7 files changed, 272 insertions(+), 250 deletions(-) create mode 100644 common/polyphase_resampler.cpp create mode 100644 common/polyphase_resampler.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 98f9ad49..a4300f65 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -551,6 +551,8 @@ SET(COMMON_OBJS common/intrusive_ptr.h common/math_defs.h common/opthelpers.h + common/polyphase_resampler.cpp + common/polyphase_resampler.h common/pragmadefs.h common/strutils.cpp common/strutils.h diff --git a/common/polyphase_resampler.cpp b/common/polyphase_resampler.cpp new file mode 100644 index 00000000..4605b732 --- /dev/null +++ b/common/polyphase_resampler.cpp @@ -0,0 +1,214 @@ + +#include "polyphase_resampler.h" + +#include +#include + + +namespace { + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define EPSILON 1e-9 + +using uint = unsigned int; + +/* This is the normalized cardinal sine (sinc) function. + * + * sinc(x) = { 1, x = 0 + * { sin(pi x) / (pi x), otherwise. + */ +double Sinc(const double x) +{ + if(std::abs(x) < EPSILON) + return 1.0; + return std::sin(M_PI * x) / (M_PI * x); +} + +/* The zero-order modified Bessel function of the first kind, used for the + * Kaiser window. + * + * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k) + * = sum_{k=0}^inf ((x / 2)^k / k!)^2 + */ +double BesselI_0(const double x) +{ + double term, sum, x2, y, last_sum; + int k; + + // Start at k=1 since k=0 is trivial. + term = 1.0; + sum = 1.0; + x2 = x/2.0; + k = 1; + + // Let the integration converge until the term of the sum is no longer + // significant. + do { + y = x2 / k; + k++; + last_sum = sum; + term *= y * y; + sum += term; + } while(sum != last_sum); + return sum; +} + +/* Calculate a Kaiser window from the given beta value and a normalized k + * [-1, 1]. + * + * w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1 + * { 0, elsewhere. + * + * Where k can be calculated as: + * + * k = i / l, where -l <= i <= l. + * + * or: + * + * k = 2 i / M - 1, where 0 <= i <= M. + */ +double Kaiser(const double b, const double k) +{ + if(!(k >= -1.0 && k <= 1.0)) + return 0.0; + return BesselI_0(b * std::sqrt(1.0 - k*k)) / BesselI_0(b); +} + +// Calculates the greatest common divisor of a and b. +uint Gcd(uint x, uint y) +{ + while(y > 0) + { + uint z{y}; + y = x % y; + x = z; + } + return x; +} + +/* Calculates the size (order) of the Kaiser window. Rejection is in dB and + * the transition width is normalized frequency (0.5 is nyquist). + * + * M = { ceil((r - 7.95) / (2.285 2 pi f_t)), r > 21 + * { ceil(5.79 / 2 pi f_t), r <= 21. + * + */ +uint CalcKaiserOrder(const double rejection, const double transition) +{ + double w_t = 2.0 * M_PI * transition; + if(rejection > 21.0) + return static_cast(std::ceil((rejection - 7.95) / (2.285 * w_t))); + return static_cast(std::ceil(5.79 / w_t)); +} + +// Calculates the beta value of the Kaiser window. Rejection is in dB. +double CalcKaiserBeta(const double rejection) +{ + if(rejection > 50.0) + return 0.1102 * (rejection - 8.7); + if(rejection >= 21.0) + return (0.5842 * std::pow(rejection - 21.0, 0.4)) + + (0.07886 * (rejection - 21.0)); + return 0.0; +} + +/* Calculates a point on the Kaiser-windowed sinc filter for the given half- + * width, beta, gain, and cutoff. The point is specified in non-normalized + * samples, from 0 to M, where M = (2 l + 1). + * + * w(k) 2 p f_t sinc(2 f_t x) + * + * x -- centered sample index (i - l) + * k -- normalized and centered window index (x / l) + * w(k) -- window function (Kaiser) + * p -- gain compensation factor when sampling + * f_t -- normalized center frequency (or cutoff; 0.5 is nyquist) + */ +double SincFilter(const uint l, const double b, const double gain, const double cutoff, + const uint i) +{ + const double x{static_cast(i) - l}; + return Kaiser(b, x / l) * 2.0 * gain * cutoff * Sinc(2.0 * cutoff * x); +} + +} // namespace + +// Calculate the resampling metrics and build the Kaiser-windowed sinc filter +// that's used to cut frequencies above the destination nyquist. +void PPhaseResampler::init(const uint srcRate, const uint dstRate) +{ + const uint gcd{Gcd(srcRate, dstRate)}; + mP = dstRate / gcd; + mQ = srcRate / gcd; + + /* The cutoff is adjusted by half the transition width, so the transition + * ends before the nyquist (0.5). Both are scaled by the downsampling + * factor. + */ + double cutoff, width; + if(mP > mQ) + { + cutoff = 0.475 / mP; + width = 0.05 / mP; + } + else + { + cutoff = 0.475 / mQ; + width = 0.05 / mQ; + } + // A rejection of -180 dB is used for the stop band. Round up when + // calculating the left offset to avoid increasing the transition width. + const uint l{(CalcKaiserOrder(180.0, width)+1) / 2}; + const double beta{CalcKaiserBeta(180.0)}; + mM = l*2 + 1; + mL = l; + mF.resize(mM); + for(uint i{0};i < mM;i++) + mF[i] = SincFilter(l, beta, mP, cutoff, i); +} + +// Perform the upsample-filter-downsample resampling operation using a +// polyphase filter implementation. +void PPhaseResampler::process(const uint inN, const double *in, const uint outN, double *out) +{ + if(outN == 0) + return; + + const uint p{mP}, q{mQ}, m{mM}, l{mL}; + + // Handle in-place operation. + std::vector workspace; + double *work{out}; + if(work == in) + { + workspace.resize(outN); + work = workspace.data(); + } + + // Resample the input. + const double *f{mF.data()}; + for(uint i{0};i < outN;i++) + { + double r{0.0}; + // Input starts at l to compensate for the filter delay. This will + // drop any build-up from the first half of the filter. + uint j_f{(l + (q * i)) % p}; + uint j_s{(l + (q * i)) / p}; + while(j_f < m) + { + // Only take input when 0 <= j_s < inN. This single unsigned + // comparison catches both cases. + if(j_s < inN) + r += f[j_f] * in[j_s]; + j_f += p; + j_s--; + } + work[i] = r; + } + // Clean up after in-place operation. + if(work != out) + std::copy_n(work, outN, out); +} diff --git a/common/polyphase_resampler.h b/common/polyphase_resampler.h new file mode 100644 index 00000000..e9833d12 --- /dev/null +++ b/common/polyphase_resampler.h @@ -0,0 +1,45 @@ +#ifndef POLYPHASE_RESAMPLER_H +#define POLYPHASE_RESAMPLER_H + +#include + + +/* This is a polyphase sinc-filtered resampler. It is built for very high + * quality results, rather than real-time performance. + * + * Upsample Downsample + * + * p/q = 3/2 p/q = 3/5 + * + * M-+-+-+-> M-+-+-+-> + * -------------------+ ---------------------+ + * p s * f f f f|f| | p s * f f f f f | + * | 0 * 0 0 0|0|0 | | 0 * 0 0 0 0|0| | + * v 0 * 0 0|0|0 0 | v 0 * 0 0 0|0|0 | + * s * f|f|f f f | s * f f|f|f f | + * 0 * |0|0 0 0 0 | 0 * 0|0|0 0 0 | + * --------+=+--------+ 0 * |0|0 0 0 0 | + * d . d .|d|. d . d ----------+=+--------+ + * d . . . .|d|. . . . + * q-> + * q-+-+-+-> + * + * P_f(i,j) = q i mod p + pj + * P_s(i,j) = floor(q i / p) - j + * d[i=0..N-1] = sum_{j=0}^{floor((M - 1) / p)} { + * { f[P_f(i,j)] s[P_s(i,j)], P_f(i,j) < M + * { 0, P_f(i,j) >= M. } + */ + +struct PPhaseResampler { + using uint = unsigned int; + + void init(const uint srcRate, const uint dstRate); + void process(const uint inN, const double *in, const uint outN, double *out); + +private: + uint mP, mQ, mM, mL; + std::vector mF; +}; + +#endif /* POLYPHASE_RESAMPLER_H */ diff --git a/utils/makemhr/loaddef.cpp b/utils/makemhr/loaddef.cpp index 619f5104..63014fe5 100644 --- a/utils/makemhr/loaddef.cpp +++ b/utils/makemhr/loaddef.cpp @@ -1710,9 +1710,9 @@ static double AverageHrirOnset(const uint rate, const uint n, const double *hrir { std::vector upsampled(10 * n); { - ResamplerT rs; - ResamplerSetup(&rs, rate, 10 * rate); - ResamplerRun(&rs, n, hrir, 10 * n, upsampled.data()); + PPhaseResampler rs; + rs.init(rate, 10 * rate); + rs.process(n, hrir, 10 * n, upsampled.data()); } double mag{0.0}; diff --git a/utils/makemhr/loadsofa.cpp b/utils/makemhr/loadsofa.cpp index c91613c8..473109fe 100644 --- a/utils/makemhr/loadsofa.cpp +++ b/utils/makemhr/loadsofa.cpp @@ -443,9 +443,9 @@ static double CalcHrirOnset(const uint rate, const uint n, std::vector & const double *hrir) { { - ResamplerT rs; - ResamplerSetup(&rs, rate, 10 * rate); - ResamplerRun(&rs, n, hrir, 10 * n, upsampled.data()); + PPhaseResampler rs; + rs.init(rate, 10 * rate); + rs.process(n, hrir, 10 * n, upsampled.data()); } double mag{std::accumulate(upsampled.cbegin(), upsampled.cend(), double{0.0}, diff --git a/utils/makemhr/makemhr.cpp b/utils/makemhr/makemhr.cpp index 1e28ca4b..42a384db 100644 --- a/utils/makemhr/makemhr.cpp +++ b/utils/makemhr/makemhr.cpp @@ -399,237 +399,6 @@ static void MinimumPhase(const uint n, const double *in, complex_d *out) } -/*************************** - *** Resampler functions *** - ***************************/ - -/* This is the normalized cardinal sine (sinc) function. - * - * sinc(x) = { 1, x = 0 - * { sin(pi x) / (pi x), otherwise. - */ -static double Sinc(const double x) -{ - if(std::abs(x) < EPSILON) - return 1.0; - return std::sin(M_PI * x) / (M_PI * x); -} - -/* The zero-order modified Bessel function of the first kind, used for the - * Kaiser window. - * - * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k) - * = sum_{k=0}^inf ((x / 2)^k / k!)^2 - */ -static double BesselI_0(const double x) -{ - double term, sum, x2, y, last_sum; - int k; - - // Start at k=1 since k=0 is trivial. - term = 1.0; - sum = 1.0; - x2 = x/2.0; - k = 1; - - // Let the integration converge until the term of the sum is no longer - // significant. - do { - y = x2 / k; - k++; - last_sum = sum; - term *= y * y; - sum += term; - } while(sum != last_sum); - return sum; -} - -/* Calculate a Kaiser window from the given beta value and a normalized k - * [-1, 1]. - * - * w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1 - * { 0, elsewhere. - * - * Where k can be calculated as: - * - * k = i / l, where -l <= i <= l. - * - * or: - * - * k = 2 i / M - 1, where 0 <= i <= M. - */ -static double Kaiser(const double b, const double k) -{ - if(!(k >= -1.0 && k <= 1.0)) - return 0.0; - return BesselI_0(b * std::sqrt(1.0 - k*k)) / BesselI_0(b); -} - -// Calculates the greatest common divisor of a and b. -static uint Gcd(uint x, uint y) -{ - while(y > 0) - { - uint z{y}; - y = x % y; - x = z; - } - return x; -} - -/* Calculates the size (order) of the Kaiser window. Rejection is in dB and - * the transition width is normalized frequency (0.5 is nyquist). - * - * M = { ceil((r - 7.95) / (2.285 2 pi f_t)), r > 21 - * { ceil(5.79 / 2 pi f_t), r <= 21. - * - */ -static uint CalcKaiserOrder(const double rejection, const double transition) -{ - double w_t = 2.0 * M_PI * transition; - if(rejection > 21.0) - return static_cast(std::ceil((rejection - 7.95) / (2.285 * w_t))); - return static_cast(std::ceil(5.79 / w_t)); -} - -// Calculates the beta value of the Kaiser window. Rejection is in dB. -static double CalcKaiserBeta(const double rejection) -{ - if(rejection > 50.0) - return 0.1102 * (rejection - 8.7); - if(rejection >= 21.0) - return (0.5842 * std::pow(rejection - 21.0, 0.4)) + - (0.07886 * (rejection - 21.0)); - return 0.0; -} - -/* Calculates a point on the Kaiser-windowed sinc filter for the given half- - * width, beta, gain, and cutoff. The point is specified in non-normalized - * samples, from 0 to M, where M = (2 l + 1). - * - * w(k) 2 p f_t sinc(2 f_t x) - * - * x -- centered sample index (i - l) - * k -- normalized and centered window index (x / l) - * w(k) -- window function (Kaiser) - * p -- gain compensation factor when sampling - * f_t -- normalized center frequency (or cutoff; 0.5 is nyquist) - */ -static double SincFilter(const uint l, const double b, const double gain, const double cutoff, const uint i) -{ - return Kaiser(b, static_cast(i - l) / l) * 2.0 * gain * cutoff * Sinc(2.0 * cutoff * (i - l)); -} - -/* This is a polyphase sinc-filtered resampler. - * - * Upsample Downsample - * - * p/q = 3/2 p/q = 3/5 - * - * M-+-+-+-> M-+-+-+-> - * -------------------+ ---------------------+ - * p s * f f f f|f| | p s * f f f f f | - * | 0 * 0 0 0|0|0 | | 0 * 0 0 0 0|0| | - * v 0 * 0 0|0|0 0 | v 0 * 0 0 0|0|0 | - * s * f|f|f f f | s * f f|f|f f | - * 0 * |0|0 0 0 0 | 0 * 0|0|0 0 0 | - * --------+=+--------+ 0 * |0|0 0 0 0 | - * d . d .|d|. d . d ----------+=+--------+ - * d . . . .|d|. . . . - * q-> - * q-+-+-+-> - * - * P_f(i,j) = q i mod p + pj - * P_s(i,j) = floor(q i / p) - j - * d[i=0..N-1] = sum_{j=0}^{floor((M - 1) / p)} { - * { f[P_f(i,j)] s[P_s(i,j)], P_f(i,j) < M - * { 0, P_f(i,j) >= M. } - */ - -// Calculate the resampling metrics and build the Kaiser-windowed sinc filter -// that's used to cut frequencies above the destination nyquist. -void ResamplerSetup(ResamplerT *rs, const uint srcRate, const uint dstRate) -{ - const uint gcd{Gcd(srcRate, dstRate)}; - rs->mP = dstRate / gcd; - rs->mQ = srcRate / gcd; - - /* The cutoff is adjusted by half the transition width, so the transition - * ends before the nyquist (0.5). Both are scaled by the downsampling - * factor. - */ - double cutoff, width; - if(rs->mP > rs->mQ) - { - cutoff = 0.475 / rs->mP; - width = 0.05 / rs->mP; - } - else - { - cutoff = 0.475 / rs->mQ; - width = 0.05 / rs->mQ; - } - // A rejection of -180 dB is used for the stop band. Round up when - // calculating the left offset to avoid increasing the transition width. - const uint l{(CalcKaiserOrder(180.0, width)+1) / 2}; - const double beta{CalcKaiserBeta(180.0)}; - rs->mM = l*2 + 1; - rs->mL = l; - rs->mF.resize(rs->mM); - for(uint i{0};i < rs->mM;i++) - rs->mF[i] = SincFilter(l, beta, rs->mP, cutoff, i); -} - -// Perform the upsample-filter-downsample resampling operation using a -// polyphase filter implementation. -void ResamplerRun(ResamplerT *rs, const uint inN, const double *in, const uint outN, double *out) -{ - const uint p = rs->mP, q = rs->mQ, m = rs->mM, l = rs->mL; - std::vector workspace; - const double *f = rs->mF.data(); - uint j_f, j_s; - double *work; - uint i; - - if(outN == 0) - return; - - // Handle in-place operation. - if(in == out) - { - workspace.resize(outN); - work = workspace.data(); - } - else - work = out; - // Resample the input. - for(i = 0;i < outN;i++) - { - double r = 0.0; - // Input starts at l to compensate for the filter delay. This will - // drop any build-up from the first half of the filter. - j_f = (l + (q * i)) % p; - j_s = (l + (q * i)) / p; - while(j_f < m) - { - // Only take input when 0 <= j_s < inN. This single unsigned - // comparison catches both cases. - if(j_s < inN) - r += f[j_f] * in[j_s]; - j_f += p; - j_s--; - } - work[i] = r; - } - // Clean up after in-place operation. - if(work != out) - { - for(i = 0;i < outN;i++) - out[i] = work[i]; - } -} - - /*************************** *** File storage output *** ***************************/ @@ -1065,9 +834,9 @@ static void ResampleHrirs(const uint rate, HrirDataT *hData) uint channels = (hData->mChannelType == CT_STEREO) ? 2 : 1; uint n = hData->mIrPoints; uint ti, fi, ei, ai; - ResamplerT rs; + PPhaseResampler rs; - ResamplerSetup(&rs, hData->mIrRate, rate); + rs.init(hData->mIrRate, rate); for(fi = 0;fi < hData->mFdCount;fi++) { for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++) @@ -1076,7 +845,7 @@ static void ResampleHrirs(const uint rate, HrirDataT *hData) { HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai]; for(ti = 0;ti < channels;ti++) - ResamplerRun(&rs, n, azd->mIrs[ti], n, azd->mIrs[ti]); + rs.process(n, azd->mIrs[ti], n, azd->mIrs[ti]); } } } diff --git a/utils/makemhr/makemhr.h b/utils/makemhr/makemhr.h index ba19efbe..89607cc0 100644 --- a/utils/makemhr/makemhr.h +++ b/utils/makemhr/makemhr.h @@ -4,6 +4,8 @@ #include #include +#include "polyphase_resampler.h" + // The maximum path length used when processing filenames. #define MAX_PATH_LEN (256) @@ -111,16 +113,6 @@ void FftForward(const uint n, complex_d *inout); void FftInverse(const uint n, complex_d *inout); -// The resampler metrics and FIR filter. -struct ResamplerT { - uint mP, mQ, mM, mL; - std::vector mF; -}; - -void ResamplerSetup(ResamplerT *rs, const uint srcRate, const uint dstRate); -void ResamplerRun(ResamplerT *rs, const uint inN, const double *in, const uint outN, double *out); - - // Performs linear interpolation. inline double Lerp(const double a, const double b, const double f) { return a + f * (b - a); } -- cgit v1.2.3 From 4eb9a0b835dda89a833d9f9d00ed94f46b471e9f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 28 Nov 2019 12:57:42 -0800 Subject: Don't keep a resampled default HRTF --- CMakeLists.txt | 3 +-- alc/hrtf.cpp | 7 ------- hrtf/default-48000.mhr | Bin 80354 -> 0 bytes 3 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 hrtf/default-48000.mhr (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index a4300f65..1f7f22b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1123,7 +1123,6 @@ if(ALSOFT_EMBED_HRTF_DATA) ENDMACRO() make_hrtf_header(default-44100.mhr "hrtf_default_44100") - make_hrtf_header(default-48000.mhr "hrtf_default_48000") endif() ADD_CUSTOM_COMMAND(OUTPUT "${OpenAL_BINARY_DIR}/bsinc_inc.h" @@ -1362,7 +1361,7 @@ ENDIF() # Install HRTF definitions IF(ALSOFT_HRTF_DEFS) - INSTALL(FILES hrtf/default-44100.mhr hrtf/default-48000.mhr + INSTALL(FILES hrtf/default-44100.mhr DESTINATION ${CMAKE_INSTALL_DATADIR}/openal/hrtf) MESSAGE(STATUS "Installing HRTF definitions") MESSAGE(STATUS "") diff --git a/alc/hrtf.cpp b/alc/hrtf.cpp index b66bbe7f..f8df522b 100644 --- a/alc/hrtf.cpp +++ b/alc/hrtf.cpp @@ -1176,7 +1176,6 @@ void AddBuiltInEntry(const std::string &dispname, ALuint residx) #define IDR_DEFAULT_44100_MHR 1 -#define IDR_DEFAULT_48000_MHR 2 using ResData = al::span; #ifndef ALSOFT_EMBED_HRTF_DATA @@ -1187,14 +1186,11 @@ ResData GetResource(int /*name*/) #else #include "default-44100.mhr.h" -#include "default-48000.mhr.h" ResData GetResource(int name) { if(name == IDR_DEFAULT_44100_MHR) return {reinterpret_cast(hrtf_default_44100), sizeof(hrtf_default_44100)}; - if(name == IDR_DEFAULT_48000_MHR) - return {reinterpret_cast(hrtf_default_48000), sizeof(hrtf_default_48000)}; return ResData{}; } #endif @@ -1249,9 +1245,6 @@ al::vector EnumerateHrtf(const char *devname) if(!GetResource(IDR_DEFAULT_44100_MHR).empty()) AddBuiltInEntry("Built-In 44100hz", IDR_DEFAULT_44100_MHR); - - if(!GetResource(IDR_DEFAULT_48000_MHR).empty()) - AddBuiltInEntry("Built-In 48000hz", IDR_DEFAULT_48000_MHR); } al::vector list; diff --git a/hrtf/default-48000.mhr b/hrtf/default-48000.mhr deleted file mode 100644 index 66fd4f56..00000000 Binary files a/hrtf/default-48000.mhr and /dev/null differ -- cgit v1.2.3 From b310e09f596b0325da774a543de086b44d98523f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 28 Nov 2019 15:47:16 -0800 Subject: Rename the default HRTF --- CMakeLists.txt | 6 +++--- alc/hrtf.cpp | 12 ++++++------ hrtf/Default HRTF.mhr | Bin 0 -> 80354 bytes hrtf/default-44100.mhr | Bin 80354 -> 0 bytes 4 files changed, 9 insertions(+), 9 deletions(-) create mode 100644 hrtf/Default HRTF.mhr delete mode 100644 hrtf/default-44100.mhr (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f7f22b0..4bc0aeeb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1112,7 +1112,7 @@ option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library foot if(ALSOFT_EMBED_HRTF_DATA) MACRO(make_hrtf_header FILENAME VARNAME) SET(infile "${OpenAL_SOURCE_DIR}/hrtf/${FILENAME}") - SET(outfile "${OpenAL_BINARY_DIR}/${FILENAME}.h") + SET(outfile "${OpenAL_BINARY_DIR}/${VARNAME}.h") ADD_CUSTOM_COMMAND(OUTPUT "${outfile}" COMMAND "${BIN2H_COMMAND}" "${infile}" "${outfile}" ${VARNAME} @@ -1122,7 +1122,7 @@ if(ALSOFT_EMBED_HRTF_DATA) SET(ALC_OBJS ${ALC_OBJS} "${outfile}") ENDMACRO() - make_hrtf_header(default-44100.mhr "hrtf_default_44100") + make_hrtf_header("Default HRTF.mhr" "hrtf_default") endif() ADD_CUSTOM_COMMAND(OUTPUT "${OpenAL_BINARY_DIR}/bsinc_inc.h" @@ -1361,7 +1361,7 @@ ENDIF() # Install HRTF definitions IF(ALSOFT_HRTF_DEFS) - INSTALL(FILES hrtf/default-44100.mhr + INSTALL(FILES "hrtf/Default HRTF.mhr" DESTINATION ${CMAKE_INSTALL_DATADIR}/openal/hrtf) MESSAGE(STATUS "Installing HRTF definitions") MESSAGE(STATUS "") diff --git a/alc/hrtf.cpp b/alc/hrtf.cpp index 041451bb..da2ac8b1 100644 --- a/alc/hrtf.cpp +++ b/alc/hrtf.cpp @@ -1172,7 +1172,7 @@ void AddBuiltInEntry(const std::string &dispname, ALuint residx) } -#define IDR_DEFAULT_44100_MHR 1 +#define IDR_DEFAULT_HRTF_MHR 1 using ResData = al::span; #ifndef ALSOFT_EMBED_HRTF_DATA @@ -1182,12 +1182,12 @@ ResData GetResource(int /*name*/) #else -#include "default-44100.mhr.h" +#include "hrtf_default.h" ResData GetResource(int name) { - if(name == IDR_DEFAULT_44100_MHR) - return {reinterpret_cast(hrtf_default_44100), sizeof(hrtf_default_44100)}; + if(name == IDR_DEFAULT_HRTF_MHR) + return {reinterpret_cast(hrtf_default), sizeof(hrtf_default)}; return ResData{}; } #endif @@ -1240,8 +1240,8 @@ al::vector EnumerateHrtf(const char *devname) for(const auto &fname : SearchDataFiles(".mhr", "openal/hrtf")) AddFileEntry(fname); - if(!GetResource(IDR_DEFAULT_44100_MHR).empty()) - AddBuiltInEntry("Built-In 44100hz", IDR_DEFAULT_44100_MHR); + if(!GetResource(IDR_DEFAULT_HRTF_MHR).empty()) + AddBuiltInEntry("Built-In HRTF", IDR_DEFAULT_HRTF_MHR); } al::vector list; diff --git a/hrtf/Default HRTF.mhr b/hrtf/Default HRTF.mhr new file mode 100644 index 00000000..f7992e13 Binary files /dev/null and b/hrtf/Default HRTF.mhr differ diff --git a/hrtf/default-44100.mhr b/hrtf/default-44100.mhr deleted file mode 100644 index f7992e13..00000000 Binary files a/hrtf/default-44100.mhr and /dev/null differ -- cgit v1.2.3 From 4867f93a34226be5d7d78e2f58f1413fc88816e4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 11 Dec 2019 00:48:03 -0800 Subject: Move duplicate SOFA-related functions to a reusable library --- CMakeLists.txt | 17 ++- utils/makemhr/loadsofa.cpp | 260 +++------------------------------------- utils/sofa-info.cpp | 270 +---------------------------------------- utils/sofa-support.cpp | 292 +++++++++++++++++++++++++++++++++++++++++++++ utils/sofa-support.h | 30 +++++ 5 files changed, 355 insertions(+), 514 deletions(-) create mode 100644 utils/sofa-support.cpp create mode 100644 utils/sofa-support.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 4bc0aeeb..1d847fe9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1393,6 +1393,15 @@ IF(ALSOFT_UTILS) find_package(MySOFA) if(MYSOFA_FOUND) + set(SOFA_SUPPORT_SRCS + utils/sofa-support.cpp + utils/sofa-support.h) + add_library(sofa-support STATIC EXCLUDE_FROM_ALL ${SOFA_SUPPORT_SRCS}) + target_compile_definitions(sofa-support PRIVATE ${CPP_DEFS}) + target_include_directories(sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common) + target_compile_options(sofa-support PRIVATE ${C_FLAGS}) + target_link_libraries(sofa-support PUBLIC common MySOFA::MySOFA PRIVATE ${LINKER_FLAGS}) + set(MAKEMHR_SRCS utils/makemhr/loaddef.cpp utils/makemhr/loaddef.h @@ -1406,17 +1415,17 @@ IF(ALSOFT_UTILS) add_executable(makemhr ${MAKEMHR_SRCS}) target_compile_definitions(makemhr PRIVATE ${CPP_DEFS}) target_include_directories(makemhr - PRIVATE ${OpenAL_SOURCE_DIR}/common ${OpenAL_BINARY_DIR}) + PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/utils) target_compile_options(makemhr PRIVATE ${C_FLAGS}) - target_link_libraries(makemhr PRIVATE common ${LINKER_FLAGS} MySOFA::MySOFA) + target_link_libraries(makemhr PRIVATE ${LINKER_FLAGS} sofa-support) set(UTIL_TARGETS ${UTIL_TARGETS} makemhr) set(SOFAINFO_SRCS utils/sofa-info.cpp) add_executable(sofa-info ${SOFAINFO_SRCS}) target_compile_definitions(sofa-info PRIVATE ${CPP_DEFS}) - target_include_directories(sofa-info PRIVATE ${OpenAL_SOURCE_DIR}/common) + target_include_directories(sofa-info PRIVATE ${OpenAL_SOURCE_DIR}/utils) target_compile_options(sofa-info PRIVATE ${C_FLAGS}) - target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} MySOFA::MySOFA) + target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} sofa-support) endif() IF(ALSOFT_INSTALL) diff --git a/utils/makemhr/loadsofa.cpp b/utils/makemhr/loadsofa.cpp index 886bd6ff..81df8aa4 100644 --- a/utils/makemhr/loadsofa.cpp +++ b/utils/makemhr/loadsofa.cpp @@ -24,11 +24,10 @@ #include "loadsofa.h" #include -#include #include +#include #include #include -#include #include #include #include @@ -37,148 +36,13 @@ #include #include "makemhr.h" +#include "polyphase_resampler.h" +#include "sofa-support.h" #include "mysofa.h" -using namespace std::placeholders; - -using double3 = std::array; - -static const char *SofaErrorStr(int err) -{ - switch(err) - { - case MYSOFA_OK: return "OK"; - case MYSOFA_INVALID_FORMAT: return "Invalid format"; - case MYSOFA_UNSUPPORTED_FORMAT: return "Unsupported format"; - case MYSOFA_INTERNAL_ERROR: return "Internal error"; - case MYSOFA_NO_MEMORY: return "Out of memory"; - case MYSOFA_READ_ERROR: return "Read error"; - } - return "Unknown"; -} - - -/* Produces a sorted array of unique elements from a particular axis of the - * triplets array. The filters are used to focus on particular coordinates - * of other axes as necessary. The epsilons are used to constrain the - * equality of unique elements. - */ -static std::vector GetUniquelySortedElems(const std::vector &aers, - const uint axis, const double *const (&filters)[3], const double (&epsilons)[3]) -{ - std::vector elems; - for(const double3 &aer : aers) - { - const double elem{aer[axis]}; - - uint j; - for(j = 0;j < 3;j++) - { - if(filters[j] && std::abs(aer[j] - *filters[j]) > epsilons[j]) - break; - } - if(j < 3) - continue; - - auto iter = elems.begin(); - for(;iter != elems.end();++iter) - { - const double delta{elem - *iter}; - if(delta > epsilons[axis]) continue; - if(delta >= -epsilons[axis]) break; - - iter = elems.emplace(iter, elem); - break; - } - if(iter == elems.end()) - elems.emplace_back(elem); - } - return elems; -} - -/* Given a list of azimuths, this will produce the smallest step size that can - * uniformly cover the list. Ideally this will be over half, but in degenerate - * cases this can fall to a minimum of 5 (the lower limit). - */ -static double GetUniformAzimStep(const double epsilon, const std::vector &elems) -{ - if(elems.size() < 5) return 0.0; - - /* Get the maximum count possible, given the first two elements. It would - * be impossible to have more than this since the first element must be - * included. - */ - uint count{static_cast(std::ceil(360.0 / (elems[1]-elems[0])))}; - count = std::min(count, uint{MAX_AZ_COUNT}); - - for(;count >= 5;--count) - { - /* Given the stepping value for this number of elements, check each - * multiple to ensure there's a matching element. - */ - const double step{360.0 / count}; - bool good{true}; - size_t idx{1u}; - for(uint mult{1u};mult < count && good;++mult) - { - const double target{step*mult + elems[0]}; - while(idx < elems.size() && target-elems[idx] > epsilon) - ++idx; - good &= (idx < elems.size()) && !(std::abs(target-elems[idx++]) > epsilon); - } - if(good) - return step; - } - return 0.0; -} - -/* Given a list of elevations, this will produce the smallest step size that - * can uniformly cover the list. Ideally this will be over half, but in - * degenerate cases this can fall to a minimum of 5 (the lower limit). - */ -static double GetUniformElevStep(const double epsilon, std::vector &elems) -{ - if(elems.size() < 5) return 0.0; - - /* Reverse the elevations so it increments starting with -90 (flipped from - * +90). This makes it easier to work out a proper stepping value. - */ - std::reverse(elems.begin(), elems.end()); - for(auto &v : elems) v *= -1.0; - - uint count{static_cast(std::ceil(180.0 / (elems[1]-elems[0])))}; - count = std::min(count, uint{MAX_EV_COUNT-1u}); - - double ret{0.0}; - for(;count >= 5;--count) - { - const double step{180.0 / count}; - bool good{true}; - size_t idx{1u}; - /* Elevations don't need to match all multiples if there's not enough - * elements to check. Missing elevations can be synthesized. - */ - for(uint mult{1u};mult <= count && idx < elems.size() && good;++mult) - { - const double target{step*mult + elems[0]}; - while(idx < elems.size() && target-elems[idx] > epsilon) - ++idx; - good &= !(idx < elems.size()) || !(std::abs(target-elems[idx++]) > epsilon); - } - if(good) - { - ret = step; - break; - } - } - /* Re-reverse the elevations to restore the correct order. */ - for(auto &v : elems) v *= -1.0; - std::reverse(elems.begin(), elems.end()); - - return ret; -} +using uint = unsigned int; /* Attempts to produce a compatible layout. Most data sets tend to be * uniform and have the same major axis as used by OpenAL Soft's HRTF model. @@ -190,16 +54,8 @@ static bool PrepareLayout(const uint m, const float *xyzs, HrirDataT *hData) { fprintf(stdout, "Detecting compatible layout...\n"); - auto aers = std::vector(m, double3{}); - for(uint i{0u};i < m;++i) - { - float vals[3]{xyzs[i*3], xyzs[i*3 + 1], xyzs[i*3 + 2]}; - mysofa_c2s(&vals[0]); - aers[i] = {vals[0], vals[1], vals[2]}; - } - - auto radii = GetUniquelySortedElems(aers, 2, {}, {0.1, 0.1, 0.001}); - if(radii.size() > MAX_FD_COUNT) + auto fds = GetCompatibleLayout(m, xyzs); + if(fds.size() > MAX_FD_COUNT) { fprintf(stdout, "Incompatible layout (inumerable radii).\n"); return false; @@ -209,104 +65,24 @@ static bool PrepareLayout(const uint m, const float *xyzs, HrirDataT *hData) uint evCounts[MAX_FD_COUNT]{}; auto azCounts = std::vector(MAX_FD_COUNT*MAX_EV_COUNT, 0u); - auto dist_end = std::copy_if(radii.cbegin(), radii.cend(), std::begin(distances), - std::bind(std::greater_equal{}, _1, hData->mRadius)); - auto fdCount = static_cast(std::distance(std::begin(distances), dist_end)); - - uint ir_total{0u}; - for(uint fi{0u};fi < fdCount;) + uint fi{0u}, ir_total{0u}; + for(const auto &field : fds) { - const double dist{distances[fi]}; - auto elevs = GetUniquelySortedElems(aers, 1, {nullptr, nullptr, &dist}, - {0.1, 0.1, 0.001}); - - /* Remove elevations that don't have a valid set of azimuths. */ - auto invalid_elev = [&dist,&aers](const double ev) -> bool - { - auto azims = GetUniquelySortedElems(aers, 0, {nullptr, &ev, &dist}, {0.1, 0.1, 0.001}); - - if(std::abs(ev) > 89.999) - return azims.size() != 1; - if(azims.empty() || !(std::abs(azims[0]) < 0.1)) - return true; - return GetUniformAzimStep(0.1, azims) <= 0.0; - }; - elevs.erase(std::remove_if(elevs.begin(), elevs.end(), invalid_elev), elevs.end()); - - double step{GetUniformElevStep(0.1, elevs)}; - if(step <= 0.0) - { - fprintf(stdout, "Non-uniform elevations on field distance %f.\n", dist); - std::copy(&distances[fi+1], &distances[fdCount], &distances[fi]); - --fdCount; - continue; - } - - uint evStart{0u}; - for(uint ei{0u};ei < elevs.size();ei++) - { - if(!(elevs[ei] < 0.0)) - { - fprintf(stdout, "Too many missing elevations on field distance %f.\n", dist); - return false; - } - - double eif{(90.0+elevs[ei]) / step}; - const double ev_start{std::round(eif)}; + distances[fi] = field.mDistance; + evCounts[fi] = field.mEvCount; - if(std::abs(eif - ev_start) < (0.1/step)) - { - evStart = static_cast(ev_start); - break; - } - } - - const auto evCount = static_cast(std::round(180.0 / step)) + 1; - if(evCount < 5) + for(uint ei{0u};ei < field.mEvStart;ei++) + azCounts[fi*MAX_EV_COUNT + ei] = field.mAzCounts[field.mEvCount-ei-1]; + for(uint ei{field.mEvStart};ei < field.mEvCount;ei++) { - fprintf(stdout, "Too few uniform elevations on field distance %f.\n", dist); - std::copy(&distances[fi+1], &distances[fdCount], &distances[fi]); - --fdCount; - continue; - } - evCounts[fi] = evCount; - - for(uint ei{evStart};ei < evCount;ei++) - { - const double ev{-90.0 + ei*180.0/(evCount - 1)}; - auto azims = GetUniquelySortedElems(aers, 0, {nullptr, &ev, &dist}, {0.1, 0.1, 0.001}); - - uint azCount; - if(ei == 0 || ei == (evCount-1)) - { - if(azims.size() != 1) - { - fprintf(stdout, "Non-singular poles on field distance %f.\n", dist); - return false; - } - azCount = 1u; - } - else - { - step = GetUniformAzimStep(0.1, azims); - if(step <= 0.0) - { - fprintf(stdout, "Non-uniform azimuths on elevation %f, field distance %f.\n", - ev, dist); - return false; - } - azCount = static_cast(std::round(360.0 / step)); - } - azCounts[fi*MAX_EV_COUNT + ei] = azCount; - ir_total += azCount; + azCounts[fi*MAX_EV_COUNT + ei] = field.mAzCounts[ei]; + ir_total += field.mAzCounts[ei]; } - for(uint ei{0u};ei < evStart;ei++) - azCounts[fi*MAX_EV_COUNT + ei] = azCounts[fi*MAX_EV_COUNT + evCount - ei - 1]; ++fi; } fprintf(stdout, "Using %u of %u IRs.\n", ir_total, m); - return PrepareHrirData(fdCount, distances, evCounts, azCounts.data(), hData) != 0; + return PrepareHrirData(fi, distances, evCounts, azCounts.data(), hData) != 0; } @@ -575,10 +351,6 @@ static bool LoadResponses(MYSOFA_HRTF *sofaHrtf, HrirDataT *hData) return load_future.get(); } -struct MySofaHrtfDeleter { - void operator()(MYSOFA_HRTF *ptr) { mysofa_free(ptr); } -}; -using MySofaHrtfPtr = std::unique_ptr; bool LoadSofaFile(const char *filename, const uint fftSize, const uint truncSize, const ChannelModeT chanMode, HrirDataT *hData) diff --git a/utils/sofa-info.cpp b/utils/sofa-info.cpp index 81136334..6ab71306 100644 --- a/utils/sofa-info.cpp +++ b/utils/sofa-info.cpp @@ -23,46 +23,16 @@ #include -#include -#include -#include #include #include -#include - +#include "sofa-support.h" #include "win_main_utf8.h" +#include "mysofa.h" -using uint = unsigned int; -using double3 = std::array; - -struct MySofaDeleter { - void operator()(MYSOFA_HRTF *sofa) { mysofa_free(sofa); } -}; -using MySofaHrtfPtr = std::unique_ptr; - -// Per-field measurement info. -struct HrirFdT { - double mDistance{0.0}; - uint mEvCount{0u}; - uint mEvStart{0u}; - std::vector mAzCounts; -}; -static const char *SofaErrorStr(int err) -{ - switch(err) - { - case MYSOFA_OK: return "OK"; - case MYSOFA_INVALID_FORMAT: return "Invalid format"; - case MYSOFA_UNSUPPORTED_FORMAT: return "Unsupported format"; - case MYSOFA_INTERNAL_ERROR: return "Internal error"; - case MYSOFA_NO_MEMORY: return "Out of memory"; - case MYSOFA_READ_ERROR: return "Read error"; - } - return "Unknown"; -} +using uint = unsigned int; static void PrintSofaAttributes(const char *prefix, struct MYSOFA_ATTRIBUTE *attribute) { @@ -76,131 +46,10 @@ static void PrintSofaAttributes(const char *prefix, struct MYSOFA_ATTRIBUTE *att static void PrintSofaArray(const char *prefix, struct MYSOFA_ARRAY *array) { PrintSofaAttributes(prefix, array->attributes); - for(uint i{0u};i < array->elements;i++) fprintf(stdout, "%s[%u]: %.6f\n", prefix, i, array->values[i]); } -/* Produces a sorted array of unique elements from a particular axis of the - * triplets array. The filters are used to focus on particular coordinates - * of other axes as necessary. The epsilons are used to constrain the - * equality of unique elements. - */ -static std::vector GetUniquelySortedElems(const std::vector &aers, - const uint axis, const double *const (&filters)[3], const double (&epsilons)[3]) -{ - std::vector elems; - for(const double3 &aer : aers) - { - const double elem{aer[axis]}; - - uint j; - for(j = 0;j < 3;j++) - { - if(filters[j] && std::abs(aer[j] - *filters[j]) > epsilons[j]) - break; - } - if(j < 3) - continue; - - auto iter = elems.begin(); - for(;iter != elems.end();++iter) - { - const double delta{elem - *iter}; - if(delta > epsilons[axis]) continue; - if(delta >= -epsilons[axis]) break; - - iter = elems.emplace(iter, elem); - break; - } - if(iter == elems.end()) - elems.emplace_back(elem); - } - return elems; -} - -/* Given a list of azimuths, this will produce the smallest step size that can - * uniformly cover the list. Ideally this will be over half, but in degenerate - * cases this can fall to a minimum of 5 (the lower limit). - */ -static double GetUniformAzimStep(const double epsilon, const std::vector &elems) -{ - if(elems.size() < 5) return 0.0; - - /* Get the maximum count possible, given the first two elements. It would - * be impossible to have more than this since the first element must be - * included. - */ - uint count{static_cast(std::ceil(360.0 / (elems[1]-elems[0])))}; - count = std::min(count, 255u); - - for(;count >= 5;--count) - { - /* Given the stepping value for this number of elements, check each - * multiple to ensure there's a matching element. - */ - const double step{360.0 / count}; - bool good{true}; - size_t idx{1u}; - for(uint mult{1u};mult < count && good;++mult) - { - const double target{step*mult + elems[0]}; - while(idx < elems.size() && target-elems[idx] > epsilon) - ++idx; - good &= (idx < elems.size()) && !(std::abs(target-elems[idx++]) > epsilon); - } - if(good) - return step; - } - return 0.0; -} - -/* Given a list of elevations, this will produce the smallest step size that - * can uniformly cover the list. Ideally this will be over half, but in - * degenerate cases this can fall to a minimum of 5 (the lower limit). - */ -static double GetUniformElevStep(const double epsilon, std::vector &elems) -{ - if(elems.size() < 5) return 0.0; - - /* Reverse the elevations so it increments starting with -90 (flipped from - * +90). This makes it easier to work out a proper stepping value. - */ - std::reverse(elems.begin(), elems.end()); - for(auto &v : elems) v *= -1.0; - - uint count{static_cast(std::ceil(180.0 / (elems[1]-elems[0])))}; - count = std::min(count, 255u); - - double ret{0.0}; - for(;count >= 5;--count) - { - const double step{180.0 / count}; - bool good{true}; - size_t idx{1u}; - /* Elevations don't need to match all multiples if there's not enough - * elements to check. Missing elevations can be synthesized. - */ - for(uint mult{1u};mult <= count && idx < elems.size() && good;++mult) - { - const double target{step*mult + elems[0]}; - while(idx < elems.size() && target-elems[idx] > epsilon) - ++idx; - good &= !(idx < elems.size()) || !(std::abs(target-elems[idx++]) > epsilon); - } - if(good) - { - ret = step; - break; - } - } - /* Re-reverse the elevations to restore the correct order. */ - for(auto &v : elems) v *= -1.0; - std::reverse(elems.begin(), elems.end()); - - return ret; -} - /* Attempts to produce a compatible layout. Most data sets tend to be * uniform and have the same major axis as used by OpenAL Soft's HRTF model. * This will remove outliers and produce a maximally dense layout when @@ -211,118 +60,7 @@ static void PrintCompatibleLayout(const uint m, const float *xyzs) { fputc('\n', stdout); - auto aers = std::vector(m, double3{}); - for(uint i{0u};i < m;++i) - { - float vals[3]{xyzs[i*3], xyzs[i*3 + 1], xyzs[i*3 + 2]}; - mysofa_c2s(&vals[0]); - aers[i] = {vals[0], vals[1], vals[2]}; - } - - auto radii = GetUniquelySortedElems(aers, 2, {}, {0.1, 0.1, 0.001}); - - auto fds = std::vector(radii.size()); - for(size_t fi{0u};fi < radii.size();fi++) - fds[fi].mDistance = radii[fi]; - - for(uint fi{0u};fi < fds.size();) - { - const double dist{fds[fi].mDistance}; - auto elevs = GetUniquelySortedElems(aers, 1, {nullptr, nullptr, &dist}, {0.1, 0.1, 0.001}); - - /* Remove elevations that don't have a valid set of azimuths. */ - auto invalid_elev = [&dist,&aers](const double ev) -> bool - { - auto azims = GetUniquelySortedElems(aers, 0, {nullptr, &ev, &dist}, {0.1, 0.1, 0.001}); - - if(std::abs(ev) > 89.999) - return azims.size() != 1; - if(azims.empty() || !(std::abs(azims[0]) < 0.1)) - return true; - return GetUniformAzimStep(0.1, azims) <= 0.0; - }; - elevs.erase(std::remove_if(elevs.begin(), elevs.end(), invalid_elev), elevs.end()); - - double step{GetUniformElevStep(0.1, elevs)}; - if(step <= 0.0) - { - if(elevs.empty()) - fprintf(stdout, "No usable elevations on field distance %f.\n", dist); - else - { - fprintf(stdout, "Non-uniform elevations on field distance %.3f.\nGot: %+.2f", dist, - elevs[0]); - for(size_t ei{1u};ei < elevs.size();++ei) - fprintf(stdout, ", %+.2f", elevs[ei]); - fputc('\n', stdout); - } - fds.erase(fds.begin() + static_cast(fi)); - continue; - } - - uint evStart{0u}; - for(uint ei{0u};ei < elevs.size();ei++) - { - if(!(elevs[ei] < 0.0)) - { - fprintf(stdout, "Too many missing elevations on field distance %f.\n", dist); - return; - } - - double eif{(90.0+elevs[ei]) / step}; - const double ev_start{std::round(eif)}; - - if(std::abs(eif - ev_start) < (0.1/step)) - { - evStart = static_cast(ev_start); - break; - } - } - - const auto evCount = static_cast(std::round(180.0 / step)) + 1; - if(evCount < 5) - { - fprintf(stdout, "Too few uniform elevations on field distance %f.\n", dist); - fds.erase(fds.begin() + static_cast(fi)); - continue; - } - - fds[fi].mEvCount = evCount; - fds[fi].mEvStart = evStart; - fds[fi].mAzCounts.resize(evCount); - auto &azCounts = fds[fi].mAzCounts; - - for(uint ei{evStart};ei < evCount;ei++) - { - double ev{-90.0 + ei*180.0/(evCount - 1)}; - auto azims = GetUniquelySortedElems(aers, 0, {nullptr, &ev, &dist}, {0.1, 0.1, 0.001}); - - if(ei == 0 || ei == (evCount-1)) - { - if(azims.size() != 1) - { - fprintf(stdout, "Non-singular poles on field distance %f.\n", dist); - return; - } - azCounts[ei] = 1; - } - else - { - step = GetUniformAzimStep(0.1, azims); - if(step <= 0.0) - { - fprintf(stdout, "Non-uniform azimuths on elevation %f, field distance %f.\n", - ev, dist); - return; - } - azCounts[ei] = static_cast(std::round(360.0f / step)); - } - } - - for(uint ei{0u};ei < evStart;ei++) - azCounts[ei] = azCounts[evCount - ei - 1]; - ++fi; - } + auto fds = GetCompatibleLayout(m, xyzs); if(fds.empty()) { fprintf(stdout, "No compatible field layouts in SOFA file.\n"); diff --git a/utils/sofa-support.cpp b/utils/sofa-support.cpp new file mode 100644 index 00000000..e37789d5 --- /dev/null +++ b/utils/sofa-support.cpp @@ -0,0 +1,292 @@ +/* + * SOFA utility methods for inspecting SOFA file metrics and determining HRTF + * utility compatible layouts. + * + * Copyright (C) 2018-2019 Christopher Fitzgerald + * Copyright (C) 2019 Christopher Robinson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Or visit: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +#include "sofa-support.h" + +#include + +#include +#include +#include +#include +#include + +#include "mysofa.h" + + +namespace { + +using uint = unsigned int; +using double3 = std::array; + + +/* Produces a sorted array of unique elements from a particular axis of the + * triplets array. The filters are used to focus on particular coordinates + * of other axes as necessary. The epsilons are used to constrain the + * equality of unique elements. + */ +std::vector GetUniquelySortedElems(const std::vector &aers, const uint axis, + const double *const (&filters)[3], const double (&epsilons)[3]) +{ + std::vector elems; + for(const double3 &aer : aers) + { + const double elem{aer[axis]}; + + uint j; + for(j = 0;j < 3;j++) + { + if(filters[j] && std::abs(aer[j] - *filters[j]) > epsilons[j]) + break; + } + if(j < 3) + continue; + + auto iter = elems.begin(); + for(;iter != elems.end();++iter) + { + const double delta{elem - *iter}; + if(delta > epsilons[axis]) continue; + if(delta >= -epsilons[axis]) break; + + iter = elems.emplace(iter, elem); + break; + } + if(iter == elems.end()) + elems.emplace_back(elem); + } + return elems; +} + +/* Given a list of azimuths, this will produce the smallest step size that can + * uniformly cover the list. Ideally this will be over half, but in degenerate + * cases this can fall to a minimum of 5 (the lower limit). + */ +double GetUniformAzimStep(const double epsilon, const std::vector &elems) +{ + if(elems.size() < 5) return 0.0; + + /* Get the maximum count possible, given the first two elements. It would + * be impossible to have more than this since the first element must be + * included. + */ + uint count{static_cast(std::ceil(360.0 / (elems[1]-elems[0])))}; + count = std::min(count, 255u); + + for(;count >= 5;--count) + { + /* Given the stepping value for this number of elements, check each + * multiple to ensure there's a matching element. + */ + const double step{360.0 / count}; + bool good{true}; + size_t idx{1u}; + for(uint mult{1u};mult < count && good;++mult) + { + const double target{step*mult + elems[0]}; + while(idx < elems.size() && target-elems[idx] > epsilon) + ++idx; + good &= (idx < elems.size()) && !(std::abs(target-elems[idx++]) > epsilon); + } + if(good) + return step; + } + return 0.0; +} + +/* Given a list of elevations, this will produce the smallest step size that + * can uniformly cover the list. Ideally this will be over half, but in + * degenerate cases this can fall to a minimum of 5 (the lower limit). + */ +double GetUniformElevStep(const double epsilon, std::vector &elems) +{ + if(elems.size() < 5) return 0.0; + + /* Reverse the elevations so it increments starting with -90 (flipped from + * +90). This makes it easier to work out a proper stepping value. + */ + std::reverse(elems.begin(), elems.end()); + for(auto &v : elems) v *= -1.0; + + uint count{static_cast(std::ceil(180.0 / (elems[1]-elems[0])))}; + count = std::min(count, 255u); + + double ret{0.0}; + for(;count >= 5;--count) + { + const double step{180.0 / count}; + bool good{true}; + size_t idx{1u}; + /* Elevations don't need to match all multiples if there's not enough + * elements to check. Missing elevations can be synthesized. + */ + for(uint mult{1u};mult <= count && idx < elems.size() && good;++mult) + { + const double target{step*mult + elems[0]}; + while(idx < elems.size() && target-elems[idx] > epsilon) + ++idx; + good &= !(idx < elems.size()) || !(std::abs(target-elems[idx++]) > epsilon); + } + if(good) + { + ret = step; + break; + } + } + /* Re-reverse the elevations to restore the correct order. */ + for(auto &v : elems) v *= -1.0; + std::reverse(elems.begin(), elems.end()); + + return ret; +} + +} // namespace + + +const char *SofaErrorStr(int err) +{ + switch(err) + { + case MYSOFA_OK: return "OK"; + case MYSOFA_INVALID_FORMAT: return "Invalid format"; + case MYSOFA_UNSUPPORTED_FORMAT: return "Unsupported format"; + case MYSOFA_INTERNAL_ERROR: return "Internal error"; + case MYSOFA_NO_MEMORY: return "Out of memory"; + case MYSOFA_READ_ERROR: return "Read error"; + } + return "Unknown"; +} + +std::vector GetCompatibleLayout(const size_t m, const float *xyzs) +{ + auto aers = std::vector(m, double3{}); + for(size_t i{0u};i < m;++i) + { + float vals[3]{xyzs[i*3], xyzs[i*3 + 1], xyzs[i*3 + 2]}; + mysofa_c2s(&vals[0]); + aers[i] = {vals[0], vals[1], vals[2]}; + } + + auto radii = GetUniquelySortedElems(aers, 2, {}, {0.1, 0.1, 0.001}); + std::vector fds; + fds.reserve(radii.size()); + + for(const double dist : radii) + { + auto elevs = GetUniquelySortedElems(aers, 1, {nullptr, nullptr, &dist}, {0.1, 0.1, 0.001}); + + /* Remove elevations that don't have a valid set of azimuths. */ + auto invalid_elev = [&dist,&aers](const double ev) -> bool + { + auto azims = GetUniquelySortedElems(aers, 0, {nullptr, &ev, &dist}, {0.1, 0.1, 0.001}); + + if(std::abs(ev) > 89.999) + return azims.size() != 1; + if(azims.empty() || !(std::abs(azims[0]) < 0.1)) + return true; + return GetUniformAzimStep(0.1, azims) <= 0.0; + }; + elevs.erase(std::remove_if(elevs.begin(), elevs.end(), invalid_elev), elevs.end()); + + double step{GetUniformElevStep(0.1, elevs)}; + if(step <= 0.0) + { + if(elevs.empty()) + fprintf(stdout, "No usable elevations on field distance %f.\n", dist); + else + { + fprintf(stdout, "Non-uniform elevations on field distance %.3f.\nGot: %+.2f", dist, + elevs[0]); + for(size_t ei{1u};ei < elevs.size();++ei) + fprintf(stdout, ", %+.2f", elevs[ei]); + fputc('\n', stdout); + } + continue; + } + + uint evStart{0u}; + for(uint ei{0u};ei < elevs.size();ei++) + { + if(!(elevs[ei] < 0.0)) + { + fprintf(stdout, "Too many missing elevations on field distance %f.\n", dist); + return fds; + } + + double eif{(90.0+elevs[ei]) / step}; + const double ev_start{std::round(eif)}; + + if(std::abs(eif - ev_start) < (0.1/step)) + { + evStart = static_cast(ev_start); + break; + } + } + + const auto evCount = static_cast(std::round(180.0 / step)) + 1; + if(evCount < 5) + { + fprintf(stdout, "Too few uniform elevations on field distance %f.\n", dist); + continue; + } + + SofaField field{}; + field.mDistance = dist; + field.mEvCount = evCount; + field.mEvStart = evStart; + field.mAzCounts.resize(evCount, 0u); + auto &azCounts = field.mAzCounts; + + for(uint ei{evStart};ei < evCount;ei++) + { + double ev{-90.0 + ei*180.0/(evCount - 1)}; + auto azims = GetUniquelySortedElems(aers, 0, {nullptr, &ev, &dist}, {0.1, 0.1, 0.001}); + + if(ei == 0 || ei == (evCount-1)) + { + if(azims.size() != 1) + { + fprintf(stdout, "Non-singular poles on field distance %f.\n", dist); + return fds; + } + azCounts[ei] = 1; + } + else + { + step = GetUniformAzimStep(0.1, azims); + if(step <= 0.0) + { + fprintf(stdout, "Non-uniform azimuths on elevation %f, field distance %f.\n", + ev, dist); + return fds; + } + azCounts[ei] = static_cast(std::round(360.0f / step)); + } + } + + fds.emplace_back(std::move(field)); + } + + return fds; +} diff --git a/utils/sofa-support.h b/utils/sofa-support.h new file mode 100644 index 00000000..1229f49d --- /dev/null +++ b/utils/sofa-support.h @@ -0,0 +1,30 @@ +#ifndef UTILS_SOFA_SUPPORT_H +#define UTILS_SOFA_SUPPORT_H + +#include +#include +#include + +#include "mysofa.h" + + +struct MySofaDeleter { + void operator()(MYSOFA_HRTF *sofa) { mysofa_free(sofa); } +}; +using MySofaHrtfPtr = std::unique_ptr; + +// Per-field measurement info. +struct SofaField { + using uint = unsigned int; + + double mDistance{0.0}; + uint mEvCount{0u}; + uint mEvStart{0u}; + std::vector mAzCounts; +}; + +const char *SofaErrorStr(int err); + +std::vector GetCompatibleLayout(const size_t m, const float *xyzs); + +#endif /* UTILS_SOFA_SUPPORT_H */ -- cgit v1.2.3 From 9fc2f8541bf5c8db1932a6c286c86a2c96cb3953 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 29 Dec 2019 21:03:28 -0800 Subject: Don't hardcode the system paths for searching frameworks --- CMakeLists.txt | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d847fe9..35739a53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -970,32 +970,25 @@ ENDIF() # Check CoreAudio backend OPTION(ALSOFT_REQUIRE_COREAUDIO "Require CoreAudio backend" OFF) -FIND_LIBRARY(COREAUDIO_FRAMEWORK - NAMES CoreAudio - PATHS /System/Library/Frameworks -) -IF(COREAUDIO_FRAMEWORK) +FIND_LIBRARY(COREAUDIO_FRAMEWORK NAMES CoreAudio) +FIND_PATH(AUDIOUNIT_INCLUDE_DIR NAMES AudioUnit/AudioUnit.h) +IF(COREAUDIO_FRAMEWORK AND AUDIOUNIT_INCLUDE_DIR) OPTION(ALSOFT_BACKEND_COREAUDIO "Enable CoreAudio backend" ON) IF(ALSOFT_BACKEND_COREAUDIO) SET(HAVE_COREAUDIO 1) SET(ALC_OBJS ${ALC_OBJS} alc/backends/coreaudio.cpp alc/backends/coreaudio.h) SET(BACKENDS "${BACKENDS} CoreAudio,") - SET(EXTRA_LIBS ${COREAUDIO_FRAMEWORK} ${EXTRA_LIBS}) - SET(EXTRA_LIBS /System/Library/Frameworks/AudioUnit.framework ${EXTRA_LIBS}) - SET(EXTRA_LIBS /System/Library/Frameworks/ApplicationServices.framework ${EXTRA_LIBS}) + SET(EXTRA_LIBS ${COREAUDIO_FRAMEWORK} /System/Library/Frameworks/AudioUnit.framework + /System/Library/Frameworks/ApplicationServices.framework ${EXTRA_LIBS}) # Some versions of OSX may need the AudioToolbox framework. Add it if # it's found. - FIND_LIBRARY(AUDIOTOOLBOX_LIBRARY - NAMES AudioToolbox - PATHS - ~/Library/Frameworks - /Library/Frameworks - /System/Library/Frameworks - ) + FIND_LIBRARY(AUDIOTOOLBOX_LIBRARY NAMES AudioToolbox) IF(AUDIOTOOLBOX_LIBRARY) SET(EXTRA_LIBS ${AUDIOTOOLBOX_LIBRARY} ${EXTRA_LIBS}) ENDIF() + + SET(INC_PATHS ${INC_PATHS} ${AUDIOUNIT_INCLUDE_DIR}) ENDIF() ENDIF() IF(ALSOFT_REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO) -- cgit v1.2.3 From f5e0eef34db3a3ab94b61a2f99f84f078ba947e7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 23 Jan 2020 23:28:45 -0800 Subject: Release 1.20.1 --- CMakeLists.txt | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 35739a53..4052c78f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,7 +115,7 @@ ENDIF() SET(LIB_MAJOR_VERSION "1") SET(LIB_MINOR_VERSION "20") -SET(LIB_REVISION "0") +SET(LIB_REVISION "1") SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}") SET(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0) diff --git a/appveyor.yml b/appveyor.yml index 81080828..ec808672 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 1.20.0.{build} +version: 1.20.1.{build} environment: matrix: -- cgit v1.2.3 From 3001d56d4b6c6daa63b8d789a7c094bbdf626974 Mon Sep 17 00:00:00 2001 From: Sergey Karchevsky Date: Sun, 26 Jan 2020 05:36:30 +0700 Subject: Set static MSVC runtim for cpp files when FORCE_STATIC_VCRT is ON. Fixes #384 --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 4052c78f..c43f95ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -199,7 +199,9 @@ IF(MSVC) IF(FORCE_STATIC_VCRT) FOREACH(flag_var CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO) + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) IF(${flag_var} MATCHES "/MD") STRING(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") ENDIF() -- cgit v1.2.3 From 642ef4edc914bd75d6e994ab2e42e03a87d1d8da Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 20 Feb 2020 17:53:09 -0800 Subject: Add a streaming example using a callback buffer --- CMakeLists.txt | 6 + examples/alstreamcb.cpp | 436 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 442 insertions(+) create mode 100644 examples/alstreamcb.cpp (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index c43f95ab..4d83203e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1492,6 +1492,12 @@ IF(ALSOFT_EXAMPLES) TARGET_LINK_LIBRARIES(alstream PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common) + ADD_EXECUTABLE(alstreamcb examples/alstreamcb.cpp) + TARGET_INCLUDE_DIRECTORIES(alstreamcb + PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + TARGET_LINK_LIBRARIES(alstreamcb + PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common) + ADD_EXECUTABLE(alreverb examples/alreverb.c) TARGET_INCLUDE_DIRECTORIES(alreverb PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) diff --git a/examples/alstreamcb.cpp b/examples/alstreamcb.cpp new file mode 100644 index 00000000..fbc3d02d --- /dev/null +++ b/examples/alstreamcb.cpp @@ -0,0 +1,436 @@ +/* + * OpenAL Callback-based Stream Example + * + * Copyright (c) 2020 by Chris Robinson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* This file contains a streaming audio player using a callback buffer. */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "SDL_sound.h" +#include "SDL_audio.h" +#include "SDL_stdinc.h" + +#include "AL/al.h" +#include "AL/alc.h" + +#include "common/alhelpers.h" + + +#ifndef SDL_AUDIO_MASK_BITSIZE +#define SDL_AUDIO_MASK_BITSIZE (0xFF) +#endif +#ifndef SDL_AUDIO_BITSIZE +#define SDL_AUDIO_BITSIZE(x) (x & SDL_AUDIO_MASK_BITSIZE) +#endif + + +#ifndef AL_SOFT_callback_buffer +#define AL_SOFT_callback_buffer +typedef unsigned int ALbitfieldSOFT; +#define AL_BUFFER_CALLBACK_FUNCTION_SOFT 0x19A0 +#define AL_BUFFER_CALLBACK_USER_PARAM_SOFT 0x19A1 +typedef ALsizei (AL_APIENTRY*LPALBUFFERCALLBACKTYPESOFT)(ALvoid *userptr, ALvoid *sampledata, ALsizei numsamples); +typedef void (AL_APIENTRY*LPALBUFFERCALLBACKSOFT)(ALuint buffer, ALenum format, ALsizei freq, LPALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr, ALbitfieldSOFT flags); +typedef void (AL_APIENTRY*LPALGETBUFFERPTRSOFT)(ALuint buffer, ALenum param, ALvoid **value); +typedef void (AL_APIENTRY*LPALGETBUFFER3PTRSOFT)(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3); +typedef void (AL_APIENTRY*LPALGETBUFFERPTRVSOFT)(ALuint buffer, ALenum param, ALvoid **values); +#endif + +namespace { + +using std::chrono::seconds; +using std::chrono::nanoseconds; + +LPALBUFFERCALLBACKSOFT alBufferCallbackSOFT; + +struct StreamPlayer { + /* A lockless ring-buffer (supports single-provider, single-consumer + * operation). + */ + std::unique_ptr mBufferData; + size_t mBufferDataSize{0}; + std::atomic mReadPos{0}; + std::atomic mWritePos{0}; + + /* The buffer to get the callback, and source to play with. */ + ALuint mBuffer{0}, mSource{0}; + size_t mStartOffset{0}; + + /* Handle for the audio file to decode. */ + Sound_Sample *mSample{nullptr}; + Uint32 mAvailableData{0}; + size_t mDecoderOffset{0}; + + /* The format of the callback samples. */ + ALenum mFormat; + ALsizei mSampleRate; + + StreamPlayer() + { + alGenBuffers(1, &mBuffer); + if(ALenum err{alGetError()}) + throw std::runtime_error{"alGenBuffers failed"}; + alGenSources(1, &mSource); + if(ALenum err{alGetError()}) + { + alDeleteBuffers(1, &mBuffer); + throw std::runtime_error{"alGenSources failed"}; + } + } + ~StreamPlayer() + { + alDeleteSources(1, &mSource); + alDeleteBuffers(1, &mBuffer); + if(mSample) + Sound_FreeSample(mSample); + } + + void close() + { + if(mSample) + { + alSourceRewind(mSource); + alSourcei(mSource, AL_BUFFER, 0); + Sound_FreeSample(mSample); + mSample = nullptr; + } + } + + bool open(const char *filename) + { + close(); + + /* Open the file in its normal format. */ + mSample = Sound_NewSampleFromFile(filename, nullptr, 0); + if(!mSample) + { + fprintf(stderr, "Could not open audio in %s\n", filename); + return false; + } + + /* Figure out the OpenAL format from the sample's format. */ + mFormat = AL_NONE; + if(mSample->actual.channels == 1) + { + if(mSample->actual.format == AUDIO_U8) + mFormat = AL_FORMAT_MONO8; + else if(mSample->actual.format == AUDIO_S16SYS) + mFormat = AL_FORMAT_MONO16; + } + else if(mSample->actual.channels == 2) + { + if(mSample->actual.format == AUDIO_U8) + mFormat = AL_FORMAT_STEREO8; + else if(mSample->actual.format == AUDIO_S16SYS) + mFormat = AL_FORMAT_STEREO16; + } + if(!mFormat) + { + fprintf(stderr, "Unsupported sample format: 0x%04x, %d channels\n", + mSample->actual.format, mSample->actual.channels); + Sound_FreeSample(mSample); + mSample = nullptr; + + return false; + } + mSampleRate = static_cast(mSample->actual.rate); + + const auto frame_size = Uint32{mSample->actual.channels} * + SDL_AUDIO_BITSIZE(mSample->actual.format) / 8; + + /* Set a 50ms decode buffer size. */ + Sound_SetBufferSize(mSample, static_cast(mSampleRate)*50/1000 * frame_size); + mAvailableData = 0; + + /* Set a 1s ring buffer size. */ + mBufferDataSize = static_cast(mSampleRate) * size_t{frame_size}; + mBufferData.reset(new ALbyte[mBufferDataSize]); + mReadPos.store(0, std::memory_order_relaxed); + mWritePos.store(0, std::memory_order_relaxed); + mDecoderOffset = 0; + + return true; + } + + /* The actual C-style callback just forwards to the non-static method. Not + * strictly needed and the compiler will optimize it to a normal function, + * but it allows the callback implementation to have a nice 'this' pointer + * with normal member access. + */ + static ALsizei AL_APIENTRY bufferCallbackC(void *userptr, void *data, ALsizei size) + { return static_cast(userptr)->bufferCallback(data, size); } + ALsizei bufferCallback(void *data, ALsizei size) + { + /* NOTE: The callback *MUST* be real-time safe! That means no blocking, + * no allocations or deallocations, no I/O, no page faults, or calls to + * functions that could do these things (this includes calling to + * libraries like SDL_sound, libsndfile, ffmpeg, etc). Nothing should + * unexpectedly stall this call since the audio has to get to the + * device on time. + */ + ALsizei got{0}; + + size_t roffset{mReadPos.load(std::memory_order_acquire)}; + while(got < size) + { + /* If the write offset == read offset, there's nothing left in the + * ring-buffer. Break from the loop and give what has been written. + */ + const size_t woffset{mWritePos.load(std::memory_order_relaxed)}; + if(woffset == roffset) break; + + /* If the write offset is behind the read offset, the readable + * portion wrapped around. Just read up to the end of the buffer in + * that case, otherwise read up to the write offset. Also limit the + * amount to copy given how much is remaining to write. + */ + size_t todo{((woffset < roffset) ? mBufferDataSize : woffset) - roffset}; + todo = std::min(todo, static_cast(size-got)); + + /* Copy from the ring buffer to the provided output buffer. Wrap + * the resulting read offset if it reached the end of the ring- + * buffer. + */ + memcpy(data, &mBufferData[roffset], todo); + data = static_cast(data) + todo; + got += static_cast(todo); + + roffset += todo; + if(roffset == mBufferDataSize) + roffset = 0; + } + /* Finally, store the updated read offset, and return how many bytes + * have been written. + */ + mReadPos.store(roffset, std::memory_order_release); + + return got; + } + + bool prepare() + { + alBufferCallbackSOFT(mBuffer, mFormat, mSampleRate, bufferCallbackC, this, 0); + alSourcei(mSource, AL_BUFFER, static_cast(mBuffer)); + if(ALenum err{alGetError()}) + { + fprintf(stderr, "Failed to set callback: %s (0x%04x)\n", alGetString(err), err); + return false; + } + + mAvailableData = Sound_Decode(mSample); + if(!mAvailableData) + fprintf(stderr, "Failed to decode any samples: %s\n", Sound_GetError()); + return mAvailableData != 0; + } + + bool update() + { + constexpr int BadFlags{SOUND_SAMPLEFLAG_EOF | SOUND_SAMPLEFLAG_ERROR}; + + ALenum state; + ALint pos; + alGetSourcei(mSource, AL_SAMPLE_OFFSET, &pos); + alGetSourcei(mSource, AL_SOURCE_STATE, &state); + + size_t woffset{mWritePos.load(std::memory_order_acquire)}; + if(state != AL_INITIAL) + { + const auto frame_size = Uint32{mSample->actual.channels} * + SDL_AUDIO_BITSIZE(mSample->actual.format) / 8; + const size_t roffset{mReadPos.load(std::memory_order_relaxed)}; + const size_t readable{((woffset >= roffset) ? woffset : (mBufferDataSize+woffset)) - + roffset}; + /* For a stopped (underrun) source, the current playback offset is + * the current decoder offset excluding the readable buffered data. + * For a playing/paused source, it's the source's offset including + * the playback offset the source was started with. + */ + const size_t curtime{((state==AL_STOPPED) ? (mDecoderOffset-readable) / frame_size + : (static_cast(pos) + mStartOffset/frame_size)) / mSample->actual.rate}; + printf("\r%3zus (%3zu%% full)", curtime, readable * 100 / mBufferDataSize); + } + else + fputs("Starting...", stdout); + fflush(stdout); + + while(mAvailableData > 0) + { + const size_t roffset{mReadPos.load(std::memory_order_relaxed)}; + if(roffset > woffset) + { + /* Note that the ring buffer's writable space is one byte less + * than the available area because the write offset ending up + * at the read offset would be interpreted as being empty + * instead of full. + */ + const size_t writable{roffset-woffset-1}; + /* Don't copy the sample data if it can't all fit. */ + if(writable < mAvailableData) break; + + memcpy(&mBufferData[woffset], mSample->buffer, mAvailableData); + woffset += mAvailableData; + } + else + { + /* If the read offset is at or behind the write offset, the + * writeable area (might) wrap around. Make sure the sample + * data can fit, and calculate how much goes in front and in + * back. + */ + const size_t writable{mBufferDataSize+roffset-woffset-1}; + if(writable < mAvailableData) break; + + const size_t todo1{std::min(mAvailableData, mBufferDataSize-woffset)}; + const size_t todo2{mAvailableData - todo1}; + + memcpy(&mBufferData[woffset], mSample->buffer, todo1); + woffset += todo1; + if(woffset == mBufferDataSize) + { + woffset = 0; + if(todo2 > 0) + { + memcpy(&mBufferData[woffset], static_cast(mSample->buffer)+todo1, + todo2); + woffset += todo2; + } + } + } + mWritePos.store(woffset, std::memory_order_release); + mDecoderOffset += mAvailableData; + + if(!(mSample->flags&BadFlags)) + mAvailableData = Sound_Decode(mSample); + else + mAvailableData = 0; + } + + if(state != AL_PLAYING && state != AL_PAUSED) + { + /* If the source is not playing or paused, it either underrun + * (AL_STOPPED) or is just getting started (AL_INITIAL). If the + * ring buffer is empty, it's done, otherwise play the source with + * what's available. + */ + const size_t roffset{mReadPos.load(std::memory_order_relaxed)}; + const size_t readable{((woffset >= roffset) ? woffset : (mBufferDataSize+woffset)) - + roffset}; + if(readable == 0) + return false; + + /* Store the playback offset that the source will start reading + * from, so it can be tracked during playback. + */ + mStartOffset = mDecoderOffset - readable; + alSourcePlay(mSource); + if(alGetError() != AL_NO_ERROR) + return false; + } + return true; + } +}; + +} // namespace + +int main(int argc, char **argv) +{ + /* A simple RAII container for OpenAL and SDL_sound startup and shutdown. */ + struct AudioManager { + AudioManager(char ***argv_, int *argc_) + { + if(InitAL(argv_, argc_) != 0) + throw std::runtime_error{"Failed to initialize OpenAL"}; + Sound_Init(); + } + ~AudioManager() { Sound_Quit(); CloseAL(); } + }; + + /* Print out usage if no arguments were specified */ + if(argc < 2) + { + fprintf(stderr, "Usage: %s [-device ] \n", argv[0]); + return 1; + } + + argv++; argc--; + AudioManager almgr{&argv, &argc}; + + if(!alIsExtensionPresent("AL_SOFTX_callback_buffer")) + { + fprintf(stderr, "AL_SOFT_callback_buffer extension not available\n"); + return 1; + } + + alBufferCallbackSOFT = reinterpret_cast( + alGetProcAddress("alBufferCallbackSOFT")); + + ALCint refresh{25}; + alcGetIntegerv(alcGetContextsDevice(alcGetCurrentContext()), ALC_REFRESH, 1, &refresh); + + std::unique_ptr player{new StreamPlayer{}}; + + /* Play each file listed on the command line */ + for(int i{0};i < argc;++i) + { + if(!player->open(argv[i])) + continue; + + /* Get the name portion, without the path, for display. */ + const char *namepart{strrchr(argv[i], '/')}; + if(namepart || (namepart=strrchr(argv[i], '\\'))) + ++namepart; + else + namepart = argv[i]; + + printf("Playing: %s (%s, %dhz)\n", namepart, FormatName(player->mFormat), + player->mSampleRate); + fflush(stdout); + + if(!player->prepare()) + { + player->close(); + continue; + } + + while(player->update()) + std::this_thread::sleep_for(nanoseconds{seconds{1}} / refresh); + putc('\n', stdout); + + /* All done with this file. Close it and go to the next */ + player->close(); + } + /* All done. */ + printf("Done.\n"); + + return 0; +} -- cgit v1.2.3 From f56ef433d8bbf8709b559276bea79c6acb8167ef Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 20 Mar 2020 15:01:45 -0700 Subject: Move the FPUCtl methods to its own source --- CMakeLists.txt | 3 ++- al/auxeffectslot.cpp | 2 +- alc/alc.cpp | 2 +- alc/alu.cpp | 2 +- alc/converter.cpp | 2 +- alc/fpu_ctrl.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ alc/fpu_ctrl.h | 25 ++++++++++++++++++++++++ alc/fpu_modes.h | 25 ------------------------ alc/helpers.cpp | 45 ------------------------------------------- 9 files changed, 85 insertions(+), 75 deletions(-) create mode 100644 alc/fpu_ctrl.cpp create mode 100644 alc/fpu_ctrl.h delete mode 100644 alc/fpu_modes.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d83203e..35c748f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -622,7 +622,8 @@ SET(ALC_OBJS alc/filters/nfc.h alc/filters/splitter.cpp alc/filters/splitter.h - alc/fpu_modes.h + alc/fpu_ctrl.cpp + alc/fpu_ctrl.h alc/helpers.cpp alc/hrtf.cpp alc/hrtf.h diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp index dbd16928..6a7bbb46 100644 --- a/al/auxeffectslot.cpp +++ b/al/auxeffectslot.cpp @@ -42,7 +42,7 @@ #include "alspan.h" #include "alu.h" #include "effect.h" -#include "fpu_modes.h" +#include "fpu_ctrl.h" #include "inprogext.h" #include "logging.h" #include "opthelpers.h" diff --git a/alc/alc.cpp b/alc/alc.cpp index 0962276d..9ce1fa65 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -79,7 +79,7 @@ #include "effects/base.h" #include "filters/nfc.h" #include "filters/splitter.h" -#include "fpu_modes.h" +#include "fpu_ctrl.h" #include "hrtf.h" #include "inprogext.h" #include "intrusive_ptr.h" diff --git a/alc/alu.cpp b/alc/alu.cpp index ae65fbcb..d3a0a858 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -65,7 +65,7 @@ #include "filters/biquad.h" #include "filters/nfc.h" #include "filters/splitter.h" -#include "fpu_modes.h" +#include "fpu_ctrl.h" #include "hrtf.h" #include "inprogext.h" #include "mastering.h" diff --git a/alc/converter.cpp b/alc/converter.cpp index 553bad58..38b8bedb 100644 --- a/alc/converter.cpp +++ b/alc/converter.cpp @@ -11,7 +11,7 @@ #include "albyte.h" #include "alu.h" -#include "fpu_modes.h" +#include "fpu_ctrl.h" #include "mixer/defs.h" diff --git a/alc/fpu_ctrl.cpp b/alc/fpu_ctrl.cpp new file mode 100644 index 00000000..24021c7d --- /dev/null +++ b/alc/fpu_ctrl.cpp @@ -0,0 +1,54 @@ + +#include "config.h" + +#include "fpu_ctrl.h" + +#ifdef HAVE_INTRIN_H +#include +#endif +#ifdef HAVE_SSE_INTRINSICS +#include +#endif + +#include "cpu_caps.h" + + +FPUCtl::FPUCtl() +{ +#if defined(HAVE_SSE_INTRINSICS) + this->sse_state = _mm_getcsr(); + unsigned int sseState = this->sse_state; + sseState |= 0x8000; /* set flush-to-zero */ + sseState |= 0x0040; /* set denormals-are-zero */ + _mm_setcsr(sseState); + +#elif defined(__GNUC__) && defined(HAVE_SSE) + + if((CPUCapFlags&CPU_CAP_SSE)) + { + __asm__ __volatile__("stmxcsr %0" : "=m" (*&this->sse_state)); + unsigned int sseState = this->sse_state; + sseState |= 0x8000; /* set flush-to-zero */ + if((CPUCapFlags&CPU_CAP_SSE2)) + sseState |= 0x0040; /* set denormals-are-zero */ + __asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState)); + } +#endif + + this->in_mode = true; +} + +void FPUCtl::leave() +{ + if(!this->in_mode) return; + +#if defined(HAVE_SSE_INTRINSICS) + _mm_setcsr(this->sse_state); + +#elif defined(__GNUC__) && defined(HAVE_SSE) + + if((CPUCapFlags&CPU_CAP_SSE)) + __asm__ __volatile__("ldmxcsr %0" : : "m" (*&this->sse_state)); +#endif + this->in_mode = false; +} diff --git a/alc/fpu_ctrl.h b/alc/fpu_ctrl.h new file mode 100644 index 00000000..e89bdc29 --- /dev/null +++ b/alc/fpu_ctrl.h @@ -0,0 +1,25 @@ +#ifndef FPU_CTRL_H +#define FPU_CTRL_H + +class FPUCtl { +#if defined(HAVE_SSE_INTRINSICS) || (defined(__GNUC__) && defined(HAVE_SSE)) + unsigned int sse_state{}; +#endif + bool in_mode{}; + +public: + FPUCtl(); + /* HACK: 32-bit targets for GCC seem to have a problem here with certain + * noexcept methods (which destructors are) causing an internal compiler + * error. No idea why it's these methods specifically, but this is needed + * to get it to compile. + */ + ~FPUCtl() noexcept(false) { leave(); } + + FPUCtl(const FPUCtl&) = delete; + FPUCtl& operator=(const FPUCtl&) = delete; + + void leave(); +}; + +#endif /* FPU_CTRL_H */ diff --git a/alc/fpu_modes.h b/alc/fpu_modes.h deleted file mode 100644 index 5465e9cf..00000000 --- a/alc/fpu_modes.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef FPU_MODES_H -#define FPU_MODES_H - -class FPUCtl { -#if defined(HAVE_SSE_INTRINSICS) || (defined(__GNUC__) && defined(HAVE_SSE)) - unsigned int sse_state{}; -#endif - bool in_mode{}; - -public: - FPUCtl(); - /* HACK: 32-bit targets for GCC seem to have a problem here with certain - * noexcept methods (which destructors are) causing an internal compiler - * error. No idea why it's these methods specifically, but this is needed - * to get it to compile. - */ - ~FPUCtl() noexcept(false) { leave(); } - - FPUCtl(const FPUCtl&) = delete; - FPUCtl& operator=(const FPUCtl&) = delete; - - void leave(); -}; - -#endif /* FPU_MODES_H */ diff --git a/alc/helpers.cpp b/alc/helpers.cpp index 4ea94c7d..5006bc51 100644 --- a/alc/helpers.cpp +++ b/alc/helpers.cpp @@ -46,9 +46,6 @@ #ifdef HAVE_CPUID_H #include #endif -#ifdef HAVE_SSE_INTRINSICS -#include -#endif #ifdef HAVE_SYS_SYSCONF_H #include #endif @@ -75,7 +72,6 @@ #include "alstring.h" #include "compat.h" #include "cpu_caps.h" -#include "fpu_modes.h" #include "logging.h" #include "strutils.h" #include "vector.h" @@ -207,47 +203,6 @@ void FillCPUCaps(int capfilter) } -FPUCtl::FPUCtl() -{ -#if defined(HAVE_SSE_INTRINSICS) - this->sse_state = _mm_getcsr(); - unsigned int sseState = this->sse_state; - sseState |= 0x8000; /* set flush-to-zero */ - sseState |= 0x0040; /* set denormals-are-zero */ - _mm_setcsr(sseState); - -#elif defined(__GNUC__) && defined(HAVE_SSE) - - if((CPUCapFlags&CPU_CAP_SSE)) - { - __asm__ __volatile__("stmxcsr %0" : "=m" (*&this->sse_state)); - unsigned int sseState = this->sse_state; - sseState |= 0x8000; /* set flush-to-zero */ - if((CPUCapFlags&CPU_CAP_SSE2)) - sseState |= 0x0040; /* set denormals-are-zero */ - __asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState)); - } -#endif - - this->in_mode = true; -} - -void FPUCtl::leave() -{ - if(!this->in_mode) return; - -#if defined(HAVE_SSE_INTRINSICS) - _mm_setcsr(this->sse_state); - -#elif defined(__GNUC__) && defined(HAVE_SSE) - - if((CPUCapFlags&CPU_CAP_SSE)) - __asm__ __volatile__("ldmxcsr %0" : : "m" (*&this->sse_state)); -#endif - this->in_mode = false; -} - - #ifdef _WIN32 const PathNamePair &GetProcBinary() -- cgit v1.2.3 From 0d11de3e8d7f5df5bed784d102fc659809ccb9a9 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 20 Mar 2020 15:29:29 -0700 Subject: Move CPUCapFlags and FillCPUCaps to their own source --- CMakeLists.txt | 1 + alc/cpu_caps.cpp | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ alc/helpers.cpp | 133 ------------------------------------------------- 3 files changed, 148 insertions(+), 133 deletions(-) create mode 100644 alc/cpu_caps.cpp (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 35c748f1..599273a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -600,6 +600,7 @@ SET(ALC_OBJS alc/compat.h alc/converter.cpp alc/converter.h + alc/cpu_caps.cpp alc/cpu_caps.h alc/devformat.h alc/effects/base.h diff --git a/alc/cpu_caps.cpp b/alc/cpu_caps.cpp new file mode 100644 index 00000000..9470d7a0 --- /dev/null +++ b/alc/cpu_caps.cpp @@ -0,0 +1,147 @@ + +#include "config.h" + +#include "cpu_caps.h" + +#ifdef HAVE_INTRIN_H +#include +#endif +#ifdef HAVE_CPUID_H +#include +#endif + +#include +#include +#include + +#include "logging.h" + + +int CPUCapFlags{0}; + +namespace { + +#if defined(HAVE_GCC_GET_CPUID) \ + && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)) +using reg_type = unsigned int; +inline void get_cpuid(unsigned int f, reg_type *regs) +{ __get_cpuid(f, ®s[0], ®s[1], ®s[2], ®s[3]); } +#define CAN_GET_CPUID +#elif defined(HAVE_CPUID_INTRINSIC) \ + && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)) +using reg_type = int; +inline void get_cpuid(unsigned int f, reg_type *regs) +{ (__cpuid)(regs, f); } +#define CAN_GET_CPUID +#endif + +} // namespace + + +void FillCPUCaps(int capfilter) +{ + int caps{0}; + +/* FIXME: We really should get this for all available CPUs in case different + * CPUs have different caps (is that possible on one machine?). + */ +#ifdef CAN_GET_CPUID + union { + reg_type regs[4]; + char str[sizeof(reg_type[4])]; + } cpuinf[3]{}; + + get_cpuid(0, cpuinf[0].regs); + if(cpuinf[0].regs[0] == 0) + ERR("Failed to get CPUID\n"); + else + { + const reg_type maxfunc{cpuinf[0].regs[0]}; + + get_cpuid(0x80000000, cpuinf[0].regs); + const reg_type maxextfunc{cpuinf[0].regs[0]}; + + TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc); + + TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8); + if(maxextfunc >= 0x80000004) + { + get_cpuid(0x80000002, cpuinf[0].regs); + get_cpuid(0x80000003, cpuinf[1].regs); + get_cpuid(0x80000004, cpuinf[2].regs); + TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str); + } + + if(maxfunc >= 1) + { + get_cpuid(1, cpuinf[0].regs); + if((cpuinf[0].regs[3]&(1<<25))) + caps |= CPU_CAP_SSE; + if((caps&CPU_CAP_SSE) && (cpuinf[0].regs[3]&(1<<26))) + caps |= CPU_CAP_SSE2; + if((caps&CPU_CAP_SSE2) && (cpuinf[0].regs[2]&(1<<0))) + caps |= CPU_CAP_SSE3; + if((caps&CPU_CAP_SSE3) && (cpuinf[0].regs[2]&(1<<19))) + caps |= CPU_CAP_SSE4_1; + } + } +#else + /* Assume support for whatever's supported if we can't check for it */ +#if defined(HAVE_SSE4_1) +#warning "Assuming SSE 4.1 run-time support!" + caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1; +#elif defined(HAVE_SSE3) +#warning "Assuming SSE 3 run-time support!" + caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3; +#elif defined(HAVE_SSE2) +#warning "Assuming SSE 2 run-time support!" + caps |= CPU_CAP_SSE | CPU_CAP_SSE2; +#elif defined(HAVE_SSE) +#warning "Assuming SSE run-time support!" + caps |= CPU_CAP_SSE; +#endif +#endif +#ifdef HAVE_NEON + al::ifstream file{"/proc/cpuinfo"}; + if(!file.is_open()) + ERR("Failed to open /proc/cpuinfo, cannot check for NEON support\n"); + else + { + std::string features; + + auto getline = [](std::istream &f, std::string &output) -> bool + { + while(f.good() && f.peek() == '\n') + f.ignore(); + return std::getline(f, output) && !output.empty(); + }; + while(getline(file, features)) + { + if(features.compare(0, 10, "Features\t:", 10) == 0) + break; + } + file.close(); + + size_t extpos{9}; + while((extpos=features.find("neon", extpos+1)) != std::string::npos) + { + if((extpos == 0 || std::isspace(features[extpos-1])) && + (extpos+4 == features.length() || std::isspace(features[extpos+4]))) + { + caps |= CPU_CAP_NEON; + break; + } + } + } +#endif + + TRACE("Extensions:%s%s%s%s%s%s\n", + ((capfilter&CPU_CAP_SSE) ? ((caps&CPU_CAP_SSE) ? " +SSE" : " -SSE") : ""), + ((capfilter&CPU_CAP_SSE2) ? ((caps&CPU_CAP_SSE2) ? " +SSE2" : " -SSE2") : ""), + ((capfilter&CPU_CAP_SSE3) ? ((caps&CPU_CAP_SSE3) ? " +SSE3" : " -SSE3") : ""), + ((capfilter&CPU_CAP_SSE4_1) ? ((caps&CPU_CAP_SSE4_1) ? " +SSE4.1" : " -SSE4.1") : ""), + ((capfilter&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +NEON" : " -NEON") : ""), + ((!capfilter) ? " -none-" : "") + ); + CPUCapFlags = caps & capfilter; +} diff --git a/alc/helpers.cpp b/alc/helpers.cpp index 5006bc51..5b3733d0 100644 --- a/alc/helpers.cpp +++ b/alc/helpers.cpp @@ -40,12 +40,6 @@ #ifdef HAVE_DIRENT_H #include #endif -#ifdef HAVE_INTRIN_H -#include -#endif -#ifdef HAVE_CPUID_H -#include -#endif #ifdef HAVE_SYS_SYSCONF_H #include #endif @@ -71,138 +65,11 @@ #include "alspan.h" #include "alstring.h" #include "compat.h" -#include "cpu_caps.h" #include "logging.h" #include "strutils.h" #include "vector.h" -#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \ - defined(_M_IX86) || defined(_M_X64)) -using reg_type = unsigned int; -static inline void get_cpuid(unsigned int f, reg_type *regs) -{ __get_cpuid(f, ®s[0], ®s[1], ®s[2], ®s[3]); } -#define CAN_GET_CPUID -#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \ - defined(_M_IX86) || defined(_M_X64)) -using reg_type = int; -static inline void get_cpuid(unsigned int f, reg_type *regs) -{ (__cpuid)(regs, f); } -#define CAN_GET_CPUID -#endif - -int CPUCapFlags = 0; - -void FillCPUCaps(int capfilter) -{ - int caps = 0; - -/* FIXME: We really should get this for all available CPUs in case different - * CPUs have different caps (is that possible on one machine?). */ -#ifdef CAN_GET_CPUID - union { - reg_type regs[4]; - char str[sizeof(reg_type[4])]; - } cpuinf[3]{}; - - get_cpuid(0, cpuinf[0].regs); - if(cpuinf[0].regs[0] == 0) - ERR("Failed to get CPUID\n"); - else - { - unsigned int maxfunc = cpuinf[0].regs[0]; - unsigned int maxextfunc; - - get_cpuid(0x80000000, cpuinf[0].regs); - maxextfunc = cpuinf[0].regs[0]; - - TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc); - - TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8); - if(maxextfunc >= 0x80000004) - { - get_cpuid(0x80000002, cpuinf[0].regs); - get_cpuid(0x80000003, cpuinf[1].regs); - get_cpuid(0x80000004, cpuinf[2].regs); - TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str); - } - - if(maxfunc >= 1) - { - get_cpuid(1, cpuinf[0].regs); - if((cpuinf[0].regs[3]&(1<<25))) - caps |= CPU_CAP_SSE; - if((caps&CPU_CAP_SSE) && (cpuinf[0].regs[3]&(1<<26))) - caps |= CPU_CAP_SSE2; - if((caps&CPU_CAP_SSE2) && (cpuinf[0].regs[2]&(1<<0))) - caps |= CPU_CAP_SSE3; - if((caps&CPU_CAP_SSE3) && (cpuinf[0].regs[2]&(1<<19))) - caps |= CPU_CAP_SSE4_1; - } - } -#else - /* Assume support for whatever's supported if we can't check for it */ -#if defined(HAVE_SSE4_1) -#warning "Assuming SSE 4.1 run-time support!" - caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1; -#elif defined(HAVE_SSE3) -#warning "Assuming SSE 3 run-time support!" - caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3; -#elif defined(HAVE_SSE2) -#warning "Assuming SSE 2 run-time support!" - caps |= CPU_CAP_SSE | CPU_CAP_SSE2; -#elif defined(HAVE_SSE) -#warning "Assuming SSE run-time support!" - caps |= CPU_CAP_SSE; -#endif -#endif -#ifdef HAVE_NEON - al::ifstream file{"/proc/cpuinfo"}; - if(!file.is_open()) - ERR("Failed to open /proc/cpuinfo, cannot check for NEON support\n"); - else - { - std::string features; - - auto getline = [](std::istream &f, std::string &output) -> bool - { - while(f.good() && f.peek() == '\n') - f.ignore(); - return std::getline(f, output) && !output.empty(); - - }; - while(getline(file, features)) - { - if(features.compare(0, 10, "Features\t:", 10) == 0) - break; - } - file.close(); - - size_t extpos{9}; - while((extpos=features.find("neon", extpos+1)) != std::string::npos) - { - if((extpos == 0 || std::isspace(features[extpos-1])) && - (extpos+4 == features.length() || std::isspace(features[extpos+4]))) - { - caps |= CPU_CAP_NEON; - break; - } - } - } -#endif - - TRACE("Extensions:%s%s%s%s%s%s\n", - ((capfilter&CPU_CAP_SSE) ? ((caps&CPU_CAP_SSE) ? " +SSE" : " -SSE") : ""), - ((capfilter&CPU_CAP_SSE2) ? ((caps&CPU_CAP_SSE2) ? " +SSE2" : " -SSE2") : ""), - ((capfilter&CPU_CAP_SSE3) ? ((caps&CPU_CAP_SSE3) ? " +SSE3" : " -SSE3") : ""), - ((capfilter&CPU_CAP_SSE4_1) ? ((caps&CPU_CAP_SSE4_1) ? " +SSE4.1" : " -SSE4.1") : ""), - ((capfilter&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +NEON" : " -NEON") : ""), - ((!capfilter) ? " -none-" : "") - ); - CPUCapFlags = caps & capfilter; -} - - #ifdef _WIN32 const PathNamePair &GetProcBinary() -- cgit v1.2.3 From adf28d87aac0be1f21a38e5c5e720cae281989ce Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 21 Mar 2020 08:39:45 -0700 Subject: Remove the check for stat() It's POSIX-2001 standard --- CMakeLists.txt | 2 -- alc/backends/oss.cpp | 2 -- alc/backends/solaris.cpp | 2 -- config.h.in | 3 --- 4 files changed, 9 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 599273a5..592a7687 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -447,8 +447,6 @@ CHECK_SYMBOL_EXISTS(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN) CHECK_SYMBOL_EXISTS(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC) CHECK_SYMBOL_EXISTS(proc_pidpath libproc.h HAVE_PROC_PIDPATH) -CHECK_FUNCTION_EXISTS(stat HAVE_STAT) - IF(NOT WIN32) # We need pthreads outside of Windows, for semaphores. It's also used to # set the priority and name of threads, when possible. diff --git a/alc/backends/oss.cpp b/alc/backends/oss.cpp index ad1df7d6..a24744c2 100644 --- a/alc/backends/oss.cpp +++ b/alc/backends/oss.cpp @@ -674,10 +674,8 @@ void OSSBackendFactory::probe(DevProbe type, std::string *outnames) { auto add_device = [outnames](const DevMap &entry) -> void { -#ifdef HAVE_STAT struct stat buf; if(stat(entry.device_name.c_str(), &buf) == 0) -#endif { /* Includes null char. */ outnames->append(entry.name.c_str(), entry.name.length()+1); diff --git a/alc/backends/solaris.cpp b/alc/backends/solaris.cpp index d292c012..93ab64cb 100644 --- a/alc/backends/solaris.cpp +++ b/alc/backends/solaris.cpp @@ -276,10 +276,8 @@ void SolarisBackendFactory::probe(DevProbe type, std::string *outnames) { case DevProbe::Playback: { -#ifdef HAVE_STAT struct stat buf; if(stat(solaris_driver.c_str(), &buf) == 0) -#endif outnames->append(solaris_device, sizeof(solaris_device)); } break; diff --git a/config.h.in b/config.h.in index 4a1e2b00..d59a954b 100644 --- a/config.h.in +++ b/config.h.in @@ -74,9 +74,6 @@ /* Define if we have the SDL2 backend */ #cmakedefine HAVE_SDL2 -/* Define if we have the stat function */ -#cmakedefine HAVE_STAT - /* Define to the size of a long int type */ #cmakedefine SIZEOF_LONG ${SIZEOF_LONG} -- cgit v1.2.3 From f6a0b004e0424e1f12a226b3b3c7193063eb02e3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 22 Mar 2020 08:05:22 -0700 Subject: Update to C++14 --- CMakeLists.txt | 4 ++-- examples/alffplay.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 592a7687..2a8d6a97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,8 +125,8 @@ SET(EXPORT_DECL "") CHECK_TYPE_SIZE("long" SIZEOF_LONG) -# Require C++11 -SET(CMAKE_CXX_STANDARD 11) +# Require C++14 +SET(CMAKE_CXX_STANDARD 14) SET(CMAKE_CXX_STANDARD_REQUIRED TRUE) # Prefer C11, but support C99 and C90 too. diff --git a/examples/alffplay.cpp b/examples/alffplay.cpp index 6f14f71e..43db1256 100644 --- a/examples/alffplay.cpp +++ b/examples/alffplay.cpp @@ -1,7 +1,7 @@ /* * An example showing how to play a stream sync'd to video, using ffmpeg. * - * Requires C++11. + * Requires C++14. */ #include -- cgit v1.2.3 From 586bc94d513125a63e61922ac0805a71c6ef1950 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 24 Mar 2020 13:36:49 -0700 Subject: Use libsndfile for the alplay example --- CMakeLists.txt | 24 +++++++++---- cmake/FindSndFile.cmake | 23 ++++++++++++ examples/alplay.c | 94 ++++++++++++++++++++++--------------------------- 3 files changed, 82 insertions(+), 59 deletions(-) create mode 100644 cmake/FindSndFile.cmake (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a8d6a97..8fc2002f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1137,6 +1137,7 @@ IF(ALSOFT_EXAMPLES) ENDIF() IF(SDL2_FOUND) FIND_PACKAGE(SDL_sound) + FIND_PACKAGE(SndFile) FIND_PACKAGE(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE) ENDIF() ENDIF() @@ -1478,14 +1479,23 @@ IF(ALSOFT_EXAMPLES) MESSAGE(STATUS "Building example programs") + IF(SNDFILE_FOUND) + ADD_EXECUTABLE(alplay examples/alplay.c) + TARGET_INCLUDE_DIRECTORIES(alplay PRIVATE ${SNDFILE_INCLUDE_DIRS}) + TARGET_LINK_LIBRARIES(alplay PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common) + + IF(ALSOFT_INSTALL) + INSTALL(TARGETS alplay + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + ENDIF() + + MESSAGE(STATUS "Building SndFile example programs") + ENDIF() + IF(SDL2_FOUND) IF(SDL_SOUND_FOUND) - ADD_EXECUTABLE(alplay examples/alplay.c) - TARGET_INCLUDE_DIRECTORIES(alplay - PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) - TARGET_LINK_LIBRARIES(alplay - PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common) - ADD_EXECUTABLE(alstream examples/alstream.c) TARGET_INCLUDE_DIRECTORIES(alstream PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) @@ -1530,7 +1540,7 @@ IF(ALSOFT_EXAMPLES) PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common ${MATH_LIB}) IF(ALSOFT_INSTALL) - INSTALL(TARGETS alplay alstream alreverb almultireverb allatency alloopback alhrtf + INSTALL(TARGETS alstream alreverb almultireverb allatency alloopback alhrtf RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/cmake/FindSndFile.cmake b/cmake/FindSndFile.cmake new file mode 100644 index 00000000..afeec961 --- /dev/null +++ b/cmake/FindSndFile.cmake @@ -0,0 +1,23 @@ +# - Try to find SndFile +# Once done this will define +# +# SNDFILE_FOUND - system has SndFile +# SNDFILE_INCLUDE_DIRS - the SndFile include directory +# SNDFILE_LIBRARIES - Link these to use SndFile + +find_path(SNDFILE_INCLUDE_DIR NAMES sndfile.h) + +find_library(SNDFILE_LIBRARY NAMES sndfile sndfile-1) + +# handle the QUIETLY and REQUIRED arguments and set SNDFILE_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(SndFile DEFAULT_MSG SNDFILE_LIBRARY SNDFILE_INCLUDE_DIR) + +if(SNDFILE_FOUND) + set(SNDFILE_INCLUDE_DIRS ${SNDFILE_INCLUDE_DIR}) + set(SNDFILE_LIBRARIES ${SNDFILE_LIBRARY}) +endif() + +# show the SNDFILE_INCLUDE_DIR and SNDFILE_LIBRARY variables only in the advanced view +mark_as_advanced(SNDFILE_INCLUDE_DIR SNDFILE_LIBRARY) diff --git a/examples/alplay.c b/examples/alplay.c index 09ad96b4..af1c313c 100644 --- a/examples/alplay.c +++ b/examples/alplay.c @@ -24,12 +24,13 @@ /* This file contains an example for playing a sound buffer. */ -#include #include +#include +#include +#include +#include -#include "SDL_sound.h" -#include "SDL_audio.h" -#include "SDL_stdinc.h" +#include "sndfile.h" #include "AL/al.h" @@ -41,68 +42,62 @@ */ static ALuint LoadSound(const char *filename) { - Sound_Sample *sample; ALenum err, format; ALuint buffer; - Uint32 slen; - - /* Open the audio file */ - sample = Sound_NewSampleFromFile(filename, NULL, 65536); - if(!sample) + SNDFILE *sndfile; + SF_INFO sfinfo; + short *membuf; + sf_count_t num_frames; + ALsizei num_bytes; + + /* Open the audio file and check that it's usable. */ + sndfile = sf_open(filename, SFM_READ, &sfinfo); + if(!sndfile) { - fprintf(stderr, "Could not open audio in %s\n", filename); + fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(sndfile)); return 0; } - - /* Get the sound format, and figure out the OpenAL format */ - if(sample->actual.channels == 1) - { - if(sample->actual.format == AUDIO_U8) - format = AL_FORMAT_MONO8; - else if(sample->actual.format == AUDIO_S16SYS) - format = AL_FORMAT_MONO16; - else - { - fprintf(stderr, "Unsupported sample format: 0x%04x\n", sample->actual.format); - Sound_FreeSample(sample); - return 0; - } - } - else if(sample->actual.channels == 2) + if(sfinfo.frames < 1 || sfinfo.frames > (sf_count_t)(INT_MAX/sizeof(short))/sfinfo.channels) { - if(sample->actual.format == AUDIO_U8) - format = AL_FORMAT_STEREO8; - else if(sample->actual.format == AUDIO_S16SYS) - format = AL_FORMAT_STEREO16; - else - { - fprintf(stderr, "Unsupported sample format: 0x%04x\n", sample->actual.format); - Sound_FreeSample(sample); - return 0; - } + fprintf(stderr, "Bad sample count in %s (%" PRId64 ")\n", filename, sfinfo.frames); + sf_close(sndfile); + return 0; } + + /* Get the sound format, and figure out the OpenAL format */ + if(sfinfo.channels == 1) + format = AL_FORMAT_MONO16; + else if(sfinfo.channels == 2) + format = AL_FORMAT_STEREO16; else { - fprintf(stderr, "Unsupported channel count: %d\n", sample->actual.channels); - Sound_FreeSample(sample); + fprintf(stderr, "Unsupported channel count: %d\n", sfinfo.channels); + sf_close(sndfile); return 0; } - /* Decode the whole audio stream to a buffer. */ - slen = Sound_DecodeAll(sample); - if(!sample->buffer || slen == 0) + /* Decode the whole audio file to a buffer. */ + membuf = malloc((size_t)(sfinfo.frames * sfinfo.channels) * sizeof(short)); + + num_frames = sf_readf_short(sndfile, membuf, sfinfo.frames); + if(num_frames < 1) { - fprintf(stderr, "Failed to read audio from %s\n", filename); - Sound_FreeSample(sample); + free(membuf); + sf_close(sndfile); + fprintf(stderr, "Failed to read samples in %s (%" PRId64 ")\n", filename, num_frames); return 0; } + num_bytes = (ALsizei)(num_frames * sfinfo.channels) * (ALsizei)sizeof(short); /* Buffer the audio data into a new buffer object, then free the data and - * close the file. */ + * close the file. + */ buffer = 0; alGenBuffers(1, &buffer); - alBufferData(buffer, format, sample->buffer, (ALsizei)slen, (ALsizei)sample->actual.rate); - Sound_FreeSample(sample); + alBufferData(buffer, format, membuf, num_bytes, sfinfo.samplerate); + + free(membuf); + sf_close(sndfile); /* Check if an error occured, and clean up if so. */ err = alGetError(); @@ -136,14 +131,10 @@ int main(int argc, char **argv) if(InitAL(&argv, &argc) != 0) return 1; - /* Initialize SDL_sound. */ - Sound_Init(); - /* Load the sound into a buffer. */ buffer = LoadSound(argv[0]); if(!buffer) { - Sound_Quit(); CloseAL(); return 1; } @@ -171,7 +162,6 @@ int main(int argc, char **argv) alDeleteSources(1, &source); alDeleteBuffers(1, &buffer); - Sound_Quit(); CloseAL(); return 0; -- cgit v1.2.3 From cae78e79e81afbc47a9a5802c4cfcc62dbc07f8e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 24 Mar 2020 15:46:47 -0700 Subject: Convert the examples from SDL_sound to libsndfile --- CMakeLists.txt | 97 +++++------ cmake/FindSDL_sound.cmake | 429 ---------------------------------------------- examples/alhrtf.c | 95 +++++----- examples/allatency.c | 97 +++++------ examples/almultireverb.c | 96 +++++------ examples/alplay.c | 2 +- examples/alreverb.c | 97 +++++------ examples/alstream.c | 133 ++++++-------- examples/alstreamcb.cpp | 145 ++++++---------- 9 files changed, 311 insertions(+), 880 deletions(-) delete mode 100644 cmake/FindSDL_sound.cmake (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fc2002f..92769b6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1132,12 +1132,9 @@ IF(ALSOFT_UTILS AND NOT ALSOFT_NO_CONFIG_UTIL) add_subdirectory(utils/alsoft-config) ENDIF() IF(ALSOFT_EXAMPLES) - IF(NOT SDL2_FOUND) - FIND_PACKAGE(SDL2) - ENDIF() + FIND_PACKAGE(SndFile) + FIND_PACKAGE(SDL2) IF(SDL2_FOUND) - FIND_PACKAGE(SDL_sound) - FIND_PACKAGE(SndFile) FIND_PACKAGE(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE) ENDIF() ENDIF() @@ -1484,8 +1481,34 @@ IF(ALSOFT_EXAMPLES) TARGET_INCLUDE_DIRECTORIES(alplay PRIVATE ${SNDFILE_INCLUDE_DIRS}) TARGET_LINK_LIBRARIES(alplay PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common) + ADD_EXECUTABLE(alstream examples/alstream.c) + TARGET_INCLUDE_DIRECTORIES(alstream PRIVATE ${SNDFILE_INCLUDE_DIRS}) + TARGET_LINK_LIBRARIES(alstream PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common) + + ADD_EXECUTABLE(alreverb examples/alreverb.c) + TARGET_INCLUDE_DIRECTORIES(alreverb PRIVATE ${SNDFILE_INCLUDE_DIRS}) + TARGET_LINK_LIBRARIES(alreverb PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common) + + ADD_EXECUTABLE(almultireverb examples/almultireverb.c) + TARGET_INCLUDE_DIRECTORIES(almultireverb PRIVATE ${SNDFILE_INCLUDE_DIRS}) + TARGET_LINK_LIBRARIES(almultireverb + PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common ${MATH_LIB}) + + ADD_EXECUTABLE(allatency examples/allatency.c) + TARGET_INCLUDE_DIRECTORIES(allatency PRIVATE ${SNDFILE_INCLUDE_DIRS}) + TARGET_LINK_LIBRARIES(allatency PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common) + + ADD_EXECUTABLE(alhrtf examples/alhrtf.c) + TARGET_INCLUDE_DIRECTORIES(alhrtf PRIVATE ${SNDFILE_INCLUDE_DIRS}) + TARGET_LINK_LIBRARIES(alhrtf + PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common ${MATH_LIB}) + + ADD_EXECUTABLE(alstreamcb examples/alstreamcb.cpp) + TARGET_INCLUDE_DIRECTORIES(alstreamcb PRIVATE ${SNDFILE_INCLUDE_DIRS}) + TARGET_LINK_LIBRARIES(alstreamcb PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common) + IF(ALSOFT_INSTALL) - INSTALL(TARGETS alplay + INSTALL(TARGETS alplay alstream alreverb almultireverb allatency alhrtf RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) @@ -1495,60 +1518,20 @@ IF(ALSOFT_EXAMPLES) ENDIF() IF(SDL2_FOUND) - IF(SDL_SOUND_FOUND) - ADD_EXECUTABLE(alstream examples/alstream.c) - TARGET_INCLUDE_DIRECTORIES(alstream - PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) - TARGET_LINK_LIBRARIES(alstream - PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common) - - ADD_EXECUTABLE(alstreamcb examples/alstreamcb.cpp) - TARGET_INCLUDE_DIRECTORIES(alstreamcb - PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) - TARGET_LINK_LIBRARIES(alstreamcb - PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common) - - ADD_EXECUTABLE(alreverb examples/alreverb.c) - TARGET_INCLUDE_DIRECTORIES(alreverb - PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) - TARGET_LINK_LIBRARIES(alreverb - PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common) - - ADD_EXECUTABLE(almultireverb examples/almultireverb.c) - TARGET_INCLUDE_DIRECTORIES(almultireverb - PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) - TARGET_LINK_LIBRARIES(almultireverb - PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common - ${MATH_LIB}) - - ADD_EXECUTABLE(allatency examples/allatency.c) - TARGET_INCLUDE_DIRECTORIES(allatency - PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) - TARGET_LINK_LIBRARIES(allatency - PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common) - - ADD_EXECUTABLE(alloopback examples/alloopback.c) - TARGET_INCLUDE_DIRECTORIES(alloopback - PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) - TARGET_LINK_LIBRARIES(alloopback - PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common ${MATH_LIB}) - - ADD_EXECUTABLE(alhrtf examples/alhrtf.c) - TARGET_INCLUDE_DIRECTORIES(alhrtf - PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) - TARGET_LINK_LIBRARIES(alhrtf - PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common ${MATH_LIB}) - - IF(ALSOFT_INSTALL) - INSTALL(TARGETS alstream alreverb almultireverb allatency alloopback alhrtf - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - ENDIF() + ADD_EXECUTABLE(alloopback examples/alloopback.c) + TARGET_INCLUDE_DIRECTORIES(alloopback PRIVATE ${SDL2_INCLUDE_DIR}) + TARGET_LINK_LIBRARIES(alloopback + PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ex-common ${MATH_LIB}) - MESSAGE(STATUS "Building SDL_sound example programs") + IF(ALSOFT_INSTALL) + INSTALL(TARGETS alloopback + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) ENDIF() + MESSAGE(STATUS "Building SDL example programs") + SET(FFVER_OK FALSE) IF(FFMPEG_FOUND) SET(FFVER_OK TRUE) diff --git a/cmake/FindSDL_sound.cmake b/cmake/FindSDL_sound.cmake deleted file mode 100644 index 5557b55b..00000000 --- a/cmake/FindSDL_sound.cmake +++ /dev/null @@ -1,429 +0,0 @@ -# - Locates the SDL_sound library -# -# This module depends on SDL being found and -# must be called AFTER FindSDL.cmake or FindSDL2.cmake is called. -# -# This module defines -# SDL_SOUND_INCLUDE_DIR, where to find SDL_sound.h -# SDL_SOUND_FOUND, if false, do not try to link to SDL_sound -# SDL_SOUND_LIBRARIES, this contains the list of libraries that you need -# to link against. This is a read-only variable and is marked INTERNAL. -# SDL_SOUND_EXTRAS, this is an optional variable for you to add your own -# flags to SDL_SOUND_LIBRARIES. This is prepended to SDL_SOUND_LIBRARIES. -# This is available mostly for cases this module failed to anticipate for -# and you must add additional flags. This is marked as ADVANCED. -# SDL_SOUND_VERSION_STRING, human-readable string containing the version of SDL_sound -# -# This module also defines (but you shouldn't need to use directly) -# SDL_SOUND_LIBRARY, the name of just the SDL_sound library you would link -# against. Use SDL_SOUND_LIBRARIES for you link instructions and not this one. -# And might define the following as needed -# MIKMOD_LIBRARY -# MODPLUG_LIBRARY -# OGG_LIBRARY -# VORBIS_LIBRARY -# SMPEG_LIBRARY -# FLAC_LIBRARY -# SPEEX_LIBRARY -# -# Typically, you should not use these variables directly, and you should use -# SDL_SOUND_LIBRARIES which contains SDL_SOUND_LIBRARY and the other audio libraries -# (if needed) to successfully compile on your system. -# -# Created by Eric Wing. -# This module is a bit more complicated than the other FindSDL* family modules. -# The reason is that SDL_sound can be compiled in a large variety of different ways -# which are independent of platform. SDL_sound may dynamically link against other 3rd -# party libraries to get additional codec support, such as Ogg Vorbis, SMPEG, ModPlug, -# MikMod, FLAC, Speex, and potentially others. -# Under some circumstances which I don't fully understand, -# there seems to be a requirement -# that dependent libraries of libraries you use must also be explicitly -# linked against in order to successfully compile. SDL_sound does not currently -# have any system in place to know how it was compiled. -# So this CMake module does the hard work in trying to discover which 3rd party -# libraries are required for building (if any). -# This module uses a brute force approach to create a test program that uses SDL_sound, -# and then tries to build it. If the build fails, it parses the error output for -# known symbol names to figure out which libraries are needed. -# -# Responds to the $SDLDIR and $SDLSOUNDDIR environmental variable that would -# correspond to the ./configure --prefix=$SDLDIR used in building SDL. -# -# On OSX, this will prefer the Framework version (if found) over others. -# People will have to manually change the cache values of -# SDL_LIBRARY or SDL2_LIBRARY to override this selection or set the CMake -# environment CMAKE_INCLUDE_PATH to modify the search paths. - -#============================================================================= -# Copyright 2005-2009 Kitware, Inc. -# Copyright 2012 Benjamin Eikel -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - -set(SDL_SOUND_EXTRAS "" CACHE STRING "SDL_sound extra flags") -mark_as_advanced(SDL_SOUND_EXTRAS) - -# Find SDL_sound.h -find_path(SDL_SOUND_INCLUDE_DIR SDL_sound.h - HINTS - ENV SDLSOUNDDIR - ENV SDLDIR - PATH_SUFFIXES SDL SDL12 SDL11 -) - -find_library(SDL_SOUND_LIBRARY - NAMES SDL_sound - HINTS - ENV SDLSOUNDDIR - ENV SDLDIR -) - -if(SDL2_FOUND OR SDL_FOUND) - if(SDL_SOUND_INCLUDE_DIR AND SDL_SOUND_LIBRARY) - # CMake is giving me problems using TRY_COMPILE with the CMAKE_FLAGS - # for the :STRING syntax if I have multiple values contained in a - # single variable. This is a problem for the SDL2_LIBRARY variable - # because it does just that. When I feed this variable to the command, - # only the first value gets the appropriate modifier (e.g. -I) and - # the rest get dropped. - # To get multiple single variables to work, I must separate them with a "\;" - # I could go back and modify the FindSDL2.cmake module, but that's kind of painful. - # The solution would be to try something like: - # set(SDL2_TRY_COMPILE_LIBRARY_LIST "${SDL2_TRY_COMPILE_LIBRARY_LIST}\;${CMAKE_THREAD_LIBS_INIT}") - # Instead, it was suggested on the mailing list to write a temporary CMakeLists.txt - # with a temporary test project and invoke that with TRY_COMPILE. - # See message thread "Figuring out dependencies for a library in order to build" - # 2005-07-16 - # try_compile( - # MY_RESULT - # ${CMAKE_BINARY_DIR} - # ${PROJECT_SOURCE_DIR}/DetermineSoundLibs.c - # CMAKE_FLAGS - # -DINCLUDE_DIRECTORIES:STRING=${SDL2_INCLUDE_DIR}\;${SDL_SOUND_INCLUDE_DIR} - # -DLINK_LIBRARIES:STRING=${SDL_SOUND_LIBRARY}\;${SDL2_LIBRARY} - # OUTPUT_VARIABLE MY_OUTPUT - # ) - - # To minimize external dependencies, create a sdlsound test program - # which will be used to figure out if additional link dependencies are - # required for the link phase. - file(WRITE ${PROJECT_BINARY_DIR}/CMakeTmp/DetermineSoundLibs.c - "#include \"SDL_sound.h\" - #include \"SDL.h\" - int main(int argc, char* argv[]) - { - Sound_AudioInfo desired; - Sound_Sample* sample; - - SDL_Init(0); - Sound_Init(); - - /* This doesn't actually have to work, but Init() is a no-op - * for some of the decoders, so this should force more symbols - * to be pulled in. - */ - sample = Sound_NewSampleFromFile(argv[1], &desired, 4096); - - Sound_Quit(); - SDL_Quit(); - return 0; - }" - ) - - # Calling - # target_link_libraries(DetermineSoundLibs "${SDL_SOUND_LIBRARY} ${SDL2_LIBRARY}) - # causes problems when SDL2_LIBRARY looks like - # /Library/Frameworks/SDL2.framework;-framework Cocoa - # The ;-framework Cocoa seems to be confusing CMake once the OS X - # framework support was added. I was told that breaking up the list - # would fix the problem. - set(TMP_LIBS "") - if(SDL2_FOUND) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARY} ${SDL2_LIBRARY}) - foreach(lib ${SDL_SOUND_LIBRARY} ${SDL2_LIBRARY}) - set(TMP_LIBS "${TMP_LIBS} \"${lib}\"") - endforeach() - set(TMP_INCLUDE_DIRS ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) - else() - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARY} ${SDL_LIBRARY}) - foreach(lib ${SDL_SOUND_LIBRARY} ${SDL_LIBRARY}) - set(TMP_LIBS "${TMP_LIBS} \"${lib}\"") - endforeach() - set(TMP_INCLUDE_DIRS ${SDL_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) - endif() - - # Keep trying to build a temp project until we find all missing libs. - set(TRY_AGAIN TRUE) - WHILE(TRY_AGAIN) - set(TRY_AGAIN FALSE) - # message("TMP_TRY_LIBS ${TMP_TRY_LIBS}") - - # Write the CMakeLists.txt and test project - # Weird, this is still sketchy. If I don't quote the variables - # in the TARGET_LINK_LIBRARIES, I seem to loose everything - # in the SDL2_LIBRARY string after the "-framework". - # But if I quote the stuff in INCLUDE_DIRECTORIES, it doesn't work. - file(WRITE ${PROJECT_BINARY_DIR}/CMakeTmp/CMakeLists.txt - "cmake_minimum_required(VERSION 2.8) - project(DetermineSoundLibs C) - include_directories(${TMP_INCLUDE_DIRS}) - add_executable(DetermineSoundLibs DetermineSoundLibs.c) - target_link_libraries(DetermineSoundLibs ${TMP_LIBS})" - ) - - try_compile( - MY_RESULT - ${PROJECT_BINARY_DIR}/CMakeTmp - ${PROJECT_BINARY_DIR}/CMakeTmp - DetermineSoundLibs - OUTPUT_VARIABLE MY_OUTPUT - ) - # message("${MY_RESULT}") - # message(${MY_OUTPUT}) - - if(NOT MY_RESULT) - # I expect that MPGLIB, VOC, WAV, AIFF, and SHN are compiled in statically. - # I think Timidity is also compiled in statically. - # I've never had to explcitly link against Quicktime, so I'll skip that for now. - - # Find libmath - if("${MY_OUTPUT}" MATCHES "cos@@GLIBC") - find_library(MATH_LIBRARY NAMES m) - if(MATH_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${MATH_LIBRARY}) - set(TMP_LIBS "${SDL_SOUND_LIBRARIES_TMP} \"${MATH_LIBRARY}\"") - set(TRY_AGAIN TRUE) - endif(MATH_LIBRARY) - endif("${MY_OUTPUT}" MATCHES "cos@@GLIBC") - - # Find MikMod - if("${MY_OUTPUT}" MATCHES "MikMod_") - find_library(MIKMOD_LIBRARY - NAMES libmikmod-coreaudio mikmod - PATHS - ENV MIKMODDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES lib - ) - if(MIKMOD_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${MIKMOD_LIBRARY}) - set(TMP_LIBS "${SDL_SOUND_LIBRARIES_TMP} \"${MIKMOD_LIBRARY}\"") - set(TRY_AGAIN TRUE) - endif(MIKMOD_LIBRARY) - endif("${MY_OUTPUT}" MATCHES "MikMod_") - - # Find ModPlug - if("${MY_OUTPUT}" MATCHES "MODPLUG_") - find_library(MODPLUG_LIBRARY - NAMES modplug - PATHS - ENV MODPLUGDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES lib - ) - if(MODPLUG_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${MODPLUG_LIBRARY}) - set(TMP_LIBS "${SDL_SOUND_LIBRARIES_TMP} \"${MODPLUG_LIBRARY}\"") - set(TRY_AGAIN TRUE) - endif() - endif() - - # Find Ogg and Vorbis - if("${MY_OUTPUT}" MATCHES "ov_") - find_library(VORBISFILE_LIBRARY - NAMES vorbisfile VorbisFile VORBISFILE - PATHS - ENV VORBISDIR - ENV OGGDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES lib - ) - if(VORBISFILE_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${VORBISFILE_LIBRARY}) - set(TMP_LIBS "${SDL_SOUND_LIBRARIES_TMP} \"${VORBISFILE_LIBRARY}\"") - set(TRY_AGAIN TRUE) - endif() - - find_library(VORBIS_LIBRARY - NAMES vorbis Vorbis VORBIS - PATHS - ENV OGGDIR - ENV VORBISDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES lib - ) - if(VORBIS_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${VORBIS_LIBRARY}) - set(TMP_LIBS "${SDL_SOUND_LIBRARIES_TMP} \"${VORBIS_LIBRARY}\"") - set(TRY_AGAIN TRUE) - endif() - - find_library(OGG_LIBRARY - NAMES ogg Ogg OGG - PATHS - ENV OGGDIR - ENV VORBISDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES lib - ) - if(OGG_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${OGG_LIBRARY}) - set(TMP_LIBS "${SDL_SOUND_LIBRARIES_TMP} \"${OGG_LIBRARY}\"") - set(TRY_AGAIN TRUE) - endif() - endif() - - # Find SMPEG - if("${MY_OUTPUT}" MATCHES "SMPEG_") - find_library(SMPEG_LIBRARY - NAMES smpeg SMPEG Smpeg SMpeg - PATHS - ENV SMPEGDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES lib - ) - if(SMPEG_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${SMPEG_LIBRARY}) - set(TMP_LIBS "${SDL_SOUND_LIBRARIES_TMP} \"${SMPEG_LIBRARY}\"") - set(TRY_AGAIN TRUE) - endif() - endif() - - - # Find FLAC - if("${MY_OUTPUT}" MATCHES "FLAC_") - find_library(FLAC_LIBRARY - NAMES flac FLAC - PATHS - ENV FLACDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES lib - ) - if(FLAC_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${FLAC_LIBRARY}) - set(TMP_LIBS "${SDL_SOUND_LIBRARIES_TMP} \"${FLAC_LIBRARY}\"") - set(TRY_AGAIN TRUE) - endif() - endif() - - - # Hmmm...Speex seems to depend on Ogg. This might be a problem if - # the TRY_COMPILE attempt gets blocked at SPEEX before it can pull - # in the Ogg symbols. I'm not sure if I should duplicate the ogg stuff - # above for here or if two ogg entries will screw up things. - if("${MY_OUTPUT}" MATCHES "speex_") - find_library(SPEEX_LIBRARY - NAMES speex SPEEX - PATHS - ENV SPEEXDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES lib - ) - if(SPEEX_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${SPEEX_LIBRARY}) - set(TMP_LIBS "${SDL_SOUND_LIBRARIES_TMP} \"${SPEEX_LIBRARY}\"") - set(TRY_AGAIN TRUE) - endif() - - # Find OGG (needed for Speex) - # We might have already found Ogg for Vorbis, so skip it if so. - if(NOT OGG_LIBRARY) - find_library(OGG_LIBRARY - NAMES ogg Ogg OGG - PATHS - ENV OGGDIR - ENV VORBISDIR - ENV SPEEXDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES lib - ) - if(OGG_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${OGG_LIBRARY}) - set(TMP_LIBS "${SDL_SOUND_LIBRARIES_TMP} \"${OGG_LIBRARY}\"") - set(TRY_AGAIN TRUE) - endif() - endif() - endif() - endif() - ENDWHILE() - unset(TMP_INCLUDE_DIRS) - unset(TMP_LIBS) - - set(SDL_SOUND_LIBRARIES ${SDL_SOUND_EXTRAS} ${SDL_SOUND_LIBRARIES_TMP} CACHE INTERNAL "SDL_sound and dependent libraries") - endif() -endif() - -if(SDL_SOUND_INCLUDE_DIR AND EXISTS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h") - file(STRINGS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h" SDL_SOUND_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SOUND_VER_MAJOR[ \t]+[0-9]+$") - file(STRINGS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h" SDL_SOUND_VERSION_MINOR_LINE REGEX "^#define[ \t]+SOUND_VER_MINOR[ \t]+[0-9]+$") - file(STRINGS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h" SDL_SOUND_VERSION_PATCH_LINE REGEX "^#define[ \t]+SOUND_VER_PATCH[ \t]+[0-9]+$") - string(REGEX REPLACE "^#define[ \t]+SOUND_VER_MAJOR[ \t]+([0-9]+)$" "\\1" SDL_SOUND_VERSION_MAJOR "${SDL_SOUND_VERSION_MAJOR_LINE}") - string(REGEX REPLACE "^#define[ \t]+SOUND_VER_MINOR[ \t]+([0-9]+)$" "\\1" SDL_SOUND_VERSION_MINOR "${SDL_SOUND_VERSION_MINOR_LINE}") - string(REGEX REPLACE "^#define[ \t]+SOUND_VER_PATCH[ \t]+([0-9]+)$" "\\1" SDL_SOUND_VERSION_PATCH "${SDL_SOUND_VERSION_PATCH_LINE}") - set(SDL_SOUND_VERSION_STRING ${SDL_SOUND_VERSION_MAJOR}.${SDL_SOUND_VERSION_MINOR}.${SDL_SOUND_VERSION_PATCH}) - unset(SDL_SOUND_VERSION_MAJOR_LINE) - unset(SDL_SOUND_VERSION_MINOR_LINE) - unset(SDL_SOUND_VERSION_PATCH_LINE) - unset(SDL_SOUND_VERSION_MAJOR) - unset(SDL_SOUND_VERSION_MINOR) - unset(SDL_SOUND_VERSION_PATCH) -endif() - -include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL_sound - REQUIRED_VARS SDL_SOUND_LIBRARIES SDL_SOUND_INCLUDE_DIR - VERSION_VAR SDL_SOUND_VERSION_STRING) diff --git a/examples/alhrtf.c b/examples/alhrtf.c index 2be28a91..bea87f56 100644 --- a/examples/alhrtf.c +++ b/examples/alhrtf.c @@ -25,13 +25,14 @@ /* This file contains an example for selecting an HRTF. */ #include +#include +#include #include #include +#include #include -#include "SDL_sound.h" -#include "SDL_audio.h" -#include "SDL_stdinc.h" +#include "sndfile.h" #include "AL/al.h" #include "AL/alc.h" @@ -52,68 +53,62 @@ static LPALCRESETDEVICESOFT alcResetDeviceSOFT; */ static ALuint LoadSound(const char *filename) { - Sound_Sample *sample; ALenum err, format; ALuint buffer; - Uint32 slen; - - /* Open the audio file */ - sample = Sound_NewSampleFromFile(filename, NULL, 65536); - if(!sample) + SNDFILE *sndfile; + SF_INFO sfinfo; + short *membuf; + sf_count_t num_frames; + ALsizei num_bytes; + + /* Open the audio file and check that it's usable. */ + sndfile = sf_open(filename, SFM_READ, &sfinfo); + if(!sndfile) { - fprintf(stderr, "Could not open audio in %s\n", filename); + fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(sndfile)); return 0; } - - /* Get the sound format, and figure out the OpenAL format */ - if(sample->actual.channels == 1) - { - if(sample->actual.format == AUDIO_U8) - format = AL_FORMAT_MONO8; - else if(sample->actual.format == AUDIO_S16SYS) - format = AL_FORMAT_MONO16; - else - { - fprintf(stderr, "Unsupported sample format: 0x%04x\n", sample->actual.format); - Sound_FreeSample(sample); - return 0; - } - } - else if(sample->actual.channels == 2) + if(sfinfo.frames < 1 || sfinfo.frames > (sf_count_t)(INT_MAX/sizeof(short))/sfinfo.channels) { - if(sample->actual.format == AUDIO_U8) - format = AL_FORMAT_STEREO8; - else if(sample->actual.format == AUDIO_S16SYS) - format = AL_FORMAT_STEREO16; - else - { - fprintf(stderr, "Unsupported sample format: 0x%04x\n", sample->actual.format); - Sound_FreeSample(sample); - return 0; - } + fprintf(stderr, "Bad sample count in %s (%" PRId64 ")\n", filename, sfinfo.frames); + sf_close(sndfile); + return 0; } + + /* Get the sound format, and figure out the OpenAL format */ + if(sfinfo.channels == 1) + format = AL_FORMAT_MONO16; + else if(sfinfo.channels == 2) + format = AL_FORMAT_STEREO16; else { - fprintf(stderr, "Unsupported channel count: %d\n", sample->actual.channels); - Sound_FreeSample(sample); + fprintf(stderr, "Unsupported channel count: %d\n", sfinfo.channels); + sf_close(sndfile); return 0; } - /* Decode the whole audio stream to a buffer. */ - slen = Sound_DecodeAll(sample); - if(!sample->buffer || slen == 0) + /* Decode the whole audio file to a buffer. */ + membuf = malloc((size_t)(sfinfo.frames * sfinfo.channels) * sizeof(short)); + + num_frames = sf_readf_short(sndfile, membuf, sfinfo.frames); + if(num_frames < 1) { - fprintf(stderr, "Failed to read audio from %s\n", filename); - Sound_FreeSample(sample); + free(membuf); + sf_close(sndfile); + fprintf(stderr, "Failed to read samples in %s (%" PRId64 ")\n", filename, num_frames); return 0; } + num_bytes = (ALsizei)(num_frames * sfinfo.channels) * (ALsizei)sizeof(short); /* Buffer the audio data into a new buffer object, then free the data and - * close the file. */ + * close the file. + */ buffer = 0; alGenBuffers(1, &buffer); - alBufferData(buffer, format, sample->buffer, (ALsizei)slen, (ALsizei)sample->actual.rate); - Sound_FreeSample(sample); + alBufferData(buffer, format, membuf, num_bytes, sfinfo.samplerate); + + free(membuf); + sf_close(sndfile); /* Check if an error occured, and clean up if so. */ err = alGetError(); @@ -240,14 +235,10 @@ int main(int argc, char **argv) } fflush(stdout); - /* Initialize SDL_sound. */ - Sound_Init(); - /* Load the sound into a buffer. */ buffer = LoadSound(soundname); if(!buffer) { - Sound_Quit(); CloseAL(); return 1; } @@ -291,11 +282,9 @@ int main(int argc, char **argv) alGetSourcei(source, AL_SOURCE_STATE, &state); } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING); - /* All done. Delete resources, and close down SDL_sound and OpenAL. */ + /* All done. Delete resources, and close down OpenAL. */ alDeleteSources(1, &source); alDeleteBuffers(1, &buffer); - - Sound_Quit(); CloseAL(); return 0; diff --git a/examples/allatency.c b/examples/allatency.c index a61fb820..7ece01eb 100644 --- a/examples/allatency.c +++ b/examples/allatency.c @@ -24,12 +24,13 @@ /* This file contains an example for checking the latency of a sound. */ -#include #include +#include +#include +#include +#include -#include "SDL_sound.h" -#include "SDL_audio.h" -#include "SDL_stdinc.h" +#include "sndfile.h" #include "AL/al.h" #include "AL/alext.h" @@ -55,68 +56,62 @@ static LPALGETSOURCEI64VSOFT alGetSourcei64vSOFT; */ static ALuint LoadSound(const char *filename) { - Sound_Sample *sample; ALenum err, format; ALuint buffer; - Uint32 slen; - - /* Open the audio file */ - sample = Sound_NewSampleFromFile(filename, NULL, 65536); - if(!sample) + SNDFILE *sndfile; + SF_INFO sfinfo; + short *membuf; + sf_count_t num_frames; + ALsizei num_bytes; + + /* Open the audio file and check that it's usable. */ + sndfile = sf_open(filename, SFM_READ, &sfinfo); + if(!sndfile) { - fprintf(stderr, "Could not open audio in %s\n", filename); + fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(sndfile)); return 0; } - - /* Get the sound format, and figure out the OpenAL format */ - if(sample->actual.channels == 1) - { - if(sample->actual.format == AUDIO_U8) - format = AL_FORMAT_MONO8; - else if(sample->actual.format == AUDIO_S16SYS) - format = AL_FORMAT_MONO16; - else - { - fprintf(stderr, "Unsupported sample format: 0x%04x\n", sample->actual.format); - Sound_FreeSample(sample); - return 0; - } - } - else if(sample->actual.channels == 2) + if(sfinfo.frames < 1 || sfinfo.frames > (sf_count_t)(INT_MAX/sizeof(short))/sfinfo.channels) { - if(sample->actual.format == AUDIO_U8) - format = AL_FORMAT_STEREO8; - else if(sample->actual.format == AUDIO_S16SYS) - format = AL_FORMAT_STEREO16; - else - { - fprintf(stderr, "Unsupported sample format: 0x%04x\n", sample->actual.format); - Sound_FreeSample(sample); - return 0; - } + fprintf(stderr, "Bad sample count in %s (%" PRId64 ")\n", filename, sfinfo.frames); + sf_close(sndfile); + return 0; } + + /* Get the sound format, and figure out the OpenAL format */ + if(sfinfo.channels == 1) + format = AL_FORMAT_MONO16; + else if(sfinfo.channels == 2) + format = AL_FORMAT_STEREO16; else { - fprintf(stderr, "Unsupported channel count: %d\n", sample->actual.channels); - Sound_FreeSample(sample); + fprintf(stderr, "Unsupported channel count: %d\n", sfinfo.channels); + sf_close(sndfile); return 0; } - /* Decode the whole audio stream to a buffer. */ - slen = Sound_DecodeAll(sample); - if(!sample->buffer || slen == 0) + /* Decode the whole audio file to a buffer. */ + membuf = malloc((size_t)(sfinfo.frames * sfinfo.channels) * sizeof(short)); + + num_frames = sf_readf_short(sndfile, membuf, sfinfo.frames); + if(num_frames < 1) { - fprintf(stderr, "Failed to read audio from %s\n", filename); - Sound_FreeSample(sample); + free(membuf); + sf_close(sndfile); + fprintf(stderr, "Failed to read samples in %s (%" PRId64 ")\n", filename, num_frames); return 0; } + num_bytes = (ALsizei)(num_frames * sfinfo.channels) * (ALsizei)sizeof(short); /* Buffer the audio data into a new buffer object, then free the data and - * close the file. */ + * close the file. + */ buffer = 0; alGenBuffers(1, &buffer); - alBufferData(buffer, format, sample->buffer, (ALsizei)slen, (ALsizei)sample->actual.rate); - Sound_FreeSample(sample); + alBufferData(buffer, format, membuf, num_bytes, sfinfo.samplerate); + + free(membuf); + sf_close(sndfile); /* Check if an error occured, and clean up if so. */ err = alGetError(); @@ -173,14 +168,10 @@ int main(int argc, char **argv) LOAD_PROC(LPALGETSOURCEI64VSOFT, alGetSourcei64vSOFT); #undef LOAD_PROC - /* Initialize SDL_sound. */ - Sound_Init(); - /* Load the sound into a buffer. */ buffer = LoadSound(argv[0]); if(!buffer) { - Sound_Quit(); CloseAL(); return 1; } @@ -206,11 +197,9 @@ int main(int argc, char **argv) } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING); printf("\n"); - /* All done. Delete resources, and close down SDL_sound and OpenAL. */ + /* All done. Delete resources, and close down OpenAL. */ alDeleteSources(1, &source); alDeleteBuffers(1, &buffer); - - Sound_Quit(); CloseAL(); return 0; diff --git a/examples/almultireverb.c b/examples/almultireverb.c index a90b3368..eb874061 100644 --- a/examples/almultireverb.c +++ b/examples/almultireverb.c @@ -29,14 +29,16 @@ * listener. */ + #include +#include +#include #include #include +#include #include -#include "SDL_sound.h" -#include "SDL_audio.h" -#include "SDL_stdinc.h" +#include "sndfile.h" #include "AL/al.h" #include "AL/alc.h" @@ -151,68 +153,62 @@ static int LoadEffect(ALuint effect, const EFXEAXREVERBPROPERTIES *reverb) */ static ALuint LoadSound(const char *filename) { - Sound_Sample *sample; ALenum err, format; ALuint buffer; - Uint32 slen; - - /* Open the audio file */ - sample = Sound_NewSampleFromFile(filename, NULL, 65536); - if(!sample) + SNDFILE *sndfile; + SF_INFO sfinfo; + short *membuf; + sf_count_t num_frames; + ALsizei num_bytes; + + /* Open the audio file and check that it's usable. */ + sndfile = sf_open(filename, SFM_READ, &sfinfo); + if(!sndfile) { - fprintf(stderr, "Could not open audio in %s\n", filename); + fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(sndfile)); return 0; } - - /* Get the sound format, and figure out the OpenAL format */ - if(sample->actual.channels == 1) - { - if(sample->actual.format == AUDIO_U8) - format = AL_FORMAT_MONO8; - else if(sample->actual.format == AUDIO_S16SYS) - format = AL_FORMAT_MONO16; - else - { - fprintf(stderr, "Unsupported sample format: 0x%04x\n", sample->actual.format); - Sound_FreeSample(sample); - return 0; - } - } - else if(sample->actual.channels == 2) + if(sfinfo.frames < 1 || sfinfo.frames > (sf_count_t)(INT_MAX/sizeof(short))/sfinfo.channels) { - if(sample->actual.format == AUDIO_U8) - format = AL_FORMAT_STEREO8; - else if(sample->actual.format == AUDIO_S16SYS) - format = AL_FORMAT_STEREO16; - else - { - fprintf(stderr, "Unsupported sample format: 0x%04x\n", sample->actual.format); - Sound_FreeSample(sample); - return 0; - } + fprintf(stderr, "Bad sample count in %s (%" PRId64 ")\n", filename, sfinfo.frames); + sf_close(sndfile); + return 0; } + + /* Get the sound format, and figure out the OpenAL format */ + if(sfinfo.channels == 1) + format = AL_FORMAT_MONO16; + else if(sfinfo.channels == 2) + format = AL_FORMAT_STEREO16; else { - fprintf(stderr, "Unsupported channel count: %d\n", sample->actual.channels); - Sound_FreeSample(sample); + fprintf(stderr, "Unsupported channel count: %d\n", sfinfo.channels); + sf_close(sndfile); return 0; } - /* Decode the whole audio stream to a buffer. */ - slen = Sound_DecodeAll(sample); - if(!sample->buffer || slen == 0) + /* Decode the whole audio file to a buffer. */ + membuf = malloc((size_t)(sfinfo.frames * sfinfo.channels) * sizeof(short)); + + num_frames = sf_readf_short(sndfile, membuf, sfinfo.frames); + if(num_frames < 1) { - fprintf(stderr, "Failed to read audio from %s\n", filename); - Sound_FreeSample(sample); + free(membuf); + sf_close(sndfile); + fprintf(stderr, "Failed to read samples in %s (%" PRId64 ")\n", filename, num_frames); return 0; } + num_bytes = (ALsizei)(num_frames * sfinfo.channels) * (ALsizei)sizeof(short); /* Buffer the audio data into a new buffer object, then free the data and - * close the file. */ + * close the file. + */ buffer = 0; alGenBuffers(1, &buffer); - alBufferData(buffer, format, sample->buffer, (ALsizei)slen, (ALsizei)sample->actual.rate); - Sound_FreeSample(sample); + alBufferData(buffer, format, membuf, num_bytes, sfinfo.samplerate); + + free(membuf); + sf_close(sndfile); /* Check if an error occured, and clean up if so. */ err = alGetError(); @@ -561,15 +557,11 @@ int main(int argc, char **argv) LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv); #undef LOAD_PROC - /* Initialize SDL_sound. */ - Sound_Init(); - /* Load the sound into a buffer. */ buffer = LoadSound(argv[0]); if(!buffer) { CloseAL(); - Sound_Quit(); return 1; } @@ -585,7 +577,6 @@ int main(int argc, char **argv) { alDeleteEffects(2, effects); alDeleteBuffers(1, &buffer); - Sound_Quit(); CloseAL(); return 1; } @@ -684,14 +675,13 @@ int main(int argc, char **argv) alGetSourcei(source, AL_SOURCE_STATE, &state); } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING && loops < MaxTransitions); - /* All done. Delete resources, and close down SDL_sound and OpenAL. */ + /* All done. Delete resources, and close down OpenAL. */ alDeleteSources(1, &source); alDeleteAuxiliaryEffectSlots(2, slots); alDeleteEffects(2, effects); alDeleteFilters(1, &direct_filter); alDeleteBuffers(1, &buffer); - Sound_Quit(); CloseAL(); return 0; diff --git a/examples/alplay.c b/examples/alplay.c index af1c313c..70314d47 100644 --- a/examples/alplay.c +++ b/examples/alplay.c @@ -158,7 +158,7 @@ int main(int argc, char **argv) } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING); printf("\n"); - /* All done. Delete resources, and close down SDL_sound and OpenAL. */ + /* All done. Delete resources, and close down OpenAL. */ alDeleteSources(1, &source); alDeleteBuffers(1, &buffer); diff --git a/examples/alreverb.c b/examples/alreverb.c index d789dffe..1f30a450 100644 --- a/examples/alreverb.c +++ b/examples/alreverb.c @@ -24,12 +24,13 @@ /* This file contains an example for applying reverb to a sound. */ -#include #include +#include +#include +#include +#include -#include "SDL_sound.h" -#include "SDL_audio.h" -#include "SDL_stdinc.h" +#include "sndfile.h" #include "AL/al.h" #include "AL/alc.h" @@ -149,68 +150,62 @@ static ALuint LoadEffect(const EFXEAXREVERBPROPERTIES *reverb) */ static ALuint LoadSound(const char *filename) { - Sound_Sample *sample; ALenum err, format; ALuint buffer; - Uint32 slen; - - /* Open the audio file */ - sample = Sound_NewSampleFromFile(filename, NULL, 65536); - if(!sample) + SNDFILE *sndfile; + SF_INFO sfinfo; + short *membuf; + sf_count_t num_frames; + ALsizei num_bytes; + + /* Open the audio file and check that it's usable. */ + sndfile = sf_open(filename, SFM_READ, &sfinfo); + if(!sndfile) { - fprintf(stderr, "Could not open audio in %s\n", filename); + fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(sndfile)); return 0; } - - /* Get the sound format, and figure out the OpenAL format */ - if(sample->actual.channels == 1) - { - if(sample->actual.format == AUDIO_U8) - format = AL_FORMAT_MONO8; - else if(sample->actual.format == AUDIO_S16SYS) - format = AL_FORMAT_MONO16; - else - { - fprintf(stderr, "Unsupported sample format: 0x%04x\n", sample->actual.format); - Sound_FreeSample(sample); - return 0; - } - } - else if(sample->actual.channels == 2) + if(sfinfo.frames < 1 || sfinfo.frames > (sf_count_t)(INT_MAX/sizeof(short))/sfinfo.channels) { - if(sample->actual.format == AUDIO_U8) - format = AL_FORMAT_STEREO8; - else if(sample->actual.format == AUDIO_S16SYS) - format = AL_FORMAT_STEREO16; - else - { - fprintf(stderr, "Unsupported sample format: 0x%04x\n", sample->actual.format); - Sound_FreeSample(sample); - return 0; - } + fprintf(stderr, "Bad sample count in %s (%" PRId64 ")\n", filename, sfinfo.frames); + sf_close(sndfile); + return 0; } + + /* Get the sound format, and figure out the OpenAL format */ + if(sfinfo.channels == 1) + format = AL_FORMAT_MONO16; + else if(sfinfo.channels == 2) + format = AL_FORMAT_STEREO16; else { - fprintf(stderr, "Unsupported channel count: %d\n", sample->actual.channels); - Sound_FreeSample(sample); + fprintf(stderr, "Unsupported channel count: %d\n", sfinfo.channels); + sf_close(sndfile); return 0; } - /* Decode the whole audio stream to a buffer. */ - slen = Sound_DecodeAll(sample); - if(!sample->buffer || slen == 0) + /* Decode the whole audio file to a buffer. */ + membuf = malloc((size_t)(sfinfo.frames * sfinfo.channels) * sizeof(short)); + + num_frames = sf_readf_short(sndfile, membuf, sfinfo.frames); + if(num_frames < 1) { - fprintf(stderr, "Failed to read audio from %s\n", filename); - Sound_FreeSample(sample); + free(membuf); + sf_close(sndfile); + fprintf(stderr, "Failed to read samples in %s (%" PRId64 ")\n", filename, num_frames); return 0; } + num_bytes = (ALsizei)(num_frames * sfinfo.channels) * (ALsizei)sizeof(short); /* Buffer the audio data into a new buffer object, then free the data and - * close the file. */ + * close the file. + */ buffer = 0; alGenBuffers(1, &buffer); - alBufferData(buffer, format, sample->buffer, (ALsizei)slen, (ALsizei)sample->actual.rate); - Sound_FreeSample(sample); + alBufferData(buffer, format, membuf, num_bytes, sfinfo.samplerate); + + free(membuf); + sf_close(sndfile); /* Check if an error occured, and clean up if so. */ err = alGetError(); @@ -278,15 +273,11 @@ int main(int argc, char **argv) LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv); #undef LOAD_PROC - /* Initialize SDL_sound. */ - Sound_Init(); - /* Load the sound into a buffer. */ buffer = LoadSound(argv[0]); if(!buffer) { CloseAL(); - Sound_Quit(); return 1; } @@ -295,7 +286,6 @@ int main(int argc, char **argv) if(!effect) { alDeleteBuffers(1, &buffer); - Sound_Quit(); CloseAL(); return 1; } @@ -330,13 +320,12 @@ int main(int argc, char **argv) alGetSourcei(source, AL_SOURCE_STATE, &state); } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING); - /* All done. Delete resources, and close down SDL_sound and OpenAL. */ + /* All done. Delete resources, and close down OpenAL. */ alDeleteSources(1, &source); alDeleteAuxiliaryEffectSlots(1, &slot); alDeleteEffects(1, &effect); alDeleteBuffers(1, &buffer); - Sound_Quit(); CloseAL(); return 0; diff --git a/examples/alstream.c b/examples/alstream.c index 56505ddb..69be236a 100644 --- a/examples/alstream.c +++ b/examples/alstream.c @@ -24,32 +24,24 @@ /* This file contains a relatively simple streaming audio player. */ -#include -#include -#include #include +#include +#include +#include +#include -#include "SDL_sound.h" -#include "SDL_audio.h" -#include "SDL_stdinc.h" +#include "sndfile.h" #include "AL/al.h" #include "common/alhelpers.h" -#ifndef SDL_AUDIO_MASK_BITSIZE -#define SDL_AUDIO_MASK_BITSIZE (0xFF) -#endif -#ifndef SDL_AUDIO_BITSIZE -#define SDL_AUDIO_BITSIZE(x) (x & SDL_AUDIO_MASK_BITSIZE) -#endif - /* Define the number of buffers and buffer size (in milliseconds) to use. 4 - * buffers with 200ms each gives a nice per-chunk size, and lets the queue last - * for almost one second. */ + * buffers with 8192 samples each gives a nice per-chunk size, and lets the + * queue last for almost one second at 44.1khz. */ #define NUM_BUFFERS 4 -#define BUFFER_TIME_MS 200 +#define BUFFER_SAMPLES 8192 typedef struct StreamPlayer { /* These are the buffers and source to play out through OpenAL with */ @@ -57,11 +49,12 @@ typedef struct StreamPlayer { ALuint source; /* Handle for the audio file */ - Sound_Sample *sample; + SNDFILE *sndfile; + SF_INFO sfinfo; + short *membuf; - /* The format of the output stream */ + /* The format of the output stream (sample rate is in sfinfo) */ ALenum format; - ALsizei srate; } StreamPlayer; static StreamPlayer *NewPlayer(void); @@ -118,73 +111,46 @@ static void DeletePlayer(StreamPlayer *player) * it will be closed first. */ static int OpenPlayerFile(StreamPlayer *player, const char *filename) { - Uint32 frame_size; + size_t frame_size; ClosePlayerFile(player); - /* Open the file and get the first stream from it */ - player->sample = Sound_NewSampleFromFile(filename, NULL, 0); - if(!player->sample) + /* Open the audio file and check that it's usable. */ + player->sndfile = sf_open(filename, SFM_READ, &player->sfinfo); + if(!player->sndfile) { - fprintf(stderr, "Could not open audio in %s\n", filename); - goto error; + fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(NULL)); + return 0; } - /* Get the stream format, and figure out the OpenAL format */ - if(player->sample->actual.channels == 1) - { - if(player->sample->actual.format == AUDIO_U8) - player->format = AL_FORMAT_MONO8; - else if(player->sample->actual.format == AUDIO_S16SYS) - player->format = AL_FORMAT_MONO16; - else - { - fprintf(stderr, "Unsupported sample format: 0x%04x\n", player->sample->actual.format); - goto error; - } - } - else if(player->sample->actual.channels == 2) - { - if(player->sample->actual.format == AUDIO_U8) - player->format = AL_FORMAT_STEREO8; - else if(player->sample->actual.format == AUDIO_S16SYS) - player->format = AL_FORMAT_STEREO16; - else - { - fprintf(stderr, "Unsupported sample format: 0x%04x\n", player->sample->actual.format); - goto error; - } - } + /* Get the sound format, and figure out the OpenAL format */ + if(player->sfinfo.channels == 1) + player->format = AL_FORMAT_MONO16; + else if(player->sfinfo.channels == 2) + player->format = AL_FORMAT_STEREO16; else { - fprintf(stderr, "Unsupported channel count: %d\n", player->sample->actual.channels); - goto error; + fprintf(stderr, "Unsupported channel count: %d\n", player->sfinfo.channels); + sf_close(player->sndfile); + player->sndfile = NULL; + return 0; } - player->srate = (ALsizei)player->sample->actual.rate; - - frame_size = player->sample->actual.channels * - SDL_AUDIO_BITSIZE(player->sample->actual.format) / 8; - /* Set the buffer size, given the desired millisecond length. */ - Sound_SetBufferSize(player->sample, (Uint32)((Uint64)player->srate*BUFFER_TIME_MS/1000) * - frame_size); + frame_size = (size_t)(BUFFER_SAMPLES * player->sfinfo.channels) * sizeof(short); + player->membuf = malloc(frame_size); return 1; - -error: - if(player->sample) - Sound_FreeSample(player->sample); - player->sample = NULL; - - return 0; } /* Closes the audio file stream */ static void ClosePlayerFile(StreamPlayer *player) { - if(player->sample) - Sound_FreeSample(player->sample); - player->sample = NULL; + if(player->sndfile) + sf_close(player->sndfile); + player->sndfile = NULL; + + free(player->membuf); + player->membuf = NULL; } @@ -201,11 +167,12 @@ static int StartPlayer(StreamPlayer *player) for(i = 0;i < NUM_BUFFERS;i++) { /* Get some data to give it to the buffer */ - Uint32 slen = Sound_Decode(player->sample); - if(slen == 0) break; + sf_count_t slen = sf_readf_short(player->sndfile, player->membuf, BUFFER_SAMPLES); + if(slen < 1) break; - alBufferData(player->buffers[i], player->format, player->sample->buffer, (ALsizei)slen, - player->srate); + slen *= player->sfinfo.channels * (sf_count_t)sizeof(short); + alBufferData(player->buffers[i], player->format, player->membuf, (ALsizei)slen, + player->sfinfo.samplerate); } if(alGetError() != AL_NO_ERROR) { @@ -242,21 +209,19 @@ static int UpdatePlayer(StreamPlayer *player) while(processed > 0) { ALuint bufid; - Uint32 slen; + sf_count_t slen; alSourceUnqueueBuffers(player->source, 1, &bufid); processed--; - if((player->sample->flags&(SOUND_SAMPLEFLAG_EOF|SOUND_SAMPLEFLAG_ERROR))) - continue; - /* Read the next chunk of data, refill the buffer, and queue it * back on the source */ - slen = Sound_Decode(player->sample); + slen = sf_readf_short(player->sndfile, player->membuf, BUFFER_SAMPLES); if(slen > 0) { - alBufferData(bufid, player->format, player->sample->buffer, (ALsizei)slen, - player->srate); + slen *= player->sfinfo.channels * (sf_count_t)sizeof(short); + alBufferData(bufid, player->format, player->membuf, (ALsizei)slen, + player->sfinfo.samplerate); alSourceQueueBuffers(player->source, 1, &bufid); } if(alGetError() != AL_NO_ERROR) @@ -304,8 +269,6 @@ int main(int argc, char **argv) if(InitAL(&argv, &argc) != 0) return 1; - Sound_Init(); - player = NewPlayer(); /* Play each file listed on the command line */ @@ -323,7 +286,8 @@ int main(int argc, char **argv) else namepart = argv[i]; - printf("Playing: %s (%s, %dhz)\n", namepart, FormatName(player->format), player->srate); + printf("Playing: %s (%s, %dhz)\n", namepart, FormatName(player->format), + player->sfinfo.samplerate); fflush(stdout); if(!StartPlayer(player)) @@ -340,11 +304,10 @@ int main(int argc, char **argv) } printf("Done.\n"); - /* All files done. Delete the player, and close down SDL_sound and OpenAL */ + /* All files done. Delete the player, and close down OpenAL */ DeletePlayer(player); player = NULL; - Sound_Quit(); CloseAL(); return 0; diff --git a/examples/alstreamcb.cpp b/examples/alstreamcb.cpp index fbc3d02d..8fb1f102 100644 --- a/examples/alstreamcb.cpp +++ b/examples/alstreamcb.cpp @@ -36,9 +36,7 @@ #include #include -#include "SDL_sound.h" -#include "SDL_audio.h" -#include "SDL_stdinc.h" +#include "sndfile.h" #include "AL/al.h" #include "AL/alc.h" @@ -46,14 +44,6 @@ #include "common/alhelpers.h" -#ifndef SDL_AUDIO_MASK_BITSIZE -#define SDL_AUDIO_MASK_BITSIZE (0xFF) -#endif -#ifndef SDL_AUDIO_BITSIZE -#define SDL_AUDIO_BITSIZE(x) (x & SDL_AUDIO_MASK_BITSIZE) -#endif - - #ifndef AL_SOFT_callback_buffer #define AL_SOFT_callback_buffer typedef unsigned int ALbitfieldSOFT; @@ -87,13 +77,12 @@ struct StreamPlayer { size_t mStartOffset{0}; /* Handle for the audio file to decode. */ - Sound_Sample *mSample{nullptr}; - Uint32 mAvailableData{0}; + SNDFILE *mSndfile{nullptr}; + SF_INFO mSfInfo{}; size_t mDecoderOffset{0}; /* The format of the callback samples. */ ALenum mFormat; - ALsizei mSampleRate; StreamPlayer() { @@ -111,18 +100,18 @@ struct StreamPlayer { { alDeleteSources(1, &mSource); alDeleteBuffers(1, &mBuffer); - if(mSample) - Sound_FreeSample(mSample); + if(mSndfile) + sf_close(mSndfile); } void close() { - if(mSample) + if(mSndfile) { alSourceRewind(mSource); alSourcei(mSource, AL_BUFFER, 0); - Sound_FreeSample(mSample); - mSample = nullptr; + sf_close(mSndfile); + mSndfile = nullptr; } } @@ -130,50 +119,30 @@ struct StreamPlayer { { close(); - /* Open the file in its normal format. */ - mSample = Sound_NewSampleFromFile(filename, nullptr, 0); - if(!mSample) + /* Open the file and figure out the OpenAL format. */ + mSndfile = sf_open(filename, SFM_READ, &mSfInfo); + if(!mSndfile) { - fprintf(stderr, "Could not open audio in %s\n", filename); + fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(mSndfile)); return false; } - /* Figure out the OpenAL format from the sample's format. */ mFormat = AL_NONE; - if(mSample->actual.channels == 1) - { - if(mSample->actual.format == AUDIO_U8) - mFormat = AL_FORMAT_MONO8; - else if(mSample->actual.format == AUDIO_S16SYS) - mFormat = AL_FORMAT_MONO16; - } - else if(mSample->actual.channels == 2) - { - if(mSample->actual.format == AUDIO_U8) - mFormat = AL_FORMAT_STEREO8; - else if(mSample->actual.format == AUDIO_S16SYS) - mFormat = AL_FORMAT_STEREO16; - } - if(!mFormat) + if(mSfInfo.channels == 1) + mFormat = AL_FORMAT_MONO16; + else if(mSfInfo.channels == 2) + mFormat = AL_FORMAT_STEREO16; + else { - fprintf(stderr, "Unsupported sample format: 0x%04x, %d channels\n", - mSample->actual.format, mSample->actual.channels); - Sound_FreeSample(mSample); - mSample = nullptr; + fprintf(stderr, "Unsupported channel count: %d\n", mSfInfo.channels); + sf_close(mSndfile); + mSndfile = nullptr; return false; } - mSampleRate = static_cast(mSample->actual.rate); - - const auto frame_size = Uint32{mSample->actual.channels} * - SDL_AUDIO_BITSIZE(mSample->actual.format) / 8; - - /* Set a 50ms decode buffer size. */ - Sound_SetBufferSize(mSample, static_cast(mSampleRate)*50/1000 * frame_size); - mAvailableData = 0; /* Set a 1s ring buffer size. */ - mBufferDataSize = static_cast(mSampleRate) * size_t{frame_size}; + mBufferDataSize = static_cast(mSfInfo.samplerate*mSfInfo.channels) * sizeof(short); mBufferData.reset(new ALbyte[mBufferDataSize]); mReadPos.store(0, std::memory_order_relaxed); mWritePos.store(0, std::memory_order_relaxed); @@ -239,34 +208,27 @@ struct StreamPlayer { bool prepare() { - alBufferCallbackSOFT(mBuffer, mFormat, mSampleRate, bufferCallbackC, this, 0); + alBufferCallbackSOFT(mBuffer, mFormat, mSfInfo.samplerate, bufferCallbackC, this, 0); alSourcei(mSource, AL_BUFFER, static_cast(mBuffer)); if(ALenum err{alGetError()}) { fprintf(stderr, "Failed to set callback: %s (0x%04x)\n", alGetString(err), err); return false; } - - mAvailableData = Sound_Decode(mSample); - if(!mAvailableData) - fprintf(stderr, "Failed to decode any samples: %s\n", Sound_GetError()); - return mAvailableData != 0; + return true; } bool update() { - constexpr int BadFlags{SOUND_SAMPLEFLAG_EOF | SOUND_SAMPLEFLAG_ERROR}; - ALenum state; ALint pos; alGetSourcei(mSource, AL_SAMPLE_OFFSET, &pos); alGetSourcei(mSource, AL_SOURCE_STATE, &state); + const size_t frame_size{static_cast(mSfInfo.channels) * sizeof(short)}; size_t woffset{mWritePos.load(std::memory_order_acquire)}; if(state != AL_INITIAL) { - const auto frame_size = Uint32{mSample->actual.channels} * - SDL_AUDIO_BITSIZE(mSample->actual.format) / 8; const size_t roffset{mReadPos.load(std::memory_order_relaxed)}; const size_t readable{((woffset >= roffset) ? woffset : (mBufferDataSize+woffset)) - roffset}; @@ -276,15 +238,17 @@ struct StreamPlayer { * the playback offset the source was started with. */ const size_t curtime{((state==AL_STOPPED) ? (mDecoderOffset-readable) / frame_size - : (static_cast(pos) + mStartOffset/frame_size)) / mSample->actual.rate}; + : (static_cast(pos) + mStartOffset/frame_size)) + / static_cast(mSfInfo.samplerate)}; printf("\r%3zus (%3zu%% full)", curtime, readable * 100 / mBufferDataSize); } else fputs("Starting...", stdout); fflush(stdout); - while(mAvailableData > 0) + while(!sf_error(mSndfile)) { + size_t read_bytes; const size_t roffset{mReadPos.load(std::memory_order_relaxed)}; if(roffset > woffset) { @@ -294,45 +258,39 @@ struct StreamPlayer { * instead of full. */ const size_t writable{roffset-woffset-1}; - /* Don't copy the sample data if it can't all fit. */ - if(writable < mAvailableData) break; + if(writable < frame_size) break; + + sf_count_t num_frames{sf_readf_short(mSndfile, + reinterpret_cast(&mBufferData[woffset]), + static_cast(writable/frame_size))}; + if(num_frames < 1) break; - memcpy(&mBufferData[woffset], mSample->buffer, mAvailableData); - woffset += mAvailableData; + read_bytes = static_cast(num_frames) * frame_size; + woffset += read_bytes; } else { /* If the read offset is at or behind the write offset, the * writeable area (might) wrap around. Make sure the sample - * data can fit, and calculate how much goes in front and in - * back. + * data can fit, and calculate how much can go in front before + * wrapping. */ - const size_t writable{mBufferDataSize+roffset-woffset-1}; - if(writable < mAvailableData) break; + const size_t writable{!roffset ? mBufferDataSize-woffset-1 : + (mBufferDataSize-woffset)}; + if(writable < frame_size) break; - const size_t todo1{std::min(mAvailableData, mBufferDataSize-woffset)}; - const size_t todo2{mAvailableData - todo1}; + sf_count_t num_frames{sf_readf_short(mSndfile, + reinterpret_cast(&mBufferData[woffset]), + static_cast(writable/frame_size))}; + if(num_frames < 1) break; - memcpy(&mBufferData[woffset], mSample->buffer, todo1); - woffset += todo1; + read_bytes = static_cast(num_frames) * frame_size; + woffset += read_bytes; if(woffset == mBufferDataSize) - { woffset = 0; - if(todo2 > 0) - { - memcpy(&mBufferData[woffset], static_cast(mSample->buffer)+todo1, - todo2); - woffset += todo2; - } - } } mWritePos.store(woffset, std::memory_order_release); - mDecoderOffset += mAvailableData; - - if(!(mSample->flags&BadFlags)) - mAvailableData = Sound_Decode(mSample); - else - mAvailableData = 0; + mDecoderOffset += read_bytes; } if(state != AL_PLAYING && state != AL_PAUSED) @@ -364,15 +322,14 @@ struct StreamPlayer { int main(int argc, char **argv) { - /* A simple RAII container for OpenAL and SDL_sound startup and shutdown. */ + /* A simple RAII container for OpenAL startup and shutdown. */ struct AudioManager { AudioManager(char ***argv_, int *argc_) { if(InitAL(argv_, argc_) != 0) throw std::runtime_error{"Failed to initialize OpenAL"}; - Sound_Init(); } - ~AudioManager() { Sound_Quit(); CloseAL(); } + ~AudioManager() { CloseAL(); } }; /* Print out usage if no arguments were specified */ @@ -413,7 +370,7 @@ int main(int argc, char **argv) namepart = argv[i]; printf("Playing: %s (%s, %dhz)\n", namepart, FormatName(player->mFormat), - player->mSampleRate); + player->mSfInfo.samplerate); fflush(stdout); if(!player->prepare()) -- cgit v1.2.3 From d70912c0345e402e8aed9835ce450330cd6a7d36 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 29 Mar 2020 20:37:58 -0700 Subject: Remove the QSA backend It's been broken for who knows how long, and could really do with a rewrite for the new interface anyway. --- CMakeLists.txt | 18 - alc/alc.cpp | 6 - alc/backends/qsa.cpp | 960 --------------------------------------------------- alc/backends/qsa.h | 19 - cmake/FindQSA.cmake | 34 -- config.h.in | 3 - 6 files changed, 1040 deletions(-) delete mode 100644 alc/backends/qsa.cpp delete mode 100644 alc/backends/qsa.h delete mode 100644 cmake/FindQSA.cmake (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 92769b6c..d00a8b51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -732,7 +732,6 @@ SET(HAVE_ALSA 0) SET(HAVE_OSS 0) SET(HAVE_SOLARIS 0) SET(HAVE_SNDIO 0) -SET(HAVE_QSA 0) SET(HAVE_DSOUND 0) SET(HAVE_WASAPI 0) SET(HAVE_WINMM 0) @@ -834,23 +833,6 @@ IF(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO) MESSAGE(FATAL_ERROR "Failed to enabled required SndIO backend") ENDIF() -# Check QSA backend -OPTION(ALSOFT_REQUIRE_QSA "Require QSA backend" OFF) -FIND_PACKAGE(QSA) -IF(QSA_FOUND) - OPTION(ALSOFT_BACKEND_QSA "Enable QSA backend" ON) - IF(ALSOFT_BACKEND_QSA) - SET(HAVE_QSA 1) - SET(BACKENDS "${BACKENDS} QSA (linked),") - SET(ALC_OBJS ${ALC_OBJS} alc/backends/qsa.cpp alc/backends/qsa.h) - SET(EXTRA_LIBS ${QSA_LIBRARIES} ${EXTRA_LIBS}) - SET(INC_PATHS ${INC_PATHS} ${QSA_INCLUDE_DIRS}) - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_QSA AND NOT HAVE_QSA) - MESSAGE(FATAL_ERROR "Failed to enabled required QSA backend") -ENDIF() - # Check Windows-only backends OPTION(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF) OPTION(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF) diff --git a/alc/alc.cpp b/alc/alc.cpp index efc822ff..9cd99f9d 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -124,9 +124,6 @@ #ifdef HAVE_OSS #include "backends/oss.h" #endif -#ifdef HAVE_QSA -#include "backends/qsa.h" -#endif #ifdef HAVE_DSOUND #include "backends/dsound.h" #endif @@ -187,9 +184,6 @@ BackendInfo BackendList[] = { #ifdef HAVE_OSS { "oss", OSSBackendFactory::getFactory }, #endif -#ifdef HAVE_QSA - { "qsa", QSABackendFactory::getFactory }, -#endif #ifdef HAVE_DSOUND { "dsound", DSoundBackendFactory::getFactory }, #endif diff --git a/alc/backends/qsa.cpp b/alc/backends/qsa.cpp deleted file mode 100644 index ef43e080..00000000 --- a/alc/backends/qsa.cpp +++ /dev/null @@ -1,960 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 2011-2013 by authors. - * 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 "backends/qsa.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "alcmain.h" -#include "alexcpt.h" -#include "alu.h" -#include "threads.h" - -#include -#include - - -namespace { - -struct qsa_data { - snd_pcm_t* pcmHandle{nullptr}; - int audio_fd{-1}; - - snd_pcm_channel_setup_t csetup{}; - snd_pcm_channel_params_t cparams{}; - - ALvoid* buffer{nullptr}; - ALsizei size{0}; - - std::atomic mKillNow{AL_TRUE}; - std::thread mThread; -}; - -struct DevMap { - ALCchar* name; - int card; - int dev; -}; - -al::vector DeviceNameMap; -al::vector CaptureNameMap; - -constexpr ALCchar qsaDevice[] = "QSA Default"; - -constexpr struct { - int32_t format; -} formatlist[] = { - {SND_PCM_SFMT_FLOAT_LE}, - {SND_PCM_SFMT_S32_LE}, - {SND_PCM_SFMT_U32_LE}, - {SND_PCM_SFMT_S16_LE}, - {SND_PCM_SFMT_U16_LE}, - {SND_PCM_SFMT_S8}, - {SND_PCM_SFMT_U8}, - {0}, -}; - -constexpr struct { - int32_t rate; -} ratelist[] = { - {192000}, - {176400}, - {96000}, - {88200}, - {48000}, - {44100}, - {32000}, - {24000}, - {22050}, - {16000}, - {12000}, - {11025}, - {8000}, - {0}, -}; - -constexpr struct { - int32_t channels; -} channellist[] = { - {8}, - {7}, - {6}, - {4}, - {2}, - {1}, - {0}, -}; - -void deviceList(int type, al::vector *devmap) -{ - snd_ctl_t* handle; - snd_pcm_info_t pcminfo; - int max_cards, card, err, dev; - DevMap entry; - char name[1024]; - snd_ctl_hw_info info; - - max_cards = snd_cards(); - if(max_cards < 0) - return; - - std::for_each(devmap->begin(), devmap->end(), - [](const DevMap &entry) -> void - { free(entry.name); } - ); - devmap->clear(); - - entry.name = strdup(qsaDevice); - entry.card = 0; - entry.dev = 0; - devmap->push_back(entry); - - for(card = 0;card < max_cards;card++) - { - if((err=snd_ctl_open(&handle, card)) < 0) - continue; - - if((err=snd_ctl_hw_info(handle, &info)) < 0) - { - snd_ctl_close(handle); - continue; - } - - for(dev = 0;dev < (int)info.pcmdevs;dev++) - { - if((err=snd_ctl_pcm_info(handle, dev, &pcminfo)) < 0) - continue; - - if((type==SND_PCM_CHANNEL_PLAYBACK && (pcminfo.flags&SND_PCM_INFO_PLAYBACK)) || - (type==SND_PCM_CHANNEL_CAPTURE && (pcminfo.flags&SND_PCM_INFO_CAPTURE))) - { - snprintf(name, sizeof(name), "%s [%s] (hw:%d,%d)", info.name, pcminfo.name, card, dev); - entry.name = strdup(name); - entry.card = card; - entry.dev = dev; - - devmap->push_back(entry); - TRACE("Got device \"%s\", card %d, dev %d\n", name, card, dev); - } - } - snd_ctl_close(handle); - } -} - - -/* Wrappers to use an old-style backend with the new interface. */ -struct PlaybackWrapper final : public BackendBase { - PlaybackWrapper(ALCdevice *device) noexcept : BackendBase{device} { } - ~PlaybackWrapper() override; - - void open(const ALCchar *name) override; - bool reset() override; - bool start() override; - void stop() override; - - std::unique_ptr mExtraData; - - DEF_NEWDEL(PlaybackWrapper) -}; - - -FORCE_ALIGN static int qsa_proc_playback(void *ptr) -{ - PlaybackWrapper *self = static_cast(ptr); - ALCdevice *device = self->mDevice; - qsa_data *data = self->mExtraData.get(); - snd_pcm_channel_status_t status; - sched_param param; - char* write_ptr; - ALint len; - int sret; - - SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); - - /* Increase default 10 priority to 11 to avoid jerky sound */ - SchedGet(0, 0, ¶m); - param.sched_priority=param.sched_curpriority+1; - SchedSet(0, 0, SCHED_NOCHANGE, ¶m); - - const ALint frame_size = device->frameSizeFromFmt(); - - while(!data->mKillNow.load(std::memory_order_acquire)) - { - pollfd pollitem{}; - pollitem.fd = data->audio_fd; - pollitem.events = POLLOUT; - - /* Select also works like time slice to OS */ - sret = poll(&pollitem, 1, 2000); - if(sret == -1) - { - if(errno == EINTR || errno == EAGAIN) - continue; - ERR("poll error: %s\n", strerror(errno)); - aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno)); - break; - } - if(sret == 0) - { - ERR("poll timeout\n"); - continue; - } - - len = data->size; - write_ptr = static_cast(data->buffer); - aluMixData(device, write_ptr, len/frame_size); - while(len>0 && !data->mKillNow.load(std::memory_order_acquire)) - { - int wrote = snd_pcm_plugin_write(data->pcmHandle, write_ptr, len); - if(wrote <= 0) - { - if(errno==EAGAIN || errno==EWOULDBLOCK) - continue; - - memset(&status, 0, sizeof(status)); - status.channel = SND_PCM_CHANNEL_PLAYBACK; - - snd_pcm_plugin_status(data->pcmHandle, &status); - - /* we need to reinitialize the sound channel if we've underrun the buffer */ - if(status.status == SND_PCM_STATUS_UNDERRUN || - status.status == SND_PCM_STATUS_READY) - { - if(snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK) < 0) - { - aluHandleDisconnect(device, "Playback recovery failed"); - break; - } - } - } - else - { - write_ptr += wrote; - len -= wrote; - } - } - } - - return 0; -} - -/************/ -/* Playback */ -/************/ - -static ALCenum qsa_open_playback(PlaybackWrapper *self, const ALCchar* deviceName) -{ - ALCdevice *device = self->mDevice; - int card, dev; - int status; - - std::unique_ptr data{new qsa_data{}}; - data->mKillNow.store(AL_TRUE, std::memory_order_relaxed); - - if(!deviceName) - deviceName = qsaDevice; - - if(strcmp(deviceName, qsaDevice) == 0) - status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_PLAYBACK); - else - { - if(DeviceNameMap.empty()) - deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap); - - auto iter = std::find_if(DeviceNameMap.begin(), DeviceNameMap.end(), - [deviceName](const DevMap &entry) -> bool - { return entry.name && strcmp(deviceName, entry.name) == 0; } - ); - if(iter == DeviceNameMap.cend()) - return ALC_INVALID_DEVICE; - - status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_PLAYBACK); - } - - if(status < 0) - return ALC_INVALID_DEVICE; - - data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK); - if(data->audio_fd < 0) - { - snd_pcm_close(data->pcmHandle); - return ALC_INVALID_DEVICE; - } - - device->DeviceName = deviceName; - self->mExtraData = std::move(data); - - return ALC_NO_ERROR; -} - -static void qsa_close_playback(PlaybackWrapper *self) -{ - qsa_data *data = self->mExtraData.get(); - - if (data->buffer!=NULL) - { - free(data->buffer); - data->buffer=NULL; - } - - snd_pcm_close(data->pcmHandle); - - self->mExtraData = nullptr; -} - -static ALCboolean qsa_reset_playback(PlaybackWrapper *self) -{ - ALCdevice *device = self->mDevice; - qsa_data *data = self->mExtraData.get(); - int32_t format=-1; - - switch(device->FmtType) - { - case DevFmtByte: - format=SND_PCM_SFMT_S8; - break; - case DevFmtUByte: - format=SND_PCM_SFMT_U8; - break; - case DevFmtShort: - format=SND_PCM_SFMT_S16_LE; - break; - case DevFmtUShort: - format=SND_PCM_SFMT_U16_LE; - break; - case DevFmtInt: - format=SND_PCM_SFMT_S32_LE; - break; - case DevFmtUInt: - format=SND_PCM_SFMT_U32_LE; - break; - case DevFmtFloat: - format=SND_PCM_SFMT_FLOAT_LE; - break; - } - - /* we actually don't want to block on writes */ - snd_pcm_nonblock_mode(data->pcmHandle, 1); - /* Disable mmap to control data transfer to the audio device */ - snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP); - snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_BUFFER_PARTIAL_BLOCKS); - - // configure a sound channel - memset(&data->cparams, 0, sizeof(data->cparams)); - data->cparams.channel=SND_PCM_CHANNEL_PLAYBACK; - data->cparams.mode=SND_PCM_MODE_BLOCK; - data->cparams.start_mode=SND_PCM_START_FULL; - data->cparams.stop_mode=SND_PCM_STOP_STOP; - - data->cparams.buf.block.frag_size=device->UpdateSize * device->frameSizeFromFmt(); - data->cparams.buf.block.frags_max=device->BufferSize / device->UpdateSize; - data->cparams.buf.block.frags_min=data->cparams.buf.block.frags_max; - - data->cparams.format.interleave=1; - data->cparams.format.rate=device->Frequency; - data->cparams.format.voices=device->channelsFromFmt(); - data->cparams.format.format=format; - - if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0) - { - int original_rate=data->cparams.format.rate; - int original_voices=data->cparams.format.voices; - int original_format=data->cparams.format.format; - int it; - int jt; - - for (it=0; it<1; it++) - { - /* Check for second pass */ - if (it==1) - { - original_rate=ratelist[0].rate; - original_voices=channellist[0].channels; - original_format=formatlist[0].format; - } - - do { - /* At first downgrade sample format */ - jt=0; - do { - if (formatlist[jt].format==data->cparams.format.format) - { - data->cparams.format.format=formatlist[jt+1].format; - break; - } - if (formatlist[jt].format==0) - { - data->cparams.format.format=0; - break; - } - jt++; - } while(1); - - if (data->cparams.format.format==0) - { - data->cparams.format.format=original_format; - - /* At secod downgrade sample rate */ - jt=0; - do { - if (ratelist[jt].rate==data->cparams.format.rate) - { - data->cparams.format.rate=ratelist[jt+1].rate; - break; - } - if (ratelist[jt].rate==0) - { - data->cparams.format.rate=0; - break; - } - jt++; - } while(1); - - if (data->cparams.format.rate==0) - { - data->cparams.format.rate=original_rate; - data->cparams.format.format=original_format; - - /* At third downgrade channels number */ - jt=0; - do { - if(channellist[jt].channels==data->cparams.format.voices) - { - data->cparams.format.voices=channellist[jt+1].channels; - break; - } - if (channellist[jt].channels==0) - { - data->cparams.format.voices=0; - break; - } - jt++; - } while(1); - } - - if (data->cparams.format.voices==0) - { - break; - } - } - - data->cparams.buf.block.frag_size=device->UpdateSize* - data->cparams.format.voices* - snd_pcm_format_width(data->cparams.format.format)/8; - data->cparams.buf.block.frags_max=device->NumUpdates; - data->cparams.buf.block.frags_min=device->NumUpdates; - if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0) - { - continue; - } - else - { - break; - } - } while(1); - - if (data->cparams.format.voices!=0) - { - break; - } - } - - if (data->cparams.format.voices==0) - { - return ALC_FALSE; - } - } - - if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0) - { - return ALC_FALSE; - } - - memset(&data->csetup, 0, sizeof(data->csetup)); - data->csetup.channel=SND_PCM_CHANNEL_PLAYBACK; - if (snd_pcm_plugin_setup(data->pcmHandle, &data->csetup)<0) - { - return ALC_FALSE; - } - - /* now fill back to the our AL device */ - device->Frequency=data->cparams.format.rate; - - switch (data->cparams.format.voices) - { - case 1: - device->FmtChans=DevFmtMono; - break; - case 2: - device->FmtChans=DevFmtStereo; - break; - case 4: - device->FmtChans=DevFmtQuad; - break; - case 6: - device->FmtChans=DevFmtX51; - break; - case 7: - device->FmtChans=DevFmtX61; - break; - case 8: - device->FmtChans=DevFmtX71; - break; - default: - device->FmtChans=DevFmtMono; - break; - } - - switch (data->cparams.format.format) - { - case SND_PCM_SFMT_S8: - device->FmtType=DevFmtByte; - break; - case SND_PCM_SFMT_U8: - device->FmtType=DevFmtUByte; - break; - case SND_PCM_SFMT_S16_LE: - device->FmtType=DevFmtShort; - break; - case SND_PCM_SFMT_U16_LE: - device->FmtType=DevFmtUShort; - break; - case SND_PCM_SFMT_S32_LE: - device->FmtType=DevFmtInt; - break; - case SND_PCM_SFMT_U32_LE: - device->FmtType=DevFmtUInt; - break; - case SND_PCM_SFMT_FLOAT_LE: - device->FmtType=DevFmtFloat; - break; - default: - device->FmtType=DevFmtShort; - break; - } - - SetDefaultChannelOrder(device); - - device->UpdateSize=data->csetup.buf.block.frag_size / device->frameSizeFromFmt(); - device->NumUpdates=data->csetup.buf.block.frags; - - data->size=data->csetup.buf.block.frag_size; - data->buffer=malloc(data->size); - if (!data->buffer) - { - return ALC_FALSE; - } - - return ALC_TRUE; -} - -static ALCboolean qsa_start_playback(PlaybackWrapper *self) -{ - qsa_data *data = self->mExtraData.get(); - - try { - data->mKillNow.store(AL_FALSE, std::memory_order_release); - data->mThread = std::thread(qsa_proc_playback, self); - return ALC_TRUE; - } - catch(std::exception& e) { - ERR("Could not create playback thread: %s\n", e.what()); - } - catch(...) { - } - return ALC_FALSE; -} - -static void qsa_stop_playback(PlaybackWrapper *self) -{ - qsa_data *data = self->mExtraData.get(); - - if(data->mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !data->mThread.joinable()) - return; - data->mThread.join(); -} - - -PlaybackWrapper::~PlaybackWrapper() -{ - if(mExtraData) - qsa_close_playback(this); -} - -void PlaybackWrapper::open(const ALCchar *name) -{ - if(auto err = qsa_open_playback(this, name)) - throw al::backend_exception{ALC_INVALID_VALUE, "%d", err}; -} - -bool PlaybackWrapper::reset() -{ - if(!qsa_reset_playback(this)) - throw al::backend_exception{ALC_INVALID_VALUE, ""}; - return true; -} - -bool PlaybackWrapper::start() -{ return qsa_start_playback(this); } - -void PlaybackWrapper::stop() -{ qsa_stop_playback(this); } - - -/***********/ -/* Capture */ -/***********/ - -struct CaptureWrapper final : public BackendBase { - CaptureWrapper(ALCdevice *device) noexcept : BackendBase{device} { } - ~CaptureWrapper() override; - - void open(const ALCchar *name) override; - bool start() override; - void stop() override; - ALCenum captureSamples(al::byte *buffer, ALCuint samples) override; - ALCuint availableSamples() override; - - std::unique_ptr mExtraData; - - DEF_NEWDEL(CaptureWrapper) -}; - -static ALCenum qsa_open_capture(CaptureWrapper *self, const ALCchar *deviceName) -{ - ALCdevice *device = self->mDevice; - int card, dev; - int format=-1; - int status; - - std::unique_ptr data{new qsa_data{}}; - - if(!deviceName) - deviceName = qsaDevice; - - if(strcmp(deviceName, qsaDevice) == 0) - status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_CAPTURE); - else - { - if(CaptureNameMap.empty()) - deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap); - - auto iter = std::find_if(CaptureNameMap.cbegin(), CaptureNameMap.cend(), - [deviceName](const DevMap &entry) -> bool - { return entry.name && strcmp(deviceName, entry.name) == 0; } - ); - if(iter == CaptureNameMap.cend()) - return ALC_INVALID_DEVICE; - - status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_CAPTURE); - } - - if(status < 0) - return ALC_INVALID_DEVICE; - - data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE); - if(data->audio_fd < 0) - { - snd_pcm_close(data->pcmHandle); - return ALC_INVALID_DEVICE; - } - - device->DeviceName = deviceName; - - switch (device->FmtType) - { - case DevFmtByte: - format=SND_PCM_SFMT_S8; - break; - case DevFmtUByte: - format=SND_PCM_SFMT_U8; - break; - case DevFmtShort: - format=SND_PCM_SFMT_S16_LE; - break; - case DevFmtUShort: - format=SND_PCM_SFMT_U16_LE; - break; - case DevFmtInt: - format=SND_PCM_SFMT_S32_LE; - break; - case DevFmtUInt: - format=SND_PCM_SFMT_U32_LE; - break; - case DevFmtFloat: - format=SND_PCM_SFMT_FLOAT_LE; - break; - } - - /* we actually don't want to block on reads */ - snd_pcm_nonblock_mode(data->pcmHandle, 1); - /* Disable mmap to control data transfer to the audio device */ - snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP); - - /* configure a sound channel */ - memset(&data->cparams, 0, sizeof(data->cparams)); - data->cparams.mode=SND_PCM_MODE_BLOCK; - data->cparams.channel=SND_PCM_CHANNEL_CAPTURE; - data->cparams.start_mode=SND_PCM_START_GO; - data->cparams.stop_mode=SND_PCM_STOP_STOP; - - data->cparams.buf.block.frag_size=device->UpdateSize * device->frameSizeFromFmt(); - data->cparams.buf.block.frags_max=device->NumUpdates; - data->cparams.buf.block.frags_min=device->NumUpdates; - - data->cparams.format.interleave=1; - data->cparams.format.rate=device->Frequency; - data->cparams.format.voices=device->channelsFromFmt(); - data->cparams.format.format=format; - - if(snd_pcm_plugin_params(data->pcmHandle, &data->cparams) < 0) - { - snd_pcm_close(data->pcmHandle); - return ALC_INVALID_VALUE; - } - - self->mExtraData = std::move(data); - - return ALC_NO_ERROR; -} - -static void qsa_close_capture(CaptureWrapper *self) -{ - qsa_data *data = self->mExtraData.get(); - - if (data->pcmHandle!=nullptr) - snd_pcm_close(data->pcmHandle); - data->pcmHandle = nullptr; - - self->mExtraData = nullptr; -} - -static void qsa_start_capture(CaptureWrapper *self) -{ - qsa_data *data = self->mExtraData.get(); - int rstatus; - - if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0) - { - ERR("capture prepare failed: %s\n", snd_strerror(rstatus)); - return; - } - - memset(&data->csetup, 0, sizeof(data->csetup)); - data->csetup.channel=SND_PCM_CHANNEL_CAPTURE; - if ((rstatus=snd_pcm_plugin_setup(data->pcmHandle, &data->csetup))<0) - { - ERR("capture setup failed: %s\n", snd_strerror(rstatus)); - return; - } - - snd_pcm_capture_go(data->pcmHandle); -} - -static void qsa_stop_capture(CaptureWrapper *self) -{ - qsa_data *data = self->mExtraData.get(); - snd_pcm_capture_flush(data->pcmHandle); -} - -static ALCuint qsa_available_samples(CaptureWrapper *self) -{ - ALCdevice *device = self->mDevice; - qsa_data *data = self->mExtraData.get(); - snd_pcm_channel_status_t status; - ALint frame_size = device->frameSizeFromFmt(); - ALint free_size; - int rstatus; - - memset(&status, 0, sizeof (status)); - status.channel=SND_PCM_CHANNEL_CAPTURE; - snd_pcm_plugin_status(data->pcmHandle, &status); - if ((status.status==SND_PCM_STATUS_OVERRUN) || - (status.status==SND_PCM_STATUS_READY)) - { - if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0) - { - ERR("capture prepare failed: %s\n", snd_strerror(rstatus)); - aluHandleDisconnect(device, "Failed capture recovery: %s", snd_strerror(rstatus)); - return 0; - } - - snd_pcm_capture_go(data->pcmHandle); - return 0; - } - - free_size=data->csetup.buf.block.frag_size*data->csetup.buf.block.frags; - free_size-=status.free; - - return free_size/frame_size; -} - -static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuint samples) -{ - ALCdevice *device = self->mDevice; - qsa_data *data = self->mExtraData.get(); - char* read_ptr; - snd_pcm_channel_status_t status; - int selectret; - int bytes_read; - ALint frame_size=device->frameSizeFromFmt(); - ALint len=samples*frame_size; - int rstatus; - - read_ptr = static_cast(buffer); - - while (len>0) - { - pollfd pollitem{}; - pollitem.fd = data->audio_fd; - pollitem.events = POLLOUT; - - /* Select also works like time slice to OS */ - bytes_read=0; - selectret = poll(&pollitem, 1, 2000); - switch (selectret) - { - case -1: - aluHandleDisconnect(device, "Failed to check capture samples"); - return ALC_INVALID_DEVICE; - case 0: - break; - default: - bytes_read=snd_pcm_plugin_read(data->pcmHandle, read_ptr, len); - break; - } - - if (bytes_read<=0) - { - if ((errno==EAGAIN) || (errno==EWOULDBLOCK)) - { - continue; - } - - memset(&status, 0, sizeof (status)); - status.channel=SND_PCM_CHANNEL_CAPTURE; - snd_pcm_plugin_status(data->pcmHandle, &status); - - /* we need to reinitialize the sound channel if we've overrun the buffer */ - if ((status.status==SND_PCM_STATUS_OVERRUN) || - (status.status==SND_PCM_STATUS_READY)) - { - if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0) - { - ERR("capture prepare failed: %s\n", snd_strerror(rstatus)); - aluHandleDisconnect(device, "Failed capture recovery: %s", - snd_strerror(rstatus)); - return ALC_INVALID_DEVICE; - } - snd_pcm_capture_go(data->pcmHandle); - } - } - else - { - read_ptr+=bytes_read; - len-=bytes_read; - } - } - - return ALC_NO_ERROR; -} - - -CaptureWrapper::~CaptureWrapper() -{ - if(mExtraData) - qsa_close_capture(this); -} - -void CaptureWrapper::open(const ALCchar *name) -{ - if(auto err = qsa_open_capture(this, name)) - throw al::backend_exception{ALC_INVALID_VALUE, "%d", err}; -} - -bool CaptureWrapper::start() -{ qsa_start_capture(this); return true; } - -void CaptureWrapper::stop() -{ qsa_stop_capture(this); } - -ALCenum CaptureWrapper::captureSamples(al::byte *buffer, ALCuint samples) -{ return qsa_capture_samples(this, buffer, samples); } - -ALCuint CaptureWrapper::availableSamples() -{ return qsa_available_samples(this); } - -} // namespace - - -bool QSABackendFactory::init() -{ return true; } - -bool QSABackendFactory::querySupport(BackendType type) -{ return (type == BackendType::Playback || type == BackendType::Capture); } - -void QSABackendFactory::probe(DevProbe type, std::string *outnames) -{ - auto add_device = [outnames](const DevMap &entry) -> void - { - const char *n = entry.name; - if(n && n[0]) - outnames->append(n, strlen(n)+1); - }; - - switch (type) - { - case DevProbe::Playback: - deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap); - std::for_each(DeviceNameMap.cbegin(), DeviceNameMap.cend(), add_device); - break; - case DevProbe::Capture: - deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap); - std::for_each(CaptureNameMap.cbegin(), CaptureNameMap.cend(), add_device); - break; - } -} - -BackendPtr QSABackendFactory::createBackend(ALCdevice *device, BackendType type) -{ - if(type == BackendType::Playback) - return BackendPtr{new PlaybackWrapper{device}}; - if(type == BackendType::Capture) - return BackendPtr{new CaptureWrapper{device}}; - return nullptr; -} - -BackendFactory &QSABackendFactory::getFactory() -{ - static QSABackendFactory factory{}; - return factory; -} diff --git a/alc/backends/qsa.h b/alc/backends/qsa.h deleted file mode 100644 index da548bba..00000000 --- a/alc/backends/qsa.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef BACKENDS_QSA_H -#define BACKENDS_QSA_H - -#include "backends/base.h" - -struct QSABackendFactory final : public BackendFactory { -public: - bool init() override; - - bool querySupport(BackendType type) override; - - void probe(DevProbe type, std::string *outnames) override; - - BackendPtr createBackend(ALCdevice *device, BackendType type) override; - - static BackendFactory &getFactory(); -}; - -#endif /* BACKENDS_QSA_H */ diff --git a/cmake/FindQSA.cmake b/cmake/FindQSA.cmake deleted file mode 100644 index 0ad1fd43..00000000 --- a/cmake/FindQSA.cmake +++ /dev/null @@ -1,34 +0,0 @@ -# - Find QSA includes and libraries -# -# QSA_FOUND - True if QSA_INCLUDE_DIR & QSA_LIBRARY are found -# QSA_LIBRARIES - Set when QSA_LIBRARY is found -# QSA_INCLUDE_DIRS - Set when QSA_INCLUDE_DIR is found -# -# QSA_INCLUDE_DIR - where to find sys/asoundlib.h, etc. -# QSA_LIBRARY - the asound library -# - -# Only check for QSA on QNX, because it conflicts with ALSA. -if("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX") - find_path(QSA_INCLUDE_DIR - NAMES sys/asoundlib.h - DOC "The QSA include directory" - ) - - find_library(QSA_LIBRARY - NAMES asound - DOC "The QSA library" - ) -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(QSA - REQUIRED_VARS QSA_LIBRARY QSA_INCLUDE_DIR -) - -if(QSA_FOUND) - set(QSA_LIBRARIES ${QSA_LIBRARY}) - set(QSA_INCLUDE_DIRS ${QSA_INCLUDE_DIR}) -endif() - -mark_as_advanced(QSA_INCLUDE_DIR QSA_LIBRARY) diff --git a/config.h.in b/config.h.in index d59a954b..8beb13a4 100644 --- a/config.h.in +++ b/config.h.in @@ -41,9 +41,6 @@ /* Define if we have the SndIO backend */ #cmakedefine HAVE_SNDIO -/* Define if we have the QSA backend */ -#cmakedefine HAVE_QSA - /* Define if we have the WASAPI backend */ #cmakedefine HAVE_WASAPI -- cgit v1.2.3 From cc009b8aa009f7da06d546781748d29c9e9c0dd1 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 30 Mar 2020 01:16:15 -0700 Subject: Move the FrontStablizer definition to its own header --- CMakeLists.txt | 1 + alc/alc.cpp | 1 + alc/alcmain.h | 16 +--------------- alc/alu.cpp | 1 + alc/front_stablizer.h | 24 ++++++++++++++++++++++++ 5 files changed, 28 insertions(+), 15 deletions(-) create mode 100644 alc/front_stablizer.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index d00a8b51..2255baeb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -623,6 +623,7 @@ SET(ALC_OBJS alc/filters/splitter.h alc/fpu_ctrl.cpp alc/fpu_ctrl.h + alc/front_stablizer.h alc/helpers.cpp alc/hrtf.cpp alc/hrtf.h diff --git a/alc/alc.cpp b/alc/alc.cpp index 9cd99f9d..1bb859f7 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -80,6 +80,7 @@ #include "filters/nfc.h" #include "filters/splitter.h" #include "fpu_ctrl.h" +#include "front_stablizer.h" #include "hrtf.h" #include "inprogext.h" #include "intrusive_ptr.h" diff --git a/alc/alcmain.h b/alc/alcmain.h index b1f1d45e..e1dcd2fa 100644 --- a/alc/alcmain.h +++ b/alc/alcmain.h @@ -37,6 +37,7 @@ struct ALfilter; struct BackendBase; struct Compressor; struct EffectState; +struct FrontStablizer; struct Uhj2Encoder; struct bs2b; @@ -170,21 +171,6 @@ using FloatBufferLine = std::array; #define MAX_RESAMPLER_PADDING 48 -struct FrontStablizer { - static constexpr size_t DelayLength{256u}; - - alignas(16) float DelayBuf[MAX_OUTPUT_CHANNELS][DelayLength]; - - BandSplitter LFilter, RFilter; - alignas(16) float LSplit[2][BUFFERSIZE]; - alignas(16) float RSplit[2][BUFFERSIZE]; - - alignas(16) float TempBuf[BUFFERSIZE + DelayLength]; - - DEF_NEWDEL(FrontStablizer) -}; - - struct MixParams { /* Coefficient channel mapping for mixing to the buffer. */ std::array AmbiMap{}; diff --git a/alc/alu.cpp b/alc/alu.cpp index 889c2271..df9af3fe 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -66,6 +66,7 @@ #include "filters/nfc.h" #include "filters/splitter.h" #include "fpu_ctrl.h" +#include "front_stablizer.h" #include "hrtf.h" #include "inprogext.h" #include "mastering.h" diff --git a/alc/front_stablizer.h b/alc/front_stablizer.h new file mode 100644 index 00000000..ad31508a --- /dev/null +++ b/alc/front_stablizer.h @@ -0,0 +1,24 @@ +#ifndef ALC_FRONT_STABLIZER_H +#define ALC_FRONT_STABLIZER_H + +#include "alcmain.h" +#include "almalloc.h" +#include "devformat.h" +#include "filters/splitter.h" + + +struct FrontStablizer { + static constexpr size_t DelayLength{256u}; + + alignas(16) float DelayBuf[MAX_OUTPUT_CHANNELS][DelayLength]; + + BandSplitter LFilter, RFilter; + alignas(16) float LSplit[2][BUFFERSIZE]; + alignas(16) float RSplit[2][BUFFERSIZE]; + + alignas(16) float TempBuf[BUFFERSIZE + DelayLength]; + + DEF_NEWDEL(FrontStablizer) +}; + +#endif /* ALC_FRONT_STABLIZER_H */ -- cgit v1.2.3 From 8853519d896f11b678873f0afacac16b3cdebc27 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 1 Apr 2020 23:17:46 -0700 Subject: Generate the bsinc tables using constexpr methods All the methods used should be compliant with C++14 constexpr rules. However, the number of scales and phases cause GenerateBSincCoeffs to reach the allowed step limit, preventing full compile-time generation. It's not a terribly big deal, it'll generate them very quickly when loading, but it does prevent using shared read-only memory pages. --- CMakeLists.txt | 3 + alc/alu.cpp | 4 +- alc/mixer/mixer_c.cpp | 2 +- alc/mixer/mixer_neon.cpp | 1 + alc/mixer/mixer_sse.cpp | 1 + alc/voice.h | 11 +- common/bsinc_defs.h | 13 ++ common/bsinc_tables.cpp | 340 +++++++++++++++++++++++++++++++++++++++++++++++ common/bsinc_tables.h | 17 +++ 9 files changed, 380 insertions(+), 12 deletions(-) create mode 100644 common/bsinc_defs.h create mode 100644 common/bsinc_tables.cpp create mode 100644 common/bsinc_tables.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 2255baeb..3ce37e84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -545,6 +545,9 @@ SET(COMMON_OBJS common/alstring.cpp common/alstring.h common/atomic.h + common/bsinc_defs.h + common/bsinc_tables.cpp + common/bsinc_tables.h common/dynload.cpp common/dynload.h common/endiantest.h diff --git a/alc/alu.cpp b/alc/alu.cpp index df9af3fe..18973942 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -80,10 +80,10 @@ #include "vecmat.h" #include "voice.h" -#include "bsinc_inc.h" +#include "bsinc_tables.h" -static_assert(!(MAX_RESAMPLER_PADDING&1) && MAX_RESAMPLER_PADDING >= bsinc24.m[0], +static_assert(!(MAX_RESAMPLER_PADDING&1) && MAX_RESAMPLER_PADDING >= BSINC_POINTS_MAX, "MAX_RESAMPLER_PADDING is not a multiple of two, or is too small"); diff --git a/alc/mixer/mixer_c.cpp b/alc/mixer/mixer_c.cpp index c086dd8d..6b68d821 100644 --- a/alc/mixer/mixer_c.cpp +++ b/alc/mixer/mixer_c.cpp @@ -6,7 +6,7 @@ #include "alcmain.h" #include "alu.h" - +#include "bsinc_defs.h" #include "defs.h" #include "hrtfbase.h" diff --git a/alc/mixer/mixer_neon.cpp b/alc/mixer/mixer_neon.cpp index afc9768a..2cdf21c3 100644 --- a/alc/mixer/mixer_neon.cpp +++ b/alc/mixer/mixer_neon.cpp @@ -10,6 +10,7 @@ #include "alu.h" #include "hrtf.h" #include "defs.h" +#include "bsinc_defs.h" #include "hrtfbase.h" diff --git a/alc/mixer/mixer_sse.cpp b/alc/mixer/mixer_sse.cpp index 3bc7b30f..32345522 100644 --- a/alc/mixer/mixer_sse.cpp +++ b/alc/mixer/mixer_sse.cpp @@ -9,6 +9,7 @@ #include "alcmain.h" #include "alu.h" +#include "bsinc_defs.h" #include "defs.h" #include "hrtfbase.h" diff --git a/alc/voice.h b/alc/voice.h index 7adffe7f..f4d09ad3 100644 --- a/alc/voice.h +++ b/alc/voice.h @@ -44,16 +44,9 @@ enum class Resampler { }; 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< +#include +#include +#include +#include + +#include "math_defs.h" +#include "vector.h" + + +namespace { + +/* The max points includes the doubling for downsampling, so the maximum number + * of base sample points is 24, which is 23rd order. + */ +constexpr int BSincPointsMax{BSINC_POINTS_MAX}; +constexpr int BSincPointsHalf{BSincPointsMax / 2}; + +constexpr int BSincPhaseCount{BSINC_PHASE_COUNT}; +constexpr int BSincScaleCount{BSINC_SCALE_COUNT}; + + +template +constexpr std::enable_if_t::value,T> sqrt(T x) +{ + if(!(x >= 0 && x < std::numeric_limits::infinity())) + throw std::domain_error{"Invalid sqrt value"}; + + T cur{x}, prev{0}; + while(cur != prev) + { + prev = cur; + cur = 0.5f*(cur + x/cur); + } + return cur; +} + +template +constexpr std::enable_if_t::value,T> sin(T x) +{ + if(x >= al::MathDefs::Tau()) + { + if(!(x < 65536)) + throw std::domain_error{"Invalid sin value"}; + do { + x -= al::MathDefs::Tau(); + } while(x >= al::MathDefs::Tau()); + } + else if(x < 0) + { + if(!(x > -65536)) + throw std::domain_error{"Invalid sin value"}; + do { + x += al::MathDefs::Tau(); + } while(x < 0); + } + + T prev{x}, n{6}; + int i{4}, s{-1}; + const T xx{x*x}; + T t{xx*x}; + + T cur{prev + t*s/n}; + while(prev != cur) + { + prev = cur; + n *= i*(i+1); + i += 2; + s = -s; + t *= xx; + + cur += t*s/n; + } + return cur; +} + + +/* This is the normalized cardinal sine (sinc) function. + * + * sinc(x) = { 1, x = 0 + * { sin(pi x) / (pi x), otherwise. + */ +constexpr double Sinc(const double x) +{ + if(std::abs(x) < 1e-15) + return 1.0; + return sin(al::MathDefs::Pi()*x) / (al::MathDefs::Pi()*x); +} + +/* The zero-order modified Bessel function of the first kind, used for the + * Kaiser window. + * + * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k) + * = sum_{k=0}^inf ((x / 2)^k / k!)^2 + */ +constexpr double BesselI_0(const double x) +{ + /* Start at k=1 since k=0 is trivial. */ + const double x2{x / 2.0}; + double term{1.0}; + double sum{1.0}; + double last_sum{}; + int k{1}; + + /* Let the integration converge until the term of the sum is no longer + * significant. + */ + do { + const double y{x2 / k}; + ++k; + last_sum = sum; + term *= y * y; + sum += term; + } while(sum != last_sum); + + return sum; +} + +/* Calculate a Kaiser window from the given beta value and a normalized k + * [-1, 1]. + * + * w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1 + * { 0, elsewhere. + * + * Where k can be calculated as: + * + * k = i / l, where -l <= i <= l. + * + * or: + * + * k = 2 i / M - 1, where 0 <= i <= M. + */ +constexpr double Kaiser(const double beta, const double k, const double besseli_0_beta) +{ + if(!(k >= -1.0 && k <= 1.0)) + return 0.0; + return BesselI_0(beta * sqrt(1.0 - k*k)) / besseli_0_beta; +} + +/* Calculates the (normalized frequency) transition width of the Kaiser window. + * Rejection is in dB. + */ +constexpr double CalcKaiserWidth(const double rejection, const int order) +{ + if(rejection > 21.19) + return (rejection - 7.95) / (order * 2.285 * al::MathDefs::Tau()); + /* This enforces a minimum rejection of just above 21.18dB */ + return 5.79 / (order * al::MathDefs::Tau()); +} + +/* Calculates the beta value of the Kaiser window. Rejection is in dB. */ +constexpr double CalcKaiserBeta(const double rejection) +{ + if(rejection > 50.0) + return 0.1102 * (rejection-8.7); + else if(rejection >= 21.0) + return (0.5842 * std::pow(rejection-21.0, 0.4)) + (0.07886 * (rejection-21.0)); + return 0.0; +} + + +struct BSincHeader { + double width; + double beta; + double scaleBase; + double scaleRange; + double besseli_0_beta; + + int a[BSINC_SCALE_COUNT]; + int total_size; +}; + +constexpr BSincHeader GenerateBSincHeader(int Rejection, int Order) +{ + BSincHeader ret{}; + ret.width = CalcKaiserWidth(Rejection, Order); + ret.beta = CalcKaiserBeta(Rejection); + ret.scaleBase = ret.width / 2.0; + ret.scaleRange = 1.0 - ret.scaleBase; + ret.besseli_0_beta = BesselI_0(ret.beta); + + int num_points{Order+1}; + for(int si{0};si < BSincScaleCount;++si) + { + const double scale{ret.scaleBase + (ret.scaleRange * si / (BSincScaleCount - 1))}; + const int a{std::min(static_cast(num_points / 2.0 / scale), num_points)}; + const int m{2 * a}; + + ret.a[si] = a; + ret.total_size += 4 * BSincPhaseCount * ((m+3) & ~3); + } + + return ret; +} + +/* 11th and 23rd order filters (12 and 24-point respectively) with a 60dB drop + * at nyquist. Each filter will scale up the order when downsampling, to 23 and + * 47th order respectively. + */ +constexpr BSincHeader bsinc12_hdr{GenerateBSincHeader(60, 11)}; +constexpr BSincHeader bsinc24_hdr{GenerateBSincHeader(60, 23)}; + + +/* std::array is missing constexpr for several methods. */ +template +struct Array { + T data[N]; +}; + +template +constexpr auto GenerateBSincCoeffs(const BSincHeader hdr) +{ + double filter[BSincScaleCount][BSincPhaseCount+1][BSincPointsMax]{}; + double phDeltas[BSincScaleCount][BSincPhaseCount ][BSincPointsMax]{}; + double scDeltas[BSincScaleCount][BSincPhaseCount ][BSincPointsMax]{}; + double spDeltas[BSincScaleCount][BSincPhaseCount ][BSincPointsMax]{}; + + /* Calculate the Kaiser-windowed Sinc filter coefficients for each scale + * and phase. + */ + for(unsigned int si{0};si < BSincScaleCount;++si) + { + const int m{hdr.a[si] * 2}; + const int o{BSincPointsHalf - (m/2)}; + const int l{hdr.a[si] - 1}; + const int a{hdr.a[si]}; + const double scale{hdr.scaleBase + (hdr.scaleRange * si / (BSincScaleCount - 1))}; + const double cutoff{scale - (hdr.scaleBase * std::max(0.5, scale) * 2.0)}; + + for(int pi{0};pi <= BSincPhaseCount;++pi) + { + const double phase{l + (pi/double{BSincPhaseCount})}; + + for(int i{0};i < m;++i) + { + const double x{i - phase}; + filter[si][pi][o + i] = Kaiser(hdr.beta, x/a, hdr.besseli_0_beta) * cutoff * + Sinc(cutoff*x); + } + } + } + + /* Linear interpolation between phases is simplified by pre-calculating the + * delta (b - a) in: x = a + f (b - a) + */ + for(unsigned int si{0};si < BSincScaleCount;++si) + { + const int m{hdr.a[si] * 2}; + const int o{BSincPointsHalf - (m/2)}; + + for(int pi{0};pi < BSincPhaseCount;++pi) + { + for(int i{0};i < m;++i) + phDeltas[si][pi][o + i] = filter[si][pi + 1][o + i] - filter[si][pi][o + i]; + } + } + + /* Linear interpolation between scales is also simplified. + * + * Given a difference in points between scales, the destination points will + * be 0, thus: x = a + f (-a) + */ + for(unsigned int si{0};si < (BSincScaleCount-1);++si) + { + const int m{hdr.a[si] * 2}; + const int o{BSincPointsHalf - (m/2)}; + + for(int pi{0};pi < BSincPhaseCount;++pi) + { + for(int i{0};i < m;++i) + scDeltas[si][pi][o + i] = filter[si + 1][pi][o + i] - filter[si][pi][o + i]; + } + } + + /* This last simplification is done to complete the bilinear equation for + * the combination of phase and scale. + */ + for(unsigned int si{0};si < (BSincScaleCount-1);++si) + { + const int m{hdr.a[si] * 2}; + const int o{BSincPointsHalf - (m/2)}; + + for(int pi{0};pi < BSincPhaseCount;++pi) + { + for(int i{0};i < m;++i) + spDeltas[si][pi][o + i] = phDeltas[si + 1][pi][o + i] - phDeltas[si][pi][o + i]; + } + } + + Array ret{}; + size_t idx{0}; + + for(unsigned int si{0};si < BSincScaleCount;++si) + { + const int m{((hdr.a[si]*2) + 3) & ~3}; + const int o{BSincPointsHalf - (m/2)}; + + for(int pi{0};pi < BSincPhaseCount;++pi) + { + for(int i{0};i < m;++i) + ret.data[idx++] = static_cast(filter[si][pi][o + i]); + for(int i{0};i < m;++i) + ret.data[idx++] = static_cast(phDeltas[si][pi][o + i]); + for(int i{0};i < m;++i) + ret.data[idx++] = static_cast(scDeltas[si][pi][o + i]); + for(int i{0};i < m;++i) + ret.data[idx++] = static_cast(spDeltas[si][pi][o + i]); + } + } + assert(idx == total_size); + + return ret; +} + +/* FIXME: These can't be constexpr due to reaching the step limit. */ +const auto bsinc12_table = GenerateBSincCoeffs(bsinc12_hdr); +const auto bsinc24_table = GenerateBSincCoeffs(bsinc24_hdr); + + +constexpr BSincTable GenerateBSincTable(const BSincHeader hdr, const float *tab) +{ + BSincTable ret{}; + ret.scaleBase = static_cast(hdr.scaleBase); + ret.scaleRange = static_cast(1.0 / hdr.scaleRange); + for(int i{0};i < BSincScaleCount;++i) + ret.m[i] = static_cast(((hdr.a[i]*2) + 3) & ~3); + ret.filterOffset[0] = 0; + for(int i{1};i < BSincScaleCount;++i) + ret.filterOffset[i] = ret.filterOffset[i-1] + ret.m[i-1]; + ret.Tab = tab; + return ret; +} + +} // namespace + +const BSincTable bsinc12{GenerateBSincTable(bsinc12_hdr, bsinc12_table.data)}; +const BSincTable bsinc24{GenerateBSincTable(bsinc24_hdr, bsinc24_table.data)}; diff --git a/common/bsinc_tables.h b/common/bsinc_tables.h new file mode 100644 index 00000000..2e2443e5 --- /dev/null +++ b/common/bsinc_tables.h @@ -0,0 +1,17 @@ +#ifndef BSINC_TABLES_H +#define BSINC_TABLES_H + +#include "bsinc_defs.h" + + +struct BSincTable { + float scaleBase, scaleRange; + unsigned int m[BSINC_SCALE_COUNT]; + unsigned int filterOffset[BSINC_SCALE_COUNT]; + const float *Tab; +}; + +extern const BSincTable bsinc12; +extern const BSincTable bsinc24; + +#endif /* BSINC_TABLES_H */ -- cgit v1.2.3 From ebe30fb0bd47ca3c16d19863ae9053d8f93bd1f0 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 2 Apr 2020 04:34:28 -0700 Subject: Remove the now-unused bsincgen --- CMakeLists.txt | 24 +-- native-tools/CMakeLists.txt | 11 -- native-tools/bsincgen.c | 366 -------------------------------------------- 3 files changed, 5 insertions(+), 396 deletions(-) delete mode 100644 native-tools/bsincgen.c (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ce37e84..5d3df4ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1056,36 +1056,29 @@ IF(ALSOFT_NATIVE_TOOLS_PATH) find_program(BIN2H_NATIVE_COMMAND NAMES bin2h PATHS "${ALSOFT_NATIVE_TOOLS_PATH}" NO_DEFAULT_PATH) - find_program(BSINCGEN_NATIVE_COMMAND NAMES bsincgen - PATHS "${ALSOFT_NATIVE_TOOLS_PATH}" - NO_DEFAULT_PATH) - if(NOT BIN2H_NATIVE_COMMAND OR NOT BSINCGEN_NATIVE_COMMAND) + if(NOT BIN2H_NATIVE_COMMAND) message(FATAL_ERROR "Failed to find native tools in ${ALSOFT_NATIVE_TOOLS_PATH}. -bin2h: ${BIN2H_NATIVE_COMMAND} -bsincgen: ${BSINCGEN_NATIVE_COMMAND}") +bin2h: ${BIN2H_NATIVE_COMMAND}") endif() SET(BIN2H_COMMAND ${BIN2H_NATIVE_COMMAND}) - SET(BSINCGEN_COMMAND ${BSINCGEN_NATIVE_COMMAND}) ELSE() SET(NATIVE_BIN_DIR "${OpenAL_BINARY_DIR}/native-tools") FILE(MAKE_DIRECTORY "${NATIVE_BIN_DIR}") SET(BIN2H_COMMAND "${NATIVE_BIN_DIR}/bin2h") - SET(BSINCGEN_COMMAND "${NATIVE_BIN_DIR}/bsincgen") - ADD_CUSTOM_COMMAND(OUTPUT "${BIN2H_COMMAND}" "${BSINCGEN_COMMAND}" + ADD_CUSTOM_COMMAND(OUTPUT "${BIN2H_COMMAND}" COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" "${NATIVE_SRC_DIR}" - COMMAND ${CMAKE_COMMAND} -E remove "${BIN2H_COMMAND}" "${BSINCGEN_COMMAND}" + COMMAND ${CMAKE_COMMAND} -E remove "${BIN2H_COMMAND}" COMMAND ${CMAKE_COMMAND} --build . --config "Release" WORKING_DIRECTORY "${NATIVE_BIN_DIR}" DEPENDS "${NATIVE_SRC_DIR}/CMakeLists.txt" IMPLICIT_DEPENDS C "${NATIVE_SRC_DIR}/bin2h.c" - C "${NATIVE_SRC_DIR}/bsincgen.c" VERBATIM ) ENDIF() ADD_CUSTOM_TARGET(native-tools - DEPENDS "${BIN2H_COMMAND}" "${BSINCGEN_COMMAND}" + DEPENDS "${BIN2H_COMMAND}" VERBATIM ) @@ -1106,13 +1099,6 @@ if(ALSOFT_EMBED_HRTF_DATA) make_hrtf_header("Default HRTF.mhr" "hrtf_default") endif() -ADD_CUSTOM_COMMAND(OUTPUT "${OpenAL_BINARY_DIR}/bsinc_inc.h" - COMMAND "${BSINCGEN_COMMAND}" "${OpenAL_BINARY_DIR}/bsinc_inc.h" - DEPENDS native-tools "${NATIVE_SRC_DIR}/bsincgen.c" - VERBATIM -) -SET(ALC_OBJS ${ALC_OBJS} "${OpenAL_BINARY_DIR}/bsinc_inc.h") - IF(ALSOFT_UTILS AND NOT ALSOFT_NO_CONFIG_UTIL) add_subdirectory(utils/alsoft-config) diff --git a/native-tools/CMakeLists.txt b/native-tools/CMakeLists.txt index 88bf1f56..994378b3 100644 --- a/native-tools/CMakeLists.txt +++ b/native-tools/CMakeLists.txt @@ -9,8 +9,6 @@ if(WIN32) set(CPP_DEFS ${CPP_DEFS} _WIN32) endif(WIN32) -check_library_exists(m pow "" HAVE_LIBM) - add_executable(bin2h bin2h.c) # Enforce no dressing for executable names, so the main script can find it set_target_properties(bin2h PROPERTIES OUTPUT_NAME bin2h) @@ -18,12 +16,3 @@ set_target_properties(bin2h PROPERTIES OUTPUT_NAME bin2h) set_target_properties(bin2h PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}") set_target_properties(bin2h PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}") target_compile_definitions(bin2h PRIVATE ${CPP_DEFS}) - -add_executable(bsincgen bsincgen.c) -set_target_properties(bsincgen PROPERTIES OUTPUT_NAME bsincgen) -set_target_properties(bsincgen PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}") -set_target_properties(bsincgen PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}") -target_compile_definitions(bsincgen PRIVATE ${CPP_DEFS}) -if(HAVE_LIBM) - target_link_libraries(bsincgen m) -endif(HAVE_LIBM) diff --git a/native-tools/bsincgen.c b/native-tools/bsincgen.c deleted file mode 100644 index 7e7a1fa3..00000000 --- a/native-tools/bsincgen.c +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Sinc interpolator coefficient and delta generator for the OpenAL Soft - * cross platform audio library. - * - * Copyright (C) 2015 by Christopher Fitzgerald. - * - * 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 visit: http://www.gnu.org/licenses/old-licenses/lgpl-2.0.html - * - * -------------------------------------------------------------------------- - * - * This is a modified version of the bandlimited windowed sinc interpolator - * algorithm presented here: - * - * Smith, J.O. "Windowed Sinc Interpolation", in - * Physical Audio Signal Processing, - * https://ccrma.stanford.edu/~jos/pasp/Windowed_Sinc_Interpolation.html, - * online book, - * accessed October 2012. - */ - -#define _UNICODE -#include -#include -#include -#include - -#include "../common/win_main_utf8.h" - - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -#if !(defined(_ISOC99_SOURCE) || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)) -#define log2(x) (log(x) / log(2.0)) -#endif - -// The number of distinct scale and phase intervals within the filter table. -// Must be the same as in alu.h! -#define BSINC_SCALE_COUNT 16 -#define BSINC_PHASE_COUNT 32 - -/* 48 points includes the doubling for downsampling, so the maximum number of - * base sample points is 24, which is 23rd order. - */ -#define BSINC_POINTS_MAX 48 - -static double MinDouble(double a, double b) -{ return (a <= b) ? a : b; } - -static double MaxDouble(double a, double b) -{ return (a >= b) ? a : b; } - -/* NOTE: This is the normalized (instead of just sin(x)/x) cardinal sine - * function. - * 2 f_t sinc(2 f_t x) - * f_t -- normalized transition frequency (0.5 is nyquist) - * x -- sample index (-N to N) - */ -static double Sinc(const double x) -{ - if(fabs(x) < 1e-15) - return 1.0; - return sin(M_PI * x) / (M_PI * x); -} - -static double BesselI_0(const double x) -{ - double term, sum, last_sum, x2, y; - int i; - - term = 1.0; - sum = 1.0; - x2 = x / 2.0; - i = 1; - - do { - y = x2 / i; - i++; - last_sum = sum; - term *= y * y; - sum += term; - } while(sum != last_sum); - - return sum; -} - -/* NOTE: k is assumed normalized (-1 to 1) - * beta is equivalent to 2 alpha - */ -static double Kaiser(const double b, const double k) -{ - if(!(k >= -1.0 && k <= 1.0)) - return 0.0; - return BesselI_0(b * sqrt(1.0 - k*k)) / BesselI_0(b); -} - -/* Calculates the (normalized frequency) transition width of the Kaiser window. - * Rejection is in dB. - */ -static double CalcKaiserWidth(const double rejection, const int order) -{ - double w_t = 2.0 * M_PI; - - if(rejection > 21.0) - return (rejection - 7.95) / (order * 2.285 * w_t); - /* This enforces a minimum rejection of just above 21.18dB */ - return 5.79 / (order * w_t); -} - -static double CalcKaiserBeta(const double rejection) -{ - if(rejection > 50.0) - return 0.1102 * (rejection - 8.7); - else if(rejection >= 21.0) - return (0.5842 * pow(rejection - 21.0, 0.4)) + - (0.07886 * (rejection - 21.0)); - return 0.0; -} - -/* Generates the coefficient, delta, and index tables required by the bsinc resampler */ -static void BsiGenerateTables(FILE *output, const char *tabname, const double rejection, const int order) -{ - static double filter[BSINC_SCALE_COUNT][BSINC_PHASE_COUNT + 1][BSINC_POINTS_MAX]; - static double phDeltas[BSINC_SCALE_COUNT][BSINC_PHASE_COUNT + 1][BSINC_POINTS_MAX]; - static double scDeltas[BSINC_SCALE_COUNT][BSINC_PHASE_COUNT ][BSINC_POINTS_MAX]; - static double spDeltas[BSINC_SCALE_COUNT][BSINC_PHASE_COUNT ][BSINC_POINTS_MAX]; - static int mt[BSINC_SCALE_COUNT]; - static double at[BSINC_SCALE_COUNT]; - const int num_points_min = order + 1; - double width, beta, scaleBase, scaleRange; - int si, pi, i; - - memset(filter, 0, sizeof(filter)); - memset(phDeltas, 0, sizeof(phDeltas)); - memset(scDeltas, 0, sizeof(scDeltas)); - memset(spDeltas, 0, sizeof(spDeltas)); - - /* Calculate windowing parameters. The width describes the transition - band, but it may vary due to the linear interpolation between scales - of the filter. - */ - width = CalcKaiserWidth(rejection, order); - beta = CalcKaiserBeta(rejection); - scaleBase = width / 2.0; - scaleRange = 1.0 - scaleBase; - - // Determine filter scaling. - for(si = 0; si < BSINC_SCALE_COUNT; si++) - { - const double scale = scaleBase + (scaleRange * si / (BSINC_SCALE_COUNT - 1)); - const double a = MinDouble(floor(num_points_min / (2.0 * scale)), num_points_min); - const int m = 2 * (int)a; - - mt[si] = m; - at[si] = a; - } - - /* Calculate the Kaiser-windowed Sinc filter coefficients for each scale - * and phase. - */ - for(si = 0; si < BSINC_SCALE_COUNT; si++) - { - const int m = mt[si]; - const int o = num_points_min - (m / 2); - const int l = (m / 2) - 1; - const double a = at[si]; - const double scale = scaleBase + (scaleRange * si / (BSINC_SCALE_COUNT - 1)); - const double cutoff = (0.5 * scale) - (scaleBase * MaxDouble(0.5, scale)); - - for(pi = 0; pi <= BSINC_PHASE_COUNT; pi++) - { - const double phase = l + ((double)pi / BSINC_PHASE_COUNT); - - for(i = 0; i < m; i++) - { - const double x = i - phase; - filter[si][pi][o + i] = Kaiser(beta, x / a) * 2.0 * cutoff * Sinc(2.0 * cutoff * x); - } - } - } - - /* Linear interpolation between phases is simplified by pre-calculating the - * delta (b - a) in: x = a + f (b - a) - */ - for(si = 0; si < BSINC_SCALE_COUNT; si++) - { - const int m = mt[si]; - const int o = num_points_min - (m / 2); - - for(pi = 0; pi < BSINC_PHASE_COUNT; pi++) - { - for(i = 0; i < m; i++) - phDeltas[si][pi][o + i] = filter[si][pi + 1][o + i] - filter[si][pi][o + i]; - } - } - - /* Linear interpolation between scales is also simplified. - * - * Given a difference in points between scales, the destination points will - * be 0, thus: x = a + f (-a) - */ - for(si = 0; si < (BSINC_SCALE_COUNT - 1); si++) - { - const int m = mt[si]; - const int o = num_points_min - (m / 2); - - for(pi = 0; pi < BSINC_PHASE_COUNT; pi++) - { - for(i = 0; i < m; i++) - scDeltas[si][pi][o + i] = filter[si + 1][pi][o + i] - filter[si][pi][o + i]; - } - } - - /* This last simplification is done to complete the bilinear equation for - * the combination of phase and scale. - */ - for(si = 0; si < (BSINC_SCALE_COUNT - 1); si++) - { - const int m = mt[si]; - const int o = num_points_min - (m / 2); - - for(pi = 0; pi < BSINC_PHASE_COUNT; pi++) - { - for(i = 0; i < m; i++) - spDeltas[si][pi][o + i] = phDeltas[si + 1][pi][o + i] - phDeltas[si][pi][o + i]; - } - } - - /* Make sure the number of points is a multiple of 4 (for SIMD). */ - for(si = 0; si < BSINC_SCALE_COUNT; si++) - mt[si] = (mt[si]+3) & ~3; - - /* Calculate the table size. */ - i = 0; - for(si = 0; si < BSINC_SCALE_COUNT; si++) - i += 4 * BSINC_PHASE_COUNT * mt[si]; - - fprintf(output, -"/* This %d%s order filter has a rejection of -%.0fdB, yielding a transition width\n" -" * of ~%.3f (normalized frequency). Order increases when downsampling to a\n" -" * limit of one octave, after which the quality of the filter (transition\n" -" * width) suffers to reduce the CPU cost. The bandlimiting will cut all sound\n" -" * after downsampling by ~%.2f octaves.\n" -" */\n" -"alignas(16) constexpr float %s_tab[%d] = {\n", - order, (((order%100)/10) == 1) ? "th" : - ((order%10) == 1) ? "st" : - ((order%10) == 2) ? "nd" : - ((order%10) == 3) ? "rd" : "th", - rejection, width, log2(1.0/scaleBase), tabname, i); - for(si = 0; si < BSINC_SCALE_COUNT; si++) - { - const int m = mt[si]; - const int o = num_points_min - (m / 2); - - for(pi = 0; pi < BSINC_PHASE_COUNT; pi++) - { - fprintf(output, " /* %2d,%2d (%d) */", si, pi, m); - fprintf(output, "\n "); - for(i = 0; i < m; i++) - fprintf(output, " %+14.9ef,", filter[si][pi][o + i]); - fprintf(output, "\n "); - for(i = 0; i < m; i++) - fprintf(output, " %+14.9ef,", phDeltas[si][pi][o + i]); - fprintf(output, "\n "); - for(i = 0; i < m; i++) - fprintf(output, " %+14.9ef,", scDeltas[si][pi][o + i]); - fprintf(output, "\n "); - for(i = 0; i < m; i++) - fprintf(output, " %+14.9ef,", spDeltas[si][pi][o + i]); - fprintf(output, "\n"); - } - } - fprintf(output, "};\nconstexpr BSincTable %s = {\n", tabname); - - /* The scaleBase is calculated from the Kaiser window transition width. - It represents the absolute limit to the filter before it fully cuts - the signal. The limit in octaves can be calculated by taking the - base-2 logarithm of its inverse: log_2(1 / scaleBase) - */ - fprintf(output, " /* scaleBase */ %.9ef, /* scaleRange */ %.9ef,\n", scaleBase, 1.0 / scaleRange); - - fprintf(output, " /* m */ {"); - fprintf(output, " %du", mt[0]); - for(si = 1; si < BSINC_SCALE_COUNT; si++) - fprintf(output, ", %du", mt[si]); - fprintf(output, " },\n"); - - fprintf(output, " /* filterOffset */ {"); - fprintf(output, " %du", 0); - i = mt[0]*4*BSINC_PHASE_COUNT; - for(si = 1; si < BSINC_SCALE_COUNT; si++) - { - fprintf(output, ", %du", i); - i += mt[si]*4*BSINC_PHASE_COUNT; - } - - fprintf(output, " },\n"); - fprintf(output, " %s_tab\n", tabname); - fprintf(output, "};\n\n"); -} - - -int main(int argc, char *argv[]) -{ - FILE *output; - - GET_UNICODE_ARGS(&argc, &argv); - - if(argc > 2) - { - fprintf(stderr, "Usage: %s [output file]\n", argv[0]); - return 1; - } - - if(argc == 2) - { - output = fopen(argv[1], "wb"); - if(!output) - { - fprintf(stderr, "Failed to open %s for writing\n", argv[1]); - return 1; - } - } - else - output = stdout; - - fprintf(output, "/* Generated by bsincgen, do not edit! */\n" -"#pragma once\n\n" -"static_assert(BSINC_SCALE_COUNT == %d, \"Unexpected BSINC_SCALE_COUNT value!\");\n" -"static_assert(BSINC_PHASE_COUNT == %d, \"Unexpected BSINC_PHASE_COUNT value!\");\n\n" -"namespace {\n\n" -"struct BSincTable {\n" -" const float scaleBase, scaleRange;\n" -" const unsigned int m[BSINC_SCALE_COUNT];\n" -" const unsigned int filterOffset[BSINC_SCALE_COUNT];\n" -" const float *Tab;\n" -"};\n\n", BSINC_SCALE_COUNT, BSINC_PHASE_COUNT); - /* A 23rd order filter with a -60dB drop at nyquist. */ - BsiGenerateTables(output, "bsinc24", 60.0, 23); - /* An 11th order filter with a -60dB drop at nyquist. */ - BsiGenerateTables(output, "bsinc12", 60.0, 11); - - fprintf(output, "} // namespace\n"); - if(output != stdout) - fclose(output); - output = NULL; - - return 0; -} -- cgit v1.2.3 From 431d01cc7f271c6bcf4aaf931a4df760bae2847e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 4 Apr 2020 08:46:18 -0700 Subject: Use a cmake script to convert a binary file to a header file --- CMakeLists.txt | 39 ++++----------------------------------- cmake/bin2h.script.cmake | 13 +++++++++++++ 2 files changed, 17 insertions(+), 35 deletions(-) create mode 100644 cmake/bin2h.script.cmake (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d3df4ed..2b81c24a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1049,39 +1049,6 @@ ELSE() ENDIF() -SET(NATIVE_SRC_DIR "${OpenAL_SOURCE_DIR}/native-tools") - -SET(ALSOFT_NATIVE_TOOLS_PATH "" CACHE STRING "Path to prebuilt native tools (leave blank to auto-build)") -IF(ALSOFT_NATIVE_TOOLS_PATH) - find_program(BIN2H_NATIVE_COMMAND NAMES bin2h - PATHS "${ALSOFT_NATIVE_TOOLS_PATH}" - NO_DEFAULT_PATH) - if(NOT BIN2H_NATIVE_COMMAND) - message(FATAL_ERROR "Failed to find native tools in ${ALSOFT_NATIVE_TOOLS_PATH}. -bin2h: ${BIN2H_NATIVE_COMMAND}") - endif() - SET(BIN2H_COMMAND ${BIN2H_NATIVE_COMMAND}) -ELSE() - SET(NATIVE_BIN_DIR "${OpenAL_BINARY_DIR}/native-tools") - FILE(MAKE_DIRECTORY "${NATIVE_BIN_DIR}") - - SET(BIN2H_COMMAND "${NATIVE_BIN_DIR}/bin2h") - ADD_CUSTOM_COMMAND(OUTPUT "${BIN2H_COMMAND}" - COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" "${NATIVE_SRC_DIR}" - COMMAND ${CMAKE_COMMAND} -E remove "${BIN2H_COMMAND}" - COMMAND ${CMAKE_COMMAND} --build . --config "Release" - WORKING_DIRECTORY "${NATIVE_BIN_DIR}" - DEPENDS "${NATIVE_SRC_DIR}/CMakeLists.txt" - IMPLICIT_DEPENDS - C "${NATIVE_SRC_DIR}/bin2h.c" - VERBATIM - ) -ENDIF() -ADD_CUSTOM_TARGET(native-tools - DEPENDS "${BIN2H_COMMAND}" - VERBATIM -) - option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" ON) if(ALSOFT_EMBED_HRTF_DATA) MACRO(make_hrtf_header FILENAME VARNAME) @@ -1089,8 +1056,10 @@ if(ALSOFT_EMBED_HRTF_DATA) SET(outfile "${OpenAL_BINARY_DIR}/${VARNAME}.h") ADD_CUSTOM_COMMAND(OUTPUT "${outfile}" - COMMAND "${BIN2H_COMMAND}" "${infile}" "${outfile}" ${VARNAME} - DEPENDS native-tools "${infile}" + COMMAND ${CMAKE_COMMAND} -D "INPUT_FILE=${infile}" -D "OUTPUT_FILE=${outfile}" + -D "VARIABLE_NAME=${VARNAME}" -P "${CMAKE_MODULE_PATH}/bin2h.script.cmake" + WORKING_DIRECTORY "${OpenAL_SOURCE_DIR}" + DEPENDS "${infile}" "${CMAKE_MODULE_PATH}/bin2h.script.cmake" VERBATIM ) SET(ALC_OBJS ${ALC_OBJS} "${outfile}") diff --git a/cmake/bin2h.script.cmake b/cmake/bin2h.script.cmake new file mode 100644 index 00000000..1438fde2 --- /dev/null +++ b/cmake/bin2h.script.cmake @@ -0,0 +1,13 @@ +# Read the input file into 'indata', converting each byte to a pair of hex +# characters +file(READ "${INPUT_FILE}" indata HEX) + +# For each pair of characters, indent them and prepend the 0x prefix, and +# append a comma separateor. +# TODO: Prettify this. Should group a number of bytes per line instead of one +# per line. +string(REGEX REPLACE "(..)" " 0x\\1,\n" output "${indata}") + +# Write the list of hex chars to the output file in a const byte array +file(WRITE "${OUTPUT_FILE}" + "const unsigned char ${VARIABLE_NAME}[] = {\n${output}};\n") -- cgit v1.2.3 From 0dc9b0392d30fab9faae6492c75dbe8240152019 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 10 Apr 2020 08:43:59 -0700 Subject: Apply static-link flags directly to the target --- CMakeLists.txt | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ac7238d..a6384858 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,11 +86,6 @@ IF(WIN32) OPTION(ALSOFT_BUILD_ROUTER "Build the router (EXPERIMENTAL; creates OpenAL32.dll and soft_oal.dll)" OFF) - # This option is mainly for static linking OpenAL Soft into another project - # that already defines the IDs. It is up to that project to ensure all - # required IDs are defined. - OPTION(ALSOFT_NO_UID_DEFS "Do not define GUIDs, IIDs, CLSIDs, or PropertyKeys" OFF) - IF(MINGW) OPTION(ALSOFT_BUILD_IMPORT_LIB "Build an import .lib using dlltool (requires sed)" ON) IF(NOT DLLTOOL) @@ -1132,12 +1127,18 @@ SET(SUBSYS_FLAG ) # Build main library IF(LIBTYPE STREQUAL "STATIC") - SET(CPP_DEFS ${CPP_DEFS} AL_LIBTYPE_STATIC) - IF(WIN32 AND ALSOFT_NO_UID_DEFS) - SET(CPP_DEFS ${CPP_DEFS} AL_NO_UID_DEFS) - ENDIF() - ADD_LIBRARY(${IMPL_TARGET} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS}) - TARGET_LINK_LIBRARIES(${IMPL_TARGET} PRIVATE ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) + add_library(${IMPL_TARGET} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS}) + target_compile_definitions(${IMPL_TARGET} PUBLIC AL_LIBTYPE_STATIC) + target_link_libraries(${IMPL_TARGET} PRIVATE ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) + if(WIN32) + # This option is for static linking OpenAL Soft into another project + # that already defines the IDs. It is up to that project to ensure all + # required IDs are defined. + option(ALSOFT_NO_UID_DEFS "Do not define GUIDs, IIDs, CLSIDs, or PropertyKeys" OFF) + if(ALSOFT_NO_UID_DEFS) + target_compile_definitions(${IMPL_TARGET} PRIVATE AL_NO_UID_DEFS) + endif() + endif() ELSE() IF(WIN32) IF(MSVC) -- cgit v1.2.3 From ae173ba8637f4ed69acb8357b6511d90cca7f010 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 10 Apr 2020 08:59:22 -0700 Subject: Remove an unused cmake variable --- CMakeLists.txt | 9 --------- 1 file changed, 9 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index a6384858..137e016e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1123,7 +1123,6 @@ SET_TARGET_PROPERTIES(common PROPERTIES POSITION_INDEPENDENT_CODE TRUE) UNSET(HAS_ROUTER) SET(IMPL_TARGET OpenAL) # Either OpenAL or soft_oal. -SET(SUBSYS_FLAG ) # Build main library IF(LIBTYPE STREQUAL "STATIC") @@ -1140,14 +1139,6 @@ IF(LIBTYPE STREQUAL "STATIC") endif() endif() ELSE() - IF(WIN32) - IF(MSVC) - SET(SUBSYS_FLAG ${SUBSYS_FLAG} "/SUBSYSTEM:WINDOWS") - ELSEIF(CMAKE_COMPILER_IS_GNUCC) - SET(SUBSYS_FLAG ${SUBSYS_FLAG} "-mwindows") - ENDIF() - ENDIF() - SET(RC_CONFIG resources/openal32.rc) IF(WIN32 AND ALSOFT_BUILD_ROUTER) ADD_LIBRARY(OpenAL SHARED -- cgit v1.2.3 From 611a0155cd4bbe588918b07a219cc830c77691c3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 10 Apr 2020 11:41:59 -0700 Subject: Ignore "attribute '...' is not recognized" MSVC warning --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 137e016e..aee11a10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,7 +180,7 @@ IF(MSVC) IF(HAVE_PERMISSIVE_SWITCH) SET(C_FLAGS ${C_FLAGS} $<$:/permissive->) ENDIF() - SET(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4268 /wd4324) + SET(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4268 /wd4324 /wd5030) IF(NOT DXSDK_DIR) STRING(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}") -- cgit v1.2.3 From ac54ab8a3e91615e8b5b7b7cd3999abc5b41960c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 14 Apr 2020 18:19:59 -0700 Subject: Remove some unnecessary cmake checks --- CMakeLists.txt | 5 ----- alc/helpers.cpp | 39 +++++++++++++++------------------------ config.h.in | 9 --------- 3 files changed, 15 insertions(+), 38 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index aee11a10..760e0e6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -367,14 +367,9 @@ IF(HAVE_EMMINTRIN_H) ENDIF() -CHECK_C_SOURCE_COMPILES("int foo(const char *str, ...) __attribute__((format(printf, 1, 2))); - int main() {return 0;}" HAVE_GCC_FORMAT) - CHECK_INCLUDE_FILE(malloc.h HAVE_MALLOC_H) -CHECK_INCLUDE_FILE(dirent.h HAVE_DIRENT_H) CHECK_INCLUDE_FILE(cpuid.h HAVE_CPUID_H) CHECK_INCLUDE_FILE(intrin.h HAVE_INTRIN_H) -CHECK_INCLUDE_FILE(sys/sysconf.h HAVE_SYS_SYSCONF_H) CHECK_INCLUDE_FILE(guiddef.h HAVE_GUIDDEF_H) IF(NOT HAVE_GUIDDEF_H) CHECK_INCLUDE_FILE(initguid.h HAVE_INITGUID_H) diff --git a/alc/helpers.cpp b/alc/helpers.cpp index aa9ca7ae..2aee6a2d 100644 --- a/alc/helpers.cpp +++ b/alc/helpers.cpp @@ -18,6 +18,9 @@ * Or go to http://www.gnu.org/copyleft/lgpl.html */ +/* Define this first since Windows' system headers may include headers affected + * by it (is it even still needed?). + */ #ifdef _WIN32 #ifdef __MINGW32__ #define _WIN32_IE 0x501 @@ -37,28 +40,6 @@ #include #include -#ifdef HAVE_DIRENT_H -#include -#endif -#ifdef HAVE_SYS_SYSCONF_H -#include -#endif - -#ifdef HAVE_PROC_PIDPATH -#include -#endif - -#ifdef __FreeBSD__ -#include -#include -#endif - -#ifndef _WIN32 -#include -#elif defined(_WIN32_IE) -#include -#endif - #include "alcmain.h" #include "almalloc.h" #include "alfstream.h" @@ -72,6 +53,8 @@ #ifdef _WIN32 +#include + const PathNamePair &GetProcBinary() { static PathNamePair ret; @@ -240,6 +223,15 @@ void SetRTPriority(void) #else +#include +#include +#include +#ifdef __FreeBSD__ +#include +#endif +#ifdef HAVE_PROC_PIDPATH +#include +#endif #if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__) #include #include @@ -327,8 +319,7 @@ const PathNamePair &GetProcBinary() void al_print(FILE *logfile, const char *fmt, ...) { - va_list ap; - + std::va_list ap; va_start(ap, fmt); vfprintf(logfile, fmt, ap); va_end(ap); diff --git a/config.h.in b/config.h.in index 8beb13a4..60879df9 100644 --- a/config.h.in +++ b/config.h.in @@ -74,9 +74,6 @@ /* Define to the size of a long int type */ #cmakedefine SIZEOF_LONG ${SIZEOF_LONG} -/* Define if we have GCC's format attribute */ -#cmakedefine HAVE_GCC_FORMAT - /* Define if we have dlfcn.h */ #cmakedefine HAVE_DLFCN_H @@ -86,18 +83,12 @@ /* Define if we have malloc.h */ #cmakedefine HAVE_MALLOC_H -/* Define if we have dirent.h */ -#cmakedefine HAVE_DIRENT_H - /* Define if we have cpuid.h */ #cmakedefine HAVE_CPUID_H /* Define if we have intrin.h */ #cmakedefine HAVE_INTRIN_H -/* Define if we have sys/sysconf.h */ -#cmakedefine HAVE_SYS_SYSCONF_H - /* Define if we have guiddef.h */ #cmakedefine HAVE_GUIDDEF_H -- cgit v1.2.3 From 930cda39cd95ec19c42a32f2a18fbbe95667952d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 14 Apr 2020 22:02:59 -0700 Subject: Simplify some indentation --- CMakeLists.txt | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 760e0e6f..567ea55b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -328,31 +328,28 @@ CHECK_INCLUDE_FILE(pmmintrin.h HAVE_PMMINTRIN_H "${SSE3_SWITCH}") CHECK_INCLUDE_FILE(smmintrin.h HAVE_SMMINTRIN_H "${SSE4_1_SWITCH}") CHECK_INCLUDE_FILE(arm_neon.h HAVE_ARM_NEON_H "${FPU_NEON_SWITCH}") -SET(SSE_FLAGS ) -SET(FPMATH_SET "0") -IF(CMAKE_SIZEOF_VOID_P MATCHES "4") - IF(SSE2_SWITCH OR MSVC) - OPTION(ALSOFT_ENABLE_SSE2_CODEGEN "Enable SSE2 code generation instead of x87 for 32-bit targets." TRUE) - ENDIF() - - IF(ALSOFT_ENABLE_SSE2_CODEGEN) - IF(SSE2_SWITCH) - CHECK_C_COMPILER_FLAG("${SSE2_SWITCH} -mfpmath=sse" HAVE_MFPMATH_SSE_2) - IF(HAVE_MFPMATH_SSE_2) - SET(SSE_FLAGS ${SSE_FLAGS} ${SSE2_SWITCH} -mfpmath=sse) - SET(C_FLAGS ${C_FLAGS} ${SSE_FLAGS}) - SET(FPMATH_SET 2) - ENDIF() - ELSEIF(MSVC) - CHECK_C_COMPILER_FLAG("/arch:SSE2" HAVE_ARCH_SSE2) - IF(HAVE_ARCH_SSE2) - SET(SSE_FLAGS ${SSE_FLAGS} "/arch:SSE2") - SET(C_FLAGS ${C_FLAGS} ${SSE_FLAGS}) - SET(FPMATH_SET 2) - ENDIF() - ENDIF() - ENDIF() -ENDIF() +set(SSE_FLAGS ) +set(FPMATH_SET "0") +if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND (SSE2_SWITCH OR MSVC)) + option(ALSOFT_ENABLE_SSE2_CODEGEN "Enable SSE2 code generation instead of x87 for 32-bit targets." TRUE) + if(ALSOFT_ENABLE_SSE2_CODEGEN) + if(SSE2_SWITCH) + check_c_compiler_flag("${SSE2_SWITCH} -mfpmath=sse" HAVE_MFPMATH_SSE_2) + if(HAVE_MFPMATH_SSE_2) + set(SSE_FLAGS ${SSE_FLAGS} ${SSE2_SWITCH} -mfpmath=sse) + set(C_FLAGS ${C_FLAGS} ${SSE_FLAGS}) + set(FPMATH_SET 2) + endif() + elseif(MSVC) + check_c_compiler_flag("/arch:SSE2" HAVE_ARCH_SSE2) + if(HAVE_ARCH_SSE2) + set(SSE_FLAGS ${SSE_FLAGS} "/arch:SSE2") + set(C_FLAGS ${C_FLAGS} ${SSE_FLAGS}) + set(FPMATH_SET 2) + endif() + endif() + endif() +endif() IF(HAVE_EMMINTRIN_H) SET(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) -- cgit v1.2.3 From cd6908fd195dfa8901f4bfcecc595a384fa2b268 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 14 Apr 2020 23:25:47 -0700 Subject: Always define install targets for the main library Examples and utilities now have separate install options. --- CMakeLists.txt | 120 +++++++++++++++++++++++++-------------------------------- 1 file changed, 53 insertions(+), 67 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 567ea55b..41ce094b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,13 +56,13 @@ OPTION(ALSOFT_WERROR "Treat compile warnings as errors" OFF) OPTION(ALSOFT_UTILS "Build and install utility programs" ON) OPTION(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF) -OPTION(ALSOFT_EXAMPLES "Build and install example programs" ON) -OPTION(ALSOFT_TESTS "Build and install test programs" ON) +OPTION(ALSOFT_EXAMPLES "Build example programs" ON) OPTION(ALSOFT_CONFIG "Install alsoft.conf sample configuration file" ON) OPTION(ALSOFT_HRTF_DEFS "Install HRTF definition files" ON) OPTION(ALSOFT_AMBDEC_PRESETS "Install AmbDec preset files" ON) -OPTION(ALSOFT_INSTALL "Install headers and libraries" ON) +OPTION(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" ON) +OPTION(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" ON) OPTION(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON) if(DEFINED SHARE_INSTALL_DIR) @@ -1218,34 +1218,32 @@ IF(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC" ENDIF() ENDIF() -IF(ALSOFT_INSTALL) - INSTALL(TARGETS OpenAL EXPORT OpenAL - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/AL) - EXPORT(TARGETS OpenAL - NAMESPACE OpenAL:: - FILE OpenALConfig.cmake) - INSTALL(EXPORT OpenAL - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL - NAMESPACE OpenAL:: - FILE OpenALConfig.cmake) - INSTALL(FILES - include/AL/al.h - include/AL/alc.h - include/AL/alext.h - include/AL/efx.h - include/AL/efx-creative.h - include/AL/efx-presets.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/AL) - INSTALL(FILES "${OpenAL_BINARY_DIR}/openal.pc" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") - IF(TARGET soft_oal) - INSTALL(TARGETS soft_oal - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) - ENDIF() -ENDIF() +install(TARGETS OpenAL EXPORT OpenAL + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/AL) +export(TARGETS OpenAL + NAMESPACE OpenAL:: + FILE OpenALConfig.cmake) +install(EXPORT OpenAL + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL + NAMESPACE OpenAL:: + FILE OpenALConfig.cmake) +install(FILES + include/AL/al.h + include/AL/alc.h + include/AL/alext.h + include/AL/efx.h + include/AL/efx-creative.h + include/AL/efx-presets.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/AL) +install(FILES "${OpenAL_BINARY_DIR}/openal.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") +if(TARGET soft_oal) + install(TARGETS soft_oal + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif() if(HAS_ROUTER) @@ -1301,7 +1299,7 @@ IF(ALSOFT_AMBDEC_PRESETS) MESSAGE(STATUS "") ENDIF() -IF(ALSOFT_UTILS) +if(ALSOFT_UTILS) set(UTIL_TARGETS ) ADD_EXECUTABLE(openal-info utils/openal-info.c) @@ -1347,19 +1345,19 @@ IF(ALSOFT_UTILS) target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} sofa-support) endif() - IF(ALSOFT_INSTALL) - INSTALL(TARGETS ${UTIL_TARGETS} + if(ALSOFT_INSTALL_UTILS) + install(TARGETS ${UTIL_TARGETS} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - ENDIF() + endif() - MESSAGE(STATUS "Building utility programs") - IF(TARGET alsoft-config) - MESSAGE(STATUS "Building configuration program") - ENDIF() - MESSAGE(STATUS "") -ENDIF() + message(STATUS "Building utility programs") + if(TARGET alsoft-config) + message(STATUS "Building configuration program") + endif() + message(STATUS "") +endif() # Add a static library with common functions used by multiple example targets @@ -1371,34 +1369,22 @@ TARGET_INCLUDE_DIRECTORIES(ex-common PUBLIC ${OpenAL_SOURCE_DIR}/common) TARGET_COMPILE_OPTIONS(ex-common PUBLIC ${C_FLAGS}) TARGET_LINK_LIBRARIES(ex-common PUBLIC OpenAL PRIVATE ${RT_LIB}) -IF(ALSOFT_TESTS) +IF(ALSOFT_EXAMPLES) ADD_EXECUTABLE(altonegen examples/altonegen.c) TARGET_COMPILE_DEFINITIONS(altonegen PRIVATE ${CPP_DEFS}) TARGET_INCLUDE_DIRECTORIES(altonegen PRIVATE ${OpenAL_SOURCE_DIR}/common) TARGET_COMPILE_OPTIONS(altonegen PRIVATE ${C_FLAGS}) TARGET_LINK_LIBRARIES(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} ex-common) - IF(ALSOFT_INSTALL) - INSTALL(TARGETS altonegen - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - ENDIF() - - MESSAGE(STATUS "Building test programs") - MESSAGE(STATUS "") -ENDIF() - -IF(ALSOFT_EXAMPLES) ADD_EXECUTABLE(alrecord examples/alrecord.c) TARGET_LINK_LIBRARIES(alrecord PRIVATE ${LINKER_FLAGS} ex-common) - IF(ALSOFT_INSTALL) - INSTALL(TARGETS alrecord + if(ALSOFT_INSTALL_EXAMPLES) + install(TARGETS altonegen alrecord RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - ENDIF() + endif() MESSAGE(STATUS "Building example programs") @@ -1433,12 +1419,12 @@ IF(ALSOFT_EXAMPLES) TARGET_INCLUDE_DIRECTORIES(alstreamcb PRIVATE ${SNDFILE_INCLUDE_DIRS}) TARGET_LINK_LIBRARIES(alstreamcb PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common) - IF(ALSOFT_INSTALL) - INSTALL(TARGETS alplay alstream alreverb almultireverb allatency alhrtf + if(ALSOFT_INSTALL_EXAMPLES) + install(TARGETS alplay alstream alreverb almultireverb allatency alhrtf RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - ENDIF() + endif() MESSAGE(STATUS "Building SndFile example programs") ENDIF() @@ -1449,12 +1435,12 @@ IF(ALSOFT_EXAMPLES) TARGET_LINK_LIBRARIES(alloopback PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ex-common ${MATH_LIB}) - IF(ALSOFT_INSTALL) - INSTALL(TARGETS alloopback + if(ALSOFT_INSTALL_EXAMPLES) + install(TARGETS alloopback RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - ENDIF() + endif() MESSAGE(STATUS "Building SDL example programs") @@ -1489,13 +1475,13 @@ IF(ALSOFT_EXAMPLES) TARGET_LINK_LIBRARIES(alffplay PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} ex-common) - IF(ALSOFT_INSTALL) - INSTALL(TARGETS alffplay + if(ALSOFT_INSTALL_EXAMPLES) + install(TARGETS alffplay RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - ENDIF() - MESSAGE(STATUS "Building SDL+FFmpeg example programs") + endif() + message(STATUS "Building SDL+FFmpeg example programs") ENDIF() MESSAGE(STATUS "") ENDIF() -- cgit v1.2.3 From 8ef8b5e21576798d61f744de9c6e4d18df4b3c6b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 14 Apr 2020 23:37:48 -0700 Subject: Combine utility target installs --- CMakeLists.txt | 12 ++++++------ utils/alsoft-config/CMakeLists.txt | 7 ------- 2 files changed, 6 insertions(+), 13 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 41ce094b..1e5e03ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1344,6 +1344,12 @@ if(ALSOFT_UTILS) target_compile_options(sofa-info PRIVATE ${C_FLAGS}) target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} sofa-support) endif() + message(STATUS "Building utility programs") + if(TARGET alsoft-config) + set(UTIL_TARGETS ${UTIL_TARGETS} alsoft-config) + message(STATUS "Building configuration program") + endif() + message(STATUS "") if(ALSOFT_INSTALL_UTILS) install(TARGETS ${UTIL_TARGETS} @@ -1351,12 +1357,6 @@ if(ALSOFT_UTILS) LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() - - message(STATUS "Building utility programs") - if(TARGET alsoft-config) - message(STATUS "Building configuration program") - endif() - message(STATUS "") endif() diff --git a/utils/alsoft-config/CMakeLists.txt b/utils/alsoft-config/CMakeLists.txt index acc6038e..3851356b 100644 --- a/utils/alsoft-config/CMakeLists.txt +++ b/utils/alsoft-config/CMakeLists.txt @@ -16,15 +16,8 @@ if(Qt5Widgets_FOUND) target_link_libraries(alsoft-config Qt5::Widgets) target_include_directories(alsoft-config PRIVATE "${alsoft-config_BINARY_DIR}" "${OpenAL_BINARY_DIR}") - set_property(TARGET alsoft-config APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) set_target_properties(alsoft-config PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${OpenAL_BINARY_DIR}) if(TARGET build_version) add_dependencies(alsoft-config build_version) endif() - - install(TARGETS alsoft-config - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - ) endif() -- cgit v1.2.3 From 3a5a9e90d07c9a0e617e73e3beacdf75ba396e92 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 15 Apr 2020 00:09:45 -0700 Subject: Fix installing alsoft-config --- CMakeLists.txt | 8 ++++---- utils/alsoft-config/CMakeLists.txt | 10 +++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e5e03ea..5e25b850 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1058,7 +1058,7 @@ endif() IF(ALSOFT_UTILS AND NOT ALSOFT_NO_CONFIG_UTIL) - add_subdirectory(utils/alsoft-config) + find_package(Qt5Widgets) ENDIF() IF(ALSOFT_EXAMPLES) FIND_PACKAGE(SndFile) @@ -1345,9 +1345,9 @@ if(ALSOFT_UTILS) target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} sofa-support) endif() message(STATUS "Building utility programs") - if(TARGET alsoft-config) - set(UTIL_TARGETS ${UTIL_TARGETS} alsoft-config) - message(STATUS "Building configuration program") + + if(NOT ALSOFT_NO_CONFIG_UTIL) + add_subdirectory(utils/alsoft-config) endif() message(STATUS "") diff --git a/utils/alsoft-config/CMakeLists.txt b/utils/alsoft-config/CMakeLists.txt index 3851356b..65a70638 100644 --- a/utils/alsoft-config/CMakeLists.txt +++ b/utils/alsoft-config/CMakeLists.txt @@ -1,6 +1,5 @@ project(alsoft-config) -find_package(Qt5Widgets) if(Qt5Widgets_FOUND) qt5_wrap_ui(UIS mainwindow.ui) @@ -20,4 +19,13 @@ if(Qt5Widgets_FOUND) if(TARGET build_version) add_dependencies(alsoft-config build_version) endif() + + message(STATUS "Building configuration program") + + if(ALSOFT_INSTALL_UTILS) + install(TARGETS alsoft-config + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() endif() -- cgit v1.2.3 From 5214a7210a2da6003d811a0f9707f417f8a7cbc5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 15 Apr 2020 00:21:36 -0700 Subject: Combine multiple target installs --- CMakeLists.txt | 46 +++++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 27 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e25b850..1745704a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1299,14 +1299,15 @@ IF(ALSOFT_AMBDEC_PRESETS) MESSAGE(STATUS "") ENDIF() +set(EXTRA_INSTALLS ) if(ALSOFT_UTILS) - set(UTIL_TARGETS ) - ADD_EXECUTABLE(openal-info utils/openal-info.c) TARGET_INCLUDE_DIRECTORIES(openal-info PRIVATE ${OpenAL_SOURCE_DIR}/common) TARGET_COMPILE_OPTIONS(openal-info PRIVATE ${C_FLAGS}) TARGET_LINK_LIBRARIES(openal-info PRIVATE ${LINKER_FLAGS} OpenAL) - set(UTIL_TARGETS ${UTIL_TARGETS} openal-info) + if(ALSOFT_INSTALL_EXAMPLES) + set(EXTRA_INSTALLS ${EXTRA_INSTALLS} openal-info) + endif() find_package(MySOFA) if(MYSOFA_FOUND) @@ -1335,7 +1336,9 @@ if(ALSOFT_UTILS) PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/utils) target_compile_options(makemhr PRIVATE ${C_FLAGS}) target_link_libraries(makemhr PRIVATE ${LINKER_FLAGS} sofa-support) - set(UTIL_TARGETS ${UTIL_TARGETS} makemhr) + if(ALSOFT_INSTALL_EXAMPLES) + set(EXTRA_INSTALLS ${EXTRA_INSTALLS} makemhr) + endif() set(SOFAINFO_SRCS utils/sofa-info.cpp) add_executable(sofa-info ${SOFAINFO_SRCS}) @@ -1350,13 +1353,6 @@ if(ALSOFT_UTILS) add_subdirectory(utils/alsoft-config) endif() message(STATUS "") - - if(ALSOFT_INSTALL_UTILS) - install(TARGETS ${UTIL_TARGETS} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - endif() endif() @@ -1380,10 +1376,7 @@ IF(ALSOFT_EXAMPLES) TARGET_LINK_LIBRARIES(alrecord PRIVATE ${LINKER_FLAGS} ex-common) if(ALSOFT_INSTALL_EXAMPLES) - install(TARGETS altonegen alrecord - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + set(EXTRA_INSTALLS ${EXTRA_INSTALLS} altonegen alrecord) endif() MESSAGE(STATUS "Building example programs") @@ -1420,10 +1413,8 @@ IF(ALSOFT_EXAMPLES) TARGET_LINK_LIBRARIES(alstreamcb PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common) if(ALSOFT_INSTALL_EXAMPLES) - install(TARGETS alplay alstream alreverb almultireverb allatency alhrtf - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alplay alstream alreverb almultireverb allatency + alhrtf) endif() MESSAGE(STATUS "Building SndFile example programs") @@ -1436,10 +1427,7 @@ IF(ALSOFT_EXAMPLES) PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ex-common ${MATH_LIB}) if(ALSOFT_INSTALL_EXAMPLES) - install(TARGETS alloopback - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alloopback) endif() MESSAGE(STATUS "Building SDL example programs") @@ -1476,13 +1464,17 @@ IF(ALSOFT_EXAMPLES) PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} ex-common) if(ALSOFT_INSTALL_EXAMPLES) - install(TARGETS alffplay - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alffplay) endif() message(STATUS "Building SDL+FFmpeg example programs") ENDIF() MESSAGE(STATUS "") ENDIF() ENDIF() + +if(EXTRA_INSTALLS) + install(TARGETS ${EXTRA_INSTALLS} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +endif() -- cgit v1.2.3 From 67e54a26697f5e898f5b0e040f18281a28c16023 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 18 Apr 2020 15:17:53 -0700 Subject: Add an Oboe backend stub --- CMakeLists.txt | 29 +++++++++++++++++++++++++++++ alc/alc.cpp | 6 ++++++ alc/backends/oboe.cpp | 28 ++++++++++++++++++++++++++++ alc/backends/oboe.h | 19 +++++++++++++++++++ cmake/FindOboe.cmake | 31 +++++++++++++++++++++++++++++++ config.h.in | 3 +++ 6 files changed, 116 insertions(+) create mode 100644 alc/backends/oboe.cpp create mode 100644 alc/backends/oboe.h create mode 100644 cmake/FindOboe.cmake (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 1745704a..782ac46d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -988,6 +988,35 @@ IF(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL) MESSAGE(FATAL_ERROR "Failed to enabled required OpenSL backend") ENDIF() +# Check for Oboe (Android) backend +set(OBOE_TARGET ) +if(ANDROID) + set(OBOE_SOURCE "" CACHE STRING "Source directory for Oboe.") + if(OBOE_SOURCE) + add_subdirectory(${OBOE_SOURCE} ./oboe) + set(OBOE_TARGET oboe) + else() + find_package(Oboe) + if(OBOE_FOUND) + set(OBOE_TARGET "oboe::oboe") + endif() + endif() +endif() + +option(ALSOFT_REQUIRE_OBOE "Require Oboe backend" OFF) +if(OBOE_TARGET) + option(ALSOFT_BACKEND_OBOE "Enable Oboe backend" ON) + if(ALSOFT_BACKEND_OBOE) + set(HAVE_OBOE 1) + set(ALC_OBJS ${ALC_OBJS} alc/backends/oboe.cpp alc/backends/oboe.h) + set(BACKENDS "${BACKENDS} Oboe,") + set(EXTRA_LIBS ${OBOE_TARGET} ${EXTRA_LIBS}) + endif() +endif() +if(ALSOFT_REQUIRE_OBOE AND NOT HAVE_OBOE) + message(FATAL_ERROR "Failed to enabled required Oboe backend") +endif() + # Check for SDL2 backend OPTION(ALSOFT_REQUIRE_SDL2 "Require SDL2 backend" OFF) FIND_PACKAGE(SDL2) diff --git a/alc/alc.cpp b/alc/alc.cpp index e8c23476..d0d8510b 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -116,6 +116,9 @@ #ifdef HAVE_OPENSL #include "backends/opensl.h" #endif +#ifdef HAVE_OBOE +#include "backends/oboe.h" +#endif #ifdef HAVE_SOLARIS #include "backends/solaris.h" #endif @@ -173,6 +176,9 @@ BackendInfo BackendList[] = { #ifdef HAVE_COREAUDIO { "core", CoreAudioBackendFactory::getFactory }, #endif +#ifdef HAVE_OBOE + { "oboe", OboeBackendFactory::getFactory }, +#endif #ifdef HAVE_OPENSL { "opensl", OSLBackendFactory::getFactory }, #endif diff --git a/alc/backends/oboe.cpp b/alc/backends/oboe.cpp new file mode 100644 index 00000000..54fd0f1c --- /dev/null +++ b/alc/backends/oboe.cpp @@ -0,0 +1,28 @@ + +#include "config.h" + +#include "oboe.h" + +#include "oboe/Oboe.h" + + +bool OboeBackendFactory::init() { return true; } + +bool OboeBackendFactory::querySupport(BackendType /*type*/) +{ return false; } + +std::string OboeBackendFactory::probe(BackendType /*type*/) +{ + return std::string{}; +} + +BackendPtr OboeBackendFactory::createBackend(ALCdevice* /*device*/, BackendType /*type*/) +{ + return nullptr; +} + +BackendFactory &OboeBackendFactory::getFactory() +{ + static OboeBackendFactory factory{}; + return factory; +} diff --git a/alc/backends/oboe.h b/alc/backends/oboe.h new file mode 100644 index 00000000..93b98e37 --- /dev/null +++ b/alc/backends/oboe.h @@ -0,0 +1,19 @@ +#ifndef BACKENDS_OBOE_H +#define BACKENDS_OBOE_H + +#include "backends/base.h" + +struct OboeBackendFactory final : public BackendFactory { +public: + bool init() override; + + bool querySupport(BackendType type) override; + + std::string probe(BackendType type) override; + + BackendPtr createBackend(ALCdevice *device, BackendType type) override; + + static BackendFactory &getFactory(); +}; + +#endif /* BACKENDS_OBOE_H */ diff --git a/cmake/FindOboe.cmake b/cmake/FindOboe.cmake new file mode 100644 index 00000000..bf12c12c --- /dev/null +++ b/cmake/FindOboe.cmake @@ -0,0 +1,31 @@ +# - Find Oboe +# Find the Oboe library +# +# This module defines the following variable: +# OBOE_FOUND - True if Oboe was found +# +# This module defines the following target: +# oboe::oboe - Import target for linking Oboe to a project +# + +find_path(OBOE_INCLUDE_DIR NAMES oboe/Oboe.h + DOC "The Oboe include directory" +) + +find_library(OBOE_LIBRARY NAMES oboe + DOC "The Oboe library" +) + +# handle the QUIETLY and REQUIRED arguments and set OBOE_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Oboe REQUIRED_VARS OBOE_LIBRARY OBOE_INCLUDE_DIR) + +if(OBOE_FOUND) + add_library(oboe::oboe UNKNOWN IMPORTED) + set_target_properties(oboe::oboe PROPERTIES + IMPORTED_LOCATION ${OBOE_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${OBOE_INCLUDE_DIR}) +endif() + +mark_as_advanced(OBOE_INCLUDE_DIR OBOE_LIBRARY) diff --git a/config.h.in b/config.h.in index 60879df9..29a10b20 100644 --- a/config.h.in +++ b/config.h.in @@ -65,6 +65,9 @@ /* Define if we have the OpenSL backend */ #cmakedefine HAVE_OPENSL +/* Define if we have the Oboe backend */ +#cmakedefine HAVE_OBOE + /* Define if we have the Wave Writer backend */ #cmakedefine HAVE_WAVE -- cgit v1.2.3 From 253773b3161a660f04ea4fa0dcf60e0ba7102f16 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 18 Apr 2020 23:03:50 -0700 Subject: Build Oboe with hidden visibility --- CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 782ac46d..d02bcc17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -993,7 +993,17 @@ set(OBOE_TARGET ) if(ANDROID) set(OBOE_SOURCE "" CACHE STRING "Source directory for Oboe.") if(OBOE_SOURCE) + # Force Oboe to build with hidden symbols. Don't want to be exporting + # them from OpenAL. + set(OLD_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + check_cxx_compiler_flag(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN_SWITCH) + if(HAVE_VISIBILITY_HIDDEN_SWITCH) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") + endif() add_subdirectory(${OBOE_SOURCE} ./oboe) + set(CMAKE_CXX_FLAGS ${OLD_CXX_FLAGS}) + unset(OLD_CXX_FLAGS) + set(OBOE_TARGET oboe) else() find_package(Oboe) -- cgit v1.2.3 From 12d71a0ad1af132111f9e20d96df97d8da8b4853 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 19 Apr 2020 00:37:03 -0700 Subject: Don't explicitly link libpthread Compiling and linking with -pthread is apparently enough. --- CMakeLists.txt | 5 ----- 1 file changed, 5 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index d02bcc17..12cbb742 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -450,11 +450,6 @@ IF(NOT WIN32) SET(LINKER_FLAGS ${LINKER_FLAGS} -pthread) ENDIF() - CHECK_LIBRARY_EXISTS(pthread pthread_create "" HAVE_LIBPTHREAD) - IF(HAVE_LIBPTHREAD) - SET(EXTRA_LIBS pthread ${EXTRA_LIBS}) - ENDIF() - CHECK_SYMBOL_EXISTS(pthread_setschedparam pthread.h HAVE_PTHREAD_SETSCHEDPARAM) # Some systems need pthread_np.h to get pthread_setname_np -- cgit v1.2.3 From 0a0478f670970126e3f53a5133c0a941db8407a5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 19 Apr 2020 00:50:12 -0700 Subject: Rename install options for consistency --- CMakeLists.txt | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 12cbb742..10da4467 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,21 +49,21 @@ include(CheckStructHasMember) include(GNUInstallDirs) -OPTION(ALSOFT_DLOPEN "Check for the dlopen API for loading optional libs" ON) +option(ALSOFT_DLOPEN "Check for the dlopen API for loading optional libs" ON) -OPTION(ALSOFT_WERROR "Treat compile warnings as errors" OFF) +option(ALSOFT_WERROR "Treat compile warnings as errors" OFF) -OPTION(ALSOFT_UTILS "Build and install utility programs" ON) -OPTION(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF) +option(ALSOFT_UTILS "Build utility programs" ON) +option(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF) -OPTION(ALSOFT_EXAMPLES "Build example programs" ON) +option(ALSOFT_EXAMPLES "Build example programs" ON) -OPTION(ALSOFT_CONFIG "Install alsoft.conf sample configuration file" ON) -OPTION(ALSOFT_HRTF_DEFS "Install HRTF definition files" ON) -OPTION(ALSOFT_AMBDEC_PRESETS "Install AmbDec preset files" ON) -OPTION(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" ON) -OPTION(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" ON) -OPTION(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON) +option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" ON) +option(ALSOFT_INSTALL_HRTF_DEFS "Install HRTF definition files" ON) +option(ALSOFT_INSTALL_AMBDEC_PRESETS "Install AmbDec preset files" ON) +option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" ON) +option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" ON) +option(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON) if(DEFINED SHARE_INSTALL_DIR) message(WARNING "SHARE_INSTALL_DIR is deprecated. Use the variables provided by the GNUInstallDirs module instead") @@ -73,6 +73,15 @@ endif() if(DEFINED LIB_SUFFIX) message(WARNING "LIB_SUFFIX is deprecated. Use the variables provided by the GNUInstallDirs module instead") endif() +if(DEFINED ALSOFT_CONFIG) + message(WARNING "ALSOFT_CONFIG is deprecated. Use ALSOFT_INSTALL_CONFIG instead") +endif() +if(DEFINED ALSOFT_HRTF_DEFS) + message(WARNING "ALSOFT_HRTF_DEFS is deprecated. Use ALSOFT_INSTALL_HRTF_DEFS instead") +endif() +if(DEFINED ALSOFT_AMBDEC_PRESETS) + message(WARNING "ALSOFT_AMBDEC_PRESETS is deprecated. Use ALSOFT_INSTALL_AMBDEC_PRESETS instead") +endif() SET(CPP_DEFS ) # C pre-processor, not C++ @@ -1303,7 +1312,7 @@ if(ALSOFT_EMBED_HRTF_DATA) endif() # Install alsoft.conf configuration file -IF(ALSOFT_CONFIG) +IF(ALSOFT_INSTALL_CONFIG) INSTALL(FILES alsoftrc.sample DESTINATION ${CMAKE_INSTALL_DATADIR}/openal) MESSAGE(STATUS "Installing sample configuration") @@ -1311,7 +1320,7 @@ IF(ALSOFT_CONFIG) ENDIF() # Install HRTF definitions -IF(ALSOFT_HRTF_DEFS) +IF(ALSOFT_INSTALL_HRTF_DEFS) INSTALL(FILES "hrtf/Default HRTF.mhr" DESTINATION ${CMAKE_INSTALL_DATADIR}/openal/hrtf) MESSAGE(STATUS "Installing HRTF definitions") @@ -1319,7 +1328,7 @@ IF(ALSOFT_HRTF_DEFS) ENDIF() # Install AmbDec presets -IF(ALSOFT_AMBDEC_PRESETS) +IF(ALSOFT_INSTALL_AMBDEC_PRESETS) INSTALL(FILES presets/3D7.1.ambdec presets/hexagon.ambdec -- cgit v1.2.3 From 1ca7f22145ffcfc2e06fc834644f43a730089a26 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 19 Apr 2020 02:47:11 -0700 Subject: Correctly test for SSE switches --- CMakeLists.txt | 58 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 25 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 10da4467..e1343798 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -309,33 +309,41 @@ ELSE() SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}") ENDIF() -SET(SSE2_SWITCH "") -SET(SSE3_SWITCH "") -SET(SSE4_1_SWITCH "") -SET(FPU_NEON_SWITCH "") -CHECK_C_COMPILER_FLAG(-msse2 HAVE_MSSE2_SWITCH) -IF(HAVE_MSSE2_SWITCH) - SET(SSE2_SWITCH "-msse2") -ENDIF() -CHECK_C_COMPILER_FLAG(-msse3 HAVE_MSSE3_SWITCH) -IF(HAVE_MSSE3_SWITCH) - SET(SSE3_SWITCH "-msse3") -ENDIF() -CHECK_C_COMPILER_FLAG(-msse4.1 HAVE_MSSE4_1_SWITCH) -IF(HAVE_MSSE4_1_SWITCH) - SET(SSE4_1_SWITCH "-msse4.1") -ENDIF() -CHECK_C_COMPILER_FLAG(-mfpu=neon HAVE_MFPU_NEON_SWITCH) -IF(HAVE_MFPU_NEON_SWITCH) - SET(FPU_NEON_SWITCH "-mfpu=neon") -ENDIF() +set(SSE2_SWITCH "") +set(SSE3_SWITCH "") +set(SSE4_1_SWITCH "") +set(FPU_NEON_SWITCH "") -CHECK_INCLUDE_FILE(xmmintrin.h HAVE_XMMINTRIN_H "${SSE2_SWITCH}") -CHECK_INCLUDE_FILE(emmintrin.h HAVE_EMMINTRIN_H "${SSE2_SWITCH}") -CHECK_INCLUDE_FILE(pmmintrin.h HAVE_PMMINTRIN_H "${SSE3_SWITCH}") -CHECK_INCLUDE_FILE(smmintrin.h HAVE_SMMINTRIN_H "${SSE4_1_SWITCH}") -CHECK_INCLUDE_FILE(arm_neon.h HAVE_ARM_NEON_H "${FPU_NEON_SWITCH}") +set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) +if(NOT MSVC) + # Yes GCC, really don't accept command line options you don't support + set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Werror") +endif() +check_c_compiler_flag(-msse2 HAVE_MSSE2_SWITCH) +if(HAVE_MSSE2_SWITCH) + set(SSE2_SWITCH "-msse2") + check_c_compiler_flag(-msse3 HAVE_MSSE3_SWITCH) + if(HAVE_MSSE3_SWITCH) + set(SSE3_SWITCH "-msse3") + check_c_compiler_flag(-msse4.1 HAVE_MSSE4_1_SWITCH) + if(HAVE_MSSE4_1_SWITCH) + set(SSE4_1_SWITCH "-msse4.1") + endif() + endif() +endif() +check_c_compiler_flag(-mfpu=neon HAVE_MFPU_NEON_SWITCH) +if(HAVE_MFPU_NEON_SWITCH) + set(FPU_NEON_SWITCH "-mfpu=neon") +endif() +set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS}) +unset(OLD_REQUIRED_FLAGS) + +check_include_file(xmmintrin.h HAVE_XMMINTRIN_H ${SSE2_SWITCH}) +check_include_file(emmintrin.h HAVE_EMMINTRIN_H ${SSE2_SWITCH}) +check_include_file(pmmintrin.h HAVE_PMMINTRIN_H ${SSE3_SWITCH}) +check_include_file(smmintrin.h HAVE_SMMINTRIN_H ${SSE4_1_SWITCH}) +check_include_file(arm_neon.h HAVE_ARM_NEON_H ${FPU_NEON_SWITCH}) set(SSE_FLAGS ) set(FPMATH_SET "0") -- cgit v1.2.3 From 07726ad63afda431f71b2ec9e5a6d70702377bb4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 19 Apr 2020 03:34:46 -0700 Subject: Make sure HAVE_OBOE is cleared before checking --- CMakeLists.txt | 53 +++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index e1343798..45f1317e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -732,33 +732,34 @@ IF(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON) ENDIF() -SET(HAVE_ALSA 0) -SET(HAVE_OSS 0) -SET(HAVE_SOLARIS 0) -SET(HAVE_SNDIO 0) -SET(HAVE_DSOUND 0) -SET(HAVE_WASAPI 0) -SET(HAVE_WINMM 0) -SET(HAVE_PORTAUDIO 0) -SET(HAVE_PULSEAUDIO 0) -SET(HAVE_COREAUDIO 0) -SET(HAVE_OPENSL 0) -SET(HAVE_WAVE 0) -SET(HAVE_SDL2 0) - -IF(WIN32 OR HAVE_DLFCN_H) - SET(IS_LINKED "") - MACRO(ADD_BACKEND_LIBS _LIBS) - ENDMACRO() -ELSE() - SET(IS_LINKED " (linked)") - MACRO(ADD_BACKEND_LIBS _LIBS) - SET(EXTRA_LIBS ${_LIBS} ${EXTRA_LIBS}) - ENDMACRO() -ENDIF() +set(HAVE_ALSA 0) +set(HAVE_OSS 0) +set(HAVE_SOLARIS 0) +set(HAVE_SNDIO 0) +set(HAVE_DSOUND 0) +set(HAVE_WASAPI 0) +set(HAVE_WINMM 0) +set(HAVE_PORTAUDIO 0) +set(HAVE_PULSEAUDIO 0) +set(HAVE_COREAUDIO 0) +set(HAVE_OPENSL 0) +set(HAVE_OBOE 0) +set(HAVE_WAVE 0) +set(HAVE_SDL2 0) + +if(WIN32 OR HAVE_DLFCN_H) + set(IS_LINKED "") + macro(ADD_BACKEND_LIBS _LIBS) + endmacro() +else() + set(IS_LINKED " (linked)") + macro(ADD_BACKEND_LIBS _LIBS) + set(EXTRA_LIBS ${_LIBS} ${EXTRA_LIBS}) + endmacro() +endif() -SET(BACKENDS "") -SET(ALC_OBJS ${ALC_OBJS} +set(BACKENDS "") +set(ALC_OBJS ${ALC_OBJS} alc/backends/base.cpp alc/backends/base.h # Default backends, always available -- cgit v1.2.3 From 3902337e01efca5ecda82556c37074e47fc87889 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 19 Apr 2020 03:37:32 -0700 Subject: Fix some message formatting --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 45f1317e..947ffcf9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1520,8 +1520,8 @@ IF(ALSOFT_EXAMPLES) endif() message(STATUS "Building SDL+FFmpeg example programs") ENDIF() - MESSAGE(STATUS "") ENDIF() + MESSAGE(STATUS "") ENDIF() if(EXTRA_INSTALLS) -- cgit v1.2.3 From 9b9a65a0bfe2aec00f6768c217b972598e22a934 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 19 Apr 2020 04:00:01 -0700 Subject: Use an import target for libsndfile --- CMakeLists.txt | 60 +++++++++++++++++++++---------------------------- cmake/FindSndFile.cmake | 10 +++++---- 2 files changed, 31 insertions(+), 39 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 947ffcf9..63f45947 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1417,60 +1417,50 @@ TARGET_INCLUDE_DIRECTORIES(ex-common PUBLIC ${OpenAL_SOURCE_DIR}/common) TARGET_COMPILE_OPTIONS(ex-common PUBLIC ${C_FLAGS}) TARGET_LINK_LIBRARIES(ex-common PUBLIC OpenAL PRIVATE ${RT_LIB}) -IF(ALSOFT_EXAMPLES) - ADD_EXECUTABLE(altonegen examples/altonegen.c) - TARGET_COMPILE_DEFINITIONS(altonegen PRIVATE ${CPP_DEFS}) - TARGET_INCLUDE_DIRECTORIES(altonegen PRIVATE ${OpenAL_SOURCE_DIR}/common) - TARGET_COMPILE_OPTIONS(altonegen PRIVATE ${C_FLAGS}) - TARGET_LINK_LIBRARIES(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} ex-common) +if(ALSOFT_EXAMPLES) + add_executable(altonegen examples/altonegen.c) + target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} ex-common) - ADD_EXECUTABLE(alrecord examples/alrecord.c) - TARGET_LINK_LIBRARIES(alrecord PRIVATE ${LINKER_FLAGS} ex-common) + add_executable(alrecord examples/alrecord.c) + target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} ex-common) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} altonegen alrecord) endif() - MESSAGE(STATUS "Building example programs") + message(STATUS "Building example programs") - IF(SNDFILE_FOUND) - ADD_EXECUTABLE(alplay examples/alplay.c) - TARGET_INCLUDE_DIRECTORIES(alplay PRIVATE ${SNDFILE_INCLUDE_DIRS}) - TARGET_LINK_LIBRARIES(alplay PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common) + if(SNDFILE_FOUND) + add_executable(alplay examples/alplay.c) + target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common) - ADD_EXECUTABLE(alstream examples/alstream.c) - TARGET_INCLUDE_DIRECTORIES(alstream PRIVATE ${SNDFILE_INCLUDE_DIRS}) - TARGET_LINK_LIBRARIES(alstream PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common) + add_executable(alstream examples/alstream.c) + target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common) - ADD_EXECUTABLE(alreverb examples/alreverb.c) - TARGET_INCLUDE_DIRECTORIES(alreverb PRIVATE ${SNDFILE_INCLUDE_DIRS}) - TARGET_LINK_LIBRARIES(alreverb PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common) + add_executable(alreverb examples/alreverb.c) + target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common) - ADD_EXECUTABLE(almultireverb examples/almultireverb.c) - TARGET_INCLUDE_DIRECTORIES(almultireverb PRIVATE ${SNDFILE_INCLUDE_DIRS}) - TARGET_LINK_LIBRARIES(almultireverb - PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common ${MATH_LIB}) + add_executable(almultireverb examples/almultireverb.c) + target_link_libraries(almultireverb + PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB}) - ADD_EXECUTABLE(allatency examples/allatency.c) - TARGET_INCLUDE_DIRECTORIES(allatency PRIVATE ${SNDFILE_INCLUDE_DIRS}) - TARGET_LINK_LIBRARIES(allatency PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common) + add_executable(allatency examples/allatency.c) + target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common) - ADD_EXECUTABLE(alhrtf examples/alhrtf.c) - TARGET_INCLUDE_DIRECTORIES(alhrtf PRIVATE ${SNDFILE_INCLUDE_DIRS}) - TARGET_LINK_LIBRARIES(alhrtf - PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common ${MATH_LIB}) + add_executable(alhrtf examples/alhrtf.c) + target_link_libraries(alhrtf + PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB}) - ADD_EXECUTABLE(alstreamcb examples/alstreamcb.cpp) - TARGET_INCLUDE_DIRECTORIES(alstreamcb PRIVATE ${SNDFILE_INCLUDE_DIRS}) - TARGET_LINK_LIBRARIES(alstreamcb PRIVATE ${LINKER_FLAGS} ${SNDFILE_LIBRARIES} ex-common) + add_executable(alstreamcb examples/alstreamcb.cpp) + target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alplay alstream alreverb almultireverb allatency alhrtf) endif() - MESSAGE(STATUS "Building SndFile example programs") - ENDIF() + message(STATUS "Building SndFile example programs") + endif() IF(SDL2_FOUND) ADD_EXECUTABLE(alloopback examples/alloopback.c) diff --git a/cmake/FindSndFile.cmake b/cmake/FindSndFile.cmake index afeec961..b931d3c0 100644 --- a/cmake/FindSndFile.cmake +++ b/cmake/FindSndFile.cmake @@ -2,8 +2,8 @@ # Once done this will define # # SNDFILE_FOUND - system has SndFile -# SNDFILE_INCLUDE_DIRS - the SndFile include directory -# SNDFILE_LIBRARIES - Link these to use SndFile +# SndFile::SndFile - the SndFile target +# find_path(SNDFILE_INCLUDE_DIR NAMES sndfile.h) @@ -15,8 +15,10 @@ include(FindPackageHandleStandardArgs) find_package_handle_standard_args(SndFile DEFAULT_MSG SNDFILE_LIBRARY SNDFILE_INCLUDE_DIR) if(SNDFILE_FOUND) - set(SNDFILE_INCLUDE_DIRS ${SNDFILE_INCLUDE_DIR}) - set(SNDFILE_LIBRARIES ${SNDFILE_LIBRARY}) + add_library(SndFile::SndFile UNKNOWN IMPORTED) + set_target_properties(SndFile::SndFile PROPERTIES + IMPORTED_LOCATION ${SNDFILE_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${SNDFILE_INCLUDE_DIR}) endif() # show the SNDFILE_INCLUDE_DIR and SNDFILE_LIBRARY variables only in the advanced view -- cgit v1.2.3 From 61cf6d3bb656c8d7f04f5c77f6bd28294551395e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 19 Apr 2020 04:38:44 -0700 Subject: Simplify some install statements --- CMakeLists.txt | 51 +++++++++++++++++++-------------------------------- 1 file changed, 19 insertions(+), 32 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 63f45947..16f2351b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1282,14 +1282,8 @@ install(EXPORT OpenAL DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL NAMESPACE OpenAL:: FILE OpenALConfig.cmake) -install(FILES - include/AL/al.h - include/AL/alc.h - include/AL/alext.h - include/AL/efx.h - include/AL/efx-creative.h - include/AL/efx-presets.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/AL) +install(DIRECTORY include/AL + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(FILES "${OpenAL_BINARY_DIR}/openal.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") if(TARGET soft_oal) @@ -1321,35 +1315,28 @@ if(ALSOFT_EMBED_HRTF_DATA) endif() # Install alsoft.conf configuration file -IF(ALSOFT_INSTALL_CONFIG) - INSTALL(FILES alsoftrc.sample +if(ALSOFT_INSTALL_CONFIG) + install(FILES alsoftrc.sample DESTINATION ${CMAKE_INSTALL_DATADIR}/openal) - MESSAGE(STATUS "Installing sample configuration") - MESSAGE(STATUS "") -ENDIF() + message(STATUS "Installing sample configuration") + message(STATUS "") +endif() # Install HRTF definitions -IF(ALSOFT_INSTALL_HRTF_DEFS) - INSTALL(FILES "hrtf/Default HRTF.mhr" - DESTINATION ${CMAKE_INSTALL_DATADIR}/openal/hrtf) - MESSAGE(STATUS "Installing HRTF definitions") - MESSAGE(STATUS "") -ENDIF() +if(ALSOFT_INSTALL_HRTF_DEFS) + install(DIRECTORY hrtf + DESTINATION ${CMAKE_INSTALL_DATADIR}/openal) + message(STATUS "Installing HRTF definitions") + message(STATUS "") +endif() # Install AmbDec presets -IF(ALSOFT_INSTALL_AMBDEC_PRESETS) - INSTALL(FILES - presets/3D7.1.ambdec - presets/hexagon.ambdec - presets/itu5.1.ambdec - presets/itu5.1-nocenter.ambdec - presets/rectangle.ambdec - presets/square.ambdec - presets/presets.txt - DESTINATION ${CMAKE_INSTALL_DATADIR}/openal/presets) - MESSAGE(STATUS "Installing AmbDec presets") - MESSAGE(STATUS "") -ENDIF() +if(ALSOFT_INSTALL_AMBDEC_PRESETS) + install(DIRECTORY presets + DESTINATION ${CMAKE_INSTALL_DATADIR}/openal) + message(STATUS "Installing AmbDec presets") + message(STATUS "") +endif() set(EXTRA_INSTALLS ) if(ALSOFT_UTILS) -- cgit v1.2.3 From e3f6afafdeab3c5ec8e28c3f9b6b26fbcf5f37bf Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 25 Apr 2020 02:12:43 -0700 Subject: Remove some hopefully unneeded macros --- CMakeLists.txt | 8 +------- alc/alconfig.cpp | 10 +--------- alc/helpers.cpp | 11 ----------- 3 files changed, 2 insertions(+), 27 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 16f2351b..169fe5a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,7 +91,7 @@ SET(LINKER_FLAGS ) SET(EXTRA_LIBS ) IF(WIN32) - SET(CPP_DEFS ${CPP_DEFS} _WIN32 _WIN32_WINNT=0x0502) + SET(CPP_DEFS ${CPP_DEFS} _WIN32) OPTION(ALSOFT_BUILD_ROUTER "Build the router (EXPERIMENTAL; creates OpenAL32.dll and soft_oal.dll)" OFF) @@ -851,9 +851,6 @@ IF(WIN32) get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_INCLUDE_DIRS) ENDIF() - SET(OLD_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}) - SET(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_WIN32_WINNT=0x0502) - # Check MMSystem backend CHECK_INCLUDE_FILES("windows.h;mmsystem.h" HAVE_MMSYSTEM_H) FIND_LIBRARY(WINMM_LIBRARY NAMES winmm @@ -892,9 +889,6 @@ IF(WIN32) SET(ALC_OBJS ${ALC_OBJS} alc/backends/wasapi.cpp alc/backends/wasapi.h) ENDIF() ENDIF() - - SET(CMAKE_REQUIRED_DEFINITIONS ${OLD_REQUIRED_DEFINITIONS}) - UNSET(OLD_REQUIRED_DEFINITIONS) ENDIF() IF(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM) MESSAGE(FATAL_ERROR "Failed to enabled required WinMM backend") diff --git a/alc/alconfig.cpp b/alc/alconfig.cpp index dfafa231..ac075b54 100644 --- a/alc/alconfig.cpp +++ b/alc/alconfig.cpp @@ -18,14 +18,6 @@ * Or go to http://www.gnu.org/copyleft/lgpl.html */ -#ifdef _WIN32 -#ifdef __MINGW32__ -#define _WIN32_IE 0x501 -#else -#define _WIN32_IE 0x400 -#endif -#endif - #include "config.h" #include "alconfig.h" @@ -33,7 +25,7 @@ #include #include #include -#ifdef _WIN32_IE +#ifdef _WIN32 #include #include #endif diff --git a/alc/helpers.cpp b/alc/helpers.cpp index 2aee6a2d..22483dd3 100644 --- a/alc/helpers.cpp +++ b/alc/helpers.cpp @@ -18,17 +18,6 @@ * Or go to http://www.gnu.org/copyleft/lgpl.html */ -/* Define this first since Windows' system headers may include headers affected - * by it (is it even still needed?). - */ -#ifdef _WIN32 -#ifdef __MINGW32__ -#define _WIN32_IE 0x501 -#else -#define _WIN32_IE 0x400 -#endif -#endif - #include "config.h" #include -- cgit v1.2.3 From 0a69adf845f7573ecfdef3dbd1ec5c40f3a74faf Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 26 Apr 2020 07:59:08 -0700 Subject: Avoid unnecessary PATH_SUFFIXES --- CMakeLists.txt | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 169fe5a7..6c8d06a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -852,19 +852,18 @@ IF(WIN32) ENDIF() # Check MMSystem backend - CHECK_INCLUDE_FILES("windows.h;mmsystem.h" HAVE_MMSYSTEM_H) - FIND_LIBRARY(WINMM_LIBRARY NAMES winmm - PATHS ${WINSDK_LIB_DIRS} - PATH_SUFFIXES lib lib/x86 lib/x64) - IF(HAVE_MMSYSTEM_H AND WINMM_LIBRARY) - OPTION(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON) - IF(ALSOFT_BACKEND_WINMM) - SET(HAVE_WINMM 1) - SET(BACKENDS "${BACKENDS} WinMM,") - SET(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h) - SET(EXTRA_LIBS ${WINMM_LIBRARY} ${EXTRA_LIBS}) - ENDIF() - ENDIF() + check_include_files("windows.h;mmsystem.h" HAVE_MMSYSTEM_H) + find_library(WINMM_LIBRARY NAMES winmm + PATHS ${WINSDK_LIB_DIRS}) + if(HAVE_MMSYSTEM_H AND WINMM_LIBRARY) + option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON) + if(ALSOFT_BACKEND_WINMM) + set(HAVE_WINMM 1) + set(BACKENDS "${BACKENDS} WinMM,") + set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h) + set(EXTRA_LIBS ${WINMM_LIBRARY} ${EXTRA_LIBS}) + endif() + endif() # Check DSound backend FIND_PACKAGE(DSound) -- cgit v1.2.3 From 4630a535b26a34a6592b7dea19da2c2261930568 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 7 May 2020 00:02:23 -0700 Subject: Re-add an option for installing the main lib --- CMakeLists.txt | 57 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 28 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c8d06a2..9bc22168 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,7 @@ option(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF) option(ALSOFT_EXAMPLES "Build example programs" ON) +option(ALSOFT_INSTALL "Install main library" ON) option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" ON) option(ALSOFT_INSTALL_HRTF_DEFS "Install HRTF definition files" ON) option(ALSOFT_INSTALL_AMBDEC_PRESETS "Install AmbDec preset files" ON) @@ -1263,28 +1264,6 @@ IF(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC" ENDIF() ENDIF() -install(TARGETS OpenAL EXPORT OpenAL - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/AL) -export(TARGETS OpenAL - NAMESPACE OpenAL:: - FILE OpenALConfig.cmake) -install(EXPORT OpenAL - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL - NAMESPACE OpenAL:: - FILE OpenALConfig.cmake) -install(DIRECTORY include/AL - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(FILES "${OpenAL_BINARY_DIR}/openal.pc" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") -if(TARGET soft_oal) - install(TARGETS soft_oal - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -endif() - - if(HAS_ROUTER) message(STATUS "") message(STATUS "Building DLL router") @@ -1307,29 +1286,51 @@ if(ALSOFT_EMBED_HRTF_DATA) message(STATUS "") endif() -# Install alsoft.conf configuration file +# Install main library +if(ALSOFT_INSTALL) + install(TARGETS OpenAL EXPORT OpenAL + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/AL) + export(TARGETS OpenAL + NAMESPACE OpenAL:: + FILE OpenALConfig.cmake) + install(EXPORT OpenAL + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL + NAMESPACE OpenAL:: + FILE OpenALConfig.cmake) + install(DIRECTORY include/AL + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + install(FILES "${OpenAL_BINARY_DIR}/openal.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + if(TARGET soft_oal) + install(TARGETS soft_oal + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() + message(STATUS "Installing library and headers") +else() + message(STATUS "NOT installing library and headers") +endif() + if(ALSOFT_INSTALL_CONFIG) install(FILES alsoftrc.sample DESTINATION ${CMAKE_INSTALL_DATADIR}/openal) message(STATUS "Installing sample configuration") - message(STATUS "") endif() -# Install HRTF definitions if(ALSOFT_INSTALL_HRTF_DEFS) install(DIRECTORY hrtf DESTINATION ${CMAKE_INSTALL_DATADIR}/openal) message(STATUS "Installing HRTF definitions") - message(STATUS "") endif() -# Install AmbDec presets if(ALSOFT_INSTALL_AMBDEC_PRESETS) install(DIRECTORY presets DESTINATION ${CMAKE_INSTALL_DATADIR}/openal) message(STATUS "Installing AmbDec presets") - message(STATUS "") endif() +message(STATUS "") set(EXTRA_INSTALLS ) if(ALSOFT_UTILS) -- cgit v1.2.3 From 2574b98f8ad0efc0c96c81325e55d686c7e33e5b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 11 May 2020 02:24:08 -0700 Subject: Move the bsinc tables out of common --- CMakeLists.txt | 6 +- alc/bsinc_defs.h | 13 ++ alc/bsinc_tables.cpp | 343 ++++++++++++++++++++++++++++++++++++++++++++++++ alc/bsinc_tables.h | 17 +++ common/bsinc_defs.h | 13 -- common/bsinc_tables.cpp | 343 ------------------------------------------------ common/bsinc_tables.h | 17 --- 7 files changed, 376 insertions(+), 376 deletions(-) create mode 100644 alc/bsinc_defs.h create mode 100644 alc/bsinc_tables.cpp create mode 100644 alc/bsinc_tables.h delete mode 100644 common/bsinc_defs.h delete mode 100644 common/bsinc_tables.cpp delete mode 100644 common/bsinc_tables.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bc22168..208a275f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -546,9 +546,6 @@ SET(COMMON_OBJS common/alstring.cpp common/alstring.h common/atomic.h - common/bsinc_defs.h - common/bsinc_tables.cpp - common/bsinc_tables.h common/dynload.cpp common/dynload.h common/endiantest.h @@ -599,6 +596,9 @@ SET(ALC_OBJS alc/bformatdec.h alc/bs2b.cpp alc/bs2b.h + alc/bsinc_defs.h + alc/bsinc_tables.cpp + alc/bsinc_tables.h alc/compat.h alc/converter.cpp alc/converter.h diff --git a/alc/bsinc_defs.h b/alc/bsinc_defs.h new file mode 100644 index 00000000..30d1219e --- /dev/null +++ b/alc/bsinc_defs.h @@ -0,0 +1,13 @@ +#ifndef BSINC_DEFS_H +#define BSINC_DEFS_H + +/* The number of distinct scale and phase intervals within the filter table. */ +#define BSINC_SCALE_BITS 4 +#define BSINC_SCALE_COUNT (1< +#include +#include +#include +#include +#include +#include + +#include "math_defs.h" +#include "vector.h" + + +namespace { + +/* The max points includes the doubling for downsampling, so the maximum number + * of base sample points is 24, which is 23rd order. + */ +constexpr int BSincPointsMax{BSINC_POINTS_MAX}; +constexpr int BSincPointsHalf{BSincPointsMax / 2}; + +constexpr int BSincPhaseCount{BSINC_PHASE_COUNT}; +constexpr int BSincScaleCount{BSINC_SCALE_COUNT}; + + +template +constexpr std::enable_if_t::value,T> sqrt(T x) +{ + if(!(x >= 0 && x < std::numeric_limits::infinity())) + throw std::domain_error{"Invalid sqrt value"}; + + T cur{x}, prev{0}; + while(cur != prev) + { + prev = cur; + cur = 0.5f*(cur + x/cur); + } + return cur; +} + +template +constexpr std::enable_if_t::value,T> sin(T x) +{ + if(x >= al::MathDefs::Tau()) + { + if(!(x < 65536)) + throw std::domain_error{"Invalid sin value"}; + do { + x -= al::MathDefs::Tau(); + } while(x >= al::MathDefs::Tau()); + } + else if(x < 0) + { + if(!(x > -65536)) + throw std::domain_error{"Invalid sin value"}; + do { + x += al::MathDefs::Tau(); + } while(x < 0); + } + + T prev{x}, n{6}; + int i{4}, s{-1}; + const T xx{x*x}; + T t{xx*x}; + + T cur{prev + t*s/n}; + while(prev != cur) + { + prev = cur; + n *= i*(i+1); + i += 2; + s = -s; + t *= xx; + + cur += t*s/n; + } + return cur; +} + + +/* This is the normalized cardinal sine (sinc) function. + * + * sinc(x) = { 1, x = 0 + * { sin(pi x) / (pi x), otherwise. + */ +constexpr double Sinc(const double x) +{ + if(!(x > 1e-15 || x < -1e-15)) + return 1.0; + return sin(al::MathDefs::Pi()*x) / (al::MathDefs::Pi()*x); +} + +/* The zero-order modified Bessel function of the first kind, used for the + * Kaiser window. + * + * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k) + * = sum_{k=0}^inf ((x / 2)^k / k!)^2 + */ +constexpr double BesselI_0(const double x) +{ + /* Start at k=1 since k=0 is trivial. */ + const double x2{x / 2.0}; + double term{1.0}; + double sum{1.0}; + double last_sum{}; + int k{1}; + + /* Let the integration converge until the term of the sum is no longer + * significant. + */ + do { + const double y{x2 / k}; + ++k; + last_sum = sum; + term *= y * y; + sum += term; + } while(sum != last_sum); + + return sum; +} + +/* Calculate a Kaiser window from the given beta value and a normalized k + * [-1, 1]. + * + * w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1 + * { 0, elsewhere. + * + * Where k can be calculated as: + * + * k = i / l, where -l <= i <= l. + * + * or: + * + * k = 2 i / M - 1, where 0 <= i <= M. + */ +constexpr double Kaiser(const double beta, const double k, const double besseli_0_beta) +{ + if(!(k >= -1.0 && k <= 1.0)) + return 0.0; + return BesselI_0(beta * sqrt(1.0 - k*k)) / besseli_0_beta; +} + +/* Calculates the (normalized frequency) transition width of the Kaiser window. + * Rejection is in dB. + */ +constexpr double CalcKaiserWidth(const double rejection, const int order) +{ + if(rejection > 21.19) + return (rejection - 7.95) / (order * 2.285 * al::MathDefs::Tau()); + /* This enforces a minimum rejection of just above 21.18dB */ + return 5.79 / (order * al::MathDefs::Tau()); +} + +/* Calculates the beta value of the Kaiser window. Rejection is in dB. */ +constexpr double CalcKaiserBeta(const double rejection) +{ + if(rejection > 50.0) + return 0.1102 * (rejection-8.7); + else if(rejection >= 21.0) + return (0.5842 * std::pow(rejection-21.0, 0.4)) + (0.07886 * (rejection-21.0)); + return 0.0; +} + + +struct BSincHeader { + double width; + double beta; + double scaleBase; + double scaleRange; + double besseli_0_beta; + + int a[BSINC_SCALE_COUNT]; + int total_size; +}; + +constexpr BSincHeader GenerateBSincHeader(int Rejection, int Order) +{ + BSincHeader ret{}; + ret.width = CalcKaiserWidth(Rejection, Order); + ret.beta = CalcKaiserBeta(Rejection); + ret.scaleBase = ret.width / 2.0; + ret.scaleRange = 1.0 - ret.scaleBase; + ret.besseli_0_beta = BesselI_0(ret.beta); + + int num_points{Order+1}; + for(int si{0};si < BSincScaleCount;++si) + { + const double scale{ret.scaleBase + (ret.scaleRange * si / (BSincScaleCount - 1))}; + const int a{std::min(static_cast(num_points / 2.0 / scale), num_points)}; + const int m{2 * a}; + + ret.a[si] = a; + ret.total_size += 4 * BSincPhaseCount * ((m+3) & ~3); + } + + return ret; +} + +/* 11th and 23rd order filters (12 and 24-point respectively) with a 60dB drop + * at nyquist. Each filter will scale up the order when downsampling, to 23rd + * and 47th order respectively. + */ +constexpr BSincHeader bsinc12_hdr{GenerateBSincHeader(60, 11)}; +constexpr BSincHeader bsinc24_hdr{GenerateBSincHeader(60, 23)}; + + +/* FIXME: This should be constexpr, but the temporary filter arrays are too + * big. This requires using heap space, which is not allowed in a constexpr + * function (maybe in C++20). + */ +template +std::array GenerateBSincCoeffs(const BSincHeader &hdr) +{ + auto filter = std::make_unique(BSincScaleCount); + + /* Calculate the Kaiser-windowed Sinc filter coefficients for each scale + * and phase index. + */ + for(unsigned int si{0};si < BSincScaleCount;++si) + { + const int m{hdr.a[si] * 2}; + const int o{BSincPointsHalf - (m/2)}; + const int l{hdr.a[si] - 1}; + const int a{hdr.a[si]}; + const double scale{hdr.scaleBase + (hdr.scaleRange * si / (BSincScaleCount - 1))}; + const double cutoff{scale - (hdr.scaleBase * std::max(0.5, scale) * 2.0)}; + + /* Do one extra phase index so that the phase delta has a proper target + * for its last index. + */ + for(int pi{0};pi <= BSincPhaseCount;++pi) + { + const double phase{l + (pi/double{BSincPhaseCount})}; + + for(int i{0};i < m;++i) + { + const double x{i - phase}; + filter[si][pi][o+i] = Kaiser(hdr.beta, x/a, hdr.besseli_0_beta) * cutoff * + Sinc(cutoff*x); + } + } + } + + auto ret = std::make_unique>(); + size_t idx{0}; + + for(unsigned int si{0};si < BSincScaleCount-1;++si) + { + const int m{((hdr.a[si]*2) + 3) & ~3}; + const int o{BSincPointsHalf - (m/2)}; + + for(int pi{0};pi < BSincPhaseCount;++pi) + { + /* Write out the filter. Also calculate and write out the phase and + * scale deltas. + */ + for(int i{0};i < m;++i) + (*ret)[idx++] = static_cast(filter[si][pi][o+i]); + + /* Linear interpolation between phases is simplified by pre- + * calculating the delta (b - a) in: x = a + f (b - a) + */ + for(int i{0};i < m;++i) + { + const double phDelta{filter[si][pi+1][o+i] - filter[si][pi][o+i]}; + (*ret)[idx++] = static_cast(phDelta); + } + + /* Linear interpolation between scales is also simplified. + * + * Given a difference in points between scales, the destination + * points will be 0, thus: x = a + f (-a) + */ + for(int i{0};i < m;++i) + { + const double scDelta{filter[si+1][pi][o+i] - filter[si][pi][o+i]}; + (*ret)[idx++] = static_cast(scDelta); + } + + /* This last simplification is done to complete the bilinear + * equation for the combination of phase and scale. + */ + for(int i{0};i < m;++i) + { + const double spDelta{(filter[si+1][pi+1][o+i] - filter[si+1][pi][o+i]) - + (filter[si][pi+1][o+i] - filter[si][pi][o+i])}; + (*ret)[idx++] = static_cast(spDelta); + } + } + } + { + /* The last scale index doesn't have any scale or scale-phase deltas. */ + const unsigned int si{BSincScaleCount - 1}; + const int m{((hdr.a[si]*2) + 3) & ~3}; + const int o{BSincPointsHalf - (m/2)}; + + for(int pi{0};pi < BSincPhaseCount;++pi) + { + for(int i{0};i < m;++i) + (*ret)[idx++] = static_cast(filter[si][pi][o+i]); + for(int i{0};i < m;++i) + { + const double phDelta{filter[si][pi+1][o+i] - filter[si][pi][o+i]}; + (*ret)[idx++] = static_cast(phDelta); + } + for(int i{0};i < m;++i) + (*ret)[idx++] = 0.0f; + for(int i{0};i < m;++i) + (*ret)[idx++] = 0.0f; + } + } + assert(idx == total_size); + + return *ret; +} + +/* FIXME: These can't be constexpr due to the calls reaching the compiler's + * step limit. + */ +alignas(16) const auto bsinc12_table = GenerateBSincCoeffs(bsinc12_hdr); +alignas(16) const auto bsinc24_table = GenerateBSincCoeffs(bsinc24_hdr); + + +constexpr BSincTable GenerateBSincTable(const BSincHeader &hdr, const float *tab) +{ + BSincTable ret{}; + ret.scaleBase = static_cast(hdr.scaleBase); + ret.scaleRange = static_cast(1.0 / hdr.scaleRange); + for(int i{0};i < BSincScaleCount;++i) + ret.m[i] = static_cast(((hdr.a[i]*2) + 3) & ~3); + ret.filterOffset[0] = 0; + for(int i{1};i < BSincScaleCount;++i) + ret.filterOffset[i] = ret.filterOffset[i-1] + ret.m[i-1]*4*BSincPhaseCount; + ret.Tab = tab; + return ret; +} + +} // namespace + +const BSincTable bsinc12{GenerateBSincTable(bsinc12_hdr, &bsinc12_table.front())}; +const BSincTable bsinc24{GenerateBSincTable(bsinc24_hdr, &bsinc24_table.front())}; diff --git a/alc/bsinc_tables.h b/alc/bsinc_tables.h new file mode 100644 index 00000000..2e2443e5 --- /dev/null +++ b/alc/bsinc_tables.h @@ -0,0 +1,17 @@ +#ifndef BSINC_TABLES_H +#define BSINC_TABLES_H + +#include "bsinc_defs.h" + + +struct BSincTable { + float scaleBase, scaleRange; + unsigned int m[BSINC_SCALE_COUNT]; + unsigned int filterOffset[BSINC_SCALE_COUNT]; + const float *Tab; +}; + +extern const BSincTable bsinc12; +extern const BSincTable bsinc24; + +#endif /* BSINC_TABLES_H */ diff --git a/common/bsinc_defs.h b/common/bsinc_defs.h deleted file mode 100644 index 30d1219e..00000000 --- a/common/bsinc_defs.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef BSINC_DEFS_H -#define BSINC_DEFS_H - -/* The number of distinct scale and phase intervals within the filter table. */ -#define BSINC_SCALE_BITS 4 -#define BSINC_SCALE_COUNT (1< -#include -#include -#include -#include -#include -#include - -#include "math_defs.h" -#include "vector.h" - - -namespace { - -/* The max points includes the doubling for downsampling, so the maximum number - * of base sample points is 24, which is 23rd order. - */ -constexpr int BSincPointsMax{BSINC_POINTS_MAX}; -constexpr int BSincPointsHalf{BSincPointsMax / 2}; - -constexpr int BSincPhaseCount{BSINC_PHASE_COUNT}; -constexpr int BSincScaleCount{BSINC_SCALE_COUNT}; - - -template -constexpr std::enable_if_t::value,T> sqrt(T x) -{ - if(!(x >= 0 && x < std::numeric_limits::infinity())) - throw std::domain_error{"Invalid sqrt value"}; - - T cur{x}, prev{0}; - while(cur != prev) - { - prev = cur; - cur = 0.5f*(cur + x/cur); - } - return cur; -} - -template -constexpr std::enable_if_t::value,T> sin(T x) -{ - if(x >= al::MathDefs::Tau()) - { - if(!(x < 65536)) - throw std::domain_error{"Invalid sin value"}; - do { - x -= al::MathDefs::Tau(); - } while(x >= al::MathDefs::Tau()); - } - else if(x < 0) - { - if(!(x > -65536)) - throw std::domain_error{"Invalid sin value"}; - do { - x += al::MathDefs::Tau(); - } while(x < 0); - } - - T prev{x}, n{6}; - int i{4}, s{-1}; - const T xx{x*x}; - T t{xx*x}; - - T cur{prev + t*s/n}; - while(prev != cur) - { - prev = cur; - n *= i*(i+1); - i += 2; - s = -s; - t *= xx; - - cur += t*s/n; - } - return cur; -} - - -/* This is the normalized cardinal sine (sinc) function. - * - * sinc(x) = { 1, x = 0 - * { sin(pi x) / (pi x), otherwise. - */ -constexpr double Sinc(const double x) -{ - if(!(x > 1e-15 || x < -1e-15)) - return 1.0; - return sin(al::MathDefs::Pi()*x) / (al::MathDefs::Pi()*x); -} - -/* The zero-order modified Bessel function of the first kind, used for the - * Kaiser window. - * - * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k) - * = sum_{k=0}^inf ((x / 2)^k / k!)^2 - */ -constexpr double BesselI_0(const double x) -{ - /* Start at k=1 since k=0 is trivial. */ - const double x2{x / 2.0}; - double term{1.0}; - double sum{1.0}; - double last_sum{}; - int k{1}; - - /* Let the integration converge until the term of the sum is no longer - * significant. - */ - do { - const double y{x2 / k}; - ++k; - last_sum = sum; - term *= y * y; - sum += term; - } while(sum != last_sum); - - return sum; -} - -/* Calculate a Kaiser window from the given beta value and a normalized k - * [-1, 1]. - * - * w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1 - * { 0, elsewhere. - * - * Where k can be calculated as: - * - * k = i / l, where -l <= i <= l. - * - * or: - * - * k = 2 i / M - 1, where 0 <= i <= M. - */ -constexpr double Kaiser(const double beta, const double k, const double besseli_0_beta) -{ - if(!(k >= -1.0 && k <= 1.0)) - return 0.0; - return BesselI_0(beta * sqrt(1.0 - k*k)) / besseli_0_beta; -} - -/* Calculates the (normalized frequency) transition width of the Kaiser window. - * Rejection is in dB. - */ -constexpr double CalcKaiserWidth(const double rejection, const int order) -{ - if(rejection > 21.19) - return (rejection - 7.95) / (order * 2.285 * al::MathDefs::Tau()); - /* This enforces a minimum rejection of just above 21.18dB */ - return 5.79 / (order * al::MathDefs::Tau()); -} - -/* Calculates the beta value of the Kaiser window. Rejection is in dB. */ -constexpr double CalcKaiserBeta(const double rejection) -{ - if(rejection > 50.0) - return 0.1102 * (rejection-8.7); - else if(rejection >= 21.0) - return (0.5842 * std::pow(rejection-21.0, 0.4)) + (0.07886 * (rejection-21.0)); - return 0.0; -} - - -struct BSincHeader { - double width; - double beta; - double scaleBase; - double scaleRange; - double besseli_0_beta; - - int a[BSINC_SCALE_COUNT]; - int total_size; -}; - -constexpr BSincHeader GenerateBSincHeader(int Rejection, int Order) -{ - BSincHeader ret{}; - ret.width = CalcKaiserWidth(Rejection, Order); - ret.beta = CalcKaiserBeta(Rejection); - ret.scaleBase = ret.width / 2.0; - ret.scaleRange = 1.0 - ret.scaleBase; - ret.besseli_0_beta = BesselI_0(ret.beta); - - int num_points{Order+1}; - for(int si{0};si < BSincScaleCount;++si) - { - const double scale{ret.scaleBase + (ret.scaleRange * si / (BSincScaleCount - 1))}; - const int a{std::min(static_cast(num_points / 2.0 / scale), num_points)}; - const int m{2 * a}; - - ret.a[si] = a; - ret.total_size += 4 * BSincPhaseCount * ((m+3) & ~3); - } - - return ret; -} - -/* 11th and 23rd order filters (12 and 24-point respectively) with a 60dB drop - * at nyquist. Each filter will scale up the order when downsampling, to 23rd - * and 47th order respectively. - */ -constexpr BSincHeader bsinc12_hdr{GenerateBSincHeader(60, 11)}; -constexpr BSincHeader bsinc24_hdr{GenerateBSincHeader(60, 23)}; - - -/* FIXME: This should be constexpr, but the temporary filter arrays are too - * big. This requires using heap space, which is not allowed in a constexpr - * function (maybe in C++20). - */ -template -std::array GenerateBSincCoeffs(const BSincHeader &hdr) -{ - auto filter = std::make_unique(BSincScaleCount); - - /* Calculate the Kaiser-windowed Sinc filter coefficients for each scale - * and phase index. - */ - for(unsigned int si{0};si < BSincScaleCount;++si) - { - const int m{hdr.a[si] * 2}; - const int o{BSincPointsHalf - (m/2)}; - const int l{hdr.a[si] - 1}; - const int a{hdr.a[si]}; - const double scale{hdr.scaleBase + (hdr.scaleRange * si / (BSincScaleCount - 1))}; - const double cutoff{scale - (hdr.scaleBase * std::max(0.5, scale) * 2.0)}; - - /* Do one extra phase index so that the phase delta has a proper target - * for its last index. - */ - for(int pi{0};pi <= BSincPhaseCount;++pi) - { - const double phase{l + (pi/double{BSincPhaseCount})}; - - for(int i{0};i < m;++i) - { - const double x{i - phase}; - filter[si][pi][o+i] = Kaiser(hdr.beta, x/a, hdr.besseli_0_beta) * cutoff * - Sinc(cutoff*x); - } - } - } - - auto ret = std::make_unique>(); - size_t idx{0}; - - for(unsigned int si{0};si < BSincScaleCount-1;++si) - { - const int m{((hdr.a[si]*2) + 3) & ~3}; - const int o{BSincPointsHalf - (m/2)}; - - for(int pi{0};pi < BSincPhaseCount;++pi) - { - /* Write out the filter. Also calculate and write out the phase and - * scale deltas. - */ - for(int i{0};i < m;++i) - (*ret)[idx++] = static_cast(filter[si][pi][o+i]); - - /* Linear interpolation between phases is simplified by pre- - * calculating the delta (b - a) in: x = a + f (b - a) - */ - for(int i{0};i < m;++i) - { - const double phDelta{filter[si][pi+1][o+i] - filter[si][pi][o+i]}; - (*ret)[idx++] = static_cast(phDelta); - } - - /* Linear interpolation between scales is also simplified. - * - * Given a difference in points between scales, the destination - * points will be 0, thus: x = a + f (-a) - */ - for(int i{0};i < m;++i) - { - const double scDelta{filter[si+1][pi][o+i] - filter[si][pi][o+i]}; - (*ret)[idx++] = static_cast(scDelta); - } - - /* This last simplification is done to complete the bilinear - * equation for the combination of phase and scale. - */ - for(int i{0};i < m;++i) - { - const double spDelta{(filter[si+1][pi+1][o+i] - filter[si+1][pi][o+i]) - - (filter[si][pi+1][o+i] - filter[si][pi][o+i])}; - (*ret)[idx++] = static_cast(spDelta); - } - } - } - { - /* The last scale index doesn't have any scale or scale-phase deltas. */ - const unsigned int si{BSincScaleCount - 1}; - const int m{((hdr.a[si]*2) + 3) & ~3}; - const int o{BSincPointsHalf - (m/2)}; - - for(int pi{0};pi < BSincPhaseCount;++pi) - { - for(int i{0};i < m;++i) - (*ret)[idx++] = static_cast(filter[si][pi][o+i]); - for(int i{0};i < m;++i) - { - const double phDelta{filter[si][pi+1][o+i] - filter[si][pi][o+i]}; - (*ret)[idx++] = static_cast(phDelta); - } - for(int i{0};i < m;++i) - (*ret)[idx++] = 0.0f; - for(int i{0};i < m;++i) - (*ret)[idx++] = 0.0f; - } - } - assert(idx == total_size); - - return *ret; -} - -/* FIXME: These can't be constexpr due to the calls reaching the compiler's - * step limit. - */ -alignas(16) const auto bsinc12_table = GenerateBSincCoeffs(bsinc12_hdr); -alignas(16) const auto bsinc24_table = GenerateBSincCoeffs(bsinc24_hdr); - - -constexpr BSincTable GenerateBSincTable(const BSincHeader &hdr, const float *tab) -{ - BSincTable ret{}; - ret.scaleBase = static_cast(hdr.scaleBase); - ret.scaleRange = static_cast(1.0 / hdr.scaleRange); - for(int i{0};i < BSincScaleCount;++i) - ret.m[i] = static_cast(((hdr.a[i]*2) + 3) & ~3); - ret.filterOffset[0] = 0; - for(int i{1};i < BSincScaleCount;++i) - ret.filterOffset[i] = ret.filterOffset[i-1] + ret.m[i-1]*4*BSincPhaseCount; - ret.Tab = tab; - return ret; -} - -} // namespace - -const BSincTable bsinc12{GenerateBSincTable(bsinc12_hdr, &bsinc12_table.front())}; -const BSincTable bsinc24{GenerateBSincTable(bsinc24_hdr, &bsinc24_table.front())}; diff --git a/common/bsinc_tables.h b/common/bsinc_tables.h deleted file mode 100644 index 2e2443e5..00000000 --- a/common/bsinc_tables.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef BSINC_TABLES_H -#define BSINC_TABLES_H - -#include "bsinc_defs.h" - - -struct BSincTable { - float scaleBase, scaleRange; - unsigned int m[BSINC_SCALE_COUNT]; - unsigned int filterOffset[BSINC_SCALE_COUNT]; - const float *Tab; -}; - -extern const BSincTable bsinc12; -extern const BSincTable bsinc24; - -#endif /* BSINC_TABLES_H */ -- cgit v1.2.3 From 463591663c2421b3436edc446d173380d6a6e106 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 19 May 2020 08:13:13 -0700 Subject: Check that aligned_alloc is available with cmake Some compilers support C++17 even on targets that lack required functions. Projects that want to force C++17 will then run into a problem with std::aligned_alloc not existing on those targets, so it needs to be explicitly checked for. The alternative is to simply never use it even when it would be available. --- CMakeLists.txt | 13 ++++++++++--- common/almalloc.cpp | 4 ++-- config.h.in | 3 +++ 3 files changed, 15 insertions(+), 5 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 208a275f..64c8116e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -449,9 +449,16 @@ IF(HAVE_INTRIN_H) }" HAVE_BITSCANFORWARD_INTRINSIC) ENDIF() -CHECK_SYMBOL_EXISTS(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN) -CHECK_SYMBOL_EXISTS(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC) -CHECK_SYMBOL_EXISTS(proc_pidpath libproc.h HAVE_PROC_PIDPATH) +check_cxx_source_compiles("#include +int main() +{ + void *ptr{std::aligned_alloc(alignof(int), sizeof(int))}; + std::free(ptr); + return 0; +}" HAVE_STD_ALIGNED_ALLOC) +check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN) +check_symbol_exists(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC) +check_symbol_exists(proc_pidpath libproc.h HAVE_PROC_PIDPATH) IF(NOT WIN32) # We need pthreads outside of Windows, for semaphores. It's also used to diff --git a/common/almalloc.cpp b/common/almalloc.cpp index 5b679b75..806cb4ab 100644 --- a/common/almalloc.cpp +++ b/common/almalloc.cpp @@ -17,7 +17,7 @@ void *al_malloc(size_t alignment, size_t size) assert((alignment & (alignment-1)) == 0); alignment = std::max(alignment, alignof(std::max_align_t)); -#if __cplusplus >= 201703L +#if defined(HAVE_STD_ALIGNED_ALLOC) size = (size+(alignment-1))&~(alignment-1); return std::aligned_alloc(alignment, size); #elif defined(HAVE_POSIX_MEMALIGN) @@ -48,7 +48,7 @@ void *al_calloc(size_t alignment, size_t size) void al_free(void *ptr) noexcept { -#if (__cplusplus >= 201703L) || defined(HAVE_POSIX_MEMALIGN) +#if defined(HAVE_STD_ALIGNED_ALLOC) || defined(HAVE_POSIX_MEMALIGN) std::free(ptr); #elif defined(HAVE__ALIGNED_MALLOC) _aligned_free(ptr); diff --git a/config.h.in b/config.h.in index 29a10b20..75aacc0d 100644 --- a/config.h.in +++ b/config.h.in @@ -8,6 +8,9 @@ /* Define if HRTF data is embedded in the library */ #cmakedefine ALSOFT_EMBED_HRTF_DATA +/* Define if we have the std::aligned_alloc function */ +#cmakedefine HAVE_STD_ALIGNED_ALLOC + /* Define if we have the posix_memalign function */ #cmakedefine HAVE_POSIX_MEMALIGN -- cgit v1.2.3 From a512eae7bb3554723b7b290985404e7c480b9bf1 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 19 May 2020 10:21:19 -0700 Subject: Move BUFFERSIZE and FloatBufferLine to a separate header --- CMakeLists.txt | 1 + alc/alcmain.h | 10 +--------- alc/bufferline.h | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 alc/bufferline.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 64c8116e..4793afe5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -606,6 +606,7 @@ SET(ALC_OBJS alc/bsinc_defs.h alc/bsinc_tables.cpp alc/bsinc_tables.h + alc/bufferline.h alc/compat.h alc/converter.cpp alc/converter.h diff --git a/alc/alcmain.h b/alc/alcmain.h index 9c539c44..3a14e68f 100644 --- a/alc/alcmain.h +++ b/alc/alcmain.h @@ -23,6 +23,7 @@ #include "alspan.h" #include "ambidefs.h" #include "atomic.h" +#include "bufferline.h" #include "devformat.h" #include "filters/splitter.h" #include "hrtf.h" @@ -157,15 +158,6 @@ struct BFChannelConfig { ALuint Index; }; -/* Size for temporary storage of buffer data, in floats. Larger values need - * more memory, while smaller values may need more iterations. The value needs - * to be a sensible size, however, as it constrains the max stepping value used - * for mixing, as well as the maximum number of samples per mixing iteration. - */ -#define BUFFERSIZE 1024 - -using FloatBufferLine = std::array; - /* Maximum number of samples to pad on the ends of a buffer for resampling. * Note that the padding is symmetric (half at the beginning and half at the * end)! diff --git a/alc/bufferline.h b/alc/bufferline.h new file mode 100644 index 00000000..79f0996c --- /dev/null +++ b/alc/bufferline.h @@ -0,0 +1,15 @@ +#ifndef ALC_BUFFERLINE_H +#define ALC_BUFFERLINE_H + +#include + +/* Size for temporary storage of buffer data, in floats. Larger values need + * more memory, while smaller values may need more iterations. The value needs + * to be a sensible size, however, as it constrains the max stepping value used + * for mixing, as well as the maximum number of samples per mixing iteration. + */ +#define BUFFERSIZE 1024 + +using FloatBufferLine = std::array; + +#endif /* ALC_BUFFERLINE_H */ -- cgit v1.2.3 From 9322c86e2f464da6889385c389dee7374f1bc30e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 7 Jun 2020 14:15:48 -0700 Subject: Avoid explicit checks for _BitScanForward[64] --- CMakeLists.txt | 14 -------------- common/alnumeric.h | 19 ++++++++++--------- config.h.in | 6 ------ 3 files changed, 10 insertions(+), 29 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 4793afe5..4d0bb217 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -433,20 +433,6 @@ IF(HAVE_INTRIN_H) __cpuid(regs, 0); return regs[0]; }" HAVE_CPUID_INTRINSIC) - CHECK_C_SOURCE_COMPILES("#include - int main() - { - unsigned long idx = 0; - _BitScanForward64(&idx, 1); - return idx; - }" HAVE_BITSCANFORWARD64_INTRINSIC) - CHECK_C_SOURCE_COMPILES("#include - int main() - { - unsigned long idx = 0; - _BitScanForward(&idx, 1); - return idx; - }" HAVE_BITSCANFORWARD_INTRINSIC) ENDIF() check_cxx_source_compiles("#include diff --git a/common/alnumeric.h b/common/alnumeric.h index 9158b764..df05b966 100644 --- a/common/alnumeric.h +++ b/common/alnumeric.h @@ -132,7 +132,7 @@ inline int fallback_popcnt64(uint64_t v) } #define POPCNT64 fallback_popcnt64 -#if defined(HAVE_BITSCANFORWARD64_INTRINSIC) +#if defined(_WIN64) inline int msvc64_ctz32(uint32_t v) { @@ -149,7 +149,7 @@ inline int msvc64_ctz64(uint64_t v) } #define CTZ64 msvc64_ctz64 -#elif defined(HAVE_BITSCANFORWARD_INTRINSIC) +#elif defined(_WIN32) inline int msvc_ctz32(uint32_t v) { @@ -225,8 +225,9 @@ inline int float2int(float f) noexcept #if defined(HAVE_SSE_INTRINSICS) return _mm_cvtt_ss2si(_mm_set_ss(f)); -#elif ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \ - !defined(__SSE_MATH__)) || (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP == 0) +#elif (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP == 0) \ + || ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) \ + && !defined(__SSE_MATH__)) int sign, shift, mant; union { float f; @@ -260,9 +261,9 @@ inline int double2int(double d) noexcept #if defined(HAVE_SSE_INTRINSICS) return _mm_cvttsd_si32(_mm_set_sd(d)); -#elif ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \ - !defined(__SSE2_MATH__)) || (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP < 2) - +#elif (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP < 2) \ + || ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) \ + && !defined(__SSE2_MATH__)) int sign, shift; int64_t mant; union { @@ -296,8 +297,8 @@ inline int double2int(double d) noexcept */ inline float fast_roundf(float f) noexcept { -#if (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \ - !defined(__SSE_MATH__) +#if (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) \ + && !defined(__SSE_MATH__) float out; __asm__ __volatile__("frndint" : "=t"(out) : "0"(f)); diff --git a/config.h.in b/config.h.in index 75aacc0d..2de3640e 100644 --- a/config.h.in +++ b/config.h.in @@ -107,12 +107,6 @@ /* Define if we have the __cpuid() intrinsic */ #cmakedefine HAVE_CPUID_INTRINSIC -/* Define if we have the _BitScanForward64() intrinsic */ -#cmakedefine HAVE_BITSCANFORWARD64_INTRINSIC - -/* Define if we have the _BitScanForward() intrinsic */ -#cmakedefine HAVE_BITSCANFORWARD_INTRINSIC - /* Define if we have SSE intrinsics */ #cmakedefine HAVE_SSE_INTRINSICS -- cgit v1.2.3 From 15437e314098af6aa220b661ae0397deb2f6b67b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 10 Jun 2020 21:03:04 -0700 Subject: Don't export functions when static linking --- CMakeLists.txt | 50 ++++++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 22 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d0bb217..83463e30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -281,34 +281,40 @@ ELSE() ENDIF() # Set visibility/export options if available -IF(WIN32) - SET(EXPORT_DECL "__declspec(dllexport)") -ELSE() - SET(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") +if(WIN32) + if(NOT LIBTYPE STREQUAL "STATIC") + set(EXPORT_DECL "__declspec(dllexport)") + endif() +else() + set(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") # Yes GCC, really don't accept visibility modes you don't support - SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror") + set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror") - CHECK_C_SOURCE_COMPILES("int foo() __attribute__((visibility(\"protected\"))); + check_c_source_compiles("int foo() __attribute__((visibility(\"protected\"))); int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY) - IF(HAVE_GCC_PROTECTED_VISIBILITY) - SET(EXPORT_DECL "__attribute__((visibility(\"protected\")))") - ELSE() - CHECK_C_SOURCE_COMPILES("int foo() __attribute__((visibility(\"default\"))); + if(HAVE_GCC_PROTECTED_VISIBILITY) + if(NOT LIBTYPE STREQUAL "STATIC") + set(EXPORT_DECL "__attribute__((visibility(\"protected\")))") + endif() + else() + check_c_source_compiles("int foo() __attribute__((visibility(\"default\"))); int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY) - IF(HAVE_GCC_DEFAULT_VISIBILITY) - SET(EXPORT_DECL "__attribute__((visibility(\"default\")))") - ENDIF() - ENDIF() + if(HAVE_GCC_DEFAULT_VISIBILITY) + if(NOT LIBTYPE STREQUAL "STATIC") + set(EXPORT_DECL "__attribute__((visibility(\"default\")))") + endif() + endif() + endif() - IF(HAVE_GCC_PROTECTED_VISIBILITY OR HAVE_GCC_DEFAULT_VISIBILITY) - CHECK_C_COMPILER_FLAG(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN_SWITCH) - IF(HAVE_VISIBILITY_HIDDEN_SWITCH) - SET(C_FLAGS ${C_FLAGS} -fvisibility=hidden) - ENDIF() - ENDIF() + if(HAVE_GCC_PROTECTED_VISIBILITY OR HAVE_GCC_DEFAULT_VISIBILITY) + check_c_compiler_flag(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN_SWITCH) + if(HAVE_VISIBILITY_HIDDEN_SWITCH) + set(C_FLAGS ${C_FLAGS} -fvisibility=hidden) + endif() + endif() - SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}") -ENDIF() + set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}") +endif() set(SSE2_SWITCH "") -- cgit v1.2.3 From df1bdc7bc4a5cb9b2a0107e164366f845724588c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 11 Jun 2020 07:46:59 -0700 Subject: Fix some capitalization --- CMakeLists.txt | 434 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 217 insertions(+), 217 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 83463e30..44a261f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,50 +1,50 @@ # CMake build file list for OpenAL -CMAKE_MINIMUM_REQUIRED(VERSION 3.0.2) - -PROJECT(OpenAL) - -IF(COMMAND CMAKE_POLICY) - CMAKE_POLICY(SET CMP0003 NEW) - CMAKE_POLICY(SET CMP0005 NEW) - IF(POLICY CMP0020) - CMAKE_POLICY(SET CMP0020 NEW) - ENDIF(POLICY CMP0020) - IF(POLICY CMP0042) - CMAKE_POLICY(SET CMP0042 NEW) - ENDIF(POLICY CMP0042) - IF(POLICY CMP0054) - CMAKE_POLICY(SET CMP0054 NEW) - ENDIF(POLICY CMP0054) - IF(POLICY CMP0075) - CMAKE_POLICY(SET CMP0075 NEW) - ENDIF(POLICY CMP0075) -ENDIF(COMMAND CMAKE_POLICY) - -IF(NOT CMAKE_BUILD_TYPE) - SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING +cmake_minimum_required(VERSION 3.0.2) + +project(OpenAL) + +if(COMMAND CMAKE_POLICY) + cmake_policy(SET CMP0003 NEW) + cmake_policy(SET CMP0005 NEW) + if(POLICY CMP0020) + cmake_policy(SET CMP0020 NEW) + endif(POLICY CMP0020) + if(POLICY CMP0042) + cmake_policy(SET CMP0042 NEW) + endif(POLICY CMP0042) + if(POLICY CMP0054) + cmake_policy(SET CMP0054 NEW) + endif(POLICY CMP0054) + if(POLICY CMP0075) + cmake_policy(SET CMP0075 NEW) + endif(POLICY CMP0075) +endif(COMMAND CMAKE_POLICY) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) -ENDIF() -IF(NOT CMAKE_DEBUG_POSTFIX) - SET(CMAKE_DEBUG_POSTFIX "" CACHE STRING +endif() +if(NOT CMAKE_DEBUG_POSTFIX) + set(CMAKE_DEBUG_POSTFIX "" CACHE STRING "Library postfix for debug builds. Normally left blank." FORCE) -ENDIF() +endif() -SET(CMAKE_MODULE_PATH "${OpenAL_SOURCE_DIR}/cmake") +set(CMAKE_MODULE_PATH "${OpenAL_SOURCE_DIR}/cmake") -INCLUDE(CheckFunctionExists) -INCLUDE(CheckLibraryExists) -INCLUDE(CheckIncludeFile) -INCLUDE(CheckIncludeFiles) -INCLUDE(CheckSymbolExists) -INCLUDE(CheckCCompilerFlag) -INCLUDE(CheckCXXCompilerFlag) -INCLUDE(CheckCSourceCompiles) -INCLUDE(CheckCXXSourceCompiles) -INCLUDE(CheckTypeSize) +include(CheckFunctionExists) +include(CheckLibraryExists) +include(CheckIncludeFile) +include(CheckIncludeFiles) +include(CheckSymbolExists) +include(CheckCCompilerFlag) +include(CheckCXXCompilerFlag) +include(CheckCSourceCompiles) +include(CheckCXXSourceCompiles) +include(CheckTypeSize) include(CheckStructHasMember) include(GNUInstallDirs) @@ -85,155 +85,155 @@ if(DEFINED ALSOFT_AMBDEC_PRESETS) endif() -SET(CPP_DEFS ) # C pre-processor, not C++ -SET(INC_PATHS ) -SET(C_FLAGS ) -SET(LINKER_FLAGS ) -SET(EXTRA_LIBS ) +set(CPP_DEFS ) # C pre-processor, not C++ +set(INC_PATHS ) +set(C_FLAGS ) +set(LINKER_FLAGS ) +set(EXTRA_LIBS ) -IF(WIN32) - SET(CPP_DEFS ${CPP_DEFS} _WIN32) +if(WIN32) + set(CPP_DEFS ${CPP_DEFS} _WIN32) - OPTION(ALSOFT_BUILD_ROUTER "Build the router (EXPERIMENTAL; creates OpenAL32.dll and soft_oal.dll)" OFF) + option(ALSOFT_BUILD_ROUTER "Build the router (EXPERIMENTAL; creates OpenAL32.dll and soft_oal.dll)" OFF) - IF(MINGW) - OPTION(ALSOFT_BUILD_IMPORT_LIB "Build an import .lib using dlltool (requires sed)" ON) - IF(NOT DLLTOOL) - IF(HOST) - SET(DLLTOOL "${HOST}-dlltool") - ELSE() - SET(DLLTOOL "dlltool") - ENDIF() - ENDIF() - ENDIF() -ENDIF() + if(MINGW) + option(ALSOFT_BUILD_IMPORT_LIB "Build an import .lib using dlltool (requires sed)" ON) + if(NOT DLLTOOL) + if(HOST) + set(DLLTOOL "${HOST}-dlltool") + else() + set(DLLTOOL "dlltool") + endif() + endif() + endif() +endif() # QNX's gcc do not uses /usr/include and /usr/lib pathes by default -IF ("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX") - SET(INC_PATHS ${INC_PATHS} /usr/include) - SET(LINKER_FLAGS ${LINKER_FLAGS} -L/usr/lib) -ENDIF() +if("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX") + set(INC_PATHS ${INC_PATHS} /usr/include) + set(LINKER_FLAGS ${LINKER_FLAGS} -L/usr/lib) +endif() -IF(NOT LIBTYPE) - SET(LIBTYPE SHARED) -ENDIF() +if(NOT LIBTYPE) + set(LIBTYPE SHARED) +endif() -SET(LIB_MAJOR_VERSION "1") -SET(LIB_MINOR_VERSION "20") -SET(LIB_REVISION "1") -SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}") -SET(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0) +set(LIB_MAJOR_VERSION "1") +set(LIB_MINOR_VERSION "20") +set(LIB_REVISION "1") +set(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}") +set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0) -SET(EXPORT_DECL "") +set(EXPORT_DECL "") -CHECK_TYPE_SIZE("long" SIZEOF_LONG) +check_type_size("long" SIZEOF_LONG) # Require C++14 -SET(CMAKE_CXX_STANDARD 14) -SET(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) # Prefer C11, but support C99 and C90 too. -SET(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD 11) if(NOT WIN32) # Check if _POSIX_C_SOURCE and _XOPEN_SOURCE needs to be set for POSIX functions - CHECK_SYMBOL_EXISTS(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_DEFAULT) - IF(NOT HAVE_POSIX_MEMALIGN_DEFAULT) - SET(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) - SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600") - CHECK_SYMBOL_EXISTS(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_POSIX) - IF(NOT HAVE_POSIX_MEMALIGN_POSIX) - SET(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS}) - ELSE() - SET(CPP_DEFS ${CPP_DEFS} _POSIX_C_SOURCE=200112L _XOPEN_SOURCE=600) - ENDIF() - ENDIF() - UNSET(OLD_REQUIRED_FLAGS) -ENDIF() + check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_DEFAULT) + if(NOT HAVE_POSIX_MEMALIGN_DEFAULT) + set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600") + check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_POSIX) + if(NOT HAVE_POSIX_MEMALIGN_POSIX) + set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS}) + else() + set(CPP_DEFS ${CPP_DEFS} _POSIX_C_SOURCE=200112L _XOPEN_SOURCE=600) + endif() + endif() + unset(OLD_REQUIRED_FLAGS) +endif() # C99 has restrict, but C++ does not, so we can only utilize __restrict. -SET(RESTRICT_DECL ) -CHECK_CXX_SOURCE_COMPILES("int *__restrict foo; +set(RESTRICT_DECL ) +check_cxx_source_compiles("int *__restrict foo; int main() { return 0; }" HAVE___RESTRICT) -IF(HAVE___RESTRICT) - SET(RESTRICT_DECL "__restrict") -ENDIF() +if(HAVE___RESTRICT) + set(RESTRICT_DECL "__restrict") +endif() # Some systems may need libatomic for atomic functions to work -SET(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) -SET(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES} atomic) -CHECK_CXX_SOURCE_COMPILES("#include +set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) +set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES} atomic) +check_cxx_source_compiles("#include std::atomic foo{0}; int main() { return foo.fetch_add(2); }" HAVE_LIBATOMIC) -IF(NOT HAVE_LIBATOMIC) - SET(CMAKE_REQUIRED_LIBRARIES "${OLD_REQUIRED_LIBRARIES}") -ELSE() - SET(EXTRA_LIBS atomic ${EXTRA_LIBS}) -ENDIF() -UNSET(OLD_REQUIRED_LIBRARIES) +if(NOT HAVE_LIBATOMIC) + set(CMAKE_REQUIRED_LIBRARIES "${OLD_REQUIRED_LIBRARIES}") +else() + set(EXTRA_LIBS atomic ${EXTRA_LIBS}) +endif() +unset(OLD_REQUIRED_LIBRARIES) # Include liblog for Android logging -CHECK_LIBRARY_EXISTS(log __android_log_print "" HAVE_LIBLOG) -IF(HAVE_LIBLOG) - SET(EXTRA_LIBS log ${EXTRA_LIBS}) - SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} log) -ENDIF() +check_library_exists(log __android_log_print "" HAVE_LIBLOG) +if(HAVE_LIBLOG) + set(EXTRA_LIBS log ${EXTRA_LIBS}) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} log) +endif() -IF(MSVC) - SET(CPP_DEFS ${CPP_DEFS} _CRT_SECURE_NO_WARNINGS NOMINMAX) - CHECK_CXX_COMPILER_FLAG(/permissive- HAVE_PERMISSIVE_SWITCH) - IF(HAVE_PERMISSIVE_SWITCH) - SET(C_FLAGS ${C_FLAGS} $<$:/permissive->) - ENDIF() - SET(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4268 /wd4324 /wd5030) +if(MSVC) + set(CPP_DEFS ${CPP_DEFS} _CRT_SECURE_NO_WARNINGS NOMINMAX) + check_cxx_compiler_flag(/permissive- HAVE_PERMISSIVE_SWITCH) + if(HAVE_PERMISSIVE_SWITCH) + set(C_FLAGS ${C_FLAGS} $<$:/permissive->) + endif() + set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4268 /wd4324 /wd5030) - IF(NOT DXSDK_DIR) - STRING(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}") - ELSE() - STRING(REGEX REPLACE "\\\\" "/" DXSDK_DIR "${DXSDK_DIR}") - ENDIF() - IF(DXSDK_DIR) - MESSAGE(STATUS "Using DirectX SDK directory: ${DXSDK_DIR}") - ENDIF() + if(NOT DXSDK_DIR) + string(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}") + else() + string(REGEX REPLACE "\\\\" "/" DXSDK_DIR "${DXSDK_DIR}") + endif() + if(DXSDK_DIR) + message(STATUS "Using DirectX SDK directory: ${DXSDK_DIR}") + endif() - OPTION(FORCE_STATIC_VCRT "Force /MT for static VC runtimes" OFF) - IF(FORCE_STATIC_VCRT) - FOREACH(flag_var + option(FORCE_STATIC_VCRT "Force /MT for static VC runtimes" OFF) + if(FORCE_STATIC_VCRT) + foreach(flag_var CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) - IF(${flag_var} MATCHES "/MD") - STRING(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") - ENDIF() - ENDFOREACH(flag_var) - ENDIF() -ELSE() - SET(C_FLAGS ${C_FLAGS} -Winline -Wunused -Wall -Wextra -Wshadow -Wconversion -Wcast-align + if(${flag_var} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + endif() + endforeach(flag_var) + endif() +else() + set(C_FLAGS ${C_FLAGS} -Winline -Wunused -Wall -Wextra -Wshadow -Wconversion -Wcast-align -Wpedantic $<$:-Wold-style-cast -Wnon-virtual-dtor -Woverloaded-virtual>) - IF(ALSOFT_WERROR) - SET(C_FLAGS ${C_FLAGS} -Werror) - ENDIF() + if(ALSOFT_WERROR) + set(C_FLAGS ${C_FLAGS} -Werror) + endif() # We want RelWithDebInfo to actually include debug stuff (define _DEBUG # instead of NDEBUG) - FOREACH(flag_var CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO) - IF(${flag_var} MATCHES "-DNDEBUG") - STRING(REGEX REPLACE "-DNDEBUG" "-D_DEBUG" ${flag_var} "${${flag_var}}") - ENDIF() - ENDFOREACH() + foreach(flag_var CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if(${flag_var} MATCHES "-DNDEBUG") + string(REGEX REPLACE "-DNDEBUG" "-D_DEBUG" ${flag_var} "${${flag_var}}") + endif() + endforeach() - CHECK_C_COMPILER_FLAG(-fno-math-errno HAVE_FNO_MATH_ERRNO) - IF(HAVE_FNO_MATH_ERRNO) - SET(C_FLAGS ${C_FLAGS} -fno-math-errno) - ENDIF() + check_c_compiler_flag(-fno-math-errno HAVE_FNO_MATH_ERRNO) + if(HAVE_FNO_MATH_ERRNO) + set(C_FLAGS ${C_FLAGS} -fno-math-errno) + endif() option(ALSOFT_STATIC_LIBGCC "Force -static-libgcc for static GCC runtimes" OFF) if(ALSOFT_STATIC_LIBGCC) @@ -278,7 +278,7 @@ ELSE() set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lwinpthread,--pop-state") endif() endif() -ENDIF() +endif() # Set visibility/export options if available if(WIN32) @@ -375,71 +375,71 @@ if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND (SSE2_SWITCH OR MSVC)) endif() endif() -IF(HAVE_EMMINTRIN_H) - SET(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) - FOREACH(flag_var ${SSE_FLAGS}) - SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${flag_var}") - ENDFOREACH() +if(HAVE_EMMINTRIN_H) + set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + foreach(flag_var ${SSE_FLAGS}) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${flag_var}") + endforeach() - CHECK_C_SOURCE_COMPILES("#include + check_c_source_compiles("#include int main() {_mm_pause(); return 0;}" HAVE_SSE_INTRINSICS) - SET(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS}) -ENDIF() + set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS}) +endif() -CHECK_INCLUDE_FILE(malloc.h HAVE_MALLOC_H) -CHECK_INCLUDE_FILE(cpuid.h HAVE_CPUID_H) -CHECK_INCLUDE_FILE(intrin.h HAVE_INTRIN_H) -CHECK_INCLUDE_FILE(guiddef.h HAVE_GUIDDEF_H) -IF(NOT HAVE_GUIDDEF_H) - CHECK_INCLUDE_FILE(initguid.h HAVE_INITGUID_H) -ENDIF() +check_include_file(malloc.h HAVE_MALLOC_H) +check_include_file(cpuid.h HAVE_CPUID_H) +check_include_file(intrin.h HAVE_INTRIN_H) +check_include_file(guiddef.h HAVE_GUIDDEF_H) +if(NOT HAVE_GUIDDEF_H) + check_include_file(initguid.h HAVE_INITGUID_H) +endif() # Some systems need libm for some math functions to work -SET(MATH_LIB ) -CHECK_LIBRARY_EXISTS(m pow "" HAVE_LIBM) -IF(HAVE_LIBM) - SET(MATH_LIB ${MATH_LIB} m) - SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} m) -ENDIF() +set(MATH_LIB ) +check_library_exists(m pow "" HAVE_LIBM) +if(HAVE_LIBM) + set(MATH_LIB ${MATH_LIB} m) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} m) +endif() # Some systems need to link with -lrt for clock_gettime as used by the common # eaxmple functions. -SET(RT_LIB ) -CHECK_LIBRARY_EXISTS(rt clock_gettime "" HAVE_LIBRT) -IF(HAVE_LIBRT) - SET(RT_LIB rt) -ENDIF() +set(RT_LIB ) +check_library_exists(rt clock_gettime "" HAVE_LIBRT) +if(HAVE_LIBRT) + set(RT_LIB rt) +endif() # Check for the dlopen API (for dynamicly loading backend libs) -IF(ALSOFT_DLOPEN) - CHECK_INCLUDE_FILE(dlfcn.h HAVE_DLFCN_H) - CHECK_LIBRARY_EXISTS(dl dlopen "" HAVE_LIBDL) - IF(HAVE_LIBDL) - SET(EXTRA_LIBS dl ${EXTRA_LIBS}) - SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} dl) - ENDIF() -ENDIF() +if(ALSOFT_DLOPEN) + check_include_file(dlfcn.h HAVE_DLFCN_H) + check_library_exists(dl dlopen "" HAVE_LIBDL) + if(HAVE_LIBDL) + set(EXTRA_LIBS dl ${EXTRA_LIBS}) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} dl) + endif() +endif() # Check for a cpuid intrinsic -IF(HAVE_CPUID_H) - CHECK_C_SOURCE_COMPILES("#include +if(HAVE_CPUID_H) + check_c_source_compiles("#include int main() { unsigned int eax, ebx, ecx, edx; return __get_cpuid(0, &eax, &ebx, &ecx, &edx); }" HAVE_GCC_GET_CPUID) -ENDIF() -IF(HAVE_INTRIN_H) - CHECK_C_SOURCE_COMPILES("#include +endif() +if(HAVE_INTRIN_H) + check_c_source_compiles("#include int main() { int regs[4]; __cpuid(regs, 0); return regs[0]; }" HAVE_CPUID_INTRINSIC) -ENDIF() +endif() check_cxx_source_compiles("#include int main() @@ -452,31 +452,31 @@ check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN) check_symbol_exists(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC) check_symbol_exists(proc_pidpath libproc.h HAVE_PROC_PIDPATH) -IF(NOT WIN32) +if(NOT WIN32) # We need pthreads outside of Windows, for semaphores. It's also used to # set the priority and name of threads, when possible. - CHECK_INCLUDE_FILE(pthread.h HAVE_PTHREAD_H) - IF(NOT HAVE_PTHREAD_H) - MESSAGE(FATAL_ERROR "PThreads is required for non-Windows builds!") - ENDIF() + check_include_file(pthread.h HAVE_PTHREAD_H) + if(NOT HAVE_PTHREAD_H) + message(FATAL_ERROR "PThreads is required for non-Windows builds!") + endif() - CHECK_C_COMPILER_FLAG(-pthread HAVE_PTHREAD) - IF(HAVE_PTHREAD) - SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -pthread") - SET(C_FLAGS ${C_FLAGS} -pthread) - SET(LINKER_FLAGS ${LINKER_FLAGS} -pthread) - ENDIF() + check_c_compiler_flag(-pthread HAVE_PTHREAD) + if(HAVE_PTHREAD) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -pthread") + set(C_FLAGS ${C_FLAGS} -pthread) + set(LINKER_FLAGS ${LINKER_FLAGS} -pthread) + endif() - CHECK_SYMBOL_EXISTS(pthread_setschedparam pthread.h HAVE_PTHREAD_SETSCHEDPARAM) + check_symbol_exists(pthread_setschedparam pthread.h HAVE_PTHREAD_SETSCHEDPARAM) # Some systems need pthread_np.h to get pthread_setname_np - CHECK_INCLUDE_FILES("pthread.h;pthread_np.h" HAVE_PTHREAD_NP_H) - IF(HAVE_PTHREAD_NP_H) - CHECK_SYMBOL_EXISTS(pthread_setname_np "pthread.h;pthread_np.h" HAVE_PTHREAD_SETNAME_NP) - IF(NOT HAVE_PTHREAD_SETNAME_NP) - CHECK_SYMBOL_EXISTS(pthread_set_name_np "pthread.h;pthread_np.h" HAVE_PTHREAD_SET_NAME_NP) - ELSE() - CHECK_C_SOURCE_COMPILES(" + check_include_files("pthread.h;pthread_np.h" HAVE_PTHREAD_NP_H) + if(HAVE_PTHREAD_NP_H) + check_symbol_exists(pthread_setname_np "pthread.h;pthread_np.h" HAVE_PTHREAD_SETNAME_NP) + if(NOT HAVE_PTHREAD_SETNAME_NP) + check_symbol_exists(pthread_set_name_np "pthread.h;pthread_np.h" HAVE_PTHREAD_SET_NAME_NP) + else() + check_c_source_compiles(" #include #include int main() @@ -486,7 +486,7 @@ int main() }" PTHREAD_SETNAME_NP_ONE_PARAM ) - CHECK_C_SOURCE_COMPILES(" + check_c_source_compiles(" #include #include int main() @@ -496,13 +496,13 @@ int main() }" PTHREAD_SETNAME_NP_THREE_PARAMS ) - ENDIF() - ELSE() - CHECK_SYMBOL_EXISTS(pthread_setname_np pthread.h HAVE_PTHREAD_SETNAME_NP) - IF(NOT HAVE_PTHREAD_SETNAME_NP) - CHECK_SYMBOL_EXISTS(pthread_set_name_np pthread.h HAVE_PTHREAD_SET_NAME_NP) - ELSE() - CHECK_C_SOURCE_COMPILES(" + endif() + else() + check_symbol_exists(pthread_setname_np pthread.h HAVE_PTHREAD_SETNAME_NP) + if(NOT HAVE_PTHREAD_SETNAME_NP) + check_symbol_exists(pthread_set_name_np pthread.h HAVE_PTHREAD_SET_NAME_NP) + else() + check_c_source_compiles(" #include int main() { @@ -511,7 +511,7 @@ int main() }" PTHREAD_SETNAME_NP_ONE_PARAM ) - CHECK_C_SOURCE_COMPILES(" + check_c_source_compiles(" #include int main() { @@ -520,16 +520,16 @@ int main() }" PTHREAD_SETNAME_NP_THREE_PARAMS ) - ENDIF() - ENDIF() -ENDIF() + endif() + endif() +endif() -CHECK_SYMBOL_EXISTS(getopt unistd.h HAVE_GETOPT) +check_symbol_exists(getopt unistd.h HAVE_GETOPT) # Common sources used by both the OpenAL implementation library and potentially # the OpenAL router. -SET(COMMON_OBJS +set(COMMON_OBJS common/albyte.h common/alcomplex.cpp common/alcomplex.h -- cgit v1.2.3 From ad9fc31bfdda7f295083e44f78a5c98cca0c32d2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 11 Jun 2020 08:42:30 -0700 Subject: More capitalization fixes --- CMakeLists.txt | 902 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 451 insertions(+), 451 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 44a261f9..83eee139 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -561,7 +561,7 @@ set(COMMON_OBJS common/vecmat.h common/vector.h ) -SET(OPENAL_OBJS +set(OPENAL_OBJS al/auxeffectslot.cpp al/auxeffectslot.h al/buffer.cpp @@ -580,7 +580,7 @@ SET(OPENAL_OBJS al/source.h al/state.cpp ) -SET(ALC_OBJS +set(ALC_OBJS alc/alc.cpp alc/alcmain.h alc/alu.cpp @@ -649,88 +649,88 @@ SET(ALC_OBJS ) -SET(CPU_EXTS "Default") -SET(HAVE_SSE 0) -SET(HAVE_SSE2 0) -SET(HAVE_SSE3 0) -SET(HAVE_SSE4_1 0) -SET(HAVE_NEON 0) +set(CPU_EXTS "Default") +set(HAVE_SSE 0) +set(HAVE_SSE2 0) +set(HAVE_SSE3 0) +set(HAVE_SSE4_1 0) +set(HAVE_NEON 0) # Check for SSE+SSE2 support -OPTION(ALSOFT_REQUIRE_SSE "Require SSE support" OFF) -OPTION(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF) -IF(HAVE_XMMINTRIN_H AND HAVE_EMMINTRIN_H) - OPTION(ALSOFT_CPUEXT_SSE "Enable SSE support" ON) - OPTION(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON) - IF(ALSOFT_CPUEXT_SSE AND ALSOFT_CPUEXT_SSE2) - SET(HAVE_SSE 1) - SET(HAVE_SSE2 1) - SET(ALC_OBJS ${ALC_OBJS} alc/mixer/mixer_sse.cpp alc/mixer/mixer_sse2.cpp) - IF(SSE2_SWITCH) - SET_SOURCE_FILES_PROPERTIES(alc/mixer/mixer_sse.cpp alc/mixer/mixer_sse2.cpp +option(ALSOFT_REQUIRE_SSE "Require SSE support" OFF) +option(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF) +if(HAVE_XMMINTRIN_H AND HAVE_EMMINTRIN_H) + option(ALSOFT_CPUEXT_SSE "Enable SSE support" ON) + option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON) + if(ALSOFT_CPUEXT_SSE AND ALSOFT_CPUEXT_SSE2) + set(HAVE_SSE 1) + set(HAVE_SSE2 1) + set(ALC_OBJS ${ALC_OBJS} alc/mixer/mixer_sse.cpp alc/mixer/mixer_sse2.cpp) + if(SSE2_SWITCH) + set_source_files_properties(alc/mixer/mixer_sse.cpp alc/mixer/mixer_sse2.cpp PROPERTIES COMPILE_FLAGS "${SSE2_SWITCH}") - ENDIF() - SET(CPU_EXTS "${CPU_EXTS}, SSE, SSE2") - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE) - MESSAGE(FATAL_ERROR "Failed to enabled required SSE CPU extensions") -ENDIF() -IF(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2) - MESSAGE(FATAL_ERROR "Failed to enable required SSE2 CPU extensions") -ENDIF() - -OPTION(ALSOFT_REQUIRE_SSE3 "Require SSE3 support" OFF) -IF(HAVE_EMMINTRIN_H) - OPTION(ALSOFT_CPUEXT_SSE3 "Enable SSE3 support" ON) - IF(HAVE_SSE2 AND ALSOFT_CPUEXT_SSE3) - SET(HAVE_SSE3 1) - SET(ALC_OBJS ${ALC_OBJS} alc/mixer/mixer_sse3.cpp) - IF(SSE2_SWITCH) - SET_SOURCE_FILES_PROPERTIES(alc/mixer/mixer_sse3.cpp PROPERTIES - COMPILE_FLAGS "${SSE3_SWITCH}") - ENDIF() - SET(CPU_EXTS "${CPU_EXTS}, SSE3") - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_SSE3 AND NOT HAVE_SSE3) - MESSAGE(FATAL_ERROR "Failed to enable required SSE3 CPU extensions") -ENDIF() - -OPTION(ALSOFT_REQUIRE_SSE4_1 "Require SSE4.1 support" OFF) -IF(HAVE_SMMINTRIN_H) - OPTION(ALSOFT_CPUEXT_SSE4_1 "Enable SSE4.1 support" ON) - IF(HAVE_SSE3 AND ALSOFT_CPUEXT_SSE4_1) - SET(HAVE_SSE4_1 1) - SET(ALC_OBJS ${ALC_OBJS} alc/mixer/mixer_sse41.cpp) - IF(SSE4_1_SWITCH) - SET_SOURCE_FILES_PROPERTIES(alc/mixer/mixer_sse41.cpp PROPERTIES - COMPILE_FLAGS "${SSE4_1_SWITCH}") - ENDIF() - SET(CPU_EXTS "${CPU_EXTS}, SSE4.1") - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_SSE4_1 AND NOT HAVE_SSE4_1) - MESSAGE(FATAL_ERROR "Failed to enable required SSE4.1 CPU extensions") -ENDIF() + endif() + set(CPU_EXTS "${CPU_EXTS}, SSE, SSE2") + endif() +endif() +if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE) + message(FATAL_ERROR "Failed to enabled required SSE CPU extensions") +endif() +if(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2) + message(FATAL_ERROR "Failed to enable required SSE2 CPU extensions") +endif() + +option(ALSOFT_REQUIRE_SSE3 "Require SSE3 support" OFF) +if(HAVE_EMMINTRIN_H) + option(ALSOFT_CPUEXT_SSE3 "Enable SSE3 support" ON) + if(HAVE_SSE2 AND ALSOFT_CPUEXT_SSE3) + set(HAVE_SSE3 1) + set(ALC_OBJS ${ALC_OBJS} alc/mixer/mixer_sse3.cpp) + if(SSE2_SWITCH) + set_source_files_properties(alc/mixer/mixer_sse3.cpp PROPERTIES + COMPILE_FLAGS "${SSE3_SWITCH}") + endif() + set(CPU_EXTS "${CPU_EXTS}, SSE3") + endif() +endif() +if(ALSOFT_REQUIRE_SSE3 AND NOT HAVE_SSE3) + message(FATAL_ERROR "Failed to enable required SSE3 CPU extensions") +endif() + +option(ALSOFT_REQUIRE_SSE4_1 "Require SSE4.1 support" OFF) +if(HAVE_SMMINTRIN_H) + option(ALSOFT_CPUEXT_SSE4_1 "Enable SSE4.1 support" ON) + if(HAVE_SSE3 AND ALSOFT_CPUEXT_SSE4_1) + set(HAVE_SSE4_1 1) + set(ALC_OBJS ${ALC_OBJS} alc/mixer/mixer_sse41.cpp) + if(SSE4_1_SWITCH) + set_source_files_properties(alc/mixer/mixer_sse41.cpp PROPERTIES + COMPILE_FLAGS "${SSE4_1_SWITCH}") + endif() + set(CPU_EXTS "${CPU_EXTS}, SSE4.1") + endif() +endif() +if(ALSOFT_REQUIRE_SSE4_1 AND NOT HAVE_SSE4_1) + message(FATAL_ERROR "Failed to enable required SSE4.1 CPU extensions") +endif() # Check for ARM Neon support -OPTION(ALSOFT_REQUIRE_NEON "Require ARM Neon support" OFF) -IF(HAVE_ARM_NEON_H) - OPTION(ALSOFT_CPUEXT_NEON "Enable ARM Neon support" ON) - IF(ALSOFT_CPUEXT_NEON) - SET(HAVE_NEON 1) - SET(ALC_OBJS ${ALC_OBJS} alc/mixer/mixer_neon.cpp) - IF(FPU_NEON_SWITCH) - SET_SOURCE_FILES_PROPERTIES(alc/mixer/mixer_neon.cpp PROPERTIES - COMPILE_FLAGS "${FPU_NEON_SWITCH}") - ENDIF() - SET(CPU_EXTS "${CPU_EXTS}, Neon") - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON) - MESSAGE(FATAL_ERROR "Failed to enabled required ARM Neon CPU extensions") -ENDIF() +option(ALSOFT_REQUIRE_NEON "Require ARM Neon support" OFF) +if(HAVE_ARM_NEON_H) + option(ALSOFT_CPUEXT_NEON "Enable ARM Neon support" ON) + if(ALSOFT_CPUEXT_NEON) + set(HAVE_NEON 1) + set(ALC_OBJS ${ALC_OBJS} alc/mixer/mixer_neon.cpp) + if(FPU_NEON_SWITCH) + set_source_files_properties(alc/mixer/mixer_neon.cpp PROPERTIES + COMPILE_FLAGS "${FPU_NEON_SWITCH}") + endif() + set(CPU_EXTS "${CPU_EXTS}, Neon") + endif() +endif() +if(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON) + message(FATAL_ERROR "Failed to enabled required ARM Neon CPU extensions") +endif() set(HAVE_ALSA 0) @@ -771,86 +771,86 @@ set(ALC_OBJS ${ALC_OBJS} ) # Check ALSA backend -OPTION(ALSOFT_REQUIRE_ALSA "Require ALSA backend" OFF) -FIND_PACKAGE(ALSA) -IF(ALSA_FOUND) - OPTION(ALSOFT_BACKEND_ALSA "Enable ALSA backend" ON) - IF(ALSOFT_BACKEND_ALSA) - SET(HAVE_ALSA 1) - SET(BACKENDS "${BACKENDS} ALSA${IS_LINKED},") - SET(ALC_OBJS ${ALC_OBJS} alc/backends/alsa.cpp alc/backends/alsa.h) - ADD_BACKEND_LIBS(${ALSA_LIBRARIES}) - SET(INC_PATHS ${INC_PATHS} ${ALSA_INCLUDE_DIRS}) - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA) - MESSAGE(FATAL_ERROR "Failed to enabled required ALSA backend") -ENDIF() +option(ALSOFT_REQUIRE_ALSA "Require ALSA backend" OFF) +find_package(ALSA) +if(ALSA_FOUND) + option(ALSOFT_BACKEND_ALSA "Enable ALSA backend" ON) + if(ALSOFT_BACKEND_ALSA) + set(HAVE_ALSA 1) + set(BACKENDS "${BACKENDS} ALSA${IS_LINKED},") + set(ALC_OBJS ${ALC_OBJS} alc/backends/alsa.cpp alc/backends/alsa.h) + add_backend_libs(${ALSA_LIBRARIES}) + set(INC_PATHS ${INC_PATHS} ${ALSA_INCLUDE_DIRS}) + endif() +endif() +if(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA) + message(FATAL_ERROR "Failed to enabled required ALSA backend") +endif() # Check OSS backend -OPTION(ALSOFT_REQUIRE_OSS "Require OSS backend" OFF) -FIND_PACKAGE(OSS) -IF(OSS_FOUND) - OPTION(ALSOFT_BACKEND_OSS "Enable OSS backend" ON) - IF(ALSOFT_BACKEND_OSS) - SET(HAVE_OSS 1) - SET(BACKENDS "${BACKENDS} OSS,") - SET(ALC_OBJS ${ALC_OBJS} alc/backends/oss.cpp alc/backends/oss.h) - IF(OSS_LIBRARIES) - SET(EXTRA_LIBS ${OSS_LIBRARIES} ${EXTRA_LIBS}) - ENDIF() - SET(INC_PATHS ${INC_PATHS} ${OSS_INCLUDE_DIRS}) - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS) - MESSAGE(FATAL_ERROR "Failed to enabled required OSS backend") -ENDIF() +option(ALSOFT_REQUIRE_OSS "Require OSS backend" OFF) +find_package(OSS) +if(OSS_FOUND) + option(ALSOFT_BACKEND_OSS "Enable OSS backend" ON) + if(ALSOFT_BACKEND_OSS) + set(HAVE_OSS 1) + set(BACKENDS "${BACKENDS} OSS,") + set(ALC_OBJS ${ALC_OBJS} alc/backends/oss.cpp alc/backends/oss.h) + if(OSS_LIBRARIES) + set(EXTRA_LIBS ${OSS_LIBRARIES} ${EXTRA_LIBS}) + endif() + set(INC_PATHS ${INC_PATHS} ${OSS_INCLUDE_DIRS}) + endif() +endif() +if(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS) + message(FATAL_ERROR "Failed to enabled required OSS backend") +endif() # Check Solaris backend -OPTION(ALSOFT_REQUIRE_SOLARIS "Require Solaris backend" OFF) -FIND_PACKAGE(AudioIO) -IF(AUDIOIO_FOUND) - OPTION(ALSOFT_BACKEND_SOLARIS "Enable Solaris backend" ON) - IF(ALSOFT_BACKEND_SOLARIS) - SET(HAVE_SOLARIS 1) - SET(BACKENDS "${BACKENDS} Solaris,") - SET(ALC_OBJS ${ALC_OBJS} alc/backends/solaris.cpp alc/backends/solaris.h) - SET(INC_PATHS ${INC_PATHS} ${AUDIOIO_INCLUDE_DIRS}) - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS) - MESSAGE(FATAL_ERROR "Failed to enabled required Solaris backend") -ENDIF() +option(ALSOFT_REQUIRE_SOLARIS "Require Solaris backend" OFF) +find_package(AudioIO) +if(AUDIOIO_FOUND) + option(ALSOFT_BACKEND_SOLARIS "Enable Solaris backend" ON) + if(ALSOFT_BACKEND_SOLARIS) + set(HAVE_SOLARIS 1) + set(BACKENDS "${BACKENDS} Solaris,") + set(ALC_OBJS ${ALC_OBJS} alc/backends/solaris.cpp alc/backends/solaris.h) + set(INC_PATHS ${INC_PATHS} ${AUDIOIO_INCLUDE_DIRS}) + endif() +endif() +if(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS) + message(FATAL_ERROR "Failed to enabled required Solaris backend") +endif() # Check SndIO backend -OPTION(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF) -FIND_PACKAGE(SoundIO) -IF(SOUNDIO_FOUND) - OPTION(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON) - IF(ALSOFT_BACKEND_SNDIO) - SET(HAVE_SNDIO 1) - SET(BACKENDS "${BACKENDS} SndIO (linked),") - SET(ALC_OBJS ${ALC_OBJS} alc/backends/sndio.cpp alc/backends/sndio.h) - SET(EXTRA_LIBS ${SOUNDIO_LIBRARIES} ${EXTRA_LIBS}) - SET(INC_PATHS ${INC_PATHS} ${SOUNDIO_INCLUDE_DIRS}) - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO) - MESSAGE(FATAL_ERROR "Failed to enabled required SndIO backend") -ENDIF() +option(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF) +find_package(SoundIO) +if(SOUNDIO_FOUND) + option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON) + if(ALSOFT_BACKEND_SNDIO) + set(HAVE_SNDIO 1) + set(BACKENDS "${BACKENDS} SndIO (linked),") + set(ALC_OBJS ${ALC_OBJS} alc/backends/sndio.cpp alc/backends/sndio.h) + set(EXTRA_LIBS ${SOUNDIO_LIBRARIES} ${EXTRA_LIBS}) + set(INC_PATHS ${INC_PATHS} ${SOUNDIO_INCLUDE_DIRS}) + endif() +endif() +if(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO) + message(FATAL_ERROR "Failed to enabled required SndIO backend") +endif() # Check Windows-only backends -OPTION(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF) -OPTION(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF) -OPTION(ALSOFT_REQUIRE_WASAPI "Require WASAPI backend" OFF) -IF(WIN32) - SET(WINSDK_LIB_DIRS ) - SET(WINSDK_INCLUDE_DIRS ) - FIND_PACKAGE(WindowsSDK) - IF(WINDOWSSDK_FOUND) +option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF) +option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF) +option(ALSOFT_REQUIRE_WASAPI "Require WASAPI backend" OFF) +if(WIN32) + set(WINSDK_LIB_DIRS ) + set(WINSDK_INCLUDE_DIRS ) + find_package(WindowsSDK) + if(WINDOWSSDK_FOUND) get_windowssdk_library_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_LIB_DIRS) get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_INCLUDE_DIRS) - ENDIF() + endif() # Check MMSystem backend check_include_files("windows.h;mmsystem.h" HAVE_MMSYSTEM_H) @@ -867,133 +867,133 @@ IF(WIN32) endif() # Check DSound backend - FIND_PACKAGE(DSound) - IF(DSOUND_FOUND) - OPTION(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON) - IF(ALSOFT_BACKEND_DSOUND) - SET(HAVE_DSOUND 1) - SET(BACKENDS "${BACKENDS} DirectSound${IS_LINKED},") - SET(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h) - ADD_BACKEND_LIBS(${DSOUND_LIBRARIES}) - SET(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIRS}) - ENDIF() - ENDIF() + find_package(DSound) + if(DSOUND_FOUND) + option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON) + if(ALSOFT_BACKEND_DSOUND) + set(HAVE_DSOUND 1) + set(BACKENDS "${BACKENDS} DirectSound${IS_LINKED},") + set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h) + add_backend_libs(${DSOUND_LIBRARIES}) + set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIRS}) + endif() + endif() # Check for WASAPI backend - CHECK_INCLUDE_FILE(mmdeviceapi.h HAVE_MMDEVICEAPI_H) - IF(HAVE_MMDEVICEAPI_H) - OPTION(ALSOFT_BACKEND_WASAPI "Enable WASAPI backend" ON) - IF(ALSOFT_BACKEND_WASAPI) - SET(HAVE_WASAPI 1) - SET(BACKENDS "${BACKENDS} WASAPI,") - SET(ALC_OBJS ${ALC_OBJS} alc/backends/wasapi.cpp alc/backends/wasapi.h) - ENDIF() - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM) - MESSAGE(FATAL_ERROR "Failed to enabled required WinMM backend") -ENDIF() -IF(ALSOFT_REQUIRE_DSOUND AND NOT HAVE_DSOUND) - MESSAGE(FATAL_ERROR "Failed to enabled required DSound backend") -ENDIF() -IF(ALSOFT_REQUIRE_WASAPI AND NOT HAVE_WASAPI) - MESSAGE(FATAL_ERROR "Failed to enabled required WASAPI backend") -ENDIF() + check_include_file(mmdeviceapi.h HAVE_MMDEVICEAPI_H) + if(HAVE_MMDEVICEAPI_H) + option(ALSOFT_BACKEND_WASAPI "Enable WASAPI backend" ON) + if(ALSOFT_BACKEND_WASAPI) + set(HAVE_WASAPI 1) + set(BACKENDS "${BACKENDS} WASAPI,") + set(ALC_OBJS ${ALC_OBJS} alc/backends/wasapi.cpp alc/backends/wasapi.h) + endif() + endif() +endif() +if(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM) + message(FATAL_ERROR "Failed to enabled required WinMM backend") +endif() +if(ALSOFT_REQUIRE_DSOUND AND NOT HAVE_DSOUND) + message(FATAL_ERROR "Failed to enabled required DSound backend") +endif() +if(ALSOFT_REQUIRE_WASAPI AND NOT HAVE_WASAPI) + message(FATAL_ERROR "Failed to enabled required WASAPI backend") +endif() # Check PortAudio backend -OPTION(ALSOFT_REQUIRE_PORTAUDIO "Require PortAudio backend" OFF) -FIND_PACKAGE(PortAudio) -IF(PORTAUDIO_FOUND) - OPTION(ALSOFT_BACKEND_PORTAUDIO "Enable PortAudio backend" ON) - IF(ALSOFT_BACKEND_PORTAUDIO) - SET(HAVE_PORTAUDIO 1) - SET(BACKENDS "${BACKENDS} PortAudio${IS_LINKED},") - SET(ALC_OBJS ${ALC_OBJS} alc/backends/portaudio.cpp alc/backends/portaudio.h) - ADD_BACKEND_LIBS(${PORTAUDIO_LIBRARIES}) - SET(INC_PATHS ${INC_PATHS} ${PORTAUDIO_INCLUDE_DIRS}) - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO) - MESSAGE(FATAL_ERROR "Failed to enabled required PortAudio backend") -ENDIF() +option(ALSOFT_REQUIRE_PORTAUDIO "Require PortAudio backend" OFF) +find_package(PortAudio) +if(PORTAUDIO_FOUND) + option(ALSOFT_BACKEND_PORTAUDIO "Enable PortAudio backend" ON) + if(ALSOFT_BACKEND_PORTAUDIO) + set(HAVE_PORTAUDIO 1) + set(BACKENDS "${BACKENDS} PortAudio${IS_LINKED},") + set(ALC_OBJS ${ALC_OBJS} alc/backends/portaudio.cpp alc/backends/portaudio.h) + add_backend_libs(${PORTAUDIO_LIBRARIES}) + set(INC_PATHS ${INC_PATHS} ${PORTAUDIO_INCLUDE_DIRS}) + endif() +endif() +if(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO) + message(FATAL_ERROR "Failed to enabled required PortAudio backend") +endif() # Check PulseAudio backend -OPTION(ALSOFT_REQUIRE_PULSEAUDIO "Require PulseAudio backend" OFF) -FIND_PACKAGE(PulseAudio) -IF(PULSEAUDIO_FOUND) - OPTION(ALSOFT_BACKEND_PULSEAUDIO "Enable PulseAudio backend" ON) - IF(ALSOFT_BACKEND_PULSEAUDIO) - SET(HAVE_PULSEAUDIO 1) - SET(BACKENDS "${BACKENDS} PulseAudio${IS_LINKED},") - SET(ALC_OBJS ${ALC_OBJS} alc/backends/pulseaudio.cpp alc/backends/pulseaudio.h) - ADD_BACKEND_LIBS(${PULSEAUDIO_LIBRARIES}) - SET(INC_PATHS ${INC_PATHS} ${PULSEAUDIO_INCLUDE_DIRS}) - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO) - MESSAGE(FATAL_ERROR "Failed to enabled required PulseAudio backend") -ENDIF() +option(ALSOFT_REQUIRE_PULSEAUDIO "Require PulseAudio backend" OFF) +find_package(PulseAudio) +if(PULSEAUDIO_FOUND) + option(ALSOFT_BACKEND_PULSEAUDIO "Enable PulseAudio backend" ON) + if(ALSOFT_BACKEND_PULSEAUDIO) + set(HAVE_PULSEAUDIO 1) + set(BACKENDS "${BACKENDS} PulseAudio${IS_LINKED},") + set(ALC_OBJS ${ALC_OBJS} alc/backends/pulseaudio.cpp alc/backends/pulseaudio.h) + add_backend_libs(${PULSEAUDIO_LIBRARIES}) + set(INC_PATHS ${INC_PATHS} ${PULSEAUDIO_INCLUDE_DIRS}) + endif() +endif() +if(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO) + message(FATAL_ERROR "Failed to enabled required PulseAudio backend") +endif() # Check JACK backend -OPTION(ALSOFT_REQUIRE_JACK "Require JACK backend" OFF) -FIND_PACKAGE(JACK) -IF(JACK_FOUND) - OPTION(ALSOFT_BACKEND_JACK "Enable JACK backend" ON) - IF(ALSOFT_BACKEND_JACK) - SET(HAVE_JACK 1) - SET(BACKENDS "${BACKENDS} JACK${IS_LINKED},") - SET(ALC_OBJS ${ALC_OBJS} alc/backends/jack.cpp alc/backends/jack.h) - ADD_BACKEND_LIBS(${JACK_LIBRARIES}) - SET(INC_PATHS ${INC_PATHS} ${JACK_INCLUDE_DIRS}) - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_JACK AND NOT HAVE_JACK) - MESSAGE(FATAL_ERROR "Failed to enabled required JACK backend") -ENDIF() +option(ALSOFT_REQUIRE_JACK "Require JACK backend" OFF) +find_package(JACK) +if(JACK_FOUND) + option(ALSOFT_BACKEND_JACK "Enable JACK backend" ON) + if(ALSOFT_BACKEND_JACK) + set(HAVE_JACK 1) + set(BACKENDS "${BACKENDS} JACK${IS_LINKED},") + set(ALC_OBJS ${ALC_OBJS} alc/backends/jack.cpp alc/backends/jack.h) + add_backend_libs(${JACK_LIBRARIES}) + set(INC_PATHS ${INC_PATHS} ${JACK_INCLUDE_DIRS}) + endif() +endif() +if(ALSOFT_REQUIRE_JACK AND NOT HAVE_JACK) + message(FATAL_ERROR "Failed to enabled required JACK backend") +endif() # Check CoreAudio backend -OPTION(ALSOFT_REQUIRE_COREAUDIO "Require CoreAudio backend" OFF) -FIND_LIBRARY(COREAUDIO_FRAMEWORK NAMES CoreAudio) -FIND_PATH(AUDIOUNIT_INCLUDE_DIR NAMES AudioUnit/AudioUnit.h) -IF(COREAUDIO_FRAMEWORK AND AUDIOUNIT_INCLUDE_DIR) - OPTION(ALSOFT_BACKEND_COREAUDIO "Enable CoreAudio backend" ON) - IF(ALSOFT_BACKEND_COREAUDIO) - SET(HAVE_COREAUDIO 1) - SET(ALC_OBJS ${ALC_OBJS} alc/backends/coreaudio.cpp alc/backends/coreaudio.h) - SET(BACKENDS "${BACKENDS} CoreAudio,") - SET(EXTRA_LIBS ${COREAUDIO_FRAMEWORK} /System/Library/Frameworks/AudioUnit.framework +option(ALSOFT_REQUIRE_COREAUDIO "Require CoreAudio backend" OFF) +find_library(COREAUDIO_FRAMEWORK NAMES CoreAudio) +find_path(AUDIOUNIT_INCLUDE_DIR NAMES AudioUnit/AudioUnit.h) +if(COREAUDIO_FRAMEWORK AND AUDIOUNIT_INCLUDE_DIR) + option(ALSOFT_BACKEND_COREAUDIO "Enable CoreAudio backend" ON) + if(ALSOFT_BACKEND_COREAUDIO) + set(HAVE_COREAUDIO 1) + set(ALC_OBJS ${ALC_OBJS} alc/backends/coreaudio.cpp alc/backends/coreaudio.h) + set(BACKENDS "${BACKENDS} CoreAudio,") + set(EXTRA_LIBS ${COREAUDIO_FRAMEWORK} /System/Library/Frameworks/AudioUnit.framework /System/Library/Frameworks/ApplicationServices.framework ${EXTRA_LIBS}) # Some versions of OSX may need the AudioToolbox framework. Add it if # it's found. - FIND_LIBRARY(AUDIOTOOLBOX_LIBRARY NAMES AudioToolbox) - IF(AUDIOTOOLBOX_LIBRARY) - SET(EXTRA_LIBS ${AUDIOTOOLBOX_LIBRARY} ${EXTRA_LIBS}) - ENDIF() - - SET(INC_PATHS ${INC_PATHS} ${AUDIOUNIT_INCLUDE_DIR}) - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO) - MESSAGE(FATAL_ERROR "Failed to enabled required CoreAudio backend") -ENDIF() + find_library(AUDIOTOOLBOX_LIBRARY NAMES AudioToolbox) + if(AUDIOTOOLBOX_LIBRARY) + set(EXTRA_LIBS ${AUDIOTOOLBOX_LIBRARY} ${EXTRA_LIBS}) + endif() + + set(INC_PATHS ${INC_PATHS} ${AUDIOUNIT_INCLUDE_DIR}) + endif() +endif() +if(ALSOFT_REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO) + message(FATAL_ERROR "Failed to enabled required CoreAudio backend") +endif() # Check for OpenSL (Android) backend -OPTION(ALSOFT_REQUIRE_OPENSL "Require OpenSL backend" OFF) -FIND_PACKAGE(OpenSL) -IF(OPENSL_FOUND) - OPTION(ALSOFT_BACKEND_OPENSL "Enable OpenSL backend" ON) - IF(ALSOFT_BACKEND_OPENSL) - SET(HAVE_OPENSL 1) - SET(ALC_OBJS ${ALC_OBJS} alc/backends/opensl.cpp alc/backends/opensl.h) - SET(BACKENDS "${BACKENDS} OpenSL,") - SET(EXTRA_LIBS ${OPENSL_LIBRARIES} ${EXTRA_LIBS}) - SET(INC_PATHS ${INC_PATHS} ${OPENSL_INCLUDE_DIRS}) - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL) - MESSAGE(FATAL_ERROR "Failed to enabled required OpenSL backend") -ENDIF() +option(ALSOFT_REQUIRE_OPENSL "Require OpenSL backend" OFF) +find_package(OpenSL) +if(OPENSL_FOUND) + option(ALSOFT_BACKEND_OPENSL "Enable OpenSL backend" ON) + if(ALSOFT_BACKEND_OPENSL) + set(HAVE_OPENSL 1) + set(ALC_OBJS ${ALC_OBJS} alc/backends/opensl.cpp alc/backends/opensl.h) + set(BACKENDS "${BACKENDS} OpenSL,") + set(EXTRA_LIBS ${OPENSL_LIBRARIES} ${EXTRA_LIBS}) + set(INC_PATHS ${INC_PATHS} ${OPENSL_INCLUDE_DIRS}) + endif() +endif() +if(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL) + message(FATAL_ERROR "Failed to enabled required OpenSL backend") +endif() # Check for Oboe (Android) backend set(OBOE_TARGET ) @@ -1035,135 +1035,135 @@ if(ALSOFT_REQUIRE_OBOE AND NOT HAVE_OBOE) endif() # Check for SDL2 backend -OPTION(ALSOFT_REQUIRE_SDL2 "Require SDL2 backend" OFF) -FIND_PACKAGE(SDL2) -IF(SDL2_FOUND) +option(ALSOFT_REQUIRE_SDL2 "Require SDL2 backend" OFF) +find_package(SDL2) +if(SDL2_FOUND) # Off by default, since it adds a runtime dependency - OPTION(ALSOFT_BACKEND_SDL2 "Enable SDL2 backend" OFF) - IF(ALSOFT_BACKEND_SDL2) - SET(HAVE_SDL2 1) - SET(ALC_OBJS ${ALC_OBJS} alc/backends/sdl2.cpp alc/backends/sdl2.h) - SET(BACKENDS "${BACKENDS} SDL2,") - SET(EXTRA_LIBS ${SDL2_LIBRARY} ${EXTRA_LIBS}) - SET(INC_PATHS ${INC_PATHS} ${SDL2_INCLUDE_DIR}) - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND) - MESSAGE(FATAL_ERROR "Failed to enabled required SDL2 backend") -ENDIF() + option(ALSOFT_BACKEND_SDL2 "Enable SDL2 backend" OFF) + if(ALSOFT_BACKEND_SDL2) + set(HAVE_SDL2 1) + set(ALC_OBJS ${ALC_OBJS} alc/backends/sdl2.cpp alc/backends/sdl2.h) + set(BACKENDS "${BACKENDS} SDL2,") + set(EXTRA_LIBS ${SDL2_LIBRARY} ${EXTRA_LIBS}) + set(INC_PATHS ${INC_PATHS} ${SDL2_INCLUDE_DIR}) + endif() +endif() +if(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND) + message(FATAL_ERROR "Failed to enabled required SDL2 backend") +endif() # Optionally enable the Wave Writer backend -OPTION(ALSOFT_BACKEND_WAVE "Enable Wave Writer backend" ON) -IF(ALSOFT_BACKEND_WAVE) - SET(HAVE_WAVE 1) - SET(ALC_OBJS ${ALC_OBJS} alc/backends/wave.cpp alc/backends/wave.h) - SET(BACKENDS "${BACKENDS} WaveFile,") -ENDIF() +option(ALSOFT_BACKEND_WAVE "Enable Wave Writer backend" ON) +if(ALSOFT_BACKEND_WAVE) + set(HAVE_WAVE 1) + set(ALC_OBJS ${ALC_OBJS} alc/backends/wave.cpp alc/backends/wave.h) + set(BACKENDS "${BACKENDS} WaveFile,") +endif() # This is always available -SET(BACKENDS "${BACKENDS} Null") +set(BACKENDS "${BACKENDS} Null") -FIND_PACKAGE(Git) -IF(ALSOFT_UPDATE_BUILD_VERSION AND GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.git") +find_package(Git) +if(ALSOFT_UPDATE_BUILD_VERSION AND GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.git") # Get the current working branch and its latest abbreviated commit hash - ADD_CUSTOM_TARGET(build_version + add_custom_target(build_version ${CMAKE_COMMAND} -D GIT_EXECUTABLE=${GIT_EXECUTABLE} -D LIB_VERSION=${LIB_VERSION} -D LIB_VERSION_NUM=${LIB_VERSION_NUM} -D SRC=${OpenAL_SOURCE_DIR}/version.h.in -D DST=${OpenAL_BINARY_DIR}/version.h -P ${OpenAL_SOURCE_DIR}/version.cmake WORKING_DIRECTORY "${OpenAL_SOURCE_DIR}" VERBATIM ) -ELSE() - SET(GIT_BRANCH "UNKNOWN") - SET(GIT_COMMIT_HASH "unknown") - CONFIGURE_FILE( +else() + set(GIT_BRANCH "UNKNOWN") + set(GIT_COMMIT_HASH "unknown") + configure_file( "${OpenAL_SOURCE_DIR}/version.h.in" "${OpenAL_BINARY_DIR}/version.h") -ENDIF() +endif() option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" ON) if(ALSOFT_EMBED_HRTF_DATA) - MACRO(make_hrtf_header FILENAME VARNAME) - SET(infile "${OpenAL_SOURCE_DIR}/hrtf/${FILENAME}") - SET(outfile "${OpenAL_BINARY_DIR}/${VARNAME}.h") + macro(make_hrtf_header FILENAME VARNAME) + set(infile "${OpenAL_SOURCE_DIR}/hrtf/${FILENAME}") + set(outfile "${OpenAL_BINARY_DIR}/${VARNAME}.h") - ADD_CUSTOM_COMMAND(OUTPUT "${outfile}" + add_custom_command(OUTPUT "${outfile}" COMMAND ${CMAKE_COMMAND} -D "INPUT_FILE=${infile}" -D "OUTPUT_FILE=${outfile}" -D "VARIABLE_NAME=${VARNAME}" -P "${CMAKE_MODULE_PATH}/bin2h.script.cmake" WORKING_DIRECTORY "${OpenAL_SOURCE_DIR}" DEPENDS "${infile}" "${CMAKE_MODULE_PATH}/bin2h.script.cmake" VERBATIM ) - SET(ALC_OBJS ${ALC_OBJS} "${outfile}") - ENDMACRO() + set(ALC_OBJS ${ALC_OBJS} "${outfile}") + endmacro() make_hrtf_header("Default HRTF.mhr" "hrtf_default") endif() -IF(ALSOFT_UTILS AND NOT ALSOFT_NO_CONFIG_UTIL) +if(ALSOFT_UTILS AND NOT ALSOFT_NO_CONFIG_UTIL) find_package(Qt5Widgets) -ENDIF() -IF(ALSOFT_EXAMPLES) - FIND_PACKAGE(SndFile) - FIND_PACKAGE(SDL2) - IF(SDL2_FOUND) - FIND_PACKAGE(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE) - ENDIF() -ENDIF() - -IF(NOT WIN32) - SET(LIBNAME "openal") -ELSE() - SET(LIBNAME "OpenAL32") -ENDIF() +endif() +if(ALSOFT_EXAMPLES) + find_package(SndFile) + find_package(SDL2) + if(SDL2_FOUND) + find_package(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE) + endif() +endif() + +if(NOT WIN32) + set(LIBNAME "openal") +else() + set(LIBNAME "OpenAL32") +endif() # Needed for openal.pc.in -SET(prefix ${CMAKE_INSTALL_PREFIX}) -SET(exec_prefix "\${prefix}") -SET(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") -SET(bindir "\${exec_prefix}/${CMAKE_INSTALL_BINDIR}") -SET(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") -SET(PACKAGE_VERSION "${LIB_VERSION}") -SET(PKG_CONFIG_CFLAGS ) -SET(PKG_CONFIG_PRIVATE_LIBS ) -IF(LIBTYPE STREQUAL "STATIC") - SET(PKG_CONFIG_CFLAGS -DAL_LIBTYPE_STATIC) - FOREACH(FLAG ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) +set(prefix ${CMAKE_INSTALL_PREFIX}) +set(exec_prefix "\${prefix}") +set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") +set(bindir "\${exec_prefix}/${CMAKE_INSTALL_BINDIR}") +set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") +set(PACKAGE_VERSION "${LIB_VERSION}") +set(PKG_CONFIG_CFLAGS ) +set(PKG_CONFIG_PRIVATE_LIBS ) +if(LIBTYPE STREQUAL "STATIC") + set(PKG_CONFIG_CFLAGS -DAL_LIBTYPE_STATIC) + foreach(FLAG ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) # If this is already a linker flag, or is a full path+file, add it # as-is. Otherwise, it's a name intended to be dressed as -lname. - IF(FLAG MATCHES "^-.*" OR EXISTS "${FLAG}") - SET(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} ${FLAG}") - ELSE() - SET(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -l${FLAG}") - ENDIF() - ENDFOREACH() -ENDIF() + if(FLAG MATCHES "^-.*" OR EXISTS "${FLAG}") + set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} ${FLAG}") + else() + set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -l${FLAG}") + endif() + endforeach() +endif() # End configuration -CONFIGURE_FILE( +configure_file( "${OpenAL_SOURCE_DIR}/config.h.in" "${OpenAL_BINARY_DIR}/config.h") -CONFIGURE_FILE( +configure_file( "${OpenAL_SOURCE_DIR}/openal.pc.in" "${OpenAL_BINARY_DIR}/openal.pc" @ONLY) -ADD_LIBRARY(common STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS}) -TARGET_INCLUDE_DIRECTORIES(common PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include) -TARGET_COMPILE_DEFINITIONS(common PRIVATE ${CPP_DEFS}) -TARGET_COMPILE_OPTIONS(common PRIVATE ${C_FLAGS}) -SET_TARGET_PROPERTIES(common PROPERTIES POSITION_INDEPENDENT_CODE TRUE) +add_library(common STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS}) +target_include_directories(common PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include) +target_compile_definitions(common PRIVATE ${CPP_DEFS}) +target_compile_options(common PRIVATE ${C_FLAGS}) +set_target_properties(common PROPERTIES POSITION_INDEPENDENT_CODE TRUE) -UNSET(HAS_ROUTER) -SET(IMPL_TARGET OpenAL) # Either OpenAL or soft_oal. +unset(HAS_ROUTER) +set(IMPL_TARGET OpenAL) # Either OpenAL or soft_oal. # Build main library -IF(LIBTYPE STREQUAL "STATIC") +if(LIBTYPE STREQUAL "STATIC") add_library(${IMPL_TARGET} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS}) target_compile_definitions(${IMPL_TARGET} PUBLIC AL_LIBTYPE_STATIC) target_link_libraries(${IMPL_TARGET} PRIVATE ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) @@ -1176,21 +1176,21 @@ IF(LIBTYPE STREQUAL "STATIC") target_compile_definitions(${IMPL_TARGET} PRIVATE AL_NO_UID_DEFS) endif() endif() -ELSE() - SET(RC_CONFIG resources/openal32.rc) - IF(WIN32 AND ALSOFT_BUILD_ROUTER) - ADD_LIBRARY(OpenAL SHARED +else() + set(RC_CONFIG resources/openal32.rc) + if(WIN32 AND ALSOFT_BUILD_ROUTER) + add_library(OpenAL SHARED resources/router.rc router/router.cpp router/router.h router/alc.cpp router/al.cpp ) - TARGET_COMPILE_DEFINITIONS(OpenAL + target_compile_definitions(OpenAL PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES ${CPP_DEFS}) - TARGET_COMPILE_OPTIONS(OpenAL PRIVATE ${C_FLAGS}) - TARGET_LINK_LIBRARIES(OpenAL PRIVATE common ${LINKER_FLAGS}) - TARGET_INCLUDE_DIRECTORIES(OpenAL + target_compile_options(OpenAL PRIVATE ${C_FLAGS}) + target_link_libraries(OpenAL PRIVATE common ${LINKER_FLAGS}) + target_include_directories(OpenAL PUBLIC $ $ @@ -1198,26 +1198,26 @@ ELSE() ${OpenAL_SOURCE_DIR}/common ${OpenAL_BINARY_DIR} ) - SET_TARGET_PROPERTIES(OpenAL PROPERTIES PREFIX "") - SET_TARGET_PROPERTIES(OpenAL PROPERTIES OUTPUT_NAME ${LIBNAME}) - IF(TARGET build_version) - ADD_DEPENDENCIES(OpenAL build_version) - ENDIF() - SET(HAS_ROUTER 1) - - SET(LIBNAME "soft_oal") - SET(IMPL_TARGET soft_oal) - SET(RC_CONFIG resources/soft_oal.rc) - ENDIF() - - ADD_LIBRARY(${IMPL_TARGET} SHARED ${OPENAL_OBJS} ${ALC_OBJS} ${RC_CONFIG}) - IF(WIN32) - SET_TARGET_PROPERTIES(${IMPL_TARGET} PROPERTIES PREFIX "") - ENDIF() - TARGET_LINK_LIBRARIES(${IMPL_TARGET} PRIVATE common ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) -ENDIF() - -TARGET_INCLUDE_DIRECTORIES(${IMPL_TARGET} + set_target_properties(OpenAL PROPERTIES PREFIX "") + set_target_properties(OpenAL PROPERTIES OUTPUT_NAME ${LIBNAME}) + if(TARGET build_version) + add_dependencies(OpenAL build_version) + endif() + set(HAS_ROUTER 1) + + set(LIBNAME "soft_oal") + set(IMPL_TARGET soft_oal) + set(RC_CONFIG resources/soft_oal.rc) + endif() + + add_library(${IMPL_TARGET} SHARED ${OPENAL_OBJS} ${ALC_OBJS} ${RC_CONFIG}) + if(WIN32) + set_target_properties(${IMPL_TARGET} PROPERTIES PREFIX "") + endif() + target_link_libraries(${IMPL_TARGET} PRIVATE common ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) +endif() + +target_include_directories(${IMPL_TARGET} PUBLIC $ $ @@ -1229,61 +1229,61 @@ TARGET_INCLUDE_DIRECTORIES(${IMPL_TARGET} ${OpenAL_SOURCE_DIR}/common ) -SET_TARGET_PROPERTIES(${IMPL_TARGET} PROPERTIES OUTPUT_NAME ${LIBNAME} +set_target_properties(${IMPL_TARGET} PROPERTIES OUTPUT_NAME ${LIBNAME} VERSION ${LIB_VERSION} SOVERSION ${LIB_MAJOR_VERSION} ) -TARGET_COMPILE_DEFINITIONS(${IMPL_TARGET} +target_compile_definitions(${IMPL_TARGET} PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES ${CPP_DEFS}) -TARGET_COMPILE_OPTIONS(${IMPL_TARGET} PRIVATE ${C_FLAGS}) - -IF(TARGET build_version) - ADD_DEPENDENCIES(${IMPL_TARGET} build_version) -ENDIF() - -IF(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC") - FIND_PROGRAM(SED_EXECUTABLE NAMES sed DOC "sed executable") - FIND_PROGRAM(DLLTOOL_EXECUTABLE NAMES "${DLLTOOL}" DOC "dlltool executable") - IF(NOT SED_EXECUTABLE OR NOT DLLTOOL_EXECUTABLE) - MESSAGE(STATUS "") - IF(NOT SED_EXECUTABLE) - MESSAGE(STATUS "WARNING: Cannot find sed, disabling .def/.lib generation") - ENDIF() - IF(NOT DLLTOOL_EXECUTABLE) - MESSAGE(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation") - ENDIF() - ELSE() - SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY +target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS}) + +if(TARGET build_version) + add_dependencies(${IMPL_TARGET} build_version) +endif() + +if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC") + find_program(SED_EXECUTABLE NAMES sed DOC "sed executable") + find_program(DLLTOOL_EXECUTABLE NAMES "${DLLTOOL}" DOC "dlltool executable") + if(NOT SED_EXECUTABLE OR NOT DLLTOOL_EXECUTABLE) + message(STATUS "") + if(NOT SED_EXECUTABLE) + message(STATUS "WARNING: Cannot find sed, disabling .def/.lib generation") + endif() + if(NOT DLLTOOL_EXECUTABLE) + message(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation") + endif() + else() + set_propertySET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--output-def,OpenAL32.def") - ADD_CUSTOM_COMMAND(TARGET OpenAL POST_BUILD + add_custom_command(TARGET OpenAL POST_BUILD COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def COMMAND "${DLLTOOL_EXECUTABLE}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll COMMENT "Stripping ordinals from OpenAL32.def and generating OpenAL32.lib..." VERBATIM ) - ENDIF() -ENDIF() + endif() +endif() if(HAS_ROUTER) message(STATUS "") message(STATUS "Building DLL router") endif() -MESSAGE(STATUS "") -MESSAGE(STATUS "Building OpenAL with support for the following backends:") -MESSAGE(STATUS " ${BACKENDS}") -MESSAGE(STATUS "") -MESSAGE(STATUS "Building with support for CPU extensions:") -MESSAGE(STATUS " ${CPU_EXTS}") -MESSAGE(STATUS "") -IF(FPMATH_SET) - MESSAGE(STATUS "Building with SSE${FPMATH_SET} codegen") - MESSAGE(STATUS "") -ENDIF() +message(STATUS " +Building OpenAL with support for the following backends: + ${BACKENDS} + +Building with support for CPU extensions: + ${CPU_EXTS} +") +if(FPMATH_SET) + message(STATUS "Building with SSE${FPMATH_SET} codegen +") +endif() if(ALSOFT_EMBED_HRTF_DATA) - message(STATUS "Embedding HRTF datasets") - message(STATUS "") + message(STATUS "Embedding HRTF datasets +") endif() # Install main library @@ -1334,10 +1334,10 @@ message(STATUS "") set(EXTRA_INSTALLS ) if(ALSOFT_UTILS) - ADD_EXECUTABLE(openal-info utils/openal-info.c) - TARGET_INCLUDE_DIRECTORIES(openal-info PRIVATE ${OpenAL_SOURCE_DIR}/common) - TARGET_COMPILE_OPTIONS(openal-info PRIVATE ${C_FLAGS}) - TARGET_LINK_LIBRARIES(openal-info PRIVATE ${LINKER_FLAGS} OpenAL) + add_executable(openal-info utils/openal-info.c) + target_include_directories(openal-info PRIVATE ${OpenAL_SOURCE_DIR}/common) + target_compile_options(openal-info PRIVATE ${C_FLAGS}) + target_link_libraries(openal-info PRIVATE ${LINKER_FLAGS} OpenAL) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} openal-info) endif() @@ -1390,13 +1390,13 @@ endif() # Add a static library with common functions used by multiple example targets -ADD_LIBRARY(ex-common STATIC EXCLUDE_FROM_ALL +add_library(ex-common STATIC EXCLUDE_FROM_ALL examples/common/alhelpers.c examples/common/alhelpers.h) -TARGET_COMPILE_DEFINITIONS(ex-common PUBLIC ${CPP_DEFS}) -TARGET_INCLUDE_DIRECTORIES(ex-common PUBLIC ${OpenAL_SOURCE_DIR}/common) -TARGET_COMPILE_OPTIONS(ex-common PUBLIC ${C_FLAGS}) -TARGET_LINK_LIBRARIES(ex-common PUBLIC OpenAL PRIVATE ${RT_LIB}) +target_compile_definitions(ex-common PUBLIC ${CPP_DEFS}) +target_include_directories(ex-common PUBLIC ${OpenAL_SOURCE_DIR}/common) +target_compile_options(ex-common PUBLIC ${C_FLAGS}) +target_link_libraries(ex-common PUBLIC OpenAL PRIVATE ${RT_LIB}) if(ALSOFT_EXAMPLES) add_executable(altonegen examples/altonegen.c) @@ -1443,57 +1443,57 @@ if(ALSOFT_EXAMPLES) message(STATUS "Building SndFile example programs") endif() - IF(SDL2_FOUND) - ADD_EXECUTABLE(alloopback examples/alloopback.c) - TARGET_INCLUDE_DIRECTORIES(alloopback PRIVATE ${SDL2_INCLUDE_DIR}) - TARGET_LINK_LIBRARIES(alloopback + if(SDL2_FOUND) + add_executable(alloopback examples/alloopback.c) + target_include_directories(alloopback PRIVATE ${SDL2_INCLUDE_DIR}) + target_link_libraries(alloopback PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ex-common ${MATH_LIB}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alloopback) endif() - MESSAGE(STATUS "Building SDL example programs") - - SET(FFVER_OK FALSE) - IF(FFMPEG_FOUND) - SET(FFVER_OK TRUE) - IF(AVFORMAT_VERSION VERSION_LESS "57.56.101") - MESSAGE(STATUS "libavformat is too old! (${AVFORMAT_VERSION}, wanted 57.56.101)") - SET(FFVER_OK FALSE) - ENDIF() - IF(AVCODEC_VERSION VERSION_LESS "57.64.101") - MESSAGE(STATUS "libavcodec is too old! (${AVCODEC_VERSION}, wanted 57.64.101)") - SET(FFVER_OK FALSE) - ENDIF() - IF(AVUTIL_VERSION VERSION_LESS "55.34.101") - MESSAGE(STATUS "libavutil is too old! (${AVUTIL_VERSION}, wanted 55.34.101)") - SET(FFVER_OK FALSE) - ENDIF() - IF(SWSCALE_VERSION VERSION_LESS "4.2.100") - MESSAGE(STATUS "libswscale is too old! (${SWSCALE_VERSION}, wanted 4.2.100)") - SET(FFVER_OK FALSE) - ENDIF() - IF(SWRESAMPLE_VERSION VERSION_LESS "2.3.100") - MESSAGE(STATUS "libswresample is too old! (${SWRESAMPLE_VERSION}, wanted 2.3.100)") - SET(FFVER_OK FALSE) - ENDIF() - ENDIF() - IF(FFVER_OK) - ADD_EXECUTABLE(alffplay examples/alffplay.cpp) - TARGET_INCLUDE_DIRECTORIES(alffplay + message(STATUS "Building SDL example programs") + + set(FFVER_OK FALSE) + if(FFMPEG_FOUND) + set(FFVER_OK TRUE) + if(AVFORMAT_VERSION VERSION_LESS "57.56.101") + message(STATUS "libavformat is too old! (${AVFORMAT_VERSION}, wanted 57.56.101)") + set(FFVER_OK FALSE) + endif() + if(AVCODEC_VERSION VERSION_LESS "57.64.101") + message(STATUS "libavcodec is too old! (${AVCODEC_VERSION}, wanted 57.64.101)") + set(FFVER_OK FALSE) + endif() + if(AVUTIL_VERSION VERSION_LESS "55.34.101") + message(STATUS "libavutil is too old! (${AVUTIL_VERSION}, wanted 55.34.101)") + set(FFVER_OK FALSE) + endif() + if(SWSCALE_VERSION VERSION_LESS "4.2.100") + message(STATUS "libswscale is too old! (${SWSCALE_VERSION}, wanted 4.2.100)") + set(FFVER_OK FALSE) + endif() + if(SWRESAMPLE_VERSION VERSION_LESS "2.3.100") + message(STATUS "libswresample is too old! (${SWRESAMPLE_VERSION}, wanted 2.3.100)") + set(FFVER_OK FALSE) + endif() + endif() + if(FFVER_OK) + add_executable(alffplay examples/alffplay.cpp) + target_include_directories(alffplay PRIVATE ${SDL2_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIRS}) - TARGET_LINK_LIBRARIES(alffplay + target_link_libraries(alffplay PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} ex-common) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alffplay) endif() message(STATUS "Building SDL+FFmpeg example programs") - ENDIF() - ENDIF() - MESSAGE(STATUS "") -ENDIF() + endif() + endif() + message(STATUS "") +endif() if(EXTRA_INSTALLS) install(TARGETS ${EXTRA_INSTALLS} -- cgit v1.2.3 From 26eccd159f0256d92ffdb8bdc9dee0bf3e73be57 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 11 Jun 2020 08:55:52 -0700 Subject: Link with the CoreFoundation framework on iOS --- CMakeLists.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 83eee139..73083973 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -961,8 +961,13 @@ if(COREAUDIO_FRAMEWORK AND AUDIOUNIT_INCLUDE_DIR) set(HAVE_COREAUDIO 1) set(ALC_OBJS ${ALC_OBJS} alc/backends/coreaudio.cpp alc/backends/coreaudio.h) set(BACKENDS "${BACKENDS} CoreAudio,") - set(EXTRA_LIBS ${COREAUDIO_FRAMEWORK} /System/Library/Frameworks/AudioUnit.framework - /System/Library/Frameworks/ApplicationServices.framework ${EXTRA_LIBS}) + if(CMAKE_SYSTEM_NAME STREQUAL "iOS") + find_library(COREFOUNDATION_FRAMEWORK NAMES CoreFoundation) + set(EXTRA_LIBS ${COREAUDIO_FRAMEWORK} ${COREFOUNDATION_FRAMEWORK} ${EXTRA_LIBS}) + else() + set(EXTRA_LIBS ${COREAUDIO_FRAMEWORK} /System/Library/Frameworks/AudioUnit.framework + /System/Library/Frameworks/ApplicationServices.framework ${EXTRA_LIBS}) + endif() # Some versions of OSX may need the AudioToolbox framework. Add it if # it's found. -- cgit v1.2.3 From c951190d3a8bf03ef619c0b3f87388762a7c1743 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 12 Jun 2020 12:54:05 -0700 Subject: Fix a typo --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 73083973..38bfb6db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1258,7 +1258,7 @@ if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC" message(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation") endif() else() - set_propertySET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY + set_property(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--output-def,OpenAL32.def") add_custom_command(TARGET OpenAL POST_BUILD COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def -- cgit v1.2.3 From c1383e3a48c6b882adb97df2255a0f2938cb2887 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 8 Jul 2020 23:32:24 -0700 Subject: Don't call find_package(WindowsSDK) if the platform ver is already set --- CMakeLists.txt | 11 +++++++---- cmake/FindDSound.cmake | 12 ++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 38bfb6db..2bdb245b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -846,10 +846,13 @@ option(ALSOFT_REQUIRE_WASAPI "Require WASAPI backend" OFF) if(WIN32) set(WINSDK_LIB_DIRS ) set(WINSDK_INCLUDE_DIRS ) - find_package(WindowsSDK) - if(WINDOWSSDK_FOUND) - get_windowssdk_library_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_LIB_DIRS) - get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_INCLUDE_DIRS) + + if(NOT CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + find_package(WindowsSDK) + if(WINDOWSSDK_FOUND) + get_windowssdk_library_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_LIB_DIRS) + get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_INCLUDE_DIRS) + endif() endif() # Check MMSystem backend diff --git a/cmake/FindDSound.cmake b/cmake/FindDSound.cmake index ecd36078..91f5f4e6 100644 --- a/cmake/FindDSound.cmake +++ b/cmake/FindDSound.cmake @@ -8,12 +8,12 @@ # DSOUND_LIBRARY - the dsound library # -if (WIN32) - FIND_PACKAGE(WindowsSDK) - if (WINDOWSSDK_FOUND) - get_windowssdk_library_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_LIB_DIRS) - get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_INCLUDE_DIRS) - endif() +if(WIN32 AND NOT CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + find_package(WindowsSDK) + if(WINDOWSSDK_FOUND) + get_windowssdk_library_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_LIB_DIRS) + get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_INCLUDE_DIRS) + endif() endif() # DSOUND_INCLUDE_DIR -- cgit v1.2.3 From f409cb4039260031b3d0c7779a0970034334139d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 9 Jul 2020 00:07:33 -0700 Subject: Revert "Don't call find_package(WindowsSDK) if the platform ver is already set" This reverts commit c1383e3a48c6b882adb97df2255a0f2938cb2887. --- CMakeLists.txt | 11 ++++------- cmake/FindDSound.cmake | 12 ++++++------ 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bdb245b..38bfb6db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -846,13 +846,10 @@ option(ALSOFT_REQUIRE_WASAPI "Require WASAPI backend" OFF) if(WIN32) set(WINSDK_LIB_DIRS ) set(WINSDK_INCLUDE_DIRS ) - - if(NOT CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) - find_package(WindowsSDK) - if(WINDOWSSDK_FOUND) - get_windowssdk_library_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_LIB_DIRS) - get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_INCLUDE_DIRS) - endif() + find_package(WindowsSDK) + if(WINDOWSSDK_FOUND) + get_windowssdk_library_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_LIB_DIRS) + get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_INCLUDE_DIRS) endif() # Check MMSystem backend diff --git a/cmake/FindDSound.cmake b/cmake/FindDSound.cmake index 91f5f4e6..ecd36078 100644 --- a/cmake/FindDSound.cmake +++ b/cmake/FindDSound.cmake @@ -8,12 +8,12 @@ # DSOUND_LIBRARY - the dsound library # -if(WIN32 AND NOT CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) - find_package(WindowsSDK) - if(WINDOWSSDK_FOUND) - get_windowssdk_library_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_LIB_DIRS) - get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_INCLUDE_DIRS) - endif() +if (WIN32) + FIND_PACKAGE(WindowsSDK) + if (WINDOWSSDK_FOUND) + get_windowssdk_library_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_LIB_DIRS) + get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_INCLUDE_DIRS) + endif() endif() # DSOUND_INCLUDE_DIR -- cgit v1.2.3 From 064710cdf7aadec0d2c10e39480fb7ff7d0afea6 Mon Sep 17 00:00:00 2001 From: Luis Cáceres Date: Wed, 5 Aug 2020 14:16:56 +0000 Subject: Force stack alignment on 32-bit gcc for proper SSE use (#462) Fixes crashes due to misaligned stack variables in SSE instructions (#460). --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 38bfb6db..b59bdf71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -364,6 +364,7 @@ if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND (SSE2_SWITCH OR MSVC)) set(C_FLAGS ${C_FLAGS} ${SSE_FLAGS}) set(FPMATH_SET 2) endif() + set(EXPORT_DECL "${EXPORT_DECL} __attribute__((force_align_arg_pointer))") elseif(MSVC) check_c_compiler_flag("/arch:SSE2" HAVE_ARCH_SSE2) if(HAVE_ARCH_SSE2) -- cgit v1.2.3 From 58a2a5e2e3cf3c9ecdc261a38ff9da227a5d4a1c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 5 Aug 2020 17:43:51 -0700 Subject: Add a comment about 32-bit GCC stack aligning with SSE codegen --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index b59bdf71..f02dc89a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -364,6 +364,10 @@ if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND (SSE2_SWITCH OR MSVC)) set(C_FLAGS ${C_FLAGS} ${SSE_FLAGS}) set(FPMATH_SET 2) endif() + # SSE depends on a 16-byte aligned stack, and by default, GCC + # assumes the stack is suitably aligned. Older Linux code or other + # OSs don't guarantee this on 32-bit, so externally-callable + # functions need to ensure an aligned stack. set(EXPORT_DECL "${EXPORT_DECL} __attribute__((force_align_arg_pointer))") elseif(MSVC) check_c_compiler_flag("/arch:SSE2" HAVE_ARCH_SSE2) -- cgit v1.2.3 From fd52c828a9c4f4bb7ed3a4dcfe4a5b4ed5664246 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Aug 2020 17:40:00 -0700 Subject: Improve handling main() with UTF-8 args on Windows --- CMakeLists.txt | 33 ++++++++++------- common/win_main_utf8.h | 92 ++++++++++++++++++++++++++++------------------- examples/alrecord.c | 2 ++ examples/altonegen.c | 2 ++ utils/makemhr/makemhr.cpp | 2 -- utils/openal-info.c | 3 ++ utils/sofa-info.cpp | 4 +-- 7 files changed, 85 insertions(+), 53 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index f02dc89a..8fe3c8de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1342,12 +1342,16 @@ if(ALSOFT_INSTALL_AMBDEC_PRESETS) endif() message(STATUS "") +set(UNICODE_FLAG ) +if(MINGW) + set(UNICODE_FLAG ${UNICODE_FLAG} -municode) +endif() set(EXTRA_INSTALLS ) if(ALSOFT_UTILS) add_executable(openal-info utils/openal-info.c) target_include_directories(openal-info PRIVATE ${OpenAL_SOURCE_DIR}/common) target_compile_options(openal-info PRIVATE ${C_FLAGS}) - target_link_libraries(openal-info PRIVATE ${LINKER_FLAGS} OpenAL) + target_link_libraries(openal-info PRIVATE ${LINKER_FLAGS} OpenAL ${UNICODE_FLAG}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} openal-info) endif() @@ -1378,7 +1382,7 @@ if(ALSOFT_UTILS) target_include_directories(makemhr PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/utils) target_compile_options(makemhr PRIVATE ${C_FLAGS}) - target_link_libraries(makemhr PRIVATE ${LINKER_FLAGS} sofa-support) + target_link_libraries(makemhr PRIVATE ${LINKER_FLAGS} sofa-support ${UNICODE_FLAG}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} makemhr) endif() @@ -1388,7 +1392,7 @@ if(ALSOFT_UTILS) target_compile_definitions(sofa-info PRIVATE ${CPP_DEFS}) target_include_directories(sofa-info PRIVATE ${OpenAL_SOURCE_DIR}/utils) target_compile_options(sofa-info PRIVATE ${C_FLAGS}) - target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} sofa-support) + target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} sofa-support ${UNICODE_FLAG}) endif() message(STATUS "Building utility programs") @@ -1410,10 +1414,10 @@ target_link_libraries(ex-common PUBLIC OpenAL PRIVATE ${RT_LIB}) if(ALSOFT_EXAMPLES) add_executable(altonegen examples/altonegen.c) - target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} ex-common) + target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} ex-common ${UNICODE_FLAG}) add_executable(alrecord examples/alrecord.c) - target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} ex-common) + target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} ex-common ${UNICODE_FLAG}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} altonegen alrecord) @@ -1423,27 +1427,32 @@ if(ALSOFT_EXAMPLES) if(SNDFILE_FOUND) add_executable(alplay examples/alplay.c) - target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common) + target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common + ${UNICODE_FLAG}) add_executable(alstream examples/alstream.c) - target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common) + target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common + ${UNICODE_FLAG}) add_executable(alreverb examples/alreverb.c) - target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common) + target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common + ${UNICODE_FLAG}) add_executable(almultireverb examples/almultireverb.c) target_link_libraries(almultireverb - PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB}) + PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB} ${UNICODE_FLAG}) add_executable(allatency examples/allatency.c) - target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common) + target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common + ${UNICODE_FLAG}) add_executable(alhrtf examples/alhrtf.c) target_link_libraries(alhrtf - PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB}) + PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB} ${UNICODE_FLAG}) add_executable(alstreamcb examples/alstreamcb.cpp) - target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common) + target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common + ${UNICODE_FLAG}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alplay alstream alreverb almultireverb allatency diff --git a/common/win_main_utf8.h b/common/win_main_utf8.h index 242d3b8a..e40c2158 100644 --- a/common/win_main_utf8.h +++ b/common/win_main_utf8.h @@ -13,10 +13,23 @@ #define WIN32_LEAN_AND_MEAN #include #include +#include + +#ifdef __cplusplus +#include + +#define STATIC_CAST(...) static_cast<__VA_ARGS__> +#define REINTERPRET_CAST(...) reinterpret_cast<__VA_ARGS__> + +#else + +#define STATIC_CAST(...) (__VA_ARGS__) +#define REINTERPRET_CAST(...) (__VA_ARGS__) +#endif static FILE *my_fopen(const char *fname, const char *mode) { - WCHAR *wname=NULL, *wmode=NULL; + wchar_t *wname=NULL, *wmode=NULL; int namelen, modelen; FILE *file = NULL; errno_t err; @@ -30,7 +43,12 @@ static FILE *my_fopen(const char *fname, const char *mode) return NULL; } - wname = (WCHAR*)calloc(sizeof(WCHAR), namelen+modelen); +#ifdef __cplusplus + auto strbuf = std::make_unique(static_cast(namelen+modelen)); + wname = strbuf.get(); +#else + wname = (wchar_t*)calloc(sizeof(wchar_t), (size_t)(namelen+modelen)); +#endif wmode = wname + namelen; MultiByteToWideChar(CP_UTF8, 0, fname, -1, wname, namelen); MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, modelen); @@ -42,56 +60,58 @@ static FILE *my_fopen(const char *fname, const char *mode) file = NULL; } +#ifndef __cplusplus free(wname); - +#endif return file; } #define fopen my_fopen -static char **arglist; -static void cleanup_arglist(void) -{ - free(arglist); -} +/* SDL overrides main and provides UTF-8 args for us. */ +#if !defined(SDL_MAIN_NEEDED) && !defined(SDL_MAIN_AVAILABLE) +int my_main(int, char**); +#define main my_main -static void GetUnicodeArgs(int *argc, char ***argv) +#ifdef __cplusplus +extern "C" +#endif +int wmain(int argc, wchar_t **wargv) { + char **argv; size_t total; - wchar_t **args; - int nargs, i; - - args = CommandLineToArgvW(GetCommandLineW(), &nargs); - if(!args) - { - fprintf(stderr, "Failed to get command line args: %ld\n", GetLastError()); - exit(EXIT_FAILURE); - } + int i; - total = sizeof(**argv) * nargs; - for(i = 0;i < nargs;i++) - total += WideCharToMultiByte(CP_UTF8, 0, args[i], -1, NULL, 0, NULL, NULL); + total = sizeof(*argv) * STATIC_CAST(size_t)(argc); + for(i = 0;i < argc;i++) + total += STATIC_CAST(size_t)(WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, NULL, + NULL)); - atexit(cleanup_arglist); - arglist = *argv = (char**)calloc(1, total); - (*argv)[0] = (char*)(*argv + nargs); - for(i = 0;i < nargs-1;i++) +#ifdef __cplusplus + auto argbuf = std::make_unique(total); + argv = reinterpret_cast(argbuf.get()); +#else + argv = (char**)calloc(1, total); +#endif + argv[0] = REINTERPRET_CAST(char*)(argv + argc); + for(i = 0;i < argc-1;i++) { - int len = WideCharToMultiByte(CP_UTF8, 0, args[i], -1, (*argv)[i], 65535, NULL, NULL); - (*argv)[i+1] = (*argv)[i] + len; + int len = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], 65535, NULL, NULL); + argv[i+1] = argv[i] + len; } - WideCharToMultiByte(CP_UTF8, 0, args[i], -1, (*argv)[i], 65535, NULL, NULL); - *argc = nargs; - - LocalFree(args); -} -#define GET_UNICODE_ARGS(argc, argv) GetUnicodeArgs(argc, argv) + WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], 65535, NULL, NULL); +#ifdef __cplusplus + return main(argc, argv); #else + i = main(argc, argv); -/* Do nothing. */ -#define GET_UNICODE_ARGS(argc, argv) - + free(argv); + return i; #endif +} +#endif /* !defined(SDL_MAIN_NEEDED) && !defined(SDL_MAIN_AVAILABLE) */ + +#endif /* _WIN32 */ #endif /* WIN_MAIN_UTF8_H */ diff --git a/examples/alrecord.c b/examples/alrecord.c index a66e5471..0e81eb76 100644 --- a/examples/alrecord.c +++ b/examples/alrecord.c @@ -35,6 +35,8 @@ #include "common/alhelpers.h" +#include "win_main_utf8.h" + #if defined(_WIN64) #define SZFMT "%I64u" diff --git a/examples/altonegen.c b/examples/altonegen.c index 553bc996..a09cbdf8 100644 --- a/examples/altonegen.c +++ b/examples/altonegen.c @@ -44,6 +44,8 @@ #include "common/alhelpers.h" +#include "win_main_utf8.h" + #ifndef M_PI #define M_PI (3.14159265358979323846) #endif diff --git a/utils/makemhr/makemhr.cpp b/utils/makemhr/makemhr.cpp index ba1d5d68..96823745 100644 --- a/utils/makemhr/makemhr.cpp +++ b/utils/makemhr/makemhr.cpp @@ -1534,8 +1534,6 @@ int main(int argc, char *argv[]) double limit; int opt; - GET_UNICODE_ARGS(&argc, &argv); - if(argc < 2) { fprintf(stdout, "HRTF Processing and Composition Utility\n\n"); diff --git a/utils/openal-info.c b/utils/openal-info.c index 46e66d45..1788d118 100644 --- a/utils/openal-info.c +++ b/utils/openal-info.c @@ -31,6 +31,9 @@ #include "AL/al.h" #include "AL/alext.h" +#include "win_main_utf8.h" + + #ifndef ALC_ENUMERATE_ALL_EXT #define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012 #define ALC_ALL_DEVICES_SPECIFIER 0x1013 diff --git a/utils/sofa-info.cpp b/utils/sofa-info.cpp index 6117b1e6..26a2ee3f 100644 --- a/utils/sofa-info.cpp +++ b/utils/sofa-info.cpp @@ -27,10 +27,10 @@ #include #include "sofa-support.h" -#include "win_main_utf8.h" #include "mysofa.h" +#include "win_main_utf8.h" using uint = unsigned int; @@ -118,8 +118,6 @@ static void SofaInfo(const char *filename) int main(int argc, char *argv[]) { - GET_UNICODE_ARGS(&argc, &argv); - if(argc != 2) { fprintf(stdout, "Usage: %s \n", argv[0]); -- cgit v1.2.3 From 8e7199cbb60a4682a3d4c28d5746c1890c40e983 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Aug 2020 14:04:29 -0700 Subject: Avoid a cmake check for determining the size of long --- CMakeLists.txt | 4 ---- common/alnumeric.h | 26 +++++++++++++++++++------- config.h.in | 3 --- 3 files changed, 19 insertions(+), 14 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fe3c8de..e7f2f460 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,6 @@ include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) include(CheckCSourceCompiles) include(CheckCXXSourceCompiles) -include(CheckTypeSize) include(CheckStructHasMember) include(GNUInstallDirs) @@ -128,9 +127,6 @@ set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0) set(EXPORT_DECL "") -check_type_size("long" SIZEOF_LONG) - - # Require C++14 set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) diff --git a/common/alnumeric.h b/common/alnumeric.h index df05b966..61bfff8b 100644 --- a/common/alnumeric.h +++ b/common/alnumeric.h @@ -96,15 +96,27 @@ inline size_t RoundUp(size_t value, size_t r) noexcept */ #ifdef __GNUC__ +template +struct NumericDetail64 { }; +template<> +struct NumericDetail64 { + constexpr static inline auto popcnt64(unsigned long long val) noexcept + { return __builtin_popcountll(val); } + constexpr static inline auto ctz64(unsigned long long val) noexcept + { return __builtin_ctzll(val); } +}; +template<> +struct NumericDetail64 { + constexpr static inline auto popcnt64(unsigned long val) noexcept + { return __builtin_popcountl(val); } + constexpr static inline auto ctz64(unsigned long val) noexcept + { return __builtin_ctzl(val); } +}; + #define POPCNT32 __builtin_popcount #define CTZ32 __builtin_ctz -#if SIZEOF_LONG == 8 -#define POPCNT64 __builtin_popcountl -#define CTZ64 __builtin_ctzl -#else -#define POPCNT64 __builtin_popcountll -#define CTZ64 __builtin_ctzll -#endif +#define POPCNT64 NumericDetail64::popcnt64 +#define CTZ64 NumericDetail64::ctz64 #else diff --git a/config.h.in b/config.h.in index 2de3640e..bd87f4fe 100644 --- a/config.h.in +++ b/config.h.in @@ -77,9 +77,6 @@ /* Define if we have the SDL2 backend */ #cmakedefine HAVE_SDL2 -/* Define to the size of a long int type */ -#cmakedefine SIZEOF_LONG ${SIZEOF_LONG} - /* Define if we have dlfcn.h */ #cmakedefine HAVE_DLFCN_H -- cgit v1.2.3 From 3796e407b5521c70980d48dabdff378be4431619 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Aug 2020 18:04:09 -0700 Subject: Rename ALSOFT_INSTALL_HRTF_DEFS to ALSOFT_INSTALL_HRTF_DATA --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index e7f2f460..16f1dd85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,7 +59,7 @@ option(ALSOFT_EXAMPLES "Build example programs" ON) option(ALSOFT_INSTALL "Install main library" ON) option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" ON) -option(ALSOFT_INSTALL_HRTF_DEFS "Install HRTF definition files" ON) +option(ALSOFT_INSTALL_HRTF_DATA "Install HRTF data files" ON) option(ALSOFT_INSTALL_AMBDEC_PRESETS "Install AmbDec preset files" ON) option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" ON) option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" ON) @@ -77,7 +77,7 @@ if(DEFINED ALSOFT_CONFIG) message(WARNING "ALSOFT_CONFIG is deprecated. Use ALSOFT_INSTALL_CONFIG instead") endif() if(DEFINED ALSOFT_HRTF_DEFS) - message(WARNING "ALSOFT_HRTF_DEFS is deprecated. Use ALSOFT_INSTALL_HRTF_DEFS instead") + message(WARNING "ALSOFT_HRTF_DEFS is deprecated. Use ALSOFT_INSTALL_HRTF_DATA instead") endif() if(DEFINED ALSOFT_AMBDEC_PRESETS) message(WARNING "ALSOFT_AMBDEC_PRESETS is deprecated. Use ALSOFT_INSTALL_AMBDEC_PRESETS instead") @@ -1325,10 +1325,10 @@ if(ALSOFT_INSTALL_CONFIG) message(STATUS "Installing sample configuration") endif() -if(ALSOFT_INSTALL_HRTF_DEFS) +if(ALSOFT_INSTALL_HRTF_DATA) install(DIRECTORY hrtf DESTINATION ${CMAKE_INSTALL_DATADIR}/openal) - message(STATUS "Installing HRTF definitions") + message(STATUS "Installing HRTF data files") endif() if(ALSOFT_INSTALL_AMBDEC_PRESETS) -- cgit v1.2.3 From 4eba5c34e97dc94174a1f8834ec4677f1da7f21a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 20 Aug 2020 16:46:51 -0700 Subject: Use CMAKE_DLLTOOL instead of looking for dlltool manually --- CMakeLists.txt | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 16f1dd85..9584b8bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,16 +94,8 @@ if(WIN32) set(CPP_DEFS ${CPP_DEFS} _WIN32) option(ALSOFT_BUILD_ROUTER "Build the router (EXPERIMENTAL; creates OpenAL32.dll and soft_oal.dll)" OFF) - if(MINGW) option(ALSOFT_BUILD_IMPORT_LIB "Build an import .lib using dlltool (requires sed)" ON) - if(NOT DLLTOOL) - if(HOST) - set(DLLTOOL "${HOST}-dlltool") - else() - set(DLLTOOL "dlltool") - endif() - endif() endif() endif() @@ -1249,13 +1241,12 @@ endif() if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC") find_program(SED_EXECUTABLE NAMES sed DOC "sed executable") - find_program(DLLTOOL_EXECUTABLE NAMES "${DLLTOOL}" DOC "dlltool executable") - if(NOT SED_EXECUTABLE OR NOT DLLTOOL_EXECUTABLE) + if(NOT SED_EXECUTABLE OR NOT CMAKE_DLLTOOL) message(STATUS "") if(NOT SED_EXECUTABLE) message(STATUS "WARNING: Cannot find sed, disabling .def/.lib generation") endif() - if(NOT DLLTOOL_EXECUTABLE) + if(NOT CMAKE_DLLTOOL) message(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation") endif() else() @@ -1263,7 +1254,7 @@ if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC" LINK_FLAGS " -Wl,--output-def,OpenAL32.def") add_custom_command(TARGET OpenAL POST_BUILD COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def - COMMAND "${DLLTOOL_EXECUTABLE}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll + COMMAND "${CMAKE_DLLTOOL}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll COMMENT "Stripping ordinals from OpenAL32.def and generating OpenAL32.lib..." VERBATIM ) -- cgit v1.2.3 From de060ce09a3b5fde8641316a7dbd7bdb8f695ffb Mon Sep 17 00:00:00 2001 From: HALX99 Date: Mon, 24 Aug 2020 14:09:02 -0700 Subject: macOS osx/ios dynamic framework support (#466) * OSX bundle support * Disable framework by default, and fix domain name typo * Remove info.plist, add efx.h for framework public header * Fix osx/ios framework PUBLIC_HEADER doesn't work * Refine comment message * Auto set CFBundleShortVersionString by var LIB_VERSION * Set CFBundleVersion from git commit count * Use space to separate elements in a list * Specific framework name to variable 'IMPL_TARGET' * Solve cmake try_compile failed with code sign, and disable framework code sign * Make ios travis to build dynamic framework bundle by default * Update ios.toolchain.cmake Since we solve code sign issue for cmake to generate dynamic framework xcode project, enable strict try_compile by default * Remove MAKE_CXX_EXTENSIONS from travis-ci * Combined flat lib armv7;arm64 support * Remvoe ios.toolchain.cmake since we don't need [skip appveyor] [skip travis] * Sets framework name to soft_oal, avoid ambiguous with system OpenAL.framework * Fix missing BUNDLE, FRAMEWORK's DESTINATION Build osx/ios dynamic framework required them. * Use @rpath instead fullPath to mac local disk see also: https://github.com/libjpeg-turbo/libjpeg-turbo/commit/c80ddef7a4ce21ace9e3ca0fd190d320cc8cdaeb * CMake, use TRUE for bool value * Don't disable examples, utils, install * Make ALSOFT_OSX_FRAMEWORK for APPLE spec * Remove unused flag and more clearly comment * More clearly comment for solve armv7 target issue Co-authored-by: deal Co-authored-by: bel --- .travis.yml | 8 +- CMakeLists.txt | 70 ++++- cmake/ios.toolchain.cmake | 725 ---------------------------------------------- 3 files changed, 70 insertions(+), 733 deletions(-) delete mode 100644 cmake/ios.toolchain.cmake (limited to 'CMakeLists.txt') diff --git a/.travis.yml b/.travis.yml index 41318268..5931c521 100644 --- a/.travis.yml +++ b/.travis.yml @@ -114,13 +114,11 @@ script: if [[ "${TRAVIS_OS_NAME}" == "osx" && "${BUILD_IOS}" == "true" ]]; then cmake \ -GXcode \ - -DCMAKE_TOOLCHAIN_FILE=cmake/ios.toolchain.cmake \ - -DPLATFORM=OS \ + -DCMAKE_SYSTEM_NAME=iOS \ + -DALSOFT_OSX_FRAMEWORK=ON \ -DALSOFT_REQUIRE_COREAUDIO=ON \ -DALSOFT_EMBED_HRTF_DATA=YES \ - -DLIBTYPE=STATIC \ - -DALSOFT_UTILS=OFF \ - -DALSOFT_EXAMPLES=OFF \ + "-DCMAKE_OSX_ARCHITECTURES=armv7;arm64" \ . fi - cmake --build . --clean-first diff --git a/CMakeLists.txt b/CMakeLists.txt index 9584b8bf..e26e53ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,30 @@ cmake_minimum_required(VERSION 3.0.2) +# The workaround for solve try_compile failed with code sign +# since cmake-3.18.2, not required +# everyting for cmake toolchain config before project(xxx) is better +set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES + "CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED" + "CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED") +set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED NO) +set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO) + +# Fix compile failed with armv7 deployment target >= 11.0, xcode clang will report follow error +# clang: error: invalid iOS deployment version '--target=armv7-apple-ios13.6', +# iOS 10 is the maximum deployment target for 32-bit targets +# If not defined CMAKE_OSX_DEPLOYMENT_TARGET, cmake will choose latest deployment target +if(CMAKE_SYSTEM_NAME STREQUAL "iOS") + if("${CMAKE_OSX_ARCHITECTURES}" MATCHES ".*armv7.*") + if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET + OR "${CMAKE_OSX_DEPLOYMENT_TARGET}" VERSION_GREATER "11.0" + OR "${CMAKE_OSX_DEPLOYMENT_TARGET}" VERSION_EQUAL "11.0") + message(STATUS "Sets iOS minimum deployment target to 10.0 for armv7") + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.0" CACHE STRING "Minimum OS X deployment version") + endif() + endif() +endif() + project(OpenAL) if(COMMAND CMAKE_POLICY) @@ -34,7 +58,6 @@ endif() set(CMAKE_MODULE_PATH "${OpenAL_SOURCE_DIR}/cmake") - include(CheckFunctionExists) include(CheckLibraryExists) include(CheckIncludeFile) @@ -97,6 +120,8 @@ if(WIN32) if(MINGW) option(ALSOFT_BUILD_IMPORT_LIB "Build an import .lib using dlltool (requires sed)" ON) endif() +elseif(APPLE) + option(ALSOFT_OSX_FRAMEWORK "Build as macOS framework" OFF) endif() @@ -126,7 +151,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED TRUE) # Prefer C11, but support C99 and C90 too. set(CMAKE_C_STANDARD 11) - if(NOT WIN32) # Check if _POSIX_C_SOURCE and _XOPEN_SOURCE needs to be set for POSIX functions check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_DEFAULT) @@ -1208,11 +1232,49 @@ else() set(RC_CONFIG resources/soft_oal.rc) endif() - add_library(${IMPL_TARGET} SHARED ${OPENAL_OBJS} ${ALC_OBJS} ${RC_CONFIG}) + # !important: for osx framework public header works, the headers must be in project + set(TARGET_PUBLIC_HEADERS include/AL/al.h include/AL/alc.h include/AL/alext.h include/AL/efx.h) + add_library(${IMPL_TARGET} SHARED ${OPENAL_OBJS} ${ALC_OBJS} ${RC_CONFIG} ${TARGET_PUBLIC_HEADERS}) if(WIN32) set_target_properties(${IMPL_TARGET} PROPERTIES PREFIX "") endif() target_link_libraries(${IMPL_TARGET} PRIVATE common ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) + + if(APPLE AND ALSOFT_OSX_FRAMEWORK) + # Sets framework name to soft_oal to avoid ambiguous with system OpenAL.framework + set(LIBNAME "soft_oal") + if(GIT_FOUND) + EXECUTE_PROCESS(COMMAND ${GIT_EXECUTABLE} rev-list --count head + TIMEOUT 5 + OUTPUT_VARIABLE BUNDLE_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + else() + set(BUNDLE_VERSION 1) + endif() + + # Build: Fix rpath in iOS shared libraries + # If no this flag set, the final dylib binary ld path will be /User/xxx/*/soft_oal.framework/soft_oal, + # And can't be load by iOS devices. + # See also: https://github.com/libjpeg-turbo/libjpeg-turbo/commit/c80ddef7a4ce21ace9e3ca0fd190d320cc8cdaeb + if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG) + set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,") + endif() + + set_target_properties(${IMPL_TARGET} PROPERTIES + FRAMEWORK TRUE + FRAMEWORK_VERSION C + MACOSX_FRAMEWORK_NAME "${IMPL_TARGET}" + MACOSX_FRAMEWORK_IDENTIFIER "org.openal-soft.openal" + MACOSX_FRAMEWORK_SHORT_VERSION_STRING ${LIB_VERSION} + MACOSX_FRAMEWORK_BUNDLE_VERSION ${BUNDLE_VERSION} + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "" + XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO" + XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO" + PUBLIC_HEADER "${TARGET_PUBLIC_HEADERS}" + MACOSX_RPATH TRUE + ) + endif() endif() target_include_directories(${IMPL_TARGET} @@ -1289,6 +1351,7 @@ if(ALSOFT_INSTALL) RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/AL) export(TARGETS OpenAL NAMESPACE OpenAL:: @@ -1504,6 +1567,7 @@ endif() if(EXTRA_INSTALLS) install(TARGETS ${EXTRA_INSTALLS} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() diff --git a/cmake/ios.toolchain.cmake b/cmake/ios.toolchain.cmake deleted file mode 100644 index e81d87c8..00000000 --- a/cmake/ios.toolchain.cmake +++ /dev/null @@ -1,725 +0,0 @@ -# This file is part of the ios-cmake project. It was retrieved from -# https://github.com/cristeab/ios-cmake.git, which is a fork of -# https://code.google.com/p/ios-cmake/. Which in turn is based off of -# the Platform/Darwin.cmake and Platform/UnixPaths.cmake files which -# are included with CMake 2.8.4 -# -# The ios-cmake project is licensed under the new BSD license. -# -# Copyright (c) 2014, Bogdan Cristea and LTE Engineering Software, -# Kitware, Inc., Insight Software Consortium. All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# This file is based off of the Platform/Darwin.cmake and -# Platform/UnixPaths.cmake files which are included with CMake 2.8.4 -# It has been altered for iOS development. -# -# Updated by Alex Stewart (alexs.mac@gmail.com) -# -# ***************************************************************************** -# Now maintained by Alexander Widerberg (widerbergaren [at] gmail.com) -# under the BSD-3-Clause license -# https://github.com/leetal/ios-cmake -# ***************************************************************************** -# -# INFORMATION / HELP -# -# The following arguments control the behaviour of this toolchain: -# -# PLATFORM: (default "OS") -# OS = Build for iPhoneOS. -# OS64 = Build for arm64 iphoneOS. -# OS64COMBINED = Build for arm64 x86_64 iphoneOS. Combined into FAT STATIC lib (supported on 3.14+ of CMakewith "-G Xcode" argument ONLY) -# SIMULATOR = Build for x86 i386 iphoneOS Simulator. -# SIMULATOR64 = Build for x86_64 iphoneOS Simulator. -# TVOS = Build for arm64 tvOS. -# TVOSCOMBINED = Build for arm64 x86_64 tvOS. Combined into FAT STATIC lib (supported on 3.14+ of CMake with "-G Xcode" argument ONLY) -# SIMULATOR_TVOS = Build for x86_64 tvOS Simulator. -# WATCHOS = Build for armv7k arm64_32 for watchOS. -# WATCHOSCOMBINED = Build for armv7k arm64_32 x86_64 watchOS. Combined into FAT STATIC lib (supported on 3.14+ of CMake with "-G Xcode" argument ONLY) -# SIMULATOR_WATCHOS = Build for x86_64 for watchOS Simulator. -# -# CMAKE_OSX_SYSROOT: Path to the SDK to use. By default this is -# automatically determined from PLATFORM and xcodebuild, but -# can also be manually specified (although this should not be required). -# -# CMAKE_DEVELOPER_ROOT: Path to the Developer directory for the platform -# being compiled for. By default this is automatically determined from -# CMAKE_OSX_SYSROOT, but can also be manually specified (although this should -# not be required). -# -# DEPLOYMENT_TARGET: Minimum SDK version to target. Default 2.0 on watchOS and 9.0 on tvOS+iOS -# -# ENABLE_BITCODE: (1|0) Enables or disables bitcode support. Default 1 (true) -# -# ENABLE_ARC: (1|0) Enables or disables ARC support. Default 1 (true, ARC enabled by default) -# -# ENABLE_VISIBILITY: (1|0) Enables or disables symbol visibility support. Default 0 (false, visibility hidden by default) -# -# ENABLE_STRICT_TRY_COMPILE: (1|0) Enables or disables strict try_compile() on all Check* directives (will run linker -# to actually check if linking is possible). Default 0 (false, will set CMAKE_TRY_COMPILE_TARGET_TYPE to STATIC_LIBRARY) -# -# ARCHS: (armv7 armv7s armv7k arm64 arm64_32 i386 x86_64) If specified, will override the default architectures for the given PLATFORM -# OS = armv7 armv7s arm64 (if applicable) -# OS64 = arm64 (if applicable) -# SIMULATOR = i386 -# SIMULATOR64 = x86_64 -# TVOS = arm64 -# SIMULATOR_TVOS = x86_64 (i386 has since long been deprecated) -# WATCHOS = armv7k arm64_32 (if applicable) -# SIMULATOR_WATCHOS = x86_64 (i386 has since long been deprecated) -# -# This toolchain defines the following variables for use externally: -# -# XCODE_VERSION: Version number (not including Build version) of Xcode detected. -# SDK_VERSION: Version of SDK being used. -# CMAKE_OSX_ARCHITECTURES: Architectures being compiled for (generated from PLATFORM). -# APPLE_TARGET_TRIPLE: Used by autoconf build systems. NOTE: If "ARCHS" are overridden, this will *NOT* be set! -# -# This toolchain defines the following macros for use externally: -# -# set_xcode_property (TARGET XCODE_PROPERTY XCODE_VALUE XCODE_VARIANT) -# A convenience macro for setting xcode specific properties on targets. -# Available variants are: All, Release, RelWithDebInfo, Debug, MinSizeRel -# example: set_xcode_property (myioslib IPHONEOS_DEPLOYMENT_TARGET "3.1" "all"). -# -# find_host_package (PROGRAM ARGS) -# A macro used to find executable programs on the host system, not within the -# environment. Thanks to the android-cmake project for providing the -# command. -# -# ******************************** DEPRECATIONS ******************************* -# -# IOS_DEPLOYMENT_TARGET: (Deprecated) Alias to DEPLOYMENT_TARGET -# CMAKE_IOS_DEVELOPER_ROOT: (Deprecated) Alias to CMAKE_DEVELOPER_ROOT -# IOS_PLATFORM: (Deprecated) Alias to PLATFORM -# IOS_ARCH: (Deprecated) Alias to ARCHS -# -# ***************************************************************************** -# - -# Fix for PThread library not in path -set(CMAKE_THREAD_LIBS_INIT "-lpthread") -set(CMAKE_HAVE_THREADS_LIBRARY 1) -set(CMAKE_USE_WIN32_THREADS_INIT 0) -set(CMAKE_USE_PTHREADS_INIT 1) - -# Cache what generator is used -set(USED_CMAKE_GENERATOR "${CMAKE_GENERATOR}" CACHE STRING "Expose CMAKE_GENERATOR" FORCE) - -if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14") - set(MODERN_CMAKE YES) -endif() - -# Get the Xcode version being used. -execute_process(COMMAND xcodebuild -version - OUTPUT_VARIABLE XCODE_VERSION - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) -string(REGEX MATCH "Xcode [0-9\\.]+" XCODE_VERSION "${XCODE_VERSION}") -string(REGEX REPLACE "Xcode ([0-9\\.]+)" "\\1" XCODE_VERSION "${XCODE_VERSION}") - -######## ALIASES (DEPRECATION WARNINGS) - -if(DEFINED IOS_PLATFORM) - set(PLATFORM ${IOS_PLATFORM}) - message(DEPRECATION "IOS_PLATFORM argument is DEPRECATED. Consider using the new PLATFORM argument instead.") -endif() - -if(DEFINED IOS_DEPLOYMENT_TARGET) - set(DEPLOYMENT_TARGET ${IOS_DEPLOYMENT_TARGET}) - message(DEPRECATION "IOS_DEPLOYMENT_TARGET argument is DEPRECATED. Consider using the new DEPLOYMENT_TARGET argument instead.") -endif() - -if(DEFINED CMAKE_IOS_DEVELOPER_ROOT) - set(CMAKE_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT}) - message(DEPRECATION "CMAKE_IOS_DEVELOPER_ROOT argument is DEPRECATED. Consider using the new CMAKE_DEVELOPER_ROOT argument instead.") -endif() - -if(DEFINED IOS_ARCH) - set(ARCHS ${IOS_ARCH}) - message(DEPRECATION "IOS_ARCH argument is DEPRECATED. Consider using the new ARCHS argument instead.") -endif() - -######## END ALIASES - -# Unset the FORCE on cache variables if in try_compile() -set(FORCE_CACHE FORCE) -get_property(_CMAKE_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE) -if(_CMAKE_IN_TRY_COMPILE) - unset(FORCE_CACHE) -endif() - -# Default to building for iPhoneOS if not specified otherwise, and we cannot -# determine the platform from the CMAKE_OSX_ARCHITECTURES variable. The use -# of CMAKE_OSX_ARCHITECTURES is such that try_compile() projects can correctly -# determine the value of PLATFORM from the root project, as -# CMAKE_OSX_ARCHITECTURES is propagated to them by CMake. -if(NOT DEFINED PLATFORM) - if (CMAKE_OSX_ARCHITECTURES) - if(CMAKE_OSX_ARCHITECTURES MATCHES ".*arm.*" AND CMAKE_OSX_SYSROOT MATCHES ".*iphoneos.*") - set(PLATFORM "OS") - elseif(CMAKE_OSX_ARCHITECTURES MATCHES "i386" AND CMAKE_OSX_SYSROOT MATCHES ".*iphonesimulator.*") - set(PLATFORM "SIMULATOR") - elseif(CMAKE_OSX_ARCHITECTURES MATCHES "x86_64" AND CMAKE_OSX_SYSROOT MATCHES ".*iphonesimulator.*") - set(PLATFORM "SIMULATOR64") - elseif(CMAKE_OSX_ARCHITECTURES MATCHES "arm64" AND CMAKE_OSX_SYSROOT MATCHES ".*appletvos.*") - set(PLATFORM "TVOS") - elseif(CMAKE_OSX_ARCHITECTURES MATCHES "x86_64" AND CMAKE_OSX_SYSROOT MATCHES ".*appletvsimulator.*") - set(PLATFORM "SIMULATOR_TVOS") - elseif(CMAKE_OSX_ARCHITECTURES MATCHES ".*armv7k.*" AND CMAKE_OSX_SYSROOT MATCHES ".*watchos.*") - set(PLATFORM "WATCHOS") - elseif(CMAKE_OSX_ARCHITECTURES MATCHES "i386" AND CMAKE_OSX_SYSROOT MATCHES ".*watchsimulator.*") - set(PLATFORM "SIMULATOR_WATCHOS") - endif() - endif() - if (NOT PLATFORM) - set(PLATFORM "OS") - endif() -endif() - -set(PLATFORM_INT "${PLATFORM}" CACHE STRING "Type of platform for which the build targets.") - -# Handle the case where we are targeting iOS and a version above 10.3.4 (32-bit support dropped officially) -if(PLATFORM_INT STREQUAL "OS" AND DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.3.4) - set(PLATFORM_INT "OS64") - message(STATUS "Targeting minimum SDK version ${DEPLOYMENT_TARGET}. Dropping 32-bit support.") -elseif(PLATFORM_INT STREQUAL "SIMULATOR" AND DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.3.4) - set(PLATFORM_INT "SIMULATOR64") - message(STATUS "Targeting minimum SDK version ${DEPLOYMENT_TARGET}. Dropping 32-bit support.") -endif() - -# Determine the platform name and architectures for use in xcodebuild commands -# from the specified PLATFORM name. -if(PLATFORM_INT STREQUAL "OS") - set(SDK_NAME iphoneos) - if(NOT ARCHS) - set(ARCHS armv7 armv7s arm64) - set(APPLE_TARGET_TRIPLE_INT arm-apple-ios) - endif() -elseif(PLATFORM_INT STREQUAL "OS64") - set(SDK_NAME iphoneos) - if(NOT ARCHS) - if (XCODE_VERSION VERSION_GREATER 10.0) - set(ARCHS arm64) # Add arm64e when Apple have fixed the integration issues with it, libarclite_iphoneos.a is currently missung bitcode markers for example - else() - set(ARCHS arm64) - endif() - set(APPLE_TARGET_TRIPLE_INT aarch64-apple-ios) - endif() -elseif(PLATFORM_INT STREQUAL "OS64COMBINED") - set(SDK_NAME iphoneos) - if(MODERN_CMAKE) - if(NOT ARCHS) - if (XCODE_VERSION VERSION_GREATER 10.0) - set(ARCHS arm64 x86_64) # Add arm64e when Apple have fixed the integration issues with it, libarclite_iphoneos.a is currently missung bitcode markers for example - else() - set(ARCHS arm64 x86_64) - endif() - set(APPLE_TARGET_TRIPLE_INT aarch64-x86_64-apple-ios) - endif() - else() - message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the OS64COMBINED setting work") - endif() -elseif(PLATFORM_INT STREQUAL "SIMULATOR") - set(SDK_NAME iphonesimulator) - if(NOT ARCHS) - set(ARCHS i386) - set(APPLE_TARGET_TRIPLE_INT i386-apple-ios) - endif() - message(DEPRECATION "SIMULATOR IS DEPRECATED. Consider using SIMULATOR64 instead.") -elseif(PLATFORM_INT STREQUAL "SIMULATOR64") - set(SDK_NAME iphonesimulator) - if(NOT ARCHS) - set(ARCHS x86_64) - set(APPLE_TARGET_TRIPLE_INT x86_64-apple-ios) - endif() -elseif(PLATFORM_INT STREQUAL "TVOS") - set(SDK_NAME appletvos) - if(NOT ARCHS) - set(ARCHS arm64) - set(APPLE_TARGET_TRIPLE_INT aarch64-apple-tvos) - endif() -elseif (PLATFORM_INT STREQUAL "TVOSCOMBINED") - set(SDK_NAME appletvos) - if(MODERN_CMAKE) - if(NOT ARCHS) - set(ARCHS arm64 x86_64) - set(APPLE_TARGET_TRIPLE_INT aarch64-x86_64-apple-tvos) - endif() - else() - message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the TVOSCOMBINED setting work") - endif() -elseif(PLATFORM_INT STREQUAL "SIMULATOR_TVOS") - set(SDK_NAME appletvsimulator) - if(NOT ARCHS) - set(ARCHS x86_64) - set(APPLE_TARGET_TRIPLE_INT x86_64-apple-tvos) - endif() -elseif(PLATFORM_INT STREQUAL "WATCHOS") - set(SDK_NAME watchos) - if(NOT ARCHS) - if (XCODE_VERSION VERSION_GREATER 10.0) - set(ARCHS armv7k arm64_32) - set(APPLE_TARGET_TRIPLE_INT aarch64_32-apple-watchos) - else() - set(ARCHS armv7k) - set(APPLE_TARGET_TRIPLE_INT arm-apple-watchos) - endif() - endif() -elseif(PLATFORM_INT STREQUAL "WATCHOSCOMBINED") - set(SDK_NAME watchos) - if(MODERN_CMAKE) - if(NOT ARCHS) - if (XCODE_VERSION VERSION_GREATER 10.0) - set(ARCHS armv7k arm64_32 i386) - set(APPLE_TARGET_TRIPLE_INT aarch64_32-i386-apple-watchos) - else() - set(ARCHS armv7k i386) - set(APPLE_TARGET_TRIPLE_INT arm-i386-apple-watchos) - endif() - endif() - else() - message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the WATCHOSCOMBINED setting work") - endif() -elseif(PLATFORM_INT STREQUAL "SIMULATOR_WATCHOS") - set(SDK_NAME watchsimulator) - if(NOT ARCHS) - set(ARCHS i386) - set(APPLE_TARGET_TRIPLE_INT i386-apple-watchos) - endif() -else() - message(FATAL_ERROR "Invalid PLATFORM: ${PLATFORM_INT}") -endif() - -if(MODERN_CMAKE AND PLATFORM_INT MATCHES ".*COMBINED" AND NOT USED_CMAKE_GENERATOR MATCHES "Xcode") - message(FATAL_ERROR "The COMBINED options only work with Xcode generator, -G Xcode") -endif() - -# If user did not specify the SDK root to use, then query xcodebuild for it. -execute_process(COMMAND xcodebuild -version -sdk ${SDK_NAME} Path - OUTPUT_VARIABLE CMAKE_OSX_SYSROOT_INT - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) -if (NOT DEFINED CMAKE_OSX_SYSROOT_INT AND NOT DEFINED CMAKE_OSX_SYSROOT) - message(SEND_ERROR "Please make sure that Xcode is installed and that the toolchain" - "is pointing to the correct path. Please run:" - "sudo xcode-select -s /Applications/Xcode.app/Contents/Developer" - "and see if that fixes the problem for you.") - message(FATAL_ERROR "Invalid CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT} " - "does not exist.") -elseif(DEFINED CMAKE_OSX_SYSROOT_INT) - set(CMAKE_OSX_SYSROOT "${CMAKE_OSX_SYSROOT_INT}" CACHE INTERNAL "") -endif() - -# Set Xcode property for SDKROOT as well if Xcode generator is used -if(USED_CMAKE_GENERATOR MATCHES "Xcode") - set(CMAKE_OSX_SYSROOT "${SDK_NAME}" CACHE INTERNAL "") - if(NOT DEFINED CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM) - set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "123456789A" CACHE INTERNAL "") - endif() -endif() - -# Specify minimum version of deployment target. -if(NOT DEFINED DEPLOYMENT_TARGET) - if (PLATFORM_INT STREQUAL "WATCHOS" OR PLATFORM_INT STREQUAL "SIMULATOR_WATCHOS") - # Unless specified, SDK version 2.0 is used by default as minimum target version (watchOS). - set(DEPLOYMENT_TARGET "2.0" - CACHE STRING "Minimum SDK version to build for." ) - else() - # Unless specified, SDK version 9.0 is used by default as minimum target version (iOS, tvOS). - set(DEPLOYMENT_TARGET "9.0" - CACHE STRING "Minimum SDK version to build for." ) - endif() - message(STATUS "Using the default min-version since DEPLOYMENT_TARGET not provided!") -endif() - -# Use bitcode or not -if(NOT DEFINED ENABLE_BITCODE AND NOT ARCHS MATCHES "((^|;|, )(i386|x86_64))+") - # Unless specified, enable bitcode support by default - message(STATUS "Enabling bitcode support by default. ENABLE_BITCODE not provided!") - set(ENABLE_BITCODE TRUE) -elseif(NOT DEFINED ENABLE_BITCODE) - message(STATUS "Disabling bitcode support by default on simulators. ENABLE_BITCODE not provided for override!") - set(ENABLE_BITCODE FALSE) -endif() -set(ENABLE_BITCODE_INT ${ENABLE_BITCODE} CACHE BOOL "Whether or not to enable bitcode" ${FORCE_CACHE}) -# Use ARC or not -if(NOT DEFINED ENABLE_ARC) - # Unless specified, enable ARC support by default - set(ENABLE_ARC TRUE) - message(STATUS "Enabling ARC support by default. ENABLE_ARC not provided!") -endif() -set(ENABLE_ARC_INT ${ENABLE_ARC} CACHE BOOL "Whether or not to enable ARC" ${FORCE_CACHE}) -# Use hidden visibility or not -if(NOT DEFINED ENABLE_VISIBILITY) - # Unless specified, disable symbols visibility by default - set(ENABLE_VISIBILITY FALSE) - message(STATUS "Hiding symbols visibility by default. ENABLE_VISIBILITY not provided!") -endif() -set(ENABLE_VISIBILITY_INT ${ENABLE_VISIBILITY} CACHE BOOL "Whether or not to hide symbols (-fvisibility=hidden)" ${FORCE_CACHE}) -# Set strict compiler checks or not -if(NOT DEFINED ENABLE_STRICT_TRY_COMPILE) - # Unless specified, disable strict try_compile() - set(ENABLE_STRICT_TRY_COMPILE FALSE) - message(STATUS "Using NON-strict compiler checks by default. ENABLE_STRICT_TRY_COMPILE not provided!") -endif() -set(ENABLE_STRICT_TRY_COMPILE_INT ${ENABLE_STRICT_TRY_COMPILE} CACHE BOOL "Whether or not to use strict compiler checks" ${FORCE_CACHE}) -# Get the SDK version information. -execute_process(COMMAND xcodebuild -sdk ${CMAKE_OSX_SYSROOT} -version SDKVersion - OUTPUT_VARIABLE SDK_VERSION - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - -# Find the Developer root for the specific iOS platform being compiled for -# from CMAKE_OSX_SYSROOT. Should be ../../ from SDK specified in -# CMAKE_OSX_SYSROOT. There does not appear to be a direct way to obtain -# this information from xcrun or xcodebuild. -if (NOT DEFINED CMAKE_DEVELOPER_ROOT AND NOT USED_CMAKE_GENERATOR MATCHES "Xcode") - get_filename_component(PLATFORM_SDK_DIR ${CMAKE_OSX_SYSROOT} PATH) - get_filename_component(CMAKE_DEVELOPER_ROOT ${PLATFORM_SDK_DIR} PATH) - if (NOT DEFINED CMAKE_DEVELOPER_ROOT) - message(FATAL_ERROR "Invalid CMAKE_DEVELOPER_ROOT: " - "${CMAKE_DEVELOPER_ROOT} does not exist.") - endif() -endif() -# Find the C & C++ compilers for the specified SDK. -if(NOT CMAKE_C_COMPILER) - execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find clang - OUTPUT_VARIABLE CMAKE_C_COMPILER - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - message(STATUS "Using C compiler: ${CMAKE_C_COMPILER}") -endif() -if(NOT CMAKE_CXX_COMPILER) - execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find clang++ - OUTPUT_VARIABLE CMAKE_CXX_COMPILER - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - message(STATUS "Using CXX compiler: ${CMAKE_CXX_COMPILER}") -endif() -# Find (Apple's) libtool. -execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find libtool - OUTPUT_VARIABLE BUILD_LIBTOOL - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) -message(STATUS "Using libtool: ${BUILD_LIBTOOL}") -# Configure libtool to be used instead of ar + ranlib to build static libraries. -# This is required on Xcode 7+, but should also work on previous versions of -# Xcode. -set(CMAKE_C_CREATE_STATIC_LIBRARY - "${BUILD_LIBTOOL} -static -o ") -set(CMAKE_CXX_CREATE_STATIC_LIBRARY - "${BUILD_LIBTOOL} -static -o ") -# Find the toolchain's provided install_name_tool if none is found on the host -if(NOT CMAKE_INSTALL_NAME_TOOL) - execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find install_name_tool - OUTPUT_VARIABLE CMAKE_INSTALL_NAME_TOOL_INT - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - set(CMAKE_INSTALL_NAME_TOOL ${CMAKE_INSTALL_NAME_TOOL_INT} CACHE STRING "" ${FORCE_CACHE}) -endif() -# Get the version of Darwin (OS X) of the host. -execute_process(COMMAND uname -r - OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) -if(SDK_NAME MATCHES "iphone") - set(CMAKE_SYSTEM_NAME iOS CACHE INTERNAL "" ${FORCE_CACHE}) -endif() -# CMake 3.14+ support building for iOS, watchOS and tvOS out of the box. -if(MODERN_CMAKE) - if(SDK_NAME MATCHES "appletv") - set(CMAKE_SYSTEM_NAME tvOS CACHE INTERNAL "" ${FORCE_CACHE}) - elseif(SDK_NAME MATCHES "watch") - set(CMAKE_SYSTEM_NAME watchOS CACHE INTERNAL "" ${FORCE_CACHE}) - endif() - # Provide flags for a combined FAT library build on newer CMake versions - if(PLATFORM_INT MATCHES ".*COMBINED") - set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO" CACHE INTERNAL "" ${FORCE_CACHE}) - set(CMAKE_IOS_INSTALL_COMBINED YES CACHE INTERNAL "" ${FORCE_CACHE}) - message(STATUS "Will combine built (static) artifacts into FAT lib...") - endif() -elseif(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.10") - # Legacy code path prior to CMake 3.14 or fallback if no SDK_NAME specified - set(CMAKE_SYSTEM_NAME iOS CACHE INTERNAL "" ${FORCE_CACHE}) -else() - # Legacy code path prior to CMake 3.14 or fallback if no SDK_NAME specified - set(CMAKE_SYSTEM_NAME Darwin CACHE INTERNAL "" ${FORCE_CACHE}) -endif() -# Standard settings. -set(CMAKE_SYSTEM_VERSION ${SDK_VERSION} CACHE INTERNAL "") -set(UNIX TRUE CACHE BOOL "") -set(APPLE TRUE CACHE BOOL "") -set(IOS TRUE CACHE BOOL "") -set(CMAKE_AR ar CACHE FILEPATH "" FORCE) -set(CMAKE_RANLIB ranlib CACHE FILEPATH "" FORCE) -set(CMAKE_STRIP strip CACHE FILEPATH "" FORCE) -# Set the architectures for which to build. -set(CMAKE_OSX_ARCHITECTURES ${ARCHS} CACHE STRING "Build architecture for iOS") -# Change the type of target generated for try_compile() so it'll work when cross-compiling, weak compiler checks -if(ENABLE_STRICT_TRY_COMPILE_INT) - message(STATUS "Using strict compiler checks (default in CMake).") -else() - set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) -endif() -# All iOS/Darwin specific settings - some may be redundant. -set(CMAKE_MACOSX_BUNDLE YES) -set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") -set(CMAKE_SHARED_LIBRARY_PREFIX "lib") -set(CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") -set(CMAKE_SHARED_MODULE_PREFIX "lib") -set(CMAKE_SHARED_MODULE_SUFFIX ".so") -set(CMAKE_C_COMPILER_ABI ELF) -set(CMAKE_CXX_COMPILER_ABI ELF) -set(CMAKE_C_HAS_ISYSROOT 1) -set(CMAKE_CXX_HAS_ISYSROOT 1) -set(CMAKE_MODULE_EXISTS 1) -set(CMAKE_DL_LIBS "") -set(CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") -set(CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") -set(CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") -set(CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") - -if(ARCHS MATCHES "((^|;|, )(arm64|arm64e|x86_64))+") - set(CMAKE_C_SIZEOF_DATA_PTR 8) - set(CMAKE_CXX_SIZEOF_DATA_PTR 8) - if(ARCHS MATCHES "((^|;|, )(arm64|arm64e))+") - set(CMAKE_SYSTEM_PROCESSOR "aarch64") - else() - set(CMAKE_SYSTEM_PROCESSOR "x86_64") - endif() -else() - set(CMAKE_C_SIZEOF_DATA_PTR 4) - set(CMAKE_CXX_SIZEOF_DATA_PTR 4) - set(CMAKE_SYSTEM_PROCESSOR "arm") -endif() - -# Note that only Xcode 7+ supports the newer more specific: -# -m${SDK_NAME}-version-min flags, older versions of Xcode use: -# -m(ios/ios-simulator)-version-min instead. -if(${CMAKE_VERSION} VERSION_LESS "3.11") - if(PLATFORM_INT STREQUAL "OS" OR PLATFORM_INT STREQUAL "OS64") - if(XCODE_VERSION VERSION_LESS 7.0) - set(SDK_NAME_VERSION_FLAGS - "-mios-version-min=${DEPLOYMENT_TARGET}") - else() - # Xcode 7.0+ uses flags we can build directly from SDK_NAME. - set(SDK_NAME_VERSION_FLAGS - "-m${SDK_NAME}-version-min=${DEPLOYMENT_TARGET}") - endif() - elseif(PLATFORM_INT STREQUAL "TVOS") - set(SDK_NAME_VERSION_FLAGS - "-mtvos-version-min=${DEPLOYMENT_TARGET}") - elseif(PLATFORM_INT STREQUAL "SIMULATOR_TVOS") - set(SDK_NAME_VERSION_FLAGS - "-mtvos-simulator-version-min=${DEPLOYMENT_TARGET}") - elseif(PLATFORM_INT STREQUAL "WATCHOS") - set(SDK_NAME_VERSION_FLAGS - "-mwatchos-version-min=${DEPLOYMENT_TARGET}") - elseif(PLATFORM_INT STREQUAL "SIMULATOR_WATCHOS") - set(SDK_NAME_VERSION_FLAGS - "-mwatchos-simulator-version-min=${DEPLOYMENT_TARGET}") - else() - # SIMULATOR or SIMULATOR64 both use -mios-simulator-version-min. - set(SDK_NAME_VERSION_FLAGS - "-mios-simulator-version-min=${DEPLOYMENT_TARGET}") - endif() -else() - # Newer versions of CMake sets the version min flags correctly - set(CMAKE_OSX_DEPLOYMENT_TARGET ${DEPLOYMENT_TARGET} CACHE STRING - "Set CMake deployment target" ${FORCE_CACHE}) -endif() - -if(DEFINED APPLE_TARGET_TRIPLE_INT) - set(APPLE_TARGET_TRIPLE ${APPLE_TARGET_TRIPLE_INT} CACHE STRING - "Autoconf target triple compatible variable" ${FORCE_CACHE}) -endif() - -if(ENABLE_BITCODE_INT) - set(BITCODE "-fembed-bitcode") - set(CMAKE_XCODE_ATTRIBUTE_BITCODE_GENERATION_MODE "bitcode" CACHE INTERNAL "") - set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES" CACHE INTERNAL "") -else() - set(BITCODE "") - set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO" CACHE INTERNAL "") -endif() - -if(ENABLE_ARC_INT) - set(FOBJC_ARC "-fobjc-arc") - set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC "YES" CACHE INTERNAL "") -else() - set(FOBJC_ARC "-fno-objc-arc") - set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC "NO" CACHE INTERNAL "") -endif() - -if(NOT ENABLE_VISIBILITY_INT) - set(VISIBILITY "-fvisibility=hidden") - set(CMAKE_XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN "YES" CACHE INTERNAL "") -else() - set(VISIBILITY "") - set(CMAKE_XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN "NO" CACHE INTERNAL "") -endif() - -if(NOT IOS_TOOLCHAIN_HAS_RUN) - #Check if Xcode generator is used, since that will handle these flags automagically - if(USED_CMAKE_GENERATOR MATCHES "Xcode") - message(STATUS "Not setting any manual command-line buildflags, since Xcode is selected as generator.") - else() - set(CMAKE_C_FLAGS - "${SDK_NAME_VERSION_FLAGS} ${BITCODE} -fobjc-abi-version=2 ${FOBJC_ARC} ${CMAKE_C_FLAGS}") - # Hidden visibilty is required for C++ on iOS. - set(CMAKE_CXX_FLAGS - "${SDK_NAME_VERSION_FLAGS} ${BITCODE} ${VISIBILITY} -fvisibility-inlines-hidden -fobjc-abi-version=2 ${FOBJC_ARC} ${CMAKE_CXX_FLAGS}") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -O0 -g ${CMAKE_CXX_FLAGS_DEBUG}") - set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS} -DNDEBUG -Os -ffast-math ${CMAKE_CXX_FLAGS_MINSIZEREL}") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} -DNDEBUG -O2 -g -ffast-math ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -DNDEBUG -O3 -ffast-math ${CMAKE_CXX_FLAGS_RELEASE}") - set(CMAKE_C_LINK_FLAGS "${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}") - set(CMAKE_CXX_LINK_FLAGS "${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}") - set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp") - - # In order to ensure that the updated compiler flags are used in try_compile() - # tests, we have to forcibly set them in the CMake cache, not merely set them - # in the local scope. - set(VARS_TO_FORCE_IN_CACHE - CMAKE_C_FLAGS - CMAKE_CXX_FLAGS - CMAKE_CXX_FLAGS_DEBUG - CMAKE_CXX_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS_MINSIZEREL - CMAKE_CXX_FLAGS_RELEASE - CMAKE_C_LINK_FLAGS - CMAKE_CXX_LINK_FLAGS) - foreach(VAR_TO_FORCE ${VARS_TO_FORCE_IN_CACHE}) - set(${VAR_TO_FORCE} "${${VAR_TO_FORCE}}" CACHE STRING "" ${FORCE_CACHE}) - endforeach() - endif() - - ## Print status messages to inform of the current state - message(STATUS "Configuring ${SDK_NAME} build for platform: ${PLATFORM_INT}, architecture(s): ${ARCHS}") - message(STATUS "Using SDK: ${CMAKE_OSX_SYSROOT_INT}") - if(DEFINED APPLE_TARGET_TRIPLE) - message(STATUS "Autoconf target triple: ${APPLE_TARGET_TRIPLE}") - endif() - message(STATUS "Using minimum deployment version: ${DEPLOYMENT_TARGET}" - " (SDK version: ${SDK_VERSION})") - if(MODERN_CMAKE) - message(STATUS "Merging integrated CMake 3.14+ iOS,tvOS,watchOS,macOS toolchain(s) with this toolchain!") - endif() - if(USED_CMAKE_GENERATOR MATCHES "Xcode") - message(STATUS "Using Xcode version: ${XCODE_VERSION}") - endif() - if(DEFINED SDK_NAME_VERSION_FLAGS) - message(STATUS "Using version flags: ${SDK_NAME_VERSION_FLAGS}") - endif() - message(STATUS "Using a data_ptr size of: ${CMAKE_CXX_SIZEOF_DATA_PTR}") - message(STATUS "Using install_name_tool: ${CMAKE_INSTALL_NAME_TOOL}") - if(ENABLE_BITCODE_INT) - message(STATUS "Enabling bitcode support.") - else() - message(STATUS "Disabling bitcode support.") - endif() - - if(ENABLE_ARC_INT) - message(STATUS "Enabling ARC support.") - else() - message(STATUS "Disabling ARC support.") - endif() - - if(NOT ENABLE_VISIBILITY_INT) - message(STATUS "Hiding symbols (-fvisibility=hidden).") - endif() -endif() - -set(CMAKE_PLATFORM_HAS_INSTALLNAME 1) -set(CMAKE_SHARED_LINKER_FLAGS "-rpath @executable_path/Frameworks -rpath @loader_path/Frameworks") -set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -Wl,-headerpad_max_install_names") -set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -Wl,-headerpad_max_install_names") -set(CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") -set(CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") -set(CMAKE_FIND_LIBRARY_SUFFIXES ".tbd" ".dylib" ".so" ".a") -set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-install_name") - -# Set the find root to the iOS developer roots and to user defined paths. -set(CMAKE_FIND_ROOT_PATH ${CMAKE_OSX_SYSROOT_INT} ${CMAKE_PREFIX_PATH} CACHE STRING "Root path that will be prepended - to all search paths") -# Default to searching for frameworks first. -set(CMAKE_FIND_FRAMEWORK FIRST) -# Set up the default search directories for frameworks. -set(CMAKE_FRAMEWORK_PATH - ${CMAKE_DEVELOPER_ROOT}/Library/PrivateFrameworks - ${CMAKE_OSX_SYSROOT_INT}/System/Library/Frameworks - ${CMAKE_FRAMEWORK_PATH} CACHE STRING "Frameworks search paths" ${FORCE_CACHE}) - -set(IOS_TOOLCHAIN_HAS_RUN TRUE CACHE BOOL "Has the CMake toolchain run already?" ${FORCE_CACHE}) - -# By default, search both the specified iOS SDK and the remainder of the host filesystem. -if(NOT CMAKE_FIND_ROOT_PATH_MODE_PROGRAM) - set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH CACHE STRING "" ${FORCE_CACHE}) -endif() -if(NOT CMAKE_FIND_ROOT_PATH_MODE_LIBRARY) - set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH CACHE STRING "" ${FORCE_CACHE}) -endif() -if(NOT CMAKE_FIND_ROOT_PATH_MODE_INCLUDE) - set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH CACHE STRING "" ${FORCE_CACHE}) -endif() -if(NOT CMAKE_FIND_ROOT_PATH_MODE_PACKAGE) - set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH CACHE STRING "" ${FORCE_CACHE}) -endif() - -# -# Some helper-macros below to simplify and beautify the CMakeFile -# - -# This little macro lets you set any Xcode specific property. -macro(set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE XCODE_RELVERSION) - set(XCODE_RELVERSION_I "${XCODE_RELVERSION}") - if(XCODE_RELVERSION_I STREQUAL "All") - set_property(TARGET ${TARGET} PROPERTY - XCODE_ATTRIBUTE_${XCODE_PROPERTY} "${XCODE_VALUE}") - else() - set_property(TARGET ${TARGET} PROPERTY - XCODE_ATTRIBUTE_${XCODE_PROPERTY}[variant=${XCODE_RELVERSION_I}] "${XCODE_VALUE}") - endif() -endmacro(set_xcode_property) - -# This macro lets you find executable programs on the host system. -macro(find_host_package) - set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) - set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) - set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE NEVER) - set(IOS FALSE) - find_package(${ARGN}) - set(IOS TRUE) - set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) - set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) - set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) - set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH) -endmacro(find_host_package) -- cgit v1.2.3 From 54a0972bfe3c4f248d495ed71ff734ed99cf9ab7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 24 Aug 2020 14:26:47 -0700 Subject: Clean up some comments --- CMakeLists.txt | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index e26e53ad..3ab81e4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,26 +2,27 @@ cmake_minimum_required(VERSION 3.0.2) -# The workaround for solve try_compile failed with code sign +# The workaround for try_compile failing with code signing # since cmake-3.18.2, not required -# everyting for cmake toolchain config before project(xxx) is better set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES "CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED" "CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED") set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED NO) set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO) -# Fix compile failed with armv7 deployment target >= 11.0, xcode clang will report follow error -# clang: error: invalid iOS deployment version '--target=armv7-apple-ios13.6', -# iOS 10 is the maximum deployment target for 32-bit targets -# If not defined CMAKE_OSX_DEPLOYMENT_TARGET, cmake will choose latest deployment target +# Fix compile failure with armv7 deployment target >= 11.0, xcode clang will +# report: +# error: invalid iOS deployment version '--target=armv7-apple-ios13.6', +# iOS 10 is the maximum deployment target for 32-bit targets +# If CMAKE_OSX_DEPLOYMENT_TARGET is not defined, cmake will choose latest +# deployment target if(CMAKE_SYSTEM_NAME STREQUAL "iOS") if("${CMAKE_OSX_ARCHITECTURES}" MATCHES ".*armv7.*") - if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET - OR "${CMAKE_OSX_DEPLOYMENT_TARGET}" VERSION_GREATER "11.0" - OR "${CMAKE_OSX_DEPLOYMENT_TARGET}" VERSION_EQUAL "11.0") - message(STATUS "Sets iOS minimum deployment target to 10.0 for armv7") - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.0" CACHE STRING "Minimum OS X deployment version") + if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET + OR NOT CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "11.0") + message(STATUS "Forcing iOS deployment target to 10.0 for armv7") + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.0" CACHE STRING "Minimum OS X deployment version" + FORCE) endif() endif() endif() @@ -1232,30 +1233,31 @@ else() set(RC_CONFIG resources/soft_oal.rc) endif() - # !important: for osx framework public header works, the headers must be in project + # !important: for osx framework public header works, the headers must be in + # the project set(TARGET_PUBLIC_HEADERS include/AL/al.h include/AL/alc.h include/AL/alext.h include/AL/efx.h) - add_library(${IMPL_TARGET} SHARED ${OPENAL_OBJS} ${ALC_OBJS} ${RC_CONFIG} ${TARGET_PUBLIC_HEADERS}) + add_library(${IMPL_TARGET} SHARED ${OPENAL_OBJS} ${ALC_OBJS} ${RC_CONFIG} + ${TARGET_PUBLIC_HEADERS}) if(WIN32) set_target_properties(${IMPL_TARGET} PROPERTIES PREFIX "") endif() target_link_libraries(${IMPL_TARGET} PRIVATE common ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) if(APPLE AND ALSOFT_OSX_FRAMEWORK) - # Sets framework name to soft_oal to avoid ambiguous with system OpenAL.framework + # Sets framework name to soft_oal to avoid ambiguity with the system OpenAL.framework set(LIBNAME "soft_oal") if(GIT_FOUND) EXECUTE_PROCESS(COMMAND ${GIT_EXECUTABLE} rev-list --count head TIMEOUT 5 OUTPUT_VARIABLE BUNDLE_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE - ) + OUTPUT_STRIP_TRAILING_WHITESPACE) else() set(BUNDLE_VERSION 1) endif() # Build: Fix rpath in iOS shared libraries - # If no this flag set, the final dylib binary ld path will be /User/xxx/*/soft_oal.framework/soft_oal, - # And can't be load by iOS devices. + # If this flag is not set, the final dylib binary ld path will be + # /User/xxx/*/soft_oal.framework/soft_oal, which can't be loaded by iOS devices. # See also: https://github.com/libjpeg-turbo/libjpeg-turbo/commit/c80ddef7a4ce21ace9e3ca0fd190d320cc8cdaeb if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG) set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,") @@ -1272,8 +1274,7 @@ else() XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO" XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO" PUBLIC_HEADER "${TARGET_PUBLIC_HEADERS}" - MACOSX_RPATH TRUE - ) + MACOSX_RPATH TRUE) endif() endif() -- cgit v1.2.3 From 9d61484e4bc0b2691b714d758391b3c3ecfd7890 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 24 Aug 2020 17:59:07 -0700 Subject: Move storable buffer format info to a separate source --- CMakeLists.txt | 2 ++ al/buffer.cpp | 31 -------------------------- al/buffer.h | 59 +++++++++++++++----------------------------------- alc/buffer_formats.cpp | 37 +++++++++++++++++++++++++++++++ alc/buffer_formats.h | 33 ++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 73 deletions(-) create mode 100644 alc/buffer_formats.cpp create mode 100644 alc/buffer_formats.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ab81e4f..803f34de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -617,6 +617,8 @@ set(ALC_OBJS alc/bsinc_tables.cpp alc/bsinc_tables.h alc/bufferline.h + alc/buffer_formats.cpp + alc/buffer_formats.h alc/compat.h alc/converter.cpp alc/converter.h diff --git a/al/buffer.cpp b/al/buffer.cpp index af1ad638..b795964a 100644 --- a/al/buffer.cpp +++ b/al/buffer.cpp @@ -1577,37 +1577,6 @@ START_API_FUNC END_API_FUNC -ALuint BytesFromFmt(FmtType type) noexcept -{ - switch(type) - { - case FmtUByte: return sizeof(uint8_t); - case FmtShort: return sizeof(int16_t); - case FmtFloat: return sizeof(float); - case FmtDouble: return sizeof(double); - case FmtMulaw: return sizeof(uint8_t); - case FmtAlaw: return sizeof(uint8_t); - } - return 0; -} -ALuint ChannelsFromFmt(FmtChannels chans, ALuint ambiorder) noexcept -{ - switch(chans) - { - case FmtMono: return 1; - case FmtStereo: return 2; - case FmtRear: return 2; - case FmtQuad: return 4; - case FmtX51: return 6; - case FmtX61: return 7; - case FmtX71: return 8; - case FmtBFormat2D: return (ambiorder*2) + 1; - case FmtBFormat3D: return (ambiorder+1) * (ambiorder+1); - } - return 0; -} - - BufferSubList::~BufferSubList() { uint64_t usemask{~FreeMask}; diff --git a/al/buffer.h b/al/buffer.h index 2e98e927..ab4fa333 100644 --- a/al/buffer.h +++ b/al/buffer.h @@ -8,60 +8,35 @@ #include "albyte.h" #include "almalloc.h" #include "atomic.h" +#include "buffer_formats.h" #include "inprogext.h" #include "vector.h" /* User formats */ enum UserFmtType : unsigned char { - UserFmtUByte, - UserFmtShort, - UserFmtFloat, - UserFmtDouble, - UserFmtMulaw, - UserFmtAlaw, + UserFmtUByte = FmtUByte, + UserFmtShort = FmtShort, + UserFmtFloat = FmtFloat, + UserFmtMulaw = FmtMulaw, + UserFmtAlaw = FmtAlaw, + + UserFmtDouble = 128, UserFmtIMA4, UserFmtMSADPCM, }; enum UserFmtChannels : unsigned char { - UserFmtMono, - UserFmtStereo, - UserFmtRear, - UserFmtQuad, - UserFmtX51, /* (WFX order) */ - UserFmtX61, /* (WFX order) */ - UserFmtX71, /* (WFX order) */ - UserFmtBFormat2D, - UserFmtBFormat3D, -}; - - -/* Storable formats */ -enum FmtType : unsigned char { - FmtUByte = UserFmtUByte, - FmtShort = UserFmtShort, - FmtFloat = UserFmtFloat, - FmtDouble = UserFmtDouble, - FmtMulaw = UserFmtMulaw, - FmtAlaw = UserFmtAlaw, -}; -enum FmtChannels : unsigned char { - FmtMono = UserFmtMono, - FmtStereo = UserFmtStereo, - FmtRear = UserFmtRear, - FmtQuad = UserFmtQuad, - FmtX51 = UserFmtX51, - FmtX61 = UserFmtX61, - FmtX71 = UserFmtX71, - FmtBFormat2D = UserFmtBFormat2D, - FmtBFormat3D = UserFmtBFormat3D, + UserFmtMono = FmtMono, + UserFmtStereo = FmtStereo, + UserFmtRear = FmtRear, + UserFmtQuad = FmtQuad, + UserFmtX51 = FmtX51, + UserFmtX61 = FmtX61, + UserFmtX71 = FmtX71, + UserFmtBFormat2D = FmtBFormat2D, + UserFmtBFormat3D = FmtBFormat3D, }; -ALuint BytesFromFmt(FmtType type) noexcept; -ALuint ChannelsFromFmt(FmtChannels chans, ALuint ambiorder) noexcept; -inline ALuint FrameSizeFromFmt(FmtChannels chans, FmtType type, ALuint ambiorder) noexcept -{ return ChannelsFromFmt(chans, ambiorder) * BytesFromFmt(type); } - struct ALbuffer { al::vector mData; diff --git a/alc/buffer_formats.cpp b/alc/buffer_formats.cpp new file mode 100644 index 00000000..7ee4bfc8 --- /dev/null +++ b/alc/buffer_formats.cpp @@ -0,0 +1,37 @@ + +#include "config.h" + +#include "buffer_formats.h" + +#include + + +ALuint BytesFromFmt(FmtType type) noexcept +{ + switch(type) + { + case FmtUByte: return sizeof(uint8_t); + case FmtShort: return sizeof(int16_t); + case FmtFloat: return sizeof(float); + case FmtDouble: return sizeof(double); + case FmtMulaw: return sizeof(uint8_t); + case FmtAlaw: return sizeof(uint8_t); + } + return 0; +} +ALuint ChannelsFromFmt(FmtChannels chans, ALuint ambiorder) noexcept +{ + switch(chans) + { + case FmtMono: return 1; + case FmtStereo: return 2; + case FmtRear: return 2; + case FmtQuad: return 4; + case FmtX51: return 6; + case FmtX61: return 7; + case FmtX71: return 8; + case FmtBFormat2D: return (ambiorder*2) + 1; + case FmtBFormat3D: return (ambiorder+1) * (ambiorder+1); + } + return 0; +} diff --git a/alc/buffer_formats.h b/alc/buffer_formats.h new file mode 100644 index 00000000..b10cea4c --- /dev/null +++ b/alc/buffer_formats.h @@ -0,0 +1,33 @@ +#ifndef ALC_BUFFER_FORMATS_H +#define ALC_BUFFER_FORMATS_H + +#include "AL/al.h" + + +/* Storable formats */ +enum FmtType : unsigned char { + FmtUByte, + FmtShort, + FmtFloat, + FmtDouble, + FmtMulaw, + FmtAlaw, +}; +enum FmtChannels : unsigned char { + FmtMono, + FmtStereo, + FmtRear, + FmtQuad, + FmtX51, /* (WFX order) */ + FmtX61, /* (WFX order) */ + FmtX71, /* (WFX order) */ + FmtBFormat2D, + FmtBFormat3D, +}; + +ALuint BytesFromFmt(FmtType type) noexcept; +ALuint ChannelsFromFmt(FmtChannels chans, ALuint ambiorder) noexcept; +inline ALuint FrameSizeFromFmt(FmtChannels chans, FmtType type, ALuint ambiorder) noexcept +{ return ChannelsFromFmt(chans, ambiorder) * BytesFromFmt(type); } + +#endif /* ALC_BUFFER_FORMATS_H */ -- cgit v1.2.3 From 1a9fbc1b2f7456f14e9cb74d95f4696cbe5c5af3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 24 Aug 2020 20:04:16 -0700 Subject: Stub out a convolution effect state --- CMakeLists.txt | 1 + al/effect.cpp | 3 +- alc/effects/base.h | 3 +- alc/effects/convolution.cpp | 155 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 alc/effects/convolution.cpp (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 803f34de..f77f5a24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -629,6 +629,7 @@ set(ALC_OBJS alc/effects/autowah.cpp alc/effects/chorus.cpp alc/effects/compressor.cpp + alc/effects/convolution.cpp alc/effects/dedicated.cpp alc/effects/distortion.cpp alc/effects/echo.cpp diff --git a/al/effect.cpp b/al/effect.cpp index fa39528b..350d0e69 100644 --- a/al/effect.cpp +++ b/al/effect.cpp @@ -101,7 +101,8 @@ constexpr struct FactoryItem { { AL_EFFECT_PITCH_SHIFTER, PshifterStateFactory_getFactory}, { AL_EFFECT_VOCAL_MORPHER, VmorpherStateFactory_getFactory}, { AL_EFFECT_DEDICATED_DIALOGUE, DedicatedStateFactory_getFactory }, - { AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, DedicatedStateFactory_getFactory } + { AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, DedicatedStateFactory_getFactory }, + { AL_EFFECT_CONVOLUTION_REVERB_SOFT, ConvolutionStateFactory_getFactory } }; diff --git a/alc/effects/base.h b/alc/effects/base.h index f4ca6df2..6aa0f9e9 100644 --- a/alc/effects/base.h +++ b/alc/effects/base.h @@ -176,7 +176,7 @@ struct EffectState : public al::intrusive_ref { * detached and deleted or altered during a mix. */ virtual EffectBufferBase *createBuffer(const ALCdevice */*device*/, - const al::byte */*samplesData*/, ALuint /*sampleRate*/, FmtType /*sampleType*/, + const al::byte */*sampleData*/, ALuint /*sampleRate*/, FmtType /*sampleType*/, FmtChannels /*channelType*/, ALuint /*numSamples*/) { return nullptr; } virtual void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) = 0; @@ -210,5 +210,6 @@ EffectStateFactory* VmorpherStateFactory_getFactory(void); EffectStateFactory *DedicatedStateFactory_getFactory(void); +EffectStateFactory *ConvolutionStateFactory_getFactory(void); #endif /* EFFECTS_BASE_H */ diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp new file mode 100644 index 00000000..56a09e5d --- /dev/null +++ b/alc/effects/convolution.cpp @@ -0,0 +1,155 @@ + +#include "config.h" + +#include "AL/al.h" +#include "AL/alc.h" + +#include "al/auxeffectslot.h" +#include "alcmain.h" +#include "alcontext.h" +#include "almalloc.h" +#include "alspan.h" +#include "effects/base.h" + + +namespace { + +struct ConvolutionState final : public EffectState { + ConvolutionState() = default; + ~ConvolutionState() override = default; + + void deviceUpdate(const ALCdevice *device) override; + EffectBufferBase *createBuffer(const ALCdevice *device, const al::byte *sampleData, + ALuint sampleRate, FmtType sampleType, FmtChannels channelType, ALuint numSamples) override; + void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override; + void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; + + DEF_NEWDEL(ConvolutionState) +}; + +void ConvolutionState::deviceUpdate(const ALCdevice* /*device*/) +{ +} + +EffectBufferBase *ConvolutionState::createBuffer(const ALCdevice */*device*/, + const al::byte */*samplesData*/, ALuint /*sampleRate*/, FmtType /*sampleType*/, + FmtChannels /*channelType*/, ALuint /*numSamples*/) +{ + return nullptr; +} + +void ConvolutionState::update(const ALCcontext* /*context*/, const ALeffectslot* /*slot*/, + const EffectProps* /*props*/, const EffectTarget /*target*/) +{ +} + +void ConvolutionState::process(const size_t/*samplesToDo*/, + const al::span /*samplesIn*/, + const al::span /*samplesOut*/) +{ +} + + +void ConvolutionEffect_setParami(EffectProps* /*props*/, ALenum param, int /*val*/) +{ + switch(param) + { + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", + param}; + } +} +void ConvolutionEffect_setParamiv(EffectProps *props, ALenum param, const int *vals) +{ + switch(param) + { + default: + ConvolutionEffect_setParami(props, param, vals[0]); + } +} +void ConvolutionEffect_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/) +{ + switch(param) + { + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", + param}; + } +} +void ConvolutionEffect_setParamfv(EffectProps *props, ALenum param, const float *vals) +{ + switch(param) + { + default: + ConvolutionEffect_setParamf(props, param, vals[0]); + } +} + +void ConvolutionEffect_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/) +{ + switch(param) + { + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", + param}; + } +} +void ConvolutionEffect_getParamiv(const EffectProps *props, ALenum param, int *vals) +{ + switch(param) + { + default: + ConvolutionEffect_getParami(props, param, vals); + } +} +void ConvolutionEffect_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/) +{ + switch(param) + { + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", + param}; + } +} +void ConvolutionEffect_getParamfv(const EffectProps *props, ALenum param, float *vals) +{ + switch(param) + { + default: + ConvolutionEffect_getParamf(props, param, vals); + } +} + +DEFINE_ALEFFECT_VTABLE(ConvolutionEffect); + + +struct ConvolutionStateFactory final : public EffectStateFactory { + EffectState *create() override; + EffectProps getDefaultProps() const noexcept override; + const EffectVtable *getEffectVtable() const noexcept override; +}; + +/* Creates EffectState objects of the appropriate type. */ +EffectState *ConvolutionStateFactory::create() +{ return new ConvolutionState{}; } + +/* Returns an ALeffectProps initialized with this effect type's default + * property values. + */ +EffectProps ConvolutionStateFactory::getDefaultProps() const noexcept +{ + EffectProps props{}; + return props; +} + +/* Returns a pointer to this effect type's global set/get vtable. */ +const EffectVtable *ConvolutionStateFactory::getEffectVtable() const noexcept +{ return &ConvolutionEffect_vtable; } + +} // namespace + +EffectStateFactory *ConvolutionStateFactory_getFactory() +{ + static ConvolutionStateFactory ConvolutionFactory{}; + return &ConvolutionFactory; +} -- cgit v1.2.3 From 309be1c6f6bc6364d758712c29bb2ccbb1cc3511 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 25 Aug 2020 04:59:04 -0700 Subject: Add an example using convolution reverb --- CMakeLists.txt | 4 + examples/alconvolve.cpp | 536 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 540 insertions(+) create mode 100644 examples/alconvolve.cpp (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index f77f5a24..092c041d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1508,6 +1508,10 @@ if(ALSOFT_EXAMPLES) target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${UNICODE_FLAG}) + add_executable(alconvolve examples/alconvolve.cpp) + target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} common SndFile::SndFile ex-common + ${UNICODE_FLAG}) + if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alplay alstream alreverb almultireverb allatency alhrtf) diff --git a/examples/alconvolve.cpp b/examples/alconvolve.cpp new file mode 100644 index 00000000..68ab5615 --- /dev/null +++ b/examples/alconvolve.cpp @@ -0,0 +1,536 @@ +/* + * OpenAL Convolution Reverb Example + * + * Copyright (c) 2020 by Chris Robinson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* This file contains a streaming audio player, using the convolution reverb + * effect. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sndfile.h" + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + +#include "common/alhelpers.h" + + +#ifndef AL_SOFT_callback_buffer +#define AL_SOFT_callback_buffer +typedef unsigned int ALbitfieldSOFT; +#define AL_BUFFER_CALLBACK_FUNCTION_SOFT 0x19A0 +#define AL_BUFFER_CALLBACK_USER_PARAM_SOFT 0x19A1 +typedef ALsizei (AL_APIENTRY*LPALBUFFERCALLBACKTYPESOFT)(ALvoid *userptr, ALvoid *sampledata, ALsizei numsamples); +typedef void (AL_APIENTRY*LPALBUFFERCALLBACKSOFT)(ALuint buffer, ALenum format, ALsizei freq, LPALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr, ALbitfieldSOFT flags); +typedef void (AL_APIENTRY*LPALGETBUFFERPTRSOFT)(ALuint buffer, ALenum param, ALvoid **value); +typedef void (AL_APIENTRY*LPALGETBUFFER3PTRSOFT)(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3); +typedef void (AL_APIENTRY*LPALGETBUFFERPTRVSOFT)(ALuint buffer, ALenum param, ALvoid **values); +#endif + +#ifndef AL_SOFT_convolution_reverb +#define AL_SOFT_convolution_reverb +#define AL_EFFECT_CONVOLUTION_REVERB_SOFT 0xA000 +#endif + + +namespace { + +/* Effect object functions */ +LPALGENEFFECTS alGenEffects; +LPALDELETEEFFECTS alDeleteEffects; +LPALISEFFECT alIsEffect; +LPALEFFECTI alEffecti; +LPALEFFECTIV alEffectiv; +LPALEFFECTF alEffectf; +LPALEFFECTFV alEffectfv; +LPALGETEFFECTI alGetEffecti; +LPALGETEFFECTIV alGetEffectiv; +LPALGETEFFECTF alGetEffectf; +LPALGETEFFECTFV alGetEffectfv; + +/* Auxiliary Effect Slot object functions */ +LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots; +LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots; +LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot; +LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti; +LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv; +LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf; +LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv; +LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti; +LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv; +LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf; +LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; + + +ALuint CreateEffect() +{ + /* Create the effect object and try to set convolution reverb. */ + ALuint effect{0}; + alGenEffects(1, &effect); + + printf("Using Convolution Reverb\n"); + + alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_CONVOLUTION_REVERB_SOFT); + + /* Check if an error occured, and clean up if so. */ + if(ALenum err{alGetError()}) + { + fprintf(stderr, "OpenAL error: %s\n", alGetString(err)); + if(alIsEffect(effect)) + alDeleteEffects(1, &effect); + return 0; + } + + return effect; +} + + +ALuint LoadSound(const char *filename) +{ + /* Open the audio file and check that it's usable. */ + SF_INFO sfinfo{}; + SNDFILE *sndfile{sf_open(filename, SFM_READ, &sfinfo)}; + if(!sndfile) + { + fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(sndfile)); + return 0; + } + constexpr sf_count_t max_samples{std::numeric_limits::max() / sizeof(float)}; + if(sfinfo.frames < 1 || sfinfo.frames > max_samples/sfinfo.channels) + { + fprintf(stderr, "Bad sample count in %s (%" PRId64 ")\n", filename, sfinfo.frames); + sf_close(sndfile); + return 0; + } + + /* Get the sound format, and figure out the OpenAL format. Use a float + * format since impulse responses are keen on having a low noise floor. + */ + ALenum format{}; + if(sfinfo.channels == 1) + format = AL_FORMAT_MONO_FLOAT32; + else if(sfinfo.channels == 2) + format = AL_FORMAT_STEREO_FLOAT32; + else + { + fprintf(stderr, "Unsupported channel count: %d\n", sfinfo.channels); + sf_close(sndfile); + return 0; + } + + auto membuf = std::make_unique(static_cast(sfinfo.frames * sfinfo.channels)); + + sf_count_t num_frames{sf_readf_float(sndfile, membuf.get(), sfinfo.frames)}; + if(num_frames < 1) + { + membuf = nullptr; + sf_close(sndfile); + fprintf(stderr, "Failed to read samples in %s (%" PRId64 ")\n", filename, num_frames); + return 0; + } + const auto num_bytes = static_cast(num_frames * sfinfo.channels) * + ALsizei{sizeof(float)}; + + ALuint buffer{0}; + alGenBuffers(1, &buffer); + alBufferData(buffer, format, membuf.get(), num_bytes, sfinfo.samplerate); + + membuf = nullptr; + sf_close(sndfile); + + if(ALenum err{alGetError()}) + { + fprintf(stderr, "OpenAL Error: %s\n", alGetString(err)); + if(buffer && alIsBuffer(buffer)) + alDeleteBuffers(1, &buffer); + return 0; + } + + return buffer; +} + + +/* This is largely the same as in alstreamcb.cpp. Comments removed for brevity, + * see the aforementioned source for more details. + */ +using std::chrono::seconds; +using std::chrono::nanoseconds; + +LPALBUFFERCALLBACKSOFT alBufferCallbackSOFT; + +struct StreamPlayer { + std::unique_ptr mBufferData; + size_t mBufferDataSize{0}; + std::atomic mReadPos{0}; + std::atomic mWritePos{0}; + + ALuint mBuffer{0}, mSource{0}; + size_t mStartOffset{0}; + + SNDFILE *mSndfile{nullptr}; + SF_INFO mSfInfo{}; + size_t mDecoderOffset{0}; + + ALenum mFormat; + + StreamPlayer() + { + alGenBuffers(1, &mBuffer); + if(ALenum err{alGetError()}) + throw std::runtime_error{"alGenBuffers failed"}; + alGenSources(1, &mSource); + if(ALenum err{alGetError()}) + { + alDeleteBuffers(1, &mBuffer); + throw std::runtime_error{"alGenSources failed"}; + } + } + ~StreamPlayer() + { + alDeleteSources(1, &mSource); + alDeleteBuffers(1, &mBuffer); + if(mSndfile) + sf_close(mSndfile); + } + + void close() + { + if(mSndfile) + { + alSourceRewind(mSource); + alSourcei(mSource, AL_BUFFER, 0); + sf_close(mSndfile); + mSndfile = nullptr; + } + } + + bool open(const char *filename) + { + close(); + + mSndfile = sf_open(filename, SFM_READ, &mSfInfo); + if(!mSndfile) + { + fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(mSndfile)); + return false; + } + + mFormat = AL_NONE; + if(mSfInfo.channels == 1) + mFormat = AL_FORMAT_MONO_FLOAT32; + else if(mSfInfo.channels == 2) + mFormat = AL_FORMAT_STEREO_FLOAT32; + else if(mSfInfo.channels == 6) + mFormat = AL_FORMAT_51CHN32; + else + { + fprintf(stderr, "Unsupported channel count: %d\n", mSfInfo.channels); + sf_close(mSndfile); + mSndfile = nullptr; + + return false; + } + + mBufferDataSize = static_cast(mSfInfo.samplerate*mSfInfo.channels) * sizeof(float); + mBufferData.reset(new ALbyte[mBufferDataSize]); + mReadPos.store(0, std::memory_order_relaxed); + mWritePos.store(0, std::memory_order_relaxed); + mDecoderOffset = 0; + + return true; + } + + static ALsizei AL_APIENTRY bufferCallbackC(void *userptr, void *data, ALsizei size) + { return static_cast(userptr)->bufferCallback(data, size); } + ALsizei bufferCallback(void *data, ALsizei size) + { + ALsizei got{0}; + + size_t roffset{mReadPos.load(std::memory_order_acquire)}; + while(got < size) + { + const size_t woffset{mWritePos.load(std::memory_order_relaxed)}; + if(woffset == roffset) break; + + size_t todo{((woffset < roffset) ? mBufferDataSize : woffset) - roffset}; + todo = std::min(todo, static_cast(size-got)); + + memcpy(data, &mBufferData[roffset], todo); + data = static_cast(data) + todo; + got += static_cast(todo); + + roffset += todo; + if(roffset == mBufferDataSize) + roffset = 0; + } + mReadPos.store(roffset, std::memory_order_release); + + return got; + } + + bool prepare() + { + alBufferCallbackSOFT(mBuffer, mFormat, mSfInfo.samplerate, bufferCallbackC, this, 0); + alSourcei(mSource, AL_BUFFER, static_cast(mBuffer)); + if(ALenum err{alGetError()}) + { + fprintf(stderr, "Failed to set callback: %s (0x%04x)\n", alGetString(err), err); + return false; + } + return true; + } + + bool update() + { + ALenum state; + ALint pos; + alGetSourcei(mSource, AL_SAMPLE_OFFSET, &pos); + alGetSourcei(mSource, AL_SOURCE_STATE, &state); + + const size_t frame_size{static_cast(mSfInfo.channels) * sizeof(float)}; + size_t woffset{mWritePos.load(std::memory_order_acquire)}; + if(state != AL_INITIAL) + { + const size_t roffset{mReadPos.load(std::memory_order_relaxed)}; + const size_t readable{((woffset >= roffset) ? woffset : (mBufferDataSize+woffset)) - + roffset}; + const size_t curtime{((state==AL_STOPPED) ? (mDecoderOffset-readable) / frame_size + : (static_cast(pos) + mStartOffset/frame_size)) + / static_cast(mSfInfo.samplerate)}; + printf("\r%3zus (%3zu%% full)", curtime, readable * 100 / mBufferDataSize); + } + else + fputs("Starting...", stdout); + fflush(stdout); + + while(!sf_error(mSndfile)) + { + size_t read_bytes; + const size_t roffset{mReadPos.load(std::memory_order_relaxed)}; + if(roffset > woffset) + { + const size_t writable{roffset-woffset-1}; + if(writable < frame_size) break; + + sf_count_t num_frames{sf_readf_float(mSndfile, + reinterpret_cast(&mBufferData[woffset]), + static_cast(writable/frame_size))}; + if(num_frames < 1) break; + + read_bytes = static_cast(num_frames) * frame_size; + woffset += read_bytes; + } + else + { + const size_t writable{!roffset ? mBufferDataSize-woffset-1 : + (mBufferDataSize-woffset)}; + if(writable < frame_size) break; + + sf_count_t num_frames{sf_readf_float(mSndfile, + reinterpret_cast(&mBufferData[woffset]), + static_cast(writable/frame_size))}; + if(num_frames < 1) break; + + read_bytes = static_cast(num_frames) * frame_size; + woffset += read_bytes; + if(woffset == mBufferDataSize) + woffset = 0; + } + mWritePos.store(woffset, std::memory_order_release); + mDecoderOffset += read_bytes; + } + + if(state != AL_PLAYING && state != AL_PAUSED) + { + const size_t roffset{mReadPos.load(std::memory_order_relaxed)}; + const size_t readable{((woffset >= roffset) ? woffset : (mBufferDataSize+woffset)) - + roffset}; + if(readable == 0) + return false; + + mStartOffset = mDecoderOffset - readable; + alSourcePlay(mSource); + if(alGetError() != AL_NO_ERROR) + return false; + } + return true; + } +}; + +} // namespace + +int main(int argc, char **argv) +{ + /* A simple RAII container for OpenAL startup and shutdown. */ + struct AudioManager { + AudioManager(char ***argv_, int *argc_) + { + if(InitAL(argv_, argc_) != 0) + throw std::runtime_error{"Failed to initialize OpenAL"}; + } + ~AudioManager() { CloseAL(); } + }; + + /* Print out usage if no arguments were specified */ + if(argc < 2) + { + fprintf(stderr, "Usage: %s [-device ] [sound files...]\n", + argv[0]); + return 1; + } + + argv++; argc--; + AudioManager almgr{&argv, &argc}; + + if(!alIsExtensionPresent("AL_SOFTX_callback_buffer")) + { + fprintf(stderr, "AL_SOFT_callback_buffer extension not available\n"); + return 1; + } + + /* Define a macro to help load the function pointers. */ +#define LOAD_PROC(T, x) ((x) = reinterpret_cast(alGetProcAddress(#x))) + LOAD_PROC(LPALBUFFERCALLBACKSOFT, alBufferCallbackSOFT); + + LOAD_PROC(LPALGENEFFECTS, alGenEffects); + LOAD_PROC(LPALDELETEEFFECTS, alDeleteEffects); + LOAD_PROC(LPALISEFFECT, alIsEffect); + LOAD_PROC(LPALEFFECTI, alEffecti); + LOAD_PROC(LPALEFFECTIV, alEffectiv); + LOAD_PROC(LPALEFFECTF, alEffectf); + LOAD_PROC(LPALEFFECTFV, alEffectfv); + LOAD_PROC(LPALGETEFFECTI, alGetEffecti); + LOAD_PROC(LPALGETEFFECTIV, alGetEffectiv); + LOAD_PROC(LPALGETEFFECTF, alGetEffectf); + LOAD_PROC(LPALGETEFFECTFV, alGetEffectfv); + + LOAD_PROC(LPALGENAUXILIARYEFFECTSLOTS, alGenAuxiliaryEffectSlots); + LOAD_PROC(LPALDELETEAUXILIARYEFFECTSLOTS, alDeleteAuxiliaryEffectSlots); + LOAD_PROC(LPALISAUXILIARYEFFECTSLOT, alIsAuxiliaryEffectSlot); + LOAD_PROC(LPALAUXILIARYEFFECTSLOTI, alAuxiliaryEffectSloti); + LOAD_PROC(LPALAUXILIARYEFFECTSLOTIV, alAuxiliaryEffectSlotiv); + LOAD_PROC(LPALAUXILIARYEFFECTSLOTF, alAuxiliaryEffectSlotf); + LOAD_PROC(LPALAUXILIARYEFFECTSLOTFV, alAuxiliaryEffectSlotfv); + LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTI, alGetAuxiliaryEffectSloti); + LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTIV, alGetAuxiliaryEffectSlotiv); + LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTF, alGetAuxiliaryEffectSlotf); + LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv); +#undef LOAD_PROC + + /* Load the impulse response sound file into a buffer. */ + ALuint buffer{LoadSound(argv[0])}; + if(!buffer) return 1; + + /* Create the convolution reverb effect. */ + ALuint effect{CreateEffect()}; + if(!effect) + { + alDeleteBuffers(1, &buffer); + return 1; + } + + /* Create the effect slot object. This is what "plays" an effect on sources + * that connect to it. */ + ALuint slot{0}; + alGenAuxiliaryEffectSlots(1, &slot); + + /* Set the impulse response sound buffer on the effect slot. This allows + * effects to access it as needed. In this case, convolution reverb uses it + * as the filter source. NOTE: Unlike the effect object, the buffer *is* + * kept referenced and may not be changed or deleted as long as it's set, + * just like with a source. When another buffer is set, or the effect slot + * is deleted, the buffer reference is released. + * + * The effect slot's gain is reduced because the impulse responses I've + * tested with result in excessively loud reverb. Is that normal? Even with + * this, it seems a bit on the loud side. + * + * Also note: unlike standard or EAX reverb, there is no automatic + * attenuation of a source's reverb response with distance, so the reverb + * will remain full volume regardless of a given sound's distance from the + * listener. You can use a send filter to alter a given source's + * contribution to reverb. + */ + alAuxiliaryEffectSloti(slot, AL_BUFFER, static_cast(buffer)); + alAuxiliaryEffectSlotf(slot, AL_EFFECTSLOT_GAIN, 1.0f / 16.0f); + alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, static_cast(effect)); + assert(alGetError()==AL_NO_ERROR && "Failed to set effect slot"); + + ALCint refresh{25}; + alcGetIntegerv(alcGetContextsDevice(alcGetCurrentContext()), ALC_REFRESH, 1, &refresh); + + std::unique_ptr player{new StreamPlayer{}}; + alSource3i(player->mSource, AL_AUXILIARY_SEND_FILTER, static_cast(slot), 0, + AL_FILTER_NULL); + + for(int i{1};i < argc;++i) + { + if(!player->open(argv[i])) + continue; + + const char *namepart{strrchr(argv[i], '/')}; + if(namepart || (namepart=strrchr(argv[i], '\\'))) + ++namepart; + else + namepart = argv[i]; + + printf("Playing: %s (%s, %dhz)\n", namepart, FormatName(player->mFormat), + player->mSfInfo.samplerate); + fflush(stdout); + + if(!player->prepare()) + { + player->close(); + continue; + } + + while(player->update()) + std::this_thread::sleep_for(nanoseconds{seconds{1}} / refresh); + putc('\n', stdout); + + player->close(); + } + /* All done. */ + printf("Done.\n"); + + player = nullptr; + alDeleteAuxiliaryEffectSlots(1, &slot); + alDeleteEffects(1, &effect); + alDeleteBuffers(1, &buffer); + + return 0; +} -- cgit v1.2.3 From 97ecf5810fb4b978db96dd607fb723d5e19cd90e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 26 Aug 2020 17:16:36 -0700 Subject: Base the convolution example on the simpler stream example --- CMakeLists.txt | 2 +- examples/alconvolve.c | 515 ++++++++++++++++++++++++++++++++++++++++++++++ examples/alconvolve.cpp | 536 ------------------------------------------------ 3 files changed, 516 insertions(+), 537 deletions(-) create mode 100644 examples/alconvolve.c delete mode 100644 examples/alconvolve.cpp (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 092c041d..55ccec6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1508,7 +1508,7 @@ if(ALSOFT_EXAMPLES) target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${UNICODE_FLAG}) - add_executable(alconvolve examples/alconvolve.cpp) + add_executable(alconvolve examples/alconvolve.c) target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} common SndFile::SndFile ex-common ${UNICODE_FLAG}) diff --git a/examples/alconvolve.c b/examples/alconvolve.c new file mode 100644 index 00000000..77ef83bf --- /dev/null +++ b/examples/alconvolve.c @@ -0,0 +1,515 @@ +/* + * OpenAL Convolution Reverb Example + * + * Copyright (c) 2020 by Chris Robinson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* This file contains an example for applying convolution reverb to a source. */ + +#include +#include +#include +#include +#include +#include + +#include "sndfile.h" + +#include "AL/al.h" +#include "AL/alext.h" + +#include "common/alhelpers.h" + + +#ifndef AL_SOFT_convolution_reverb +#define AL_SOFT_convolution_reverb +#define AL_EFFECT_CONVOLUTION_REVERB_SOFT 0xA000 +#endif + + +/* Effect object functions */ +static LPALGENEFFECTS alGenEffects; +static LPALDELETEEFFECTS alDeleteEffects; +static LPALISEFFECT alIsEffect; +static LPALEFFECTI alEffecti; +static LPALEFFECTIV alEffectiv; +static LPALEFFECTF alEffectf; +static LPALEFFECTFV alEffectfv; +static LPALGETEFFECTI alGetEffecti; +static LPALGETEFFECTIV alGetEffectiv; +static LPALGETEFFECTF alGetEffectf; +static LPALGETEFFECTFV alGetEffectfv; + +/* Auxiliary Effect Slot object functions */ +static LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots; +static LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots; +static LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot; +static LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti; +static LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv; +static LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf; +static LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv; +static LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti; +static LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv; +static LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf; +static LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; + + +/* This stuff defines a simple streaming player object, the same as alstream.c. + * Comments are removed for brevity, see alstream.c for more details. + */ +#define NUM_BUFFERS 4 +#define BUFFER_SAMPLES 8192 + +typedef struct StreamPlayer { + ALuint buffers[NUM_BUFFERS]; + ALuint source; + + SNDFILE *sndfile; + SF_INFO sfinfo; + float *membuf; + + ALenum format; +} StreamPlayer; + +static StreamPlayer *NewPlayer(void) +{ + StreamPlayer *player; + + player = calloc(1, sizeof(*player)); + assert(player != NULL); + + alGenBuffers(NUM_BUFFERS, player->buffers); + assert(alGetError() == AL_NO_ERROR && "Could not create buffers"); + + alGenSources(1, &player->source); + assert(alGetError() == AL_NO_ERROR && "Could not create source"); + + alSource3i(player->source, AL_POSITION, 0, 0, -1); + alSourcei(player->source, AL_SOURCE_RELATIVE, AL_TRUE); + alSourcei(player->source, AL_ROLLOFF_FACTOR, 0); + assert(alGetError() == AL_NO_ERROR && "Could not set source parameters"); + + return player; +} + +static void ClosePlayerFile(StreamPlayer *player) +{ + if(player->sndfile) + sf_close(player->sndfile); + player->sndfile = NULL; + + free(player->membuf); + player->membuf = NULL; +} + +static void DeletePlayer(StreamPlayer *player) +{ + ClosePlayerFile(player); + + alDeleteSources(1, &player->source); + alDeleteBuffers(NUM_BUFFERS, player->buffers); + if(alGetError() != AL_NO_ERROR) + fprintf(stderr, "Failed to delete object IDs\n"); + + memset(player, 0, sizeof(*player)); + free(player); +} + +static int OpenPlayerFile(StreamPlayer *player, const char *filename) +{ + size_t frame_size; + + ClosePlayerFile(player); + + player->sndfile = sf_open(filename, SFM_READ, &player->sfinfo); + if(!player->sndfile) + { + fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(NULL)); + return 0; + } + + if(player->sfinfo.channels == 1) + player->format = AL_FORMAT_MONO_FLOAT32; + else if(player->sfinfo.channels == 2) + player->format = AL_FORMAT_STEREO_FLOAT32; + else if(player->sfinfo.channels == 6) + player->format = AL_FORMAT_51CHN32; + else + { + fprintf(stderr, "Unsupported channel count: %d\n", player->sfinfo.channels); + sf_close(player->sndfile); + player->sndfile = NULL; + return 0; + } + + frame_size = (size_t)(BUFFER_SAMPLES * player->sfinfo.channels) * sizeof(float); + player->membuf = malloc(frame_size); + + return 1; +} + +static int StartPlayer(StreamPlayer *player) +{ + ALsizei i; + + alSourceRewind(player->source); + alSourcei(player->source, AL_BUFFER, 0); + + for(i = 0;i < NUM_BUFFERS;i++) + { + sf_count_t slen = sf_readf_float(player->sndfile, player->membuf, BUFFER_SAMPLES); + if(slen < 1) break; + + slen *= player->sfinfo.channels * (sf_count_t)sizeof(float); + alBufferData(player->buffers[i], player->format, player->membuf, (ALsizei)slen, + player->sfinfo.samplerate); + } + if(alGetError() != AL_NO_ERROR) + { + fprintf(stderr, "Error buffering for playback\n"); + return 0; + } + + alSourceQueueBuffers(player->source, i, player->buffers); + alSourcePlay(player->source); + if(alGetError() != AL_NO_ERROR) + { + fprintf(stderr, "Error starting playback\n"); + return 0; + } + + return 1; +} + +static int UpdatePlayer(StreamPlayer *player) +{ + ALint processed, state; + + alGetSourcei(player->source, AL_SOURCE_STATE, &state); + alGetSourcei(player->source, AL_BUFFERS_PROCESSED, &processed); + if(alGetError() != AL_NO_ERROR) + { + fprintf(stderr, "Error checking source state\n"); + return 0; + } + + while(processed > 0) + { + ALuint bufid; + sf_count_t slen; + + alSourceUnqueueBuffers(player->source, 1, &bufid); + processed--; + + slen = sf_readf_float(player->sndfile, player->membuf, BUFFER_SAMPLES); + if(slen > 0) + { + slen *= player->sfinfo.channels * (sf_count_t)sizeof(float); + alBufferData(bufid, player->format, player->membuf, (ALsizei)slen, + player->sfinfo.samplerate); + alSourceQueueBuffers(player->source, 1, &bufid); + } + if(alGetError() != AL_NO_ERROR) + { + fprintf(stderr, "Error buffering data\n"); + return 0; + } + } + + if(state != AL_PLAYING && state != AL_PAUSED) + { + ALint queued; + + alGetSourcei(player->source, AL_BUFFERS_QUEUED, &queued); + if(queued == 0) + return 0; + + alSourcePlay(player->source); + if(alGetError() != AL_NO_ERROR) + { + fprintf(stderr, "Error restarting playback\n"); + return 0; + } + } + + return 1; +} + + +/* CreateEffect creates a new OpenAL effect object with a convolution reverb + * type, and returns the new effect ID. + */ +static ALuint CreateEffect(void) +{ + ALuint effect = 0; + ALenum err; + + printf("Using Convolution Reverb\n"); + + /* Create the effect object and set the convolution reverb effect type. */ + alGenEffects(1, &effect); + alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_CONVOLUTION_REVERB_SOFT); + + /* Check if an error occured, and clean up if so. */ + err = alGetError(); + if(err != AL_NO_ERROR) + { + fprintf(stderr, "OpenAL error: %s\n", alGetString(err)); + if(alIsEffect(effect)) + alDeleteEffects(1, &effect); + return 0; + } + + return effect; +} + +/* LoadBuffer loads the named audio file into an OpenAL buffer object, and + * returns the new buffer ID. + */ +static ALuint LoadSound(const char *filename) +{ + ALenum err, format; + ALuint buffer; + SNDFILE *sndfile; + SF_INFO sfinfo; + float *membuf; + sf_count_t num_frames; + ALsizei num_bytes; + + /* Open the audio file and check that it's usable. */ + sndfile = sf_open(filename, SFM_READ, &sfinfo); + if(!sndfile) + { + fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(sndfile)); + return 0; + } + if(sfinfo.frames < 1 || sfinfo.frames > (sf_count_t)(INT_MAX/sizeof(float))/sfinfo.channels) + { + fprintf(stderr, "Bad sample count in %s (%" PRId64 ")\n", filename, sfinfo.frames); + sf_close(sndfile); + return 0; + } + + /* Get the sound format, and figure out the OpenAL format. Use floats since + * impulse responses will usually have more than 16-bit precision. + */ + if(sfinfo.channels == 1) + format = AL_FORMAT_MONO_FLOAT32; + else if(sfinfo.channels == 2) + format = AL_FORMAT_STEREO_FLOAT32; + else + { + fprintf(stderr, "Unsupported channel count: %d\n", sfinfo.channels); + sf_close(sndfile); + return 0; + } + + /* Decode the whole audio file to a buffer. */ + membuf = malloc((size_t)(sfinfo.frames * sfinfo.channels) * sizeof(float)); + + num_frames = sf_readf_float(sndfile, membuf, sfinfo.frames); + if(num_frames < 1) + { + free(membuf); + sf_close(sndfile); + fprintf(stderr, "Failed to read samples in %s (%" PRId64 ")\n", filename, num_frames); + return 0; + } + num_bytes = (ALsizei)(num_frames * sfinfo.channels) * (ALsizei)sizeof(float); + + /* Buffer the audio data into a new buffer object, then free the data and + * close the file. + */ + buffer = 0; + alGenBuffers(1, &buffer); + alBufferData(buffer, format, membuf, num_bytes, sfinfo.samplerate); + + free(membuf); + sf_close(sndfile); + + /* Check if an error occured, and clean up if so. */ + err = alGetError(); + if(err != AL_NO_ERROR) + { + fprintf(stderr, "OpenAL Error: %s\n", alGetString(err)); + if(buffer && alIsBuffer(buffer)) + alDeleteBuffers(1, &buffer); + return 0; + } + + return buffer; +} + + +int main(int argc, char **argv) +{ + ALuint ir_buffer, effect, slot; + StreamPlayer *player; + int i; + + /* Print out usage if no arguments were specified */ + if(argc < 2) + { + fprintf(stderr, "Usage: %s [-device ] \n", + argv[0]); + return 1; + } + + argv++; argc--; + if(InitAL(&argv, &argc) != 0) + return 1; + + if(!alcIsExtensionPresent(alcGetContextsDevice(alcGetCurrentContext()), "ALC_EXT_EFX")) + { + CloseAL(); + fprintf(stderr, "Error: EFX not supported\n"); + return 1; + } + + if(argc < 2) + { + CloseAL(); + fprintf(stderr, "Error: Missing impulse response or sound files\n"); + return 1; + } + + /* Define a macro to help load the function pointers. */ +#define LOAD_PROC(T, x) ((x) = (T)alGetProcAddress(#x)) + LOAD_PROC(LPALGENEFFECTS, alGenEffects); + LOAD_PROC(LPALDELETEEFFECTS, alDeleteEffects); + LOAD_PROC(LPALISEFFECT, alIsEffect); + LOAD_PROC(LPALEFFECTI, alEffecti); + LOAD_PROC(LPALEFFECTIV, alEffectiv); + LOAD_PROC(LPALEFFECTF, alEffectf); + LOAD_PROC(LPALEFFECTFV, alEffectfv); + LOAD_PROC(LPALGETEFFECTI, alGetEffecti); + LOAD_PROC(LPALGETEFFECTIV, alGetEffectiv); + LOAD_PROC(LPALGETEFFECTF, alGetEffectf); + LOAD_PROC(LPALGETEFFECTFV, alGetEffectfv); + + LOAD_PROC(LPALGENAUXILIARYEFFECTSLOTS, alGenAuxiliaryEffectSlots); + LOAD_PROC(LPALDELETEAUXILIARYEFFECTSLOTS, alDeleteAuxiliaryEffectSlots); + LOAD_PROC(LPALISAUXILIARYEFFECTSLOT, alIsAuxiliaryEffectSlot); + LOAD_PROC(LPALAUXILIARYEFFECTSLOTI, alAuxiliaryEffectSloti); + LOAD_PROC(LPALAUXILIARYEFFECTSLOTIV, alAuxiliaryEffectSlotiv); + LOAD_PROC(LPALAUXILIARYEFFECTSLOTF, alAuxiliaryEffectSlotf); + LOAD_PROC(LPALAUXILIARYEFFECTSLOTFV, alAuxiliaryEffectSlotfv); + LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTI, alGetAuxiliaryEffectSloti); + LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTIV, alGetAuxiliaryEffectSlotiv); + LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTF, alGetAuxiliaryEffectSlotf); + LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv); +#undef LOAD_PROC + + /* Load the impulse response sound into a buffer. */ + ir_buffer = LoadSound(argv[0]); + if(!ir_buffer) + { + CloseAL(); + return 1; + } + + /* Load the reverb into an effect. */ + effect = CreateEffect(); + if(!effect) + { + alDeleteBuffers(1, &ir_buffer); + CloseAL(); + return 1; + } + + /* Create the effect slot object. This is what "plays" an effect on sources + * that connect to it. + */ + slot = 0; + alGenAuxiliaryEffectSlots(1, &slot); + + /* Set the impulse response sound buffer on the effect slot. This allows + * effects to access it as needed. In this case, convolution reverb uses it + * as the filter source. NOTE: Unlike the effect object, the buffer *is* + * kept referenced and may not be changed or deleted as long as it's set, + * just like with a source. When another buffer is set, or the effect slot + * is deleted, the buffer reference is released. + * + * The effect slot's gain is reduced because the impulse responses I've + * tested with result in excessively loud reverb. Is that normal? Even with + * this, it seems a bit on the loud side. + * + * Also note: unlike standard or EAX reverb, there is no automatic + * attenuation of a source's reverb response with distance, so the reverb + * will remain full volume regardless of a given sound's distance from the + * listener. You can use a send filter to alter a given source's + * contribution to reverb. + */ + alAuxiliaryEffectSloti(slot, AL_BUFFER, (ALint)ir_buffer); + alAuxiliaryEffectSlotf(slot, AL_EFFECTSLOT_GAIN, 1.0f / 16.0f); + alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, (ALint)effect); + assert(alGetError()==AL_NO_ERROR && "Failed to set effect slot"); + + player = NewPlayer(); + /* Connect the player's source to the effect slot. */ + alSource3i(player->source, AL_AUXILIARY_SEND_FILTER, (ALint)slot, 0, AL_FILTER_NULL); + assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source"); + + /* Play each file listed on the command line */ + for(i = 1;i < argc;i++) + { + const char *namepart; + + if(!OpenPlayerFile(player, argv[i])) + continue; + + namepart = strrchr(argv[i], '/'); + if(namepart || (namepart=strrchr(argv[i], '\\'))) + namepart++; + else + namepart = argv[i]; + + printf("Playing: %s (%s, %dhz)\n", namepart, FormatName(player->format), + player->sfinfo.samplerate); + fflush(stdout); + + if(!StartPlayer(player)) + { + ClosePlayerFile(player); + continue; + } + + while(UpdatePlayer(player)) + al_nssleep(10000000); + + ClosePlayerFile(player); + } + printf("Done.\n"); + + /* All files done. Delete the player and effect resources, and close down + * OpenAL. + */ + DeletePlayer(player); + player = NULL; + + alDeleteAuxiliaryEffectSlots(1, &slot); + alDeleteEffects(1, &effect); + alDeleteBuffers(1, &ir_buffer); + + CloseAL(); + + return 0; +} diff --git a/examples/alconvolve.cpp b/examples/alconvolve.cpp deleted file mode 100644 index 68ab5615..00000000 --- a/examples/alconvolve.cpp +++ /dev/null @@ -1,536 +0,0 @@ -/* - * OpenAL Convolution Reverb Example - * - * Copyright (c) 2020 by Chris Robinson - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* This file contains a streaming audio player, using the convolution reverb - * effect. - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sndfile.h" - -#include "AL/al.h" -#include "AL/alc.h" -#include "AL/alext.h" - -#include "common/alhelpers.h" - - -#ifndef AL_SOFT_callback_buffer -#define AL_SOFT_callback_buffer -typedef unsigned int ALbitfieldSOFT; -#define AL_BUFFER_CALLBACK_FUNCTION_SOFT 0x19A0 -#define AL_BUFFER_CALLBACK_USER_PARAM_SOFT 0x19A1 -typedef ALsizei (AL_APIENTRY*LPALBUFFERCALLBACKTYPESOFT)(ALvoid *userptr, ALvoid *sampledata, ALsizei numsamples); -typedef void (AL_APIENTRY*LPALBUFFERCALLBACKSOFT)(ALuint buffer, ALenum format, ALsizei freq, LPALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr, ALbitfieldSOFT flags); -typedef void (AL_APIENTRY*LPALGETBUFFERPTRSOFT)(ALuint buffer, ALenum param, ALvoid **value); -typedef void (AL_APIENTRY*LPALGETBUFFER3PTRSOFT)(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3); -typedef void (AL_APIENTRY*LPALGETBUFFERPTRVSOFT)(ALuint buffer, ALenum param, ALvoid **values); -#endif - -#ifndef AL_SOFT_convolution_reverb -#define AL_SOFT_convolution_reverb -#define AL_EFFECT_CONVOLUTION_REVERB_SOFT 0xA000 -#endif - - -namespace { - -/* Effect object functions */ -LPALGENEFFECTS alGenEffects; -LPALDELETEEFFECTS alDeleteEffects; -LPALISEFFECT alIsEffect; -LPALEFFECTI alEffecti; -LPALEFFECTIV alEffectiv; -LPALEFFECTF alEffectf; -LPALEFFECTFV alEffectfv; -LPALGETEFFECTI alGetEffecti; -LPALGETEFFECTIV alGetEffectiv; -LPALGETEFFECTF alGetEffectf; -LPALGETEFFECTFV alGetEffectfv; - -/* Auxiliary Effect Slot object functions */ -LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots; -LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots; -LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot; -LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti; -LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv; -LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf; -LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv; -LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti; -LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv; -LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf; -LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; - - -ALuint CreateEffect() -{ - /* Create the effect object and try to set convolution reverb. */ - ALuint effect{0}; - alGenEffects(1, &effect); - - printf("Using Convolution Reverb\n"); - - alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_CONVOLUTION_REVERB_SOFT); - - /* Check if an error occured, and clean up if so. */ - if(ALenum err{alGetError()}) - { - fprintf(stderr, "OpenAL error: %s\n", alGetString(err)); - if(alIsEffect(effect)) - alDeleteEffects(1, &effect); - return 0; - } - - return effect; -} - - -ALuint LoadSound(const char *filename) -{ - /* Open the audio file and check that it's usable. */ - SF_INFO sfinfo{}; - SNDFILE *sndfile{sf_open(filename, SFM_READ, &sfinfo)}; - if(!sndfile) - { - fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(sndfile)); - return 0; - } - constexpr sf_count_t max_samples{std::numeric_limits::max() / sizeof(float)}; - if(sfinfo.frames < 1 || sfinfo.frames > max_samples/sfinfo.channels) - { - fprintf(stderr, "Bad sample count in %s (%" PRId64 ")\n", filename, sfinfo.frames); - sf_close(sndfile); - return 0; - } - - /* Get the sound format, and figure out the OpenAL format. Use a float - * format since impulse responses are keen on having a low noise floor. - */ - ALenum format{}; - if(sfinfo.channels == 1) - format = AL_FORMAT_MONO_FLOAT32; - else if(sfinfo.channels == 2) - format = AL_FORMAT_STEREO_FLOAT32; - else - { - fprintf(stderr, "Unsupported channel count: %d\n", sfinfo.channels); - sf_close(sndfile); - return 0; - } - - auto membuf = std::make_unique(static_cast(sfinfo.frames * sfinfo.channels)); - - sf_count_t num_frames{sf_readf_float(sndfile, membuf.get(), sfinfo.frames)}; - if(num_frames < 1) - { - membuf = nullptr; - sf_close(sndfile); - fprintf(stderr, "Failed to read samples in %s (%" PRId64 ")\n", filename, num_frames); - return 0; - } - const auto num_bytes = static_cast(num_frames * sfinfo.channels) * - ALsizei{sizeof(float)}; - - ALuint buffer{0}; - alGenBuffers(1, &buffer); - alBufferData(buffer, format, membuf.get(), num_bytes, sfinfo.samplerate); - - membuf = nullptr; - sf_close(sndfile); - - if(ALenum err{alGetError()}) - { - fprintf(stderr, "OpenAL Error: %s\n", alGetString(err)); - if(buffer && alIsBuffer(buffer)) - alDeleteBuffers(1, &buffer); - return 0; - } - - return buffer; -} - - -/* This is largely the same as in alstreamcb.cpp. Comments removed for brevity, - * see the aforementioned source for more details. - */ -using std::chrono::seconds; -using std::chrono::nanoseconds; - -LPALBUFFERCALLBACKSOFT alBufferCallbackSOFT; - -struct StreamPlayer { - std::unique_ptr mBufferData; - size_t mBufferDataSize{0}; - std::atomic mReadPos{0}; - std::atomic mWritePos{0}; - - ALuint mBuffer{0}, mSource{0}; - size_t mStartOffset{0}; - - SNDFILE *mSndfile{nullptr}; - SF_INFO mSfInfo{}; - size_t mDecoderOffset{0}; - - ALenum mFormat; - - StreamPlayer() - { - alGenBuffers(1, &mBuffer); - if(ALenum err{alGetError()}) - throw std::runtime_error{"alGenBuffers failed"}; - alGenSources(1, &mSource); - if(ALenum err{alGetError()}) - { - alDeleteBuffers(1, &mBuffer); - throw std::runtime_error{"alGenSources failed"}; - } - } - ~StreamPlayer() - { - alDeleteSources(1, &mSource); - alDeleteBuffers(1, &mBuffer); - if(mSndfile) - sf_close(mSndfile); - } - - void close() - { - if(mSndfile) - { - alSourceRewind(mSource); - alSourcei(mSource, AL_BUFFER, 0); - sf_close(mSndfile); - mSndfile = nullptr; - } - } - - bool open(const char *filename) - { - close(); - - mSndfile = sf_open(filename, SFM_READ, &mSfInfo); - if(!mSndfile) - { - fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(mSndfile)); - return false; - } - - mFormat = AL_NONE; - if(mSfInfo.channels == 1) - mFormat = AL_FORMAT_MONO_FLOAT32; - else if(mSfInfo.channels == 2) - mFormat = AL_FORMAT_STEREO_FLOAT32; - else if(mSfInfo.channels == 6) - mFormat = AL_FORMAT_51CHN32; - else - { - fprintf(stderr, "Unsupported channel count: %d\n", mSfInfo.channels); - sf_close(mSndfile); - mSndfile = nullptr; - - return false; - } - - mBufferDataSize = static_cast(mSfInfo.samplerate*mSfInfo.channels) * sizeof(float); - mBufferData.reset(new ALbyte[mBufferDataSize]); - mReadPos.store(0, std::memory_order_relaxed); - mWritePos.store(0, std::memory_order_relaxed); - mDecoderOffset = 0; - - return true; - } - - static ALsizei AL_APIENTRY bufferCallbackC(void *userptr, void *data, ALsizei size) - { return static_cast(userptr)->bufferCallback(data, size); } - ALsizei bufferCallback(void *data, ALsizei size) - { - ALsizei got{0}; - - size_t roffset{mReadPos.load(std::memory_order_acquire)}; - while(got < size) - { - const size_t woffset{mWritePos.load(std::memory_order_relaxed)}; - if(woffset == roffset) break; - - size_t todo{((woffset < roffset) ? mBufferDataSize : woffset) - roffset}; - todo = std::min(todo, static_cast(size-got)); - - memcpy(data, &mBufferData[roffset], todo); - data = static_cast(data) + todo; - got += static_cast(todo); - - roffset += todo; - if(roffset == mBufferDataSize) - roffset = 0; - } - mReadPos.store(roffset, std::memory_order_release); - - return got; - } - - bool prepare() - { - alBufferCallbackSOFT(mBuffer, mFormat, mSfInfo.samplerate, bufferCallbackC, this, 0); - alSourcei(mSource, AL_BUFFER, static_cast(mBuffer)); - if(ALenum err{alGetError()}) - { - fprintf(stderr, "Failed to set callback: %s (0x%04x)\n", alGetString(err), err); - return false; - } - return true; - } - - bool update() - { - ALenum state; - ALint pos; - alGetSourcei(mSource, AL_SAMPLE_OFFSET, &pos); - alGetSourcei(mSource, AL_SOURCE_STATE, &state); - - const size_t frame_size{static_cast(mSfInfo.channels) * sizeof(float)}; - size_t woffset{mWritePos.load(std::memory_order_acquire)}; - if(state != AL_INITIAL) - { - const size_t roffset{mReadPos.load(std::memory_order_relaxed)}; - const size_t readable{((woffset >= roffset) ? woffset : (mBufferDataSize+woffset)) - - roffset}; - const size_t curtime{((state==AL_STOPPED) ? (mDecoderOffset-readable) / frame_size - : (static_cast(pos) + mStartOffset/frame_size)) - / static_cast(mSfInfo.samplerate)}; - printf("\r%3zus (%3zu%% full)", curtime, readable * 100 / mBufferDataSize); - } - else - fputs("Starting...", stdout); - fflush(stdout); - - while(!sf_error(mSndfile)) - { - size_t read_bytes; - const size_t roffset{mReadPos.load(std::memory_order_relaxed)}; - if(roffset > woffset) - { - const size_t writable{roffset-woffset-1}; - if(writable < frame_size) break; - - sf_count_t num_frames{sf_readf_float(mSndfile, - reinterpret_cast(&mBufferData[woffset]), - static_cast(writable/frame_size))}; - if(num_frames < 1) break; - - read_bytes = static_cast(num_frames) * frame_size; - woffset += read_bytes; - } - else - { - const size_t writable{!roffset ? mBufferDataSize-woffset-1 : - (mBufferDataSize-woffset)}; - if(writable < frame_size) break; - - sf_count_t num_frames{sf_readf_float(mSndfile, - reinterpret_cast(&mBufferData[woffset]), - static_cast(writable/frame_size))}; - if(num_frames < 1) break; - - read_bytes = static_cast(num_frames) * frame_size; - woffset += read_bytes; - if(woffset == mBufferDataSize) - woffset = 0; - } - mWritePos.store(woffset, std::memory_order_release); - mDecoderOffset += read_bytes; - } - - if(state != AL_PLAYING && state != AL_PAUSED) - { - const size_t roffset{mReadPos.load(std::memory_order_relaxed)}; - const size_t readable{((woffset >= roffset) ? woffset : (mBufferDataSize+woffset)) - - roffset}; - if(readable == 0) - return false; - - mStartOffset = mDecoderOffset - readable; - alSourcePlay(mSource); - if(alGetError() != AL_NO_ERROR) - return false; - } - return true; - } -}; - -} // namespace - -int main(int argc, char **argv) -{ - /* A simple RAII container for OpenAL startup and shutdown. */ - struct AudioManager { - AudioManager(char ***argv_, int *argc_) - { - if(InitAL(argv_, argc_) != 0) - throw std::runtime_error{"Failed to initialize OpenAL"}; - } - ~AudioManager() { CloseAL(); } - }; - - /* Print out usage if no arguments were specified */ - if(argc < 2) - { - fprintf(stderr, "Usage: %s [-device ] [sound files...]\n", - argv[0]); - return 1; - } - - argv++; argc--; - AudioManager almgr{&argv, &argc}; - - if(!alIsExtensionPresent("AL_SOFTX_callback_buffer")) - { - fprintf(stderr, "AL_SOFT_callback_buffer extension not available\n"); - return 1; - } - - /* Define a macro to help load the function pointers. */ -#define LOAD_PROC(T, x) ((x) = reinterpret_cast(alGetProcAddress(#x))) - LOAD_PROC(LPALBUFFERCALLBACKSOFT, alBufferCallbackSOFT); - - LOAD_PROC(LPALGENEFFECTS, alGenEffects); - LOAD_PROC(LPALDELETEEFFECTS, alDeleteEffects); - LOAD_PROC(LPALISEFFECT, alIsEffect); - LOAD_PROC(LPALEFFECTI, alEffecti); - LOAD_PROC(LPALEFFECTIV, alEffectiv); - LOAD_PROC(LPALEFFECTF, alEffectf); - LOAD_PROC(LPALEFFECTFV, alEffectfv); - LOAD_PROC(LPALGETEFFECTI, alGetEffecti); - LOAD_PROC(LPALGETEFFECTIV, alGetEffectiv); - LOAD_PROC(LPALGETEFFECTF, alGetEffectf); - LOAD_PROC(LPALGETEFFECTFV, alGetEffectfv); - - LOAD_PROC(LPALGENAUXILIARYEFFECTSLOTS, alGenAuxiliaryEffectSlots); - LOAD_PROC(LPALDELETEAUXILIARYEFFECTSLOTS, alDeleteAuxiliaryEffectSlots); - LOAD_PROC(LPALISAUXILIARYEFFECTSLOT, alIsAuxiliaryEffectSlot); - LOAD_PROC(LPALAUXILIARYEFFECTSLOTI, alAuxiliaryEffectSloti); - LOAD_PROC(LPALAUXILIARYEFFECTSLOTIV, alAuxiliaryEffectSlotiv); - LOAD_PROC(LPALAUXILIARYEFFECTSLOTF, alAuxiliaryEffectSlotf); - LOAD_PROC(LPALAUXILIARYEFFECTSLOTFV, alAuxiliaryEffectSlotfv); - LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTI, alGetAuxiliaryEffectSloti); - LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTIV, alGetAuxiliaryEffectSlotiv); - LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTF, alGetAuxiliaryEffectSlotf); - LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv); -#undef LOAD_PROC - - /* Load the impulse response sound file into a buffer. */ - ALuint buffer{LoadSound(argv[0])}; - if(!buffer) return 1; - - /* Create the convolution reverb effect. */ - ALuint effect{CreateEffect()}; - if(!effect) - { - alDeleteBuffers(1, &buffer); - return 1; - } - - /* Create the effect slot object. This is what "plays" an effect on sources - * that connect to it. */ - ALuint slot{0}; - alGenAuxiliaryEffectSlots(1, &slot); - - /* Set the impulse response sound buffer on the effect slot. This allows - * effects to access it as needed. In this case, convolution reverb uses it - * as the filter source. NOTE: Unlike the effect object, the buffer *is* - * kept referenced and may not be changed or deleted as long as it's set, - * just like with a source. When another buffer is set, or the effect slot - * is deleted, the buffer reference is released. - * - * The effect slot's gain is reduced because the impulse responses I've - * tested with result in excessively loud reverb. Is that normal? Even with - * this, it seems a bit on the loud side. - * - * Also note: unlike standard or EAX reverb, there is no automatic - * attenuation of a source's reverb response with distance, so the reverb - * will remain full volume regardless of a given sound's distance from the - * listener. You can use a send filter to alter a given source's - * contribution to reverb. - */ - alAuxiliaryEffectSloti(slot, AL_BUFFER, static_cast(buffer)); - alAuxiliaryEffectSlotf(slot, AL_EFFECTSLOT_GAIN, 1.0f / 16.0f); - alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, static_cast(effect)); - assert(alGetError()==AL_NO_ERROR && "Failed to set effect slot"); - - ALCint refresh{25}; - alcGetIntegerv(alcGetContextsDevice(alcGetCurrentContext()), ALC_REFRESH, 1, &refresh); - - std::unique_ptr player{new StreamPlayer{}}; - alSource3i(player->mSource, AL_AUXILIARY_SEND_FILTER, static_cast(slot), 0, - AL_FILTER_NULL); - - for(int i{1};i < argc;++i) - { - if(!player->open(argv[i])) - continue; - - const char *namepart{strrchr(argv[i], '/')}; - if(namepart || (namepart=strrchr(argv[i], '\\'))) - ++namepart; - else - namepart = argv[i]; - - printf("Playing: %s (%s, %dhz)\n", namepart, FormatName(player->mFormat), - player->mSfInfo.samplerate); - fflush(stdout); - - if(!player->prepare()) - { - player->close(); - continue; - } - - while(player->update()) - std::this_thread::sleep_for(nanoseconds{seconds{1}} / refresh); - putc('\n', stdout); - - player->close(); - } - /* All done. */ - printf("Done.\n"); - - player = nullptr; - alDeleteAuxiliaryEffectSlots(1, &slot); - alDeleteEffects(1, &effect); - alDeleteBuffers(1, &buffer); - - return 0; -} -- cgit v1.2.3 From 2a019400418c64f4891041e5a63c6c884bba8df5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 26 Aug 2020 21:29:16 -0700 Subject: De-duplicate LoadSampleArray and FmtTypeTraits --- CMakeLists.txt | 2 + alc/effects/convolution.cpp | 137 +++---------------------------------------- alc/fmt_traits.cpp | 79 +++++++++++++++++++++++++ alc/fmt_traits.h | 81 ++++++++++++++++++++++++++ alc/voice.cpp | 139 +++----------------------------------------- 5 files changed, 178 insertions(+), 260 deletions(-) create mode 100644 alc/fmt_traits.cpp create mode 100644 alc/fmt_traits.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 55ccec6b..b5304da6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -646,6 +646,8 @@ set(ALC_OBJS alc/filters/nfc.h alc/filters/splitter.cpp alc/filters/splitter.h + alc/fmt_traits.cpp + alc/fmt_traits.h alc/fpu_ctrl.cpp alc/fpu_ctrl.h alc/front_stablizer.h diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index 77cd394d..8213113b 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -11,6 +11,7 @@ #include "almalloc.h" #include "alspan.h" #include "effects/base.h" +#include "fmt_traits.h" #include "logging.h" #include "polyphase_resampler.h" @@ -50,144 +51,22 @@ namespace { */ -/* TODO: De-duplicate this load stuff (also in voice.cpp). */ - -constexpr int16_t muLawDecompressionTable[256] = { - -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, - -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, - -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, - -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, - -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, - -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, - -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, - -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, - -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, - -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, - -876, -844, -812, -780, -748, -716, -684, -652, - -620, -588, -556, -524, -492, -460, -428, -396, - -372, -356, -340, -324, -308, -292, -276, -260, - -244, -228, -212, -196, -180, -164, -148, -132, - -120, -112, -104, -96, -88, -80, -72, -64, - -56, -48, -40, -32, -24, -16, -8, 0, - 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, - 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, - 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, - 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, - 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, - 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, - 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, - 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, - 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, - 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, - 876, 844, 812, 780, 748, 716, 684, 652, - 620, 588, 556, 524, 492, 460, 428, 396, - 372, 356, 340, 324, 308, 292, 276, 260, - 244, 228, 212, 196, 180, 164, 148, 132, - 120, 112, 104, 96, 88, 80, 72, 64, - 56, 48, 40, 32, 24, 16, 8, 0 -}; - -constexpr int16_t aLawDecompressionTable[256] = { - -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, - -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, - -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, - -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, - -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, - -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, - -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472, - -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, - -344, -328, -376, -360, -280, -264, -312, -296, - -472, -456, -504, -488, -408, -392, -440, -424, - -88, -72, -120, -104, -24, -8, -56, -40, - -216, -200, -248, -232, -152, -136, -184, -168, - -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, - -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, - -688, -656, -752, -720, -560, -528, -624, -592, - -944, -912, -1008, -976, -816, -784, -880, -848, - 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, - 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, - 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, - 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, - 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, - 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, - 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, - 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, - 344, 328, 376, 360, 280, 264, 312, 296, - 472, 456, 504, 488, 408, 392, 440, 424, - 88, 72, 120, 104, 24, 8, 56, 40, - 216, 200, 248, 232, 152, 136, 184, 168, - 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, - 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, - 688, 656, 752, 720, 560, 528, 624, 592, - 944, 912, 1008, 976, 816, 784, 880, 848 -}; - -template -struct FmtTypeTraits { }; - -template<> -struct FmtTypeTraits { - using Type = uint8_t; - static constexpr inline double to_double(const Type val) noexcept - { return val*(1.0/128.0) - 1.0; } -}; -template<> -struct FmtTypeTraits { - using Type = int16_t; - static constexpr inline double to_double(const Type val) noexcept { return val*(1.0/32768.0); } -}; -template<> -struct FmtTypeTraits { - using Type = float; - static constexpr inline double to_double(const Type val) noexcept { return val; } -}; -template<> -struct FmtTypeTraits { - using Type = double; - static constexpr inline double to_double(const Type val) noexcept { return val; } -}; -template<> -struct FmtTypeTraits { - using Type = uint8_t; - static constexpr inline double to_double(const Type val) noexcept - { return muLawDecompressionTable[val] * (1.0/32768.0); } -}; -template<> -struct FmtTypeTraits { - using Type = uint8_t; - static constexpr inline double to_double(const Type val) noexcept - { return aLawDecompressionTable[val] * (1.0/32768.0); } -}; - - -template -inline void LoadSampleArray(double *RESTRICT dst, const al::byte *src, const size_t srcstep, - const size_t samples) noexcept -{ - using SampleType = typename FmtTypeTraits::Type; - - const SampleType *RESTRICT ssrc{reinterpret_cast(src)}; - for(size_t i{0u};i < samples;i++) - dst[i] = FmtTypeTraits::to_double(ssrc[i*srcstep]); -} - void LoadSamples(double *RESTRICT dst, const al::byte *src, const size_t srcstep, FmtType srctype, const size_t samples) noexcept { -#define HANDLE_FMT(T) case T: LoadSampleArray(dst, src, srcstep, samples); break +#define HANDLE_FMT(T) case T: al::LoadSampleArray(dst, src, srcstep, samples); break switch(srctype) { - HANDLE_FMT(FmtUByte); - HANDLE_FMT(FmtShort); - HANDLE_FMT(FmtFloat); - HANDLE_FMT(FmtDouble); - HANDLE_FMT(FmtMulaw); - HANDLE_FMT(FmtAlaw); + HANDLE_FMT(FmtUByte); + HANDLE_FMT(FmtShort); + HANDLE_FMT(FmtFloat); + HANDLE_FMT(FmtDouble); + HANDLE_FMT(FmtMulaw); + HANDLE_FMT(FmtAlaw); } #undef HANDLE_FMT } - using complex_d = std::complex; constexpr size_t ConvolveUpdateSize{1024}; diff --git a/alc/fmt_traits.cpp b/alc/fmt_traits.cpp new file mode 100644 index 00000000..054d8766 --- /dev/null +++ b/alc/fmt_traits.cpp @@ -0,0 +1,79 @@ + +#include "config.h" + +#include "fmt_traits.h" + + +namespace al { + +const int16_t muLawDecompressionTable[256] = { + -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, + -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, + -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, + -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, 0, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 0 +}; + +const int16_t aLawDecompressionTable[256] = { + -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, + -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, + -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, + -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472, + -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, + -344, -328, -376, -360, -280, -264, -312, -296, + -472, -456, -504, -488, -408, -392, -440, -424, + -88, -72, -120, -104, -24, -8, -56, -40, + -216, -200, -248, -232, -152, -136, -184, -168, + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, -592, + -944, -912, -1008, -976, -816, -784, -880, -848, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848 +}; + +} // namespace al diff --git a/alc/fmt_traits.h b/alc/fmt_traits.h new file mode 100644 index 00000000..c3bf5fa5 --- /dev/null +++ b/alc/fmt_traits.h @@ -0,0 +1,81 @@ +#ifndef ALC_FMT_TRAITS_H +#define ALC_FMT_TRAITS_H + +#include +#include + +#include "albyte.h" +#include "buffer_formats.h" + + +namespace al { + +extern const int16_t muLawDecompressionTable[256]; +extern const int16_t aLawDecompressionTable[256]; + + +template +struct FmtTypeTraits { }; + +template<> +struct FmtTypeTraits { + using Type = uint8_t; + + template + static constexpr inline OutT to(const Type val) noexcept + { return val*OutT{1.0/128.0} - OutT{1.0}; } +}; +template<> +struct FmtTypeTraits { + using Type = int16_t; + + template + static constexpr inline OutT to(const Type val) noexcept { return val*OutT{1.0/32768.0}; } +}; +template<> +struct FmtTypeTraits { + using Type = float; + + template + static constexpr inline OutT to(const Type val) noexcept { return val; } +}; +template<> +struct FmtTypeTraits { + using Type = double; + + template + static constexpr inline OutT to(const Type val) noexcept { return static_cast(val); } +}; +template<> +struct FmtTypeTraits { + using Type = uint8_t; + + template + static constexpr inline OutT to(const Type val) noexcept + { return muLawDecompressionTable[val] * OutT{1.0/32768.0}; } +}; +template<> +struct FmtTypeTraits { + using Type = uint8_t; + + template + static constexpr inline OutT to(const Type val) noexcept + { return aLawDecompressionTable[val] * OutT{1.0/32768.0}; } +}; + + +template +inline void LoadSampleArray(DstT *RESTRICT dst, const al::byte *src, const size_t srcstep, + const size_t samples) noexcept +{ + using TypeTraits = FmtTypeTraits; + using SampleType = typename TypeTraits::Type; + + const SampleType *RESTRICT ssrc{reinterpret_cast(src)}; + for(size_t i{0u};i < samples;i++) + dst[i] = TypeTraits::template to(ssrc[i*srcstep]); +} + +} // namespace al + +#endif /* ALC_FMT_TRAITS_H */ diff --git a/alc/voice.cpp b/alc/voice.cpp index e98e91ed..612c611d 100644 --- a/alc/voice.cpp +++ b/alc/voice.cpp @@ -54,6 +54,7 @@ #include "filters/biquad.h" #include "filters/nfc.h" #include "filters/splitter.h" +#include "fmt_traits.h" #include "hrtf.h" #include "inprogext.h" #include "logging.h" @@ -182,119 +183,6 @@ void aluInitMixer() namespace { -/* A quick'n'dirty lookup table to decode a muLaw-encoded byte sample into a - * signed 16-bit sample */ -constexpr int16_t muLawDecompressionTable[256] = { - -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, - -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, - -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, - -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, - -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, - -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, - -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, - -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, - -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, - -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, - -876, -844, -812, -780, -748, -716, -684, -652, - -620, -588, -556, -524, -492, -460, -428, -396, - -372, -356, -340, -324, -308, -292, -276, -260, - -244, -228, -212, -196, -180, -164, -148, -132, - -120, -112, -104, -96, -88, -80, -72, -64, - -56, -48, -40, -32, -24, -16, -8, 0, - 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, - 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, - 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, - 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, - 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, - 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, - 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, - 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, - 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, - 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, - 876, 844, 812, 780, 748, 716, 684, 652, - 620, 588, 556, 524, 492, 460, 428, 396, - 372, 356, 340, 324, 308, 292, 276, 260, - 244, 228, 212, 196, 180, 164, 148, 132, - 120, 112, 104, 96, 88, 80, 72, 64, - 56, 48, 40, 32, 24, 16, 8, 0 -}; - -/* A quick'n'dirty lookup table to decode an aLaw-encoded byte sample into a - * signed 16-bit sample */ -constexpr int16_t aLawDecompressionTable[256] = { - -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, - -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, - -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, - -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, - -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, - -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, - -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472, - -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, - -344, -328, -376, -360, -280, -264, -312, -296, - -472, -456, -504, -488, -408, -392, -440, -424, - -88, -72, -120, -104, -24, -8, -56, -40, - -216, -200, -248, -232, -152, -136, -184, -168, - -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, - -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, - -688, -656, -752, -720, -560, -528, -624, -592, - -944, -912, -1008, -976, -816, -784, -880, -848, - 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, - 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, - 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, - 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, - 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, - 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, - 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, - 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, - 344, 328, 376, 360, 280, 264, 312, 296, - 472, 456, 504, 488, 408, 392, 440, 424, - 88, 72, 120, 104, 24, 8, 56, 40, - 216, 200, 248, 232, 152, 136, 184, 168, - 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, - 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, - 688, 656, 752, 720, 560, 528, 624, 592, - 944, 912, 1008, 976, 816, 784, 880, 848 -}; - -template -struct FmtTypeTraits { }; - -template<> -struct FmtTypeTraits { - using Type = uint8_t; - static constexpr inline float to_float(const Type val) noexcept - { return val*(1.0f/128.0f) - 1.0f; } -}; -template<> -struct FmtTypeTraits { - using Type = int16_t; - static constexpr inline float to_float(const Type val) noexcept { return val*(1.0f/32768.0f); } -}; -template<> -struct FmtTypeTraits { - using Type = float; - static constexpr inline float to_float(const Type val) noexcept { return val; } -}; -template<> -struct FmtTypeTraits { - using Type = double; - static constexpr inline float to_float(const Type val) noexcept - { return static_cast(val); } -}; -template<> -struct FmtTypeTraits { - using Type = uint8_t; - static constexpr inline float to_float(const Type val) noexcept - { return muLawDecompressionTable[val] * (1.0f/32768.0f); } -}; -template<> -struct FmtTypeTraits { - using Type = uint8_t; - static constexpr inline float to_float(const Type val) noexcept - { return aLawDecompressionTable[val] * (1.0f/32768.0f); } -}; - - void SendSourceStoppedEvent(ALCcontext *context, ALuint id) { RingBuffer *ring{context->mAsyncEvents.get()}; @@ -336,29 +224,18 @@ const float *DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, float *ds } -template -inline void LoadSampleArray(float *RESTRICT dst, const al::byte *src, const size_t srcstep, - const size_t samples) noexcept -{ - using SampleType = typename FmtTypeTraits::Type; - - const SampleType *RESTRICT ssrc{reinterpret_cast(src)}; - for(size_t i{0u};i < samples;i++) - dst[i] = FmtTypeTraits::to_float(ssrc[i*srcstep]); -} - void LoadSamples(float *RESTRICT dst, const al::byte *src, const size_t srcstep, FmtType srctype, const size_t samples) noexcept { -#define HANDLE_FMT(T) case T: LoadSampleArray(dst, src, srcstep, samples); break +#define HANDLE_FMT(T) case T: al::LoadSampleArray(dst, src, srcstep, samples); break switch(srctype) { - HANDLE_FMT(FmtUByte); - HANDLE_FMT(FmtShort); - HANDLE_FMT(FmtFloat); - HANDLE_FMT(FmtDouble); - HANDLE_FMT(FmtMulaw); - HANDLE_FMT(FmtAlaw); + HANDLE_FMT(FmtUByte); + HANDLE_FMT(FmtShort); + HANDLE_FMT(FmtFloat); + HANDLE_FMT(FmtDouble); + HANDLE_FMT(FmtMulaw); + HANDLE_FMT(FmtAlaw); } #undef HANDLE_FMT } -- cgit v1.2.3 From ecf30de36f6487c1f8a19ae0d03ba810078706f4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 27 Aug 2020 23:02:17 -0700 Subject: Rename buffer_formats to buffer_storage --- CMakeLists.txt | 4 ++-- al/buffer.h | 2 +- alc/buffer_formats.cpp | 37 ------------------------------------- alc/buffer_formats.h | 33 --------------------------------- alc/buffer_storage.cpp | 37 +++++++++++++++++++++++++++++++++++++ alc/buffer_storage.h | 33 +++++++++++++++++++++++++++++++++ alc/effects/base.h | 2 +- alc/fmt_traits.h | 2 +- 8 files changed, 75 insertions(+), 75 deletions(-) delete mode 100644 alc/buffer_formats.cpp delete mode 100644 alc/buffer_formats.h create mode 100644 alc/buffer_storage.cpp create mode 100644 alc/buffer_storage.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index b5304da6..04946b9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -617,8 +617,8 @@ set(ALC_OBJS alc/bsinc_tables.cpp alc/bsinc_tables.h alc/bufferline.h - alc/buffer_formats.cpp - alc/buffer_formats.h + alc/buffer_storage.cpp + alc/buffer_storage.h alc/compat.h alc/converter.cpp alc/converter.h diff --git a/al/buffer.h b/al/buffer.h index 5faded0d..6b1d45f5 100644 --- a/al/buffer.h +++ b/al/buffer.h @@ -8,7 +8,7 @@ #include "albyte.h" #include "almalloc.h" #include "atomic.h" -#include "buffer_formats.h" +#include "buffer_storage.h" #include "inprogext.h" #include "vector.h" diff --git a/alc/buffer_formats.cpp b/alc/buffer_formats.cpp deleted file mode 100644 index 7ee4bfc8..00000000 --- a/alc/buffer_formats.cpp +++ /dev/null @@ -1,37 +0,0 @@ - -#include "config.h" - -#include "buffer_formats.h" - -#include - - -ALuint BytesFromFmt(FmtType type) noexcept -{ - switch(type) - { - case FmtUByte: return sizeof(uint8_t); - case FmtShort: return sizeof(int16_t); - case FmtFloat: return sizeof(float); - case FmtDouble: return sizeof(double); - case FmtMulaw: return sizeof(uint8_t); - case FmtAlaw: return sizeof(uint8_t); - } - return 0; -} -ALuint ChannelsFromFmt(FmtChannels chans, ALuint ambiorder) noexcept -{ - switch(chans) - { - case FmtMono: return 1; - case FmtStereo: return 2; - case FmtRear: return 2; - case FmtQuad: return 4; - case FmtX51: return 6; - case FmtX61: return 7; - case FmtX71: return 8; - case FmtBFormat2D: return (ambiorder*2) + 1; - case FmtBFormat3D: return (ambiorder+1) * (ambiorder+1); - } - return 0; -} diff --git a/alc/buffer_formats.h b/alc/buffer_formats.h deleted file mode 100644 index b10cea4c..00000000 --- a/alc/buffer_formats.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef ALC_BUFFER_FORMATS_H -#define ALC_BUFFER_FORMATS_H - -#include "AL/al.h" - - -/* Storable formats */ -enum FmtType : unsigned char { - FmtUByte, - FmtShort, - FmtFloat, - FmtDouble, - FmtMulaw, - FmtAlaw, -}; -enum FmtChannels : unsigned char { - FmtMono, - FmtStereo, - FmtRear, - FmtQuad, - FmtX51, /* (WFX order) */ - FmtX61, /* (WFX order) */ - FmtX71, /* (WFX order) */ - FmtBFormat2D, - FmtBFormat3D, -}; - -ALuint BytesFromFmt(FmtType type) noexcept; -ALuint ChannelsFromFmt(FmtChannels chans, ALuint ambiorder) noexcept; -inline ALuint FrameSizeFromFmt(FmtChannels chans, FmtType type, ALuint ambiorder) noexcept -{ return ChannelsFromFmt(chans, ambiorder) * BytesFromFmt(type); } - -#endif /* ALC_BUFFER_FORMATS_H */ diff --git a/alc/buffer_storage.cpp b/alc/buffer_storage.cpp new file mode 100644 index 00000000..727cb281 --- /dev/null +++ b/alc/buffer_storage.cpp @@ -0,0 +1,37 @@ + +#include "config.h" + +#include "buffer_storage.h" + +#include + + +ALuint BytesFromFmt(FmtType type) noexcept +{ + switch(type) + { + case FmtUByte: return sizeof(uint8_t); + case FmtShort: return sizeof(int16_t); + case FmtFloat: return sizeof(float); + case FmtDouble: return sizeof(double); + case FmtMulaw: return sizeof(uint8_t); + case FmtAlaw: return sizeof(uint8_t); + } + return 0; +} +ALuint ChannelsFromFmt(FmtChannels chans, ALuint ambiorder) noexcept +{ + switch(chans) + { + case FmtMono: return 1; + case FmtStereo: return 2; + case FmtRear: return 2; + case FmtQuad: return 4; + case FmtX51: return 6; + case FmtX61: return 7; + case FmtX71: return 8; + case FmtBFormat2D: return (ambiorder*2) + 1; + case FmtBFormat3D: return (ambiorder+1) * (ambiorder+1); + } + return 0; +} diff --git a/alc/buffer_storage.h b/alc/buffer_storage.h new file mode 100644 index 00000000..b10cea4c --- /dev/null +++ b/alc/buffer_storage.h @@ -0,0 +1,33 @@ +#ifndef ALC_BUFFER_FORMATS_H +#define ALC_BUFFER_FORMATS_H + +#include "AL/al.h" + + +/* Storable formats */ +enum FmtType : unsigned char { + FmtUByte, + FmtShort, + FmtFloat, + FmtDouble, + FmtMulaw, + FmtAlaw, +}; +enum FmtChannels : unsigned char { + FmtMono, + FmtStereo, + FmtRear, + FmtQuad, + FmtX51, /* (WFX order) */ + FmtX61, /* (WFX order) */ + FmtX71, /* (WFX order) */ + FmtBFormat2D, + FmtBFormat3D, +}; + +ALuint BytesFromFmt(FmtType type) noexcept; +ALuint ChannelsFromFmt(FmtChannels chans, ALuint ambiorder) noexcept; +inline ALuint FrameSizeFromFmt(FmtChannels chans, FmtType type, ALuint ambiorder) noexcept +{ return ChannelsFromFmt(chans, ambiorder) * BytesFromFmt(type); } + +#endif /* ALC_BUFFER_FORMATS_H */ diff --git a/alc/effects/base.h b/alc/effects/base.h index 6aa0f9e9..e662d69c 100644 --- a/alc/effects/base.h +++ b/alc/effects/base.h @@ -8,7 +8,7 @@ #include "almalloc.h" #include "alspan.h" #include "atomic.h" -#include "buffer_formats.h" +#include "buffer_storage.h" #include "intrusive_ptr.h" struct ALeffectslot; diff --git a/alc/fmt_traits.h b/alc/fmt_traits.h index c3bf5fa5..9d2a2567 100644 --- a/alc/fmt_traits.h +++ b/alc/fmt_traits.h @@ -5,7 +5,7 @@ #include #include "albyte.h" -#include "buffer_formats.h" +#include "buffer_storage.h" namespace al { -- cgit v1.2.3 From eeba1a385c466e1c25468eb2ac23b41a49e4a978 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 1 Sep 2020 06:01:14 -0700 Subject: Don't use config.h to define RESTRICT --- CMakeLists.txt | 5 +++-- config.h.in | 3 --- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 04946b9d..7d09547c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,11 +169,12 @@ if(NOT WIN32) endif() # C99 has restrict, but C++ does not, so we can only utilize __restrict. -set(RESTRICT_DECL ) check_cxx_source_compiles("int *__restrict foo; int main() { return 0; }" HAVE___RESTRICT) if(HAVE___RESTRICT) - set(RESTRICT_DECL "__restrict") + set(CPP_DEFS ${CPP_DEFS} RESTRICT=__restrict) +else() + set(CPP_DEFS ${CPP_DEFS} "RESTRICT=") endif() # Some systems may need libatomic for atomic functions to work diff --git a/config.h.in b/config.h.in index bd87f4fe..f7e1542e 100644 --- a/config.h.in +++ b/config.h.in @@ -2,9 +2,6 @@ #define AL_API ${EXPORT_DECL} #define ALC_API ${EXPORT_DECL} -/* Define a restrict macro for non-aliased pointers */ -#define RESTRICT ${RESTRICT_DECL} - /* Define if HRTF data is embedded in the library */ #cmakedefine ALSOFT_EMBED_HRTF_DATA -- cgit v1.2.3 From 5f8fe0e5bb67a52f14dec50e8517a126c059aaf7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 13 Oct 2020 05:59:35 -0700 Subject: Enable standard stdio methods with MinGW This unfortunately doesn't fix the %z warnings for whatever reason, but it should help guarantee correct function behavior by not relying on msvcrt's stdio functions. --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d09547c..e7f82dc3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,6 +116,9 @@ set(EXTRA_LIBS ) if(WIN32) set(CPP_DEFS ${CPP_DEFS} _WIN32) + if(MINGW) + set(CPP_DEFS ${CPP_DEFS} __USE_MINGW_ANSI_STDIO) + endif() option(ALSOFT_BUILD_ROUTER "Build the router (EXPERIMENTAL; creates OpenAL32.dll and soft_oal.dll)" OFF) if(MINGW) -- cgit v1.2.3 From 0311f6431c377505b69a0c0b60dcc508b05b5672 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 25 Oct 2020 00:08:54 -0700 Subject: Add a comment about building for static linking --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index e7f82dc3..5facfd2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,6 +135,8 @@ if("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX") set(LINKER_FLAGS ${LINKER_FLAGS} -L/usr/lib) endif() +# When the library is built for static linking, apps should define +# AL_LIBTYPE_STATIC when including the AL headers. if(NOT LIBTYPE) set(LIBTYPE SHARED) endif() -- cgit v1.2.3 From 36ecea458a5625df0e07dc4f6fb1c1280d348b3a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 25 Oct 2020 14:53:44 -0700 Subject: Clear /W3 on MSVC since we use /W4 --- CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 5facfd2b..4ae87cbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,6 +210,13 @@ if(MSVC) set(C_FLAGS ${C_FLAGS} $<$:/permissive->) endif() set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4268 /wd4324 /wd5030) + # Remove /W3, which is added by default, since we set /W4. Some versions of + # MSVC complain about both /W3 and /W4 being specified. + foreach(flag_var CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + if(${flag_var} MATCHES "-DNDEBUG") + string(REGEX REPLACE "/W3" "" ${flag_var} "${${flag_var}}") + endif() + endforeach() if(NOT DXSDK_DIR) string(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}") -- cgit v1.2.3 From 9e7c81649869432f99f8fd4780af1dbda007a219 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 26 Oct 2020 10:46:00 -0700 Subject: Fix copy-paste error --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ae87cbe..f3c3a317 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,10 +210,10 @@ if(MSVC) set(C_FLAGS ${C_FLAGS} $<$:/permissive->) endif() set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4268 /wd4324 /wd5030) - # Remove /W3, which is added by default, since we set /W4. Some versions of - # MSVC complain about both /W3 and /W4 being specified. + # Remove /W3, which is added by default, since we set /W4. Some build + # generators with MSVC complain about both /W3 and /W4 being specified. foreach(flag_var CMAKE_C_FLAGS CMAKE_CXX_FLAGS) - if(${flag_var} MATCHES "-DNDEBUG") + if(${flag_var} MATCHES "/W3") string(REGEX REPLACE "/W3" "" ${flag_var} "${${flag_var}}") endif() endforeach() -- cgit v1.2.3 From c0cbe602ce66481a822ba16856052a47154f5cc5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 4 Nov 2020 02:00:35 -0800 Subject: Release 1.21.0 --- CMakeLists.txt | 4 ++-- appveyor.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index f3c3a317..f811f31f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,8 +142,8 @@ if(NOT LIBTYPE) endif() set(LIB_MAJOR_VERSION "1") -set(LIB_MINOR_VERSION "20") -set(LIB_REVISION "1") +set(LIB_MINOR_VERSION "21") +set(LIB_REVISION "0") set(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}") set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0) diff --git a/appveyor.yml b/appveyor.yml index 9a137e9c..e54760b2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 1.20.1.{build} +version: 1.21.0.{build} environment: APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 -- cgit v1.2.3 From 4e760bbecc6ac4031dca45b4882c6faa71842e90 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 7 Nov 2020 08:36:49 -0800 Subject: Use a separate structure for the active effect slot properties --- CMakeLists.txt | 2 + al/auxeffectslot.cpp | 72 ++++++++++++++-------------- al/auxeffectslot.h | 49 +------------------ al/source.cpp | 6 ++- alc/alc.cpp | 16 +++---- alc/alcontext.h | 9 ++-- alc/alu.cpp | 114 +++++++++++++++++++++----------------------- alc/effects/autowah.cpp | 14 ++++-- alc/effects/base.h | 8 ++-- alc/effects/chorus.cpp | 13 +++-- alc/effects/compressor.cpp | 14 ++++-- alc/effects/convolution.cpp | 10 ++-- alc/effects/dedicated.cpp | 15 +++--- alc/effects/distortion.cpp | 11 +++-- alc/effects/echo.cpp | 13 +++-- alc/effects/equalizer.cpp | 11 +++-- alc/effects/fshifter.cpp | 13 +++-- alc/effects/modulator.cpp | 11 +++-- alc/effects/null.cpp | 8 ++-- alc/effects/pshifter.cpp | 11 +++-- alc/effects/reverb.cpp | 11 +++-- alc/effects/vmorpher.cpp | 11 +++-- alc/effectslot.cpp | 18 +++++++ alc/effectslot.h | 63 ++++++++++++++++++++++++ alc/panning.cpp | 9 ++-- alc/voice.h | 3 +- 26 files changed, 309 insertions(+), 226 deletions(-) create mode 100644 alc/effectslot.cpp create mode 100644 alc/effectslot.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index f811f31f..06dc5f9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -638,6 +638,8 @@ set(ALC_OBJS alc/cpu_caps.cpp alc/cpu_caps.h alc/devformat.h + alc/effectslot.cpp + alc/effectslot.h alc/effects/base.h alc/effects/autowah.cpp alc/effects/chorus.cpp diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp index df04f430..8271f988 100644 --- a/al/auxeffectslot.cpp +++ b/al/auxeffectslot.cpp @@ -94,16 +94,16 @@ inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept void AddActiveEffectSlots(const al::span slotids, ALCcontext *context) { if(slotids.empty()) return; - ALeffectslotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)}; + EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)}; size_t newcount{curarray->size() + slotids.size()}; /* Insert the new effect slots into the head of the array, followed by the * existing ones. */ - ALeffectslotArray *newarray = ALeffectslot::CreatePtrArray(newcount); + EffectSlotArray *newarray = EffectSlot::CreatePtrArray(newcount); auto slotiter = std::transform(slotids.begin(), slotids.end(), newarray->begin(), - [context](ALuint id) noexcept -> ALeffectslot* - { return LookupEffectSlot(context, id); }); + [context](ALuint id) noexcept -> EffectSlot* + { return &LookupEffectSlot(context, id)->mSlot; }); std::copy(curarray->begin(), curarray->end(), slotiter); /* Remove any duplicates (first instance of each will be kept). */ @@ -122,7 +122,7 @@ void AddActiveEffectSlots(const al::span slotids, ALCcontext *cont if UNLIKELY(newcount < newarray->size()) { curarray = newarray; - newarray = ALeffectslot::CreatePtrArray(newcount); + newarray = EffectSlot::CreatePtrArray(newcount); std::copy_n(curarray->begin(), newcount, newarray->begin()); delete curarray; curarray = nullptr; @@ -139,24 +139,31 @@ void AddActiveEffectSlots(const al::span slotids, ALCcontext *cont void RemoveActiveEffectSlots(const al::span slotids, ALCcontext *context) { if(slotids.empty()) return; - ALeffectslotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)}; + EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)}; /* Don't shrink the allocated array size since we don't know how many (if * any) of the effect slots to remove are in the array. */ - ALeffectslotArray *newarray = ALeffectslot::CreatePtrArray(curarray->size()); + EffectSlotArray *newarray = EffectSlot::CreatePtrArray(curarray->size()); - /* Copy each element in curarray to newarray whose ID is not in slotids. */ - auto slotiter = std::copy_if(curarray->begin(), curarray->end(), newarray->begin(), - [slotids](const ALeffectslot *slot) -> bool - { return std::find(slotids.begin(), slotids.end(), slot->id) == slotids.end(); }); + auto new_end = std::copy(curarray->begin(), curarray->end(), newarray->begin()); + /* Remove elements from newarray that match any ID in slotids. */ + for(const ALuint id : slotids) + { + if(ALeffectslot *auxslot{LookupEffectSlot(context, id)}) + { + auto proc_match = [auxslot](EffectSlot *slot) noexcept -> bool + { return (slot == &auxslot->mSlot); }; + new_end = std::remove_if(newarray->begin(), new_end, proc_match); + } + } /* Reallocate with the new size. */ - auto newsize = static_cast(std::distance(newarray->begin(), slotiter)); + auto newsize = static_cast(std::distance(newarray->begin(), new_end)); if LIKELY(newsize != newarray->size()) { curarray = newarray; - newarray = ALeffectslot::CreatePtrArray(newsize); + newarray = EffectSlot::CreatePtrArray(newsize); std::copy_n(curarray->begin(), newsize, newarray->begin()); delete curarray; @@ -250,15 +257,6 @@ void FreeEffectSlot(ALCcontext *context, ALeffectslot *slot) } // namespace -ALeffectslotArray *ALeffectslot::CreatePtrArray(size_t count) noexcept -{ - /* Allocate space for twice as many pointers, so the mixer has scratch - * space to store a sorted list during mixing. - */ - void *ptr{al_calloc(alignof(ALeffectslotArray), ALeffectslotArray::Sizeof(count*2))}; - return new (ptr) ALeffectslotArray{count}; -} - AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots) START_API_FUNC @@ -814,7 +812,7 @@ ALeffectslot::~ALeffectslot() DecrementRef(Buffer->ref); Buffer = nullptr; - ALeffectslotProps *props{Params.Update.load()}; + EffectSlotProps *props{mSlot.Update.exchange(nullptr)}; if(props) { TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", @@ -824,8 +822,8 @@ ALeffectslot::~ALeffectslot() if(mWetBuffer) mWetBuffer->mInUse = false; - if(Params.mEffectState) - Params.mEffectState->release(); + if(mSlot.mEffectState) + mSlot.mEffectState->release(); } ALenum ALeffectslot::init() @@ -837,7 +835,7 @@ ALenum ALeffectslot::init() if(!Effect.State) return AL_OUT_OF_MEMORY; Effect.State->add_ref(); - Params.mEffectState = Effect.State.get(); + mSlot.mEffectState = Effect.State.get(); return AL_NO_ERROR; } @@ -882,7 +880,7 @@ ALenum ALeffectslot::initEffect(ALeffect *effect, ALCcontext *context) Effect.Props = effect->Props; /* Remove state references from old effect slot property updates. */ - ALeffectslotProps *props{context->mFreeEffectslotProps.load()}; + EffectSlotProps *props{context->mFreeEffectslotProps.load()}; while(props) { props->State = nullptr; @@ -895,12 +893,12 @@ ALenum ALeffectslot::initEffect(ALeffect *effect, ALCcontext *context) void ALeffectslot::updateProps(ALCcontext *context) { /* Get an unused property container, or allocate a new one as needed. */ - ALeffectslotProps *props{context->mFreeEffectslotProps.load(std::memory_order_relaxed)}; + EffectSlotProps *props{context->mFreeEffectslotProps.load(std::memory_order_relaxed)}; if(!props) - props = new ALeffectslotProps{}; + props = new EffectSlotProps{}; else { - ALeffectslotProps *next; + EffectSlotProps *next; do { next = props->next.load(std::memory_order_relaxed); } while(context->mFreeEffectslotProps.compare_exchange_weak(props, next, @@ -910,14 +908,14 @@ void ALeffectslot::updateProps(ALCcontext *context) /* Copy in current property values. */ props->Gain = Gain; props->AuxSendAuto = AuxSendAuto; - props->Target = Target; + props->Target = Target ? &Target->mSlot : nullptr; props->Type = Effect.Type; props->Props = Effect.Props; props->State = Effect.State; /* Set the new container for updating internal parameters. */ - props = Params.Update.exchange(props, std::memory_order_acq_rel); + props = mSlot.Update.exchange(props, std::memory_order_acq_rel); if(props) { /* If there was an unused update container, put it back in the @@ -931,11 +929,13 @@ void ALeffectslot::updateProps(ALCcontext *context) void UpdateAllEffectSlotProps(ALCcontext *context) { std::lock_guard _{context->mEffectSlotLock}; - ALeffectslotArray *auxslots{context->mActiveAuxSlots.load(std::memory_order_acquire)}; - for(ALeffectslot *slot : *auxslots) + EffectSlotArray *slots{context->mActiveAuxSlots.load(std::memory_order_acquire)}; + for(EffectSlot *slot : *slots) { - if(!slot->PropsClean.test_and_set(std::memory_order_acq_rel)) - slot->updateProps(context); + ALeffectslot *auxslot{reinterpret_cast( + reinterpret_cast(slot) - offsetof(ALeffectslot,mSlot))}; + if(!auxslot->PropsClean.test_and_set(std::memory_order_acq_rel)) + auxslot->updateProps(context); } } diff --git a/al/auxeffectslot.h b/al/auxeffectslot.h index 03ba034a..c2967c93 100644 --- a/al/auxeffectslot.h +++ b/al/auxeffectslot.h @@ -11,35 +11,16 @@ #include "alcmain.h" #include "almalloc.h" #include "atomic.h" +#include "effectslot.h" #include "effects/base.h" #include "intrusive_ptr.h" #include "vector.h" struct ALbuffer; struct ALeffect; -struct ALeffectslot; struct WetBuffer; -using ALeffectslotArray = al::FlexArray; - - -struct ALeffectslotProps { - float Gain; - bool AuxSendAuto; - ALeffectslot *Target; - - ALenum Type; - EffectProps Props; - - al::intrusive_ptr State; - - std::atomic next; - - DEF_NEWDEL(ALeffectslotProps) -}; - - enum class SlotState : ALenum { Initial = AL_INITIAL, Playing = AL_PLAYING, @@ -65,24 +46,7 @@ struct ALeffectslot { RefCount ref{0u}; - struct { - std::atomic Update{nullptr}; - - float Gain{1.0f}; - bool AuxSendAuto{true}; - ALeffectslot *Target{nullptr}; - - ALenum EffectType{AL_EFFECT_NULL}; - EffectProps mEffectProps{}; - EffectState *mEffectState{nullptr}; - - float RoomRolloff{0.0f}; /* Added to the source's room rolloff, not multiplied. */ - float DecayTime{0.0f}; - float DecayLFRatio{0.0f}; - float DecayHFRatio{0.0f}; - bool DecayHFLimit{false}; - float AirAbsorptionGainHF{1.0f}; - } Params; + EffectSlot mSlot; /* Self ID */ ALuint id{}; @@ -90,13 +54,6 @@ struct ALeffectslot { /* Mixing buffer used by the Wet mix. */ WetBuffer *mWetBuffer{nullptr}; - /* Wet buffer configuration is ACN channel order with N3D scaling. - * Consequently, effects that only want to work with mono input can use - * channel 0 by itself. Effects that want multichannel can process the - * ambisonics signal and make a B-Format source pan. - */ - MixParams Wet; - ALeffectslot() { PropsClean.test_and_set(std::memory_order_relaxed); } ALeffectslot(const ALeffectslot&) = delete; ALeffectslot& operator=(const ALeffectslot&) = delete; @@ -106,8 +63,6 @@ struct ALeffectslot { ALenum initEffect(ALeffect *effect, ALCcontext *context); void updateProps(ALCcontext *context); - static ALeffectslotArray *CreatePtrArray(size_t count) noexcept; - /* This can be new'd for the context's default effect slot. */ DEF_NEWDEL(ALeffectslot) }; diff --git a/al/source.cpp b/al/source.cpp index 64a7afc1..d089f24c 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -149,8 +149,8 @@ void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context auto copy_send = [](const ALsource::SendData &srcsend) noexcept -> VoiceProps::SendData { - VoiceProps::SendData ret; - ret.Slot = srcsend.Slot; + VoiceProps::SendData ret{}; + ret.Slot = srcsend.Slot ? &srcsend.Slot->mSlot : nullptr; ret.Gain = srcsend.Gain; ret.GainHF = srcsend.GainHF; ret.HFReference = srcsend.HFReference; @@ -159,6 +159,8 @@ void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context return ret; }; std::transform(source->Send.cbegin(), source->Send.cend(), props->Send, copy_send); + if(!props->Send[0].Slot && context->mDefaultSlot) + props->Send[0].Slot = &context->mDefaultSlot->mSlot; /* Set the new container for updating internal parameters. */ props = voice->mUpdate.exchange(props, std::memory_order_acq_rel); diff --git a/alc/alc.cpp b/alc/alc.cpp index c94a9d53..08bb4c2f 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -2122,7 +2122,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) slot->updateProps(context); } - if(ALeffectslotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_relaxed)}) + if(EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_relaxed)}) std::fill_n(curarray->end(), curarray->size(), nullptr); for(auto &sublist : context->mEffectSlotList) { @@ -2366,17 +2366,17 @@ ALCcontext::~ALCcontext() mNumSources = 0; count = 0; - ALeffectslotProps *eprops{mFreeEffectslotProps.exchange(nullptr, std::memory_order_acquire)}; + EffectSlotProps *eprops{mFreeEffectslotProps.exchange(nullptr, std::memory_order_acquire)}; while(eprops) { - ALeffectslotProps *next{eprops->next.load(std::memory_order_relaxed)}; + EffectSlotProps *next{eprops->next.load(std::memory_order_relaxed)}; delete eprops; eprops = next; ++count; } TRACE("Freed %zu AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s"); - if(ALeffectslotArray *curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)}) + if(EffectSlotArray *curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)}) { al::destroy_n(curarray->end(), curarray->size()); delete curarray; @@ -2455,13 +2455,13 @@ void ALCcontext::init() } } - ALeffectslotArray *auxslots; + EffectSlotArray *auxslots; if(!mDefaultSlot) - auxslots = ALeffectslot::CreatePtrArray(0); + auxslots = EffectSlot::CreatePtrArray(0); else { - auxslots = ALeffectslot::CreatePtrArray(1); - (*auxslots)[0] = mDefaultSlot.get(); + auxslots = EffectSlot::CreatePtrArray(1); + (*auxslots)[0] = &mDefaultSlot->mSlot; mDefaultSlot->mState = SlotState::Playing; } mActiveAuxSlots.store(auxslots, std::memory_order_relaxed); diff --git a/alc/alcontext.h b/alc/alcontext.h index 20e48253..3699b244 100644 --- a/alc/alcontext.h +++ b/alc/alcontext.h @@ -24,8 +24,9 @@ #include "voice.h" struct ALeffectslot; -struct ALeffectslotProps; struct ALsource; +struct EffectSlot; +struct EffectSlotProps; struct RingBuffer; @@ -148,7 +149,7 @@ struct ALCcontext : public al::intrusive_ref { std::atomic mFreeContextProps{nullptr}; std::atomic mFreeListenerProps{nullptr}; std::atomic mFreeVoiceProps{nullptr}; - std::atomic mFreeEffectslotProps{nullptr}; + std::atomic mFreeEffectslotProps{nullptr}; /* Asynchronous voice change actions are processed as a linked list of * VoiceChange objects by the mixer, which is atomically appended to. @@ -192,8 +193,8 @@ struct ALCcontext : public al::intrusive_ref { /* Wet buffers used by effect slots. */ al::vector mWetBuffers; - using ALeffectslotArray = al::FlexArray; - std::atomic mActiveAuxSlots{nullptr}; + using EffectSlotArray = al::FlexArray; + std::atomic mActiveAuxSlots{nullptr}; std::thread mEventThread; al::semaphore mEventSem; diff --git a/alc/alu.cpp b/alc/alu.cpp index 1073a160..8964a515 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -442,43 +442,43 @@ bool CalcListenerParams(ALCcontext *Context) return true; } -bool CalcEffectSlotParams(ALeffectslot *slot, ALeffectslot **sorted_slots, ALCcontext *context) +bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ALCcontext *context) { - ALeffectslotProps *props{slot->Params.Update.exchange(nullptr, std::memory_order_acq_rel)}; + EffectSlotProps *props{slot->Update.exchange(nullptr, std::memory_order_acq_rel)}; if(!props) return false; /* If the effect slot target changed, clear the first sorted entry to force * a re-sort. */ - if(slot->Params.Target != props->Target) + if(slot->Target != props->Target) *sorted_slots = nullptr; - slot->Params.Gain = props->Gain; - slot->Params.AuxSendAuto = props->AuxSendAuto; - slot->Params.Target = props->Target; - slot->Params.EffectType = props->Type; - slot->Params.mEffectProps = props->Props; + slot->Gain = props->Gain; + slot->AuxSendAuto = props->AuxSendAuto; + slot->Target = props->Target; + slot->EffectType = props->Type; + slot->mEffectProps = props->Props; if(IsReverbEffect(props->Type)) { - slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor; - slot->Params.DecayTime = props->Props.Reverb.DecayTime; - slot->Params.DecayLFRatio = props->Props.Reverb.DecayLFRatio; - slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio; - slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit; - slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF; + slot->RoomRolloff = props->Props.Reverb.RoomRolloffFactor; + slot->DecayTime = props->Props.Reverb.DecayTime; + slot->DecayLFRatio = props->Props.Reverb.DecayLFRatio; + slot->DecayHFRatio = props->Props.Reverb.DecayHFRatio; + slot->DecayHFLimit = props->Props.Reverb.DecayHFLimit; + slot->AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF; } else { - slot->Params.RoomRolloff = 0.0f; - slot->Params.DecayTime = 0.0f; - slot->Params.DecayLFRatio = 0.0f; - slot->Params.DecayHFRatio = 0.0f; - slot->Params.DecayHFLimit = false; - slot->Params.AirAbsorptionGainHF = 1.0f; + slot->RoomRolloff = 0.0f; + slot->DecayTime = 0.0f; + slot->DecayLFRatio = 0.0f; + slot->DecayHFRatio = 0.0f; + slot->DecayHFLimit = false; + slot->AirAbsorptionGainHF = 1.0f; } EffectState *state{props->State.release()}; - EffectState *oldstate{slot->Params.mEffectState}; - slot->Params.mEffectState = state; + EffectState *oldstate{slot->mEffectState}; + slot->mEffectState = state; /* Only release the old state if it won't get deleted, since we can't be * deleting/freeing anything in the mixer. @@ -508,14 +508,14 @@ bool CalcEffectSlotParams(ALeffectslot *slot, ALeffectslot **sorted_slots, ALCco AtomicReplaceHead(context->mFreeEffectslotProps, props); EffectTarget output; - if(ALeffectslot *target{slot->Params.Target}) + if(EffectSlot *target{slot->Target}) output = EffectTarget{&target->Wet, nullptr}; else { ALCdevice *device{context->mDevice.get()}; output = EffectTarget{&device->Dry, &device->RealOut}; } - state->update(context, slot, &slot->Params.mEffectProps, output); + state->update(context, slot, &slot->mEffectProps, output); return true; } @@ -701,7 +701,7 @@ struct GainTriplet { float Base, HF, LF; }; void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, const float zpos, const float Distance, const float Spread, const GainTriplet &DryGain, - const al::span WetGain, ALeffectslot *(&SendSlots)[MAX_SENDS], + const al::span WetGain, EffectSlot *(&SendSlots)[MAX_SENDS], const VoiceProps *props, const ALlistener &Listener, const ALCdevice *Device) { static const ChanMap MonoMap[1]{ @@ -884,7 +884,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con voice->mChans[0].mDryParams.Gains.Target); for(ALuint i{0};i < NumSends;i++) { - if(const ALeffectslot *Slot{SendSlots[i]}) + if(const EffectSlot *Slot{SendSlots[i]}) ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base*scales[0], voice->mChans[0].mWetParams[i].Gains.Target); } @@ -946,7 +946,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con for(ALuint i{0};i < NumSends;i++) { - if(const ALeffectslot *Slot{SendSlots[i]}) + if(const EffectSlot *Slot{SendSlots[i]}) ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } @@ -991,7 +991,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con for(ALuint i{0};i < NumSends;i++) { - if(const ALeffectslot *Slot{SendSlots[i]}) + if(const EffectSlot *Slot{SendSlots[i]}) ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } @@ -1037,7 +1037,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con continue; for(ALuint i{0};i < NumSends;i++) { - if(const ALeffectslot *Slot{SendSlots[i]}) + if(const EffectSlot *Slot{SendSlots[i]}) ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base * downmix_gain, voice->mChans[c].mWetParams[i].Gains.Target); } @@ -1069,7 +1069,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con for(ALuint i{0};i < NumSends;i++) { - if(const ALeffectslot *Slot{SendSlots[i]}) + if(const EffectSlot *Slot{SendSlots[i]}) ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } @@ -1131,7 +1131,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con voice->mChans[c].mDryParams.Gains.Target); for(ALuint i{0};i < NumSends;i++) { - if(const ALeffectslot *Slot{SendSlots[i]}) + if(const EffectSlot *Slot{SendSlots[i]}) ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base * downmix_gain, voice->mChans[c].mWetParams[i].Gains.Target); } @@ -1173,7 +1173,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con voice->mChans[c].mDryParams.Gains.Target); for(ALuint i{0};i < NumSends;i++) { - if(const ALeffectslot *Slot{SendSlots[i]}) + if(const EffectSlot *Slot{SendSlots[i]}) ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } @@ -1223,15 +1223,13 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const ALCcontext *ALContext) { const ALCdevice *Device{ALContext->mDevice.get()}; - ALeffectslot *SendSlots[MAX_SENDS]; + EffectSlot *SendSlots[MAX_SENDS]; voice->mDirect.Buffer = Device->Dry.Buffer; for(ALuint i{0};i < Device->NumAuxSends;i++) { SendSlots[i] = props->Send[i].Slot; - if(!SendSlots[i] && i == 0) - SendSlots[i] = ALContext->mDefaultSlot.get(); - if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL) + if(!SendSlots[i] || SendSlots[i]->EffectType == AL_EFFECT_NULL) { SendSlots[i] = nullptr; voice->mSend[i].Buffer = {}; @@ -1277,15 +1275,13 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ALCcontex /* Set mixing buffers and get send parameters. */ voice->mDirect.Buffer = Device->Dry.Buffer; - ALeffectslot *SendSlots[MAX_SENDS]; + EffectSlot *SendSlots[MAX_SENDS]; float RoomRolloff[MAX_SENDS]; GainTriplet DecayDistance[MAX_SENDS]; for(ALuint i{0};i < NumSends;i++) { SendSlots[i] = props->Send[i].Slot; - if(!SendSlots[i] && i == 0) - SendSlots[i] = ALContext->mDefaultSlot.get(); - if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL) + if(!SendSlots[i] || SendSlots[i]->EffectType == AL_EFFECT_NULL) { SendSlots[i] = nullptr; RoomRolloff[i] = 0.0f; @@ -1293,18 +1289,18 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ALCcontex DecayDistance[i].LF = 0.0f; DecayDistance[i].HF = 0.0f; } - else if(SendSlots[i]->Params.AuxSendAuto) + else if(SendSlots[i]->AuxSendAuto) { - RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor; + RoomRolloff[i] = SendSlots[i]->RoomRolloff + props->RoomRolloffFactor; /* Calculate the distances to where this effect's decay reaches * -60dB. */ - DecayDistance[i].Base = SendSlots[i]->Params.DecayTime * SpeedOfSoundMetersPerSec; - DecayDistance[i].LF = DecayDistance[i].Base * SendSlots[i]->Params.DecayLFRatio; - DecayDistance[i].HF = DecayDistance[i].Base * SendSlots[i]->Params.DecayHFRatio; - if(SendSlots[i]->Params.DecayHFLimit) + DecayDistance[i].Base = SendSlots[i]->DecayTime * SpeedOfSoundMetersPerSec; + DecayDistance[i].LF = DecayDistance[i].Base * SendSlots[i]->DecayLFRatio; + DecayDistance[i].HF = DecayDistance[i].Base * SendSlots[i]->DecayHFRatio; + if(SendSlots[i]->DecayHFLimit) { - const float airAbsorption{SendSlots[i]->Params.AirAbsorptionGainHF}; + const float airAbsorption{SendSlots[i]->AirAbsorptionGainHF}; if(airAbsorption < 1.0f) { /* Calculate the distance to where this effect's air @@ -1700,7 +1696,7 @@ void ProcessVoiceChanges(ALCcontext *ctx) ctx->mCurrentVoiceChange.store(cur, std::memory_order_release); } -void ProcessParamUpdates(ALCcontext *ctx, const ALeffectslotArray &slots, +void ProcessParamUpdates(ALCcontext *ctx, const EffectSlotArray &slots, const al::span voices) { ProcessVoiceChanges(ctx); @@ -1710,8 +1706,8 @@ void ProcessParamUpdates(ALCcontext *ctx, const ALeffectslotArray &slots, { bool force{CalcContextParams(ctx)}; force |= CalcListenerParams(ctx); - auto sorted_slots = const_cast(slots.data() + slots.size()); - for(ALeffectslot *slot : slots) + auto sorted_slots = const_cast(slots.data() + slots.size()); + for(EffectSlot *slot : slots) force |= CalcEffectSlotParams(slot, sorted_slots, ctx); for(Voice *voice : voices) @@ -1730,14 +1726,14 @@ void ProcessContexts(ALCdevice *device, const ALuint SamplesToDo) for(ALCcontext *ctx : *device->mContexts.load(std::memory_order_acquire)) { - const ALeffectslotArray &auxslots = *ctx->mActiveAuxSlots.load(std::memory_order_acquire); + const EffectSlotArray &auxslots = *ctx->mActiveAuxSlots.load(std::memory_order_acquire); const al::span voices{ctx->getVoicesSpanAcquired()}; /* Process pending propery updates for objects on the context. */ ProcessParamUpdates(ctx, auxslots, voices); /* Clear auxiliary effect slot mixing buffers. */ - for(ALeffectslot *slot : auxslots) + for(EffectSlot *slot : auxslots) { for(auto &buffer : slot->Wet.Buffer) buffer.fill(0.0f); @@ -1760,7 +1756,7 @@ void ProcessContexts(ALCdevice *device, const ALuint SamplesToDo) /* Sort the slots into extra storage, so that effect slots come * before their effect slot target (or their targets' target). */ - const al::span sorted_slots{const_cast(slots_end), + const al::span sorted_slots{const_cast(slots_end), num_slots}; /* Skip sorting if it has already been done. */ if(!sorted_slots[0]) @@ -1771,8 +1767,8 @@ void ProcessContexts(ALCdevice *device, const ALuint SamplesToDo) */ std::copy(slots, slots_end, sorted_slots.begin()); auto split_point = std::partition(sorted_slots.begin(), sorted_slots.end(), - [](const ALeffectslot *slot) noexcept -> bool - { return slot->Params.Target != nullptr; }); + [](const EffectSlot *slot) noexcept -> bool + { return slot->Target != nullptr; }); /* There must be at least one slot without a slot target. */ assert(split_point != sorted_slots.end()); @@ -1801,15 +1797,15 @@ void ProcessContexts(ALCdevice *device, const ALuint SamplesToDo) --next_target; split_point = std::partition(sorted_slots.begin(), split_point, - [next_target](const ALeffectslot *slot) noexcept -> bool - { return slot->Params.Target != *next_target; }); + [next_target](const EffectSlot *slot) noexcept -> bool + { return slot->Target != *next_target; }); } while(split_point - sorted_slots.begin() > 1); } } - for(const ALeffectslot *slot : sorted_slots) + for(const EffectSlot *slot : sorted_slots) { - EffectState *state{slot->Params.mEffectState}; + EffectState *state{slot->mEffectState}; state->process(SamplesToDo, slot->Wet.Buffer, state->mOutTarget); } } diff --git a/alc/effects/autowah.cpp b/alc/effects/autowah.cpp index 3ebb1544..423d1b54 100644 --- a/alc/effects/autowah.cpp +++ b/alc/effects/autowah.cpp @@ -70,8 +70,10 @@ struct AutowahState final : public EffectState { void deviceUpdate(const ALCdevice *device) override; - void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; + void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props, + const EffectTarget target) override; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) override; DEF_NEWDEL(AutowahState) }; @@ -102,7 +104,8 @@ void AutowahState::deviceUpdate(const ALCdevice*) } } -void AutowahState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) +void AutowahState::update(const ALCcontext *context, const EffectSlot *slot, + const EffectProps *props, const EffectTarget target) { const ALCdevice *device{context->mDevice.get()}; const auto frequency = static_cast(device->Frequency); @@ -119,11 +122,12 @@ void AutowahState::update(const ALCcontext *context, const ALeffectslot *slot, c mOutTarget = target.Main->Buffer; auto set_gains = [slot,target](auto &chan, al::span coeffs) - { ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, chan.TargetGains); }; + { ComputePanGains(target.Main, coeffs.data(), slot->Gain, chan.TargetGains); }; SetAmbiPanIdentity(std::begin(mChans), slot->Wet.Buffer.size(), set_gains); } -void AutowahState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) +void AutowahState::process(const size_t samplesToDo, + const al::span samplesIn, const al::span samplesOut) { const float attack_rate{mAttackRate}; const float release_rate{mReleaseRate}; diff --git a/alc/effects/base.h b/alc/effects/base.h index db38fc49..0efd5599 100644 --- a/alc/effects/base.h +++ b/alc/effects/base.h @@ -10,7 +10,7 @@ #include "atomic.h" #include "intrusive_ptr.h" -struct ALeffectslot; +struct EffectSlot; struct BufferStorage; @@ -168,8 +168,10 @@ struct EffectState : public al::intrusive_ref { virtual void deviceUpdate(const ALCdevice *device) = 0; virtual void setBuffer(const ALCdevice* /*device*/, const BufferStorage* /*buffer*/) { } - virtual void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) = 0; - virtual void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) = 0; + virtual void update(const ALCcontext *context, const EffectSlot *slot, + const EffectProps *props, const EffectTarget target) = 0; + virtual void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) = 0; }; diff --git a/alc/effects/chorus.cpp b/alc/effects/chorus.cpp index 7e68508d..a531c5e1 100644 --- a/alc/effects/chorus.cpp +++ b/alc/effects/chorus.cpp @@ -81,8 +81,10 @@ struct ChorusState final : public EffectState { void getSinusoidDelays(ALuint (*delays)[MAX_UPDATE_SAMPLES], const size_t todo); void deviceUpdate(const ALCdevice *device) override; - void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; + void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props, + const EffectTarget target) override; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) override; DEF_NEWDEL(ChorusState) }; @@ -104,7 +106,8 @@ void ChorusState::deviceUpdate(const ALCdevice *Device) } } -void ChorusState::update(const ALCcontext *Context, const ALeffectslot *Slot, const EffectProps *props, const EffectTarget target) +void ChorusState::update(const ALCcontext *Context, const EffectSlot *Slot, + const EffectProps *props, const EffectTarget target) { constexpr ALsizei mindelay{(MAX_RESAMPLER_PADDING>>1) << MixerFracBits}; @@ -135,8 +138,8 @@ void ChorusState::update(const ALCcontext *Context, const ALeffectslot *Slot, co const auto rcoeffs = CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f}, 0.0f); mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, lcoeffs.data(), Slot->Params.Gain, mGains[0].Target); - ComputePanGains(target.Main, rcoeffs.data(), Slot->Params.Gain, mGains[1].Target); + ComputePanGains(target.Main, lcoeffs.data(), Slot->Gain, mGains[0].Target); + ComputePanGains(target.Main, rcoeffs.data(), Slot->Gain, mGains[1].Target); float rate{props->Chorus.Rate}; if(!(rate > 0.0f)) diff --git a/alc/effects/compressor.cpp b/alc/effects/compressor.cpp index e02dec3b..9d92fdc7 100644 --- a/alc/effects/compressor.cpp +++ b/alc/effects/compressor.cpp @@ -50,8 +50,10 @@ struct CompressorState final : public EffectState { void deviceUpdate(const ALCdevice *device) override; - void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; + void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props, + const EffectTarget target) override; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) override; DEF_NEWDEL(CompressorState) }; @@ -71,17 +73,19 @@ void CompressorState::deviceUpdate(const ALCdevice *device) mReleaseMult = std::pow(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount); } -void CompressorState::update(const ALCcontext*, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) +void CompressorState::update(const ALCcontext*, const EffectSlot *slot, + const EffectProps *props, const EffectTarget target) { mEnabled = props->Compressor.OnOff; mOutTarget = target.Main->Buffer; auto set_gains = [slot,target](auto &gains, al::span coeffs) - { ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, gains); }; + { ComputePanGains(target.Main, coeffs.data(), slot->Gain, gains); }; SetAmbiPanIdentity(std::begin(mGain), slot->Wet.Buffer.size(), set_gains); } -void CompressorState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) +void CompressorState::process(const size_t samplesToDo, + const al::span samplesIn, const al::span samplesOut) { for(size_t base{0u};base < samplesToDo;) { diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index ffd2f0c7..c4bc41dc 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -177,8 +177,10 @@ struct ConvolutionState final : public EffectState { void deviceUpdate(const ALCdevice *device) override; void setBuffer(const ALCdevice *device, const BufferStorage *buffer) override; - void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; + void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props, + const EffectTarget target) override; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) override; DEF_NEWDEL(ConvolutionState) }; @@ -304,7 +306,7 @@ void ConvolutionState::setBuffer(const ALCdevice *device, const BufferStorage *b } -void ConvolutionState::update(const ALCcontext *context, const ALeffectslot *slot, +void ConvolutionState::update(const ALCcontext *context, const EffectSlot *slot, const EffectProps* /*props*/, const EffectTarget target) { /* NOTE: Stereo and Rear are slightly different from normal mixing (as @@ -361,7 +363,7 @@ void ConvolutionState::update(const ALCcontext *context, const ALeffectslot *slo for(auto &chan : *mChans) std::fill(std::begin(chan.Target), std::end(chan.Target), 0.0f); - const float gain{slot->Params.Gain}; + const float gain{slot->Gain}; if(mChannels == FmtBFormat3D || mChannels == FmtBFormat2D) { ALCdevice *device{context->mDevice.get()}; diff --git a/alc/effects/dedicated.cpp b/alc/effects/dedicated.cpp index 2f985412..283d009a 100644 --- a/alc/effects/dedicated.cpp +++ b/alc/effects/dedicated.cpp @@ -38,8 +38,10 @@ struct DedicatedState final : public EffectState { void deviceUpdate(const ALCdevice *device) override; - void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; + void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props, + const EffectTarget target) override; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) override; DEF_NEWDEL(DedicatedState) }; @@ -49,13 +51,14 @@ void DedicatedState::deviceUpdate(const ALCdevice*) std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f); } -void DedicatedState::update(const ALCcontext*, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) +void DedicatedState::update(const ALCcontext*, const EffectSlot *slot, + const EffectProps *props, const EffectTarget target) { std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f); - const float Gain{slot->Params.Gain * props->Dedicated.Gain}; + const float Gain{slot->Gain * props->Dedicated.Gain}; - if(slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT) + if(slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT) { const ALuint idx{!target.RealOut ? INVALID_CHANNEL_INDEX : GetChannelIdxByName(*target.RealOut, LFE)}; @@ -65,7 +68,7 @@ void DedicatedState::update(const ALCcontext*, const ALeffectslot *slot, const E mTargetGains[idx] = Gain; } } - else if(slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE) + else if(slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE) { /* Dialog goes to the front-center speaker if it exists, otherwise it * plays from the front-center location. */ diff --git a/alc/effects/distortion.cpp b/alc/effects/distortion.cpp index be787b78..a199952f 100644 --- a/alc/effects/distortion.cpp +++ b/alc/effects/distortion.cpp @@ -47,8 +47,10 @@ struct DistortionState final : public EffectState { void deviceUpdate(const ALCdevice *device) override; - void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; + void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props, + const EffectTarget target) override; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) override; DEF_NEWDEL(DistortionState) }; @@ -59,7 +61,8 @@ void DistortionState::deviceUpdate(const ALCdevice*) mBandpass.clear(); } -void DistortionState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) +void DistortionState::update(const ALCcontext *context, const EffectSlot *slot, + const EffectProps *props, const EffectTarget target) { const ALCdevice *device{context->mDevice.get()}; @@ -85,7 +88,7 @@ void DistortionState::update(const ALCcontext *context, const ALeffectslot *slot const auto coeffs = CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}, 0.0f); mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain*props->Distortion.Gain, mGain); + ComputePanGains(target.Main, coeffs.data(), slot->Gain*props->Distortion.Gain, mGain); } void DistortionState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) diff --git a/alc/effects/echo.cpp b/alc/effects/echo.cpp index b53f4f30..93305cdb 100644 --- a/alc/effects/echo.cpp +++ b/alc/effects/echo.cpp @@ -58,8 +58,10 @@ struct EchoState final : public EffectState { alignas(16) float mTempBuffer[2][BUFFERSIZE]; void deviceUpdate(const ALCdevice *device) override; - void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; + void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props, + const EffectTarget target) override; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) override; DEF_NEWDEL(EchoState) }; @@ -83,7 +85,8 @@ void EchoState::deviceUpdate(const ALCdevice *Device) } } -void EchoState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) +void EchoState::update(const ALCcontext *context, const EffectSlot *slot, + const EffectProps *props, const EffectTarget target) { const ALCdevice *device{context->mDevice.get()}; const auto frequency = static_cast(device->Frequency); @@ -103,8 +106,8 @@ void EchoState::update(const ALCcontext *context, const ALeffectslot *slot, cons const auto coeffs1 = CalcAngleCoeffs( angle, 0.0f, 0.0f); mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, coeffs0.data(), slot->Params.Gain, mGains[0].Target); - ComputePanGains(target.Main, coeffs1.data(), slot->Params.Gain, mGains[1].Target); + ComputePanGains(target.Main, coeffs0.data(), slot->Gain, mGains[0].Target); + ComputePanGains(target.Main, coeffs1.data(), slot->Gain, mGains[1].Target); } void EchoState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) diff --git a/alc/effects/equalizer.cpp b/alc/effects/equalizer.cpp index 8f002f8c..2f02182c 100644 --- a/alc/effects/equalizer.cpp +++ b/alc/effects/equalizer.cpp @@ -92,8 +92,10 @@ struct EqualizerState final : public EffectState { void deviceUpdate(const ALCdevice *device) override; - void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; + void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props, + const EffectTarget target) override; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) override; DEF_NEWDEL(EqualizerState) }; @@ -107,7 +109,8 @@ void EqualizerState::deviceUpdate(const ALCdevice*) } } -void EqualizerState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) +void EqualizerState::update(const ALCcontext *context, const EffectSlot *slot, + const EffectProps *props, const EffectTarget target) { const ALCdevice *device{context->mDevice.get()}; auto frequency = static_cast(device->Frequency); @@ -148,7 +151,7 @@ void EqualizerState::update(const ALCcontext *context, const ALeffectslot *slot, mOutTarget = target.Main->Buffer; auto set_gains = [slot,target](auto &chan, al::span coeffs) - { ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, chan.TargetGains); }; + { ComputePanGains(target.Main, coeffs.data(), slot->Gain, chan.TargetGains); }; SetAmbiPanIdentity(std::begin(mChans), slot->Wet.Buffer.size(), set_gains); } diff --git a/alc/effects/fshifter.cpp b/alc/effects/fshifter.cpp index f3f409e0..3eb4bb79 100644 --- a/alc/effects/fshifter.cpp +++ b/alc/effects/fshifter.cpp @@ -83,8 +83,10 @@ struct FshifterState final : public EffectState { void deviceUpdate(const ALCdevice *device) override; - void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; + void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props, + const EffectTarget target) override; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) override; DEF_NEWDEL(FshifterState) }; @@ -109,7 +111,8 @@ void FshifterState::deviceUpdate(const ALCdevice*) } } -void FshifterState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) +void FshifterState::update(const ALCcontext *context, const EffectSlot *slot, + const EffectProps *props, const EffectTarget target) { const ALCdevice *device{context->mDevice.get()}; @@ -152,8 +155,8 @@ void FshifterState::update(const ALCcontext *context, const ALeffectslot *slot, const auto rcoeffs = CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f}, 0.0f); mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, lcoeffs.data(), slot->Params.Gain, mGains[0].Target); - ComputePanGains(target.Main, rcoeffs.data(), slot->Params.Gain, mGains[1].Target); + ComputePanGains(target.Main, lcoeffs.data(), slot->Gain, mGains[0].Target); + ComputePanGains(target.Main, rcoeffs.data(), slot->Gain, mGains[1].Target); } void FshifterState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) diff --git a/alc/effects/modulator.cpp b/alc/effects/modulator.cpp index f75e4dc6..cd48f91e 100644 --- a/alc/effects/modulator.cpp +++ b/alc/effects/modulator.cpp @@ -83,8 +83,10 @@ struct ModulatorState final : public EffectState { void deviceUpdate(const ALCdevice *device) override; - void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; + void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props, + const EffectTarget target) override; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) override; DEF_NEWDEL(ModulatorState) }; @@ -98,7 +100,8 @@ void ModulatorState::deviceUpdate(const ALCdevice*) } } -void ModulatorState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) +void ModulatorState::update(const ALCcontext *context, const EffectSlot *slot, + const EffectProps *props, const EffectTarget target) { const ALCdevice *device{context->mDevice.get()}; @@ -123,7 +126,7 @@ void ModulatorState::update(const ALCcontext *context, const ALeffectslot *slot, mOutTarget = target.Main->Buffer; auto set_gains = [slot,target](auto &chan, al::span coeffs) - { ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, chan.TargetGains); }; + { ComputePanGains(target.Main, coeffs.data(), slot->Gain, chan.TargetGains); }; SetAmbiPanIdentity(std::begin(mChans), slot->Wet.Buffer.size(), set_gains); } diff --git a/alc/effects/null.cpp b/alc/effects/null.cpp index 5bef1827..2f676d4d 100644 --- a/alc/effects/null.cpp +++ b/alc/effects/null.cpp @@ -19,8 +19,10 @@ struct NullState final : public EffectState { ~NullState() override; void deviceUpdate(const ALCdevice *device) override; - void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; + void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props, + const EffectTarget target) override; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) override; DEF_NEWDEL(NullState) }; @@ -47,7 +49,7 @@ void NullState::deviceUpdate(const ALCdevice* /*device*/) /* This updates the effect state with new properties. This is called any time * the effect is (re)loaded into a slot. */ -void NullState::update(const ALCcontext* /*context*/, const ALeffectslot* /*slot*/, +void NullState::update(const ALCcontext* /*context*/, const EffectSlot* /*slot*/, const EffectProps* /*props*/, const EffectTarget /*target*/) { } diff --git a/alc/effects/pshifter.cpp b/alc/effects/pshifter.cpp index 44ddc694..4326c929 100644 --- a/alc/effects/pshifter.cpp +++ b/alc/effects/pshifter.cpp @@ -97,8 +97,10 @@ struct PshifterState final : public EffectState { void deviceUpdate(const ALCdevice *device) override; - void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; + void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props, + const EffectTarget target) override; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) override; DEF_NEWDEL(PshifterState) }; @@ -123,7 +125,8 @@ void PshifterState::deviceUpdate(const ALCdevice *device) std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f); } -void PshifterState::update(const ALCcontext*, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) +void PshifterState::update(const ALCcontext*, const EffectSlot *slot, + const EffectProps *props, const EffectTarget target) { const int tune{props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune}; const float pitch{std::pow(2.0f, static_cast(tune) / 1200.0f)}; @@ -133,7 +136,7 @@ void PshifterState::update(const ALCcontext*, const ALeffectslot *slot, const Ef const auto coeffs = CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}, 0.0f); mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, mTargetGains); + ComputePanGains(target.Main, coeffs.data(), slot->Gain, mTargetGains); } void PshifterState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) diff --git a/alc/effects/reverb.cpp b/alc/effects/reverb.cpp index 3fa0b271..45464193 100644 --- a/alc/effects/reverb.cpp +++ b/alc/effects/reverb.cpp @@ -529,8 +529,10 @@ struct ReverbState final : public EffectState { const float fadeStep); void deviceUpdate(const ALCdevice *device) override; - void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; + void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props, + const EffectTarget target) override; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) override; DEF_NEWDEL(ReverbState) }; @@ -984,7 +986,8 @@ void ReverbState::update3DPanning(const float *ReflectionsPan, const float *Late } } -void ReverbState::update(const ALCcontext *Context, const ALeffectslot *Slot, const EffectProps *props, const EffectTarget target) +void ReverbState::update(const ALCcontext *Context, const EffectSlot *Slot, + const EffectProps *props, const EffectTarget target) { const ALCdevice *Device{Context->mDevice.get()}; const auto frequency = static_cast(Device->Frequency); @@ -1036,7 +1039,7 @@ void ReverbState::update(const ALCcontext *Context, const ALeffectslot *Slot, co props->Reverb.DecayTime, hfDecayTime, lf0norm, hf0norm, frequency); /* Update early and late 3D panning. */ - const float gain{props->Reverb.Gain * Slot->Params.Gain * ReverbBoost}; + const float gain{props->Reverb.Gain * Slot->Gain * ReverbBoost}; update3DPanning(props->Reverb.ReflectionsPan, props->Reverb.LateReverbPan, props->Reverb.ReflectionsGain*gain, props->Reverb.LateReverbGain*gain, target); diff --git a/alc/effects/vmorpher.cpp b/alc/effects/vmorpher.cpp index 0ffe23e6..8e945396 100644 --- a/alc/effects/vmorpher.cpp +++ b/alc/effects/vmorpher.cpp @@ -137,8 +137,10 @@ struct VmorpherState final : public EffectState { alignas(16) float mLfo[MAX_UPDATE_SAMPLES]{}; void deviceUpdate(const ALCdevice *device) override; - void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; + void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props, + const EffectTarget target) override; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) override; static std::array getFiltersByPhoneme(ALenum phoneme, float frequency, float pitch); @@ -206,7 +208,8 @@ void VmorpherState::deviceUpdate(const ALCdevice* /*device*/) } } -void VmorpherState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) +void VmorpherState::update(const ALCcontext *context, const EffectSlot *slot, + const EffectProps *props, const EffectTarget target) { const ALCdevice *device{context->mDevice.get()}; const float frequency{static_cast(device->Frequency)}; @@ -239,7 +242,7 @@ void VmorpherState::update(const ALCcontext *context, const ALeffectslot *slot, mOutTarget = target.Main->Buffer; auto set_gains = [slot,target](auto &chan, al::span coeffs) - { ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, chan.TargetGains); }; + { ComputePanGains(target.Main, coeffs.data(), slot->Gain, chan.TargetGains); }; SetAmbiPanIdentity(std::begin(mChans), slot->Wet.Buffer.size(), set_gains); } diff --git a/alc/effectslot.cpp b/alc/effectslot.cpp new file mode 100644 index 00000000..f3324858 --- /dev/null +++ b/alc/effectslot.cpp @@ -0,0 +1,18 @@ + +#include "config.h" + +#include "effectslot.h" + +#include + +#include "almalloc.h" + + +EffectSlotArray *EffectSlot::CreatePtrArray(size_t count) noexcept +{ + /* Allocate space for twice as many pointers, so the mixer has scratch + * space to store a sorted list during mixing. + */ + void *ptr{al_calloc(alignof(EffectSlotArray), EffectSlotArray::Sizeof(count*2))}; + return new(ptr) EffectSlotArray{count}; +} diff --git a/alc/effectslot.h b/alc/effectslot.h new file mode 100644 index 00000000..2bef5477 --- /dev/null +++ b/alc/effectslot.h @@ -0,0 +1,63 @@ +#ifndef EFFECTSLOT_H +#define EFFECTSLOT_H + +#include + +#include "almalloc.h" +#include "alcmain.h" +#include "effects/base.h" +#include "intrusive_ptr.h" + + +struct EffectSlot; + +using EffectSlotArray = al::FlexArray; + + +struct EffectSlotProps { + float Gain; + bool AuxSendAuto; + EffectSlot *Target; + + ALenum Type; + EffectProps Props; + + al::intrusive_ptr State; + + std::atomic next; + + DEF_NEWDEL(EffectSlotProps) +}; + + +struct EffectSlot { + std::atomic Update{nullptr}; + + /* Wet buffer configuration is ACN channel order with N3D scaling. + * Consequently, effects that only want to work with mono input can use + * channel 0 by itself. Effects that want multichannel can process the + * ambisonics signal and make a B-Format source pan. + */ + MixParams Wet; + + float Gain{1.0f}; + bool AuxSendAuto{true}; + EffectSlot *Target{nullptr}; + + ALenum EffectType{}; + EffectProps mEffectProps{}; + EffectState *mEffectState{nullptr}; + + float RoomRolloff{0.0f}; /* Added to the source's room rolloff, not multiplied. */ + float DecayTime{0.0f}; + float DecayLFRatio{0.0f}; + float DecayHFRatio{0.0f}; + bool DecayHFLimit{false}; + float AirAbsorptionGainHF{1.0f}; + + static EffectSlotArray *CreatePtrArray(size_t count) noexcept; + + DISABLE_ALLOC() +}; + +#endif /* EFFECTSLOT_H */ diff --git a/alc/panning.cpp b/alc/panning.cpp index 47d916d4..eadf6f12 100644 --- a/alc/panning.cpp +++ b/alc/panning.cpp @@ -1052,7 +1052,7 @@ void aluInitEffectPanning(ALeffectslot *slot, ALCcontext *context) if(wetbuffer_iter->get() == slot->mWetBuffer) { slot->mWetBuffer = nullptr; - slot->Wet.Buffer = {}; + slot->mSlot.Wet.Buffer = {}; *wetbuffer_iter = WetBufferPtr{new(FamCount(count)) WetBuffer{count}}; @@ -1080,11 +1080,12 @@ void aluInitEffectPanning(ALeffectslot *slot, ALCcontext *context) wetbuffer->mInUse = true; auto acnmap_end = AmbiIndex::FromACN.begin() + count; - auto iter = std::transform(AmbiIndex::FromACN.begin(), acnmap_end, slot->Wet.AmbiMap.begin(), + auto iter = std::transform(AmbiIndex::FromACN.begin(), acnmap_end, + slot->mSlot.Wet.AmbiMap.begin(), [](const uint8_t &acn) noexcept -> BFChannelConfig { return BFChannelConfig{1.0f, acn}; }); - std::fill(iter, slot->Wet.AmbiMap.end(), BFChannelConfig{}); - slot->Wet.Buffer = wetbuffer->mBuffer; + std::fill(iter, slot->mSlot.Wet.AmbiMap.end(), BFChannelConfig{}); + slot->mSlot.Wet.Buffer = wetbuffer->mBuffer; } diff --git a/alc/voice.h b/alc/voice.h index a9774fd3..7822ea39 100644 --- a/alc/voice.h +++ b/alc/voice.h @@ -17,6 +17,7 @@ #include "filters/splitter.h" #include "hrtf.h" +struct EffectSlot; enum class DistanceModel; @@ -160,7 +161,7 @@ struct VoiceProps { float LFReference; } Direct; struct SendData { - ALeffectslot *Slot; + EffectSlot *Slot; float Gain; float GainHF; float HFReference; -- cgit v1.2.3 From 32b9a46b39e3bfb3ccf1e05c520ce40232efa5d9 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 25 Nov 2020 13:55:29 -0800 Subject: Move AL EffectProp handling to separate sources --- CMakeLists.txt | 15 ++ al/effect.cpp | 59 +++-- al/effect.h | 1 + al/effects/autowah.cpp | 109 +++++++++ al/effects/chorus.cpp | 250 ++++++++++++++++++++ al/effects/compressor.cpp | 72 ++++++ al/effects/convolution.cpp | 93 ++++++++ al/effects/dedicated.cpp | 72 ++++++ al/effects/distortion.cpp | 114 +++++++++ al/effects/echo.cpp | 108 +++++++++ al/effects/effects.h | 75 ++++++ al/effects/equalizer.cpp | 169 ++++++++++++++ al/effects/fshifter.cpp | 104 +++++++++ al/effects/modulator.cpp | 110 +++++++++ al/effects/null.cpp | 93 ++++++++ al/effects/pshifter.cpp | 84 +++++++ al/effects/reverb.cpp | 556 ++++++++++++++++++++++++++++++++++++++++++++ al/effects/vmorpher.cpp | 141 +++++++++++ alc/effects/autowah.cpp | 94 -------- alc/effects/base.h | 31 --- alc/effects/chorus.cpp | 238 ------------------- alc/effects/compressor.cpp | 60 ----- alc/effects/convolution.cpp | 94 +------- alc/effects/dedicated.cpp | 58 ----- alc/effects/distortion.cpp | 102 -------- alc/effects/echo.cpp | 96 -------- alc/effects/equalizer.cpp | 157 ------------- alc/effects/fshifter.cpp | 92 -------- alc/effects/modulator.cpp | 98 -------- alc/effects/null.cpp | 88 ------- alc/effects/pshifter.cpp | 81 +------ alc/effects/reverb.cpp | 543 ------------------------------------------ alc/effects/vmorpher.cpp | 129 ---------- 33 files changed, 2203 insertions(+), 1983 deletions(-) create mode 100644 al/effects/autowah.cpp create mode 100644 al/effects/chorus.cpp create mode 100644 al/effects/compressor.cpp create mode 100644 al/effects/convolution.cpp create mode 100644 al/effects/dedicated.cpp create mode 100644 al/effects/distortion.cpp create mode 100644 al/effects/echo.cpp create mode 100644 al/effects/effects.h create mode 100644 al/effects/equalizer.cpp create mode 100644 al/effects/fshifter.cpp create mode 100644 al/effects/modulator.cpp create mode 100644 al/effects/null.cpp create mode 100644 al/effects/pshifter.cpp create mode 100644 al/effects/reverb.cpp create mode 100644 al/effects/vmorpher.cpp (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 06dc5f9c..59637da0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -599,6 +599,21 @@ set(OPENAL_OBJS al/buffer.h al/effect.cpp al/effect.h + al/effects/autowah.cpp + al/effects/chorus.cpp + al/effects/compressor.cpp + al/effects/convolution.cpp + al/effects/dedicated.cpp + al/effects/distortion.cpp + al/effects/echo.cpp + al/effects/effects.h + al/effects/equalizer.cpp + al/effects/fshifter.cpp + al/effects/modulator.cpp + al/effects/null.cpp + al/effects/pshifter.cpp + al/effects/reverb.cpp + al/effects/vmorpher.cpp al/error.cpp al/event.cpp al/event.h diff --git a/al/effect.cpp b/al/effect.cpp index a90adf3c..b45ba00a 100644 --- a/al/effect.cpp +++ b/al/effect.cpp @@ -82,27 +82,30 @@ effect_exception::effect_exception(ALenum code, const char *msg, ...) : base_exc namespace { -constexpr struct FactoryItem { +struct FactoryItem { ALenum Type; EffectStateFactory* (&GetFactory)(void); -} FactoryList[] = { - { AL_EFFECT_NULL, NullStateFactory_getFactory }, - { AL_EFFECT_EAXREVERB, ReverbStateFactory_getFactory }, - { AL_EFFECT_REVERB, StdReverbStateFactory_getFactory }, - { AL_EFFECT_AUTOWAH, AutowahStateFactory_getFactory }, - { AL_EFFECT_CHORUS, ChorusStateFactory_getFactory }, - { AL_EFFECT_COMPRESSOR, CompressorStateFactory_getFactory }, - { AL_EFFECT_DISTORTION, DistortionStateFactory_getFactory }, - { AL_EFFECT_ECHO, EchoStateFactory_getFactory }, - { AL_EFFECT_EQUALIZER, EqualizerStateFactory_getFactory }, - { AL_EFFECT_FLANGER, FlangerStateFactory_getFactory }, - { AL_EFFECT_FREQUENCY_SHIFTER, FshifterStateFactory_getFactory }, - { AL_EFFECT_RING_MODULATOR, ModulatorStateFactory_getFactory }, - { AL_EFFECT_PITCH_SHIFTER, PshifterStateFactory_getFactory}, - { AL_EFFECT_VOCAL_MORPHER, VmorpherStateFactory_getFactory}, - { AL_EFFECT_DEDICATED_DIALOGUE, DedicatedStateFactory_getFactory }, - { AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, DedicatedStateFactory_getFactory }, - { AL_EFFECT_CONVOLUTION_REVERB_SOFT, ConvolutionStateFactory_getFactory } + const EffectProps &DefaultProps; + const EffectVtable &Vtable; +}; +constexpr FactoryItem FactoryList[] = { + { AL_EFFECT_NULL, NullStateFactory_getFactory, NullEffectProps, NullEffectVtable }, + { AL_EFFECT_EAXREVERB, ReverbStateFactory_getFactory, ReverbEffectProps, ReverbEffectVtable }, + { AL_EFFECT_REVERB, StdReverbStateFactory_getFactory, StdReverbEffectProps, StdReverbEffectVtable }, + { AL_EFFECT_AUTOWAH, AutowahStateFactory_getFactory, AutowahEffectProps, AutowahEffectVtable }, + { AL_EFFECT_CHORUS, ChorusStateFactory_getFactory, ChorusEffectProps, ChorusEffectVtable }, + { AL_EFFECT_COMPRESSOR, CompressorStateFactory_getFactory, CompressorEffectProps, CompressorEffectVtable }, + { AL_EFFECT_DISTORTION, DistortionStateFactory_getFactory, DistortionEffectProps, DistortionEffectVtable }, + { AL_EFFECT_ECHO, EchoStateFactory_getFactory, EchoEffectProps, EchoEffectVtable }, + { AL_EFFECT_EQUALIZER, EqualizerStateFactory_getFactory, EqualizerEffectProps, EqualizerEffectVtable }, + { AL_EFFECT_FLANGER, FlangerStateFactory_getFactory, FlangerEffectProps, FlangerEffectVtable }, + { AL_EFFECT_FREQUENCY_SHIFTER, FshifterStateFactory_getFactory, FshifterEffectProps, FshifterEffectVtable }, + { AL_EFFECT_RING_MODULATOR, ModulatorStateFactory_getFactory, ModulatorEffectProps, ModulatorEffectVtable }, + { AL_EFFECT_PITCH_SHIFTER, PshifterStateFactory_getFactory, PshifterEffectProps, PshifterEffectVtable }, + { AL_EFFECT_VOCAL_MORPHER, VmorpherStateFactory_getFactory, VmorpherEffectProps, VmorpherEffectVtable }, + { AL_EFFECT_DEDICATED_DIALOGUE, DedicatedStateFactory_getFactory, DedicatedEffectProps, DedicatedEffectVtable }, + { AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, DedicatedStateFactory_getFactory, DedicatedEffectProps, DedicatedEffectVtable }, + { AL_EFFECT_CONVOLUTION_REVERB_SOFT, ConvolutionStateFactory_getFactory, ConvolutionEffectProps, ConvolutionEffectVtable }, }; @@ -125,18 +128,26 @@ void ALeffect_getParamfv(const ALeffect *effect, ALenum param, float *values) { effect->vtab->getParamfv(&effect->Props, param, values); } +const FactoryItem *getFactoryItemByType(ALenum type) +{ + auto iter = std::find_if(std::begin(FactoryList), std::end(FactoryList), + [type](const FactoryItem &item) noexcept -> bool + { return item.Type == type; }); + return (iter != std::end(FactoryList)) ? std::addressof(*iter) : nullptr; +} + void InitEffectParams(ALeffect *effect, ALenum type) { - EffectStateFactory *factory = getFactoryByType(type); - if(factory) + const FactoryItem *item{getFactoryItemByType(type)}; + if(item) { - effect->Props = factory->getDefaultProps(); - effect->vtab = factory->getEffectVtable(); + effect->Props = item->DefaultProps; + effect->vtab = &item->Vtable; } else { effect->Props = EffectProps{}; - effect->vtab = nullptr; + effect->vtab = &NullEffectVtable; } effect->type = type; } diff --git a/al/effect.h b/al/effect.h index 28ce1f3e..c30a749a 100644 --- a/al/effect.h +++ b/al/effect.h @@ -5,6 +5,7 @@ #include "AL/efx.h" #include "effects/base.h" +#include "al/effects/effects.h" enum { diff --git a/al/effects/autowah.cpp b/al/effects/autowah.cpp new file mode 100644 index 00000000..65d4b702 --- /dev/null +++ b/al/effects/autowah.cpp @@ -0,0 +1,109 @@ + +#include "config.h" + +#include +#include + +#include + +#include "AL/efx.h" + +#include "effects/base.h" +#include "effects.h" + +namespace { + +void Autowah_setParamf(EffectProps *props, ALenum param, float val) +{ + switch(param) + { + case AL_AUTOWAH_ATTACK_TIME: + if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME)) + throw effect_exception{AL_INVALID_VALUE, "Autowah attack time out of range"}; + props->Autowah.AttackTime = val; + break; + + case AL_AUTOWAH_RELEASE_TIME: + if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME)) + throw effect_exception{AL_INVALID_VALUE, "Autowah release time out of range"}; + props->Autowah.ReleaseTime = val; + break; + + case AL_AUTOWAH_RESONANCE: + if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE)) + throw effect_exception{AL_INVALID_VALUE, "Autowah resonance out of range"}; + props->Autowah.Resonance = val; + break; + + case AL_AUTOWAH_PEAK_GAIN: + if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN)) + throw effect_exception{AL_INVALID_VALUE, "Autowah peak gain out of range"}; + props->Autowah.PeakGain = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param}; + } +} +void Autowah_setParamfv(EffectProps *props, ALenum param, const float *vals) +{ Autowah_setParamf(props, param, vals[0]); } + +void Autowah_setParami(EffectProps*, ALenum param, int) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; } +void Autowah_setParamiv(EffectProps*, ALenum param, const int*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", + param}; +} + +void Autowah_getParamf(const EffectProps *props, ALenum param, float *val) +{ + switch(param) + { + case AL_AUTOWAH_ATTACK_TIME: + *val = props->Autowah.AttackTime; + break; + + case AL_AUTOWAH_RELEASE_TIME: + *val = props->Autowah.ReleaseTime; + break; + + case AL_AUTOWAH_RESONANCE: + *val = props->Autowah.Resonance; + break; + + case AL_AUTOWAH_PEAK_GAIN: + *val = props->Autowah.PeakGain; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param}; + } + +} +void Autowah_getParamfv(const EffectProps *props, ALenum param, float *vals) +{ Autowah_getParamf(props, param, vals); } + +void Autowah_getParami(const EffectProps*, ALenum param, int*) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; } +void Autowah_getParamiv(const EffectProps*, ALenum param, int*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", + param}; +} + +EffectProps genDefaultProps() noexcept +{ + EffectProps props{}; + props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME; + props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME; + props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE; + props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN; + return props; +} + +} // namespace + +DEFINE_ALEFFECT_VTABLE(Autowah); + +const EffectProps AutowahEffectProps{genDefaultProps()}; diff --git a/al/effects/chorus.cpp b/al/effects/chorus.cpp new file mode 100644 index 00000000..2d983885 --- /dev/null +++ b/al/effects/chorus.cpp @@ -0,0 +1,250 @@ + +#include "config.h" + +#include "AL/al.h" +#include "AL/efx.h" + +#include "effects.h" +#include "effects/base.h" + + +namespace { + +void Chorus_setParami(EffectProps *props, ALenum param, int val) +{ + switch(param) + { + case AL_CHORUS_WAVEFORM: + if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM)) + throw effect_exception{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)) + throw effect_exception{AL_INVALID_VALUE, "Chorus phase out of range"}; + props->Chorus.Phase = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param}; + } +} +void Chorus_setParamiv(EffectProps *props, ALenum param, const int *vals) +{ Chorus_setParami(props, param, vals[0]); } +void Chorus_setParamf(EffectProps *props, ALenum param, float val) +{ + switch(param) + { + case AL_CHORUS_RATE: + if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{AL_INVALID_VALUE, "Chorus delay out of range"}; + props->Chorus.Delay = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param}; + } +} +void Chorus_setParamfv(EffectProps *props, ALenum param, const float *vals) +{ Chorus_setParamf(props, param, vals[0]); } + +void Chorus_getParami(const EffectProps *props, ALenum param, int *val) +{ + switch(param) + { + case AL_CHORUS_WAVEFORM: + *val = props->Chorus.Waveform; + break; + + case AL_CHORUS_PHASE: + *val = props->Chorus.Phase; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param}; + } +} +void Chorus_getParamiv(const EffectProps *props, ALenum param, int *vals) +{ Chorus_getParami(props, param, vals); } +void Chorus_getParamf(const EffectProps *props, ALenum param, float *val) +{ + switch(param) + { + case AL_CHORUS_RATE: + *val = props->Chorus.Rate; + break; + + case AL_CHORUS_DEPTH: + *val = props->Chorus.Depth; + break; + + case AL_CHORUS_FEEDBACK: + *val = props->Chorus.Feedback; + break; + + case AL_CHORUS_DELAY: + *val = props->Chorus.Delay; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param}; + } +} +void Chorus_getParamfv(const EffectProps *props, ALenum param, float *vals) +{ Chorus_getParamf(props, param, vals); } + +const EffectProps genDefaultChorusProps() noexcept +{ + EffectProps props{}; + props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM; + props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE; + props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE; + props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH; + props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK; + props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY; + return props; +} + + +void Flanger_setParami(EffectProps *props, ALenum param, int val) +{ + switch(param) + { + case AL_FLANGER_WAVEFORM: + if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM)) + throw effect_exception{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)) + throw effect_exception{AL_INVALID_VALUE, "Flanger phase out of range"}; + props->Chorus.Phase = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param}; + } +} +void Flanger_setParamiv(EffectProps *props, ALenum param, const int *vals) +{ Flanger_setParami(props, param, vals[0]); } +void Flanger_setParamf(EffectProps *props, ALenum param, float val) +{ + switch(param) + { + case AL_FLANGER_RATE: + if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{AL_INVALID_VALUE, "Flanger delay out of range"}; + props->Chorus.Delay = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param}; + } +} +void Flanger_setParamfv(EffectProps *props, ALenum param, const float *vals) +{ Flanger_setParamf(props, param, vals[0]); } + +void Flanger_getParami(const EffectProps *props, ALenum param, int *val) +{ + switch(param) + { + case AL_FLANGER_WAVEFORM: + *val = props->Chorus.Waveform; + break; + + case AL_FLANGER_PHASE: + *val = props->Chorus.Phase; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param}; + } +} +void Flanger_getParamiv(const EffectProps *props, ALenum param, int *vals) +{ Flanger_getParami(props, param, vals); } +void Flanger_getParamf(const EffectProps *props, ALenum param, float *val) +{ + 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: + throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param}; + } +} +void Flanger_getParamfv(const EffectProps *props, ALenum param, float *vals) +{ Flanger_getParamf(props, param, vals); } + +EffectProps genDefaultFlangerProps() noexcept +{ + EffectProps props{}; + props.Chorus.Waveform = AL_FLANGER_DEFAULT_WAVEFORM; + props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE; + props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE; + props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH; + props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK; + props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY; + return props; +} + +} // namespace + +DEFINE_ALEFFECT_VTABLE(Chorus); + +const EffectProps ChorusEffectProps{genDefaultChorusProps()}; + +DEFINE_ALEFFECT_VTABLE(Flanger); + +const EffectProps FlangerEffectProps{genDefaultFlangerProps()}; diff --git a/al/effects/compressor.cpp b/al/effects/compressor.cpp new file mode 100644 index 00000000..94e07431 --- /dev/null +++ b/al/effects/compressor.cpp @@ -0,0 +1,72 @@ + +#include "config.h" + +#include "AL/al.h" +#include "AL/efx.h" + +#include "effects.h" +#include "effects/base.h" + + +namespace { + +void Compressor_setParami(EffectProps *props, ALenum param, int val) +{ + switch(param) + { + case AL_COMPRESSOR_ONOFF: + if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF)) + throw effect_exception{AL_INVALID_VALUE, "Compressor state out of range"}; + props->Compressor.OnOff = (val != AL_FALSE); + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x", + param}; + } +} +void Compressor_setParamiv(EffectProps *props, ALenum param, const int *vals) +{ Compressor_setParami(props, param, vals[0]); } +void Compressor_setParamf(EffectProps*, ALenum param, float) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; } +void Compressor_setParamfv(EffectProps*, ALenum param, const float*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", + param}; +} + +void Compressor_getParami(const EffectProps *props, ALenum param, int *val) +{ + switch(param) + { + case AL_COMPRESSOR_ONOFF: + *val = props->Compressor.OnOff; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x", + param}; + } +} +void Compressor_getParamiv(const EffectProps *props, ALenum param, int *vals) +{ Compressor_getParami(props, param, vals); } +void Compressor_getParamf(const EffectProps*, ALenum param, float*) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; } +void Compressor_getParamfv(const EffectProps*, ALenum param, float*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", + param}; +} + +EffectProps genDefaultProps() noexcept +{ + EffectProps props{}; + props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF; + return props; +} + +} // namespace + +DEFINE_ALEFFECT_VTABLE(Compressor); + +const EffectProps CompressorEffectProps{genDefaultProps()}; diff --git a/al/effects/convolution.cpp b/al/effects/convolution.cpp new file mode 100644 index 00000000..4d87b1c4 --- /dev/null +++ b/al/effects/convolution.cpp @@ -0,0 +1,93 @@ + +#include "config.h" + +#include "AL/al.h" +#include "inprogext.h" + +#include "effects.h" +#include "effects/base.h" + + +namespace { + +void Convolution_setParami(EffectProps* /*props*/, ALenum param, int /*val*/) +{ + switch(param) + { + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", + param}; + } +} +void Convolution_setParamiv(EffectProps *props, ALenum param, const int *vals) +{ + switch(param) + { + default: + Convolution_setParami(props, param, vals[0]); + } +} +void Convolution_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/) +{ + switch(param) + { + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", + param}; + } +} +void Convolution_setParamfv(EffectProps *props, ALenum param, const float *vals) +{ + switch(param) + { + default: + Convolution_setParamf(props, param, vals[0]); + } +} + +void Convolution_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/) +{ + switch(param) + { + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", + param}; + } +} +void Convolution_getParamiv(const EffectProps *props, ALenum param, int *vals) +{ + switch(param) + { + default: + Convolution_getParami(props, param, vals); + } +} +void Convolution_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/) +{ + switch(param) + { + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", + param}; + } +} +void Convolution_getParamfv(const EffectProps *props, ALenum param, float *vals) +{ + switch(param) + { + default: + Convolution_getParamf(props, param, vals); + } +} + +EffectProps genDefaultProps() noexcept +{ + EffectProps props{}; + return props; +} + +} // namespace + +DEFINE_ALEFFECT_VTABLE(Convolution); + +const EffectProps ConvolutionEffectProps{genDefaultProps()}; diff --git a/al/effects/dedicated.cpp b/al/effects/dedicated.cpp new file mode 100644 index 00000000..334d9e56 --- /dev/null +++ b/al/effects/dedicated.cpp @@ -0,0 +1,72 @@ + +#include "config.h" + +#include + +#include "AL/al.h" +#include "AL/efx.h" + +#include "effects.h" +#include "effects/base.h" + + +namespace { + +void Dedicated_setParami(EffectProps*, ALenum param, int) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; } +void Dedicated_setParamiv(EffectProps*, ALenum param, const int*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", + param}; +} +void Dedicated_setParamf(EffectProps *props, ALenum param, float val) +{ + switch(param) + { + case AL_DEDICATED_GAIN: + if(!(val >= 0.0f && std::isfinite(val))) + throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"}; + props->Dedicated.Gain = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param}; + } +} +void Dedicated_setParamfv(EffectProps *props, ALenum param, const float *vals) +{ Dedicated_setParamf(props, param, vals[0]); } + +void Dedicated_getParami(const EffectProps*, ALenum param, int*) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; } +void Dedicated_getParamiv(const EffectProps*, ALenum param, int*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", + param}; +} +void Dedicated_getParamf(const EffectProps *props, ALenum param, float *val) +{ + switch(param) + { + case AL_DEDICATED_GAIN: + *val = props->Dedicated.Gain; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param}; + } +} +void Dedicated_getParamfv(const EffectProps *props, ALenum param, float *vals) +{ Dedicated_getParamf(props, param, vals); } + +EffectProps genDefaultProps() noexcept +{ + EffectProps props{}; + props.Dedicated.Gain = 1.0f; + return props; +} + +} // namespace + +DEFINE_ALEFFECT_VTABLE(Dedicated); + +const EffectProps DedicatedEffectProps{genDefaultProps()}; diff --git a/al/effects/distortion.cpp b/al/effects/distortion.cpp new file mode 100644 index 00000000..8961a4d9 --- /dev/null +++ b/al/effects/distortion.cpp @@ -0,0 +1,114 @@ + +#include "config.h" + +#include "AL/al.h" +#include "AL/efx.h" + +#include "effects.h" +#include "effects/base.h" + + +namespace { + +void Distortion_setParami(EffectProps*, ALenum param, int) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; } +void Distortion_setParamiv(EffectProps*, ALenum param, const int*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", + param}; +} +void Distortion_setParamf(EffectProps *props, ALenum param, float val) +{ + switch(param) + { + case AL_DISTORTION_EDGE: + if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{AL_INVALID_VALUE, "Distortion EQ bandwidth out of range"}; + props->Distortion.EQBandwidth = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param}; + } +} +void Distortion_setParamfv(EffectProps *props, ALenum param, const float *vals) +{ Distortion_setParamf(props, param, vals[0]); } + +void Distortion_getParami(const EffectProps*, ALenum param, int*) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; } +void Distortion_getParamiv(const EffectProps*, ALenum param, int*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", + param}; +} +void Distortion_getParamf(const EffectProps *props, ALenum param, float *val) +{ + switch(param) + { + case AL_DISTORTION_EDGE: + *val = props->Distortion.Edge; + break; + + case AL_DISTORTION_GAIN: + *val = props->Distortion.Gain; + break; + + case AL_DISTORTION_LOWPASS_CUTOFF: + *val = props->Distortion.LowpassCutoff; + break; + + case AL_DISTORTION_EQCENTER: + *val = props->Distortion.EQCenter; + break; + + case AL_DISTORTION_EQBANDWIDTH: + *val = props->Distortion.EQBandwidth; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param}; + } +} +void Distortion_getParamfv(const EffectProps *props, ALenum param, float *vals) +{ Distortion_getParamf(props, param, vals); } + +EffectProps genDefaultProps() noexcept +{ + EffectProps props{}; + props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE; + props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN; + props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF; + props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER; + props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH; + return props; +} + +} // namespace + +DEFINE_ALEFFECT_VTABLE(Distortion); + +const EffectProps DistortionEffectProps{genDefaultProps()}; diff --git a/al/effects/echo.cpp b/al/effects/echo.cpp new file mode 100644 index 00000000..b242a9cd --- /dev/null +++ b/al/effects/echo.cpp @@ -0,0 +1,108 @@ + +#include "config.h" + +#include "AL/al.h" +#include "AL/efx.h" + +#include "effects.h" +#include "effects/base.h" + + +namespace { + +void Echo_setParami(EffectProps*, ALenum param, int) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; } +void Echo_setParamiv(EffectProps*, ALenum param, const int*) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; } +void Echo_setParamf(EffectProps *props, ALenum param, float val) +{ + switch(param) + { + case AL_ECHO_DELAY: + if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{AL_INVALID_VALUE, "Echo spread out of range"}; + props->Echo.Spread = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param}; + } +} +void Echo_setParamfv(EffectProps *props, ALenum param, const float *vals) +{ Echo_setParamf(props, param, vals[0]); } + +void Echo_getParami(const EffectProps*, ALenum param, int*) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; } +void Echo_getParamiv(const EffectProps*, ALenum param, int*) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; } +void Echo_getParamf(const EffectProps *props, ALenum param, float *val) +{ + switch(param) + { + case AL_ECHO_DELAY: + *val = props->Echo.Delay; + break; + + case AL_ECHO_LRDELAY: + *val = props->Echo.LRDelay; + break; + + case AL_ECHO_DAMPING: + *val = props->Echo.Damping; + break; + + case AL_ECHO_FEEDBACK: + *val = props->Echo.Feedback; + break; + + case AL_ECHO_SPREAD: + *val = props->Echo.Spread; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param}; + } +} +void Echo_getParamfv(const EffectProps *props, ALenum param, float *vals) +{ Echo_getParamf(props, param, vals); } + +EffectProps genDefaultProps() noexcept +{ + EffectProps props{}; + props.Echo.Delay = AL_ECHO_DEFAULT_DELAY; + props.Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY; + props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING; + props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK; + props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD; + return props; +} + +} // namespace + +DEFINE_ALEFFECT_VTABLE(Echo); + +const EffectProps EchoEffectProps{genDefaultProps()}; diff --git a/al/effects/effects.h b/al/effects/effects.h new file mode 100644 index 00000000..1850271b --- /dev/null +++ b/al/effects/effects.h @@ -0,0 +1,75 @@ +#ifndef AL_EFFECTS_EFFECTS_H +#define AL_EFFECTS_EFFECTS_H + +#include "AL/al.h" + +#include "alexcpt.h" + +union EffectProps; + + +class effect_exception final : public al::base_exception { +public: + [[gnu::format(printf, 3, 4)]] + effect_exception(ALenum code, const char *msg, ...); +}; + + +struct EffectVtable { + void (*const setParami)(EffectProps *props, ALenum param, int val); + void (*const setParamiv)(EffectProps *props, ALenum param, const int *vals); + void (*const setParamf)(EffectProps *props, ALenum param, float val); + void (*const setParamfv)(EffectProps *props, ALenum param, const float *vals); + + void (*const getParami)(const EffectProps *props, ALenum param, int *val); + void (*const getParamiv)(const EffectProps *props, ALenum param, int *vals); + void (*const getParamf)(const EffectProps *props, ALenum param, float *val); + void (*const getParamfv)(const EffectProps *props, ALenum param, float *vals); +}; + +#define DEFINE_ALEFFECT_VTABLE(T) \ +const EffectVtable T##EffectVtable = { \ + T##_setParami, T##_setParamiv, \ + T##_setParamf, T##_setParamfv, \ + T##_getParami, T##_getParamiv, \ + T##_getParamf, T##_getParamfv, \ +} + + +/* Default properties for the given effect types. */ +extern const EffectProps NullEffectProps; +extern const EffectProps ReverbEffectProps; +extern const EffectProps StdReverbEffectProps; +extern const EffectProps AutowahEffectProps; +extern const EffectProps ChorusEffectProps; +extern const EffectProps CompressorEffectProps; +extern const EffectProps DistortionEffectProps; +extern const EffectProps EchoEffectProps; +extern const EffectProps EqualizerEffectProps; +extern const EffectProps FlangerEffectProps; +extern const EffectProps FshifterEffectProps; +extern const EffectProps ModulatorEffectProps; +extern const EffectProps PshifterEffectProps; +extern const EffectProps VmorpherEffectProps; +extern const EffectProps DedicatedEffectProps; +extern const EffectProps ConvolutionEffectProps; + +/* Vtables to get/set properties for the given effect types. */ +extern const EffectVtable NullEffectVtable; +extern const EffectVtable ReverbEffectVtable; +extern const EffectVtable StdReverbEffectVtable; +extern const EffectVtable AutowahEffectVtable; +extern const EffectVtable ChorusEffectVtable; +extern const EffectVtable CompressorEffectVtable; +extern const EffectVtable DistortionEffectVtable; +extern const EffectVtable EchoEffectVtable; +extern const EffectVtable EqualizerEffectVtable; +extern const EffectVtable FlangerEffectVtable; +extern const EffectVtable FshifterEffectVtable; +extern const EffectVtable ModulatorEffectVtable; +extern const EffectVtable PshifterEffectVtable; +extern const EffectVtable VmorpherEffectVtable; +extern const EffectVtable DedicatedEffectVtable; +extern const EffectVtable ConvolutionEffectVtable; + +#endif /* AL_EFFECTS_EFFECTS_H */ diff --git a/al/effects/equalizer.cpp b/al/effects/equalizer.cpp new file mode 100644 index 00000000..3a7c0a8f --- /dev/null +++ b/al/effects/equalizer.cpp @@ -0,0 +1,169 @@ + +#include "config.h" + +#include "AL/al.h" +#include "AL/efx.h" + +#include "effects.h" +#include "effects/base.h" + + +namespace { + +void Equalizer_setParami(EffectProps*, ALenum param, int) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; } +void Equalizer_setParamiv(EffectProps*, ALenum param, const int*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", + param}; +} +void Equalizer_setParamf(EffectProps *props, ALenum param, float val) +{ + switch(param) + { + case AL_EQUALIZER_LOW_GAIN: + if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{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)) + throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band cutoff out of range"}; + props->Equalizer.HighCutoff = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param}; + } +} +void Equalizer_setParamfv(EffectProps *props, ALenum param, const float *vals) +{ Equalizer_setParamf(props, param, vals[0]); } + +void Equalizer_getParami(const EffectProps*, ALenum param, int*) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; } +void Equalizer_getParamiv(const EffectProps*, ALenum param, int*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", + param}; +} +void Equalizer_getParamf(const EffectProps *props, ALenum param, float *val) +{ + switch(param) + { + case AL_EQUALIZER_LOW_GAIN: + *val = props->Equalizer.LowGain; + break; + + case AL_EQUALIZER_LOW_CUTOFF: + *val = props->Equalizer.LowCutoff; + break; + + case AL_EQUALIZER_MID1_GAIN: + *val = props->Equalizer.Mid1Gain; + break; + + case AL_EQUALIZER_MID1_CENTER: + *val = props->Equalizer.Mid1Center; + break; + + case AL_EQUALIZER_MID1_WIDTH: + *val = props->Equalizer.Mid1Width; + break; + + case AL_EQUALIZER_MID2_GAIN: + *val = props->Equalizer.Mid2Gain; + break; + + case AL_EQUALIZER_MID2_CENTER: + *val = props->Equalizer.Mid2Center; + break; + + case AL_EQUALIZER_MID2_WIDTH: + *val = props->Equalizer.Mid2Width; + break; + + case AL_EQUALIZER_HIGH_GAIN: + *val = props->Equalizer.HighGain; + break; + + case AL_EQUALIZER_HIGH_CUTOFF: + *val = props->Equalizer.HighCutoff; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param}; + } +} +void Equalizer_getParamfv(const EffectProps *props, ALenum param, float *vals) +{ Equalizer_getParamf(props, param, vals); } + +EffectProps genDefaultProps() noexcept +{ + EffectProps props{}; + props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF; + props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN; + props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER; + props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN; + props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH; + props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER; + props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN; + props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH; + props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF; + props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN; + return props; +} + +} // namespace + +DEFINE_ALEFFECT_VTABLE(Equalizer); + +const EffectProps EqualizerEffectProps{genDefaultProps()}; diff --git a/al/effects/fshifter.cpp b/al/effects/fshifter.cpp new file mode 100644 index 00000000..31138fe6 --- /dev/null +++ b/al/effects/fshifter.cpp @@ -0,0 +1,104 @@ + +#include "config.h" + +#include "AL/al.h" +#include "AL/efx.h" + +#include "effects.h" +#include "effects/base.h" + + +namespace { + +void Fshifter_setParamf(EffectProps *props, ALenum param, float val) +{ + switch(param) + { + case AL_FREQUENCY_SHIFTER_FREQUENCY: + if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY)) + throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"}; + props->Fshifter.Frequency = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", + param}; + } +} +void Fshifter_setParamfv(EffectProps *props, ALenum param, const float *vals) +{ Fshifter_setParamf(props, param, vals[0]); } + +void Fshifter_setParami(EffectProps *props, ALenum param, int val) +{ + switch(param) + { + case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION: + if(!(val >= AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION)) + throw effect_exception{AL_INVALID_VALUE, + "Frequency shifter left direction out of range"}; + props->Fshifter.LeftDirection = val; + break; + + case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION: + if(!(val >= AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION)) + throw effect_exception{AL_INVALID_VALUE, + "Frequency shifter right direction out of range"}; + props->Fshifter.RightDirection = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, + "Invalid frequency shifter integer property 0x%04x", param}; + } +} +void Fshifter_setParamiv(EffectProps *props, ALenum param, const int *vals) +{ Fshifter_setParami(props, param, vals[0]); } + +void Fshifter_getParami(const EffectProps *props, ALenum param, int *val) +{ + switch(param) + { + case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION: + *val = props->Fshifter.LeftDirection; + break; + case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION: + *val = props->Fshifter.RightDirection; + break; + default: + throw effect_exception{AL_INVALID_ENUM, + "Invalid frequency shifter integer property 0x%04x", param}; + } +} +void Fshifter_getParamiv(const EffectProps *props, ALenum param, int *vals) +{ Fshifter_getParami(props, param, vals); } + +void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val) +{ + switch(param) + { + case AL_FREQUENCY_SHIFTER_FREQUENCY: + *val = props->Fshifter.Frequency; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", + param}; + } +} +void Fshifter_getParamfv(const EffectProps *props, ALenum param, float *vals) +{ Fshifter_getParamf(props, param, vals); } + +EffectProps genDefaultProps() noexcept +{ + EffectProps props{}; + props.Fshifter.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY; + props.Fshifter.LeftDirection = AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION; + props.Fshifter.RightDirection = AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION; + return props; +} + +} // namespace + +DEFINE_ALEFFECT_VTABLE(Fshifter); + +const EffectProps FshifterEffectProps{genDefaultProps()}; diff --git a/al/effects/modulator.cpp b/al/effects/modulator.cpp new file mode 100644 index 00000000..95f379e2 --- /dev/null +++ b/al/effects/modulator.cpp @@ -0,0 +1,110 @@ + +#include "config.h" + +#include "AL/al.h" +#include "AL/efx.h" + +#include "effects.h" +#include "effects/base.h" + + +namespace { + +void Modulator_setParamf(EffectProps *props, ALenum param, float val) +{ + switch(param) + { + case AL_RING_MODULATOR_FREQUENCY: + if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY)) + throw effect_exception{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)) + throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range"}; + props->Modulator.HighPassCutoff = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param}; + } +} +void Modulator_setParamfv(EffectProps *props, ALenum param, const float *vals) +{ Modulator_setParamf(props, param, vals[0]); } +void Modulator_setParami(EffectProps *props, ALenum param, int val) +{ + switch(param) + { + case AL_RING_MODULATOR_FREQUENCY: + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: + Modulator_setParamf(props, param, static_cast(val)); + break; + + case AL_RING_MODULATOR_WAVEFORM: + if(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM)) + throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform"}; + props->Modulator.Waveform = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", + param}; + } +} +void Modulator_setParamiv(EffectProps *props, ALenum param, const int *vals) +{ Modulator_setParami(props, param, vals[0]); } + +void Modulator_getParami(const EffectProps *props, ALenum param, int *val) +{ + switch(param) + { + case AL_RING_MODULATOR_FREQUENCY: + *val = static_cast(props->Modulator.Frequency); + break; + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: + *val = static_cast(props->Modulator.HighPassCutoff); + break; + case AL_RING_MODULATOR_WAVEFORM: + *val = props->Modulator.Waveform; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", + param}; + } +} +void Modulator_getParamiv(const EffectProps *props, ALenum param, int *vals) +{ Modulator_getParami(props, param, vals); } +void Modulator_getParamf(const EffectProps *props, ALenum param, float *val) +{ + switch(param) + { + case AL_RING_MODULATOR_FREQUENCY: + *val = props->Modulator.Frequency; + break; + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: + *val = props->Modulator.HighPassCutoff; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param}; + } +} +void Modulator_getParamfv(const EffectProps *props, ALenum param, float *vals) +{ Modulator_getParamf(props, param, vals); } + +EffectProps genDefaultProps() noexcept +{ + EffectProps props{}; + props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY; + props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF; + props.Modulator.Waveform = AL_RING_MODULATOR_DEFAULT_WAVEFORM; + return props; +} + +} // namespace + +DEFINE_ALEFFECT_VTABLE(Modulator); + +const EffectProps ModulatorEffectProps{genDefaultProps()}; diff --git a/al/effects/null.cpp b/al/effects/null.cpp new file mode 100644 index 00000000..0ac5278f --- /dev/null +++ b/al/effects/null.cpp @@ -0,0 +1,93 @@ + +#include "config.h" + +#include "AL/al.h" +#include "AL/efx.h" + +#include "effects.h" +#include "effects/base.h" + + +namespace { + +void Null_setParami(EffectProps* /*props*/, ALenum param, int /*val*/) +{ + switch(param) + { + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", + param}; + } +} +void Null_setParamiv(EffectProps *props, ALenum param, const int *vals) +{ + switch(param) + { + default: + Null_setParami(props, param, vals[0]); + } +} +void Null_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/) +{ + switch(param) + { + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", + param}; + } +} +void Null_setParamfv(EffectProps *props, ALenum param, const float *vals) +{ + switch(param) + { + default: + Null_setParamf(props, param, vals[0]); + } +} + +void Null_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/) +{ + switch(param) + { + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", + param}; + } +} +void Null_getParamiv(const EffectProps *props, ALenum param, int *vals) +{ + switch(param) + { + default: + Null_getParami(props, param, vals); + } +} +void Null_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/) +{ + switch(param) + { + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", + param}; + } +} +void Null_getParamfv(const EffectProps *props, ALenum param, float *vals) +{ + switch(param) + { + default: + Null_getParamf(props, param, vals); + } +} + +EffectProps genDefaultProps() noexcept +{ + EffectProps props{}; + return props; +} + +} // namespace + +DEFINE_ALEFFECT_VTABLE(Null); + +const EffectProps NullEffectProps{genDefaultProps()}; diff --git a/al/effects/pshifter.cpp b/al/effects/pshifter.cpp new file mode 100644 index 00000000..e6b0b3b0 --- /dev/null +++ b/al/effects/pshifter.cpp @@ -0,0 +1,84 @@ + +#include "config.h" + +#include "AL/al.h" +#include "AL/efx.h" + +#include "effects.h" +#include "effects/base.h" + + +namespace { + +void Pshifter_setParamf(EffectProps*, ALenum param, float) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; } +void Pshifter_setParamfv(EffectProps*, ALenum param, const float*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x", + param}; +} + +void Pshifter_setParami(EffectProps *props, ALenum param, int val) +{ + switch(param) + { + case AL_PITCH_SHIFTER_COARSE_TUNE: + if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE)) + throw effect_exception{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)) + throw effect_exception{AL_INVALID_VALUE, "Pitch shifter fine tune out of range"}; + props->Pshifter.FineTune = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", + param}; + } +} +void Pshifter_setParamiv(EffectProps *props, ALenum param, const int *vals) +{ Pshifter_setParami(props, param, vals[0]); } + +void Pshifter_getParami(const EffectProps *props, ALenum param, int *val) +{ + switch(param) + { + case AL_PITCH_SHIFTER_COARSE_TUNE: + *val = props->Pshifter.CoarseTune; + break; + case AL_PITCH_SHIFTER_FINE_TUNE: + *val = props->Pshifter.FineTune; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", + param}; + } +} +void Pshifter_getParamiv(const EffectProps *props, ALenum param, int *vals) +{ Pshifter_getParami(props, param, vals); } + +void Pshifter_getParamf(const EffectProps*, ALenum param, float*) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; } +void Pshifter_getParamfv(const EffectProps*, ALenum param, float*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x", + param}; +} + +EffectProps genDefaultProps() noexcept +{ + EffectProps props{}; + props.Pshifter.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE; + props.Pshifter.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE; + return props; +} + +} // namespace + +DEFINE_ALEFFECT_VTABLE(Pshifter); + +const EffectProps PshifterEffectProps{genDefaultProps()}; diff --git a/al/effects/reverb.cpp b/al/effects/reverb.cpp new file mode 100644 index 00000000..caa0c81e --- /dev/null +++ b/al/effects/reverb.cpp @@ -0,0 +1,556 @@ + +#include "config.h" + +#include + +#include "AL/al.h" +#include "AL/efx.h" + +#include "effects.h" +#include "effects/base.h" + + +namespace { + +void Reverb_setParami(EffectProps *props, ALenum param, int val) +{ + switch(param) + { + case AL_EAXREVERB_DECAY_HFLIMIT: + if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hflimit out of range"}; + props->Reverb.DecayHFLimit = val != AL_FALSE; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x", + param}; + } +} +void Reverb_setParamiv(EffectProps *props, ALenum param, const int *vals) +{ Reverb_setParami(props, param, vals[0]); } +void Reverb_setParamf(EffectProps *props, ALenum param, float val) +{ + switch(param) + { + case AL_EAXREVERB_DENSITY: + if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb density out of range"}; + props->Reverb.Density = val; + break; + + case AL_EAXREVERB_DIFFUSION: + if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb diffusion out of range"}; + props->Reverb.Diffusion = val; + break; + + case AL_EAXREVERB_GAIN: + if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gain out of range"}; + props->Reverb.Gain = val; + break; + + case AL_EAXREVERB_GAINHF: + if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainhf out of range"}; + props->Reverb.GainHF = val; + break; + + case AL_EAXREVERB_GAINLF: + if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainlf out of range"}; + props->Reverb.GainLF = val; + break; + + case AL_EAXREVERB_DECAY_TIME: + if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay time out of range"}; + props->Reverb.DecayTime = val; + break; + + case AL_EAXREVERB_DECAY_HFRATIO: + if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hfratio out of range"}; + props->Reverb.DecayHFRatio = val; + break; + + case AL_EAXREVERB_DECAY_LFRATIO: + if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay lfratio out of range"}; + props->Reverb.DecayLFRatio = val; + break; + + case AL_EAXREVERB_REFLECTIONS_GAIN: + if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections gain out of range"}; + props->Reverb.ReflectionsGain = val; + break; + + case AL_EAXREVERB_REFLECTIONS_DELAY: + if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections delay out of range"}; + props->Reverb.ReflectionsDelay = val; + break; + + case AL_EAXREVERB_LATE_REVERB_GAIN: + if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb gain out of range"}; + props->Reverb.LateReverbGain = val; + break; + + case AL_EAXREVERB_LATE_REVERB_DELAY: + if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb delay out of range"}; + props->Reverb.LateReverbDelay = val; + break; + + case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: + if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb air absorption gainhf out of range"}; + props->Reverb.AirAbsorptionGainHF = val; + break; + + case AL_EAXREVERB_ECHO_TIME: + if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb echo time out of range"}; + props->Reverb.EchoTime = val; + break; + + case AL_EAXREVERB_ECHO_DEPTH: + if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb echo depth out of range"}; + props->Reverb.EchoDepth = val; + break; + + case AL_EAXREVERB_MODULATION_TIME: + if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb modulation time out of range"}; + props->Reverb.ModulationTime = val; + break; + + case AL_EAXREVERB_MODULATION_DEPTH: + if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb modulation depth out of range"}; + props->Reverb.ModulationDepth = val; + break; + + case AL_EAXREVERB_HFREFERENCE: + if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb hfreference out of range"}; + props->Reverb.HFReference = val; + break; + + case AL_EAXREVERB_LFREFERENCE: + if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb lfreference out of range"}; + props->Reverb.LFReference = val; + break; + + case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: + if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR)) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb room rolloff factor out of range"}; + props->Reverb.RoomRolloffFactor = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param}; + } +} +void Reverb_setParamfv(EffectProps *props, ALenum param, const float *vals) +{ + switch(param) + { + case AL_EAXREVERB_REFLECTIONS_PAN: + if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2]))) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections pan out of range"}; + props->Reverb.ReflectionsPan[0] = vals[0]; + props->Reverb.ReflectionsPan[1] = vals[1]; + props->Reverb.ReflectionsPan[2] = vals[2]; + break; + case AL_EAXREVERB_LATE_REVERB_PAN: + if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2]))) + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb pan out of range"}; + props->Reverb.LateReverbPan[0] = vals[0]; + props->Reverb.LateReverbPan[1] = vals[1]; + props->Reverb.LateReverbPan[2] = vals[2]; + break; + + default: + Reverb_setParamf(props, param, vals[0]); + break; + } +} + +void Reverb_getParami(const EffectProps *props, ALenum param, int *val) +{ + switch(param) + { + case AL_EAXREVERB_DECAY_HFLIMIT: + *val = props->Reverb.DecayHFLimit; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x", + param}; + } +} +void Reverb_getParamiv(const EffectProps *props, ALenum param, int *vals) +{ Reverb_getParami(props, param, vals); } +void Reverb_getParamf(const EffectProps *props, ALenum param, float *val) +{ + switch(param) + { + case AL_EAXREVERB_DENSITY: + *val = props->Reverb.Density; + break; + + case AL_EAXREVERB_DIFFUSION: + *val = props->Reverb.Diffusion; + break; + + case AL_EAXREVERB_GAIN: + *val = props->Reverb.Gain; + break; + + case AL_EAXREVERB_GAINHF: + *val = props->Reverb.GainHF; + break; + + case AL_EAXREVERB_GAINLF: + *val = props->Reverb.GainLF; + break; + + case AL_EAXREVERB_DECAY_TIME: + *val = props->Reverb.DecayTime; + break; + + case AL_EAXREVERB_DECAY_HFRATIO: + *val = props->Reverb.DecayHFRatio; + break; + + case AL_EAXREVERB_DECAY_LFRATIO: + *val = props->Reverb.DecayLFRatio; + break; + + case AL_EAXREVERB_REFLECTIONS_GAIN: + *val = props->Reverb.ReflectionsGain; + break; + + case AL_EAXREVERB_REFLECTIONS_DELAY: + *val = props->Reverb.ReflectionsDelay; + break; + + case AL_EAXREVERB_LATE_REVERB_GAIN: + *val = props->Reverb.LateReverbGain; + break; + + case AL_EAXREVERB_LATE_REVERB_DELAY: + *val = props->Reverb.LateReverbDelay; + break; + + case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: + *val = props->Reverb.AirAbsorptionGainHF; + break; + + case AL_EAXREVERB_ECHO_TIME: + *val = props->Reverb.EchoTime; + break; + + case AL_EAXREVERB_ECHO_DEPTH: + *val = props->Reverb.EchoDepth; + break; + + case AL_EAXREVERB_MODULATION_TIME: + *val = props->Reverb.ModulationTime; + break; + + case AL_EAXREVERB_MODULATION_DEPTH: + *val = props->Reverb.ModulationDepth; + break; + + case AL_EAXREVERB_HFREFERENCE: + *val = props->Reverb.HFReference; + break; + + case AL_EAXREVERB_LFREFERENCE: + *val = props->Reverb.LFReference; + break; + + case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: + *val = props->Reverb.RoomRolloffFactor; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param}; + } +} +void Reverb_getParamfv(const EffectProps *props, ALenum param, float *vals) +{ + switch(param) + { + case AL_EAXREVERB_REFLECTIONS_PAN: + vals[0] = props->Reverb.ReflectionsPan[0]; + vals[1] = props->Reverb.ReflectionsPan[1]; + vals[2] = props->Reverb.ReflectionsPan[2]; + break; + case AL_EAXREVERB_LATE_REVERB_PAN: + vals[0] = props->Reverb.LateReverbPan[0]; + vals[1] = props->Reverb.LateReverbPan[1]; + vals[2] = props->Reverb.LateReverbPan[2]; + break; + + default: + Reverb_getParamf(props, param, vals); + break; + } +} + +EffectProps genDefaultProps() noexcept +{ + EffectProps props{}; + props.Reverb.Density = AL_EAXREVERB_DEFAULT_DENSITY; + props.Reverb.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION; + props.Reverb.Gain = AL_EAXREVERB_DEFAULT_GAIN; + props.Reverb.GainHF = AL_EAXREVERB_DEFAULT_GAINHF; + props.Reverb.GainLF = AL_EAXREVERB_DEFAULT_GAINLF; + props.Reverb.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME; + props.Reverb.DecayHFRatio = AL_EAXREVERB_DEFAULT_DECAY_HFRATIO; + props.Reverb.DecayLFRatio = AL_EAXREVERB_DEFAULT_DECAY_LFRATIO; + props.Reverb.ReflectionsGain = AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN; + props.Reverb.ReflectionsDelay = AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY; + props.Reverb.ReflectionsPan[0] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + props.Reverb.ReflectionsPan[1] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + props.Reverb.ReflectionsPan[2] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + props.Reverb.LateReverbGain = AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN; + props.Reverb.LateReverbDelay = AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY; + props.Reverb.LateReverbPan[0] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + props.Reverb.LateReverbPan[1] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + props.Reverb.LateReverbPan[2] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + props.Reverb.EchoTime = AL_EAXREVERB_DEFAULT_ECHO_TIME; + props.Reverb.EchoDepth = AL_EAXREVERB_DEFAULT_ECHO_DEPTH; + props.Reverb.ModulationTime = AL_EAXREVERB_DEFAULT_MODULATION_TIME; + props.Reverb.ModulationDepth = AL_EAXREVERB_DEFAULT_MODULATION_DEPTH; + props.Reverb.AirAbsorptionGainHF = AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF; + props.Reverb.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE; + props.Reverb.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE; + props.Reverb.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; + props.Reverb.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT; + return props; +} + + +void StdReverb_setParami(EffectProps *props, ALenum param, int val) +{ + switch(param) + { + case AL_REVERB_DECAY_HFLIMIT: + if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT)) + throw effect_exception{AL_INVALID_VALUE, "Reverb decay hflimit out of range"}; + props->Reverb.DecayHFLimit = val != AL_FALSE; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param}; + } +} +void StdReverb_setParamiv(EffectProps *props, ALenum param, const int *vals) +{ StdReverb_setParami(props, param, vals[0]); } +void StdReverb_setParamf(EffectProps *props, ALenum param, float val) +{ + switch(param) + { + case AL_REVERB_DENSITY: + if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY)) + throw effect_exception{AL_INVALID_VALUE, "Reverb density out of range"}; + props->Reverb.Density = val; + break; + + case AL_REVERB_DIFFUSION: + if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION)) + throw effect_exception{AL_INVALID_VALUE, "Reverb diffusion out of range"}; + props->Reverb.Diffusion = val; + break; + + case AL_REVERB_GAIN: + if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN)) + throw effect_exception{AL_INVALID_VALUE, "Reverb gain out of range"}; + props->Reverb.Gain = val; + break; + + case AL_REVERB_GAINHF: + if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF)) + throw effect_exception{AL_INVALID_VALUE, "Reverb gainhf out of range"}; + props->Reverb.GainHF = val; + break; + + case AL_REVERB_DECAY_TIME: + if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME)) + throw effect_exception{AL_INVALID_VALUE, "Reverb decay time out of range"}; + props->Reverb.DecayTime = val; + break; + + case AL_REVERB_DECAY_HFRATIO: + if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO)) + throw effect_exception{AL_INVALID_VALUE, "Reverb decay hfratio out of range"}; + props->Reverb.DecayHFRatio = val; + break; + + case AL_REVERB_REFLECTIONS_GAIN: + if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN)) + throw effect_exception{AL_INVALID_VALUE, "Reverb reflections gain out of range"}; + props->Reverb.ReflectionsGain = val; + break; + + case AL_REVERB_REFLECTIONS_DELAY: + if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY)) + throw effect_exception{AL_INVALID_VALUE, "Reverb reflections delay out of range"}; + props->Reverb.ReflectionsDelay = val; + break; + + case AL_REVERB_LATE_REVERB_GAIN: + if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN)) + throw effect_exception{AL_INVALID_VALUE, "Reverb late reverb gain out of range"}; + props->Reverb.LateReverbGain = val; + break; + + case AL_REVERB_LATE_REVERB_DELAY: + if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY)) + throw effect_exception{AL_INVALID_VALUE, "Reverb late reverb delay out of range"}; + props->Reverb.LateReverbDelay = val; + break; + + case AL_REVERB_AIR_ABSORPTION_GAINHF: + if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF)) + throw effect_exception{AL_INVALID_VALUE, "Reverb air absorption gainhf out of range"}; + props->Reverb.AirAbsorptionGainHF = val; + break; + + case AL_REVERB_ROOM_ROLLOFF_FACTOR: + if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR)) + throw effect_exception{AL_INVALID_VALUE, "Reverb room rolloff factor out of range"}; + props->Reverb.RoomRolloffFactor = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param}; + } +} +void StdReverb_setParamfv(EffectProps *props, ALenum param, const float *vals) +{ StdReverb_setParamf(props, param, vals[0]); } + +void StdReverb_getParami(const EffectProps *props, ALenum param, int *val) +{ + switch(param) + { + case AL_REVERB_DECAY_HFLIMIT: + *val = props->Reverb.DecayHFLimit; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param}; + } +} +void StdReverb_getParamiv(const EffectProps *props, ALenum param, int *vals) +{ StdReverb_getParami(props, param, vals); } +void StdReverb_getParamf(const EffectProps *props, ALenum param, float *val) +{ + switch(param) + { + case AL_REVERB_DENSITY: + *val = props->Reverb.Density; + break; + + case AL_REVERB_DIFFUSION: + *val = props->Reverb.Diffusion; + break; + + case AL_REVERB_GAIN: + *val = props->Reverb.Gain; + break; + + case AL_REVERB_GAINHF: + *val = props->Reverb.GainHF; + break; + + case AL_REVERB_DECAY_TIME: + *val = props->Reverb.DecayTime; + break; + + case AL_REVERB_DECAY_HFRATIO: + *val = props->Reverb.DecayHFRatio; + break; + + case AL_REVERB_REFLECTIONS_GAIN: + *val = props->Reverb.ReflectionsGain; + break; + + case AL_REVERB_REFLECTIONS_DELAY: + *val = props->Reverb.ReflectionsDelay; + break; + + case AL_REVERB_LATE_REVERB_GAIN: + *val = props->Reverb.LateReverbGain; + break; + + case AL_REVERB_LATE_REVERB_DELAY: + *val = props->Reverb.LateReverbDelay; + break; + + case AL_REVERB_AIR_ABSORPTION_GAINHF: + *val = props->Reverb.AirAbsorptionGainHF; + break; + + case AL_REVERB_ROOM_ROLLOFF_FACTOR: + *val = props->Reverb.RoomRolloffFactor; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param}; + } +} +void StdReverb_getParamfv(const EffectProps *props, ALenum param, float *vals) +{ StdReverb_getParamf(props, param, vals); } + +EffectProps genDefaultStdProps() noexcept +{ + EffectProps props{}; + props.Reverb.Density = AL_REVERB_DEFAULT_DENSITY; + props.Reverb.Diffusion = AL_REVERB_DEFAULT_DIFFUSION; + props.Reverb.Gain = AL_REVERB_DEFAULT_GAIN; + props.Reverb.GainHF = AL_REVERB_DEFAULT_GAINHF; + props.Reverb.GainLF = 1.0f; + props.Reverb.DecayTime = AL_REVERB_DEFAULT_DECAY_TIME; + props.Reverb.DecayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO; + props.Reverb.DecayLFRatio = 1.0f; + props.Reverb.ReflectionsGain = AL_REVERB_DEFAULT_REFLECTIONS_GAIN; + props.Reverb.ReflectionsDelay = AL_REVERB_DEFAULT_REFLECTIONS_DELAY; + props.Reverb.ReflectionsPan[0] = 0.0f; + props.Reverb.ReflectionsPan[1] = 0.0f; + props.Reverb.ReflectionsPan[2] = 0.0f; + props.Reverb.LateReverbGain = AL_REVERB_DEFAULT_LATE_REVERB_GAIN; + props.Reverb.LateReverbDelay = AL_REVERB_DEFAULT_LATE_REVERB_DELAY; + props.Reverb.LateReverbPan[0] = 0.0f; + props.Reverb.LateReverbPan[1] = 0.0f; + props.Reverb.LateReverbPan[2] = 0.0f; + props.Reverb.EchoTime = 0.25f; + props.Reverb.EchoDepth = 0.0f; + props.Reverb.ModulationTime = 0.25f; + props.Reverb.ModulationDepth = 0.0f; + props.Reverb.AirAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF; + props.Reverb.HFReference = 5000.0f; + props.Reverb.LFReference = 250.0f; + props.Reverb.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; + props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT; + return props; +} + +} // namespace + +DEFINE_ALEFFECT_VTABLE(Reverb); + +const EffectProps ReverbEffectProps{genDefaultProps()}; + +DEFINE_ALEFFECT_VTABLE(StdReverb); + +const EffectProps StdReverbEffectProps{genDefaultStdProps()}; diff --git a/al/effects/vmorpher.cpp b/al/effects/vmorpher.cpp new file mode 100644 index 00000000..f6b73705 --- /dev/null +++ b/al/effects/vmorpher.cpp @@ -0,0 +1,141 @@ + +#include "config.h" + +#include "AL/al.h" +#include "AL/efx.h" + +#include "effects.h" +#include "effects/base.h" + + +namespace { + +void Vmorpher_setParami(EffectProps *props, ALenum param, int val) +{ + switch(param) + { + case AL_VOCAL_MORPHER_WAVEFORM: + if(!(val >= AL_VOCAL_MORPHER_MIN_WAVEFORM && val <= AL_VOCAL_MORPHER_MAX_WAVEFORM)) + throw effect_exception{AL_INVALID_VALUE, "Vocal morpher waveform out of range"}; + props->Vmorpher.Waveform = val; + break; + + case AL_VOCAL_MORPHER_PHONEMEA: + if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA)) + throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a out of range"}; + props->Vmorpher.PhonemeA = val; + break; + + case AL_VOCAL_MORPHER_PHONEMEB: + if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB)) + throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b out of range"}; + props->Vmorpher.PhonemeB = val; + break; + + case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: + if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING)) + throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a coarse tuning out of range"}; + props->Vmorpher.PhonemeACoarseTuning = val; + break; + + case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: + if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING)) + throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b coarse tuning out of range"}; + props->Vmorpher.PhonemeBCoarseTuning = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x", + param}; + } +} +void Vmorpher_setParamiv(EffectProps*, ALenum param, const int*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x", + param}; +} +void Vmorpher_setParamf(EffectProps *props, ALenum param, float val) +{ + switch(param) + { + case AL_VOCAL_MORPHER_RATE: + if(!(val >= AL_VOCAL_MORPHER_MIN_RATE && val <= AL_VOCAL_MORPHER_MAX_RATE)) + throw effect_exception{AL_INVALID_VALUE, "Vocal morpher rate out of range"}; + props->Vmorpher.Rate = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x", + param}; + } +} +void Vmorpher_setParamfv(EffectProps *props, ALenum param, const float *vals) +{ Vmorpher_setParamf(props, param, vals[0]); } + +void Vmorpher_getParami(const EffectProps *props, ALenum param, int* val) +{ + switch(param) + { + case AL_VOCAL_MORPHER_PHONEMEA: + *val = props->Vmorpher.PhonemeA; + break; + + case AL_VOCAL_MORPHER_PHONEMEB: + *val = props->Vmorpher.PhonemeB; + break; + + case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: + *val = props->Vmorpher.PhonemeACoarseTuning; + break; + + case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: + *val = props->Vmorpher.PhonemeBCoarseTuning; + break; + + case AL_VOCAL_MORPHER_WAVEFORM: + *val = props->Vmorpher.Waveform; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x", + param}; + } +} +void Vmorpher_getParamiv(const EffectProps*, ALenum param, int*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x", + param}; +} +void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val) +{ + switch(param) + { + case AL_VOCAL_MORPHER_RATE: + *val = props->Vmorpher.Rate; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x", + param}; + } +} +void Vmorpher_getParamfv(const EffectProps *props, ALenum param, float *vals) +{ Vmorpher_getParamf(props, param, vals); } + +EffectProps genDefaultProps() noexcept +{ + EffectProps props{}; + props.Vmorpher.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE; + props.Vmorpher.PhonemeA = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA; + props.Vmorpher.PhonemeB = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB; + props.Vmorpher.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING; + props.Vmorpher.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING; + props.Vmorpher.Waveform = AL_VOCAL_MORPHER_DEFAULT_WAVEFORM; + return props; +} + +} // namespace + +DEFINE_ALEFFECT_VTABLE(Vmorpher); + +const EffectProps VmorpherEffectProps{genDefaultProps()}; diff --git a/alc/effects/autowah.cpp b/alc/effects/autowah.cpp index 423d1b54..de91c32f 100644 --- a/alc/effects/autowah.cpp +++ b/alc/effects/autowah.cpp @@ -198,104 +198,10 @@ void AutowahState::process(const size_t samplesToDo, } -void Autowah_setParamf(EffectProps *props, ALenum param, float val) -{ - switch(param) - { - case AL_AUTOWAH_ATTACK_TIME: - if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME)) - throw effect_exception{AL_INVALID_VALUE, "Autowah attack time out of range"}; - props->Autowah.AttackTime = val; - break; - - case AL_AUTOWAH_RELEASE_TIME: - if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME)) - throw effect_exception{AL_INVALID_VALUE, "Autowah release time out of range"}; - props->Autowah.ReleaseTime = val; - break; - - case AL_AUTOWAH_RESONANCE: - if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE)) - throw effect_exception{AL_INVALID_VALUE, "Autowah resonance out of range"}; - props->Autowah.Resonance = val; - break; - - case AL_AUTOWAH_PEAK_GAIN: - if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Autowah peak gain out of range"}; - props->Autowah.PeakGain = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param}; - } -} -void Autowah_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Autowah_setParamf(props, param, vals[0]); } - -void Autowah_setParami(EffectProps*, ALenum param, int) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; } -void Autowah_setParamiv(EffectProps*, ALenum param, const int*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", - param}; -} - -void Autowah_getParamf(const EffectProps *props, ALenum param, float *val) -{ - switch(param) - { - case AL_AUTOWAH_ATTACK_TIME: - *val = props->Autowah.AttackTime; - break; - - case AL_AUTOWAH_RELEASE_TIME: - *val = props->Autowah.ReleaseTime; - break; - - case AL_AUTOWAH_RESONANCE: - *val = props->Autowah.Resonance; - break; - - case AL_AUTOWAH_PEAK_GAIN: - *val = props->Autowah.PeakGain; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param}; - } - -} -void Autowah_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Autowah_getParamf(props, param, vals); } - -void Autowah_getParami(const EffectProps*, ALenum param, int*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; } -void Autowah_getParamiv(const EffectProps*, ALenum param, int*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", - param}; -} - -DEFINE_ALEFFECT_VTABLE(Autowah); - - struct AutowahStateFactory final : public EffectStateFactory { EffectState *create() override { return new AutowahState{}; } - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override { return &Autowah_vtable; } }; -EffectProps AutowahStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME; - props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME; - props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE; - props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN; - return props; -} - } // namespace EffectStateFactory *AutowahStateFactory_getFactory() diff --git a/alc/effects/base.h b/alc/effects/base.h index 0efd5599..d365cede 100644 --- a/alc/effects/base.h +++ b/alc/effects/base.h @@ -4,7 +4,6 @@ #include #include "alcmain.h" -#include "alexcpt.h" #include "almalloc.h" #include "alspan.h" #include "atomic.h" @@ -127,34 +126,6 @@ union EffectProps { }; -class effect_exception final : public al::base_exception { -public: - [[gnu::format(printf, 3, 4)]] - effect_exception(ALenum code, const char *msg, ...); -}; - - -struct EffectVtable { - void (*const setParami)(EffectProps *props, ALenum param, int val); - void (*const setParamiv)(EffectProps *props, ALenum param, const int *vals); - void (*const setParamf)(EffectProps *props, ALenum param, float val); - void (*const setParamfv)(EffectProps *props, ALenum param, const float *vals); - - void (*const getParami)(const EffectProps *props, ALenum param, int *val); - void (*const getParamiv)(const EffectProps *props, ALenum param, int *vals); - void (*const getParamf)(const EffectProps *props, ALenum param, float *val); - void (*const getParamfv)(const EffectProps *props, ALenum param, float *vals); -}; - -#define DEFINE_ALEFFECT_VTABLE(T) \ -const EffectVtable T##_vtable = { \ - T##_setParami, T##_setParamiv, \ - T##_setParamf, T##_setParamfv, \ - T##_getParami, T##_getParamiv, \ - T##_getParamf, T##_getParamfv, \ -} - - struct EffectTarget { MixParams *Main; RealMixParams *RealOut; @@ -179,8 +150,6 @@ struct EffectStateFactory { virtual ~EffectStateFactory() = default; virtual EffectState *create() = 0; - virtual EffectProps getDefaultProps() const noexcept = 0; - virtual const EffectVtable *getEffectVtable() const noexcept = 0; }; diff --git a/alc/effects/chorus.cpp b/alc/effects/chorus.cpp index a531c5e1..81449b9b 100644 --- a/alc/effects/chorus.cpp +++ b/alc/effects/chorus.cpp @@ -278,256 +278,18 @@ void ChorusState::process(const size_t samplesToDo, const al::span= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM)) - throw effect_exception{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)) - throw effect_exception{AL_INVALID_VALUE, "Chorus phase out of range"}; - props->Chorus.Phase = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param}; - } -} -void Chorus_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Chorus_setParami(props, param, vals[0]); } -void Chorus_setParamf(EffectProps *props, ALenum param, float val) -{ - switch(param) - { - case AL_CHORUS_RATE: - if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{AL_INVALID_VALUE, "Chorus delay out of range"}; - props->Chorus.Delay = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param}; - } -} -void Chorus_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Chorus_setParamf(props, param, vals[0]); } - -void Chorus_getParami(const EffectProps *props, ALenum param, int *val) -{ - switch(param) - { - case AL_CHORUS_WAVEFORM: - *val = props->Chorus.Waveform; - break; - - case AL_CHORUS_PHASE: - *val = props->Chorus.Phase; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param}; - } -} -void Chorus_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Chorus_getParami(props, param, vals); } -void Chorus_getParamf(const EffectProps *props, ALenum param, float *val) -{ - switch(param) - { - case AL_CHORUS_RATE: - *val = props->Chorus.Rate; - break; - - case AL_CHORUS_DEPTH: - *val = props->Chorus.Depth; - break; - - case AL_CHORUS_FEEDBACK: - *val = props->Chorus.Feedback; - break; - - case AL_CHORUS_DELAY: - *val = props->Chorus.Delay; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param}; - } -} -void Chorus_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Chorus_getParamf(props, param, vals); } - -DEFINE_ALEFFECT_VTABLE(Chorus); - - struct ChorusStateFactory final : public EffectStateFactory { EffectState *create() override { return new ChorusState{}; } - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override { return &Chorus_vtable; } }; -EffectProps ChorusStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM; - props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE; - props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE; - props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH; - props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK; - props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY; - return props; -} - - -void Flanger_setParami(EffectProps *props, ALenum param, int val) -{ - switch(param) - { - case AL_FLANGER_WAVEFORM: - if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM)) - throw effect_exception{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)) - throw effect_exception{AL_INVALID_VALUE, "Flanger phase out of range"}; - props->Chorus.Phase = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param}; - } -} -void Flanger_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Flanger_setParami(props, param, vals[0]); } -void Flanger_setParamf(EffectProps *props, ALenum param, float val) -{ - switch(param) - { - case AL_FLANGER_RATE: - if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{AL_INVALID_VALUE, "Flanger delay out of range"}; - props->Chorus.Delay = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param}; - } -} -void Flanger_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Flanger_setParamf(props, param, vals[0]); } - -void Flanger_getParami(const EffectProps *props, ALenum param, int *val) -{ - switch(param) - { - case AL_FLANGER_WAVEFORM: - *val = props->Chorus.Waveform; - break; - - case AL_FLANGER_PHASE: - *val = props->Chorus.Phase; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param}; - } -} -void Flanger_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Flanger_getParami(props, param, vals); } -void Flanger_getParamf(const EffectProps *props, ALenum param, float *val) -{ - 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: - throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param}; - } -} -void Flanger_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Flanger_getParamf(props, param, vals); } - -DEFINE_ALEFFECT_VTABLE(Flanger); - /* 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. */ struct FlangerStateFactory final : public EffectStateFactory { EffectState *create() override { return new ChorusState{}; } - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override { return &Flanger_vtable; } }; -EffectProps FlangerStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - props.Chorus.Waveform = AL_FLANGER_DEFAULT_WAVEFORM; - props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE; - props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE; - props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH; - props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK; - props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY; - return props; -} - } // namespace EffectStateFactory *ChorusStateFactory_getFactory() diff --git a/alc/effects/compressor.cpp b/alc/effects/compressor.cpp index 9d92fdc7..f1fe6f36 100644 --- a/alc/effects/compressor.cpp +++ b/alc/effects/compressor.cpp @@ -154,70 +154,10 @@ void CompressorState::process(const size_t samplesToDo, } -void Compressor_setParami(EffectProps *props, ALenum param, int val) -{ - switch(param) - { - case AL_COMPRESSOR_ONOFF: - if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF)) - throw effect_exception{AL_INVALID_VALUE, "Compressor state out of range"}; - props->Compressor.OnOff = (val != AL_FALSE); - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x", - param}; - } -} -void Compressor_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Compressor_setParami(props, param, vals[0]); } -void Compressor_setParamf(EffectProps*, ALenum param, float) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; } -void Compressor_setParamfv(EffectProps*, ALenum param, const float*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", - param}; -} - -void Compressor_getParami(const EffectProps *props, ALenum param, int *val) -{ - switch(param) - { - case AL_COMPRESSOR_ONOFF: - *val = props->Compressor.OnOff; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x", - param}; - } -} -void Compressor_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Compressor_getParami(props, param, vals); } -void Compressor_getParamf(const EffectProps*, ALenum param, float*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; } -void Compressor_getParamfv(const EffectProps*, ALenum param, float*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", - param}; -} - -DEFINE_ALEFFECT_VTABLE(Compressor); - - struct CompressorStateFactory final : public EffectStateFactory { EffectState *create() override { return new CompressorState{}; } - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override { return &Compressor_vtable; } }; -EffectProps CompressorStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF; - return props; -} - } // namespace EffectStateFactory *CompressorStateFactory_getFactory() diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index c4bc41dc..e191e7bc 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -543,102 +543,10 @@ void ConvolutionState::process(const size_t samplesToDo, } -void ConvolutionEffect_setParami(EffectProps* /*props*/, ALenum param, int /*val*/) -{ - switch(param) - { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", - param}; - } -} -void ConvolutionEffect_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ - switch(param) - { - default: - ConvolutionEffect_setParami(props, param, vals[0]); - } -} -void ConvolutionEffect_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/) -{ - switch(param) - { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", - param}; - } -} -void ConvolutionEffect_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ - switch(param) - { - default: - ConvolutionEffect_setParamf(props, param, vals[0]); - } -} - -void ConvolutionEffect_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/) -{ - switch(param) - { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", - param}; - } -} -void ConvolutionEffect_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ - switch(param) - { - default: - ConvolutionEffect_getParami(props, param, vals); - } -} -void ConvolutionEffect_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/) -{ - switch(param) - { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", - param}; - } -} -void ConvolutionEffect_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ - switch(param) - { - default: - ConvolutionEffect_getParamf(props, param, vals); - } -} - -DEFINE_ALEFFECT_VTABLE(ConvolutionEffect); - - struct ConvolutionStateFactory final : public EffectStateFactory { - EffectState *create() override; - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override; + EffectState *create() override { return new ConvolutionState{}; } }; -/* Creates EffectState objects of the appropriate type. */ -EffectState *ConvolutionStateFactory::create() -{ return new ConvolutionState{}; } - -/* Returns an ALeffectProps initialized with this effect type's default - * property values. - */ -EffectProps ConvolutionStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - return props; -} - -/* Returns a pointer to this effect type's global set/get vtable. */ -const EffectVtable *ConvolutionStateFactory::getEffectVtable() const noexcept -{ return &ConvolutionEffect_vtable; } - } // namespace EffectStateFactory *ConvolutionStateFactory_getFactory() diff --git a/alc/effects/dedicated.cpp b/alc/effects/dedicated.cpp index 283d009a..8b9636ba 100644 --- a/alc/effects/dedicated.cpp +++ b/alc/effects/dedicated.cpp @@ -96,68 +96,10 @@ void DedicatedState::process(const size_t samplesToDo, const al::span= 0.0f && std::isfinite(val))) - throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"}; - props->Dedicated.Gain = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param}; - } -} -void Dedicated_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Dedicated_setParamf(props, param, vals[0]); } - -void Dedicated_getParami(const EffectProps*, ALenum param, int*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; } -void Dedicated_getParamiv(const EffectProps*, ALenum param, int*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", - param}; -} -void Dedicated_getParamf(const EffectProps *props, ALenum param, float *val) -{ - switch(param) - { - case AL_DEDICATED_GAIN: - *val = props->Dedicated.Gain; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param}; - } -} -void Dedicated_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Dedicated_getParamf(props, param, vals); } - -DEFINE_ALEFFECT_VTABLE(Dedicated); - - struct DedicatedStateFactory final : public EffectStateFactory { EffectState *create() override { return new DedicatedState{}; } - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override { return &Dedicated_vtable; } }; -EffectProps DedicatedStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - props.Dedicated.Gain = 1.0f; - return props; -} - } // namespace EffectStateFactory *DedicatedStateFactory_getFactory() diff --git a/alc/effects/distortion.cpp b/alc/effects/distortion.cpp index a199952f..c3c1d241 100644 --- a/alc/effects/distortion.cpp +++ b/alc/effects/distortion.cpp @@ -154,112 +154,10 @@ void DistortionState::process(const size_t samplesToDo, const al::span= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{AL_INVALID_VALUE, "Distortion EQ bandwidth out of range"}; - props->Distortion.EQBandwidth = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param}; - } -} -void Distortion_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Distortion_setParamf(props, param, vals[0]); } - -void Distortion_getParami(const EffectProps*, ALenum param, int*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; } -void Distortion_getParamiv(const EffectProps*, ALenum param, int*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", - param}; -} -void Distortion_getParamf(const EffectProps *props, ALenum param, float *val) -{ - switch(param) - { - case AL_DISTORTION_EDGE: - *val = props->Distortion.Edge; - break; - - case AL_DISTORTION_GAIN: - *val = props->Distortion.Gain; - break; - - case AL_DISTORTION_LOWPASS_CUTOFF: - *val = props->Distortion.LowpassCutoff; - break; - - case AL_DISTORTION_EQCENTER: - *val = props->Distortion.EQCenter; - break; - - case AL_DISTORTION_EQBANDWIDTH: - *val = props->Distortion.EQBandwidth; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param}; - } -} -void Distortion_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Distortion_getParamf(props, param, vals); } - -DEFINE_ALEFFECT_VTABLE(Distortion); - - struct DistortionStateFactory final : public EffectStateFactory { EffectState *create() override { return new DistortionState{}; } - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override { return &Distortion_vtable; } }; -EffectProps DistortionStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE; - props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN; - props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF; - props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER; - props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH; - return props; -} - } // namespace EffectStateFactory *DistortionStateFactory_getFactory() diff --git a/alc/effects/echo.cpp b/alc/effects/echo.cpp index 93305cdb..70aaf59c 100644 --- a/alc/effects/echo.cpp +++ b/alc/effects/echo.cpp @@ -154,106 +154,10 @@ void EchoState::process(const size_t samplesToDo, const al::span= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{AL_INVALID_VALUE, "Echo spread out of range"}; - props->Echo.Spread = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param}; - } -} -void Echo_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Echo_setParamf(props, param, vals[0]); } - -void Echo_getParami(const EffectProps*, ALenum param, int*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; } -void Echo_getParamiv(const EffectProps*, ALenum param, int*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; } -void Echo_getParamf(const EffectProps *props, ALenum param, float *val) -{ - switch(param) - { - case AL_ECHO_DELAY: - *val = props->Echo.Delay; - break; - - case AL_ECHO_LRDELAY: - *val = props->Echo.LRDelay; - break; - - case AL_ECHO_DAMPING: - *val = props->Echo.Damping; - break; - - case AL_ECHO_FEEDBACK: - *val = props->Echo.Feedback; - break; - - case AL_ECHO_SPREAD: - *val = props->Echo.Spread; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param}; - } -} -void Echo_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Echo_getParamf(props, param, vals); } - -DEFINE_ALEFFECT_VTABLE(Echo); - - struct EchoStateFactory final : public EffectStateFactory { EffectState *create() override { return new EchoState{}; } - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override { return &Echo_vtable; } }; -EffectProps EchoStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - props.Echo.Delay = AL_ECHO_DEFAULT_DELAY; - props.Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY; - props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING; - props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK; - props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD; - return props; -} - } // namespace EffectStateFactory *EchoStateFactory_getFactory() diff --git a/alc/effects/equalizer.cpp b/alc/effects/equalizer.cpp index 2f02182c..19d38498 100644 --- a/alc/effects/equalizer.cpp +++ b/alc/effects/equalizer.cpp @@ -171,167 +171,10 @@ void EqualizerState::process(const size_t samplesToDo, const al::span= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{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)) - throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band cutoff out of range"}; - props->Equalizer.HighCutoff = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param}; - } -} -void Equalizer_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Equalizer_setParamf(props, param, vals[0]); } - -void Equalizer_getParami(const EffectProps*, ALenum param, int*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; } -void Equalizer_getParamiv(const EffectProps*, ALenum param, int*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", - param}; -} -void Equalizer_getParamf(const EffectProps *props, ALenum param, float *val) -{ - switch(param) - { - case AL_EQUALIZER_LOW_GAIN: - *val = props->Equalizer.LowGain; - break; - - case AL_EQUALIZER_LOW_CUTOFF: - *val = props->Equalizer.LowCutoff; - break; - - case AL_EQUALIZER_MID1_GAIN: - *val = props->Equalizer.Mid1Gain; - break; - - case AL_EQUALIZER_MID1_CENTER: - *val = props->Equalizer.Mid1Center; - break; - - case AL_EQUALIZER_MID1_WIDTH: - *val = props->Equalizer.Mid1Width; - break; - - case AL_EQUALIZER_MID2_GAIN: - *val = props->Equalizer.Mid2Gain; - break; - - case AL_EQUALIZER_MID2_CENTER: - *val = props->Equalizer.Mid2Center; - break; - - case AL_EQUALIZER_MID2_WIDTH: - *val = props->Equalizer.Mid2Width; - break; - - case AL_EQUALIZER_HIGH_GAIN: - *val = props->Equalizer.HighGain; - break; - - case AL_EQUALIZER_HIGH_CUTOFF: - *val = props->Equalizer.HighCutoff; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param}; - } -} -void Equalizer_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Equalizer_getParamf(props, param, vals); } - -DEFINE_ALEFFECT_VTABLE(Equalizer); - - struct EqualizerStateFactory final : public EffectStateFactory { EffectState *create() override { return new EqualizerState{}; } - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override { return &Equalizer_vtable; } }; -EffectProps EqualizerStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF; - props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN; - props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER; - props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN; - props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH; - props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER; - props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN; - props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH; - props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF; - props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN; - return props; -} - } // namespace EffectStateFactory *EqualizerStateFactory_getFactory() diff --git a/alc/effects/fshifter.cpp b/alc/effects/fshifter.cpp index 3eb4bb79..da9b0ce2 100644 --- a/alc/effects/fshifter.cpp +++ b/alc/effects/fshifter.cpp @@ -221,102 +221,10 @@ void FshifterState::process(const size_t samplesToDo, const al::span= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY)) - throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"}; - props->Fshifter.Frequency = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", - param}; - } -} -void Fshifter_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Fshifter_setParamf(props, param, vals[0]); } - -void Fshifter_setParami(EffectProps *props, ALenum param, int val) -{ - switch(param) - { - case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION: - if(!(val >= AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION)) - throw effect_exception{AL_INVALID_VALUE, - "Frequency shifter left direction out of range"}; - props->Fshifter.LeftDirection = val; - break; - - case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION: - if(!(val >= AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION)) - throw effect_exception{AL_INVALID_VALUE, - "Frequency shifter right direction out of range"}; - props->Fshifter.RightDirection = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, - "Invalid frequency shifter integer property 0x%04x", param}; - } -} -void Fshifter_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Fshifter_setParami(props, param, vals[0]); } - -void Fshifter_getParami(const EffectProps *props, ALenum param, int *val) -{ - switch(param) - { - case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION: - *val = props->Fshifter.LeftDirection; - break; - case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION: - *val = props->Fshifter.RightDirection; - break; - default: - throw effect_exception{AL_INVALID_ENUM, - "Invalid frequency shifter integer property 0x%04x", param}; - } -} -void Fshifter_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Fshifter_getParami(props, param, vals); } - -void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val) -{ - switch(param) - { - case AL_FREQUENCY_SHIFTER_FREQUENCY: - *val = props->Fshifter.Frequency; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", - param}; - } -} -void Fshifter_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Fshifter_getParamf(props, param, vals); } - -DEFINE_ALEFFECT_VTABLE(Fshifter); - - struct FshifterStateFactory final : public EffectStateFactory { EffectState *create() override { return new FshifterState{}; } - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override { return &Fshifter_vtable; } }; -EffectProps FshifterStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - props.Fshifter.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY; - props.Fshifter.LeftDirection = AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION; - props.Fshifter.RightDirection = AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION; - return props; -} - } // namespace EffectStateFactory *FshifterStateFactory_getFactory() diff --git a/alc/effects/modulator.cpp b/alc/effects/modulator.cpp index cd48f91e..7e4f9fc0 100644 --- a/alc/effects/modulator.cpp +++ b/alc/effects/modulator.cpp @@ -160,108 +160,10 @@ void ModulatorState::process(const size_t samplesToDo, const al::span= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY)) - throw effect_exception{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)) - throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range"}; - props->Modulator.HighPassCutoff = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param}; - } -} -void Modulator_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Modulator_setParamf(props, param, vals[0]); } -void Modulator_setParami(EffectProps *props, ALenum param, int val) -{ - switch(param) - { - case AL_RING_MODULATOR_FREQUENCY: - case AL_RING_MODULATOR_HIGHPASS_CUTOFF: - Modulator_setParamf(props, param, static_cast(val)); - break; - - case AL_RING_MODULATOR_WAVEFORM: - if(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM)) - throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform"}; - props->Modulator.Waveform = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", - param}; - } -} -void Modulator_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Modulator_setParami(props, param, vals[0]); } - -void Modulator_getParami(const EffectProps *props, ALenum param, int *val) -{ - switch(param) - { - case AL_RING_MODULATOR_FREQUENCY: - *val = static_cast(props->Modulator.Frequency); - break; - case AL_RING_MODULATOR_HIGHPASS_CUTOFF: - *val = static_cast(props->Modulator.HighPassCutoff); - break; - case AL_RING_MODULATOR_WAVEFORM: - *val = props->Modulator.Waveform; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", - param}; - } -} -void Modulator_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Modulator_getParami(props, param, vals); } -void Modulator_getParamf(const EffectProps *props, ALenum param, float *val) -{ - switch(param) - { - case AL_RING_MODULATOR_FREQUENCY: - *val = props->Modulator.Frequency; - break; - case AL_RING_MODULATOR_HIGHPASS_CUTOFF: - *val = props->Modulator.HighPassCutoff; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param}; - } -} -void Modulator_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Modulator_getParamf(props, param, vals); } - -DEFINE_ALEFFECT_VTABLE(Modulator); - - struct ModulatorStateFactory final : public EffectStateFactory { EffectState *create() override { return new ModulatorState{}; } - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override { return &Modulator_vtable; } }; -EffectProps ModulatorStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY; - props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF; - props.Modulator.Waveform = AL_RING_MODULATOR_DEFAULT_WAVEFORM; - return props; -} - } // namespace EffectStateFactory *ModulatorStateFactory_getFactory() diff --git a/alc/effects/null.cpp b/alc/effects/null.cpp index 2f676d4d..6ae74021 100644 --- a/alc/effects/null.cpp +++ b/alc/effects/null.cpp @@ -65,102 +65,14 @@ void NullState::process(const size_t/*samplesToDo*/, } -void NullEffect_setParami(EffectProps* /*props*/, ALenum param, int /*val*/) -{ - switch(param) - { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", - param}; - } -} -void NullEffect_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ - switch(param) - { - default: - NullEffect_setParami(props, param, vals[0]); - } -} -void NullEffect_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/) -{ - switch(param) - { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", - param}; - } -} -void NullEffect_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ - switch(param) - { - default: - NullEffect_setParamf(props, param, vals[0]); - } -} - -void NullEffect_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/) -{ - switch(param) - { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", - param}; - } -} -void NullEffect_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ - switch(param) - { - default: - NullEffect_getParami(props, param, vals); - } -} -void NullEffect_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/) -{ - switch(param) - { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", - param}; - } -} -void NullEffect_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ - switch(param) - { - default: - NullEffect_getParamf(props, param, vals); - } -} - -DEFINE_ALEFFECT_VTABLE(NullEffect); - - struct NullStateFactory final : public EffectStateFactory { EffectState *create() override; - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override; }; /* Creates EffectState objects of the appropriate type. */ EffectState *NullStateFactory::create() { return new NullState{}; } -/* Returns an ALeffectProps initialized with this effect type's default - * property values. - */ -EffectProps NullStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - return props; -} - -/* Returns a pointer to this effect type's global set/get vtable. */ -const EffectVtable *NullStateFactory::getEffectVtable() const noexcept -{ return &NullEffect_vtable; } - } // namespace EffectStateFactory *NullStateFactory_getFactory() diff --git a/alc/effects/pshifter.cpp b/alc/effects/pshifter.cpp index 84a001d1..15ff626d 100644 --- a/alc/effects/pshifter.cpp +++ b/alc/effects/pshifter.cpp @@ -20,10 +20,6 @@ #include "config.h" -#ifdef HAVE_SSE_INTRINSICS -#include -#endif - #include #include #include @@ -249,85 +245,10 @@ void PshifterState::process(const size_t samplesToDo, const al::span= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE)) - throw effect_exception{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)) - throw effect_exception{AL_INVALID_VALUE, "Pitch shifter fine tune out of range"}; - props->Pshifter.FineTune = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", - param}; - } -} -void Pshifter_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Pshifter_setParami(props, param, vals[0]); } - -void Pshifter_getParami(const EffectProps *props, ALenum param, int *val) -{ - switch(param) - { - case AL_PITCH_SHIFTER_COARSE_TUNE: - *val = props->Pshifter.CoarseTune; - break; - case AL_PITCH_SHIFTER_FINE_TUNE: - *val = props->Pshifter.FineTune; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", - param}; - } -} -void Pshifter_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Pshifter_getParami(props, param, vals); } - -void Pshifter_getParamf(const EffectProps*, ALenum param, float*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; } -void Pshifter_getParamfv(const EffectProps*, ALenum param, float*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x", - param}; -} - -DEFINE_ALEFFECT_VTABLE(Pshifter); - - struct PshifterStateFactory final : public EffectStateFactory { - EffectState *create() override; - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override { return &Pshifter_vtable; } + EffectState *create() override { return new PshifterState{}; } }; -EffectState *PshifterStateFactory::create() -{ return new PshifterState{}; } - -EffectProps PshifterStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - props.Pshifter.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE; - props.Pshifter.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE; - return props; -} - } // namespace EffectStateFactory *PshifterStateFactory_getFactory() diff --git a/alc/effects/reverb.cpp b/alc/effects/reverb.cpp index 45464193..78773abf 100644 --- a/alc/effects/reverb.cpp +++ b/alc/effects/reverb.cpp @@ -1680,557 +1680,14 @@ void ReverbState::process(const size_t samplesToDo, const al::span= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hflimit out of range"}; - props->Reverb.DecayHFLimit = val != AL_FALSE; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x", - param}; - } -} -void EAXReverb_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ EAXReverb_setParami(props, param, vals[0]); } -void EAXReverb_setParamf(EffectProps *props, ALenum param, float val) -{ - switch(param) - { - case AL_EAXREVERB_DENSITY: - if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb density out of range"}; - props->Reverb.Density = val; - break; - - case AL_EAXREVERB_DIFFUSION: - if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb diffusion out of range"}; - props->Reverb.Diffusion = val; - break; - - case AL_EAXREVERB_GAIN: - if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gain out of range"}; - props->Reverb.Gain = val; - break; - - case AL_EAXREVERB_GAINHF: - if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainhf out of range"}; - props->Reverb.GainHF = val; - break; - - case AL_EAXREVERB_GAINLF: - if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainlf out of range"}; - props->Reverb.GainLF = val; - break; - - case AL_EAXREVERB_DECAY_TIME: - if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay time out of range"}; - props->Reverb.DecayTime = val; - break; - - case AL_EAXREVERB_DECAY_HFRATIO: - if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hfratio out of range"}; - props->Reverb.DecayHFRatio = val; - break; - - case AL_EAXREVERB_DECAY_LFRATIO: - if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay lfratio out of range"}; - props->Reverb.DecayLFRatio = val; - break; - - case AL_EAXREVERB_REFLECTIONS_GAIN: - if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections gain out of range"}; - props->Reverb.ReflectionsGain = val; - break; - - case AL_EAXREVERB_REFLECTIONS_DELAY: - if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections delay out of range"}; - props->Reverb.ReflectionsDelay = val; - break; - - case AL_EAXREVERB_LATE_REVERB_GAIN: - if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb gain out of range"}; - props->Reverb.LateReverbGain = val; - break; - - case AL_EAXREVERB_LATE_REVERB_DELAY: - if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb delay out of range"}; - props->Reverb.LateReverbDelay = val; - break; - - case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: - if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb air absorption gainhf out of range"}; - props->Reverb.AirAbsorptionGainHF = val; - break; - - case AL_EAXREVERB_ECHO_TIME: - if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb echo time out of range"}; - props->Reverb.EchoTime = val; - break; - - case AL_EAXREVERB_ECHO_DEPTH: - if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb echo depth out of range"}; - props->Reverb.EchoDepth = val; - break; - - case AL_EAXREVERB_MODULATION_TIME: - if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb modulation time out of range"}; - props->Reverb.ModulationTime = val; - break; - - case AL_EAXREVERB_MODULATION_DEPTH: - if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb modulation depth out of range"}; - props->Reverb.ModulationDepth = val; - break; - - case AL_EAXREVERB_HFREFERENCE: - if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb hfreference out of range"}; - props->Reverb.HFReference = val; - break; - - case AL_EAXREVERB_LFREFERENCE: - if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb lfreference out of range"}; - props->Reverb.LFReference = val; - break; - - case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: - if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb room rolloff factor out of range"}; - props->Reverb.RoomRolloffFactor = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param}; - } -} -void EAXReverb_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ - switch(param) - { - case AL_EAXREVERB_REFLECTIONS_PAN: - if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2]))) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections pan out of range"}; - props->Reverb.ReflectionsPan[0] = vals[0]; - props->Reverb.ReflectionsPan[1] = vals[1]; - props->Reverb.ReflectionsPan[2] = vals[2]; - break; - case AL_EAXREVERB_LATE_REVERB_PAN: - if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2]))) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb pan out of range"}; - props->Reverb.LateReverbPan[0] = vals[0]; - props->Reverb.LateReverbPan[1] = vals[1]; - props->Reverb.LateReverbPan[2] = vals[2]; - break; - - default: - EAXReverb_setParamf(props, param, vals[0]); - break; - } -} - -void EAXReverb_getParami(const EffectProps *props, ALenum param, int *val) -{ - switch(param) - { - case AL_EAXREVERB_DECAY_HFLIMIT: - *val = props->Reverb.DecayHFLimit; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x", - param}; - } -} -void EAXReverb_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ EAXReverb_getParami(props, param, vals); } -void EAXReverb_getParamf(const EffectProps *props, ALenum param, float *val) -{ - switch(param) - { - case AL_EAXREVERB_DENSITY: - *val = props->Reverb.Density; - break; - - case AL_EAXREVERB_DIFFUSION: - *val = props->Reverb.Diffusion; - break; - - case AL_EAXREVERB_GAIN: - *val = props->Reverb.Gain; - break; - - case AL_EAXREVERB_GAINHF: - *val = props->Reverb.GainHF; - break; - - case AL_EAXREVERB_GAINLF: - *val = props->Reverb.GainLF; - break; - - case AL_EAXREVERB_DECAY_TIME: - *val = props->Reverb.DecayTime; - break; - - case AL_EAXREVERB_DECAY_HFRATIO: - *val = props->Reverb.DecayHFRatio; - break; - - case AL_EAXREVERB_DECAY_LFRATIO: - *val = props->Reverb.DecayLFRatio; - break; - - case AL_EAXREVERB_REFLECTIONS_GAIN: - *val = props->Reverb.ReflectionsGain; - break; - - case AL_EAXREVERB_REFLECTIONS_DELAY: - *val = props->Reverb.ReflectionsDelay; - break; - - case AL_EAXREVERB_LATE_REVERB_GAIN: - *val = props->Reverb.LateReverbGain; - break; - - case AL_EAXREVERB_LATE_REVERB_DELAY: - *val = props->Reverb.LateReverbDelay; - break; - - case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: - *val = props->Reverb.AirAbsorptionGainHF; - break; - - case AL_EAXREVERB_ECHO_TIME: - *val = props->Reverb.EchoTime; - break; - - case AL_EAXREVERB_ECHO_DEPTH: - *val = props->Reverb.EchoDepth; - break; - - case AL_EAXREVERB_MODULATION_TIME: - *val = props->Reverb.ModulationTime; - break; - - case AL_EAXREVERB_MODULATION_DEPTH: - *val = props->Reverb.ModulationDepth; - break; - - case AL_EAXREVERB_HFREFERENCE: - *val = props->Reverb.HFReference; - break; - - case AL_EAXREVERB_LFREFERENCE: - *val = props->Reverb.LFReference; - break; - - case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: - *val = props->Reverb.RoomRolloffFactor; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param}; - } -} -void EAXReverb_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ - switch(param) - { - case AL_EAXREVERB_REFLECTIONS_PAN: - vals[0] = props->Reverb.ReflectionsPan[0]; - vals[1] = props->Reverb.ReflectionsPan[1]; - vals[2] = props->Reverb.ReflectionsPan[2]; - break; - case AL_EAXREVERB_LATE_REVERB_PAN: - vals[0] = props->Reverb.LateReverbPan[0]; - vals[1] = props->Reverb.LateReverbPan[1]; - vals[2] = props->Reverb.LateReverbPan[2]; - break; - - default: - EAXReverb_getParamf(props, param, vals); - break; - } -} - -DEFINE_ALEFFECT_VTABLE(EAXReverb); - - struct ReverbStateFactory final : public EffectStateFactory { EffectState *create() override { return new ReverbState{}; } - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override { return &EAXReverb_vtable; } }; -EffectProps ReverbStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - props.Reverb.Density = AL_EAXREVERB_DEFAULT_DENSITY; - props.Reverb.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION; - props.Reverb.Gain = AL_EAXREVERB_DEFAULT_GAIN; - props.Reverb.GainHF = AL_EAXREVERB_DEFAULT_GAINHF; - props.Reverb.GainLF = AL_EAXREVERB_DEFAULT_GAINLF; - props.Reverb.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME; - props.Reverb.DecayHFRatio = AL_EAXREVERB_DEFAULT_DECAY_HFRATIO; - props.Reverb.DecayLFRatio = AL_EAXREVERB_DEFAULT_DECAY_LFRATIO; - props.Reverb.ReflectionsGain = AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN; - props.Reverb.ReflectionsDelay = AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY; - props.Reverb.ReflectionsPan[0] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; - props.Reverb.ReflectionsPan[1] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; - props.Reverb.ReflectionsPan[2] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; - props.Reverb.LateReverbGain = AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN; - props.Reverb.LateReverbDelay = AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY; - props.Reverb.LateReverbPan[0] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; - props.Reverb.LateReverbPan[1] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; - props.Reverb.LateReverbPan[2] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; - props.Reverb.EchoTime = AL_EAXREVERB_DEFAULT_ECHO_TIME; - props.Reverb.EchoDepth = AL_EAXREVERB_DEFAULT_ECHO_DEPTH; - props.Reverb.ModulationTime = AL_EAXREVERB_DEFAULT_MODULATION_TIME; - props.Reverb.ModulationDepth = AL_EAXREVERB_DEFAULT_MODULATION_DEPTH; - props.Reverb.AirAbsorptionGainHF = AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF; - props.Reverb.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE; - props.Reverb.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE; - props.Reverb.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; - props.Reverb.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT; - return props; -} - - -void StdReverb_setParami(EffectProps *props, ALenum param, int val) -{ - switch(param) - { - case AL_REVERB_DECAY_HFLIMIT: - if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT)) - throw effect_exception{AL_INVALID_VALUE, "Reverb decay hflimit out of range"}; - props->Reverb.DecayHFLimit = val != AL_FALSE; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param}; - } -} -void StdReverb_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ StdReverb_setParami(props, param, vals[0]); } -void StdReverb_setParamf(EffectProps *props, ALenum param, float val) -{ - switch(param) - { - case AL_REVERB_DENSITY: - if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY)) - throw effect_exception{AL_INVALID_VALUE, "Reverb density out of range"}; - props->Reverb.Density = val; - break; - - case AL_REVERB_DIFFUSION: - if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION)) - throw effect_exception{AL_INVALID_VALUE, "Reverb diffusion out of range"}; - props->Reverb.Diffusion = val; - break; - - case AL_REVERB_GAIN: - if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Reverb gain out of range"}; - props->Reverb.Gain = val; - break; - - case AL_REVERB_GAINHF: - if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF)) - throw effect_exception{AL_INVALID_VALUE, "Reverb gainhf out of range"}; - props->Reverb.GainHF = val; - break; - - case AL_REVERB_DECAY_TIME: - if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME)) - throw effect_exception{AL_INVALID_VALUE, "Reverb decay time out of range"}; - props->Reverb.DecayTime = val; - break; - - case AL_REVERB_DECAY_HFRATIO: - if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO)) - throw effect_exception{AL_INVALID_VALUE, "Reverb decay hfratio out of range"}; - props->Reverb.DecayHFRatio = val; - break; - - case AL_REVERB_REFLECTIONS_GAIN: - if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Reverb reflections gain out of range"}; - props->Reverb.ReflectionsGain = val; - break; - - case AL_REVERB_REFLECTIONS_DELAY: - if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY)) - throw effect_exception{AL_INVALID_VALUE, "Reverb reflections delay out of range"}; - props->Reverb.ReflectionsDelay = val; - break; - - case AL_REVERB_LATE_REVERB_GAIN: - if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Reverb late reverb gain out of range"}; - props->Reverb.LateReverbGain = val; - break; - - case AL_REVERB_LATE_REVERB_DELAY: - if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY)) - throw effect_exception{AL_INVALID_VALUE, "Reverb late reverb delay out of range"}; - props->Reverb.LateReverbDelay = val; - break; - - case AL_REVERB_AIR_ABSORPTION_GAINHF: - if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF)) - throw effect_exception{AL_INVALID_VALUE, "Reverb air absorption gainhf out of range"}; - props->Reverb.AirAbsorptionGainHF = val; - break; - - case AL_REVERB_ROOM_ROLLOFF_FACTOR: - if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR)) - throw effect_exception{AL_INVALID_VALUE, "Reverb room rolloff factor out of range"}; - props->Reverb.RoomRolloffFactor = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param}; - } -} -void StdReverb_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ StdReverb_setParamf(props, param, vals[0]); } - -void StdReverb_getParami(const EffectProps *props, ALenum param, int *val) -{ - switch(param) - { - case AL_REVERB_DECAY_HFLIMIT: - *val = props->Reverb.DecayHFLimit; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param}; - } -} -void StdReverb_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ StdReverb_getParami(props, param, vals); } -void StdReverb_getParamf(const EffectProps *props, ALenum param, float *val) -{ - switch(param) - { - case AL_REVERB_DENSITY: - *val = props->Reverb.Density; - break; - - case AL_REVERB_DIFFUSION: - *val = props->Reverb.Diffusion; - break; - - case AL_REVERB_GAIN: - *val = props->Reverb.Gain; - break; - - case AL_REVERB_GAINHF: - *val = props->Reverb.GainHF; - break; - - case AL_REVERB_DECAY_TIME: - *val = props->Reverb.DecayTime; - break; - - case AL_REVERB_DECAY_HFRATIO: - *val = props->Reverb.DecayHFRatio; - break; - - case AL_REVERB_REFLECTIONS_GAIN: - *val = props->Reverb.ReflectionsGain; - break; - - case AL_REVERB_REFLECTIONS_DELAY: - *val = props->Reverb.ReflectionsDelay; - break; - - case AL_REVERB_LATE_REVERB_GAIN: - *val = props->Reverb.LateReverbGain; - break; - - case AL_REVERB_LATE_REVERB_DELAY: - *val = props->Reverb.LateReverbDelay; - break; - - case AL_REVERB_AIR_ABSORPTION_GAINHF: - *val = props->Reverb.AirAbsorptionGainHF; - break; - - case AL_REVERB_ROOM_ROLLOFF_FACTOR: - *val = props->Reverb.RoomRolloffFactor; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param}; - } -} -void StdReverb_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ StdReverb_getParamf(props, param, vals); } - -DEFINE_ALEFFECT_VTABLE(StdReverb); - - struct StdReverbStateFactory final : public EffectStateFactory { EffectState *create() override { return new ReverbState{}; } - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override { return &StdReverb_vtable; } }; -EffectProps StdReverbStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - props.Reverb.Density = AL_REVERB_DEFAULT_DENSITY; - props.Reverb.Diffusion = AL_REVERB_DEFAULT_DIFFUSION; - props.Reverb.Gain = AL_REVERB_DEFAULT_GAIN; - props.Reverb.GainHF = AL_REVERB_DEFAULT_GAINHF; - props.Reverb.GainLF = 1.0f; - props.Reverb.DecayTime = AL_REVERB_DEFAULT_DECAY_TIME; - props.Reverb.DecayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO; - props.Reverb.DecayLFRatio = 1.0f; - props.Reverb.ReflectionsGain = AL_REVERB_DEFAULT_REFLECTIONS_GAIN; - props.Reverb.ReflectionsDelay = AL_REVERB_DEFAULT_REFLECTIONS_DELAY; - props.Reverb.ReflectionsPan[0] = 0.0f; - props.Reverb.ReflectionsPan[1] = 0.0f; - props.Reverb.ReflectionsPan[2] = 0.0f; - props.Reverb.LateReverbGain = AL_REVERB_DEFAULT_LATE_REVERB_GAIN; - props.Reverb.LateReverbDelay = AL_REVERB_DEFAULT_LATE_REVERB_DELAY; - props.Reverb.LateReverbPan[0] = 0.0f; - props.Reverb.LateReverbPan[1] = 0.0f; - props.Reverb.LateReverbPan[2] = 0.0f; - props.Reverb.EchoTime = 0.25f; - props.Reverb.EchoDepth = 0.0f; - props.Reverb.ModulationTime = 0.25f; - props.Reverb.ModulationDepth = 0.0f; - props.Reverb.AirAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF; - props.Reverb.HFReference = 5000.0f; - props.Reverb.LFReference = 250.0f; - props.Reverb.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; - props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT; - return props; -} - } // namespace EffectStateFactory *ReverbStateFactory_getFactory() diff --git a/alc/effects/vmorpher.cpp b/alc/effects/vmorpher.cpp index 8e945396..2adcab56 100644 --- a/alc/effects/vmorpher.cpp +++ b/alc/effects/vmorpher.cpp @@ -294,139 +294,10 @@ void VmorpherState::process(const size_t samplesToDo, const al::span= AL_VOCAL_MORPHER_MIN_WAVEFORM && val <= AL_VOCAL_MORPHER_MAX_WAVEFORM)) - throw effect_exception{AL_INVALID_VALUE, "Vocal morpher waveform out of range"}; - props->Vmorpher.Waveform = val; - break; - - case AL_VOCAL_MORPHER_PHONEMEA: - if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA)) - throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a out of range"}; - props->Vmorpher.PhonemeA = val; - break; - - case AL_VOCAL_MORPHER_PHONEMEB: - if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB)) - throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b out of range"}; - props->Vmorpher.PhonemeB = val; - break; - - case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: - if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING)) - throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a coarse tuning out of range"}; - props->Vmorpher.PhonemeACoarseTuning = val; - break; - - case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: - if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING)) - throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b coarse tuning out of range"}; - props->Vmorpher.PhonemeBCoarseTuning = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x", - param}; - } -} -void Vmorpher_setParamiv(EffectProps*, ALenum param, const int*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x", - param}; -} -void Vmorpher_setParamf(EffectProps *props, ALenum param, float val) -{ - switch(param) - { - case AL_VOCAL_MORPHER_RATE: - if(!(val >= AL_VOCAL_MORPHER_MIN_RATE && val <= AL_VOCAL_MORPHER_MAX_RATE)) - throw effect_exception{AL_INVALID_VALUE, "Vocal morpher rate out of range"}; - props->Vmorpher.Rate = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x", - param}; - } -} -void Vmorpher_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Vmorpher_setParamf(props, param, vals[0]); } - -void Vmorpher_getParami(const EffectProps *props, ALenum param, int* val) -{ - switch(param) - { - case AL_VOCAL_MORPHER_PHONEMEA: - *val = props->Vmorpher.PhonemeA; - break; - - case AL_VOCAL_MORPHER_PHONEMEB: - *val = props->Vmorpher.PhonemeB; - break; - - case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: - *val = props->Vmorpher.PhonemeACoarseTuning; - break; - - case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: - *val = props->Vmorpher.PhonemeBCoarseTuning; - break; - - case AL_VOCAL_MORPHER_WAVEFORM: - *val = props->Vmorpher.Waveform; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x", - param}; - } -} -void Vmorpher_getParamiv(const EffectProps*, ALenum param, int*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x", - param}; -} -void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val) -{ - switch(param) - { - case AL_VOCAL_MORPHER_RATE: - *val = props->Vmorpher.Rate; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x", - param}; - } -} -void Vmorpher_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Vmorpher_getParamf(props, param, vals); } - -DEFINE_ALEFFECT_VTABLE(Vmorpher); - - struct VmorpherStateFactory final : public EffectStateFactory { EffectState *create() override { return new VmorpherState{}; } - EffectProps getDefaultProps() const noexcept override; - const EffectVtable *getEffectVtable() const noexcept override { return &Vmorpher_vtable; } }; -EffectProps VmorpherStateFactory::getDefaultProps() const noexcept -{ - EffectProps props{}; - props.Vmorpher.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE; - props.Vmorpher.PhonemeA = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA; - props.Vmorpher.PhonemeB = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB; - props.Vmorpher.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING; - props.Vmorpher.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING; - props.Vmorpher.Waveform = AL_VOCAL_MORPHER_DEFAULT_WAVEFORM; - return props; -} - } // namespace EffectStateFactory *VmorpherStateFactory_getFactory() -- cgit v1.2.3 From b0919240ab73f2e340f26a913baf7524b35a4c85 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 27 Nov 2020 19:18:17 -0800 Subject: Move some sources to a separate directory To begin separating the ALC interfaces from internal ones. --- CMakeLists.txt | 6 ++- alc/alc.cpp | 62 +----------------------------- alc/alcmain.h | 7 +--- alc/alu.cpp | 2 +- alc/bformatdec.h | 2 +- alc/bufferline.h | 15 -------- alc/converter.h | 2 +- alc/devformat.h | 100 ------------------------------------------------ alc/front_stablizer.h | 2 +- alc/hrtf.h | 2 +- alc/panning.cpp | 2 +- alc/voice.cpp | 2 +- alc/voice.h | 2 +- core/bufferline.h | 14 +++++++ core/devformat.cpp | 65 +++++++++++++++++++++++++++++++ core/devformat.h | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++ 16 files changed, 197 insertions(+), 191 deletions(-) delete mode 100644 alc/bufferline.h delete mode 100644 alc/devformat.h create mode 100644 core/bufferline.h create mode 100644 core/devformat.cpp create mode 100644 core/devformat.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 59637da0..31ef66a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -627,6 +627,10 @@ set(OPENAL_OBJS al/state.cpp ) set(ALC_OBJS + core/bufferline.h + core/devformat.cpp + core/devformat.h + alc/alc.cpp alc/alcmain.h alc/alu.cpp @@ -644,7 +648,6 @@ set(ALC_OBJS alc/bsinc_defs.h alc/bsinc_tables.cpp alc/bsinc_tables.h - alc/bufferline.h alc/buffer_storage.cpp alc/buffer_storage.h alc/compat.h @@ -652,7 +655,6 @@ set(ALC_OBJS alc/converter.h alc/cpu_caps.cpp alc/cpu_caps.h - alc/devformat.h alc/effectslot.cpp alc/effectslot.h alc/effects/base.h diff --git a/alc/alc.cpp b/alc/alc.cpp index 623fd688..7cf6c5a9 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -80,8 +80,8 @@ #include "bformatdec.h" #include "bs2b.h" #include "compat.h" +#include "core/devformat.h" #include "cpu_caps.h" -#include "devformat.h" #include "effects/base.h" #include "filters/nfc.h" #include "filters/splitter.h" @@ -1309,66 +1309,6 @@ BOOL APIENTRY DllMain(HINSTANCE module, DWORD reason, LPVOID /*reserved*/) /************************************************ * Device format information ************************************************/ -const ALCchar *DevFmtTypeString(DevFmtType type) noexcept -{ - switch(type) - { - case DevFmtByte: return "Int8"; - case DevFmtUByte: return "UInt8"; - case DevFmtShort: return "Int16"; - case DevFmtUShort: return "UInt16"; - case DevFmtInt: return "Int32"; - case DevFmtUInt: return "UInt32"; - case DevFmtFloat: return "Float32"; - } - return "(unknown type)"; -} -const ALCchar *DevFmtChannelsString(DevFmtChannels chans) noexcept -{ - switch(chans) - { - case DevFmtMono: return "Mono"; - case DevFmtStereo: return "Stereo"; - case DevFmtQuad: return "Quadraphonic"; - case DevFmtX51: return "5.1 Surround"; - case DevFmtX51Rear: return "5.1 Surround (Rear)"; - case DevFmtX61: return "6.1 Surround"; - case DevFmtX71: return "7.1 Surround"; - case DevFmtAmbi3D: return "Ambisonic 3D"; - } - return "(unknown channels)"; -} - -uint BytesFromDevFmt(DevFmtType type) noexcept -{ - switch(type) - { - case DevFmtByte: return sizeof(int8_t); - case DevFmtUByte: return sizeof(uint8_t); - case DevFmtShort: return sizeof(int16_t); - case DevFmtUShort: return sizeof(uint16_t); - case DevFmtInt: return sizeof(int32_t); - case DevFmtUInt: return sizeof(uint32_t); - case DevFmtFloat: return sizeof(float); - } - return 0; -} -uint ChannelsFromDevFmt(DevFmtChannels chans, uint ambiorder) noexcept -{ - switch(chans) - { - case DevFmtMono: return 1; - case DevFmtStereo: return 2; - case DevFmtQuad: return 4; - case DevFmtX51: return 6; - case DevFmtX51Rear: return 6; - case DevFmtX61: return 7; - case DevFmtX71: return 8; - case DevFmtAmbi3D: return (ambiorder+1) * (ambiorder+1); - } - return 0; -} - namespace { struct DevFmtPair { DevFmtChannels chans; DevFmtType type; }; diff --git a/alc/alcmain.h b/alc/alcmain.h index abeb0201..41906bde 100644 --- a/alc/alcmain.h +++ b/alc/alcmain.h @@ -23,8 +23,8 @@ #include "alspan.h" #include "ambidefs.h" #include "atomic.h" -#include "bufferline.h" -#include "devformat.h" +#include "core/bufferline.h" +#include "core/devformat.h" #include "filters/splitter.h" #include "hrtf.h" #include "inprogext.h" @@ -366,9 +366,6 @@ struct ALCdevice : public al::intrusive_ref { extern int RTPrioLevel; void SetRTPriority(void); -const ALCchar *DevFmtTypeString(DevFmtType type) noexcept; -const ALCchar *DevFmtChannelsString(DevFmtChannels chans) noexcept; - /** * Returns the index for the given channel name (e.g. FrontCenter), or * INVALID_CHANNEL_INDEX if it doesn't exist. diff --git a/alc/alu.cpp b/alc/alu.cpp index 7e7dabf9..10069fd1 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -59,8 +59,8 @@ #include "atomic.h" #include "bformatdec.h" #include "bs2b.h" +#include "core/devformat.h" #include "cpu_caps.h" -#include "devformat.h" #include "effects/base.h" #include "filters/biquad.h" #include "filters/nfc.h" diff --git a/alc/bformatdec.h b/alc/bformatdec.h index 03249c55..280ca2fc 100644 --- a/alc/bformatdec.h +++ b/alc/bformatdec.h @@ -11,7 +11,7 @@ #include "almalloc.h" #include "alspan.h" #include "ambidefs.h" -#include "devformat.h" +#include "core/devformat.h" #include "filters/splitter.h" struct AmbDecConf; diff --git a/alc/bufferline.h b/alc/bufferline.h deleted file mode 100644 index 79f0996c..00000000 --- a/alc/bufferline.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef ALC_BUFFERLINE_H -#define ALC_BUFFERLINE_H - -#include - -/* Size for temporary storage of buffer data, in floats. Larger values need - * more memory, while smaller values may need more iterations. The value needs - * to be a sensible size, however, as it constrains the max stepping value used - * for mixing, as well as the maximum number of samples per mixing iteration. - */ -#define BUFFERSIZE 1024 - -using FloatBufferLine = std::array; - -#endif /* ALC_BUFFERLINE_H */ diff --git a/alc/converter.h b/alc/converter.h index 17da3aae..a97d1ea5 100644 --- a/alc/converter.h +++ b/alc/converter.h @@ -10,7 +10,7 @@ #include "almalloc.h" #include "alnumeric.h" #include "alu.h" -#include "devformat.h" +#include "core/devformat.h" #include "voice.h" diff --git a/alc/devformat.h b/alc/devformat.h deleted file mode 100644 index b02ad025..00000000 --- a/alc/devformat.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef ALC_DEVFORMAT_H -#define ALC_DEVFORMAT_H - -#include - - -using uint = unsigned int; - -enum Channel { - FrontLeft = 0, - FrontRight, - FrontCenter, - LFE, - BackLeft, - BackRight, - BackCenter, - SideLeft, - SideRight, - - TopFrontLeft, - TopFrontCenter, - TopFrontRight, - TopCenter, - TopBackLeft, - TopBackCenter, - TopBackRight, - - MaxChannels -}; - - -/* Device formats */ -enum DevFmtType : unsigned char { - DevFmtByte, - DevFmtUByte, - DevFmtShort, - DevFmtUShort, - DevFmtInt, - DevFmtUInt, - DevFmtFloat, - - DevFmtTypeDefault = DevFmtFloat -}; -enum DevFmtChannels : unsigned char { - DevFmtMono, - DevFmtStereo, - DevFmtQuad, - DevFmtX51, - DevFmtX61, - DevFmtX71, - DevFmtAmbi3D, - - /* Similar to 5.1, except using rear channels instead of sides */ - DevFmtX51Rear, - - DevFmtChannelsDefault = DevFmtStereo -}; -#define MAX_OUTPUT_CHANNELS 16 - -/* DevFmtType traits, providing the type, etc given a DevFmtType. */ -template -struct DevFmtTypeTraits { }; - -template<> -struct DevFmtTypeTraits { using Type = int8_t; }; -template<> -struct DevFmtTypeTraits { using Type = uint8_t; }; -template<> -struct DevFmtTypeTraits { using Type = int16_t; }; -template<> -struct DevFmtTypeTraits { using Type = uint16_t; }; -template<> -struct DevFmtTypeTraits { using Type = int32_t; }; -template<> -struct DevFmtTypeTraits { using Type = uint32_t; }; -template<> -struct DevFmtTypeTraits { using Type = float; }; - - -uint BytesFromDevFmt(DevFmtType type) noexcept; -uint ChannelsFromDevFmt(DevFmtChannels chans, uint ambiorder) noexcept; -inline uint FrameSizeFromDevFmt(DevFmtChannels chans, DevFmtType type, uint ambiorder) noexcept -{ return ChannelsFromDevFmt(chans, ambiorder) * BytesFromDevFmt(type); } - -enum class DevAmbiLayout : bool { - FuMa, - ACN, - - Default = ACN -}; - -enum class DevAmbiScaling : unsigned char { - FuMa, - SN3D, - N3D, - - Default = SN3D -}; - -#endif /* ALC_DEVFORMAT_H */ diff --git a/alc/front_stablizer.h b/alc/front_stablizer.h index 5e7c267f..066a70af 100644 --- a/alc/front_stablizer.h +++ b/alc/front_stablizer.h @@ -5,7 +5,7 @@ #include #include "almalloc.h" -#include "bufferline.h" +#include "core/bufferline.h" #include "filters/splitter.h" diff --git a/alc/hrtf.h b/alc/hrtf.h index 64f0e994..9bbe36b7 100644 --- a/alc/hrtf.h +++ b/alc/hrtf.h @@ -10,7 +10,7 @@ #include "alspan.h" #include "ambidefs.h" #include "atomic.h" -#include "bufferline.h" +#include "core/bufferline.h" #include "filters/splitter.h" #include "intrusive_ptr.h" #include "vector.h" diff --git a/alc/panning.cpp b/alc/panning.cpp index adc7db30..86a71a24 100644 --- a/alc/panning.cpp +++ b/alc/panning.cpp @@ -51,7 +51,7 @@ #include "ambidefs.h" #include "bformatdec.h" #include "bs2b.h" -#include "devformat.h" +#include "core/devformat.h" #include "front_stablizer.h" #include "hrtf.h" #include "logging.h" diff --git a/alc/voice.cpp b/alc/voice.cpp index 4fe84234..174e8545 100644 --- a/alc/voice.cpp +++ b/alc/voice.cpp @@ -46,8 +46,8 @@ #include "alspan.h" #include "alstring.h" #include "alu.h" +#include "core/devformat.h" #include "cpu_caps.h" -#include "devformat.h" #include "filters/biquad.h" #include "filters/nfc.h" #include "filters/splitter.h" diff --git a/alc/voice.h b/alc/voice.h index 5900d849..5ae7a945 100644 --- a/alc/voice.h +++ b/alc/voice.h @@ -7,7 +7,7 @@ #include "alspan.h" #include "alu.h" #include "buffer_storage.h" -#include "devformat.h" +#include "core/devformat.h" #include "filters/biquad.h" #include "filters/nfc.h" #include "filters/splitter.h" diff --git a/core/bufferline.h b/core/bufferline.h new file mode 100644 index 00000000..07983140 --- /dev/null +++ b/core/bufferline.h @@ -0,0 +1,14 @@ +#ifndef CORE_BUFFERLINE_H +#define CORE_BUFFERLINE_H + +#include + +/* Size for temporary storage of buffer data, in floats. Larger values need + * more memory and are harder on cache, while smaller values may need more + * iterations for mixing. + */ +#define BUFFERSIZE 1024 + +using FloatBufferLine = std::array; + +#endif /* CORE_BUFFERLINE_H */ diff --git a/core/devformat.cpp b/core/devformat.cpp new file mode 100644 index 00000000..d13ef3c6 --- /dev/null +++ b/core/devformat.cpp @@ -0,0 +1,65 @@ + +#include "config.h" + +#include "devformat.h" + + +uint BytesFromDevFmt(DevFmtType type) noexcept +{ + switch(type) + { + case DevFmtByte: return sizeof(int8_t); + case DevFmtUByte: return sizeof(uint8_t); + case DevFmtShort: return sizeof(int16_t); + case DevFmtUShort: return sizeof(uint16_t); + case DevFmtInt: return sizeof(int32_t); + case DevFmtUInt: return sizeof(uint32_t); + case DevFmtFloat: return sizeof(float); + } + return 0; +} +uint ChannelsFromDevFmt(DevFmtChannels chans, uint ambiorder) noexcept +{ + switch(chans) + { + case DevFmtMono: return 1; + case DevFmtStereo: return 2; + case DevFmtQuad: return 4; + case DevFmtX51: return 6; + case DevFmtX51Rear: return 6; + case DevFmtX61: return 7; + case DevFmtX71: return 8; + case DevFmtAmbi3D: return (ambiorder+1) * (ambiorder+1); + } + return 0; +} + +const char *DevFmtTypeString(DevFmtType type) noexcept +{ + switch(type) + { + case DevFmtByte: return "Int8"; + case DevFmtUByte: return "UInt8"; + case DevFmtShort: return "Int16"; + case DevFmtUShort: return "UInt16"; + case DevFmtInt: return "Int32"; + case DevFmtUInt: return "UInt32"; + case DevFmtFloat: return "Float32"; + } + return "(unknown type)"; +} +const char *DevFmtChannelsString(DevFmtChannels chans) noexcept +{ + switch(chans) + { + case DevFmtMono: return "Mono"; + case DevFmtStereo: return "Stereo"; + case DevFmtQuad: return "Quadraphonic"; + case DevFmtX51: return "5.1 Surround"; + case DevFmtX51Rear: return "5.1 Surround (Rear)"; + case DevFmtX61: return "6.1 Surround"; + case DevFmtX71: return "7.1 Surround"; + case DevFmtAmbi3D: return "Ambisonic 3D"; + } + return "(unknown channels)"; +} diff --git a/core/devformat.h b/core/devformat.h new file mode 100644 index 00000000..bcd42539 --- /dev/null +++ b/core/devformat.h @@ -0,0 +1,103 @@ +#ifndef CORE_DEVFORMAT_H +#define CORE_DEVFORMAT_H + +#include + + +using uint = unsigned int; + +enum Channel { + FrontLeft = 0, + FrontRight, + FrontCenter, + LFE, + BackLeft, + BackRight, + BackCenter, + SideLeft, + SideRight, + + TopFrontLeft, + TopFrontCenter, + TopFrontRight, + TopCenter, + TopBackLeft, + TopBackCenter, + TopBackRight, + + MaxChannels +}; + + +/* Device formats */ +enum DevFmtType : unsigned char { + DevFmtByte, + DevFmtUByte, + DevFmtShort, + DevFmtUShort, + DevFmtInt, + DevFmtUInt, + DevFmtFloat, + + DevFmtTypeDefault = DevFmtFloat +}; +enum DevFmtChannels : unsigned char { + DevFmtMono, + DevFmtStereo, + DevFmtQuad, + DevFmtX51, + DevFmtX61, + DevFmtX71, + DevFmtAmbi3D, + + /* Similar to 5.1, except using rear channels instead of sides */ + DevFmtX51Rear, + + DevFmtChannelsDefault = DevFmtStereo +}; +#define MAX_OUTPUT_CHANNELS 16 + +/* DevFmtType traits, providing the type, etc given a DevFmtType. */ +template +struct DevFmtTypeTraits { }; + +template<> +struct DevFmtTypeTraits { using Type = int8_t; }; +template<> +struct DevFmtTypeTraits { using Type = uint8_t; }; +template<> +struct DevFmtTypeTraits { using Type = int16_t; }; +template<> +struct DevFmtTypeTraits { using Type = uint16_t; }; +template<> +struct DevFmtTypeTraits { using Type = int32_t; }; +template<> +struct DevFmtTypeTraits { using Type = uint32_t; }; +template<> +struct DevFmtTypeTraits { using Type = float; }; + + +uint BytesFromDevFmt(DevFmtType type) noexcept; +uint ChannelsFromDevFmt(DevFmtChannels chans, uint ambiorder) noexcept; +inline uint FrameSizeFromDevFmt(DevFmtChannels chans, DevFmtType type, uint ambiorder) noexcept +{ return ChannelsFromDevFmt(chans, ambiorder) * BytesFromDevFmt(type); } + +const char *DevFmtTypeString(DevFmtType type) noexcept; +const char *DevFmtChannelsString(DevFmtChannels chans) noexcept; + +enum class DevAmbiLayout : bool { + FuMa, + ACN, + + Default = ACN +}; + +enum class DevAmbiScaling : unsigned char { + FuMa, + SN3D, + N3D, + + Default = SN3D +}; + +#endif /* CORE_DEVFORMAT_H */ -- cgit v1.2.3 From 36c1589c11cb8f197576e748e06bc5fbb6dcc8bf Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 3 Dec 2020 23:37:50 -0800 Subject: Move mastering.cpp/h to core --- CMakeLists.txt | 4 +- alc/alc.cpp | 2 +- alc/alu.cpp | 2 +- alc/mastering.cpp | 441 ----------------------------------------------------- alc/mastering.h | 104 ------------- core/mastering.cpp | 441 +++++++++++++++++++++++++++++++++++++++++++++++++++++ core/mastering.h | 104 +++++++++++++ 7 files changed, 549 insertions(+), 549 deletions(-) delete mode 100644 alc/mastering.cpp delete mode 100644 alc/mastering.h create mode 100644 core/mastering.cpp create mode 100644 core/mastering.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 31ef66a9..7b4c044e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -630,6 +630,8 @@ set(ALC_OBJS core/bufferline.h core/devformat.cpp core/devformat.h + core/mastering.cpp + core/mastering.h alc/alc.cpp alc/alcmain.h @@ -688,8 +690,6 @@ set(ALC_OBJS alc/hrtf.h alc/inprogext.h alc/logging.h - alc/mastering.cpp - alc/mastering.h alc/panning.cpp alc/ringbuffer.cpp alc/ringbuffer.h diff --git a/alc/alc.cpp b/alc/alc.cpp index 7cf6c5a9..faf6392e 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -81,6 +81,7 @@ #include "bs2b.h" #include "compat.h" #include "core/devformat.h" +#include "core/mastering.h" #include "cpu_caps.h" #include "effects/base.h" #include "filters/nfc.h" @@ -91,7 +92,6 @@ #include "inprogext.h" #include "intrusive_ptr.h" #include "logging.h" -#include "mastering.h" #include "opthelpers.h" #include "pragmadefs.h" #include "ringbuffer.h" diff --git a/alc/alu.cpp b/alc/alu.cpp index eb1fb291..5c532a05 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -60,6 +60,7 @@ #include "bformatdec.h" #include "bs2b.h" #include "core/devformat.h" +#include "core/mastering.h" #include "cpu_caps.h" #include "effects/base.h" #include "filters/biquad.h" @@ -69,7 +70,6 @@ #include "front_stablizer.h" #include "hrtf.h" #include "inprogext.h" -#include "mastering.h" #include "math_defs.h" #include "mixer/defs.h" #include "opthelpers.h" diff --git a/alc/mastering.cpp b/alc/mastering.cpp deleted file mode 100644 index 58f7ffa3..00000000 --- a/alc/mastering.cpp +++ /dev/null @@ -1,441 +0,0 @@ - -#include "config.h" - -#include "mastering.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "almalloc.h" -#include "alnumeric.h" -#include "alu.h" -#include "opthelpers.h" - - -/* These structures assume BufferLineSize is a power of 2. */ -static_assert((BufferLineSize & (BufferLineSize-1)) == 0, "BufferLineSize is not a power of 2"); - -struct SlidingHold { - alignas(16) float mValues[BufferLineSize]; - uint mExpiries[BufferLineSize]; - uint mLowerIndex; - uint mUpperIndex; - uint mLength; -}; - - -namespace { - -using namespace std::placeholders; - -/* This sliding hold follows the input level with an instant attack and a - * fixed duration hold before an instant release to the next highest level. - * It is a sliding window maximum (descending maxima) implementation based on - * Richard Harter's ascending minima algorithm available at: - * - * http://www.richardhartersworld.com/cri/2001/slidingmin.html - */ -float UpdateSlidingHold(SlidingHold *Hold, const uint i, const float in) -{ - static constexpr uint mask{BufferLineSize - 1}; - const uint length{Hold->mLength}; - float (&values)[BufferLineSize] = Hold->mValues; - uint (&expiries)[BufferLineSize] = Hold->mExpiries; - uint lowerIndex{Hold->mLowerIndex}; - uint upperIndex{Hold->mUpperIndex}; - - if(i >= expiries[upperIndex]) - upperIndex = (upperIndex + 1) & mask; - - if(in >= values[upperIndex]) - { - values[upperIndex] = in; - expiries[upperIndex] = i + length; - lowerIndex = upperIndex; - } - else - { - do { - do { - if(!(in >= values[lowerIndex])) - goto found_place; - } while(lowerIndex--); - lowerIndex = mask; - } while(1); - found_place: - - lowerIndex = (lowerIndex + 1) & mask; - values[lowerIndex] = in; - expiries[lowerIndex] = i + length; - } - - Hold->mLowerIndex = lowerIndex; - Hold->mUpperIndex = upperIndex; - - return values[upperIndex]; -} - -void ShiftSlidingHold(SlidingHold *Hold, const uint n) -{ - auto exp_begin = std::begin(Hold->mExpiries) + Hold->mUpperIndex; - auto exp_last = std::begin(Hold->mExpiries) + Hold->mLowerIndex; - if(exp_last-exp_begin < 0) - { - std::transform(exp_begin, std::end(Hold->mExpiries), exp_begin, - std::bind(std::minus<>{}, _1, n)); - exp_begin = std::begin(Hold->mExpiries); - } - std::transform(exp_begin, exp_last+1, exp_begin, std::bind(std::minus<>{}, _1, n)); -} - - -/* Multichannel compression is linked via the absolute maximum of all - * channels. - */ -void LinkChannels(Compressor *Comp, const uint SamplesToDo, const FloatBufferLine *OutBuffer) -{ - const size_t numChans{Comp->mNumChans}; - - ASSUME(SamplesToDo > 0); - ASSUME(numChans > 0); - - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; - std::fill(side_begin, side_begin+SamplesToDo, 0.0f); - - auto fill_max = [SamplesToDo,side_begin](const FloatBufferLine &input) -> void - { - const float *RESTRICT buffer{al::assume_aligned<16>(input.data())}; - auto max_abs = std::bind(maxf, _1, std::bind(static_cast(std::fabs), _2)); - std::transform(side_begin, side_begin+SamplesToDo, buffer, side_begin, max_abs); - }; - std::for_each(OutBuffer, OutBuffer+numChans, fill_max); -} - -/* This calculates the squared crest factor of the control signal for the - * basic automation of the attack/release times. As suggested by the paper, - * it uses an instantaneous squared peak detector and a squared RMS detector - * both with 200ms release times. - */ -static void CrestDetector(Compressor *Comp, const uint SamplesToDo) -{ - const float a_crest{Comp->mCrestCoeff}; - float y2_peak{Comp->mLastPeakSq}; - float y2_rms{Comp->mLastRmsSq}; - - ASSUME(SamplesToDo > 0); - - auto calc_crest = [&y2_rms,&y2_peak,a_crest](const float x_abs) noexcept -> float - { - const float x2{clampf(x_abs * x_abs, 0.000001f, 1000000.0f)}; - - y2_peak = maxf(x2, lerp(x2, y2_peak, a_crest)); - y2_rms = lerp(x2, y2_rms, a_crest); - return y2_peak / y2_rms; - }; - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; - std::transform(side_begin, side_begin+SamplesToDo, std::begin(Comp->mCrestFactor), calc_crest); - - Comp->mLastPeakSq = y2_peak; - Comp->mLastRmsSq = y2_rms; -} - -/* The side-chain starts with a simple peak detector (based on the absolute - * value of the incoming signal) and performs most of its operations in the - * log domain. - */ -void PeakDetector(Compressor *Comp, const uint SamplesToDo) -{ - ASSUME(SamplesToDo > 0); - - /* Clamp the minimum amplitude to near-zero and convert to logarithm. */ - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; - std::transform(side_begin, side_begin+SamplesToDo, side_begin, - [](const float s) -> float { return std::log(maxf(0.000001f, s)); }); -} - -/* An optional hold can be used to extend the peak detector so it can more - * solidly detect fast transients. This is best used when operating as a - * limiter. - */ -void PeakHoldDetector(Compressor *Comp, const uint SamplesToDo) -{ - ASSUME(SamplesToDo > 0); - - SlidingHold *hold{Comp->mHold}; - uint i{0}; - auto detect_peak = [&i,hold](const float x_abs) -> float - { - const float x_G{std::log(maxf(0.000001f, x_abs))}; - return UpdateSlidingHold(hold, i++, x_G); - }; - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; - std::transform(side_begin, side_begin+SamplesToDo, side_begin, detect_peak); - - ShiftSlidingHold(hold, SamplesToDo); -} - -/* This is the heart of the feed-forward compressor. It operates in the log - * domain (to better match human hearing) and can apply some basic automation - * to knee width, attack/release times, make-up/post gain, and clipping - * reduction. - */ -void GainCompressor(Compressor *Comp, const uint SamplesToDo) -{ - const bool autoKnee{Comp->mAuto.Knee}; - const bool autoAttack{Comp->mAuto.Attack}; - const bool autoRelease{Comp->mAuto.Release}; - const bool autoPostGain{Comp->mAuto.PostGain}; - const bool autoDeclip{Comp->mAuto.Declip}; - const uint lookAhead{Comp->mLookAhead}; - const float threshold{Comp->mThreshold}; - const float slope{Comp->mSlope}; - const float attack{Comp->mAttack}; - const float release{Comp->mRelease}; - const float c_est{Comp->mGainEstimate}; - const float a_adp{Comp->mAdaptCoeff}; - const float *crestFactor{Comp->mCrestFactor}; - float postGain{Comp->mPostGain}; - float knee{Comp->mKnee}; - float t_att{attack}; - float t_rel{release - attack}; - float a_att{std::exp(-1.0f / t_att)}; - float a_rel{std::exp(-1.0f / t_rel)}; - float y_1{Comp->mLastRelease}; - float y_L{Comp->mLastAttack}; - float c_dev{Comp->mLastGainDev}; - - ASSUME(SamplesToDo > 0); - - for(float &sideChain : al::span{Comp->mSideChain, SamplesToDo}) - { - if(autoKnee) - knee = maxf(0.0f, 2.5f * (c_dev + c_est)); - const float knee_h{0.5f * knee}; - - /* This is the gain computer. It applies a static compression curve - * to the control signal. - */ - const float x_over{std::addressof(sideChain)[lookAhead] - threshold}; - const float y_G{ - (x_over <= -knee_h) ? 0.0f : - (std::fabs(x_over) < knee_h) ? (x_over + knee_h) * (x_over + knee_h) / (2.0f * knee) : - x_over}; - - const float y2_crest{*(crestFactor++)}; - if(autoAttack) - { - t_att = 2.0f*attack/y2_crest; - a_att = std::exp(-1.0f / t_att); - } - if(autoRelease) - { - t_rel = 2.0f*release/y2_crest - t_att; - a_rel = std::exp(-1.0f / t_rel); - } - - /* Gain smoothing (ballistics) is done via a smooth decoupled peak - * detector. The attack time is subtracted from the release time - * above to compensate for the chained operating mode. - */ - const float x_L{-slope * y_G}; - y_1 = maxf(x_L, lerp(x_L, y_1, a_rel)); - y_L = lerp(y_1, y_L, a_att); - - /* Knee width and make-up gain automation make use of a smoothed - * measurement of deviation between the control signal and estimate. - * The estimate is also used to bias the measurement to hot-start its - * average. - */ - c_dev = lerp(-(y_L+c_est), c_dev, a_adp); - - if(autoPostGain) - { - /* Clipping reduction is only viable when make-up gain is being - * automated. It modifies the deviation to further attenuate the - * control signal when clipping is detected. The adaptation time - * is sufficiently long enough to suppress further clipping at the - * same output level. - */ - if(autoDeclip) - c_dev = maxf(c_dev, sideChain - y_L - threshold - c_est); - - postGain = -(c_dev + c_est); - } - - sideChain = std::exp(postGain - y_L); - } - - Comp->mLastRelease = y_1; - Comp->mLastAttack = y_L; - Comp->mLastGainDev = c_dev; -} - -/* Combined with the hold time, a look-ahead delay can improve handling of - * fast transients by allowing the envelope time to converge prior to - * reaching the offending impulse. This is best used when operating as a - * limiter. - */ -void SignalDelay(Compressor *Comp, const uint SamplesToDo, FloatBufferLine *OutBuffer) -{ - const size_t numChans{Comp->mNumChans}; - const uint lookAhead{Comp->mLookAhead}; - - ASSUME(SamplesToDo > 0); - ASSUME(numChans > 0); - ASSUME(lookAhead > 0); - - for(size_t c{0};c < numChans;c++) - { - float *inout{al::assume_aligned<16>(OutBuffer[c].data())}; - float *delaybuf{al::assume_aligned<16>(Comp->mDelay[c].data())}; - - auto inout_end = inout + SamplesToDo; - if LIKELY(SamplesToDo >= lookAhead) - { - auto delay_end = std::rotate(inout, inout_end - lookAhead, inout_end); - std::swap_ranges(inout, delay_end, delaybuf); - } - else - { - auto delay_start = std::swap_ranges(inout, inout_end, delaybuf); - std::rotate(delaybuf, delay_start, delaybuf + lookAhead); - } - } -} - -} // namespace - - -std::unique_ptr Compressor::Create(const size_t NumChans, const float SampleRate, - const bool AutoKnee, const bool AutoAttack, const bool AutoRelease, const bool AutoPostGain, - const bool AutoDeclip, const float LookAheadTime, const float HoldTime, const float PreGainDb, - const float PostGainDb, const float ThresholdDb, const float Ratio, const float KneeDb, - const float AttackTime, const float ReleaseTime) -{ - const auto lookAhead = static_cast( - clampf(std::round(LookAheadTime*SampleRate), 0.0f, BufferLineSize-1)); - const auto hold = static_cast( - clampf(std::round(HoldTime*SampleRate), 0.0f, BufferLineSize-1)); - - size_t size{sizeof(Compressor)}; - if(lookAhead > 0) - { - size += sizeof(*Compressor::mDelay) * NumChans; - /* The sliding hold implementation doesn't handle a length of 1. A 1- - * sample hold is useless anyway, it would only ever give back what was - * just given to it. - */ - if(hold > 1) - size += sizeof(*Compressor::mHold); - } - - auto Comp = std::unique_ptr{new (al_calloc(16, size)) Compressor{}}; - Comp->mNumChans = NumChans; - Comp->mAuto.Knee = AutoKnee; - Comp->mAuto.Attack = AutoAttack; - Comp->mAuto.Release = AutoRelease; - Comp->mAuto.PostGain = AutoPostGain; - Comp->mAuto.Declip = AutoPostGain && AutoDeclip; - Comp->mLookAhead = lookAhead; - Comp->mPreGain = std::pow(10.0f, PreGainDb / 20.0f); - Comp->mPostGain = PostGainDb * std::log(10.0f) / 20.0f; - Comp->mThreshold = ThresholdDb * std::log(10.0f) / 20.0f; - Comp->mSlope = 1.0f / maxf(1.0f, Ratio) - 1.0f; - Comp->mKnee = maxf(0.0f, KneeDb * std::log(10.0f) / 20.0f); - Comp->mAttack = maxf(1.0f, AttackTime * SampleRate); - Comp->mRelease = maxf(1.0f, ReleaseTime * SampleRate); - - /* Knee width automation actually treats the compressor as a limiter. By - * varying the knee width, it can effectively be seen as applying - * compression over a wide range of ratios. - */ - if(AutoKnee) - Comp->mSlope = -1.0f; - - if(lookAhead > 0) - { - if(hold > 1) - { - Comp->mHold = ::new (static_cast(Comp.get() + 1)) SlidingHold{}; - Comp->mHold->mValues[0] = -std::numeric_limits::infinity(); - Comp->mHold->mExpiries[0] = hold; - Comp->mHold->mLength = hold; - Comp->mDelay = ::new(static_cast(Comp->mHold + 1)) FloatBufferLine[NumChans]; - } - else - { - Comp->mDelay = ::new(static_cast(Comp.get() + 1)) FloatBufferLine[NumChans]; - } - std::fill_n(Comp->mDelay, NumChans, FloatBufferLine{}); - } - - Comp->mCrestCoeff = std::exp(-1.0f / (0.200f * SampleRate)); // 200ms - Comp->mGainEstimate = Comp->mThreshold * -0.5f * Comp->mSlope; - Comp->mAdaptCoeff = std::exp(-1.0f / (2.0f * SampleRate)); // 2s - - return Comp; -} - -Compressor::~Compressor() -{ - if(mHold) - al::destroy_at(mHold); - mHold = nullptr; - if(mDelay) - al::destroy_n(mDelay, mNumChans); - mDelay = nullptr; -} - - -void Compressor::process(const uint SamplesToDo, FloatBufferLine *OutBuffer) -{ - const size_t numChans{mNumChans}; - - ASSUME(SamplesToDo > 0); - ASSUME(numChans > 0); - - const float preGain{mPreGain}; - if(preGain != 1.0f) - { - auto apply_gain = [SamplesToDo,preGain](FloatBufferLine &input) noexcept -> void - { - float *buffer{al::assume_aligned<16>(input.data())}; - std::transform(buffer, buffer+SamplesToDo, buffer, - std::bind(std::multiplies{}, _1, preGain)); - }; - std::for_each(OutBuffer, OutBuffer+numChans, apply_gain); - } - - LinkChannels(this, SamplesToDo, OutBuffer); - - if(mAuto.Attack || mAuto.Release) - CrestDetector(this, SamplesToDo); - - if(mHold) - PeakHoldDetector(this, SamplesToDo); - else - PeakDetector(this, SamplesToDo); - - GainCompressor(this, SamplesToDo); - - if(mDelay) - SignalDelay(this, SamplesToDo, OutBuffer); - - const float (&sideChain)[BufferLineSize*2] = mSideChain; - auto apply_comp = [SamplesToDo,&sideChain](FloatBufferLine &input) noexcept -> void - { - float *buffer{al::assume_aligned<16>(input.data())}; - const float *gains{al::assume_aligned<16>(&sideChain[0])}; - std::transform(gains, gains+SamplesToDo, buffer, buffer, - std::bind(std::multiplies{}, _1, _2)); - }; - std::for_each(OutBuffer, OutBuffer+numChans, apply_comp); - - auto side_begin = std::begin(mSideChain) + SamplesToDo; - std::copy(side_begin, side_begin+mLookAhead, std::begin(mSideChain)); -} diff --git a/alc/mastering.h b/alc/mastering.h deleted file mode 100644 index 0a98fe1f..00000000 --- a/alc/mastering.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef MASTERING_H -#define MASTERING_H - -#include - -#include "almalloc.h" -#include "core/bufferline.h" - -struct SlidingHold; - -using uint = unsigned int; - - -/* General topology and basic automation was based on the following paper: - * - * D. Giannoulis, M. Massberg and J. D. Reiss, - * "Parameter Automation in a Dynamic Range Compressor," - * Journal of the Audio Engineering Society, v61 (10), Oct. 2013 - * - * Available (along with supplemental reading) at: - * - * http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/ - */ -struct Compressor { - size_t mNumChans{0u}; - - struct { - bool Knee : 1; - bool Attack : 1; - bool Release : 1; - bool PostGain : 1; - bool Declip : 1; - } mAuto{}; - - uint mLookAhead{0}; - - float mPreGain{0.0f}; - float mPostGain{0.0f}; - - float mThreshold{0.0f}; - float mSlope{0.0f}; - float mKnee{0.0f}; - - float mAttack{0.0f}; - float mRelease{0.0f}; - - alignas(16) float mSideChain[2*BufferLineSize]{}; - alignas(16) float mCrestFactor[BufferLineSize]{}; - - SlidingHold *mHold{nullptr}; - FloatBufferLine *mDelay{nullptr}; - - float mCrestCoeff{0.0f}; - float mGainEstimate{0.0f}; - float mAdaptCoeff{0.0f}; - - float mLastPeakSq{0.0f}; - float mLastRmsSq{0.0f}; - float mLastRelease{0.0f}; - float mLastAttack{0.0f}; - float mLastGainDev{0.0f}; - - - ~Compressor(); - void process(const uint SamplesToDo, FloatBufferLine *OutBuffer); - int getLookAhead() const noexcept { return static_cast(mLookAhead); } - - DEF_PLACE_NEWDEL() - - /** - * The compressor is initialized with the following settings: - * - * \param NumChans Number of channels to process. - * \param SampleRate Sample rate to process. - * \param AutoKnee Whether to automate the knee width parameter. - * \param AutoAttack Whether to automate the attack time parameter. - * \param AutoRelease Whether to automate the release time parameter. - * \param AutoPostGain Whether to automate the make-up (post) gain - * parameter. - * \param AutoDeclip Whether to automate clipping reduction. Ignored - * when not automating make-up gain. - * \param LookAheadTime Look-ahead time (in seconds). - * \param HoldTime Peak hold-time (in seconds). - * \param PreGainDb Gain applied before detection (in dB). - * \param PostGainDb Make-up gain applied after compression (in dB). - * \param ThresholdDb Triggering threshold (in dB). - * \param Ratio Compression ratio (x:1). Set to INFINIFTY for true - * limiting. Ignored when automating knee width. - * \param KneeDb Knee width (in dB). Ignored when automating knee - * width. - * \param AttackTime Attack time (in seconds). Acts as a maximum when - * automating attack time. - * \param ReleaseTime Release time (in seconds). Acts as a maximum when - * automating release time. - */ - static std::unique_ptr Create(const size_t NumChans, const float SampleRate, - const bool AutoKnee, const bool AutoAttack, const bool AutoRelease, - const bool AutoPostGain, const bool AutoDeclip, const float LookAheadTime, - const float HoldTime, const float PreGainDb, const float PostGainDb, - const float ThresholdDb, const float Ratio, const float KneeDb, const float AttackTime, - const float ReleaseTime); -}; - -#endif /* MASTERING_H */ diff --git a/core/mastering.cpp b/core/mastering.cpp new file mode 100644 index 00000000..e0cb2ca7 --- /dev/null +++ b/core/mastering.cpp @@ -0,0 +1,441 @@ + +#include "config.h" + +#include "mastering.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "almalloc.h" +#include "alnumeric.h" +#include "alspan.h" +#include "opthelpers.h" + + +/* These structures assume BufferLineSize is a power of 2. */ +static_assert((BufferLineSize & (BufferLineSize-1)) == 0, "BufferLineSize is not a power of 2"); + +struct SlidingHold { + alignas(16) float mValues[BufferLineSize]; + uint mExpiries[BufferLineSize]; + uint mLowerIndex; + uint mUpperIndex; + uint mLength; +}; + + +namespace { + +using namespace std::placeholders; + +/* This sliding hold follows the input level with an instant attack and a + * fixed duration hold before an instant release to the next highest level. + * It is a sliding window maximum (descending maxima) implementation based on + * Richard Harter's ascending minima algorithm available at: + * + * http://www.richardhartersworld.com/cri/2001/slidingmin.html + */ +float UpdateSlidingHold(SlidingHold *Hold, const uint i, const float in) +{ + static constexpr uint mask{BufferLineSize - 1}; + const uint length{Hold->mLength}; + float (&values)[BufferLineSize] = Hold->mValues; + uint (&expiries)[BufferLineSize] = Hold->mExpiries; + uint lowerIndex{Hold->mLowerIndex}; + uint upperIndex{Hold->mUpperIndex}; + + if(i >= expiries[upperIndex]) + upperIndex = (upperIndex + 1) & mask; + + if(in >= values[upperIndex]) + { + values[upperIndex] = in; + expiries[upperIndex] = i + length; + lowerIndex = upperIndex; + } + else + { + do { + do { + if(!(in >= values[lowerIndex])) + goto found_place; + } while(lowerIndex--); + lowerIndex = mask; + } while(1); + found_place: + + lowerIndex = (lowerIndex + 1) & mask; + values[lowerIndex] = in; + expiries[lowerIndex] = i + length; + } + + Hold->mLowerIndex = lowerIndex; + Hold->mUpperIndex = upperIndex; + + return values[upperIndex]; +} + +void ShiftSlidingHold(SlidingHold *Hold, const uint n) +{ + auto exp_begin = std::begin(Hold->mExpiries) + Hold->mUpperIndex; + auto exp_last = std::begin(Hold->mExpiries) + Hold->mLowerIndex; + if(exp_last-exp_begin < 0) + { + std::transform(exp_begin, std::end(Hold->mExpiries), exp_begin, + std::bind(std::minus<>{}, _1, n)); + exp_begin = std::begin(Hold->mExpiries); + } + std::transform(exp_begin, exp_last+1, exp_begin, std::bind(std::minus<>{}, _1, n)); +} + + +/* Multichannel compression is linked via the absolute maximum of all + * channels. + */ +void LinkChannels(Compressor *Comp, const uint SamplesToDo, const FloatBufferLine *OutBuffer) +{ + const size_t numChans{Comp->mNumChans}; + + ASSUME(SamplesToDo > 0); + ASSUME(numChans > 0); + + auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; + std::fill(side_begin, side_begin+SamplesToDo, 0.0f); + + auto fill_max = [SamplesToDo,side_begin](const FloatBufferLine &input) -> void + { + const float *RESTRICT buffer{al::assume_aligned<16>(input.data())}; + auto max_abs = std::bind(maxf, _1, std::bind(static_cast(std::fabs), _2)); + std::transform(side_begin, side_begin+SamplesToDo, buffer, side_begin, max_abs); + }; + std::for_each(OutBuffer, OutBuffer+numChans, fill_max); +} + +/* This calculates the squared crest factor of the control signal for the + * basic automation of the attack/release times. As suggested by the paper, + * it uses an instantaneous squared peak detector and a squared RMS detector + * both with 200ms release times. + */ +static void CrestDetector(Compressor *Comp, const uint SamplesToDo) +{ + const float a_crest{Comp->mCrestCoeff}; + float y2_peak{Comp->mLastPeakSq}; + float y2_rms{Comp->mLastRmsSq}; + + ASSUME(SamplesToDo > 0); + + auto calc_crest = [&y2_rms,&y2_peak,a_crest](const float x_abs) noexcept -> float + { + const float x2{clampf(x_abs * x_abs, 0.000001f, 1000000.0f)}; + + y2_peak = maxf(x2, lerp(x2, y2_peak, a_crest)); + y2_rms = lerp(x2, y2_rms, a_crest); + return y2_peak / y2_rms; + }; + auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; + std::transform(side_begin, side_begin+SamplesToDo, std::begin(Comp->mCrestFactor), calc_crest); + + Comp->mLastPeakSq = y2_peak; + Comp->mLastRmsSq = y2_rms; +} + +/* The side-chain starts with a simple peak detector (based on the absolute + * value of the incoming signal) and performs most of its operations in the + * log domain. + */ +void PeakDetector(Compressor *Comp, const uint SamplesToDo) +{ + ASSUME(SamplesToDo > 0); + + /* Clamp the minimum amplitude to near-zero and convert to logarithm. */ + auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; + std::transform(side_begin, side_begin+SamplesToDo, side_begin, + [](const float s) -> float { return std::log(maxf(0.000001f, s)); }); +} + +/* An optional hold can be used to extend the peak detector so it can more + * solidly detect fast transients. This is best used when operating as a + * limiter. + */ +void PeakHoldDetector(Compressor *Comp, const uint SamplesToDo) +{ + ASSUME(SamplesToDo > 0); + + SlidingHold *hold{Comp->mHold}; + uint i{0}; + auto detect_peak = [&i,hold](const float x_abs) -> float + { + const float x_G{std::log(maxf(0.000001f, x_abs))}; + return UpdateSlidingHold(hold, i++, x_G); + }; + auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; + std::transform(side_begin, side_begin+SamplesToDo, side_begin, detect_peak); + + ShiftSlidingHold(hold, SamplesToDo); +} + +/* This is the heart of the feed-forward compressor. It operates in the log + * domain (to better match human hearing) and can apply some basic automation + * to knee width, attack/release times, make-up/post gain, and clipping + * reduction. + */ +void GainCompressor(Compressor *Comp, const uint SamplesToDo) +{ + const bool autoKnee{Comp->mAuto.Knee}; + const bool autoAttack{Comp->mAuto.Attack}; + const bool autoRelease{Comp->mAuto.Release}; + const bool autoPostGain{Comp->mAuto.PostGain}; + const bool autoDeclip{Comp->mAuto.Declip}; + const uint lookAhead{Comp->mLookAhead}; + const float threshold{Comp->mThreshold}; + const float slope{Comp->mSlope}; + const float attack{Comp->mAttack}; + const float release{Comp->mRelease}; + const float c_est{Comp->mGainEstimate}; + const float a_adp{Comp->mAdaptCoeff}; + const float *crestFactor{Comp->mCrestFactor}; + float postGain{Comp->mPostGain}; + float knee{Comp->mKnee}; + float t_att{attack}; + float t_rel{release - attack}; + float a_att{std::exp(-1.0f / t_att)}; + float a_rel{std::exp(-1.0f / t_rel)}; + float y_1{Comp->mLastRelease}; + float y_L{Comp->mLastAttack}; + float c_dev{Comp->mLastGainDev}; + + ASSUME(SamplesToDo > 0); + + for(float &sideChain : al::span{Comp->mSideChain, SamplesToDo}) + { + if(autoKnee) + knee = maxf(0.0f, 2.5f * (c_dev + c_est)); + const float knee_h{0.5f * knee}; + + /* This is the gain computer. It applies a static compression curve + * to the control signal. + */ + const float x_over{std::addressof(sideChain)[lookAhead] - threshold}; + const float y_G{ + (x_over <= -knee_h) ? 0.0f : + (std::fabs(x_over) < knee_h) ? (x_over + knee_h) * (x_over + knee_h) / (2.0f * knee) : + x_over}; + + const float y2_crest{*(crestFactor++)}; + if(autoAttack) + { + t_att = 2.0f*attack/y2_crest; + a_att = std::exp(-1.0f / t_att); + } + if(autoRelease) + { + t_rel = 2.0f*release/y2_crest - t_att; + a_rel = std::exp(-1.0f / t_rel); + } + + /* Gain smoothing (ballistics) is done via a smooth decoupled peak + * detector. The attack time is subtracted from the release time + * above to compensate for the chained operating mode. + */ + const float x_L{-slope * y_G}; + y_1 = maxf(x_L, lerp(x_L, y_1, a_rel)); + y_L = lerp(y_1, y_L, a_att); + + /* Knee width and make-up gain automation make use of a smoothed + * measurement of deviation between the control signal and estimate. + * The estimate is also used to bias the measurement to hot-start its + * average. + */ + c_dev = lerp(-(y_L+c_est), c_dev, a_adp); + + if(autoPostGain) + { + /* Clipping reduction is only viable when make-up gain is being + * automated. It modifies the deviation to further attenuate the + * control signal when clipping is detected. The adaptation time + * is sufficiently long enough to suppress further clipping at the + * same output level. + */ + if(autoDeclip) + c_dev = maxf(c_dev, sideChain - y_L - threshold - c_est); + + postGain = -(c_dev + c_est); + } + + sideChain = std::exp(postGain - y_L); + } + + Comp->mLastRelease = y_1; + Comp->mLastAttack = y_L; + Comp->mLastGainDev = c_dev; +} + +/* Combined with the hold time, a look-ahead delay can improve handling of + * fast transients by allowing the envelope time to converge prior to + * reaching the offending impulse. This is best used when operating as a + * limiter. + */ +void SignalDelay(Compressor *Comp, const uint SamplesToDo, FloatBufferLine *OutBuffer) +{ + const size_t numChans{Comp->mNumChans}; + const uint lookAhead{Comp->mLookAhead}; + + ASSUME(SamplesToDo > 0); + ASSUME(numChans > 0); + ASSUME(lookAhead > 0); + + for(size_t c{0};c < numChans;c++) + { + float *inout{al::assume_aligned<16>(OutBuffer[c].data())}; + float *delaybuf{al::assume_aligned<16>(Comp->mDelay[c].data())}; + + auto inout_end = inout + SamplesToDo; + if LIKELY(SamplesToDo >= lookAhead) + { + auto delay_end = std::rotate(inout, inout_end - lookAhead, inout_end); + std::swap_ranges(inout, delay_end, delaybuf); + } + else + { + auto delay_start = std::swap_ranges(inout, inout_end, delaybuf); + std::rotate(delaybuf, delay_start, delaybuf + lookAhead); + } + } +} + +} // namespace + + +std::unique_ptr Compressor::Create(const size_t NumChans, const float SampleRate, + const bool AutoKnee, const bool AutoAttack, const bool AutoRelease, const bool AutoPostGain, + const bool AutoDeclip, const float LookAheadTime, const float HoldTime, const float PreGainDb, + const float PostGainDb, const float ThresholdDb, const float Ratio, const float KneeDb, + const float AttackTime, const float ReleaseTime) +{ + const auto lookAhead = static_cast( + clampf(std::round(LookAheadTime*SampleRate), 0.0f, BufferLineSize-1)); + const auto hold = static_cast( + clampf(std::round(HoldTime*SampleRate), 0.0f, BufferLineSize-1)); + + size_t size{sizeof(Compressor)}; + if(lookAhead > 0) + { + size += sizeof(*Compressor::mDelay) * NumChans; + /* The sliding hold implementation doesn't handle a length of 1. A 1- + * sample hold is useless anyway, it would only ever give back what was + * just given to it. + */ + if(hold > 1) + size += sizeof(*Compressor::mHold); + } + + auto Comp = std::unique_ptr{new (al_calloc(16, size)) Compressor{}}; + Comp->mNumChans = NumChans; + Comp->mAuto.Knee = AutoKnee; + Comp->mAuto.Attack = AutoAttack; + Comp->mAuto.Release = AutoRelease; + Comp->mAuto.PostGain = AutoPostGain; + Comp->mAuto.Declip = AutoPostGain && AutoDeclip; + Comp->mLookAhead = lookAhead; + Comp->mPreGain = std::pow(10.0f, PreGainDb / 20.0f); + Comp->mPostGain = PostGainDb * std::log(10.0f) / 20.0f; + Comp->mThreshold = ThresholdDb * std::log(10.0f) / 20.0f; + Comp->mSlope = 1.0f / maxf(1.0f, Ratio) - 1.0f; + Comp->mKnee = maxf(0.0f, KneeDb * std::log(10.0f) / 20.0f); + Comp->mAttack = maxf(1.0f, AttackTime * SampleRate); + Comp->mRelease = maxf(1.0f, ReleaseTime * SampleRate); + + /* Knee width automation actually treats the compressor as a limiter. By + * varying the knee width, it can effectively be seen as applying + * compression over a wide range of ratios. + */ + if(AutoKnee) + Comp->mSlope = -1.0f; + + if(lookAhead > 0) + { + if(hold > 1) + { + Comp->mHold = ::new (static_cast(Comp.get() + 1)) SlidingHold{}; + Comp->mHold->mValues[0] = -std::numeric_limits::infinity(); + Comp->mHold->mExpiries[0] = hold; + Comp->mHold->mLength = hold; + Comp->mDelay = ::new(static_cast(Comp->mHold + 1)) FloatBufferLine[NumChans]; + } + else + { + Comp->mDelay = ::new(static_cast(Comp.get() + 1)) FloatBufferLine[NumChans]; + } + std::fill_n(Comp->mDelay, NumChans, FloatBufferLine{}); + } + + Comp->mCrestCoeff = std::exp(-1.0f / (0.200f * SampleRate)); // 200ms + Comp->mGainEstimate = Comp->mThreshold * -0.5f * Comp->mSlope; + Comp->mAdaptCoeff = std::exp(-1.0f / (2.0f * SampleRate)); // 2s + + return Comp; +} + +Compressor::~Compressor() +{ + if(mHold) + al::destroy_at(mHold); + mHold = nullptr; + if(mDelay) + al::destroy_n(mDelay, mNumChans); + mDelay = nullptr; +} + + +void Compressor::process(const uint SamplesToDo, FloatBufferLine *OutBuffer) +{ + const size_t numChans{mNumChans}; + + ASSUME(SamplesToDo > 0); + ASSUME(numChans > 0); + + const float preGain{mPreGain}; + if(preGain != 1.0f) + { + auto apply_gain = [SamplesToDo,preGain](FloatBufferLine &input) noexcept -> void + { + float *buffer{al::assume_aligned<16>(input.data())}; + std::transform(buffer, buffer+SamplesToDo, buffer, + std::bind(std::multiplies{}, _1, preGain)); + }; + std::for_each(OutBuffer, OutBuffer+numChans, apply_gain); + } + + LinkChannels(this, SamplesToDo, OutBuffer); + + if(mAuto.Attack || mAuto.Release) + CrestDetector(this, SamplesToDo); + + if(mHold) + PeakHoldDetector(this, SamplesToDo); + else + PeakDetector(this, SamplesToDo); + + GainCompressor(this, SamplesToDo); + + if(mDelay) + SignalDelay(this, SamplesToDo, OutBuffer); + + const float (&sideChain)[BufferLineSize*2] = mSideChain; + auto apply_comp = [SamplesToDo,&sideChain](FloatBufferLine &input) noexcept -> void + { + float *buffer{al::assume_aligned<16>(input.data())}; + const float *gains{al::assume_aligned<16>(&sideChain[0])}; + std::transform(gains, gains+SamplesToDo, buffer, buffer, + std::bind(std::multiplies{}, _1, _2)); + }; + std::for_each(OutBuffer, OutBuffer+numChans, apply_comp); + + auto side_begin = std::begin(mSideChain) + SamplesToDo; + std::copy(side_begin, side_begin+mLookAhead, std::begin(mSideChain)); +} diff --git a/core/mastering.h b/core/mastering.h new file mode 100644 index 00000000..322d3654 --- /dev/null +++ b/core/mastering.h @@ -0,0 +1,104 @@ +#ifndef CORE_MASTERING_H +#define CORE_MASTERING_H + +#include + +#include "almalloc.h" +#include "bufferline.h" + +struct SlidingHold; + +using uint = unsigned int; + + +/* General topology and basic automation was based on the following paper: + * + * D. Giannoulis, M. Massberg and J. D. Reiss, + * "Parameter Automation in a Dynamic Range Compressor," + * Journal of the Audio Engineering Society, v61 (10), Oct. 2013 + * + * Available (along with supplemental reading) at: + * + * http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/ + */ +struct Compressor { + size_t mNumChans{0u}; + + struct { + bool Knee : 1; + bool Attack : 1; + bool Release : 1; + bool PostGain : 1; + bool Declip : 1; + } mAuto{}; + + uint mLookAhead{0}; + + float mPreGain{0.0f}; + float mPostGain{0.0f}; + + float mThreshold{0.0f}; + float mSlope{0.0f}; + float mKnee{0.0f}; + + float mAttack{0.0f}; + float mRelease{0.0f}; + + alignas(16) float mSideChain[2*BufferLineSize]{}; + alignas(16) float mCrestFactor[BufferLineSize]{}; + + SlidingHold *mHold{nullptr}; + FloatBufferLine *mDelay{nullptr}; + + float mCrestCoeff{0.0f}; + float mGainEstimate{0.0f}; + float mAdaptCoeff{0.0f}; + + float mLastPeakSq{0.0f}; + float mLastRmsSq{0.0f}; + float mLastRelease{0.0f}; + float mLastAttack{0.0f}; + float mLastGainDev{0.0f}; + + + ~Compressor(); + void process(const uint SamplesToDo, FloatBufferLine *OutBuffer); + int getLookAhead() const noexcept { return static_cast(mLookAhead); } + + DEF_PLACE_NEWDEL() + + /** + * The compressor is initialized with the following settings: + * + * \param NumChans Number of channels to process. + * \param SampleRate Sample rate to process. + * \param AutoKnee Whether to automate the knee width parameter. + * \param AutoAttack Whether to automate the attack time parameter. + * \param AutoRelease Whether to automate the release time parameter. + * \param AutoPostGain Whether to automate the make-up (post) gain + * parameter. + * \param AutoDeclip Whether to automate clipping reduction. Ignored + * when not automating make-up gain. + * \param LookAheadTime Look-ahead time (in seconds). + * \param HoldTime Peak hold-time (in seconds). + * \param PreGainDb Gain applied before detection (in dB). + * \param PostGainDb Make-up gain applied after compression (in dB). + * \param ThresholdDb Triggering threshold (in dB). + * \param Ratio Compression ratio (x:1). Set to INFINIFTY for true + * limiting. Ignored when automating knee width. + * \param KneeDb Knee width (in dB). Ignored when automating knee + * width. + * \param AttackTime Attack time (in seconds). Acts as a maximum when + * automating attack time. + * \param ReleaseTime Release time (in seconds). Acts as a maximum when + * automating release time. + */ + static std::unique_ptr Create(const size_t NumChans, const float SampleRate, + const bool AutoKnee, const bool AutoAttack, const bool AutoRelease, + const bool AutoPostGain, const bool AutoDeclip, const float LookAheadTime, + const float HoldTime, const float PreGainDb, const float PostGainDb, + const float ThresholdDb, const float Ratio, const float KneeDb, const float AttackTime, + const float ReleaseTime); +}; + +#endif /* CORE_MASTERING_H */ -- cgit v1.2.3 From 84d47f7d4c2d1355a6eb914dd091b39683f83c15 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Dec 2020 07:35:40 -0800 Subject: Move the bsinc tables to core --- CMakeLists.txt | 6 +- alc/alu.cpp | 3 +- alc/bsinc_defs.h | 16 --- alc/bsinc_tables.cpp | 289 ----------------------------------------------- alc/bsinc_tables.h | 17 --- alc/mixer/mixer_c.cpp | 4 +- alc/mixer/mixer_neon.cpp | 2 +- alc/mixer/mixer_sse.cpp | 2 +- core/bsinc_defs.h | 16 +++ core/bsinc_tables.cpp | 288 ++++++++++++++++++++++++++++++++++++++++++++++ core/bsinc_tables.h | 17 +++ 11 files changed, 329 insertions(+), 331 deletions(-) delete mode 100644 alc/bsinc_defs.h delete mode 100644 alc/bsinc_tables.cpp delete mode 100644 alc/bsinc_tables.h create mode 100644 core/bsinc_defs.h create mode 100644 core/bsinc_tables.cpp create mode 100644 core/bsinc_tables.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b4c044e..97004962 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -627,6 +627,9 @@ set(OPENAL_OBJS al/state.cpp ) set(ALC_OBJS + core/bsinc_defs.h + core/bsinc_tables.cpp + core/bsinc_tables.h core/bufferline.h core/devformat.cpp core/devformat.h @@ -647,9 +650,6 @@ set(ALC_OBJS alc/bformatdec.h alc/bs2b.cpp alc/bs2b.h - alc/bsinc_defs.h - alc/bsinc_tables.cpp - alc/bsinc_tables.h alc/buffer_storage.cpp alc/buffer_storage.h alc/compat.h diff --git a/alc/alu.cpp b/alc/alu.cpp index 5c532a05..18a26a38 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -59,6 +59,7 @@ #include "atomic.h" #include "bformatdec.h" #include "bs2b.h" +#include "core/bsinc_tables.h" #include "core/devformat.h" #include "core/mastering.h" #include "cpu_caps.h" @@ -80,8 +81,6 @@ #include "vecmat.h" #include "voice.h" -#include "bsinc_tables.h" - struct CTag; #ifdef HAVE_SSE struct SSETag; diff --git a/alc/bsinc_defs.h b/alc/bsinc_defs.h deleted file mode 100644 index 179e025d..00000000 --- a/alc/bsinc_defs.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef BSINC_DEFS_H -#define BSINC_DEFS_H - -/* The number of distinct scale and phase intervals within the filter table. */ -constexpr unsigned int BSincScaleBits{4}; -constexpr unsigned int BSincScaleCount{1 << BSincScaleBits}; -constexpr unsigned int BSincPhaseBits{5}; -constexpr unsigned int BSincPhaseCount{1 << BSincPhaseBits}; - -/* The maximum number of sample points for the bsinc filters. The max points - * includes the doubling for downsampling, so the maximum number of base sample - * points is 24, which is 23rd order. - */ -constexpr unsigned int BSincPointsMax{48}; - -#endif /* BSINC_DEFS_H */ diff --git a/alc/bsinc_tables.cpp b/alc/bsinc_tables.cpp deleted file mode 100644 index 893e9792..00000000 --- a/alc/bsinc_tables.cpp +++ /dev/null @@ -1,289 +0,0 @@ - -#include "bsinc_tables.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "math_defs.h" -#include "vector.h" - - -namespace { - -using uint = unsigned int; - - -/* This is the normalized cardinal sine (sinc) function. - * - * sinc(x) = { 1, x = 0 - * { sin(pi x) / (pi x), otherwise. - */ -constexpr double Sinc(const double x) -{ - if(!(x > 1e-15 || x < -1e-15)) - return 1.0; - return std::sin(al::MathDefs::Pi()*x) / (al::MathDefs::Pi()*x); -} - -/* The zero-order modified Bessel function of the first kind, used for the - * Kaiser window. - * - * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k) - * = sum_{k=0}^inf ((x / 2)^k / k!)^2 - */ -constexpr double BesselI_0(const double x) -{ - /* Start at k=1 since k=0 is trivial. */ - const double x2{x / 2.0}; - double term{1.0}; - double sum{1.0}; - double last_sum{}; - int k{1}; - - /* Let the integration converge until the term of the sum is no longer - * significant. - */ - do { - const double y{x2 / k}; - ++k; - last_sum = sum; - term *= y * y; - sum += term; - } while(sum != last_sum); - - return sum; -} - -/* Calculate a Kaiser window from the given beta value and a normalized k - * [-1, 1]. - * - * w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1 - * { 0, elsewhere. - * - * Where k can be calculated as: - * - * k = i / l, where -l <= i <= l. - * - * or: - * - * k = 2 i / M - 1, where 0 <= i <= M. - */ -constexpr double Kaiser(const double beta, const double k, const double besseli_0_beta) -{ - if(!(k >= -1.0 && k <= 1.0)) - return 0.0; - return BesselI_0(beta * std::sqrt(1.0 - k*k)) / besseli_0_beta; -} - -/* Calculates the (normalized frequency) transition width of the Kaiser window. - * Rejection is in dB. - */ -constexpr double CalcKaiserWidth(const double rejection, const uint order) -{ - if(rejection > 21.19) - return (rejection - 7.95) / (order * 2.285 * al::MathDefs::Tau()); - /* This enforces a minimum rejection of just above 21.18dB */ - return 5.79 / (order * al::MathDefs::Tau()); -} - -/* Calculates the beta value of the Kaiser window. Rejection is in dB. */ -constexpr double CalcKaiserBeta(const double rejection) -{ - if(rejection > 50.0) - return 0.1102 * (rejection-8.7); - else if(rejection >= 21.0) - return (0.5842 * std::pow(rejection-21.0, 0.4)) + (0.07886 * (rejection-21.0)); - return 0.0; -} - - -struct BSincHeader { - double width{}; - double beta{}; - double scaleBase{}; - double scaleRange{}; - double besseli_0_beta{}; - - uint a[BSincScaleCount]{}; - uint total_size{}; - - constexpr BSincHeader(uint Rejection, uint Order) noexcept - { - width = CalcKaiserWidth(Rejection, Order); - beta = CalcKaiserBeta(Rejection); - scaleBase = width / 2.0; - scaleRange = 1.0 - scaleBase; - besseli_0_beta = BesselI_0(beta); - - uint num_points{Order+1}; - for(uint si{0};si < BSincScaleCount;++si) - { - const double scale{scaleBase + (scaleRange * si / (BSincScaleCount-1))}; - const uint a_{std::min(static_cast(num_points / 2.0 / scale), num_points)}; - const uint m{2 * a_}; - - a[si] = a_; - total_size += 4 * BSincPhaseCount * ((m+3) & ~3u); - } - } -}; - -/* 11th and 23rd order filters (12 and 24-point respectively) with a 60dB drop - * at nyquist. Each filter will scale up the order when downsampling, to 23rd - * and 47th order respectively. - */ -constexpr BSincHeader bsinc12_hdr{60, 11}; -constexpr BSincHeader bsinc24_hdr{60, 23}; - - -/* NOTE: GCC 5 has an issue with BSincHeader objects being in an anonymous - * namespace while also being used as non-type template parameters. - */ -#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 6 -template -struct BSincFilterArray { - alignas(16) std::array mTable; - - BSincFilterArray(const BSincHeader &hdr) -#else -template -struct BSincFilterArray { - alignas(16) std::array mTable; - - BSincFilterArray() -#endif - { - using filter_type = double[][BSincPhaseCount+1][BSincPointsMax]; - auto filter = std::make_unique(BSincScaleCount); - - /* Calculate the Kaiser-windowed Sinc filter coefficients for each - * scale and phase index. - */ - for(uint si{0};si < BSincScaleCount;++si) - { - const uint m{hdr.a[si] * 2}; - const size_t o{(BSincPointsMax-m) / 2}; - const double scale{hdr.scaleBase + (hdr.scaleRange * si / (BSincScaleCount-1))}; - const double cutoff{scale - (hdr.scaleBase * std::max(0.5, scale) * 2.0)}; - const auto a = static_cast(hdr.a[si]); - const double l{a - 1.0}; - - /* Do one extra phase index so that the phase delta has a proper - * target for its last index. - */ - for(uint pi{0};pi <= BSincPhaseCount;++pi) - { - const double phase{l + (pi/double{BSincPhaseCount})}; - - for(uint i{0};i < m;++i) - { - const double x{i - phase}; - filter[si][pi][o+i] = Kaiser(hdr.beta, x/a, hdr.besseli_0_beta) * cutoff * - Sinc(cutoff*x); - } - } - } - - size_t idx{0}; - for(size_t si{0};si < BSincScaleCount-1;++si) - { - const size_t m{((hdr.a[si]*2) + 3) & ~3u}; - const size_t o{(BSincPointsMax-m) / 2}; - - for(size_t pi{0};pi < BSincPhaseCount;++pi) - { - /* Write out the filter. Also calculate and write out the phase - * and scale deltas. - */ - for(size_t i{0};i < m;++i) - mTable[idx++] = static_cast(filter[si][pi][o+i]); - - /* Linear interpolation between phases is simplified by pre- - * calculating the delta (b - a) in: x = a + f (b - a) - */ - for(size_t i{0};i < m;++i) - { - const double phDelta{filter[si][pi+1][o+i] - filter[si][pi][o+i]}; - mTable[idx++] = static_cast(phDelta); - } - - /* Linear interpolation between scales is also simplified. - * - * Given a difference in points between scales, the destination - * points will be 0, thus: x = a + f (-a) - */ - for(size_t i{0};i < m;++i) - { - const double scDelta{filter[si+1][pi][o+i] - filter[si][pi][o+i]}; - mTable[idx++] = static_cast(scDelta); - } - - /* This last simplification is done to complete the bilinear - * equation for the combination of phase and scale. - */ - for(size_t i{0};i < m;++i) - { - const double spDelta{(filter[si+1][pi+1][o+i] - filter[si+1][pi][o+i]) - - (filter[si][pi+1][o+i] - filter[si][pi][o+i])}; - mTable[idx++] = static_cast(spDelta); - } - } - } - { - /* The last scale index doesn't have any scale or scale-phase - * deltas. - */ - constexpr size_t si{BSincScaleCount-1}; - const size_t m{((hdr.a[si]*2) + 3) & ~3u}; - const size_t o{(BSincPointsMax-m) / 2}; - - for(size_t pi{0};pi < BSincPhaseCount;++pi) - { - for(size_t i{0};i < m;++i) - mTable[idx++] = static_cast(filter[si][pi][o+i]); - for(size_t i{0};i < m;++i) - { - const double phDelta{filter[si][pi+1][o+i] - filter[si][pi][o+i]}; - mTable[idx++] = static_cast(phDelta); - } - for(size_t i{0};i < m;++i) - mTable[idx++] = 0.0f; - for(size_t i{0};i < m;++i) - mTable[idx++] = 0.0f; - } - } - assert(idx == hdr.total_size); - } -}; - -#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 6 -const BSincFilterArray bsinc12_filter{bsinc12_hdr}; -const BSincFilterArray bsinc24_filter{bsinc24_hdr}; -#else -const BSincFilterArray bsinc12_filter{}; -const BSincFilterArray bsinc24_filter{}; -#endif - -constexpr BSincTable GenerateBSincTable(const BSincHeader &hdr, const float *tab) -{ - BSincTable ret{}; - ret.scaleBase = static_cast(hdr.scaleBase); - ret.scaleRange = static_cast(1.0 / hdr.scaleRange); - for(size_t i{0};i < BSincScaleCount;++i) - ret.m[i] = ((hdr.a[i]*2) + 3) & ~3u; - ret.filterOffset[0] = 0; - for(size_t i{1};i < BSincScaleCount;++i) - ret.filterOffset[i] = ret.filterOffset[i-1] + ret.m[i-1]*4*BSincPhaseCount; - ret.Tab = tab; - return ret; -} - -} // namespace - -const BSincTable bsinc12{GenerateBSincTable(bsinc12_hdr, &bsinc12_filter.mTable.front())}; -const BSincTable bsinc24{GenerateBSincTable(bsinc24_hdr, &bsinc24_filter.mTable.front())}; diff --git a/alc/bsinc_tables.h b/alc/bsinc_tables.h deleted file mode 100644 index 8e37336d..00000000 --- a/alc/bsinc_tables.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef BSINC_TABLES_H -#define BSINC_TABLES_H - -#include "bsinc_defs.h" - - -struct BSincTable { - float scaleBase, scaleRange; - unsigned int m[BSincScaleCount]; - unsigned int filterOffset[BSincScaleCount]; - const float *Tab; -}; - -extern const BSincTable bsinc12; -extern const BSincTable bsinc24; - -#endif /* BSINC_TABLES_H */ diff --git a/alc/mixer/mixer_c.cpp b/alc/mixer/mixer_c.cpp index c292235a..702401e6 100644 --- a/alc/mixer/mixer_c.cpp +++ b/alc/mixer/mixer_c.cpp @@ -5,8 +5,8 @@ #include #include "alcmain.h" -#include "alu.h" -#include "bsinc_defs.h" +#include "alnumeric.h" +#include "core/bsinc_tables.h" #include "defs.h" #include "hrtfbase.h" diff --git a/alc/mixer/mixer_neon.cpp b/alc/mixer/mixer_neon.cpp index bce2f4f8..631b0371 100644 --- a/alc/mixer/mixer_neon.cpp +++ b/alc/mixer/mixer_neon.cpp @@ -5,7 +5,7 @@ #include #include "alnumeric.h" -#include "bsinc_defs.h" +#include "core/bsinc_defs.h" #include "defs.h" #include "hrtfbase.h" diff --git a/alc/mixer/mixer_sse.cpp b/alc/mixer/mixer_sse.cpp index 858f5bff..c59e8579 100644 --- a/alc/mixer/mixer_sse.cpp +++ b/alc/mixer/mixer_sse.cpp @@ -5,7 +5,7 @@ #include #include "alnumeric.h" -#include "bsinc_defs.h" +#include "core/bsinc_defs.h" #include "defs.h" #include "hrtfbase.h" diff --git a/core/bsinc_defs.h b/core/bsinc_defs.h new file mode 100644 index 00000000..43865289 --- /dev/null +++ b/core/bsinc_defs.h @@ -0,0 +1,16 @@ +#ifndef CORE_BSINC_DEFS_H +#define CORE_BSINC_DEFS_H + +/* The number of distinct scale and phase intervals within the filter table. */ +constexpr unsigned int BSincScaleBits{4}; +constexpr unsigned int BSincScaleCount{1 << BSincScaleBits}; +constexpr unsigned int BSincPhaseBits{5}; +constexpr unsigned int BSincPhaseCount{1 << BSincPhaseBits}; + +/* The maximum number of sample points for the bsinc filters. The max points + * includes the doubling for downsampling, so the maximum number of base sample + * points is 24, which is 23rd order. + */ +constexpr unsigned int BSincPointsMax{48}; + +#endif /* CORE_BSINC_DEFS_H */ diff --git a/core/bsinc_tables.cpp b/core/bsinc_tables.cpp new file mode 100644 index 00000000..315e1448 --- /dev/null +++ b/core/bsinc_tables.cpp @@ -0,0 +1,288 @@ + +#include "bsinc_tables.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "math_defs.h" + + +namespace { + +using uint = unsigned int; + + +/* This is the normalized cardinal sine (sinc) function. + * + * sinc(x) = { 1, x = 0 + * { sin(pi x) / (pi x), otherwise. + */ +constexpr double Sinc(const double x) +{ + if(!(x > 1e-15 || x < -1e-15)) + return 1.0; + return std::sin(al::MathDefs::Pi()*x) / (al::MathDefs::Pi()*x); +} + +/* The zero-order modified Bessel function of the first kind, used for the + * Kaiser window. + * + * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k) + * = sum_{k=0}^inf ((x / 2)^k / k!)^2 + */ +constexpr double BesselI_0(const double x) +{ + /* Start at k=1 since k=0 is trivial. */ + const double x2{x / 2.0}; + double term{1.0}; + double sum{1.0}; + double last_sum{}; + int k{1}; + + /* Let the integration converge until the term of the sum is no longer + * significant. + */ + do { + const double y{x2 / k}; + ++k; + last_sum = sum; + term *= y * y; + sum += term; + } while(sum != last_sum); + + return sum; +} + +/* Calculate a Kaiser window from the given beta value and a normalized k + * [-1, 1]. + * + * w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1 + * { 0, elsewhere. + * + * Where k can be calculated as: + * + * k = i / l, where -l <= i <= l. + * + * or: + * + * k = 2 i / M - 1, where 0 <= i <= M. + */ +constexpr double Kaiser(const double beta, const double k, const double besseli_0_beta) +{ + if(!(k >= -1.0 && k <= 1.0)) + return 0.0; + return BesselI_0(beta * std::sqrt(1.0 - k*k)) / besseli_0_beta; +} + +/* Calculates the (normalized frequency) transition width of the Kaiser window. + * Rejection is in dB. + */ +constexpr double CalcKaiserWidth(const double rejection, const uint order) +{ + if(rejection > 21.19) + return (rejection - 7.95) / (order * 2.285 * al::MathDefs::Tau()); + /* This enforces a minimum rejection of just above 21.18dB */ + return 5.79 / (order * al::MathDefs::Tau()); +} + +/* Calculates the beta value of the Kaiser window. Rejection is in dB. */ +constexpr double CalcKaiserBeta(const double rejection) +{ + if(rejection > 50.0) + return 0.1102 * (rejection-8.7); + else if(rejection >= 21.0) + return (0.5842 * std::pow(rejection-21.0, 0.4)) + (0.07886 * (rejection-21.0)); + return 0.0; +} + + +struct BSincHeader { + double width{}; + double beta{}; + double scaleBase{}; + double scaleRange{}; + double besseli_0_beta{}; + + uint a[BSincScaleCount]{}; + uint total_size{}; + + constexpr BSincHeader(uint Rejection, uint Order) noexcept + { + width = CalcKaiserWidth(Rejection, Order); + beta = CalcKaiserBeta(Rejection); + scaleBase = width / 2.0; + scaleRange = 1.0 - scaleBase; + besseli_0_beta = BesselI_0(beta); + + uint num_points{Order+1}; + for(uint si{0};si < BSincScaleCount;++si) + { + const double scale{scaleBase + (scaleRange * si / (BSincScaleCount-1))}; + const uint a_{std::min(static_cast(num_points / 2.0 / scale), num_points)}; + const uint m{2 * a_}; + + a[si] = a_; + total_size += 4 * BSincPhaseCount * ((m+3) & ~3u); + } + } +}; + +/* 11th and 23rd order filters (12 and 24-point respectively) with a 60dB drop + * at nyquist. Each filter will scale up the order when downsampling, to 23rd + * and 47th order respectively. + */ +constexpr BSincHeader bsinc12_hdr{60, 11}; +constexpr BSincHeader bsinc24_hdr{60, 23}; + + +/* NOTE: GCC 5 has an issue with BSincHeader objects being in an anonymous + * namespace while also being used as non-type template parameters. + */ +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 6 +template +struct BSincFilterArray { + alignas(16) std::array mTable; + + BSincFilterArray(const BSincHeader &hdr) +#else +template +struct BSincFilterArray { + alignas(16) std::array mTable; + + BSincFilterArray() +#endif + { + using filter_type = double[][BSincPhaseCount+1][BSincPointsMax]; + auto filter = std::make_unique(BSincScaleCount); + + /* Calculate the Kaiser-windowed Sinc filter coefficients for each + * scale and phase index. + */ + for(uint si{0};si < BSincScaleCount;++si) + { + const uint m{hdr.a[si] * 2}; + const size_t o{(BSincPointsMax-m) / 2}; + const double scale{hdr.scaleBase + (hdr.scaleRange * si / (BSincScaleCount-1))}; + const double cutoff{scale - (hdr.scaleBase * std::max(0.5, scale) * 2.0)}; + const auto a = static_cast(hdr.a[si]); + const double l{a - 1.0}; + + /* Do one extra phase index so that the phase delta has a proper + * target for its last index. + */ + for(uint pi{0};pi <= BSincPhaseCount;++pi) + { + const double phase{l + (pi/double{BSincPhaseCount})}; + + for(uint i{0};i < m;++i) + { + const double x{i - phase}; + filter[si][pi][o+i] = Kaiser(hdr.beta, x/a, hdr.besseli_0_beta) * cutoff * + Sinc(cutoff*x); + } + } + } + + size_t idx{0}; + for(size_t si{0};si < BSincScaleCount-1;++si) + { + const size_t m{((hdr.a[si]*2) + 3) & ~3u}; + const size_t o{(BSincPointsMax-m) / 2}; + + for(size_t pi{0};pi < BSincPhaseCount;++pi) + { + /* Write out the filter. Also calculate and write out the phase + * and scale deltas. + */ + for(size_t i{0};i < m;++i) + mTable[idx++] = static_cast(filter[si][pi][o+i]); + + /* Linear interpolation between phases is simplified by pre- + * calculating the delta (b - a) in: x = a + f (b - a) + */ + for(size_t i{0};i < m;++i) + { + const double phDelta{filter[si][pi+1][o+i] - filter[si][pi][o+i]}; + mTable[idx++] = static_cast(phDelta); + } + + /* Linear interpolation between scales is also simplified. + * + * Given a difference in points between scales, the destination + * points will be 0, thus: x = a + f (-a) + */ + for(size_t i{0};i < m;++i) + { + const double scDelta{filter[si+1][pi][o+i] - filter[si][pi][o+i]}; + mTable[idx++] = static_cast(scDelta); + } + + /* This last simplification is done to complete the bilinear + * equation for the combination of phase and scale. + */ + for(size_t i{0};i < m;++i) + { + const double spDelta{(filter[si+1][pi+1][o+i] - filter[si+1][pi][o+i]) - + (filter[si][pi+1][o+i] - filter[si][pi][o+i])}; + mTable[idx++] = static_cast(spDelta); + } + } + } + { + /* The last scale index doesn't have any scale or scale-phase + * deltas. + */ + constexpr size_t si{BSincScaleCount-1}; + const size_t m{((hdr.a[si]*2) + 3) & ~3u}; + const size_t o{(BSincPointsMax-m) / 2}; + + for(size_t pi{0};pi < BSincPhaseCount;++pi) + { + for(size_t i{0};i < m;++i) + mTable[idx++] = static_cast(filter[si][pi][o+i]); + for(size_t i{0};i < m;++i) + { + const double phDelta{filter[si][pi+1][o+i] - filter[si][pi][o+i]}; + mTable[idx++] = static_cast(phDelta); + } + for(size_t i{0};i < m;++i) + mTable[idx++] = 0.0f; + for(size_t i{0};i < m;++i) + mTable[idx++] = 0.0f; + } + } + assert(idx == hdr.total_size); + } +}; + +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 6 +const BSincFilterArray bsinc12_filter{bsinc12_hdr}; +const BSincFilterArray bsinc24_filter{bsinc24_hdr}; +#else +const BSincFilterArray bsinc12_filter{}; +const BSincFilterArray bsinc24_filter{}; +#endif + +constexpr BSincTable GenerateBSincTable(const BSincHeader &hdr, const float *tab) +{ + BSincTable ret{}; + ret.scaleBase = static_cast(hdr.scaleBase); + ret.scaleRange = static_cast(1.0 / hdr.scaleRange); + for(size_t i{0};i < BSincScaleCount;++i) + ret.m[i] = ((hdr.a[i]*2) + 3) & ~3u; + ret.filterOffset[0] = 0; + for(size_t i{1};i < BSincScaleCount;++i) + ret.filterOffset[i] = ret.filterOffset[i-1] + ret.m[i-1]*4*BSincPhaseCount; + ret.Tab = tab; + return ret; +} + +} // namespace + +const BSincTable bsinc12{GenerateBSincTable(bsinc12_hdr, &bsinc12_filter.mTable.front())}; +const BSincTable bsinc24{GenerateBSincTable(bsinc24_hdr, &bsinc24_filter.mTable.front())}; diff --git a/core/bsinc_tables.h b/core/bsinc_tables.h new file mode 100644 index 00000000..f52cda66 --- /dev/null +++ b/core/bsinc_tables.h @@ -0,0 +1,17 @@ +#ifndef CORE_BSINC_TABLES_H +#define CORE_BSINC_TABLES_H + +#include "bsinc_defs.h" + + +struct BSincTable { + float scaleBase, scaleRange; + unsigned int m[BSincScaleCount]; + unsigned int filterOffset[BSincScaleCount]; + const float *Tab; +}; + +extern const BSincTable bsinc12; +extern const BSincTable bsinc24; + +#endif /* CORE_BSINC_TABLES_H */ -- cgit v1.2.3 From 69d55d7e03996484cc899de1e21172a7a4532d6b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Dec 2020 09:42:13 -0800 Subject: Move the filters to core --- CMakeLists.txt | 12 +- al/source.cpp | 4 +- alc/alc.cpp | 4 +- alc/alcmain.h | 2 +- alc/alu.cpp | 6 +- alc/bformatdec.cpp | 12 +- alc/bformatdec.h | 14 +- alc/effects/autowah.cpp | 2 +- alc/effects/convolution.cpp | 4 +- alc/effects/distortion.cpp | 5 +- alc/effects/echo.cpp | 12 +- alc/effects/equalizer.cpp | 5 +- alc/effects/modulator.cpp | 5 +- alc/effects/reverb.cpp | 7 +- alc/filters/biquad.cpp | 163 ------------------- alc/filters/biquad.h | 144 ----------------- alc/filters/nfc.cpp | 383 -------------------------------------------- alc/filters/nfc.h | 63 -------- alc/filters/splitter.cpp | 113 ------------- alc/filters/splitter.h | 36 ----- alc/front_stablizer.h | 2 +- alc/hrtf.cpp | 2 +- alc/hrtf.h | 2 +- alc/voice.cpp | 6 +- alc/voice.h | 6 +- core/filters/biquad.cpp | 163 +++++++++++++++++++ core/filters/biquad.h | 144 +++++++++++++++++ core/filters/nfc.cpp | 383 ++++++++++++++++++++++++++++++++++++++++++++ core/filters/nfc.h | 63 ++++++++ core/filters/splitter.cpp | 113 +++++++++++++ core/filters/splitter.h | 36 +++++ 31 files changed, 955 insertions(+), 961 deletions(-) delete mode 100644 alc/filters/biquad.cpp delete mode 100644 alc/filters/biquad.h delete mode 100644 alc/filters/nfc.cpp delete mode 100644 alc/filters/nfc.h delete mode 100644 alc/filters/splitter.cpp delete mode 100644 alc/filters/splitter.h create mode 100644 core/filters/biquad.cpp create mode 100644 core/filters/biquad.h create mode 100644 core/filters/nfc.cpp create mode 100644 core/filters/nfc.h create mode 100644 core/filters/splitter.cpp create mode 100644 core/filters/splitter.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 97004962..8c08af6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -633,6 +633,12 @@ set(ALC_OBJS core/bufferline.h core/devformat.cpp core/devformat.h + core/filters/biquad.h + core/filters/biquad.cpp + core/filters/nfc.cpp + core/filters/nfc.h + core/filters/splitter.cpp + core/filters/splitter.h core/mastering.cpp core/mastering.h @@ -674,12 +680,6 @@ set(ALC_OBJS alc/effects/pshifter.cpp alc/effects/reverb.cpp alc/effects/vmorpher.cpp - alc/filters/biquad.h - alc/filters/biquad.cpp - alc/filters/nfc.cpp - alc/filters/nfc.h - alc/filters/splitter.cpp - alc/filters/splitter.h alc/fmt_traits.cpp alc/fmt_traits.h alc/fpu_ctrl.cpp diff --git a/al/source.cpp b/al/source.cpp index 1af39da2..5dcd1cca 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -59,10 +59,10 @@ #include "backends/base.h" #include "bformatdec.h" #include "buffer.h" +#include "core/filters/nfc.h" +#include "core/filters/splitter.h" #include "event.h" #include "filter.h" -#include "filters/nfc.h" -#include "filters/splitter.h" #include "inprogext.h" #include "logging.h" #include "math_defs.h" diff --git a/alc/alc.cpp b/alc/alc.cpp index faf6392e..87c0578e 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -82,10 +82,10 @@ #include "compat.h" #include "core/devformat.h" #include "core/mastering.h" +#include "core/filters/nfc.h" +#include "core/filters/splitter.h" #include "cpu_caps.h" #include "effects/base.h" -#include "filters/nfc.h" -#include "filters/splitter.h" #include "fpu_ctrl.h" #include "front_stablizer.h" #include "hrtf.h" diff --git a/alc/alcmain.h b/alc/alcmain.h index 7949baf2..42808f26 100644 --- a/alc/alcmain.h +++ b/alc/alcmain.h @@ -25,7 +25,7 @@ #include "atomic.h" #include "core/bufferline.h" #include "core/devformat.h" -#include "filters/splitter.h" +#include "core/filters/splitter.h" #include "hrtf.h" #include "inprogext.h" #include "intrusive_ptr.h" diff --git a/alc/alu.cpp b/alc/alu.cpp index 18a26a38..c13365ec 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -61,12 +61,12 @@ #include "bs2b.h" #include "core/bsinc_tables.h" #include "core/devformat.h" +#include "core/filters/biquad.h" +#include "core/filters/nfc.h" +#include "core/filters/splitter.h" #include "core/mastering.h" #include "cpu_caps.h" #include "effects/base.h" -#include "filters/biquad.h" -#include "filters/nfc.h" -#include "filters/splitter.h" #include "fpu_ctrl.h" #include "front_stablizer.h" #include "hrtf.h" diff --git a/alc/bformatdec.cpp b/alc/bformatdec.cpp index b2a2aec9..6f229947 100644 --- a/alc/bformatdec.cpp +++ b/alc/bformatdec.cpp @@ -10,12 +10,10 @@ #include #include -#include "AL/al.h" - #include "almalloc.h" #include "alu.h" #include "ambdec.h" -#include "filters/splitter.h" +#include "core/filters/splitter.h" #include "front_stablizer.h" #include "math_defs.h" #include "opthelpers.h" @@ -33,7 +31,7 @@ constexpr std::array Ambi3DDecoderHFScale3O{{ 5.89792205e-01f, 8.79693856e-01f, 1.00000000e+00f, 1.00000000e+00f }}; -inline auto GetDecoderHFScales(ALuint order) noexcept -> const std::array& +inline auto GetDecoderHFScales(uint order) noexcept -> const std::array& { if(order >= 3) return Ambi3DDecoderHFScale3O; if(order == 2) return Ambi3DDecoderHFScale2O; @@ -52,7 +50,7 @@ inline auto GetAmbiScales(AmbDecScale scaletype) noexcept BFormatDec::BFormatDec(const AmbDecConf *conf, const bool allow_2band, const size_t inchans, - const ALuint srate, const ALuint (&chanmap)[MAX_OUTPUT_CHANNELS], + const uint srate, const uint (&chanmap)[MAX_OUTPUT_CHANNELS], std::unique_ptr stablizer) : mStablizer{std::move(stablizer)}, mDualBand{allow_2band && (conf->FreqBands == 2)} , mChannelDec{inchans} @@ -269,7 +267,7 @@ void BFormatDec::processStablize(const al::span OutBuffer, } -auto BFormatDec::GetHFOrderScales(const ALuint in_order, const ALuint out_order) noexcept +auto BFormatDec::GetHFOrderScales(const uint in_order, const uint out_order) noexcept -> std::array { std::array ret{}; @@ -286,7 +284,7 @@ auto BFormatDec::GetHFOrderScales(const ALuint in_order, const ALuint out_order) } std::unique_ptr BFormatDec::Create(const AmbDecConf *conf, const bool allow_2band, - const size_t inchans, const ALuint srate, const ALuint (&chanmap)[MAX_OUTPUT_CHANNELS], + const size_t inchans, const uint srate, const uint (&chanmap)[MAX_OUTPUT_CHANNELS], std::unique_ptr stablizer) { return std::unique_ptr{new(FamCount(inchans)) diff --git a/alc/bformatdec.h b/alc/bformatdec.h index 280ca2fc..adc69dc3 100644 --- a/alc/bformatdec.h +++ b/alc/bformatdec.h @@ -5,14 +5,12 @@ #include #include -#include "AL/al.h" - -#include "alcmain.h" #include "almalloc.h" #include "alspan.h" #include "ambidefs.h" +#include "core/bufferline.h" #include "core/devformat.h" -#include "filters/splitter.h" +#include "core/filters/splitter.h" struct AmbDecConf; struct FrontStablizer; @@ -44,7 +42,7 @@ class BFormatDec { public: BFormatDec(const AmbDecConf *conf, const bool allow_2band, const size_t inchans, - const ALuint srate, const ALuint (&chanmap)[MAX_OUTPUT_CHANNELS], + const uint srate, const uint (&chanmap)[MAX_OUTPUT_CHANNELS], std::unique_ptr stablizer); BFormatDec(const size_t inchans, const al::span coeffs, const al::span coeffslf, std::unique_ptr stablizer); @@ -61,11 +59,11 @@ public: const size_t SamplesToDo); /* Retrieves per-order HF scaling factors for "upsampling" ambisonic data. */ - static std::array GetHFOrderScales(const ALuint in_order, - const ALuint out_order) noexcept; + static std::array GetHFOrderScales(const uint in_order, + const uint out_order) noexcept; static std::unique_ptr Create(const AmbDecConf *conf, const bool allow_2band, - const size_t inchans, const ALuint srate, const ALuint (&chanmap)[MAX_OUTPUT_CHANNELS], + const size_t inchans, const uint srate, const uint (&chanmap)[MAX_OUTPUT_CHANNELS], std::unique_ptr stablizer); static std::unique_ptr Create(const size_t inchans, const al::span coeffs, const al::span coeffslf, diff --git a/alc/effects/autowah.cpp b/alc/effects/autowah.cpp index 5ac51e32..f2ffab44 100644 --- a/alc/effects/autowah.cpp +++ b/alc/effects/autowah.cpp @@ -29,7 +29,7 @@ #include "alcmain.h" #include "alcontext.h" #include "alu.h" -#include "filters/biquad.h" +#include "core/filters/biquad.h" #include "vecmat.h" namespace { diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index e191e7bc..2442f97e 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -8,7 +8,6 @@ #include "AL/al.h" #include "AL/alc.h" -#include "al/auxeffectslot.h" #include "alcmain.h" #include "alcomplex.h" #include "alcontext.h" @@ -17,8 +16,9 @@ #include "ambidefs.h" #include "bformatdec.h" #include "buffer_storage.h" +#include "core/filters/splitter.h" #include "effects/base.h" -#include "filters/splitter.h" +#include "effectslot.h" #include "fmt_traits.h" #include "logging.h" #include "polyphase_resampler.h" diff --git a/alc/effects/distortion.cpp b/alc/effects/distortion.cpp index 757244c5..65f8977b 100644 --- a/alc/effects/distortion.cpp +++ b/alc/effects/distortion.cpp @@ -24,11 +24,10 @@ #include #include -#include "al/auxeffectslot.h" #include "alcmain.h" #include "alcontext.h" -#include "alu.h" -#include "filters/biquad.h" +#include "core/filters/biquad.h" +#include "effectslot.h" namespace { diff --git a/alc/effects/echo.cpp b/alc/effects/echo.cpp index a50d3c61..c030ac5b 100644 --- a/alc/effects/echo.cpp +++ b/alc/effects/echo.cpp @@ -25,17 +25,19 @@ #include -#include "al/auxeffectslot.h" -#include "al/filter.h" +#include "AL/efx.h" + #include "alcmain.h" #include "alcontext.h" -#include "alu.h" -#include "filters/biquad.h" +#include "core/filters/biquad.h" +#include "effectslot.h" #include "vector.h" namespace { +constexpr float LowpassFreqRef{5000.0f}; + struct EchoState final : public EffectState { al::vector mSampleBuffer; @@ -95,7 +97,7 @@ void EchoState::update(const ALCcontext *context, const EffectSlot *slot, mTap[1].delay = float2uint(props->Echo.LRDelay*frequency + 0.5f) + mTap[0].delay; const float gainhf{maxf(1.0f - props->Echo.Damping, 0.0625f)}; /* Limit -24dB */ - mFilter.setParamsFromSlope(BiquadType::HighShelf, LOWPASSFREQREF/frequency, gainhf, 1.0f); + mFilter.setParamsFromSlope(BiquadType::HighShelf, LowpassFreqRef/frequency, gainhf, 1.0f); mFeedGain = props->Echo.Feedback; diff --git a/alc/effects/equalizer.cpp b/alc/effects/equalizer.cpp index 19d38498..c311a941 100644 --- a/alc/effects/equalizer.cpp +++ b/alc/effects/equalizer.cpp @@ -26,11 +26,10 @@ #include #include -#include "al/auxeffectslot.h" #include "alcmain.h" #include "alcontext.h" -#include "alu.h" -#include "filters/biquad.h" +#include "core/filters/biquad.h" +#include "effectslot.h" #include "vecmat.h" diff --git a/alc/effects/modulator.cpp b/alc/effects/modulator.cpp index 7e4f9fc0..a0af9890 100644 --- a/alc/effects/modulator.cpp +++ b/alc/effects/modulator.cpp @@ -26,11 +26,10 @@ #include #include -#include "al/auxeffectslot.h" #include "alcmain.h" #include "alcontext.h" -#include "alu.h" -#include "filters/biquad.h" +#include "core/filters/biquad.h" +#include "effectslot.h" #include "vecmat.h" diff --git a/alc/effects/reverb.cpp b/alc/effects/reverb.cpp index 6471b210..a4b423c7 100644 --- a/alc/effects/reverb.cpp +++ b/alc/effects/reverb.cpp @@ -29,13 +29,12 @@ #include #include -#include "al/auxeffectslot.h" -#include "al/listener.h" #include "alcmain.h" #include "alcontext.h" -#include "alu.h" +#include "alnumeric.h" #include "bformatdec.h" -#include "filters/biquad.h" +#include "core/filters/biquad.h" +#include "effectslot.h" #include "vector.h" #include "vecmat.h" diff --git a/alc/filters/biquad.cpp b/alc/filters/biquad.cpp deleted file mode 100644 index fefdc8e1..00000000 --- a/alc/filters/biquad.cpp +++ /dev/null @@ -1,163 +0,0 @@ - -#include "config.h" - -#include "biquad.h" - -#include -#include -#include - -#include "opthelpers.h" - - -template -void BiquadFilterR::setParams(BiquadType type, Real f0norm, Real gain, Real rcpQ) -{ - // Limit gain to -100dB - assert(gain > 0.00001f); - - const Real w0{al::MathDefs::Tau() * f0norm}; - const Real sin_w0{std::sin(w0)}; - const Real cos_w0{std::cos(w0)}; - const Real alpha{sin_w0/2.0f * rcpQ}; - - Real sqrtgain_alpha_2; - Real a[3]{ 1.0f, 0.0f, 0.0f }; - Real b[3]{ 1.0f, 0.0f, 0.0f }; - - /* Calculate filter coefficients depending on filter type */ - switch(type) - { - case BiquadType::HighShelf: - sqrtgain_alpha_2 = 2.0f * std::sqrt(gain) * alpha; - b[0] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2); - b[1] = -2.0f*gain*((gain-1.0f) + (gain+1.0f)*cos_w0 ); - b[2] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2); - a[0] = (gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2; - a[1] = 2.0f* ((gain-1.0f) - (gain+1.0f)*cos_w0 ); - a[2] = (gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2; - break; - case BiquadType::LowShelf: - sqrtgain_alpha_2 = 2.0f * std::sqrt(gain) * alpha; - b[0] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2); - b[1] = 2.0f*gain*((gain-1.0f) - (gain+1.0f)*cos_w0 ); - b[2] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2); - a[0] = (gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2; - a[1] = -2.0f* ((gain-1.0f) + (gain+1.0f)*cos_w0 ); - a[2] = (gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2; - break; - case BiquadType::Peaking: - b[0] = 1.0f + alpha * gain; - b[1] = -2.0f * cos_w0; - b[2] = 1.0f - alpha * gain; - a[0] = 1.0f + alpha / gain; - a[1] = -2.0f * cos_w0; - a[2] = 1.0f - alpha / gain; - break; - - case BiquadType::LowPass: - b[0] = (1.0f - cos_w0) / 2.0f; - b[1] = 1.0f - cos_w0; - b[2] = (1.0f - cos_w0) / 2.0f; - a[0] = 1.0f + alpha; - a[1] = -2.0f * cos_w0; - a[2] = 1.0f - alpha; - break; - case BiquadType::HighPass: - b[0] = (1.0f + cos_w0) / 2.0f; - b[1] = -(1.0f + cos_w0); - b[2] = (1.0f + cos_w0) / 2.0f; - a[0] = 1.0f + alpha; - a[1] = -2.0f * cos_w0; - a[2] = 1.0f - alpha; - break; - case BiquadType::BandPass: - b[0] = alpha; - b[1] = 0.0f; - b[2] = -alpha; - a[0] = 1.0f + alpha; - a[1] = -2.0f * cos_w0; - a[2] = 1.0f - alpha; - break; - } - - mA1 = a[1] / a[0]; - mA2 = a[2] / a[0]; - mB0 = b[0] / a[0]; - mB1 = b[1] / a[0]; - mB2 = b[2] / a[0]; -} - -template -void BiquadFilterR::process(const al::span src, Real *dst) -{ - const Real b0{mB0}; - const Real b1{mB1}; - const Real b2{mB2}; - const Real a1{mA1}; - const Real a2{mA2}; - Real z1{mZ1}; - Real z2{mZ2}; - - /* Processing loop is Transposed Direct Form II. This requires less storage - * compared to Direct Form I (only two delay components, instead of a four- - * sample history; the last two inputs and outputs), and works better for - * floating-point which favors summing similarly-sized values while being - * less bothered by overflow. - * - * See: http://www.earlevel.com/main/2003/02/28/biquads/ - */ - auto proc_sample = [b0,b1,b2,a1,a2,&z1,&z2](Real input) noexcept -> Real - { - const Real output{input*b0 + z1}; - z1 = input*b1 - output*a1 + z2; - z2 = input*b2 - output*a2; - return output; - }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); - - mZ1 = z1; - mZ2 = z2; -} - -template -void BiquadFilterR::dualProcess(BiquadFilterR &other, const al::span src, - Real *dst) -{ - const Real b00{mB0}; - const Real b01{mB1}; - const Real b02{mB2}; - const Real a01{mA1}; - const Real a02{mA2}; - const Real b10{other.mB0}; - const Real b11{other.mB1}; - const Real b12{other.mB2}; - const Real a11{other.mA1}; - const Real a12{other.mA2}; - Real z01{mZ1}; - Real z02{mZ2}; - Real z11{other.mZ1}; - Real z12{other.mZ2}; - - auto proc_sample = [b00,b01,b02,a01,a02,b10,b11,b12,a11,a12,&z01,&z02,&z11,&z12](Real input) noexcept -> Real - { - const Real tmpout{input*b00 + z01}; - z01 = input*b01 - tmpout*a01 + z02; - z02 = input*b02 - tmpout*a02; - input = tmpout; - - const Real output{input*b10 + z11}; - z11 = input*b11 - output*a11 + z12; - z12 = input*b12 - output*a12; - return output; - }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); - - mZ1 = z01; - mZ2 = z02; - other.mZ1 = z11; - other.mZ2 = z12; -} - -template class BiquadFilterR; -template class BiquadFilterR; diff --git a/alc/filters/biquad.h b/alc/filters/biquad.h deleted file mode 100644 index 3ce70cb3..00000000 --- a/alc/filters/biquad.h +++ /dev/null @@ -1,144 +0,0 @@ -#ifndef FILTERS_BIQUAD_H -#define FILTERS_BIQUAD_H - -#include -#include -#include -#include - -#include "alspan.h" -#include "math_defs.h" - - -/* Filters implementation is based on the "Cookbook formulae for audio - * EQ biquad filter coefficients" by Robert Bristow-Johnson - * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt - */ -/* Implementation note: For the shelf and peaking filters, the specified gain - * is for the centerpoint of the transition band. This better fits EFX filter - * behavior, which expects the shelf's reference frequency to reach the given - * gain. To set the gain for the shelf or peak itself, use the square root of - * the desired linear gain (or halve the dB gain). - */ - -enum class BiquadType { - /** EFX-style low-pass filter, specifying a gain and reference frequency. */ - HighShelf, - /** EFX-style high-pass filter, specifying a gain and reference frequency. */ - LowShelf, - /** Peaking filter, specifying a gain and reference frequency. */ - Peaking, - - /** Low-pass cut-off filter, specifying a cut-off frequency. */ - LowPass, - /** High-pass cut-off filter, specifying a cut-off frequency. */ - HighPass, - /** Band-pass filter, specifying a center frequency. */ - BandPass, -}; - -template -class BiquadFilterR { - /* Last two delayed components for direct form II. */ - Real mZ1{0.0f}, mZ2{0.0f}; - /* Transfer function coefficients "b" (numerator) */ - Real mB0{1.0f}, mB1{0.0f}, mB2{0.0f}; - /* Transfer function coefficients "a" (denominator; a0 is pre-applied). */ - Real mA1{0.0f}, mA2{0.0f}; - - void setParams(BiquadType type, Real f0norm, Real gain, Real rcpQ); - - /** - * Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using - * the reference gain and shelf slope parameter. - * \param gain 0 < gain - * \param slope 0 < slope <= 1 - */ - static Real rcpQFromSlope(Real gain, Real slope) - { return std::sqrt((gain + 1.0f/gain)*(1.0f/slope - 1.0f) + 2.0f); } - - /** - * Calculates the rcpQ (i.e. 1/Q) coefficient for filters, using the - * normalized reference frequency and bandwidth. - * \param f0norm 0 < f0norm < 0.5. - * \param bandwidth 0 < bandwidth - */ - static Real rcpQFromBandwidth(Real f0norm, Real bandwidth) - { - const Real w0{al::MathDefs::Tau() * f0norm}; - return 2.0f*std::sinh(std::log(Real{2.0f})/2.0f*bandwidth*w0/std::sin(w0)); - } - -public: - void clear() noexcept { mZ1 = mZ2 = 0.0f; } - - /** - * Sets the filter state for the specified filter type and its parameters. - * - * \param type The type of filter to apply. - * \param f0norm The normalized reference frequency (ref / sample_rate). - * This is the center point for the Shelf, Peaking, and BandPass filter - * types, or the cutoff frequency for the LowPass and HighPass filter - * types. - * \param gain The gain for the reference frequency response. Only used by - * the Shelf and Peaking filter types. - * \param slope Slope steepness of the transition band. - */ - void setParamsFromSlope(BiquadType type, Real f0norm, Real gain, Real slope) - { - gain = std::max(gain, 0.001f); /* Limit -60dB */ - setParams(type, f0norm, gain, rcpQFromSlope(gain, slope)); - } - - /** - * Sets the filter state for the specified filter type and its parameters. - * - * \param type The type of filter to apply. - * \param f0norm The normalized reference frequency (ref / sample_rate). - * This is the center point for the Shelf, Peaking, and BandPass filter - * types, or the cutoff frequency for the LowPass and HighPass filter - * types. - * \param gain The gain for the reference frequency response. Only used by - * the Shelf and Peaking filter types. - * \param bandwidth Normalized bandwidth of the transition band. - */ - void setParamsFromBandwidth(BiquadType type, Real f0norm, Real gain, Real bandwidth) - { setParams(type, f0norm, gain, rcpQFromBandwidth(f0norm, bandwidth)); } - - void copyParamsFrom(const BiquadFilterR &other) - { - mB0 = other.mB0; - mB1 = other.mB1; - mB2 = other.mB2; - mA1 = other.mA1; - mA2 = other.mA2; - } - - void process(const al::span src, Real *dst); - /** Processes this filter and the other at the same time. */ - void dualProcess(BiquadFilterR &other, const al::span src, Real *dst); - - /* Rather hacky. It's just here to support "manual" processing. */ - std::pair getComponents() const noexcept { return {mZ1, mZ2}; } - void setComponents(Real z1, Real z2) noexcept { mZ1 = z1; mZ2 = z2; } - Real processOne(const Real in, Real &z1, Real &z2) const noexcept - { - const Real out{in*mB0 + z1}; - z1 = in*mB1 - out*mA1 + z2; - z2 = in*mB2 - out*mA2; - return out; - } -}; - -template -struct DualBiquadR { - BiquadFilterR &f0, &f1; - - void process(const al::span src, Real *dst) - { f0.dualProcess(f1, src, dst); } -}; - -using BiquadFilter = BiquadFilterR; -using DualBiquad = DualBiquadR; - -#endif /* FILTERS_BIQUAD_H */ diff --git a/alc/filters/nfc.cpp b/alc/filters/nfc.cpp deleted file mode 100644 index 9a28517c..00000000 --- a/alc/filters/nfc.cpp +++ /dev/null @@ -1,383 +0,0 @@ - -#include "config.h" - -#include "nfc.h" - -#include - -#include "opthelpers.h" - - -/* Near-field control filters are the basis for handling the near-field effect. - * The near-field effect is a bass-boost present in the directional components - * of a recorded signal, created as a result of the wavefront curvature (itself - * a function of sound distance). Proper reproduction dictates this be - * compensated for using a bass-cut given the playback speaker distance, to - * avoid excessive bass in the playback. - * - * For real-time rendered audio, emulating the near-field effect based on the - * sound source's distance, and subsequently compensating for it at output - * based on the speaker distances, can create a more realistic perception of - * sound distance beyond a simple 1/r attenuation. - * - * These filters do just that. Each one applies a low-shelf filter, created as - * the combination of a bass-boost for a given sound source distance (near- - * field emulation) along with a bass-cut for a given control/speaker distance - * (near-field compensation). - * - * Note that it is necessary to apply a cut along with the boost, since the - * boost alone is unstable in higher-order ambisonics as it causes an infinite - * DC gain (even first-order ambisonics requires there to be no DC offset for - * the boost to work). Consequently, ambisonics requires a control parameter to - * be used to avoid an unstable boost-only filter. NFC-HOA defines this control - * as a reference delay, calculated with: - * - * reference_delay = control_distance / speed_of_sound - * - * This means w0 (for input) or w1 (for output) should be set to: - * - * wN = 1 / (reference_delay * sample_rate) - * - * when dealing with NFC-HOA content. For FOA input content, which does not - * specify a reference_delay variable, w0 should be set to 0 to apply only - * near-field compensation for output. It's important that w1 be a finite, - * positive, non-0 value or else the bass-boost will become unstable again. - * Also, w0 should not be too large compared to w1, to avoid excessively loud - * low frequencies. - */ - -namespace { - -constexpr float B[5][4] = { - { 0.0f }, - { 1.0f }, - { 3.0f, 3.0f }, - { 3.6778f, 6.4595f, 2.3222f }, - { 4.2076f, 11.4877f, 5.7924f, 9.1401f } -}; - -NfcFilter1 NfcFilterCreate1(const float w0, const float w1) noexcept -{ - NfcFilter1 nfc{}; - float b_00, g_0; - float r; - - nfc.base_gain = 1.0f; - nfc.gain = 1.0f; - - /* Calculate bass-boost coefficients. */ - r = 0.5f * w0; - b_00 = B[1][0] * r; - g_0 = 1.0f + b_00; - - nfc.gain *= g_0; - nfc.b1 = 2.0f * b_00 / g_0; - - /* Calculate bass-cut coefficients. */ - r = 0.5f * w1; - b_00 = B[1][0] * r; - g_0 = 1.0f + b_00; - - nfc.base_gain /= g_0; - nfc.gain /= g_0; - nfc.a1 = 2.0f * b_00 / g_0; - - return nfc; -} - -void NfcFilterAdjust1(NfcFilter1 *nfc, const float w0) noexcept -{ - const float r{0.5f * w0}; - const float b_00{B[1][0] * r}; - const float g_0{1.0f + b_00}; - - nfc->gain = nfc->base_gain * g_0; - nfc->b1 = 2.0f * b_00 / g_0; -} - - -NfcFilter2 NfcFilterCreate2(const float w0, const float w1) noexcept -{ - NfcFilter2 nfc{}; - float b_10, b_11, g_1; - float r; - - nfc.base_gain = 1.0f; - nfc.gain = 1.0f; - - /* Calculate bass-boost coefficients. */ - r = 0.5f * w0; - b_10 = B[2][0] * r; - b_11 = B[2][1] * r * r; - g_1 = 1.0f + b_10 + b_11; - - nfc.gain *= g_1; - nfc.b1 = (2.0f*b_10 + 4.0f*b_11) / g_1; - nfc.b2 = 4.0f * b_11 / g_1; - - /* Calculate bass-cut coefficients. */ - r = 0.5f * w1; - b_10 = B[2][0] * r; - b_11 = B[2][1] * r * r; - g_1 = 1.0f + b_10 + b_11; - - nfc.base_gain /= g_1; - nfc.gain /= g_1; - nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1; - nfc.a2 = 4.0f * b_11 / g_1; - - return nfc; -} - -void NfcFilterAdjust2(NfcFilter2 *nfc, const float w0) noexcept -{ - const float r{0.5f * w0}; - const float b_10{B[2][0] * r}; - const float b_11{B[2][1] * r * r}; - const float g_1{1.0f + b_10 + b_11}; - - nfc->gain = nfc->base_gain * g_1; - nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1; - nfc->b2 = 4.0f * b_11 / g_1; -} - - -NfcFilter3 NfcFilterCreate3(const float w0, const float w1) noexcept -{ - NfcFilter3 nfc{}; - float b_10, b_11, g_1; - float b_00, g_0; - float r; - - nfc.base_gain = 1.0f; - nfc.gain = 1.0f; - - /* Calculate bass-boost coefficients. */ - r = 0.5f * w0; - b_10 = B[3][0] * r; - b_11 = B[3][1] * r * r; - b_00 = B[3][2] * r; - g_1 = 1.0f + b_10 + b_11; - g_0 = 1.0f + b_00; - - nfc.gain *= g_1 * g_0; - nfc.b1 = (2.0f*b_10 + 4.0f*b_11) / g_1; - nfc.b2 = 4.0f * b_11 / g_1; - nfc.b3 = 2.0f * b_00 / g_0; - - /* Calculate bass-cut coefficients. */ - r = 0.5f * w1; - b_10 = B[3][0] * r; - b_11 = B[3][1] * r * r; - b_00 = B[3][2] * r; - g_1 = 1.0f + b_10 + b_11; - g_0 = 1.0f + b_00; - - nfc.base_gain /= g_1 * g_0; - nfc.gain /= g_1 * g_0; - nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1; - nfc.a2 = 4.0f * b_11 / g_1; - nfc.a3 = 2.0f * b_00 / g_0; - - return nfc; -} - -void NfcFilterAdjust3(NfcFilter3 *nfc, const float w0) noexcept -{ - const float r{0.5f * w0}; - const float b_10{B[3][0] * r}; - const float b_11{B[3][1] * r * r}; - const float b_00{B[3][2] * r}; - const float g_1{1.0f + b_10 + b_11}; - const float g_0{1.0f + b_00}; - - nfc->gain = nfc->base_gain * g_1 * g_0; - nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1; - nfc->b2 = 4.0f * b_11 / g_1; - nfc->b3 = 2.0f * b_00 / g_0; -} - - -NfcFilter4 NfcFilterCreate4(const float w0, const float w1) noexcept -{ - NfcFilter4 nfc{}; - float b_10, b_11, g_1; - float b_00, b_01, g_0; - float r; - - nfc.base_gain = 1.0f; - nfc.gain = 1.0f; - - /* Calculate bass-boost coefficients. */ - r = 0.5f * w0; - b_10 = B[4][0] * r; - b_11 = B[4][1] * r * r; - b_00 = B[4][2] * r; - b_01 = B[4][3] * r * r; - g_1 = 1.0f + b_10 + b_11; - g_0 = 1.0f + b_00 + b_01; - - nfc.gain *= g_1 * g_0; - nfc.b1 = (2.0f*b_10 + 4.0f*b_11) / g_1; - nfc.b2 = 4.0f * b_11 / g_1; - nfc.b3 = (2.0f*b_00 + 4.0f*b_01) / g_0; - nfc.b4 = 4.0f * b_01 / g_0; - - /* Calculate bass-cut coefficients. */ - r = 0.5f * w1; - b_10 = B[4][0] * r; - b_11 = B[4][1] * r * r; - b_00 = B[4][2] * r; - b_01 = B[4][3] * r * r; - g_1 = 1.0f + b_10 + b_11; - g_0 = 1.0f + b_00 + b_01; - - nfc.base_gain /= g_1 * g_0; - nfc.gain /= g_1 * g_0; - nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1; - nfc.a2 = 4.0f * b_11 / g_1; - nfc.a3 = (2.0f*b_00 + 4.0f*b_01) / g_0; - nfc.a4 = 4.0f * b_01 / g_0; - - return nfc; -} - -void NfcFilterAdjust4(NfcFilter4 *nfc, const float w0) noexcept -{ - const float r{0.5f * w0}; - const float b_10{B[4][0] * r}; - const float b_11{B[4][1] * r * r}; - const float b_00{B[4][2] * r}; - const float b_01{B[4][3] * r * r}; - const float g_1{1.0f + b_10 + b_11}; - const float g_0{1.0f + b_00 + b_01}; - - nfc->gain = nfc->base_gain * g_1 * g_0; - nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1; - nfc->b2 = 4.0f * b_11 / g_1; - nfc->b3 = (2.0f*b_00 + 4.0f*b_01) / g_0; - nfc->b4 = 4.0f * b_01 / g_0; -} - -} // namespace - -void NfcFilter::init(const float w1) noexcept -{ - first = NfcFilterCreate1(0.0f, w1); - second = NfcFilterCreate2(0.0f, w1); - third = NfcFilterCreate3(0.0f, w1); - fourth = NfcFilterCreate4(0.0f, w1); -} - -void NfcFilter::adjust(const float w0) noexcept -{ - NfcFilterAdjust1(&first, w0); - NfcFilterAdjust2(&second, w0); - NfcFilterAdjust3(&third, w0); - NfcFilterAdjust4(&fourth, w0); -} - - -void NfcFilter::process1(const al::span src, float *RESTRICT dst) -{ - const float gain{first.gain}; - const float b1{first.b1}; - const float a1{first.a1}; - float z1{first.z[0]}; - auto proc_sample = [gain,b1,a1,&z1](const float in) noexcept -> float - { - const float y{in*gain - a1*z1}; - const float out{y + b1*z1}; - z1 += y; - return out; - }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); - first.z[0] = z1; -} - -void NfcFilter::process2(const al::span src, float *RESTRICT dst) -{ - const float gain{second.gain}; - const float b1{second.b1}; - const float b2{second.b2}; - const float a1{second.a1}; - const float a2{second.a2}; - float z1{second.z[0]}; - float z2{second.z[1]}; - auto proc_sample = [gain,b1,b2,a1,a2,&z1,&z2](const float in) noexcept -> float - { - const float y{in*gain - a1*z1 - a2*z2}; - const float out{y + b1*z1 + b2*z2}; - z2 += z1; - z1 += y; - return out; - }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); - second.z[0] = z1; - second.z[1] = z2; -} - -void NfcFilter::process3(const al::span src, float *RESTRICT dst) -{ - const float gain{third.gain}; - const float b1{third.b1}; - const float b2{third.b2}; - const float b3{third.b3}; - const float a1{third.a1}; - const float a2{third.a2}; - const float a3{third.a3}; - float z1{third.z[0]}; - float z2{third.z[1]}; - float z3{third.z[2]}; - auto proc_sample = [gain,b1,b2,b3,a1,a2,a3,&z1,&z2,&z3](const float in) noexcept -> float - { - float y{in*gain - a1*z1 - a2*z2}; - float out{y + b1*z1 + b2*z2}; - z2 += z1; - z1 += y; - - y = out - a3*z3; - out = y + b3*z3; - z3 += y; - return out; - }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); - third.z[0] = z1; - third.z[1] = z2; - third.z[2] = z3; -} - -void NfcFilter::process4(const al::span src, float *RESTRICT dst) -{ - const float gain{fourth.gain}; - const float b1{fourth.b1}; - const float b2{fourth.b2}; - const float b3{fourth.b3}; - const float b4{fourth.b4}; - const float a1{fourth.a1}; - const float a2{fourth.a2}; - const float a3{fourth.a3}; - const float a4{fourth.a4}; - float z1{fourth.z[0]}; - float z2{fourth.z[1]}; - float z3{fourth.z[2]}; - float z4{fourth.z[3]}; - auto proc_sample = [gain,b1,b2,b3,b4,a1,a2,a3,a4,&z1,&z2,&z3,&z4](const float in) noexcept -> float - { - float y{in*gain - a1*z1 - a2*z2}; - float out{y + b1*z1 + b2*z2}; - z2 += z1; - z1 += y; - - y = out - a3*z3 - a4*z4; - out = y + b3*z3 + b4*z4; - z4 += z3; - z3 += y; - return out; - }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); - fourth.z[0] = z1; - fourth.z[1] = z2; - fourth.z[2] = z3; - fourth.z[3] = z4; -} diff --git a/alc/filters/nfc.h b/alc/filters/nfc.h deleted file mode 100644 index 2327c966..00000000 --- a/alc/filters/nfc.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef FILTER_NFC_H -#define FILTER_NFC_H - -#include - -#include "alspan.h" - - -struct NfcFilter1 { - float base_gain, gain; - float b1, a1; - float z[1]; -}; -struct NfcFilter2 { - float base_gain, gain; - float b1, b2, a1, a2; - float z[2]; -}; -struct NfcFilter3 { - float base_gain, gain; - float b1, b2, b3, a1, a2, a3; - float z[3]; -}; -struct NfcFilter4 { - float base_gain, gain; - float b1, b2, b3, b4, a1, a2, a3, a4; - float z[4]; -}; - -class NfcFilter { - NfcFilter1 first; - NfcFilter2 second; - NfcFilter3 third; - NfcFilter4 fourth; - -public: - /* NOTE: - * w0 = speed_of_sound / (source_distance * sample_rate); - * w1 = speed_of_sound / (control_distance * sample_rate); - * - * Generally speaking, the control distance should be approximately the - * average speaker distance, or based on the reference delay if outputing - * NFC-HOA. It must not be negative, 0, or infinite. The source distance - * should not be too small relative to the control distance. - */ - - void init(const float w1) noexcept; - void adjust(const float w0) noexcept; - - /* Near-field control filter for first-order ambisonic channels (1-3). */ - void process1(const al::span src, float *RESTRICT dst); - - /* Near-field control filter for second-order ambisonic channels (4-8). */ - void process2(const al::span src, float *RESTRICT dst); - - /* Near-field control filter for third-order ambisonic channels (9-15). */ - void process3(const al::span src, float *RESTRICT dst); - - /* Near-field control filter for fourth-order ambisonic channels (16-24). */ - void process4(const al::span src, float *RESTRICT dst); -}; - -#endif /* FILTER_NFC_H */ diff --git a/alc/filters/splitter.cpp b/alc/filters/splitter.cpp deleted file mode 100644 index 5cc670b7..00000000 --- a/alc/filters/splitter.cpp +++ /dev/null @@ -1,113 +0,0 @@ - -#include "config.h" - -#include "splitter.h" - -#include -#include -#include - -#include "math_defs.h" -#include "opthelpers.h" - - -template -void BandSplitterR::init(Real f0norm) -{ - const Real w{f0norm * al::MathDefs::Tau()}; - const Real cw{std::cos(w)}; - if(cw > std::numeric_limits::epsilon()) - mCoeff = (std::sin(w) - 1.0f) / cw; - else - mCoeff = cw * -0.5f; - - mLpZ1 = 0.0f; - mLpZ2 = 0.0f; - mApZ1 = 0.0f; -} - -template -void BandSplitterR::process(const al::span input, Real *hpout, Real *lpout) -{ - const Real ap_coeff{mCoeff}; - const Real lp_coeff{mCoeff*0.5f + 0.5f}; - Real lp_z1{mLpZ1}; - Real lp_z2{mLpZ2}; - Real ap_z1{mApZ1}; - auto proc_sample = [ap_coeff,lp_coeff,&lp_z1,&lp_z2,&ap_z1,&lpout](const Real in) noexcept -> Real - { - /* Low-pass sample processing. */ - Real d{(in - lp_z1) * lp_coeff}; - Real lp_y{lp_z1 + d}; - lp_z1 = lp_y + d; - - d = (lp_y - lp_z2) * lp_coeff; - lp_y = lp_z2 + d; - lp_z2 = lp_y + d; - - *(lpout++) = lp_y; - - /* All-pass sample processing. */ - Real ap_y{in*ap_coeff + ap_z1}; - ap_z1 = in - ap_y*ap_coeff; - - /* High-pass generated from removing low-passed output. */ - return ap_y - lp_y; - }; - std::transform(input.cbegin(), input.cend(), hpout, proc_sample); - mLpZ1 = lp_z1; - mLpZ2 = lp_z2; - mApZ1 = ap_z1; -} - -template -void BandSplitterR::processHfScale(const al::span samples, const Real hfscale) -{ - const Real ap_coeff{mCoeff}; - const Real lp_coeff{mCoeff*0.5f + 0.5f}; - Real lp_z1{mLpZ1}; - Real lp_z2{mLpZ2}; - Real ap_z1{mApZ1}; - auto proc_sample = [hfscale,ap_coeff,lp_coeff,&lp_z1,&lp_z2,&ap_z1](const Real in) noexcept -> Real - { - /* Low-pass sample processing. */ - Real d{(in - lp_z1) * lp_coeff}; - Real lp_y{lp_z1 + d}; - lp_z1 = lp_y + d; - - d = (lp_y - lp_z2) * lp_coeff; - lp_y = lp_z2 + d; - lp_z2 = lp_y + d; - - /* All-pass sample processing. */ - Real ap_y{in*ap_coeff + ap_z1}; - ap_z1 = in - ap_y*ap_coeff; - - /* High-pass generated by removing the low-passed signal, which is then - * scaled and added back to the low-passed signal. - */ - return (ap_y-lp_y)*hfscale + lp_y; - }; - std::transform(samples.begin(), samples.end(), samples.begin(), proc_sample); - mLpZ1 = lp_z1; - mLpZ2 = lp_z2; - mApZ1 = ap_z1; -} - -template -void BandSplitterR::applyAllpass(const al::span samples) const -{ - const Real coeff{mCoeff}; - Real z1{0.0f}; - auto proc_sample = [coeff,&z1](const Real in) noexcept -> Real - { - const Real out{in*coeff + z1}; - z1 = in - out*coeff; - return out; - }; - std::transform(samples.begin(), samples.end(), samples.begin(), proc_sample); -} - - -template class BandSplitterR; -template class BandSplitterR; diff --git a/alc/filters/splitter.h b/alc/filters/splitter.h deleted file mode 100644 index 18ab4998..00000000 --- a/alc/filters/splitter.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef FILTER_SPLITTER_H -#define FILTER_SPLITTER_H - -#include - -#include "alspan.h" - - -/* Band splitter. Splits a signal into two phase-matching frequency bands. */ -template -class BandSplitterR { - Real mCoeff{0.0f}; - Real mLpZ1{0.0f}; - Real mLpZ2{0.0f}; - Real mApZ1{0.0f}; - -public: - BandSplitterR() = default; - BandSplitterR(const BandSplitterR&) = default; - BandSplitterR(Real f0norm) { init(f0norm); } - - void init(Real f0norm); - void clear() noexcept { mLpZ1 = mLpZ2 = mApZ1 = 0.0f; } - void process(const al::span input, Real *hpout, Real *lpout); - - void processHfScale(const al::span samples, const Real hfscale); - - /* The all-pass portion of the band splitter. Applies the same phase shift - * without splitting the signal. Note that each use of this method is - * indepedent, it does not track history between calls. - */ - void applyAllpass(const al::span samples) const; -}; -using BandSplitter = BandSplitterR; - -#endif /* FILTER_SPLITTER_H */ diff --git a/alc/front_stablizer.h b/alc/front_stablizer.h index 94882835..0fedeb50 100644 --- a/alc/front_stablizer.h +++ b/alc/front_stablizer.h @@ -6,7 +6,7 @@ #include "almalloc.h" #include "core/bufferline.h" -#include "filters/splitter.h" +#include "core/filters/splitter.h" struct FrontStablizer { diff --git a/alc/hrtf.cpp b/alc/hrtf.cpp index 12cea416..01dc342f 100644 --- a/alc/hrtf.cpp +++ b/alc/hrtf.cpp @@ -47,7 +47,7 @@ #include "alnumeric.h" #include "aloptional.h" #include "alspan.h" -#include "filters/splitter.h" +#include "core/filters/splitter.h" #include "logging.h" #include "math_defs.h" #include "opthelpers.h" diff --git a/alc/hrtf.h b/alc/hrtf.h index 3b9a272c..c26947de 100644 --- a/alc/hrtf.h +++ b/alc/hrtf.h @@ -11,7 +11,7 @@ #include "ambidefs.h" #include "atomic.h" #include "core/bufferline.h" -#include "filters/splitter.h" +#include "core/filters/splitter.h" #include "intrusive_ptr.h" #include "vector.h" diff --git a/alc/voice.cpp b/alc/voice.cpp index 54775a72..8b5cd02f 100644 --- a/alc/voice.cpp +++ b/alc/voice.cpp @@ -47,10 +47,10 @@ #include "alstring.h" #include "alu.h" #include "core/devformat.h" +#include "core/filters/biquad.h" +#include "core/filters/nfc.h" +#include "core/filters/splitter.h" #include "cpu_caps.h" -#include "filters/biquad.h" -#include "filters/nfc.h" -#include "filters/splitter.h" #include "fmt_traits.h" #include "hrtf.h" #include "inprogext.h" diff --git a/alc/voice.h b/alc/voice.h index 8a7d12d1..00012de3 100644 --- a/alc/voice.h +++ b/alc/voice.h @@ -8,9 +8,9 @@ #include "alu.h" #include "buffer_storage.h" #include "core/devformat.h" -#include "filters/biquad.h" -#include "filters/nfc.h" -#include "filters/splitter.h" +#include "core/filters/biquad.h" +#include "core/filters/nfc.h" +#include "core/filters/splitter.h" #include "hrtf.h" #include "mixer/defs.h" diff --git a/core/filters/biquad.cpp b/core/filters/biquad.cpp new file mode 100644 index 00000000..fefdc8e1 --- /dev/null +++ b/core/filters/biquad.cpp @@ -0,0 +1,163 @@ + +#include "config.h" + +#include "biquad.h" + +#include +#include +#include + +#include "opthelpers.h" + + +template +void BiquadFilterR::setParams(BiquadType type, Real f0norm, Real gain, Real rcpQ) +{ + // Limit gain to -100dB + assert(gain > 0.00001f); + + const Real w0{al::MathDefs::Tau() * f0norm}; + const Real sin_w0{std::sin(w0)}; + const Real cos_w0{std::cos(w0)}; + const Real alpha{sin_w0/2.0f * rcpQ}; + + Real sqrtgain_alpha_2; + Real a[3]{ 1.0f, 0.0f, 0.0f }; + Real b[3]{ 1.0f, 0.0f, 0.0f }; + + /* Calculate filter coefficients depending on filter type */ + switch(type) + { + case BiquadType::HighShelf: + sqrtgain_alpha_2 = 2.0f * std::sqrt(gain) * alpha; + b[0] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2); + b[1] = -2.0f*gain*((gain-1.0f) + (gain+1.0f)*cos_w0 ); + b[2] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2); + a[0] = (gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2; + a[1] = 2.0f* ((gain-1.0f) - (gain+1.0f)*cos_w0 ); + a[2] = (gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2; + break; + case BiquadType::LowShelf: + sqrtgain_alpha_2 = 2.0f * std::sqrt(gain) * alpha; + b[0] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2); + b[1] = 2.0f*gain*((gain-1.0f) - (gain+1.0f)*cos_w0 ); + b[2] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2); + a[0] = (gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2; + a[1] = -2.0f* ((gain-1.0f) + (gain+1.0f)*cos_w0 ); + a[2] = (gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2; + break; + case BiquadType::Peaking: + b[0] = 1.0f + alpha * gain; + b[1] = -2.0f * cos_w0; + b[2] = 1.0f - alpha * gain; + a[0] = 1.0f + alpha / gain; + a[1] = -2.0f * cos_w0; + a[2] = 1.0f - alpha / gain; + break; + + case BiquadType::LowPass: + b[0] = (1.0f - cos_w0) / 2.0f; + b[1] = 1.0f - cos_w0; + b[2] = (1.0f - cos_w0) / 2.0f; + a[0] = 1.0f + alpha; + a[1] = -2.0f * cos_w0; + a[2] = 1.0f - alpha; + break; + case BiquadType::HighPass: + b[0] = (1.0f + cos_w0) / 2.0f; + b[1] = -(1.0f + cos_w0); + b[2] = (1.0f + cos_w0) / 2.0f; + a[0] = 1.0f + alpha; + a[1] = -2.0f * cos_w0; + a[2] = 1.0f - alpha; + break; + case BiquadType::BandPass: + b[0] = alpha; + b[1] = 0.0f; + b[2] = -alpha; + a[0] = 1.0f + alpha; + a[1] = -2.0f * cos_w0; + a[2] = 1.0f - alpha; + break; + } + + mA1 = a[1] / a[0]; + mA2 = a[2] / a[0]; + mB0 = b[0] / a[0]; + mB1 = b[1] / a[0]; + mB2 = b[2] / a[0]; +} + +template +void BiquadFilterR::process(const al::span src, Real *dst) +{ + const Real b0{mB0}; + const Real b1{mB1}; + const Real b2{mB2}; + const Real a1{mA1}; + const Real a2{mA2}; + Real z1{mZ1}; + Real z2{mZ2}; + + /* Processing loop is Transposed Direct Form II. This requires less storage + * compared to Direct Form I (only two delay components, instead of a four- + * sample history; the last two inputs and outputs), and works better for + * floating-point which favors summing similarly-sized values while being + * less bothered by overflow. + * + * See: http://www.earlevel.com/main/2003/02/28/biquads/ + */ + auto proc_sample = [b0,b1,b2,a1,a2,&z1,&z2](Real input) noexcept -> Real + { + const Real output{input*b0 + z1}; + z1 = input*b1 - output*a1 + z2; + z2 = input*b2 - output*a2; + return output; + }; + std::transform(src.cbegin(), src.cend(), dst, proc_sample); + + mZ1 = z1; + mZ2 = z2; +} + +template +void BiquadFilterR::dualProcess(BiquadFilterR &other, const al::span src, + Real *dst) +{ + const Real b00{mB0}; + const Real b01{mB1}; + const Real b02{mB2}; + const Real a01{mA1}; + const Real a02{mA2}; + const Real b10{other.mB0}; + const Real b11{other.mB1}; + const Real b12{other.mB2}; + const Real a11{other.mA1}; + const Real a12{other.mA2}; + Real z01{mZ1}; + Real z02{mZ2}; + Real z11{other.mZ1}; + Real z12{other.mZ2}; + + auto proc_sample = [b00,b01,b02,a01,a02,b10,b11,b12,a11,a12,&z01,&z02,&z11,&z12](Real input) noexcept -> Real + { + const Real tmpout{input*b00 + z01}; + z01 = input*b01 - tmpout*a01 + z02; + z02 = input*b02 - tmpout*a02; + input = tmpout; + + const Real output{input*b10 + z11}; + z11 = input*b11 - output*a11 + z12; + z12 = input*b12 - output*a12; + return output; + }; + std::transform(src.cbegin(), src.cend(), dst, proc_sample); + + mZ1 = z01; + mZ2 = z02; + other.mZ1 = z11; + other.mZ2 = z12; +} + +template class BiquadFilterR; +template class BiquadFilterR; diff --git a/core/filters/biquad.h b/core/filters/biquad.h new file mode 100644 index 00000000..b2e2cfdb --- /dev/null +++ b/core/filters/biquad.h @@ -0,0 +1,144 @@ +#ifndef CORE_FILTERS_BIQUAD_H +#define CORE_FILTERS_BIQUAD_H + +#include +#include +#include +#include + +#include "alspan.h" +#include "math_defs.h" + + +/* Filters implementation is based on the "Cookbook formulae for audio + * EQ biquad filter coefficients" by Robert Bristow-Johnson + * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt + */ +/* Implementation note: For the shelf and peaking filters, the specified gain + * is for the centerpoint of the transition band. This better fits EFX filter + * behavior, which expects the shelf's reference frequency to reach the given + * gain. To set the gain for the shelf or peak itself, use the square root of + * the desired linear gain (or halve the dB gain). + */ + +enum class BiquadType { + /** EFX-style low-pass filter, specifying a gain and reference frequency. */ + HighShelf, + /** EFX-style high-pass filter, specifying a gain and reference frequency. */ + LowShelf, + /** Peaking filter, specifying a gain and reference frequency. */ + Peaking, + + /** Low-pass cut-off filter, specifying a cut-off frequency. */ + LowPass, + /** High-pass cut-off filter, specifying a cut-off frequency. */ + HighPass, + /** Band-pass filter, specifying a center frequency. */ + BandPass, +}; + +template +class BiquadFilterR { + /* Last two delayed components for direct form II. */ + Real mZ1{0.0f}, mZ2{0.0f}; + /* Transfer function coefficients "b" (numerator) */ + Real mB0{1.0f}, mB1{0.0f}, mB2{0.0f}; + /* Transfer function coefficients "a" (denominator; a0 is pre-applied). */ + Real mA1{0.0f}, mA2{0.0f}; + + void setParams(BiquadType type, Real f0norm, Real gain, Real rcpQ); + + /** + * Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using + * the reference gain and shelf slope parameter. + * \param gain 0 < gain + * \param slope 0 < slope <= 1 + */ + static Real rcpQFromSlope(Real gain, Real slope) + { return std::sqrt((gain + 1.0f/gain)*(1.0f/slope - 1.0f) + 2.0f); } + + /** + * Calculates the rcpQ (i.e. 1/Q) coefficient for filters, using the + * normalized reference frequency and bandwidth. + * \param f0norm 0 < f0norm < 0.5. + * \param bandwidth 0 < bandwidth + */ + static Real rcpQFromBandwidth(Real f0norm, Real bandwidth) + { + const Real w0{al::MathDefs::Tau() * f0norm}; + return 2.0f*std::sinh(std::log(Real{2.0f})/2.0f*bandwidth*w0/std::sin(w0)); + } + +public: + void clear() noexcept { mZ1 = mZ2 = 0.0f; } + + /** + * Sets the filter state for the specified filter type and its parameters. + * + * \param type The type of filter to apply. + * \param f0norm The normalized reference frequency (ref / sample_rate). + * This is the center point for the Shelf, Peaking, and BandPass filter + * types, or the cutoff frequency for the LowPass and HighPass filter + * types. + * \param gain The gain for the reference frequency response. Only used by + * the Shelf and Peaking filter types. + * \param slope Slope steepness of the transition band. + */ + void setParamsFromSlope(BiquadType type, Real f0norm, Real gain, Real slope) + { + gain = std::max(gain, 0.001f); /* Limit -60dB */ + setParams(type, f0norm, gain, rcpQFromSlope(gain, slope)); + } + + /** + * Sets the filter state for the specified filter type and its parameters. + * + * \param type The type of filter to apply. + * \param f0norm The normalized reference frequency (ref / sample_rate). + * This is the center point for the Shelf, Peaking, and BandPass filter + * types, or the cutoff frequency for the LowPass and HighPass filter + * types. + * \param gain The gain for the reference frequency response. Only used by + * the Shelf and Peaking filter types. + * \param bandwidth Normalized bandwidth of the transition band. + */ + void setParamsFromBandwidth(BiquadType type, Real f0norm, Real gain, Real bandwidth) + { setParams(type, f0norm, gain, rcpQFromBandwidth(f0norm, bandwidth)); } + + void copyParamsFrom(const BiquadFilterR &other) + { + mB0 = other.mB0; + mB1 = other.mB1; + mB2 = other.mB2; + mA1 = other.mA1; + mA2 = other.mA2; + } + + void process(const al::span src, Real *dst); + /** Processes this filter and the other at the same time. */ + void dualProcess(BiquadFilterR &other, const al::span src, Real *dst); + + /* Rather hacky. It's just here to support "manual" processing. */ + std::pair getComponents() const noexcept { return {mZ1, mZ2}; } + void setComponents(Real z1, Real z2) noexcept { mZ1 = z1; mZ2 = z2; } + Real processOne(const Real in, Real &z1, Real &z2) const noexcept + { + const Real out{in*mB0 + z1}; + z1 = in*mB1 - out*mA1 + z2; + z2 = in*mB2 - out*mA2; + return out; + } +}; + +template +struct DualBiquadR { + BiquadFilterR &f0, &f1; + + void process(const al::span src, Real *dst) + { f0.dualProcess(f1, src, dst); } +}; + +using BiquadFilter = BiquadFilterR; +using DualBiquad = DualBiquadR; + +#endif /* CORE_FILTERS_BIQUAD_H */ diff --git a/core/filters/nfc.cpp b/core/filters/nfc.cpp new file mode 100644 index 00000000..9a28517c --- /dev/null +++ b/core/filters/nfc.cpp @@ -0,0 +1,383 @@ + +#include "config.h" + +#include "nfc.h" + +#include + +#include "opthelpers.h" + + +/* Near-field control filters are the basis for handling the near-field effect. + * The near-field effect is a bass-boost present in the directional components + * of a recorded signal, created as a result of the wavefront curvature (itself + * a function of sound distance). Proper reproduction dictates this be + * compensated for using a bass-cut given the playback speaker distance, to + * avoid excessive bass in the playback. + * + * For real-time rendered audio, emulating the near-field effect based on the + * sound source's distance, and subsequently compensating for it at output + * based on the speaker distances, can create a more realistic perception of + * sound distance beyond a simple 1/r attenuation. + * + * These filters do just that. Each one applies a low-shelf filter, created as + * the combination of a bass-boost for a given sound source distance (near- + * field emulation) along with a bass-cut for a given control/speaker distance + * (near-field compensation). + * + * Note that it is necessary to apply a cut along with the boost, since the + * boost alone is unstable in higher-order ambisonics as it causes an infinite + * DC gain (even first-order ambisonics requires there to be no DC offset for + * the boost to work). Consequently, ambisonics requires a control parameter to + * be used to avoid an unstable boost-only filter. NFC-HOA defines this control + * as a reference delay, calculated with: + * + * reference_delay = control_distance / speed_of_sound + * + * This means w0 (for input) or w1 (for output) should be set to: + * + * wN = 1 / (reference_delay * sample_rate) + * + * when dealing with NFC-HOA content. For FOA input content, which does not + * specify a reference_delay variable, w0 should be set to 0 to apply only + * near-field compensation for output. It's important that w1 be a finite, + * positive, non-0 value or else the bass-boost will become unstable again. + * Also, w0 should not be too large compared to w1, to avoid excessively loud + * low frequencies. + */ + +namespace { + +constexpr float B[5][4] = { + { 0.0f }, + { 1.0f }, + { 3.0f, 3.0f }, + { 3.6778f, 6.4595f, 2.3222f }, + { 4.2076f, 11.4877f, 5.7924f, 9.1401f } +}; + +NfcFilter1 NfcFilterCreate1(const float w0, const float w1) noexcept +{ + NfcFilter1 nfc{}; + float b_00, g_0; + float r; + + nfc.base_gain = 1.0f; + nfc.gain = 1.0f; + + /* Calculate bass-boost coefficients. */ + r = 0.5f * w0; + b_00 = B[1][0] * r; + g_0 = 1.0f + b_00; + + nfc.gain *= g_0; + nfc.b1 = 2.0f * b_00 / g_0; + + /* Calculate bass-cut coefficients. */ + r = 0.5f * w1; + b_00 = B[1][0] * r; + g_0 = 1.0f + b_00; + + nfc.base_gain /= g_0; + nfc.gain /= g_0; + nfc.a1 = 2.0f * b_00 / g_0; + + return nfc; +} + +void NfcFilterAdjust1(NfcFilter1 *nfc, const float w0) noexcept +{ + const float r{0.5f * w0}; + const float b_00{B[1][0] * r}; + const float g_0{1.0f + b_00}; + + nfc->gain = nfc->base_gain * g_0; + nfc->b1 = 2.0f * b_00 / g_0; +} + + +NfcFilter2 NfcFilterCreate2(const float w0, const float w1) noexcept +{ + NfcFilter2 nfc{}; + float b_10, b_11, g_1; + float r; + + nfc.base_gain = 1.0f; + nfc.gain = 1.0f; + + /* Calculate bass-boost coefficients. */ + r = 0.5f * w0; + b_10 = B[2][0] * r; + b_11 = B[2][1] * r * r; + g_1 = 1.0f + b_10 + b_11; + + nfc.gain *= g_1; + nfc.b1 = (2.0f*b_10 + 4.0f*b_11) / g_1; + nfc.b2 = 4.0f * b_11 / g_1; + + /* Calculate bass-cut coefficients. */ + r = 0.5f * w1; + b_10 = B[2][0] * r; + b_11 = B[2][1] * r * r; + g_1 = 1.0f + b_10 + b_11; + + nfc.base_gain /= g_1; + nfc.gain /= g_1; + nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1; + nfc.a2 = 4.0f * b_11 / g_1; + + return nfc; +} + +void NfcFilterAdjust2(NfcFilter2 *nfc, const float w0) noexcept +{ + const float r{0.5f * w0}; + const float b_10{B[2][0] * r}; + const float b_11{B[2][1] * r * r}; + const float g_1{1.0f + b_10 + b_11}; + + nfc->gain = nfc->base_gain * g_1; + nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1; + nfc->b2 = 4.0f * b_11 / g_1; +} + + +NfcFilter3 NfcFilterCreate3(const float w0, const float w1) noexcept +{ + NfcFilter3 nfc{}; + float b_10, b_11, g_1; + float b_00, g_0; + float r; + + nfc.base_gain = 1.0f; + nfc.gain = 1.0f; + + /* Calculate bass-boost coefficients. */ + r = 0.5f * w0; + b_10 = B[3][0] * r; + b_11 = B[3][1] * r * r; + b_00 = B[3][2] * r; + g_1 = 1.0f + b_10 + b_11; + g_0 = 1.0f + b_00; + + nfc.gain *= g_1 * g_0; + nfc.b1 = (2.0f*b_10 + 4.0f*b_11) / g_1; + nfc.b2 = 4.0f * b_11 / g_1; + nfc.b3 = 2.0f * b_00 / g_0; + + /* Calculate bass-cut coefficients. */ + r = 0.5f * w1; + b_10 = B[3][0] * r; + b_11 = B[3][1] * r * r; + b_00 = B[3][2] * r; + g_1 = 1.0f + b_10 + b_11; + g_0 = 1.0f + b_00; + + nfc.base_gain /= g_1 * g_0; + nfc.gain /= g_1 * g_0; + nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1; + nfc.a2 = 4.0f * b_11 / g_1; + nfc.a3 = 2.0f * b_00 / g_0; + + return nfc; +} + +void NfcFilterAdjust3(NfcFilter3 *nfc, const float w0) noexcept +{ + const float r{0.5f * w0}; + const float b_10{B[3][0] * r}; + const float b_11{B[3][1] * r * r}; + const float b_00{B[3][2] * r}; + const float g_1{1.0f + b_10 + b_11}; + const float g_0{1.0f + b_00}; + + nfc->gain = nfc->base_gain * g_1 * g_0; + nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1; + nfc->b2 = 4.0f * b_11 / g_1; + nfc->b3 = 2.0f * b_00 / g_0; +} + + +NfcFilter4 NfcFilterCreate4(const float w0, const float w1) noexcept +{ + NfcFilter4 nfc{}; + float b_10, b_11, g_1; + float b_00, b_01, g_0; + float r; + + nfc.base_gain = 1.0f; + nfc.gain = 1.0f; + + /* Calculate bass-boost coefficients. */ + r = 0.5f * w0; + b_10 = B[4][0] * r; + b_11 = B[4][1] * r * r; + b_00 = B[4][2] * r; + b_01 = B[4][3] * r * r; + g_1 = 1.0f + b_10 + b_11; + g_0 = 1.0f + b_00 + b_01; + + nfc.gain *= g_1 * g_0; + nfc.b1 = (2.0f*b_10 + 4.0f*b_11) / g_1; + nfc.b2 = 4.0f * b_11 / g_1; + nfc.b3 = (2.0f*b_00 + 4.0f*b_01) / g_0; + nfc.b4 = 4.0f * b_01 / g_0; + + /* Calculate bass-cut coefficients. */ + r = 0.5f * w1; + b_10 = B[4][0] * r; + b_11 = B[4][1] * r * r; + b_00 = B[4][2] * r; + b_01 = B[4][3] * r * r; + g_1 = 1.0f + b_10 + b_11; + g_0 = 1.0f + b_00 + b_01; + + nfc.base_gain /= g_1 * g_0; + nfc.gain /= g_1 * g_0; + nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1; + nfc.a2 = 4.0f * b_11 / g_1; + nfc.a3 = (2.0f*b_00 + 4.0f*b_01) / g_0; + nfc.a4 = 4.0f * b_01 / g_0; + + return nfc; +} + +void NfcFilterAdjust4(NfcFilter4 *nfc, const float w0) noexcept +{ + const float r{0.5f * w0}; + const float b_10{B[4][0] * r}; + const float b_11{B[4][1] * r * r}; + const float b_00{B[4][2] * r}; + const float b_01{B[4][3] * r * r}; + const float g_1{1.0f + b_10 + b_11}; + const float g_0{1.0f + b_00 + b_01}; + + nfc->gain = nfc->base_gain * g_1 * g_0; + nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1; + nfc->b2 = 4.0f * b_11 / g_1; + nfc->b3 = (2.0f*b_00 + 4.0f*b_01) / g_0; + nfc->b4 = 4.0f * b_01 / g_0; +} + +} // namespace + +void NfcFilter::init(const float w1) noexcept +{ + first = NfcFilterCreate1(0.0f, w1); + second = NfcFilterCreate2(0.0f, w1); + third = NfcFilterCreate3(0.0f, w1); + fourth = NfcFilterCreate4(0.0f, w1); +} + +void NfcFilter::adjust(const float w0) noexcept +{ + NfcFilterAdjust1(&first, w0); + NfcFilterAdjust2(&second, w0); + NfcFilterAdjust3(&third, w0); + NfcFilterAdjust4(&fourth, w0); +} + + +void NfcFilter::process1(const al::span src, float *RESTRICT dst) +{ + const float gain{first.gain}; + const float b1{first.b1}; + const float a1{first.a1}; + float z1{first.z[0]}; + auto proc_sample = [gain,b1,a1,&z1](const float in) noexcept -> float + { + const float y{in*gain - a1*z1}; + const float out{y + b1*z1}; + z1 += y; + return out; + }; + std::transform(src.cbegin(), src.cend(), dst, proc_sample); + first.z[0] = z1; +} + +void NfcFilter::process2(const al::span src, float *RESTRICT dst) +{ + const float gain{second.gain}; + const float b1{second.b1}; + const float b2{second.b2}; + const float a1{second.a1}; + const float a2{second.a2}; + float z1{second.z[0]}; + float z2{second.z[1]}; + auto proc_sample = [gain,b1,b2,a1,a2,&z1,&z2](const float in) noexcept -> float + { + const float y{in*gain - a1*z1 - a2*z2}; + const float out{y + b1*z1 + b2*z2}; + z2 += z1; + z1 += y; + return out; + }; + std::transform(src.cbegin(), src.cend(), dst, proc_sample); + second.z[0] = z1; + second.z[1] = z2; +} + +void NfcFilter::process3(const al::span src, float *RESTRICT dst) +{ + const float gain{third.gain}; + const float b1{third.b1}; + const float b2{third.b2}; + const float b3{third.b3}; + const float a1{third.a1}; + const float a2{third.a2}; + const float a3{third.a3}; + float z1{third.z[0]}; + float z2{third.z[1]}; + float z3{third.z[2]}; + auto proc_sample = [gain,b1,b2,b3,a1,a2,a3,&z1,&z2,&z3](const float in) noexcept -> float + { + float y{in*gain - a1*z1 - a2*z2}; + float out{y + b1*z1 + b2*z2}; + z2 += z1; + z1 += y; + + y = out - a3*z3; + out = y + b3*z3; + z3 += y; + return out; + }; + std::transform(src.cbegin(), src.cend(), dst, proc_sample); + third.z[0] = z1; + third.z[1] = z2; + third.z[2] = z3; +} + +void NfcFilter::process4(const al::span src, float *RESTRICT dst) +{ + const float gain{fourth.gain}; + const float b1{fourth.b1}; + const float b2{fourth.b2}; + const float b3{fourth.b3}; + const float b4{fourth.b4}; + const float a1{fourth.a1}; + const float a2{fourth.a2}; + const float a3{fourth.a3}; + const float a4{fourth.a4}; + float z1{fourth.z[0]}; + float z2{fourth.z[1]}; + float z3{fourth.z[2]}; + float z4{fourth.z[3]}; + auto proc_sample = [gain,b1,b2,b3,b4,a1,a2,a3,a4,&z1,&z2,&z3,&z4](const float in) noexcept -> float + { + float y{in*gain - a1*z1 - a2*z2}; + float out{y + b1*z1 + b2*z2}; + z2 += z1; + z1 += y; + + y = out - a3*z3 - a4*z4; + out = y + b3*z3 + b4*z4; + z4 += z3; + z3 += y; + return out; + }; + std::transform(src.cbegin(), src.cend(), dst, proc_sample); + fourth.z[0] = z1; + fourth.z[1] = z2; + fourth.z[2] = z3; + fourth.z[3] = z4; +} diff --git a/core/filters/nfc.h b/core/filters/nfc.h new file mode 100644 index 00000000..33f67a5f --- /dev/null +++ b/core/filters/nfc.h @@ -0,0 +1,63 @@ +#ifndef CORE_FILTERS_NFC_H +#define CORE_FILTERS_NFC_H + +#include + +#include "alspan.h" + + +struct NfcFilter1 { + float base_gain, gain; + float b1, a1; + float z[1]; +}; +struct NfcFilter2 { + float base_gain, gain; + float b1, b2, a1, a2; + float z[2]; +}; +struct NfcFilter3 { + float base_gain, gain; + float b1, b2, b3, a1, a2, a3; + float z[3]; +}; +struct NfcFilter4 { + float base_gain, gain; + float b1, b2, b3, b4, a1, a2, a3, a4; + float z[4]; +}; + +class NfcFilter { + NfcFilter1 first; + NfcFilter2 second; + NfcFilter3 third; + NfcFilter4 fourth; + +public: + /* NOTE: + * w0 = speed_of_sound / (source_distance * sample_rate); + * w1 = speed_of_sound / (control_distance * sample_rate); + * + * Generally speaking, the control distance should be approximately the + * average speaker distance, or based on the reference delay if outputing + * NFC-HOA. It must not be negative, 0, or infinite. The source distance + * should not be too small relative to the control distance. + */ + + void init(const float w1) noexcept; + void adjust(const float w0) noexcept; + + /* Near-field control filter for first-order ambisonic channels (1-3). */ + void process1(const al::span src, float *RESTRICT dst); + + /* Near-field control filter for second-order ambisonic channels (4-8). */ + void process2(const al::span src, float *RESTRICT dst); + + /* Near-field control filter for third-order ambisonic channels (9-15). */ + void process3(const al::span src, float *RESTRICT dst); + + /* Near-field control filter for fourth-order ambisonic channels (16-24). */ + void process4(const al::span src, float *RESTRICT dst); +}; + +#endif /* CORE_FILTERS_NFC_H */ diff --git a/core/filters/splitter.cpp b/core/filters/splitter.cpp new file mode 100644 index 00000000..5cc670b7 --- /dev/null +++ b/core/filters/splitter.cpp @@ -0,0 +1,113 @@ + +#include "config.h" + +#include "splitter.h" + +#include +#include +#include + +#include "math_defs.h" +#include "opthelpers.h" + + +template +void BandSplitterR::init(Real f0norm) +{ + const Real w{f0norm * al::MathDefs::Tau()}; + const Real cw{std::cos(w)}; + if(cw > std::numeric_limits::epsilon()) + mCoeff = (std::sin(w) - 1.0f) / cw; + else + mCoeff = cw * -0.5f; + + mLpZ1 = 0.0f; + mLpZ2 = 0.0f; + mApZ1 = 0.0f; +} + +template +void BandSplitterR::process(const al::span input, Real *hpout, Real *lpout) +{ + const Real ap_coeff{mCoeff}; + const Real lp_coeff{mCoeff*0.5f + 0.5f}; + Real lp_z1{mLpZ1}; + Real lp_z2{mLpZ2}; + Real ap_z1{mApZ1}; + auto proc_sample = [ap_coeff,lp_coeff,&lp_z1,&lp_z2,&ap_z1,&lpout](const Real in) noexcept -> Real + { + /* Low-pass sample processing. */ + Real d{(in - lp_z1) * lp_coeff}; + Real lp_y{lp_z1 + d}; + lp_z1 = lp_y + d; + + d = (lp_y - lp_z2) * lp_coeff; + lp_y = lp_z2 + d; + lp_z2 = lp_y + d; + + *(lpout++) = lp_y; + + /* All-pass sample processing. */ + Real ap_y{in*ap_coeff + ap_z1}; + ap_z1 = in - ap_y*ap_coeff; + + /* High-pass generated from removing low-passed output. */ + return ap_y - lp_y; + }; + std::transform(input.cbegin(), input.cend(), hpout, proc_sample); + mLpZ1 = lp_z1; + mLpZ2 = lp_z2; + mApZ1 = ap_z1; +} + +template +void BandSplitterR::processHfScale(const al::span samples, const Real hfscale) +{ + const Real ap_coeff{mCoeff}; + const Real lp_coeff{mCoeff*0.5f + 0.5f}; + Real lp_z1{mLpZ1}; + Real lp_z2{mLpZ2}; + Real ap_z1{mApZ1}; + auto proc_sample = [hfscale,ap_coeff,lp_coeff,&lp_z1,&lp_z2,&ap_z1](const Real in) noexcept -> Real + { + /* Low-pass sample processing. */ + Real d{(in - lp_z1) * lp_coeff}; + Real lp_y{lp_z1 + d}; + lp_z1 = lp_y + d; + + d = (lp_y - lp_z2) * lp_coeff; + lp_y = lp_z2 + d; + lp_z2 = lp_y + d; + + /* All-pass sample processing. */ + Real ap_y{in*ap_coeff + ap_z1}; + ap_z1 = in - ap_y*ap_coeff; + + /* High-pass generated by removing the low-passed signal, which is then + * scaled and added back to the low-passed signal. + */ + return (ap_y-lp_y)*hfscale + lp_y; + }; + std::transform(samples.begin(), samples.end(), samples.begin(), proc_sample); + mLpZ1 = lp_z1; + mLpZ2 = lp_z2; + mApZ1 = ap_z1; +} + +template +void BandSplitterR::applyAllpass(const al::span samples) const +{ + const Real coeff{mCoeff}; + Real z1{0.0f}; + auto proc_sample = [coeff,&z1](const Real in) noexcept -> Real + { + const Real out{in*coeff + z1}; + z1 = in - out*coeff; + return out; + }; + std::transform(samples.begin(), samples.end(), samples.begin(), proc_sample); +} + + +template class BandSplitterR; +template class BandSplitterR; diff --git a/core/filters/splitter.h b/core/filters/splitter.h new file mode 100644 index 00000000..ba548c10 --- /dev/null +++ b/core/filters/splitter.h @@ -0,0 +1,36 @@ +#ifndef CORE_FILTERS_SPLITTER_H +#define CORE_FILTERS_SPLITTER_H + +#include + +#include "alspan.h" + + +/* Band splitter. Splits a signal into two phase-matching frequency bands. */ +template +class BandSplitterR { + Real mCoeff{0.0f}; + Real mLpZ1{0.0f}; + Real mLpZ2{0.0f}; + Real mApZ1{0.0f}; + +public: + BandSplitterR() = default; + BandSplitterR(const BandSplitterR&) = default; + BandSplitterR(Real f0norm) { init(f0norm); } + + void init(Real f0norm); + void clear() noexcept { mLpZ1 = mLpZ2 = mApZ1 = 0.0f; } + void process(const al::span input, Real *hpout, Real *lpout); + + void processHfScale(const al::span samples, const Real hfscale); + + /* The all-pass portion of the band splitter. Applies the same phase shift + * without splitting the signal. Note that each use of this method is + * indepedent, it does not track history between calls. + */ + void applyAllpass(const al::span samples) const; +}; +using BandSplitter = BandSplitterR; + +#endif /* CORE_FILTERS_SPLITTER_H */ -- cgit v1.2.3 From c4132b80ede60ead27fae595623ac61674ed166a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Dec 2020 13:13:52 -0800 Subject: Move a couple more things to core --- CMakeLists.txt | 8 +-- alc/alc.cpp | 4 +- alc/alu.cpp | 4 +- alc/ambidefs.h | 3 +- alc/bs2b.cpp | 183 ---------------------------------------------- alc/bs2b.h | 89 ----------------------- alc/panning.cpp | 4 +- alc/uhjfilter.cpp | 208 ----------------------------------------------------- alc/uhjfilter.h | 39 ---------- core/bs2b.cpp | 183 ++++++++++++++++++++++++++++++++++++++++++++++ core/bs2b.h | 89 +++++++++++++++++++++++ core/uhjfilter.cpp | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++++ core/uhjfilter.h | 39 ++++++++++ 13 files changed, 529 insertions(+), 530 deletions(-) delete mode 100644 alc/bs2b.cpp delete mode 100644 alc/bs2b.h delete mode 100644 alc/uhjfilter.cpp delete mode 100644 alc/uhjfilter.h create mode 100644 core/bs2b.cpp create mode 100644 core/bs2b.h create mode 100644 core/uhjfilter.cpp create mode 100644 core/uhjfilter.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c08af6b..d74c3f75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -627,6 +627,8 @@ set(OPENAL_OBJS al/state.cpp ) set(ALC_OBJS + core/bs2b.cpp + core/bs2b.h core/bsinc_defs.h core/bsinc_tables.cpp core/bsinc_tables.h @@ -641,6 +643,8 @@ set(ALC_OBJS core/filters/splitter.h core/mastering.cpp core/mastering.h + core/uhjfilter.cpp + core/uhjfilter.h alc/alc.cpp alc/alcmain.h @@ -654,8 +658,6 @@ set(ALC_OBJS alc/ambidefs.h alc/bformatdec.cpp alc/bformatdec.h - alc/bs2b.cpp - alc/bs2b.h alc/buffer_storage.cpp alc/buffer_storage.h alc/compat.h @@ -693,8 +695,6 @@ set(ALC_OBJS alc/panning.cpp alc/ringbuffer.cpp alc/ringbuffer.h - alc/uhjfilter.cpp - alc/uhjfilter.h alc/uiddefs.cpp alc/voice.cpp alc/voice.h diff --git a/alc/alc.cpp b/alc/alc.cpp index 87c0578e..00008a81 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -78,12 +78,13 @@ #include "ambidefs.h" #include "atomic.h" #include "bformatdec.h" -#include "bs2b.h" #include "compat.h" +#include "core/bs2b.h" #include "core/devformat.h" #include "core/mastering.h" #include "core/filters/nfc.h" #include "core/filters/splitter.h" +#include "core/uhjfilter.h" #include "cpu_caps.h" #include "effects/base.h" #include "fpu_ctrl.h" @@ -97,7 +98,6 @@ #include "ringbuffer.h" #include "strutils.h" #include "threads.h" -#include "uhjfilter.h" #include "vecmat.h" #include "vector.h" diff --git a/alc/alu.cpp b/alc/alu.cpp index c13365ec..62938b2a 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -58,13 +58,14 @@ #include "ambidefs.h" #include "atomic.h" #include "bformatdec.h" -#include "bs2b.h" +#include "core/bs2b.h" #include "core/bsinc_tables.h" #include "core/devformat.h" #include "core/filters/biquad.h" #include "core/filters/nfc.h" #include "core/filters/splitter.h" #include "core/mastering.h" +#include "core/uhjfilter.h" #include "cpu_caps.h" #include "effects/base.h" #include "fpu_ctrl.h" @@ -77,7 +78,6 @@ #include "ringbuffer.h" #include "strutils.h" #include "threads.h" -#include "uhjfilter.h" #include "vecmat.h" #include "voice.h" diff --git a/alc/ambidefs.h b/alc/ambidefs.h index 8d4b96ca..79c0f1cf 100644 --- a/alc/ambidefs.h +++ b/alc/ambidefs.h @@ -2,7 +2,8 @@ #define AMBIDEFS_H #include -#include +#include +#include /* The maximum number of Ambisonics channels. For a given order (o), the size * needed will be (o+1)**2, thus zero-order has 1, first-order has 4, second- diff --git a/alc/bs2b.cpp b/alc/bs2b.cpp deleted file mode 100644 index 00207bc0..00000000 --- a/alc/bs2b.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/*- - * Copyright (c) 2005 Boris Mikhaylov - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include - -#include "bs2b.h" -#include "math_defs.h" - - -/* Set up all data. */ -static void init(struct bs2b *bs2b) -{ - float Fc_lo, Fc_hi; - float G_lo, G_hi; - float x, g; - - switch(bs2b->level) - { - case BS2B_LOW_CLEVEL: /* Low crossfeed level */ - Fc_lo = 360.0f; - Fc_hi = 501.0f; - G_lo = 0.398107170553497f; - G_hi = 0.205671765275719f; - break; - - case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */ - Fc_lo = 500.0f; - Fc_hi = 711.0f; - G_lo = 0.459726988530872f; - G_hi = 0.228208484414988f; - break; - - case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */ - Fc_lo = 700.0f; - Fc_hi = 1021.0f; - G_lo = 0.530884444230988f; - G_hi = 0.250105790667544f; - break; - - case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */ - Fc_lo = 360.0f; - Fc_hi = 494.0f; - G_lo = 0.316227766016838f; - G_hi = 0.168236228897329f; - break; - - case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */ - Fc_lo = 500.0f; - Fc_hi = 689.0f; - G_lo = 0.354813389233575f; - G_hi = 0.187169483835901f; - break; - - default: /* High easy crossfeed level */ - bs2b->level = BS2B_HIGH_ECLEVEL; - - Fc_lo = 700.0f; - Fc_hi = 975.0f; - G_lo = 0.398107170553497f; - G_hi = 0.205671765275719f; - break; - } /* switch */ - - g = 1.0f / (1.0f - G_hi + G_lo); - - /* $fc = $Fc / $s; - * $d = 1 / 2 / pi / $fc; - * $x = exp(-1 / $d); - */ - x = std::exp(-al::MathDefs::Tau() * Fc_lo / static_cast(bs2b->srate)); - bs2b->b1_lo = x; - bs2b->a0_lo = G_lo * (1.0f - x) * g; - - x = std::exp(-al::MathDefs::Tau() * Fc_hi / static_cast(bs2b->srate)); - bs2b->b1_hi = x; - bs2b->a0_hi = (1.0f - G_hi * (1.0f - x)) * g; - bs2b->a1_hi = -x * g; -} /* init */ - - -/* Exported functions. - * See descriptions in "bs2b.h" - */ - -void bs2b_set_params(struct bs2b *bs2b, int level, int srate) -{ - if(srate <= 0) srate = 1; - - bs2b->level = level; - bs2b->srate = srate; - init(bs2b); -} /* bs2b_set_params */ - -int bs2b_get_level(struct bs2b *bs2b) -{ - return bs2b->level; -} /* bs2b_get_level */ - -int bs2b_get_srate(struct bs2b *bs2b) -{ - return bs2b->srate; -} /* bs2b_get_srate */ - -void bs2b_clear(struct bs2b *bs2b) -{ - std::fill(std::begin(bs2b->history), std::end(bs2b->history), bs2b::t_last_sample{}); -} /* bs2b_clear */ - -void bs2b_cross_feed(struct bs2b *bs2b, float *Left, float *Right, size_t SamplesToDo) -{ - const float a0_lo{bs2b->a0_lo}; - const float b1_lo{bs2b->b1_lo}; - const float a0_hi{bs2b->a0_hi}; - const float a1_hi{bs2b->a1_hi}; - const float b1_hi{bs2b->b1_hi}; - float lsamples[128][2]; - float rsamples[128][2]; - - for(size_t base{0};base < SamplesToDo;) - { - const size_t todo{std::min(128, SamplesToDo-base)}; - - /* Process left input */ - float z_lo{bs2b->history[0].lo}; - float z_hi{bs2b->history[0].hi}; - for(size_t i{0};i < todo;i++) - { - lsamples[i][0] = a0_lo*Left[i] + z_lo; - z_lo = b1_lo*lsamples[i][0]; - - lsamples[i][1] = a0_hi*Left[i] + z_hi; - z_hi = a1_hi*Left[i] + b1_hi*lsamples[i][1]; - } - bs2b->history[0].lo = z_lo; - bs2b->history[0].hi = z_hi; - - /* Process right input */ - z_lo = bs2b->history[1].lo; - z_hi = bs2b->history[1].hi; - for(size_t i{0};i < todo;i++) - { - rsamples[i][0] = a0_lo*Right[i] + z_lo; - z_lo = b1_lo*rsamples[i][0]; - - rsamples[i][1] = a0_hi*Right[i] + z_hi; - z_hi = a1_hi*Right[i] + b1_hi*rsamples[i][1]; - } - bs2b->history[1].lo = z_lo; - bs2b->history[1].hi = z_hi; - - /* Crossfeed */ - for(size_t i{0};i < todo;i++) - *(Left++) = lsamples[i][1] + rsamples[i][0]; - for(size_t i{0};i < todo;i++) - *(Right++) = rsamples[i][1] + lsamples[i][0]; - - base += todo; - } -} /* bs2b_cross_feed */ diff --git a/alc/bs2b.h b/alc/bs2b.h deleted file mode 100644 index df717cd4..00000000 --- a/alc/bs2b.h +++ /dev/null @@ -1,89 +0,0 @@ -/*- - * Copyright (c) 2005 Boris Mikhaylov - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef BS2B_H -#define BS2B_H - -#include "almalloc.h" - -/* Number of crossfeed levels */ -#define BS2B_CLEVELS 3 - -/* Normal crossfeed levels */ -#define BS2B_HIGH_CLEVEL 3 -#define BS2B_MIDDLE_CLEVEL 2 -#define BS2B_LOW_CLEVEL 1 - -/* Easy crossfeed levels */ -#define BS2B_HIGH_ECLEVEL BS2B_HIGH_CLEVEL + BS2B_CLEVELS -#define BS2B_MIDDLE_ECLEVEL BS2B_MIDDLE_CLEVEL + BS2B_CLEVELS -#define BS2B_LOW_ECLEVEL BS2B_LOW_CLEVEL + BS2B_CLEVELS - -/* Default crossfeed levels */ -#define BS2B_DEFAULT_CLEVEL BS2B_HIGH_ECLEVEL -/* Default sample rate (Hz) */ -#define BS2B_DEFAULT_SRATE 44100 - -struct bs2b { - int level; /* Crossfeed level */ - int srate; /* Sample rate (Hz) */ - - /* Lowpass IIR filter coefficients */ - float a0_lo; - float b1_lo; - - /* Highboost IIR filter coefficients */ - float a0_hi; - float a1_hi; - float b1_hi; - - /* Buffer of filter history - * [0] - first channel, [1] - second channel - */ - struct t_last_sample { - float lo; - float hi; - } history[2]; - - DEF_NEWDEL(bs2b) -}; - -/* Clear buffers and set new coefficients with new crossfeed level and sample - * rate values. - * level - crossfeed level of *LEVEL values. - * srate - sample rate by Hz. - */ -void bs2b_set_params(bs2b *bs2b, int level, int srate); - -/* Return current crossfeed level value */ -int bs2b_get_level(bs2b *bs2b); - -/* Return current sample rate value */ -int bs2b_get_srate(bs2b *bs2b); - -/* Clear buffer */ -void bs2b_clear(bs2b *bs2b); - -void bs2b_cross_feed(bs2b *bs2b, float *Left, float *Right, size_t SamplesToDo); - -#endif /* BS2B_H */ diff --git a/alc/panning.cpp b/alc/panning.cpp index 86a71a24..bfc640c0 100644 --- a/alc/panning.cpp +++ b/alc/panning.cpp @@ -50,14 +50,14 @@ #include "ambdec.h" #include "ambidefs.h" #include "bformatdec.h" -#include "bs2b.h" +#include "core/bs2b.h" #include "core/devformat.h" +#include "core/uhjfilter.h" #include "front_stablizer.h" #include "hrtf.h" #include "logging.h" #include "math_defs.h" #include "opthelpers.h" -#include "uhjfilter.h" constexpr std::array AmbiScale::FromN3D; diff --git a/alc/uhjfilter.cpp b/alc/uhjfilter.cpp deleted file mode 100644 index 09edba84..00000000 --- a/alc/uhjfilter.cpp +++ /dev/null @@ -1,208 +0,0 @@ - -#include "config.h" - -#include "uhjfilter.h" - -#ifdef HAVE_SSE_INTRINSICS -#include -#endif - -#include -#include - -#include "AL/al.h" - -#include "alcomplex.h" -#include "alnumeric.h" -#include "opthelpers.h" - - -namespace { - -using complex_d = std::complex; - -std::array GenerateFilter() -{ - /* Some notes on this filter construction. - * - * A wide-band phase-shift filter needs a delay to maintain linearity. A - * dirac impulse in the center of a time-domain buffer represents a filter - * passing all frequencies through as-is with a pure delay. Converting that - * to the frequency domain, adjusting the phase of each frequency bin by - * +90 degrees, then converting back to the time domain, results in a FIR - * filter that applies a +90 degree wide-band phase-shift. - * - * A particularly notable aspect of the time-domain filter response is that - * every other coefficient is 0. This allows doubling the effective size of - * the filter, by storing only the non-0 coefficients and double-stepping - * over the input to apply it. - * - * Additionally, the resulting filter is independent of the sample rate. - * The same filter can be applied regardless of the device's sample rate - * and achieve the same effect. - */ - constexpr size_t fft_size{Uhj2Encoder::sFilterSize * 2}; - constexpr size_t half_size{fft_size / 2}; - - /* Generate a frequency domain impulse with a +90 degree phase offset. - * Reconstruct the mirrored frequencies to convert to the time domain. - */ - auto fftBuffer = std::make_unique(fft_size); - std::fill_n(fftBuffer.get(), fft_size, complex_d{}); - fftBuffer[half_size] = 1.0; - - forward_fft({fftBuffer.get(), fft_size}); - for(size_t i{0};i < half_size+1;++i) - fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()}; - for(size_t i{half_size+1};i < fft_size;++i) - fftBuffer[i] = std::conj(fftBuffer[fft_size - i]); - inverse_fft({fftBuffer.get(), fft_size}); - - /* Reverse the filter for simpler processing, and store only the non-0 - * coefficients. - */ - auto ret = std::make_unique>(); - auto fftiter = fftBuffer.get() + half_size + (Uhj2Encoder::sFilterSize-1); - for(float &coeff : *ret) - { - coeff = static_cast(fftiter->real() / double{fft_size}); - fftiter -= 2; - } - return *ret; -} -alignas(16) const auto PShiftCoeffs = GenerateFilter(); - - -void allpass_process(al::span dst, const float *RESTRICT src) -{ -#ifdef HAVE_SSE_INTRINSICS - size_t pos{0}; - if(size_t todo{dst.size()>>1}) - { - do { - __m128 r04{_mm_setzero_ps()}; - __m128 r14{_mm_setzero_ps()}; - for(size_t j{0};j < PShiftCoeffs.size();j+=4) - { - const __m128 coeffs{_mm_load_ps(&PShiftCoeffs[j])}; - const __m128 s0{_mm_loadu_ps(&src[j*2])}; - const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])}; - - __m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))}; - r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs)); - - s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1)); - r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs)); - } - r04 = _mm_add_ps(r04, _mm_shuffle_ps(r04, r04, _MM_SHUFFLE(0, 1, 2, 3))); - r04 = _mm_add_ps(r04, _mm_movehl_ps(r04, r04)); - dst[pos++] += _mm_cvtss_f32(r04); - - r14 = _mm_add_ps(r14, _mm_shuffle_ps(r14, r14, _MM_SHUFFLE(0, 1, 2, 3))); - r14 = _mm_add_ps(r14, _mm_movehl_ps(r14, r14)); - dst[pos++] += _mm_cvtss_f32(r14); - - src += 2; - } while(--todo); - } - if((dst.size()&1)) - { - __m128 r4{_mm_setzero_ps()}; - for(size_t j{0};j < PShiftCoeffs.size();j+=4) - { - const __m128 coeffs{_mm_load_ps(&PShiftCoeffs[j])}; - /* NOTE: This could alternatively be done with two unaligned loads - * and a shuffle. Which would be better? - */ - const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; - r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs)); - } - r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); - r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - - dst[pos] += _mm_cvtss_f32(r4); - } - -#else - - for(float &output : dst) - { - float ret{0.0f}; - for(size_t j{0};j < PShiftCoeffs.size();++j) - ret += src[j*2] * PShiftCoeffs[j]; - - output += ret; - ++src; - } -#endif -} - -} // namespace - - -/* Encoding 2-channel UHJ from B-Format is done as: - * - * S = 0.9396926*W + 0.1855740*X - * D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y - * - * Left = (S + D)/2.0 - * Right = (S - D)/2.0 - * - * where j is a wide-band +90 degree phase shift. - * - * The phase shift is done using a FIR filter derived from an FFT'd impulse - * with the desired shift. - */ - -void Uhj2Encoder::encode(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, - const FloatBufferLine *InSamples, const size_t SamplesToDo) -{ - ASSUME(SamplesToDo > 0); - - float *RESTRICT left{al::assume_aligned<16>(LeftOut.data())}; - float *RESTRICT right{al::assume_aligned<16>(RightOut.data())}; - - const float *RESTRICT winput{al::assume_aligned<16>(InSamples[0].data())}; - const float *RESTRICT xinput{al::assume_aligned<16>(InSamples[1].data())}; - const float *RESTRICT yinput{al::assume_aligned<16>(InSamples[2].data())}; - - /* Combine the previously delayed mid/side signal with the input. */ - - /* S = 0.9396926*W + 0.1855740*X */ - auto miditer = std::copy(mMidDelay.cbegin(), mMidDelay.cend(), mMid.begin()); - std::transform(winput, winput+SamplesToDo, xinput, miditer, - [](const float w, const float x) noexcept -> float - { return 0.9396926f*w + 0.1855740f*x; }); - - /* D = 0.6554516*Y */ - auto sideiter = std::copy(mSideDelay.cbegin(), mSideDelay.cend(), mSide.begin()); - std::transform(yinput, yinput+SamplesToDo, sideiter, - [](const float y) noexcept -> float { return 0.6554516f*y; }); - - /* Include any existing direct signal in the mid/side buffers. */ - for(size_t i{0};i < SamplesToDo;++i,++miditer) - *miditer += left[i] + right[i]; - for(size_t i{0};i < SamplesToDo;++i,++sideiter) - *sideiter += left[i] - right[i]; - - /* Copy the future samples back to the delay buffers for next time. */ - std::copy_n(mMid.cbegin()+SamplesToDo, mMidDelay.size(), mMidDelay.begin()); - std::copy_n(mSide.cbegin()+SamplesToDo, mSideDelay.size(), mSideDelay.begin()); - - /* Now add the all-passed signal into the side signal. */ - - /* D += j(-0.3420201*W + 0.5098604*X) */ - auto tmpiter = std::copy(mSideHistory.cbegin(), mSideHistory.cend(), mTemp.begin()); - std::transform(winput, winput+SamplesToDo, xinput, tmpiter, - [](const float w, const float x) noexcept -> float - { return -0.3420201f*w + 0.5098604f*x; }); - std::copy_n(mTemp.cbegin()+SamplesToDo, mSideHistory.size(), mSideHistory.begin()); - allpass_process({mSide.data(), SamplesToDo}, mTemp.data()); - - /* Left = (S + D)/2.0 */ - for(size_t i{0};i < SamplesToDo;i++) - left[i] = (mMid[i] + mSide[i]) * 0.5f; - /* Right = (S - D)/2.0 */ - for(size_t i{0};i < SamplesToDo;i++) - right[i] = (mMid[i] - mSide[i]) * 0.5f; -} diff --git a/alc/uhjfilter.h b/alc/uhjfilter.h deleted file mode 100644 index 4984bcab..00000000 --- a/alc/uhjfilter.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef UHJFILTER_H -#define UHJFILTER_H - -#include - -#include "almalloc.h" -#include "core/bufferline.h" - - -struct Uhj2Encoder { - /* A particular property of the filter allows it to cover nearly twice its - * length, so the filter size is also the effective delay (despite being - * center-aligned). - */ - constexpr static size_t sFilterSize{128}; - - /* Delays for the unfiltered signal. */ - alignas(16) std::array mMidDelay{}; - alignas(16) std::array mSideDelay{}; - - alignas(16) std::array mMid{}; - alignas(16) std::array mSide{}; - - /* History for the FIR filter. */ - alignas(16) std::array mSideHistory{}; - - alignas(16) std::array mTemp{}; - - /** - * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input - * signal. The input must use FuMa channel ordering and scaling. - */ - void encode(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, - const FloatBufferLine *InSamples, const size_t SamplesToDo); - - DEF_NEWDEL(Uhj2Encoder) -}; - -#endif /* UHJFILTER_H */ diff --git a/core/bs2b.cpp b/core/bs2b.cpp new file mode 100644 index 00000000..00207bc0 --- /dev/null +++ b/core/bs2b.cpp @@ -0,0 +1,183 @@ +/*- + * Copyright (c) 2005 Boris Mikhaylov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include + +#include "bs2b.h" +#include "math_defs.h" + + +/* Set up all data. */ +static void init(struct bs2b *bs2b) +{ + float Fc_lo, Fc_hi; + float G_lo, G_hi; + float x, g; + + switch(bs2b->level) + { + case BS2B_LOW_CLEVEL: /* Low crossfeed level */ + Fc_lo = 360.0f; + Fc_hi = 501.0f; + G_lo = 0.398107170553497f; + G_hi = 0.205671765275719f; + break; + + case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */ + Fc_lo = 500.0f; + Fc_hi = 711.0f; + G_lo = 0.459726988530872f; + G_hi = 0.228208484414988f; + break; + + case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */ + Fc_lo = 700.0f; + Fc_hi = 1021.0f; + G_lo = 0.530884444230988f; + G_hi = 0.250105790667544f; + break; + + case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */ + Fc_lo = 360.0f; + Fc_hi = 494.0f; + G_lo = 0.316227766016838f; + G_hi = 0.168236228897329f; + break; + + case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */ + Fc_lo = 500.0f; + Fc_hi = 689.0f; + G_lo = 0.354813389233575f; + G_hi = 0.187169483835901f; + break; + + default: /* High easy crossfeed level */ + bs2b->level = BS2B_HIGH_ECLEVEL; + + Fc_lo = 700.0f; + Fc_hi = 975.0f; + G_lo = 0.398107170553497f; + G_hi = 0.205671765275719f; + break; + } /* switch */ + + g = 1.0f / (1.0f - G_hi + G_lo); + + /* $fc = $Fc / $s; + * $d = 1 / 2 / pi / $fc; + * $x = exp(-1 / $d); + */ + x = std::exp(-al::MathDefs::Tau() * Fc_lo / static_cast(bs2b->srate)); + bs2b->b1_lo = x; + bs2b->a0_lo = G_lo * (1.0f - x) * g; + + x = std::exp(-al::MathDefs::Tau() * Fc_hi / static_cast(bs2b->srate)); + bs2b->b1_hi = x; + bs2b->a0_hi = (1.0f - G_hi * (1.0f - x)) * g; + bs2b->a1_hi = -x * g; +} /* init */ + + +/* Exported functions. + * See descriptions in "bs2b.h" + */ + +void bs2b_set_params(struct bs2b *bs2b, int level, int srate) +{ + if(srate <= 0) srate = 1; + + bs2b->level = level; + bs2b->srate = srate; + init(bs2b); +} /* bs2b_set_params */ + +int bs2b_get_level(struct bs2b *bs2b) +{ + return bs2b->level; +} /* bs2b_get_level */ + +int bs2b_get_srate(struct bs2b *bs2b) +{ + return bs2b->srate; +} /* bs2b_get_srate */ + +void bs2b_clear(struct bs2b *bs2b) +{ + std::fill(std::begin(bs2b->history), std::end(bs2b->history), bs2b::t_last_sample{}); +} /* bs2b_clear */ + +void bs2b_cross_feed(struct bs2b *bs2b, float *Left, float *Right, size_t SamplesToDo) +{ + const float a0_lo{bs2b->a0_lo}; + const float b1_lo{bs2b->b1_lo}; + const float a0_hi{bs2b->a0_hi}; + const float a1_hi{bs2b->a1_hi}; + const float b1_hi{bs2b->b1_hi}; + float lsamples[128][2]; + float rsamples[128][2]; + + for(size_t base{0};base < SamplesToDo;) + { + const size_t todo{std::min(128, SamplesToDo-base)}; + + /* Process left input */ + float z_lo{bs2b->history[0].lo}; + float z_hi{bs2b->history[0].hi}; + for(size_t i{0};i < todo;i++) + { + lsamples[i][0] = a0_lo*Left[i] + z_lo; + z_lo = b1_lo*lsamples[i][0]; + + lsamples[i][1] = a0_hi*Left[i] + z_hi; + z_hi = a1_hi*Left[i] + b1_hi*lsamples[i][1]; + } + bs2b->history[0].lo = z_lo; + bs2b->history[0].hi = z_hi; + + /* Process right input */ + z_lo = bs2b->history[1].lo; + z_hi = bs2b->history[1].hi; + for(size_t i{0};i < todo;i++) + { + rsamples[i][0] = a0_lo*Right[i] + z_lo; + z_lo = b1_lo*rsamples[i][0]; + + rsamples[i][1] = a0_hi*Right[i] + z_hi; + z_hi = a1_hi*Right[i] + b1_hi*rsamples[i][1]; + } + bs2b->history[1].lo = z_lo; + bs2b->history[1].hi = z_hi; + + /* Crossfeed */ + for(size_t i{0};i < todo;i++) + *(Left++) = lsamples[i][1] + rsamples[i][0]; + for(size_t i{0};i < todo;i++) + *(Right++) = rsamples[i][1] + lsamples[i][0]; + + base += todo; + } +} /* bs2b_cross_feed */ diff --git a/core/bs2b.h b/core/bs2b.h new file mode 100644 index 00000000..4d0b9dd8 --- /dev/null +++ b/core/bs2b.h @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 2005 Boris Mikhaylov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef CORE_BS2B_H +#define CORE_BS2B_H + +#include "almalloc.h" + +/* Number of crossfeed levels */ +#define BS2B_CLEVELS 3 + +/* Normal crossfeed levels */ +#define BS2B_HIGH_CLEVEL 3 +#define BS2B_MIDDLE_CLEVEL 2 +#define BS2B_LOW_CLEVEL 1 + +/* Easy crossfeed levels */ +#define BS2B_HIGH_ECLEVEL BS2B_HIGH_CLEVEL + BS2B_CLEVELS +#define BS2B_MIDDLE_ECLEVEL BS2B_MIDDLE_CLEVEL + BS2B_CLEVELS +#define BS2B_LOW_ECLEVEL BS2B_LOW_CLEVEL + BS2B_CLEVELS + +/* Default crossfeed levels */ +#define BS2B_DEFAULT_CLEVEL BS2B_HIGH_ECLEVEL +/* Default sample rate (Hz) */ +#define BS2B_DEFAULT_SRATE 44100 + +struct bs2b { + int level; /* Crossfeed level */ + int srate; /* Sample rate (Hz) */ + + /* Lowpass IIR filter coefficients */ + float a0_lo; + float b1_lo; + + /* Highboost IIR filter coefficients */ + float a0_hi; + float a1_hi; + float b1_hi; + + /* Buffer of filter history + * [0] - first channel, [1] - second channel + */ + struct t_last_sample { + float lo; + float hi; + } history[2]; + + DEF_NEWDEL(bs2b) +}; + +/* Clear buffers and set new coefficients with new crossfeed level and sample + * rate values. + * level - crossfeed level of *LEVEL values. + * srate - sample rate by Hz. + */ +void bs2b_set_params(bs2b *bs2b, int level, int srate); + +/* Return current crossfeed level value */ +int bs2b_get_level(bs2b *bs2b); + +/* Return current sample rate value */ +int bs2b_get_srate(bs2b *bs2b); + +/* Clear buffer */ +void bs2b_clear(bs2b *bs2b); + +void bs2b_cross_feed(bs2b *bs2b, float *Left, float *Right, size_t SamplesToDo); + +#endif /* CORE_BS2B_H */ diff --git a/core/uhjfilter.cpp b/core/uhjfilter.cpp new file mode 100644 index 00000000..a415f07c --- /dev/null +++ b/core/uhjfilter.cpp @@ -0,0 +1,206 @@ + +#include "config.h" + +#include "uhjfilter.h" + +#ifdef HAVE_SSE_INTRINSICS +#include +#endif + +#include +#include + +#include "alcomplex.h" +#include "alnumeric.h" +#include "opthelpers.h" + + +namespace { + +using complex_d = std::complex; + +std::array GenerateFilter() +{ + /* Some notes on this filter construction. + * + * A wide-band phase-shift filter needs a delay to maintain linearity. A + * dirac impulse in the center of a time-domain buffer represents a filter + * passing all frequencies through as-is with a pure delay. Converting that + * to the frequency domain, adjusting the phase of each frequency bin by + * +90 degrees, then converting back to the time domain, results in a FIR + * filter that applies a +90 degree wide-band phase-shift. + * + * A particularly notable aspect of the time-domain filter response is that + * every other coefficient is 0. This allows doubling the effective size of + * the filter, by storing only the non-0 coefficients and double-stepping + * over the input to apply it. + * + * Additionally, the resulting filter is independent of the sample rate. + * The same filter can be applied regardless of the device's sample rate + * and achieve the same effect. + */ + constexpr size_t fft_size{Uhj2Encoder::sFilterSize * 2}; + constexpr size_t half_size{fft_size / 2}; + + /* Generate a frequency domain impulse with a +90 degree phase offset. + * Reconstruct the mirrored frequencies to convert to the time domain. + */ + auto fftBuffer = std::make_unique(fft_size); + std::fill_n(fftBuffer.get(), fft_size, complex_d{}); + fftBuffer[half_size] = 1.0; + + forward_fft({fftBuffer.get(), fft_size}); + for(size_t i{0};i < half_size+1;++i) + fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()}; + for(size_t i{half_size+1};i < fft_size;++i) + fftBuffer[i] = std::conj(fftBuffer[fft_size - i]); + inverse_fft({fftBuffer.get(), fft_size}); + + /* Reverse the filter for simpler processing, and store only the non-0 + * coefficients. + */ + auto ret = std::make_unique>(); + auto fftiter = fftBuffer.get() + half_size + (Uhj2Encoder::sFilterSize-1); + for(float &coeff : *ret) + { + coeff = static_cast(fftiter->real() / double{fft_size}); + fftiter -= 2; + } + return *ret; +} +alignas(16) const auto PShiftCoeffs = GenerateFilter(); + + +void allpass_process(al::span dst, const float *RESTRICT src) +{ +#ifdef HAVE_SSE_INTRINSICS + size_t pos{0}; + if(size_t todo{dst.size()>>1}) + { + do { + __m128 r04{_mm_setzero_ps()}; + __m128 r14{_mm_setzero_ps()}; + for(size_t j{0};j < PShiftCoeffs.size();j+=4) + { + const __m128 coeffs{_mm_load_ps(&PShiftCoeffs[j])}; + const __m128 s0{_mm_loadu_ps(&src[j*2])}; + const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])}; + + __m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))}; + r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs)); + + s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1)); + r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs)); + } + r04 = _mm_add_ps(r04, _mm_shuffle_ps(r04, r04, _MM_SHUFFLE(0, 1, 2, 3))); + r04 = _mm_add_ps(r04, _mm_movehl_ps(r04, r04)); + dst[pos++] += _mm_cvtss_f32(r04); + + r14 = _mm_add_ps(r14, _mm_shuffle_ps(r14, r14, _MM_SHUFFLE(0, 1, 2, 3))); + r14 = _mm_add_ps(r14, _mm_movehl_ps(r14, r14)); + dst[pos++] += _mm_cvtss_f32(r14); + + src += 2; + } while(--todo); + } + if((dst.size()&1)) + { + __m128 r4{_mm_setzero_ps()}; + for(size_t j{0};j < PShiftCoeffs.size();j+=4) + { + const __m128 coeffs{_mm_load_ps(&PShiftCoeffs[j])}; + /* NOTE: This could alternatively be done with two unaligned loads + * and a shuffle. Which would be better? + */ + const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; + r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs)); + } + r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + + dst[pos] += _mm_cvtss_f32(r4); + } + +#else + + for(float &output : dst) + { + float ret{0.0f}; + for(size_t j{0};j < PShiftCoeffs.size();++j) + ret += src[j*2] * PShiftCoeffs[j]; + + output += ret; + ++src; + } +#endif +} + +} // namespace + + +/* Encoding 2-channel UHJ from B-Format is done as: + * + * S = 0.9396926*W + 0.1855740*X + * D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y + * + * Left = (S + D)/2.0 + * Right = (S - D)/2.0 + * + * where j is a wide-band +90 degree phase shift. + * + * The phase shift is done using a FIR filter derived from an FFT'd impulse + * with the desired shift. + */ + +void Uhj2Encoder::encode(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, + const FloatBufferLine *InSamples, const size_t SamplesToDo) +{ + ASSUME(SamplesToDo > 0); + + float *RESTRICT left{al::assume_aligned<16>(LeftOut.data())}; + float *RESTRICT right{al::assume_aligned<16>(RightOut.data())}; + + const float *RESTRICT winput{al::assume_aligned<16>(InSamples[0].data())}; + const float *RESTRICT xinput{al::assume_aligned<16>(InSamples[1].data())}; + const float *RESTRICT yinput{al::assume_aligned<16>(InSamples[2].data())}; + + /* Combine the previously delayed mid/side signal with the input. */ + + /* S = 0.9396926*W + 0.1855740*X */ + auto miditer = std::copy(mMidDelay.cbegin(), mMidDelay.cend(), mMid.begin()); + std::transform(winput, winput+SamplesToDo, xinput, miditer, + [](const float w, const float x) noexcept -> float + { return 0.9396926f*w + 0.1855740f*x; }); + + /* D = 0.6554516*Y */ + auto sideiter = std::copy(mSideDelay.cbegin(), mSideDelay.cend(), mSide.begin()); + std::transform(yinput, yinput+SamplesToDo, sideiter, + [](const float y) noexcept -> float { return 0.6554516f*y; }); + + /* Include any existing direct signal in the mid/side buffers. */ + for(size_t i{0};i < SamplesToDo;++i,++miditer) + *miditer += left[i] + right[i]; + for(size_t i{0};i < SamplesToDo;++i,++sideiter) + *sideiter += left[i] - right[i]; + + /* Copy the future samples back to the delay buffers for next time. */ + std::copy_n(mMid.cbegin()+SamplesToDo, mMidDelay.size(), mMidDelay.begin()); + std::copy_n(mSide.cbegin()+SamplesToDo, mSideDelay.size(), mSideDelay.begin()); + + /* Now add the all-passed signal into the side signal. */ + + /* D += j(-0.3420201*W + 0.5098604*X) */ + auto tmpiter = std::copy(mSideHistory.cbegin(), mSideHistory.cend(), mTemp.begin()); + std::transform(winput, winput+SamplesToDo, xinput, tmpiter, + [](const float w, const float x) noexcept -> float + { return -0.3420201f*w + 0.5098604f*x; }); + std::copy_n(mTemp.cbegin()+SamplesToDo, mSideHistory.size(), mSideHistory.begin()); + allpass_process({mSide.data(), SamplesToDo}, mTemp.data()); + + /* Left = (S + D)/2.0 */ + for(size_t i{0};i < SamplesToDo;i++) + left[i] = (mMid[i] + mSide[i]) * 0.5f; + /* Right = (S - D)/2.0 */ + for(size_t i{0};i < SamplesToDo;i++) + right[i] = (mMid[i] - mSide[i]) * 0.5f; +} diff --git a/core/uhjfilter.h b/core/uhjfilter.h new file mode 100644 index 00000000..c2cb8722 --- /dev/null +++ b/core/uhjfilter.h @@ -0,0 +1,39 @@ +#ifndef CORE_UHJFILTER_H +#define CORE_UHJFILTER_H + +#include + +#include "almalloc.h" +#include "bufferline.h" + + +struct Uhj2Encoder { + /* A particular property of the filter allows it to cover nearly twice its + * length, so the filter size is also the effective delay (despite being + * center-aligned). + */ + constexpr static size_t sFilterSize{128}; + + /* Delays for the unfiltered signal. */ + alignas(16) std::array mMidDelay{}; + alignas(16) std::array mSideDelay{}; + + alignas(16) std::array mMid{}; + alignas(16) std::array mSide{}; + + /* History for the FIR filter. */ + alignas(16) std::array mSideHistory{}; + + alignas(16) std::array mTemp{}; + + /** + * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input + * signal. The input must use FuMa channel ordering and scaling. + */ + void encode(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, + const FloatBufferLine *InSamples, const size_t SamplesToDo); + + DEF_NEWDEL(Uhj2Encoder) +}; + +#endif /* CORE_UHJFILTER_H */ -- cgit v1.2.3 From 8a352d25f96faf570cf8c3a2ee1f11a30d9ae0fe Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 5 Dec 2020 03:28:19 -0800 Subject: Move the ringbuffer to common --- CMakeLists.txt | 4 +- alc/ringbuffer.cpp | 236 -------------------------------------------------- alc/ringbuffer.h | 111 ------------------------ common/ringbuffer.cpp | 224 +++++++++++++++++++++++++++++++++++++++++++++++ common/ringbuffer.h | 113 ++++++++++++++++++++++++ 5 files changed, 339 insertions(+), 349 deletions(-) delete mode 100644 alc/ringbuffer.cpp delete mode 100644 alc/ringbuffer.h create mode 100644 common/ringbuffer.cpp create mode 100644 common/ringbuffer.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index d74c3f75..ca776f4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -585,6 +585,8 @@ set(COMMON_OBJS common/polyphase_resampler.cpp common/polyphase_resampler.h common/pragmadefs.h + common/ringbuffer.cpp + common/ringbuffer.h common/strutils.cpp common/strutils.h common/threads.cpp @@ -693,8 +695,6 @@ set(ALC_OBJS alc/inprogext.h alc/logging.h alc/panning.cpp - alc/ringbuffer.cpp - alc/ringbuffer.h alc/uiddefs.cpp alc/voice.cpp alc/voice.h diff --git a/alc/ringbuffer.cpp b/alc/ringbuffer.cpp deleted file mode 100644 index 5d891dbe..00000000 --- a/alc/ringbuffer.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 1999-2007 by authors. - * 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 "ringbuffer.h" - -#include -#include -#include -#include - -#include "almalloc.h" - - -RingBufferPtr RingBuffer::Create(size_t sz, size_t elem_sz, int limit_writes) -{ - size_t power_of_two{0u}; - if(sz > 0) - { - power_of_two = sz; - power_of_two |= power_of_two>>1; - power_of_two |= power_of_two>>2; - power_of_two |= power_of_two>>4; - power_of_two |= power_of_two>>8; - power_of_two |= power_of_two>>16; -#if SIZE_MAX > UINT_MAX - power_of_two |= power_of_two>>32; -#endif - } - ++power_of_two; - if(power_of_two <= sz || power_of_two > std::numeric_limits::max()/elem_sz) - throw std::overflow_error{"Ring buffer size overflow"}; - - const size_t bufbytes{power_of_two * elem_sz}; - RingBufferPtr rb{new(FamCount(bufbytes)) RingBuffer{bufbytes}}; - rb->mWriteSize = limit_writes ? sz : (power_of_two-1); - rb->mSizeMask = power_of_two - 1; - rb->mElemSize = elem_sz; - - return rb; -} - -void RingBuffer::reset() noexcept -{ - mWritePtr.store(0, std::memory_order_relaxed); - mReadPtr.store(0, std::memory_order_relaxed); - std::fill_n(mBuffer.begin(), (mSizeMask+1)*mElemSize, al::byte{}); -} - - -size_t RingBuffer::read(void *dest, size_t cnt) noexcept -{ - const size_t free_cnt{readSpace()}; - if(free_cnt == 0) return 0; - - const size_t to_read{std::min(cnt, free_cnt)}; - size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask}; - - size_t n1, n2; - const size_t cnt2{read_ptr + to_read}; - if(cnt2 > mSizeMask+1) - { - n1 = mSizeMask+1 - read_ptr; - n2 = cnt2 & mSizeMask; - } - else - { - n1 = to_read; - n2 = 0; - } - - auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize, - static_cast(dest)); - read_ptr += n1; - if(n2 > 0) - { - std::copy_n(mBuffer.begin(), n2*mElemSize, outiter); - read_ptr += n2; - } - mReadPtr.store(read_ptr, std::memory_order_release); - return to_read; -} - -size_t RingBuffer::peek(void *dest, size_t cnt) const noexcept -{ - const size_t free_cnt{readSpace()}; - if(free_cnt == 0) return 0; - - const size_t to_read{std::min(cnt, free_cnt)}; - size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask}; - - size_t n1, n2; - const size_t cnt2{read_ptr + to_read}; - if(cnt2 > mSizeMask+1) - { - n1 = mSizeMask+1 - read_ptr; - n2 = cnt2 & mSizeMask; - } - else - { - n1 = to_read; - n2 = 0; - } - - auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize, - static_cast(dest)); - if(n2 > 0) - std::copy_n(mBuffer.begin(), n2*mElemSize, outiter); - return to_read; -} - -size_t RingBuffer::write(const void *src, size_t cnt) noexcept -{ - const size_t free_cnt{writeSpace()}; - if(free_cnt == 0) return 0; - - const size_t to_write{std::min(cnt, free_cnt)}; - size_t write_ptr{mWritePtr.load(std::memory_order_relaxed) & mSizeMask}; - - size_t n1, n2; - const size_t cnt2{write_ptr + to_write}; - if(cnt2 > mSizeMask+1) - { - n1 = mSizeMask+1 - write_ptr; - n2 = cnt2 & mSizeMask; - } - else - { - n1 = to_write; - n2 = 0; - } - - auto srcbytes = static_cast(src); - std::copy_n(srcbytes, n1*mElemSize, mBuffer.begin() + write_ptr*mElemSize); - write_ptr += n1; - if(n2 > 0) - { - std::copy_n(srcbytes + n1*mElemSize, n2*mElemSize, mBuffer.begin()); - write_ptr += n2; - } - mWritePtr.store(write_ptr, std::memory_order_release); - return to_write; -} - - -void RingBuffer::readAdvance(size_t cnt) noexcept -{ - mReadPtr.fetch_add(cnt, std::memory_order_acq_rel); -} - -void RingBuffer::writeAdvance(size_t cnt) noexcept -{ - mWritePtr.fetch_add(cnt, std::memory_order_acq_rel); -} - - -ll_ringbuffer_data_pair RingBuffer::getReadVector() const noexcept -{ - ll_ringbuffer_data_pair ret; - - size_t w{mWritePtr.load(std::memory_order_acquire)}; - size_t r{mReadPtr.load(std::memory_order_acquire)}; - w &= mSizeMask; - r &= mSizeMask; - const size_t free_cnt{(w-r) & mSizeMask}; - - const size_t cnt2{r + free_cnt}; - if(cnt2 > mSizeMask+1) - { - /* Two part vector: the rest of the buffer after the current read ptr, - * plus some from the start of the buffer. */ - ret.first.buf = const_cast(mBuffer.data() + r*mElemSize); - ret.first.len = mSizeMask+1 - r; - ret.second.buf = const_cast(mBuffer.data()); - ret.second.len = cnt2 & mSizeMask; - } - else - { - /* Single part vector: just the rest of the buffer */ - ret.first.buf = const_cast(mBuffer.data() + r*mElemSize); - ret.first.len = free_cnt; - ret.second.buf = nullptr; - ret.second.len = 0; - } - - return ret; -} - -ll_ringbuffer_data_pair RingBuffer::getWriteVector() const noexcept -{ - ll_ringbuffer_data_pair ret; - - size_t w{mWritePtr.load(std::memory_order_acquire)}; - size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask}; - w &= mSizeMask; - r &= mSizeMask; - const size_t free_cnt{(r-w-1) & mSizeMask}; - - const size_t cnt2{w + free_cnt}; - if(cnt2 > mSizeMask+1) - { - /* Two part vector: the rest of the buffer after the current write ptr, - * plus some from the start of the buffer. */ - ret.first.buf = const_cast(mBuffer.data() + w*mElemSize); - ret.first.len = mSizeMask+1 - w; - ret.second.buf = const_cast(mBuffer.data()); - ret.second.len = cnt2 & mSizeMask; - } - else - { - ret.first.buf = const_cast(mBuffer.data() + w*mElemSize); - ret.first.len = free_cnt; - ret.second.buf = nullptr; - ret.second.len = 0; - } - - return ret; -} diff --git a/alc/ringbuffer.h b/alc/ringbuffer.h deleted file mode 100644 index 32fb951c..00000000 --- a/alc/ringbuffer.h +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef RINGBUFFER_H -#define RINGBUFFER_H - -#include - -#include -#include -#include - -#include "albyte.h" -#include "almalloc.h" - - -/* NOTE: This lockless ringbuffer implementation is copied from JACK, extended - * to include an element size. Consequently, parameters and return values for a - * size or count is in 'elements', not bytes. Additionally, it only supports - * single-consumer/single-provider operation. - */ - -struct ll_ringbuffer_data { - al::byte *buf; - size_t len; -}; -using ll_ringbuffer_data_pair = std::pair; - - -struct RingBuffer { -private: - std::atomic mWritePtr{0u}; - std::atomic mReadPtr{0u}; - size_t mWriteSize{0u}; - size_t mSizeMask{0u}; - size_t mElemSize{0u}; - - al::FlexArray mBuffer; - -public: - RingBuffer(const size_t count) : mBuffer{count} { } - - /** Reset the read and write pointers to zero. This is not thread safe. */ - void reset() noexcept; - - /** - * The non-copying data reader. Returns two ringbuffer data pointers that - * hold the current readable data. If the readable data is in one segment - * the second segment has zero length. - */ - ll_ringbuffer_data_pair getReadVector() const noexcept; - /** - * The non-copying data writer. Returns two ringbuffer data pointers that - * hold the current writeable data. If the writeable data is in one segment - * the second segment has zero length. - */ - ll_ringbuffer_data_pair getWriteVector() const noexcept; - - /** - * Return the number of elements available for reading. This is the number - * of elements in front of the read pointer and behind the write pointer. - */ - size_t readSpace() const noexcept - { - const size_t w{mWritePtr.load(std::memory_order_acquire)}; - const size_t r{mReadPtr.load(std::memory_order_acquire)}; - return (w-r) & mSizeMask; - } - - /** - * The copying data reader. Copy at most `cnt' elements into `dest'. - * Returns the actual number of elements copied. - */ - size_t read(void *dest, size_t cnt) noexcept; - /** - * The copying data reader w/o read pointer advance. Copy at most `cnt' - * elements into `dest'. Returns the actual number of elements copied. - */ - size_t peek(void *dest, size_t cnt) const noexcept; - /** Advance the read pointer `cnt' places. */ - void readAdvance(size_t cnt) noexcept; - - /** - * Return the number of elements available for writing. This is the number - * of elements in front of the write pointer and behind the read pointer. - */ - size_t writeSpace() const noexcept - { - const size_t w{mWritePtr.load(std::memory_order_acquire)}; - const size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask}; - return (r-w-1) & mSizeMask; - } - - /** - * The copying data writer. Copy at most `cnt' elements from `src'. Returns - * the actual number of elements copied. - */ - size_t write(const void *src, size_t cnt) noexcept; - /** Advance the write pointer `cnt' places. */ - void writeAdvance(size_t cnt) noexcept; - - /** - * Create a new ringbuffer to hold at least `sz' elements of `elem_sz' - * bytes. The number of elements is rounded up to the next power of two - * (even if it is already a power of two, to ensure the requested amount - * can be written). - */ - static std::unique_ptr Create(size_t sz, size_t elem_sz, int limit_writes); - - DEF_FAM_NEWDEL(RingBuffer, mBuffer) -}; -using RingBufferPtr = std::unique_ptr; - -#endif /* RINGBUFFER_H */ diff --git a/common/ringbuffer.cpp b/common/ringbuffer.cpp new file mode 100644 index 00000000..91857499 --- /dev/null +++ b/common/ringbuffer.cpp @@ -0,0 +1,224 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 by authors. + * 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 "ringbuffer.h" + +#include +#include +#include + +#include "almalloc.h" + + +RingBufferPtr RingBuffer::Create(size_t sz, size_t elem_sz, int limit_writes) +{ + size_t power_of_two{0u}; + if(sz > 0) + { + power_of_two = sz; + power_of_two |= power_of_two>>1; + power_of_two |= power_of_two>>2; + power_of_two |= power_of_two>>4; + power_of_two |= power_of_two>>8; + power_of_two |= power_of_two>>16; +#if SIZE_MAX > UINT_MAX + power_of_two |= power_of_two>>32; +#endif + } + ++power_of_two; + if(power_of_two <= sz || power_of_two > std::numeric_limits::max()/elem_sz) + throw std::overflow_error{"Ring buffer size overflow"}; + + const size_t bufbytes{power_of_two * elem_sz}; + RingBufferPtr rb{new(FamCount(bufbytes)) RingBuffer{bufbytes}}; + rb->mWriteSize = limit_writes ? sz : (power_of_two-1); + rb->mSizeMask = power_of_two - 1; + rb->mElemSize = elem_sz; + + return rb; +} + +void RingBuffer::reset() noexcept +{ + mWritePtr.store(0, std::memory_order_relaxed); + mReadPtr.store(0, std::memory_order_relaxed); + std::fill_n(mBuffer.begin(), (mSizeMask+1)*mElemSize, al::byte{}); +} + + +size_t RingBuffer::read(void *dest, size_t cnt) noexcept +{ + const size_t free_cnt{readSpace()}; + if(free_cnt == 0) return 0; + + const size_t to_read{std::min(cnt, free_cnt)}; + size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask}; + + size_t n1, n2; + const size_t cnt2{read_ptr + to_read}; + if(cnt2 > mSizeMask+1) + { + n1 = mSizeMask+1 - read_ptr; + n2 = cnt2 & mSizeMask; + } + else + { + n1 = to_read; + n2 = 0; + } + + auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize, + static_cast(dest)); + read_ptr += n1; + if(n2 > 0) + { + std::copy_n(mBuffer.begin(), n2*mElemSize, outiter); + read_ptr += n2; + } + mReadPtr.store(read_ptr, std::memory_order_release); + return to_read; +} + +size_t RingBuffer::peek(void *dest, size_t cnt) const noexcept +{ + const size_t free_cnt{readSpace()}; + if(free_cnt == 0) return 0; + + const size_t to_read{std::min(cnt, free_cnt)}; + size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask}; + + size_t n1, n2; + const size_t cnt2{read_ptr + to_read}; + if(cnt2 > mSizeMask+1) + { + n1 = mSizeMask+1 - read_ptr; + n2 = cnt2 & mSizeMask; + } + else + { + n1 = to_read; + n2 = 0; + } + + auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize, + static_cast(dest)); + if(n2 > 0) + std::copy_n(mBuffer.begin(), n2*mElemSize, outiter); + return to_read; +} + +size_t RingBuffer::write(const void *src, size_t cnt) noexcept +{ + const size_t free_cnt{writeSpace()}; + if(free_cnt == 0) return 0; + + const size_t to_write{std::min(cnt, free_cnt)}; + size_t write_ptr{mWritePtr.load(std::memory_order_relaxed) & mSizeMask}; + + size_t n1, n2; + const size_t cnt2{write_ptr + to_write}; + if(cnt2 > mSizeMask+1) + { + n1 = mSizeMask+1 - write_ptr; + n2 = cnt2 & mSizeMask; + } + else + { + n1 = to_write; + n2 = 0; + } + + auto srcbytes = static_cast(src); + std::copy_n(srcbytes, n1*mElemSize, mBuffer.begin() + write_ptr*mElemSize); + write_ptr += n1; + if(n2 > 0) + { + std::copy_n(srcbytes + n1*mElemSize, n2*mElemSize, mBuffer.begin()); + write_ptr += n2; + } + mWritePtr.store(write_ptr, std::memory_order_release); + return to_write; +} + + +ll_ringbuffer_data_pair RingBuffer::getReadVector() const noexcept +{ + ll_ringbuffer_data_pair ret; + + size_t w{mWritePtr.load(std::memory_order_acquire)}; + size_t r{mReadPtr.load(std::memory_order_acquire)}; + w &= mSizeMask; + r &= mSizeMask; + const size_t free_cnt{(w-r) & mSizeMask}; + + const size_t cnt2{r + free_cnt}; + if(cnt2 > mSizeMask+1) + { + /* Two part vector: the rest of the buffer after the current read ptr, + * plus some from the start of the buffer. */ + ret.first.buf = const_cast(mBuffer.data() + r*mElemSize); + ret.first.len = mSizeMask+1 - r; + ret.second.buf = const_cast(mBuffer.data()); + ret.second.len = cnt2 & mSizeMask; + } + else + { + /* Single part vector: just the rest of the buffer */ + ret.first.buf = const_cast(mBuffer.data() + r*mElemSize); + ret.first.len = free_cnt; + ret.second.buf = nullptr; + ret.second.len = 0; + } + + return ret; +} + +ll_ringbuffer_data_pair RingBuffer::getWriteVector() const noexcept +{ + ll_ringbuffer_data_pair ret; + + size_t w{mWritePtr.load(std::memory_order_acquire)}; + size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask}; + w &= mSizeMask; + r &= mSizeMask; + const size_t free_cnt{(r-w-1) & mSizeMask}; + + const size_t cnt2{w + free_cnt}; + if(cnt2 > mSizeMask+1) + { + /* Two part vector: the rest of the buffer after the current write ptr, + * plus some from the start of the buffer. */ + ret.first.buf = const_cast(mBuffer.data() + w*mElemSize); + ret.first.len = mSizeMask+1 - w; + ret.second.buf = const_cast(mBuffer.data()); + ret.second.len = cnt2 & mSizeMask; + } + else + { + ret.first.buf = const_cast(mBuffer.data() + w*mElemSize); + ret.first.len = free_cnt; + ret.second.buf = nullptr; + ret.second.len = 0; + } + + return ret; +} diff --git a/common/ringbuffer.h b/common/ringbuffer.h new file mode 100644 index 00000000..fa8fce10 --- /dev/null +++ b/common/ringbuffer.h @@ -0,0 +1,113 @@ +#ifndef RINGBUFFER_H +#define RINGBUFFER_H + +#include +#include +#include +#include + +#include "albyte.h" +#include "almalloc.h" + + +/* NOTE: This lockless ringbuffer implementation is copied from JACK, extended + * to include an element size. Consequently, parameters and return values for a + * size or count is in 'elements', not bytes. Additionally, it only supports + * single-consumer/single-provider operation. + */ + +struct ll_ringbuffer_data { + al::byte *buf; + size_t len; +}; +using ll_ringbuffer_data_pair = std::pair; + + +struct RingBuffer { +private: + std::atomic mWritePtr{0u}; + std::atomic mReadPtr{0u}; + size_t mWriteSize{0u}; + size_t mSizeMask{0u}; + size_t mElemSize{0u}; + + al::FlexArray mBuffer; + +public: + RingBuffer(const size_t count) : mBuffer{count} { } + + /** Reset the read and write pointers to zero. This is not thread safe. */ + void reset() noexcept; + + /** + * The non-copying data reader. Returns two ringbuffer data pointers that + * hold the current readable data. If the readable data is in one segment + * the second segment has zero length. + */ + ll_ringbuffer_data_pair getReadVector() const noexcept; + /** + * The non-copying data writer. Returns two ringbuffer data pointers that + * hold the current writeable data. If the writeable data is in one segment + * the second segment has zero length. + */ + ll_ringbuffer_data_pair getWriteVector() const noexcept; + + /** + * Return the number of elements available for reading. This is the number + * of elements in front of the read pointer and behind the write pointer. + */ + size_t readSpace() const noexcept + { + const size_t w{mWritePtr.load(std::memory_order_acquire)}; + const size_t r{mReadPtr.load(std::memory_order_acquire)}; + return (w-r) & mSizeMask; + } + + /** + * The copying data reader. Copy at most `cnt' elements into `dest'. + * Returns the actual number of elements copied. + */ + size_t read(void *dest, size_t cnt) noexcept; + /** + * The copying data reader w/o read pointer advance. Copy at most `cnt' + * elements into `dest'. Returns the actual number of elements copied. + */ + size_t peek(void *dest, size_t cnt) const noexcept; + /** Advance the read pointer `cnt' places. */ + void readAdvance(size_t cnt) noexcept + { mReadPtr.fetch_add(cnt, std::memory_order_acq_rel); } + + + /** + * Return the number of elements available for writing. This is the number + * of elements in front of the write pointer and behind the read pointer. + */ + size_t writeSpace() const noexcept + { + const size_t w{mWritePtr.load(std::memory_order_acquire)}; + const size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask}; + return (r-w-1) & mSizeMask; + } + + /** + * The copying data writer. Copy at most `cnt' elements from `src'. Returns + * the actual number of elements copied. + */ + size_t write(const void *src, size_t cnt) noexcept; + /** Advance the write pointer `cnt' places. */ + void writeAdvance(size_t cnt) noexcept + { mWritePtr.fetch_add(cnt, std::memory_order_acq_rel); } + + /** + * Create a new ringbuffer to hold at least `sz' elements of `elem_sz' + * bytes. The number of elements is rounded up to the next power of two + * (even if it is already a power of two, to ensure the requested amount + * can be written). + */ + static std::unique_ptr Create(size_t sz, size_t elem_sz, int limit_writes); + + DEF_FAM_NEWDEL(RingBuffer, mBuffer) +}; +using RingBufferPtr = std::unique_ptr; + +#endif /* RINGBUFFER_H */ -- cgit v1.2.3 From 191fe888b4bf55aac539315c75ed7f6d15f2ea7e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 12 Dec 2020 10:38:24 -0800 Subject: Move ambidefs.h to core --- CMakeLists.txt | 3 +- al/source.cpp | 2 +- alc/alc.cpp | 2 +- alc/alcmain.h | 2 +- alc/alu.cpp | 2 +- alc/alu.h | 2 +- alc/ambdec.h | 2 +- alc/ambidefs.h | 135 -------------------------------------------- alc/bformatdec.h | 2 +- alc/effects/chorus.cpp | 2 +- alc/effects/convolution.cpp | 2 +- alc/hrtf.h | 2 +- alc/panning.cpp | 13 +---- core/ambidefs.cpp | 15 +++++ core/ambidefs.h | 135 ++++++++++++++++++++++++++++++++++++++++++++ 15 files changed, 163 insertions(+), 158 deletions(-) delete mode 100644 alc/ambidefs.h create mode 100644 core/ambidefs.cpp create mode 100644 core/ambidefs.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index ca776f4e..ea09bbe2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -629,6 +629,8 @@ set(OPENAL_OBJS al/state.cpp ) set(ALC_OBJS + core/ambidefs.cpp + core/ambidefs.h core/bs2b.cpp core/bs2b.h core/bsinc_defs.h @@ -657,7 +659,6 @@ set(ALC_OBJS alc/alcontext.h alc/ambdec.cpp alc/ambdec.h - alc/ambidefs.h alc/bformatdec.cpp alc/bformatdec.h alc/buffer_storage.cpp diff --git a/al/source.cpp b/al/source.cpp index 5dcd1cca..762c9c09 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -53,12 +53,12 @@ #include "aloptional.h" #include "alspan.h" #include "alu.h" -#include "ambidefs.h" #include "atomic.h" #include "auxeffectslot.h" #include "backends/base.h" #include "bformatdec.h" #include "buffer.h" +#include "core/ambidefs.h" #include "core/filters/nfc.h" #include "core/filters/splitter.h" #include "event.h" diff --git a/alc/alc.cpp b/alc/alc.cpp index 2e416d00..70890186 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -75,10 +75,10 @@ #include "alspan.h" #include "alstring.h" #include "alu.h" -#include "ambidefs.h" #include "atomic.h" #include "bformatdec.h" #include "compat.h" +#include "core/ambidefs.h" #include "core/bs2b.h" #include "core/devformat.h" #include "core/mastering.h" diff --git a/alc/alcmain.h b/alc/alcmain.h index d809f8ee..ef52afc7 100644 --- a/alc/alcmain.h +++ b/alc/alcmain.h @@ -21,8 +21,8 @@ #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" -#include "ambidefs.h" #include "atomic.h" +#include "core/ambidefs.h" #include "core/bufferline.h" #include "core/devformat.h" #include "core/filters/splitter.h" diff --git a/alc/alu.cpp b/alc/alu.cpp index 1824a5a9..5ea54f23 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -55,9 +55,9 @@ #include "alnumeric.h" #include "alspan.h" #include "alstring.h" -#include "ambidefs.h" #include "atomic.h" #include "bformatdec.h" +#include "core/ambidefs.h" #include "core/bs2b.h" #include "core/bsinc_tables.h" #include "core/devformat.h" diff --git a/alc/alu.h b/alc/alu.h index fd1d0b37..1393b824 100644 --- a/alc/alu.h +++ b/alc/alu.h @@ -7,7 +7,7 @@ #include #include "alspan.h" -#include "ambidefs.h" +#include "core/ambidefs.h" #include "core/bufferline.h" #include "core/devformat.h" diff --git a/alc/ambdec.h b/alc/ambdec.h index db662a76..c96ded5b 100644 --- a/alc/ambdec.h +++ b/alc/ambdec.h @@ -4,7 +4,7 @@ #include #include -#include "ambidefs.h" +#include "core/ambidefs.h" #include "vector.h" /* Helpers to read .ambdec configuration files. */ diff --git a/alc/ambidefs.h b/alc/ambidefs.h deleted file mode 100644 index 339b51f9..00000000 --- a/alc/ambidefs.h +++ /dev/null @@ -1,135 +0,0 @@ -#ifndef AMBIDEFS_H -#define AMBIDEFS_H - -#include -#include -#include - -using uint = unsigned int; - -/* The maximum number of Ambisonics channels. For a given order (o), the size - * needed will be (o+1)**2, thus zero-order has 1, first-order has 4, second- - * order has 9, third-order has 16, and fourth-order has 25. - */ -constexpr uint8_t MaxAmbiOrder{3}; -constexpr inline size_t AmbiChannelsFromOrder(size_t order) noexcept -{ return (order+1) * (order+1); } -constexpr size_t MaxAmbiChannels{AmbiChannelsFromOrder(MaxAmbiOrder)}; - -/* A bitmask of ambisonic channels for 0 to 4th order. This only specifies up - * to 4th order, which is the highest order a 32-bit mask value can specify (a - * 64-bit mask could handle up to 7th order). - */ -constexpr uint Ambi0OrderMask{0x00000001}; -constexpr uint Ambi1OrderMask{0x0000000f}; -constexpr uint Ambi2OrderMask{0x000001ff}; -constexpr uint Ambi3OrderMask{0x0000ffff}; -constexpr uint Ambi4OrderMask{0x01ffffff}; - -/* A bitmask of ambisonic channels with height information. If none of these - * channels are used/needed, there's no height (e.g. with most surround sound - * speaker setups). This is ACN ordering, with bit 0 being ACN 0, etc. - */ -constexpr uint AmbiPeriphonicMask{0xfe7ce4}; - -/* The maximum number of ambisonic channels for 2D (non-periphonic) - * representation. This is 2 per each order above zero-order, plus 1 for zero- - * order. Or simply, o*2 + 1. - */ -constexpr inline size_t Ambi2DChannelsFromOrder(size_t order) noexcept -{ return order*2 + 1; } -constexpr size_t MaxAmbi2DChannels{Ambi2DChannelsFromOrder(MaxAmbiOrder)}; - - -/* NOTE: These are scale factors as applied to Ambisonics content. Decoder - * coefficients should be divided by these values to get proper scalings. - */ -struct AmbiScale { - static constexpr std::array FromN3D{{ - 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f - }}; - static constexpr std::array FromSN3D{{ - 1.000000000f, /* ACN 0, sqrt(1) */ - 1.732050808f, /* ACN 1, sqrt(3) */ - 1.732050808f, /* ACN 2, sqrt(3) */ - 1.732050808f, /* ACN 3, sqrt(3) */ - 2.236067978f, /* ACN 4, sqrt(5) */ - 2.236067978f, /* ACN 5, sqrt(5) */ - 2.236067978f, /* ACN 6, sqrt(5) */ - 2.236067978f, /* ACN 7, sqrt(5) */ - 2.236067978f, /* ACN 8, sqrt(5) */ - 2.645751311f, /* ACN 9, sqrt(7) */ - 2.645751311f, /* ACN 10, sqrt(7) */ - 2.645751311f, /* ACN 11, sqrt(7) */ - 2.645751311f, /* ACN 12, sqrt(7) */ - 2.645751311f, /* ACN 13, sqrt(7) */ - 2.645751311f, /* ACN 14, sqrt(7) */ - 2.645751311f, /* ACN 15, sqrt(7) */ - }}; - static constexpr std::array FromFuMa{{ - 1.414213562f, /* ACN 0 (W), sqrt(2) */ - 1.732050808f, /* ACN 1 (Y), sqrt(3) */ - 1.732050808f, /* ACN 2 (Z), sqrt(3) */ - 1.732050808f, /* ACN 3 (X), sqrt(3) */ - 1.936491673f, /* ACN 4 (V), sqrt(15)/2 */ - 1.936491673f, /* ACN 5 (T), sqrt(15)/2 */ - 2.236067978f, /* ACN 6 (R), sqrt(5) */ - 1.936491673f, /* ACN 7 (S), sqrt(15)/2 */ - 1.936491673f, /* ACN 8 (U), sqrt(15)/2 */ - 2.091650066f, /* ACN 9 (Q), sqrt(35/8) */ - 1.972026594f, /* ACN 10 (O), sqrt(35)/3 */ - 2.231093404f, /* ACN 11 (M), sqrt(224/45) */ - 2.645751311f, /* ACN 12 (K), sqrt(7) */ - 2.231093404f, /* ACN 13 (L), sqrt(224/45) */ - 1.972026594f, /* ACN 14 (N), sqrt(35)/3 */ - 2.091650066f, /* ACN 15 (P), sqrt(35/8) */ - }}; -}; - -struct AmbiIndex { - static constexpr std::array FromFuMa{{ - 0, /* W */ - 3, /* X */ - 1, /* Y */ - 2, /* Z */ - 6, /* R */ - 7, /* S */ - 5, /* T */ - 8, /* U */ - 4, /* V */ - 12, /* K */ - 13, /* L */ - 11, /* M */ - 14, /* N */ - 10, /* O */ - 15, /* P */ - 9, /* Q */ - }}; - static constexpr std::array FromFuMa2D{{ - 0, /* W */ - 3, /* X */ - 1, /* Y */ - 8, /* U */ - 4, /* V */ - 15, /* P */ - 9, /* Q */ - }}; - - static constexpr std::array FromACN{{ - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15 - }}; - static constexpr std::array From2D{{ - 0, 1,3, 4,8, 9,15 - }}; - - static constexpr std::array OrderFromChannel{{ - 0, 1,1,1, 2,2,2,2,2, 3,3,3,3,3,3,3, - }}; - static constexpr std::array OrderFrom2DChannel{{ - 0, 1,1, 2,2, 3,3, - }}; -}; - -#endif /* AMBIDEFS_H */ diff --git a/alc/bformatdec.h b/alc/bformatdec.h index d9cddf51..7715d364 100644 --- a/alc/bformatdec.h +++ b/alc/bformatdec.h @@ -7,7 +7,7 @@ #include "almalloc.h" #include "alspan.h" -#include "ambidefs.h" +#include "core/ambidefs.h" #include "core/bufferline.h" #include "core/devformat.h" #include "core/filters/splitter.h" diff --git a/alc/effects/chorus.cpp b/alc/effects/chorus.cpp index 68a79fba..2006b163 100644 --- a/alc/effects/chorus.cpp +++ b/alc/effects/chorus.cpp @@ -37,7 +37,7 @@ #include "alnumeric.h" #include "alspan.h" #include "alu.h" -#include "ambidefs.h" +#include "core/ambidefs.h" #include "effects/base.h" #include "math_defs.h" #include "opthelpers.h" diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index 8914835a..b190e75e 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -13,9 +13,9 @@ #include "alcontext.h" #include "almalloc.h" #include "alspan.h" -#include "ambidefs.h" #include "bformatdec.h" #include "buffer_storage.h" +#include "core/ambidefs.h" #include "core/filters/splitter.h" #include "effects/base.h" #include "effectslot.h" diff --git a/alc/hrtf.h b/alc/hrtf.h index 9bb7f65f..025bfa92 100644 --- a/alc/hrtf.h +++ b/alc/hrtf.h @@ -8,8 +8,8 @@ #include "almalloc.h" #include "alspan.h" -#include "ambidefs.h" #include "atomic.h" +#include "core/ambidefs.h" #include "core/bufferline.h" #include "core/filters/splitter.h" #include "intrusive_ptr.h" diff --git a/alc/panning.cpp b/alc/panning.cpp index c8db28c2..ad297ef7 100644 --- a/alc/panning.cpp +++ b/alc/panning.cpp @@ -48,8 +48,8 @@ #include "alstring.h" #include "alu.h" #include "ambdec.h" -#include "ambidefs.h" #include "bformatdec.h" +#include "core/ambidefs.h" #include "core/bs2b.h" #include "core/devformat.h" #include "core/uhjfilter.h" @@ -60,17 +60,6 @@ #include "opthelpers.h" -constexpr std::array AmbiScale::FromN3D; -constexpr std::array AmbiScale::FromSN3D; -constexpr std::array AmbiScale::FromFuMa; -constexpr std::array AmbiIndex::FromFuMa; -constexpr std::array AmbiIndex::FromFuMa2D; -constexpr std::array AmbiIndex::FromACN; -constexpr std::array AmbiIndex::From2D; -constexpr std::array AmbiIndex::OrderFromChannel; -constexpr std::array AmbiIndex::OrderFrom2DChannel; - - namespace { using namespace std::placeholders; diff --git a/core/ambidefs.cpp b/core/ambidefs.cpp new file mode 100644 index 00000000..ab36d50b --- /dev/null +++ b/core/ambidefs.cpp @@ -0,0 +1,15 @@ + +#include "config.h" + +#include "ambidefs.h" + + +constexpr std::array AmbiScale::FromN3D; +constexpr std::array AmbiScale::FromSN3D; +constexpr std::array AmbiScale::FromFuMa; +constexpr std::array AmbiIndex::FromFuMa; +constexpr std::array AmbiIndex::FromFuMa2D; +constexpr std::array AmbiIndex::FromACN; +constexpr std::array AmbiIndex::From2D; +constexpr std::array AmbiIndex::OrderFromChannel; +constexpr std::array AmbiIndex::OrderFrom2DChannel; diff --git a/core/ambidefs.h b/core/ambidefs.h new file mode 100644 index 00000000..05e3f5c8 --- /dev/null +++ b/core/ambidefs.h @@ -0,0 +1,135 @@ +#ifndef CORE_AMBIDEFS_H +#define CORE_AMBIDEFS_H + +#include +#include +#include + +using uint = unsigned int; + +/* The maximum number of Ambisonics channels. For a given order (o), the size + * needed will be (o+1)**2, thus zero-order has 1, first-order has 4, second- + * order has 9, third-order has 16, and fourth-order has 25. + */ +constexpr uint8_t MaxAmbiOrder{3}; +constexpr inline size_t AmbiChannelsFromOrder(size_t order) noexcept +{ return (order+1) * (order+1); } +constexpr size_t MaxAmbiChannels{AmbiChannelsFromOrder(MaxAmbiOrder)}; + +/* A bitmask of ambisonic channels for 0 to 4th order. This only specifies up + * to 4th order, which is the highest order a 32-bit mask value can specify (a + * 64-bit mask could handle up to 7th order). + */ +constexpr uint Ambi0OrderMask{0x00000001}; +constexpr uint Ambi1OrderMask{0x0000000f}; +constexpr uint Ambi2OrderMask{0x000001ff}; +constexpr uint Ambi3OrderMask{0x0000ffff}; +constexpr uint Ambi4OrderMask{0x01ffffff}; + +/* A bitmask of ambisonic channels with height information. If none of these + * channels are used/needed, there's no height (e.g. with most surround sound + * speaker setups). This is ACN ordering, with bit 0 being ACN 0, etc. + */ +constexpr uint AmbiPeriphonicMask{0xfe7ce4}; + +/* The maximum number of ambisonic channels for 2D (non-periphonic) + * representation. This is 2 per each order above zero-order, plus 1 for zero- + * order. Or simply, o*2 + 1. + */ +constexpr inline size_t Ambi2DChannelsFromOrder(size_t order) noexcept +{ return order*2 + 1; } +constexpr size_t MaxAmbi2DChannels{Ambi2DChannelsFromOrder(MaxAmbiOrder)}; + + +/* NOTE: These are scale factors as applied to Ambisonics content. Decoder + * coefficients should be divided by these values to get proper scalings. + */ +struct AmbiScale { + static constexpr std::array FromN3D{{ + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f + }}; + static constexpr std::array FromSN3D{{ + 1.000000000f, /* ACN 0, sqrt(1) */ + 1.732050808f, /* ACN 1, sqrt(3) */ + 1.732050808f, /* ACN 2, sqrt(3) */ + 1.732050808f, /* ACN 3, sqrt(3) */ + 2.236067978f, /* ACN 4, sqrt(5) */ + 2.236067978f, /* ACN 5, sqrt(5) */ + 2.236067978f, /* ACN 6, sqrt(5) */ + 2.236067978f, /* ACN 7, sqrt(5) */ + 2.236067978f, /* ACN 8, sqrt(5) */ + 2.645751311f, /* ACN 9, sqrt(7) */ + 2.645751311f, /* ACN 10, sqrt(7) */ + 2.645751311f, /* ACN 11, sqrt(7) */ + 2.645751311f, /* ACN 12, sqrt(7) */ + 2.645751311f, /* ACN 13, sqrt(7) */ + 2.645751311f, /* ACN 14, sqrt(7) */ + 2.645751311f, /* ACN 15, sqrt(7) */ + }}; + static constexpr std::array FromFuMa{{ + 1.414213562f, /* ACN 0 (W), sqrt(2) */ + 1.732050808f, /* ACN 1 (Y), sqrt(3) */ + 1.732050808f, /* ACN 2 (Z), sqrt(3) */ + 1.732050808f, /* ACN 3 (X), sqrt(3) */ + 1.936491673f, /* ACN 4 (V), sqrt(15)/2 */ + 1.936491673f, /* ACN 5 (T), sqrt(15)/2 */ + 2.236067978f, /* ACN 6 (R), sqrt(5) */ + 1.936491673f, /* ACN 7 (S), sqrt(15)/2 */ + 1.936491673f, /* ACN 8 (U), sqrt(15)/2 */ + 2.091650066f, /* ACN 9 (Q), sqrt(35/8) */ + 1.972026594f, /* ACN 10 (O), sqrt(35)/3 */ + 2.231093404f, /* ACN 11 (M), sqrt(224/45) */ + 2.645751311f, /* ACN 12 (K), sqrt(7) */ + 2.231093404f, /* ACN 13 (L), sqrt(224/45) */ + 1.972026594f, /* ACN 14 (N), sqrt(35)/3 */ + 2.091650066f, /* ACN 15 (P), sqrt(35/8) */ + }}; +}; + +struct AmbiIndex { + static constexpr std::array FromFuMa{{ + 0, /* W */ + 3, /* X */ + 1, /* Y */ + 2, /* Z */ + 6, /* R */ + 7, /* S */ + 5, /* T */ + 8, /* U */ + 4, /* V */ + 12, /* K */ + 13, /* L */ + 11, /* M */ + 14, /* N */ + 10, /* O */ + 15, /* P */ + 9, /* Q */ + }}; + static constexpr std::array FromFuMa2D{{ + 0, /* W */ + 3, /* X */ + 1, /* Y */ + 8, /* U */ + 4, /* V */ + 15, /* P */ + 9, /* Q */ + }}; + + static constexpr std::array FromACN{{ + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15 + }}; + static constexpr std::array From2D{{ + 0, 1,3, 4,8, 9,15 + }}; + + static constexpr std::array OrderFromChannel{{ + 0, 1,1,1, 2,2,2,2,2, 3,3,3,3,3,3,3, + }}; + static constexpr std::array OrderFrom2DChannel{{ + 0, 1,1, 2,2, 3,3, + }}; +}; + +#endif /* CORE_AMBIDEFS_H */ -- cgit v1.2.3 From 1ad944555c36620f901d13be04e3eacc7e8dcee0 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 12 Dec 2020 14:15:17 -0800 Subject: Move some HRTF definitions to a separate header --- CMakeLists.txt | 1 + alc/alu.h | 2 -- alc/hrtf.h | 33 ++---------------------------- alc/mixer/defs.h | 3 +++ alc/mixer/hrtfbase.h | 5 +++-- alc/mixer/hrtfdefs.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ alc/mixer/mixer_c.cpp | 6 +++--- alc/mixer/mixer_neon.cpp | 5 +++-- alc/mixer/mixer_sse.cpp | 5 +++-- alc/voice.cpp | 1 + alc/voice.h | 8 -------- 11 files changed, 71 insertions(+), 50 deletions(-) create mode 100644 alc/mixer/hrtfdefs.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index ea09bbe2..4ee16a6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -701,6 +701,7 @@ set(ALC_OBJS alc/voice.h alc/mixer/defs.h alc/mixer/hrtfbase.h + alc/mixer/hrtfdefs.h alc/mixer/mixer_c.cpp ) diff --git a/alc/alu.h b/alc/alu.h index 1393b824..b1f48045 100644 --- a/alc/alu.h +++ b/alc/alu.h @@ -29,8 +29,6 @@ extern MixerFunc MixSamples; constexpr float GainMixMax{1000.0f}; /* +60dB */ -constexpr float GainSilenceThreshold{0.00001f}; /* -100dB */ - constexpr float SpeedOfSoundMetersPerSec{343.3f}; constexpr float AirAbsorbGainHF{0.99426f}; /* -0.05dB */ diff --git a/alc/hrtf.h b/alc/hrtf.h index e0e406dc..573cef53 100644 --- a/alc/hrtf.h +++ b/alc/hrtf.h @@ -13,26 +13,10 @@ #include "core/bufferline.h" #include "core/filters/splitter.h" #include "intrusive_ptr.h" +#include "mixer/hrtfdefs.h" #include "vector.h" -#define HRTF_HISTORY_BITS 6 -#define HRTF_HISTORY_LENGTH (1<; -using HrirArray = std::array; -using ubyte = unsigned char; -using ubyte2 = std::array; -using ushort = unsigned short; -using uint = unsigned int; - struct HrtfStore { RefCount mRef; @@ -65,13 +49,6 @@ struct HrtfStore { using HrtfStorePtr = al::intrusive_ptr; -struct HrtfFilter { - alignas(16) HrirArray Coeffs; - std::array Delay; - float Gain; -}; - - struct EvRadians { float value; }; struct AzRadians { float value; }; struct AngularPoint { @@ -79,13 +56,7 @@ struct AngularPoint { AzRadians Azim; }; -#define HRTF_DIRECT_DELAY 192 -struct HrtfChannelState { - std::array mDelay{}; - BandSplitter mSplitter; - float mHfScale{}; - alignas(16) HrirArray mCoeffs{}; -}; + struct DirectHrtfState { std::array mTemp; diff --git a/alc/mixer/defs.h b/alc/mixer/defs.h index 897b23ff..69042ea4 100644 --- a/alc/mixer/defs.h +++ b/alc/mixer/defs.h @@ -2,6 +2,7 @@ #define MIXER_DEFS_H #include +#include #include "alspan.h" #include "core/bufferline.h" @@ -25,6 +26,8 @@ constexpr int MixerFracMask{MixerFracOne - 1}; */ constexpr int MaxResamplerPadding{48}; +constexpr float GainSilenceThreshold{0.00001f}; /* -100dB */ + enum class Resampler { Point, diff --git a/alc/mixer/hrtfbase.h b/alc/mixer/hrtfbase.h index 4f5b399a..f25801b5 100644 --- a/alc/mixer/hrtfbase.h +++ b/alc/mixer/hrtfbase.h @@ -2,10 +2,11 @@ #define MIXER_HRTFBASE_H #include +#include -#include "../hrtf.h" +#include "almalloc.h" +#include "hrtfdefs.h" #include "opthelpers.h" -#include "voice.h" using uint = unsigned int; diff --git a/alc/mixer/hrtfdefs.h b/alc/mixer/hrtfdefs.h new file mode 100644 index 00000000..5f9711cf --- /dev/null +++ b/alc/mixer/hrtfdefs.h @@ -0,0 +1,52 @@ +#ifndef MIXER_HRTFDEFS_H +#define MIXER_HRTFDEFS_H + +#include + +#include "core/ambidefs.h" +#include "core/bufferline.h" +#include "core/filters/splitter.h" + + +#define HRTF_HISTORY_BITS 6 +#define HRTF_HISTORY_LENGTH (1<; +using HrirArray = std::array; +using ubyte = unsigned char; +using ubyte2 = std::array; +using ushort = unsigned short; +using uint = unsigned int; + + +struct MixHrtfFilter { + const HrirArray *Coeffs; + std::array Delay; + float Gain; + float GainStep; +}; + +struct HrtfFilter { + alignas(16) HrirArray Coeffs; + std::array Delay; + float Gain; +}; + + +struct HrtfChannelState { + std::array mDelay{}; + BandSplitter mSplitter; + float mHfScale{}; + alignas(16) HrirArray mCoeffs{}; +}; + +#endif /* MIXER_HRTFDEFS_H */ diff --git a/alc/mixer/mixer_c.cpp b/alc/mixer/mixer_c.cpp index c2fdec66..e7e66947 100644 --- a/alc/mixer/mixer_c.cpp +++ b/alc/mixer/mixer_c.cpp @@ -1,7 +1,7 @@ #include "config.h" #include - +#include #include #include "alcmain.h" @@ -172,7 +172,7 @@ void Mix_(const al::span InSamples, const al::span std::numeric_limits::epsilon())) + if(!(std::abs(step) > std::numeric_limits::epsilon())) gain = *TargetGains; else { @@ -191,7 +191,7 @@ void Mix_(const al::span InSamples, const al::span GainSilenceThreshold)) + if(!(std::abs(gain) > GainSilenceThreshold)) continue; for(;pos != InSamples.size();++pos) dst[pos] += InSamples[pos] * gain; diff --git a/alc/mixer/mixer_neon.cpp b/alc/mixer/mixer_neon.cpp index 0c3367a9..af8f6b0c 100644 --- a/alc/mixer/mixer_neon.cpp +++ b/alc/mixer/mixer_neon.cpp @@ -2,6 +2,7 @@ #include +#include #include #include "alnumeric.h" @@ -234,7 +235,7 @@ void Mix_(const al::span InSamples, const al::span std::numeric_limits::epsilon())) + if(!(std::abs(step) > std::numeric_limits::epsilon())) gain = *TargetGains; else { @@ -283,7 +284,7 @@ void Mix_(const al::span InSamples, const al::span GainSilenceThreshold)) + if(!(std::abs(gain) > GainSilenceThreshold)) continue; if(size_t todo{(InSamples.size()-pos) >> 2}) { diff --git a/alc/mixer/mixer_sse.cpp b/alc/mixer/mixer_sse.cpp index 6adbc7fb..85b2f1ce 100644 --- a/alc/mixer/mixer_sse.cpp +++ b/alc/mixer/mixer_sse.cpp @@ -2,6 +2,7 @@ #include +#include #include #include "alnumeric.h" @@ -198,7 +199,7 @@ void Mix_(const al::span InSamples, const al::span std::numeric_limits::epsilon())) + if(!(std::abs(step) > std::numeric_limits::epsilon())) gain = *TargetGains; else { @@ -246,7 +247,7 @@ void Mix_(const al::span InSamples, const al::span GainSilenceThreshold)) + if(!(std::abs(gain) > GainSilenceThreshold)) continue; if(size_t todo{(InSamples.size()-pos) >> 2}) { diff --git a/alc/voice.cpp b/alc/voice.cpp index eec34246..a188a04d 100644 --- a/alc/voice.cpp +++ b/alc/voice.cpp @@ -56,6 +56,7 @@ #include "inprogext.h" #include "logging.h" #include "mixer/defs.h" +#include "mixer/hrtfdefs.h" #include "opthelpers.h" #include "ringbuffer.h" #include "threads.h" diff --git a/alc/voice.h b/alc/voice.h index 00012de3..ccffaf01 100644 --- a/alc/voice.h +++ b/alc/voice.h @@ -42,14 +42,6 @@ enum { }; -struct MixHrtfFilter { - const HrirArray *Coeffs; - std::array Delay; - float Gain; - float GainStep; -}; - - struct DirectParams { BiquadFilter LowPass; BiquadFilter HighPass; -- cgit v1.2.3 From 14df53411402bae0e5dcdea8bc0d2d3ba30e7923 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 12 Dec 2020 14:31:29 -0800 Subject: Use a separate list for core objects --- CMakeLists.txt | 65 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 31 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ee16a6f..55666e4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -558,8 +558,8 @@ endif() check_symbol_exists(getopt unistd.h HAVE_GETOPT) -# Common sources used by both the OpenAL implementation library and potentially -# the OpenAL router. +# Common sources used by both the OpenAL implementation library, the OpenAL +# router, and certain tools and examples. set(COMMON_OBJS common/albyte.h common/alcomplex.cpp @@ -592,8 +592,32 @@ set(COMMON_OBJS common/threads.cpp common/threads.h common/vecmat.h - common/vector.h -) + common/vector.h) + +# Core library routines +set(CORE_OBJS + core/ambidefs.cpp + core/ambidefs.h + core/bs2b.cpp + core/bs2b.h + core/bsinc_defs.h + core/bsinc_tables.cpp + core/bsinc_tables.h + core/bufferline.h + core/devformat.cpp + core/devformat.h + core/filters/biquad.h + core/filters/biquad.cpp + core/filters/nfc.cpp + core/filters/nfc.h + core/filters/splitter.cpp + core/filters/splitter.h + core/mastering.cpp + core/mastering.h + core/uhjfilter.cpp + core/uhjfilter.h) + +# AL and related routines set(OPENAL_OBJS al/auxeffectslot.cpp al/auxeffectslot.h @@ -626,30 +650,10 @@ set(OPENAL_OBJS al/listener.h al/source.cpp al/source.h - al/state.cpp -) -set(ALC_OBJS - core/ambidefs.cpp - core/ambidefs.h - core/bs2b.cpp - core/bs2b.h - core/bsinc_defs.h - core/bsinc_tables.cpp - core/bsinc_tables.h - core/bufferline.h - core/devformat.cpp - core/devformat.h - core/filters/biquad.h - core/filters/biquad.cpp - core/filters/nfc.cpp - core/filters/nfc.h - core/filters/splitter.cpp - core/filters/splitter.h - core/mastering.cpp - core/mastering.h - core/uhjfilter.cpp - core/uhjfilter.h + al/state.cpp) +# ALC and related routines +set(ALC_OBJS alc/alc.cpp alc/alcmain.h alc/alu.cpp @@ -702,8 +706,7 @@ set(ALC_OBJS alc/mixer/defs.h alc/mixer/hrtfbase.h alc/mixer/hrtfdefs.h - alc/mixer/mixer_c.cpp -) + alc/mixer/mixer_c.cpp) set(CPU_EXTS "Default") @@ -1226,7 +1229,7 @@ set(IMPL_TARGET OpenAL) # Either OpenAL or soft_oal. # Build main library if(LIBTYPE STREQUAL "STATIC") - add_library(${IMPL_TARGET} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS}) + add_library(${IMPL_TARGET} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS} ${CORE_OBJS}) target_compile_definitions(${IMPL_TARGET} PUBLIC AL_LIBTYPE_STATIC) target_link_libraries(${IMPL_TARGET} PRIVATE ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) if(WIN32) @@ -1275,7 +1278,7 @@ else() # !important: for osx framework public header works, the headers must be in # the project set(TARGET_PUBLIC_HEADERS include/AL/al.h include/AL/alc.h include/AL/alext.h include/AL/efx.h) - add_library(${IMPL_TARGET} SHARED ${OPENAL_OBJS} ${ALC_OBJS} ${RC_CONFIG} + add_library(${IMPL_TARGET} SHARED ${OPENAL_OBJS} ${ALC_OBJS} ${CORE_OBJS} ${RC_CONFIG} ${TARGET_PUBLIC_HEADERS}) if(WIN32) set_target_properties(${IMPL_TARGET} PROPERTIES PREFIX "") -- cgit v1.2.3 From e179bf0a12e80eb41041469bc04ba1fbcffe11e8 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 12 Dec 2020 14:58:09 -0800 Subject: Move the mixer functions to core --- CMakeLists.txt | 28 ++--- alc/alcmain.h | 2 +- alc/alu.cpp | 2 +- alc/converter.cpp | 1 - alc/converter.h | 2 +- alc/hrtf.h | 2 +- alc/mixer/defs.h | 100 --------------- alc/mixer/hrtfbase.h | 159 ------------------------ alc/mixer/hrtfdefs.h | 52 -------- alc/mixer/mixer_c.cpp | 198 ----------------------------- alc/mixer/mixer_neon.cpp | 303 --------------------------------------------- alc/mixer/mixer_sse.cpp | 266 --------------------------------------- alc/mixer/mixer_sse2.cpp | 85 ------------- alc/mixer/mixer_sse3.cpp | 0 alc/mixer/mixer_sse41.cpp | 90 -------------- alc/voice.cpp | 4 +- alc/voice.h | 2 +- core/mixer/defs.h | 100 +++++++++++++++ core/mixer/hrtfbase.h | 159 ++++++++++++++++++++++++ core/mixer/hrtfdefs.h | 52 ++++++++ core/mixer/mixer_c.cpp | 198 +++++++++++++++++++++++++++++ core/mixer/mixer_neon.cpp | 303 +++++++++++++++++++++++++++++++++++++++++++++ core/mixer/mixer_sse.cpp | 266 +++++++++++++++++++++++++++++++++++++++ core/mixer/mixer_sse2.cpp | 85 +++++++++++++ core/mixer/mixer_sse3.cpp | 0 core/mixer/mixer_sse41.cpp | 90 ++++++++++++++ 26 files changed, 1274 insertions(+), 1275 deletions(-) delete mode 100644 alc/mixer/defs.h delete mode 100644 alc/mixer/hrtfbase.h delete mode 100644 alc/mixer/hrtfdefs.h delete mode 100644 alc/mixer/mixer_c.cpp delete mode 100644 alc/mixer/mixer_neon.cpp delete mode 100644 alc/mixer/mixer_sse.cpp delete mode 100644 alc/mixer/mixer_sse2.cpp delete mode 100644 alc/mixer/mixer_sse3.cpp delete mode 100644 alc/mixer/mixer_sse41.cpp create mode 100644 core/mixer/defs.h create mode 100644 core/mixer/hrtfbase.h create mode 100644 core/mixer/hrtfdefs.h create mode 100644 core/mixer/mixer_c.cpp create mode 100644 core/mixer/mixer_neon.cpp create mode 100644 core/mixer/mixer_sse.cpp create mode 100644 core/mixer/mixer_sse2.cpp create mode 100644 core/mixer/mixer_sse3.cpp create mode 100644 core/mixer/mixer_sse41.cpp (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 55666e4f..a5ae4d43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -615,7 +615,11 @@ set(CORE_OBJS core/mastering.cpp core/mastering.h core/uhjfilter.cpp - core/uhjfilter.h) + core/uhjfilter.h + core/mixer/defs.h + core/mixer/hrtfbase.h + core/mixer/hrtfdefs.h + core/mixer/mixer_c.cpp) # AL and related routines set(OPENAL_OBJS @@ -702,11 +706,7 @@ set(ALC_OBJS alc/panning.cpp alc/uiddefs.cpp alc/voice.cpp - alc/voice.h - alc/mixer/defs.h - alc/mixer/hrtfbase.h - alc/mixer/hrtfdefs.h - alc/mixer/mixer_c.cpp) + alc/voice.h) set(CPU_EXTS "Default") @@ -725,9 +725,9 @@ if(HAVE_XMMINTRIN_H AND HAVE_EMMINTRIN_H) if(ALSOFT_CPUEXT_SSE AND ALSOFT_CPUEXT_SSE2) set(HAVE_SSE 1) set(HAVE_SSE2 1) - set(ALC_OBJS ${ALC_OBJS} alc/mixer/mixer_sse.cpp alc/mixer/mixer_sse2.cpp) + set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse.cpp core/mixer/mixer_sse2.cpp) if(SSE2_SWITCH) - set_source_files_properties(alc/mixer/mixer_sse.cpp alc/mixer/mixer_sse2.cpp + set_source_files_properties(core/mixer/mixer_sse.cpp core/mixer/mixer_sse2.cpp PROPERTIES COMPILE_FLAGS "${SSE2_SWITCH}") endif() set(CPU_EXTS "${CPU_EXTS}, SSE, SSE2") @@ -745,9 +745,9 @@ if(HAVE_EMMINTRIN_H) option(ALSOFT_CPUEXT_SSE3 "Enable SSE3 support" ON) if(HAVE_SSE2 AND ALSOFT_CPUEXT_SSE3) set(HAVE_SSE3 1) - set(ALC_OBJS ${ALC_OBJS} alc/mixer/mixer_sse3.cpp) + set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse3.cpp) if(SSE2_SWITCH) - set_source_files_properties(alc/mixer/mixer_sse3.cpp PROPERTIES + set_source_files_properties(core/mixer/mixer_sse3.cpp PROPERTIES COMPILE_FLAGS "${SSE3_SWITCH}") endif() set(CPU_EXTS "${CPU_EXTS}, SSE3") @@ -762,9 +762,9 @@ if(HAVE_SMMINTRIN_H) option(ALSOFT_CPUEXT_SSE4_1 "Enable SSE4.1 support" ON) if(HAVE_SSE3 AND ALSOFT_CPUEXT_SSE4_1) set(HAVE_SSE4_1 1) - set(ALC_OBJS ${ALC_OBJS} alc/mixer/mixer_sse41.cpp) + set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse41.cpp) if(SSE4_1_SWITCH) - set_source_files_properties(alc/mixer/mixer_sse41.cpp PROPERTIES + set_source_files_properties(core/mixer/mixer_sse41.cpp PROPERTIES COMPILE_FLAGS "${SSE4_1_SWITCH}") endif() set(CPU_EXTS "${CPU_EXTS}, SSE4.1") @@ -780,9 +780,9 @@ if(HAVE_ARM_NEON_H) option(ALSOFT_CPUEXT_NEON "Enable ARM Neon support" ON) if(ALSOFT_CPUEXT_NEON) set(HAVE_NEON 1) - set(ALC_OBJS ${ALC_OBJS} alc/mixer/mixer_neon.cpp) + set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_neon.cpp) if(FPU_NEON_SWITCH) - set_source_files_properties(alc/mixer/mixer_neon.cpp PROPERTIES + set_source_files_properties(core/mixer/mixer_neon.cpp PROPERTIES COMPILE_FLAGS "${FPU_NEON_SWITCH}") endif() set(CPU_EXTS "${CPU_EXTS}, Neon") diff --git a/alc/alcmain.h b/alc/alcmain.h index ef52afc7..c1432463 100644 --- a/alc/alcmain.h +++ b/alc/alcmain.h @@ -26,10 +26,10 @@ #include "core/bufferline.h" #include "core/devformat.h" #include "core/filters/splitter.h" +#include "core/mixer/defs.h" #include "hrtf.h" #include "inprogext.h" #include "intrusive_ptr.h" -#include "mixer/defs.h" #include "vector.h" class BFormatDec; diff --git a/alc/alu.cpp b/alc/alu.cpp index cd180f41..eefeca4d 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -65,6 +65,7 @@ #include "core/filters/nfc.h" #include "core/filters/splitter.h" #include "core/mastering.h" +#include "core/mixer/defs.h" #include "core/uhjfilter.h" #include "cpu_caps.h" #include "effects/base.h" @@ -73,7 +74,6 @@ #include "hrtf.h" #include "inprogext.h" #include "math_defs.h" -#include "mixer/defs.h" #include "opthelpers.h" #include "ringbuffer.h" #include "strutils.h" diff --git a/alc/converter.cpp b/alc/converter.cpp index 9a3fa2f2..342085c5 100644 --- a/alc/converter.cpp +++ b/alc/converter.cpp @@ -12,7 +12,6 @@ #include "albyte.h" #include "alnumeric.h" #include "fpu_ctrl.h" -#include "mixer/defs.h" struct CTag; struct CopyTag; diff --git a/alc/converter.h b/alc/converter.h index f84efd72..ffcfbc18 100644 --- a/alc/converter.h +++ b/alc/converter.h @@ -6,7 +6,7 @@ #include "almalloc.h" #include "core/devformat.h" -#include "mixer/defs.h" +#include "core/mixer/defs.h" using uint = unsigned int; diff --git a/alc/hrtf.h b/alc/hrtf.h index 573cef53..caa35a0a 100644 --- a/alc/hrtf.h +++ b/alc/hrtf.h @@ -12,8 +12,8 @@ #include "core/ambidefs.h" #include "core/bufferline.h" #include "core/filters/splitter.h" +#include "core/mixer/hrtfdefs.h" #include "intrusive_ptr.h" -#include "mixer/hrtfdefs.h" #include "vector.h" diff --git a/alc/mixer/defs.h b/alc/mixer/defs.h deleted file mode 100644 index a34fba26..00000000 --- a/alc/mixer/defs.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef MIXER_DEFS_H -#define MIXER_DEFS_H - -#include -#include - -#include "alspan.h" -#include "core/bufferline.h" - -struct HrtfChannelState; -struct HrtfFilter; -struct MixHrtfFilter; - -using uint = unsigned int; -using float2 = std::array; - - -constexpr int MixerFracBits{12}; -constexpr int MixerFracOne{1 << MixerFracBits}; -constexpr int MixerFracMask{MixerFracOne - 1}; - -/* Maximum number of samples to pad on the ends of a buffer for resampling. - * Note that the padding is symmetric (half at the beginning and half at the - * end)! - */ -constexpr int MaxResamplerPadding{48}; - -constexpr float GainSilenceThreshold{0.00001f}; /* -100dB */ - - -enum class Resampler { - Point, - Linear, - Cubic, - FastBSinc12, - BSinc12, - FastBSinc24, - BSinc24, - - Max = BSinc24 -}; - -/* 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 { - float sf; /* Scale interpolation factor. */ - uint m; /* Coefficient count. */ - uint l; /* Left coefficient offset. */ - /* Filter coefficients, followed by the phase, scale, and scale-phase - * delta coefficients. Starting at phase index 0, each subsequent phase - * index follows contiguously. - */ - const float *filter; -}; - -union InterpState { - BsincState bsinc; -}; - -using ResamplerFunc = const float*(*)(const InterpState *state, const float *RESTRICT src, - uint frac, uint increment, const al::span dst); - -ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState *state); - - -template -const float *Resample_(const InterpState *state, const float *RESTRICT src, uint frac, - uint increment, const al::span dst); - -template -void Mix_(const al::span InSamples, const al::span OutBuffer, - float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos); - -template -void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize); -template -void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize); -template -void MixDirectHrtf_(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, - const al::span InSamples, float2 *AccumSamples, - float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize); - -/* Vectorized resampler helpers */ -inline void InitPosArrays(uint frac, uint increment, uint *frac_arr, uint *pos_arr, size_t size) -{ - pos_arr[0] = 0; - frac_arr[0] = frac; - for(size_t i{1};i < size;i++) - { - const uint frac_tmp{frac_arr[i-1] + increment}; - pos_arr[i] = pos_arr[i-1] + (frac_tmp>>MixerFracBits); - frac_arr[i] = frac_tmp&MixerFracMask; - } -} - -#endif /* MIXER_DEFS_H */ diff --git a/alc/mixer/hrtfbase.h b/alc/mixer/hrtfbase.h deleted file mode 100644 index f25801b5..00000000 --- a/alc/mixer/hrtfbase.h +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef MIXER_HRTFBASE_H -#define MIXER_HRTFBASE_H - -#include -#include - -#include "almalloc.h" -#include "hrtfdefs.h" -#include "opthelpers.h" - - -using uint = unsigned int; - -using ApplyCoeffsT = void(&)(float2 *RESTRICT Values, const uint_fast32_t irSize, - const HrirArray &Coeffs, const float left, const float right); - -template -inline void MixHrtfBase(const float *InSamples, float2 *RESTRICT AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize) -{ - ASSUME(BufferSize > 0); - - const HrirArray &Coeffs = *hrtfparams->Coeffs; - const float gainstep{hrtfparams->GainStep}; - const float gain{hrtfparams->Gain}; - - size_t ldelay{HRTF_HISTORY_LENGTH - hrtfparams->Delay[0]}; - size_t rdelay{HRTF_HISTORY_LENGTH - hrtfparams->Delay[1]}; - float stepcount{0.0f}; - for(size_t i{0u};i < BufferSize;++i) - { - const float g{gain + gainstep*stepcount}; - const float left{InSamples[ldelay++] * g}; - const float right{InSamples[rdelay++] * g}; - ApplyCoeffs(AccumSamples+i, IrSize, Coeffs, left, right); - - stepcount += 1.0f; - } -} - -template -inline void MixHrtfBlendBase(const float *InSamples, float2 *RESTRICT AccumSamples, - const uint IrSize, const HrtfFilter *oldparams, const MixHrtfFilter *newparams, - const size_t BufferSize) -{ - ASSUME(BufferSize > 0); - - const auto &OldCoeffs = oldparams->Coeffs; - const float oldGainStep{oldparams->Gain / static_cast(BufferSize)}; - const auto &NewCoeffs = *newparams->Coeffs; - const float newGainStep{newparams->GainStep}; - - if LIKELY(oldparams->Gain > GainSilenceThreshold) - { - size_t ldelay{HRTF_HISTORY_LENGTH - oldparams->Delay[0]}; - size_t rdelay{HRTF_HISTORY_LENGTH - oldparams->Delay[1]}; - auto stepcount = static_cast(BufferSize); - for(size_t i{0u};i < BufferSize;++i) - { - const float g{oldGainStep*stepcount}; - const float left{InSamples[ldelay++] * g}; - const float right{InSamples[rdelay++] * g}; - ApplyCoeffs(AccumSamples+i, IrSize, OldCoeffs, left, right); - - stepcount -= 1.0f; - } - } - - if LIKELY(newGainStep*static_cast(BufferSize) > GainSilenceThreshold) - { - size_t ldelay{HRTF_HISTORY_LENGTH+1 - newparams->Delay[0]}; - size_t rdelay{HRTF_HISTORY_LENGTH+1 - newparams->Delay[1]}; - float stepcount{1.0f}; - for(size_t i{1u};i < BufferSize;++i) - { - const float g{newGainStep*stepcount}; - const float left{InSamples[ldelay++] * g}; - const float right{InSamples[rdelay++] * g}; - ApplyCoeffs(AccumSamples+i, IrSize, NewCoeffs, left, right); - - stepcount += 1.0f; - } - } -} - -template -inline void MixDirectHrtfBase(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, - const al::span InSamples, float2 *RESTRICT AccumSamples, - float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) -{ - ASSUME(BufferSize > 0); - - /* Add the existing signal directly to the accumulation buffer, unfiltered, - * and with a delay to align with the input delay. - */ - for(size_t i{0};i < BufferSize;++i) - { - AccumSamples[HRTF_DIRECT_DELAY+i][0] += LeftOut[i]; - AccumSamples[HRTF_DIRECT_DELAY+i][1] += RightOut[i]; - } - - for(const FloatBufferLine &input : InSamples) - { - /* For dual-band processing, the signal needs extra scaling applied to - * the high frequency response. The band-splitter alone creates a - * frequency-dependent phase shift, which is not ideal. To counteract - * it, combine it with a backwards phase shift. - */ - - /* Load the input signal backwards, into a temp buffer with delay - * padding. The delay serves to reduce the error caused by the IIR - * filter's phase shift on a partial input. - */ - al::span tempbuf{al::assume_aligned<16>(TempBuf), HRTF_DIRECT_DELAY+BufferSize}; - auto tmpiter = std::reverse_copy(input.begin(), input.begin()+BufferSize, tempbuf.begin()); - std::copy(ChanState->mDelay.cbegin(), ChanState->mDelay.cend(), tmpiter); - - /* Save the unfiltered newest input samples for next time. */ - std::copy_n(tempbuf.begin(), ChanState->mDelay.size(), ChanState->mDelay.begin()); - - /* Apply the all-pass on the reversed signal and reverse the resulting - * sample array. This produces the forward response with a backwards - * phase shift (+n degrees becomes -n degrees). - */ - ChanState->mSplitter.applyAllpass(tempbuf); - tempbuf = tempbuf.subspan(); - std::reverse(tempbuf.begin(), tempbuf.end()); - - /* Now apply the HF scale with the band-splitter. This applies the - * forward phase shift, which cancels out with the backwards phase - * shift to get the original phase on the scaled signal. - */ - ChanState->mSplitter.processHfScale(tempbuf, ChanState->mHfScale); - - /* Now apply the HRIR coefficients to this channel. */ - const auto &Coeffs = ChanState->mCoeffs; - for(size_t i{0u};i < BufferSize;++i) - { - const float insample{tempbuf[i]}; - ApplyCoeffs(AccumSamples+i, IrSize, Coeffs, insample, insample); - } - - ++ChanState; - } - - for(size_t i{0u};i < BufferSize;++i) - LeftOut[i] = AccumSamples[i][0]; - for(size_t i{0u};i < BufferSize;++i) - RightOut[i] = AccumSamples[i][1]; - - /* Copy the new in-progress accumulation values to the front and clear the - * following samples for the next mix. - */ - auto accum_iter = std::copy_n(AccumSamples+BufferSize, HRIR_LENGTH+HRTF_DIRECT_DELAY, - AccumSamples); - std::fill_n(accum_iter, BufferSize, float2{}); -} - -#endif /* MIXER_HRTFBASE_H */ diff --git a/alc/mixer/hrtfdefs.h b/alc/mixer/hrtfdefs.h deleted file mode 100644 index 5f9711cf..00000000 --- a/alc/mixer/hrtfdefs.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef MIXER_HRTFDEFS_H -#define MIXER_HRTFDEFS_H - -#include - -#include "core/ambidefs.h" -#include "core/bufferline.h" -#include "core/filters/splitter.h" - - -#define HRTF_HISTORY_BITS 6 -#define HRTF_HISTORY_LENGTH (1<; -using HrirArray = std::array; -using ubyte = unsigned char; -using ubyte2 = std::array; -using ushort = unsigned short; -using uint = unsigned int; - - -struct MixHrtfFilter { - const HrirArray *Coeffs; - std::array Delay; - float Gain; - float GainStep; -}; - -struct HrtfFilter { - alignas(16) HrirArray Coeffs; - std::array Delay; - float Gain; -}; - - -struct HrtfChannelState { - std::array mDelay{}; - BandSplitter mSplitter; - float mHfScale{}; - alignas(16) HrirArray mCoeffs{}; -}; - -#endif /* MIXER_HRTFDEFS_H */ diff --git a/alc/mixer/mixer_c.cpp b/alc/mixer/mixer_c.cpp deleted file mode 100644 index 24ccd175..00000000 --- a/alc/mixer/mixer_c.cpp +++ /dev/null @@ -1,198 +0,0 @@ -#include "config.h" - -#include -#include -#include - -#include "alnumeric.h" -#include "core/bsinc_tables.h" -#include "defs.h" -#include "hrtfbase.h" - -struct CTag; -struct CopyTag; -struct PointTag; -struct LerpTag; -struct CubicTag; -struct BSincTag; -struct FastBSincTag; - - -namespace { - -constexpr uint FracPhaseBitDiff{MixerFracBits - BSincPhaseBits}; -constexpr uint FracPhaseDiffOne{1 << FracPhaseBitDiff}; - -inline float do_point(const InterpState&, const float *RESTRICT vals, const uint) -{ return vals[0]; } -inline float do_lerp(const InterpState&, const float *RESTRICT vals, const uint frac) -{ return lerp(vals[0], vals[1], static_cast(frac)*(1.0f/MixerFracOne)); } -inline float do_cubic(const InterpState&, const float *RESTRICT vals, const uint frac) -{ return cubic(vals[0], vals[1], vals[2], vals[3], static_cast(frac)*(1.0f/MixerFracOne)); } -inline float do_bsinc(const InterpState &istate, const float *RESTRICT vals, const uint frac) -{ - const size_t m{istate.bsinc.m}; - - // Calculate the phase index and factor. - const uint pi{frac >> FracPhaseBitDiff}; - const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; - - const float *fil{istate.bsinc.filter + m*pi*4}; - const float *phd{fil + m}; - const float *scd{phd + m}; - const float *spd{scd + m}; - - // Apply the scale and phase interpolated filter. - float r{0.0f}; - for(size_t j_f{0};j_f < m;j_f++) - r += (fil[j_f] + istate.bsinc.sf*scd[j_f] + pf*(phd[j_f] + istate.bsinc.sf*spd[j_f])) * vals[j_f]; - return r; -} -inline float do_fastbsinc(const InterpState &istate, const float *RESTRICT vals, const uint frac) -{ - const size_t m{istate.bsinc.m}; - - // Calculate the phase index and factor. - const uint pi{frac >> FracPhaseBitDiff}; - const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; - - const float *fil{istate.bsinc.filter + m*pi*4}; - const float *phd{fil + m}; - - // Apply the phase interpolated filter. - float r{0.0f}; - for(size_t j_f{0};j_f < m;j_f++) - r += (fil[j_f] + pf*phd[j_f]) * vals[j_f]; - return r; -} - -using SamplerT = float(&)(const InterpState&, const float*RESTRICT, const uint); -template -const float *DoResample(const InterpState *state, const float *RESTRICT src, uint frac, - uint increment, const al::span dst) -{ - const InterpState istate{*state}; - for(float &out : dst) - { - out = Sampler(istate, src, frac); - - frac += increment; - src += frac>>MixerFracBits; - frac &= MixerFracMask; - } - return dst.data(); -} - -inline void ApplyCoeffs(float2 *RESTRICT Values, const uint_fast32_t IrSize, - const HrirArray &Coeffs, const float left, const float right) -{ - ASSUME(IrSize >= MIN_IR_LENGTH); - for(size_t c{0};c < IrSize;++c) - { - Values[c][0] += Coeffs[c][0] * left; - Values[c][1] += Coeffs[c][1] * right; - } -} - -} // namespace - -template<> -const float *Resample_(const InterpState*, const float *RESTRICT src, uint, uint, - const al::span dst) -{ -#if defined(HAVE_SSE) || defined(HAVE_NEON) - /* Avoid copying the source data if it's aligned like the destination. */ - if((reinterpret_cast(src)&15) == (reinterpret_cast(dst.data())&15)) - return src; -#endif - std::copy_n(src, dst.size(), dst.begin()); - return dst.data(); -} - -template<> -const float *Resample_(const InterpState *state, const float *RESTRICT src, - uint frac, uint increment, const al::span dst) -{ return DoResample(state, src, frac, increment, dst); } - -template<> -const float *Resample_(const InterpState *state, const float *RESTRICT src, - uint frac, uint increment, const al::span dst) -{ return DoResample(state, src, frac, increment, dst); } - -template<> -const float *Resample_(const InterpState *state, const float *RESTRICT src, - uint frac, uint increment, const al::span dst) -{ return DoResample(state, src-1, frac, increment, dst); } - -template<> -const float *Resample_(const InterpState *state, const float *RESTRICT src, - uint frac, uint increment, const al::span dst) -{ return DoResample(state, src-state->bsinc.l, frac, increment, dst); } - -template<> -const float *Resample_(const InterpState *state, const float *RESTRICT src, - uint frac, uint increment, const al::span dst) -{ return DoResample(state, src-state->bsinc.l, frac, increment, dst); } - - -template<> -void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize) -{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, BufferSize); } - -template<> -void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize) -{ - MixHrtfBlendBase(InSamples, AccumSamples, IrSize, oldparams, newparams, - BufferSize); -} - -template<> -void MixDirectHrtf_(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, - const al::span InSamples, float2 *AccumSamples, - float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) -{ - MixDirectHrtfBase(LeftOut, RightOut, InSamples, AccumSamples, TempBuf, ChanState, - IrSize, BufferSize); -} - - -template<> -void Mix_(const al::span InSamples, const al::span OutBuffer, - float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos) -{ - const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; - const auto min_len = minz(Counter, InSamples.size()); - for(FloatBufferLine &output : OutBuffer) - { - float *RESTRICT dst{al::assume_aligned<16>(output.data()+OutPos)}; - float gain{*CurrentGains}; - const float step{(*TargetGains-gain) * delta}; - - size_t pos{0}; - if(!(std::abs(step) > std::numeric_limits::epsilon())) - gain = *TargetGains; - else - { - float step_count{0.0f}; - for(;pos != min_len;++pos) - { - dst[pos] += InSamples[pos] * (gain + step*step_count); - step_count += 1.0f; - } - if(pos == Counter) - gain = *TargetGains; - else - gain += step*step_count; - } - *CurrentGains = gain; - ++CurrentGains; - ++TargetGains; - - if(!(std::abs(gain) > GainSilenceThreshold)) - continue; - for(;pos != InSamples.size();++pos) - dst[pos] += InSamples[pos] * gain; - } -} diff --git a/alc/mixer/mixer_neon.cpp b/alc/mixer/mixer_neon.cpp deleted file mode 100644 index af8f6b0c..00000000 --- a/alc/mixer/mixer_neon.cpp +++ /dev/null @@ -1,303 +0,0 @@ -#include "config.h" - -#include - -#include -#include - -#include "alnumeric.h" -#include "core/bsinc_defs.h" -#include "defs.h" -#include "hrtfbase.h" - -struct NEONTag; -struct LerpTag; -struct BSincTag; -struct FastBSincTag; - - -namespace { - -inline float32x4_t set_f4(float l0, float l1, float l2, float l3) -{ - float32x4_t ret{}; - ret = vsetq_lane_f32(l0, ret, 0); - ret = vsetq_lane_f32(l1, ret, 1); - ret = vsetq_lane_f32(l2, ret, 2); - ret = vsetq_lane_f32(l3, ret, 3); - return ret; -} - -constexpr uint FracPhaseBitDiff{MixerFracBits - BSincPhaseBits}; -constexpr uint FracPhaseDiffOne{1 << FracPhaseBitDiff}; - -inline void ApplyCoeffs(float2 *RESTRICT Values, const uint_fast32_t IrSize, - const HrirArray &Coeffs, const float left, const float right) -{ - float32x4_t leftright4; - { - float32x2_t leftright2 = vdup_n_f32(0.0); - leftright2 = vset_lane_f32(left, leftright2, 0); - leftright2 = vset_lane_f32(right, leftright2, 1); - leftright4 = vcombine_f32(leftright2, leftright2); - } - - ASSUME(IrSize >= MIN_IR_LENGTH); - for(size_t c{0};c < IrSize;c += 2) - { - float32x4_t vals = vld1q_f32(&Values[c][0]); - float32x4_t coefs = vld1q_f32(&Coeffs[c][0]); - - vals = vmlaq_f32(vals, coefs, leftright4); - - vst1q_f32(&Values[c][0], vals); - } -} - -} // namespace - -template<> -const float *Resample_(const InterpState*, const float *RESTRICT src, uint frac, - uint increment, const al::span dst) -{ - const int32x4_t increment4 = vdupq_n_s32(static_cast(increment*4)); - const float32x4_t fracOne4 = vdupq_n_f32(1.0f/MixerFracOne); - const int32x4_t fracMask4 = vdupq_n_s32(MixerFracMask); - alignas(16) uint pos_[4], frac_[4]; - int32x4_t pos4, frac4; - - InitPosArrays(frac, increment, frac_, pos_, 4); - frac4 = vld1q_s32(reinterpret_cast(frac_)); - pos4 = vld1q_s32(reinterpret_cast(pos_)); - - auto dst_iter = dst.begin(); - for(size_t todo{dst.size()>>2};todo;--todo) - { - const int pos0{vgetq_lane_s32(pos4, 0)}; - const int pos1{vgetq_lane_s32(pos4, 1)}; - const int pos2{vgetq_lane_s32(pos4, 2)}; - const int pos3{vgetq_lane_s32(pos4, 3)}; - const float32x4_t val1{set_f4(src[pos0], src[pos1], src[pos2], src[pos3])}; - const float32x4_t val2{set_f4(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1])}; - - /* val1 + (val2-val1)*mu */ - const float32x4_t r0{vsubq_f32(val2, val1)}; - const float32x4_t mu{vmulq_f32(vcvtq_f32_s32(frac4), fracOne4)}; - const float32x4_t out{vmlaq_f32(val1, mu, r0)}; - - vst1q_f32(dst_iter, out); - dst_iter += 4; - - frac4 = vaddq_s32(frac4, increment4); - pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, MixerFracBits)); - frac4 = vandq_s32(frac4, fracMask4); - } - - if(size_t todo{dst.size()&3}) - { - src += static_cast(vgetq_lane_s32(pos4, 0)); - frac = static_cast(vgetq_lane_s32(frac4, 0)); - - do { - *(dst_iter++) = lerp(src[0], src[1], static_cast(frac) * (1.0f/MixerFracOne)); - - frac += increment; - src += frac>>MixerFracBits; - frac &= MixerFracMask; - } while(--todo); - } - return dst.data(); -} - -template<> -const float *Resample_(const InterpState *state, const float *RESTRICT src, - uint frac, uint increment, const al::span dst) -{ - const float *const filter{state->bsinc.filter}; - const float32x4_t sf4{vdupq_n_f32(state->bsinc.sf)}; - const size_t m{state->bsinc.m}; - - src -= state->bsinc.l; - for(float &out_sample : dst) - { - // Calculate the phase index and factor. - const uint pi{frac >> FracPhaseBitDiff}; - const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; - - // Apply the scale and phase interpolated filter. - float32x4_t r4{vdupq_n_f32(0.0f)}; - { - const float32x4_t pf4{vdupq_n_f32(pf)}; - const float *fil{filter + m*pi*4}; - const float *phd{fil + m}; - const float *scd{phd + m}; - const float *spd{scd + m}; - size_t td{m >> 2}; - size_t j{0u}; - - do { - /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */ - const float32x4_t f4 = vmlaq_f32( - vmlaq_f32(vld1q_f32(&fil[j]), sf4, vld1q_f32(&scd[j])), - pf4, vmlaq_f32(vld1q_f32(&phd[j]), sf4, vld1q_f32(&spd[j]))); - /* r += f*src */ - r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j])); - j += 4; - } while(--td); - } - r4 = vaddq_f32(r4, vrev64q_f32(r4)); - out_sample = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); - - frac += increment; - src += frac>>MixerFracBits; - frac &= MixerFracMask; - } - return dst.data(); -} - -template<> -const float *Resample_(const InterpState *state, - const float *RESTRICT src, uint frac, uint increment, const al::span dst) -{ - const float *const filter{state->bsinc.filter}; - const size_t m{state->bsinc.m}; - - src -= state->bsinc.l; - for(float &out_sample : dst) - { - // Calculate the phase index and factor. - const uint pi{frac >> FracPhaseBitDiff}; - const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; - - // Apply the phase interpolated filter. - float32x4_t r4{vdupq_n_f32(0.0f)}; - { - const float32x4_t pf4{vdupq_n_f32(pf)}; - const float *fil{filter + m*pi*4}; - const float *phd{fil + m}; - size_t td{m >> 2}; - size_t j{0u}; - - do { - /* f = fil + pf*phd */ - const float32x4_t f4 = vmlaq_f32(vld1q_f32(&fil[j]), pf4, vld1q_f32(&phd[j])); - /* r += f*src */ - r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j])); - j += 4; - } while(--td); - } - r4 = vaddq_f32(r4, vrev64q_f32(r4)); - out_sample = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); - - frac += increment; - src += frac>>MixerFracBits; - frac &= MixerFracMask; - } - return dst.data(); -} - - -template<> -void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize) -{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, BufferSize); } - -template<> -void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize) -{ - MixHrtfBlendBase(InSamples, AccumSamples, IrSize, oldparams, newparams, - BufferSize); -} - -template<> -void MixDirectHrtf_(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, - const al::span InSamples, float2 *AccumSamples, - float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) -{ - MixDirectHrtfBase(LeftOut, RightOut, InSamples, AccumSamples, TempBuf, ChanState, - IrSize, BufferSize); -} - - -template<> -void Mix_(const al::span InSamples, const al::span OutBuffer, - float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos) -{ - const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; - const auto min_len = minz(Counter, InSamples.size()); - const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len; - - for(FloatBufferLine &output : OutBuffer) - { - float *RESTRICT dst{al::assume_aligned<16>(output.data()+OutPos)}; - float gain{*CurrentGains}; - const float step{(*TargetGains-gain) * delta}; - - size_t pos{0}; - if(!(std::abs(step) > std::numeric_limits::epsilon())) - gain = *TargetGains; - else - { - float step_count{0.0f}; - /* Mix with applying gain steps in aligned multiples of 4. */ - if(size_t todo{(min_len-pos) >> 2}) - { - const float32x4_t four4{vdupq_n_f32(4.0f)}; - const float32x4_t step4{vdupq_n_f32(step)}; - const float32x4_t gain4{vdupq_n_f32(gain)}; - float32x4_t step_count4{vdupq_n_f32(0.0f)}; - step_count4 = vsetq_lane_f32(1.0f, step_count4, 1); - step_count4 = vsetq_lane_f32(2.0f, step_count4, 2); - step_count4 = vsetq_lane_f32(3.0f, step_count4, 3); - - do { - const float32x4_t val4 = vld1q_f32(&InSamples[pos]); - float32x4_t dry4 = vld1q_f32(&dst[pos]); - dry4 = vmlaq_f32(dry4, val4, vmlaq_f32(gain4, step4, step_count4)); - step_count4 = vaddq_f32(step_count4, four4); - vst1q_f32(&dst[pos], dry4); - pos += 4; - } while(--todo); - /* NOTE: step_count4 now represents the next four counts after - * the last four mixed samples, so the lowest element - * represents the next step count to apply. - */ - step_count = vgetq_lane_f32(step_count4, 0); - } - /* Mix with applying left over gain steps that aren't aligned multiples of 4. */ - for(size_t leftover{min_len&3};leftover;++pos,--leftover) - { - dst[pos] += InSamples[pos] * (gain + step*step_count); - step_count += 1.0f; - } - if(pos == Counter) - gain = *TargetGains; - else - gain += step*step_count; - - /* Mix until pos is aligned with 4 or the mix is done. */ - for(size_t leftover{aligned_len&3};leftover;++pos,--leftover) - dst[pos] += InSamples[pos] * gain; - } - *CurrentGains = gain; - ++CurrentGains; - ++TargetGains; - - if(!(std::abs(gain) > GainSilenceThreshold)) - continue; - if(size_t todo{(InSamples.size()-pos) >> 2}) - { - const float32x4_t gain4 = vdupq_n_f32(gain); - do { - const float32x4_t val4 = vld1q_f32(&InSamples[pos]); - float32x4_t dry4 = vld1q_f32(&dst[pos]); - dry4 = vmlaq_f32(dry4, val4, gain4); - vst1q_f32(&dst[pos], dry4); - pos += 4; - } while(--todo); - } - for(size_t leftover{(InSamples.size()-pos)&3};leftover;++pos,--leftover) - dst[pos] += InSamples[pos] * gain; - } -} diff --git a/alc/mixer/mixer_sse.cpp b/alc/mixer/mixer_sse.cpp deleted file mode 100644 index 85b2f1ce..00000000 --- a/alc/mixer/mixer_sse.cpp +++ /dev/null @@ -1,266 +0,0 @@ -#include "config.h" - -#include - -#include -#include - -#include "alnumeric.h" -#include "core/bsinc_defs.h" -#include "defs.h" -#include "hrtfbase.h" - -struct SSETag; -struct BSincTag; -struct FastBSincTag; - - -namespace { - -constexpr uint FracPhaseBitDiff{MixerFracBits - BSincPhaseBits}; -constexpr uint FracPhaseDiffOne{1 << FracPhaseBitDiff}; - -#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z)) - -inline void ApplyCoeffs(float2 *RESTRICT Values, const uint_fast32_t IrSize, - const HrirArray &Coeffs, const float left, const float right) -{ - const __m128 lrlr{_mm_setr_ps(left, right, left, right)}; - - ASSUME(IrSize >= MIN_IR_LENGTH); - /* This isn't technically correct to test alignment, but it's true for - * systems that support SSE, which is the only one that needs to know the - * alignment of Values (which alternates between 8- and 16-byte aligned). - */ - if(reinterpret_cast(Values)&0x8) - { - __m128 imp0, imp1; - __m128 coeffs{_mm_load_ps(&Coeffs[0][0])}; - __m128 vals{_mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<__m64*>(&Values[0][0]))}; - imp0 = _mm_mul_ps(lrlr, coeffs); - vals = _mm_add_ps(imp0, vals); - _mm_storel_pi(reinterpret_cast<__m64*>(&Values[0][0]), vals); - uint_fast32_t td{((IrSize+1)>>1) - 1}; - size_t i{1}; - do { - coeffs = _mm_load_ps(&Coeffs[i+1][0]); - vals = _mm_load_ps(&Values[i][0]); - imp1 = _mm_mul_ps(lrlr, coeffs); - imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2)); - vals = _mm_add_ps(imp0, vals); - _mm_store_ps(&Values[i][0], vals); - imp0 = imp1; - i += 2; - } while(--td); - vals = _mm_loadl_pi(vals, reinterpret_cast<__m64*>(&Values[i][0])); - imp0 = _mm_movehl_ps(imp0, imp0); - vals = _mm_add_ps(imp0, vals); - _mm_storel_pi(reinterpret_cast<__m64*>(&Values[i][0]), vals); - } - else - { - for(size_t i{0};i < IrSize;i += 2) - { - const __m128 coeffs{_mm_load_ps(&Coeffs[i][0])}; - __m128 vals{_mm_load_ps(&Values[i][0])}; - vals = MLA4(vals, lrlr, coeffs); - _mm_store_ps(&Values[i][0], vals); - } - } -} - -} // namespace - -template<> -const float *Resample_(const InterpState *state, const float *RESTRICT src, - uint frac, uint increment, const al::span dst) -{ - const float *const filter{state->bsinc.filter}; - const __m128 sf4{_mm_set1_ps(state->bsinc.sf)}; - const size_t m{state->bsinc.m}; - - src -= state->bsinc.l; - for(float &out_sample : dst) - { - // Calculate the phase index and factor. - const uint pi{frac >> FracPhaseBitDiff}; - const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; - - // Apply the scale and phase interpolated filter. - __m128 r4{_mm_setzero_ps()}; - { - const __m128 pf4{_mm_set1_ps(pf)}; - const float *fil{filter + m*pi*4}; - const float *phd{fil + m}; - const float *scd{phd + m}; - const float *spd{scd + m}; - size_t td{m >> 2}; - size_t j{0u}; - - do { - /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */ - const __m128 f4 = MLA4( - MLA4(_mm_load_ps(&fil[j]), sf4, _mm_load_ps(&scd[j])), - pf4, MLA4(_mm_load_ps(&phd[j]), sf4, _mm_load_ps(&spd[j]))); - /* r += f*src */ - r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j])); - j += 4; - } while(--td); - } - r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); - r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - out_sample = _mm_cvtss_f32(r4); - - frac += increment; - src += frac>>MixerFracBits; - frac &= MixerFracMask; - } - return dst.data(); -} - -template<> -const float *Resample_(const InterpState *state, const float *RESTRICT src, - uint frac, uint increment, const al::span dst) -{ - const float *const filter{state->bsinc.filter}; - const size_t m{state->bsinc.m}; - - src -= state->bsinc.l; - for(float &out_sample : dst) - { - // Calculate the phase index and factor. - const uint pi{frac >> FracPhaseBitDiff}; - const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; - - // Apply the phase interpolated filter. - __m128 r4{_mm_setzero_ps()}; - { - const __m128 pf4{_mm_set1_ps(pf)}; - const float *fil{filter + m*pi*4}; - const float *phd{fil + m}; - size_t td{m >> 2}; - size_t j{0u}; - - do { - /* f = fil + pf*phd */ - const __m128 f4 = MLA4(_mm_load_ps(&fil[j]), pf4, _mm_load_ps(&phd[j])); - /* r += f*src */ - r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j])); - j += 4; - } while(--td); - } - r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); - r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - out_sample = _mm_cvtss_f32(r4); - - frac += increment; - src += frac>>MixerFracBits; - frac &= MixerFracMask; - } - return dst.data(); -} - - -template<> -void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize) -{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, BufferSize); } - -template<> -void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize) -{ - MixHrtfBlendBase(InSamples, AccumSamples, IrSize, oldparams, newparams, - BufferSize); -} - -template<> -void MixDirectHrtf_(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, - const al::span InSamples, float2 *AccumSamples, - float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) -{ - MixDirectHrtfBase(LeftOut, RightOut, InSamples, AccumSamples, TempBuf, ChanState, - IrSize, BufferSize); -} - - -template<> -void Mix_(const al::span InSamples, const al::span OutBuffer, - float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos) -{ - const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; - const auto min_len = minz(Counter, InSamples.size()); - const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len; - - for(FloatBufferLine &output : OutBuffer) - { - float *RESTRICT dst{al::assume_aligned<16>(output.data()+OutPos)}; - float gain{*CurrentGains}; - const float step{(*TargetGains-gain) * delta}; - - size_t pos{0}; - if(!(std::abs(step) > std::numeric_limits::epsilon())) - gain = *TargetGains; - else - { - float step_count{0.0f}; - /* Mix with applying gain steps in aligned multiples of 4. */ - if(size_t todo{(min_len-pos) >> 2}) - { - const __m128 four4{_mm_set1_ps(4.0f)}; - const __m128 step4{_mm_set1_ps(step)}; - const __m128 gain4{_mm_set1_ps(gain)}; - __m128 step_count4{_mm_setr_ps(0.0f, 1.0f, 2.0f, 3.0f)}; - do { - const __m128 val4{_mm_load_ps(&InSamples[pos])}; - __m128 dry4{_mm_load_ps(&dst[pos])}; - - /* dry += val * (gain + step*step_count) */ - dry4 = MLA4(dry4, val4, MLA4(gain4, step4, step_count4)); - - _mm_store_ps(&dst[pos], dry4); - step_count4 = _mm_add_ps(step_count4, four4); - pos += 4; - } while(--todo); - /* NOTE: step_count4 now represents the next four counts after - * the last four mixed samples, so the lowest element - * represents the next step count to apply. - */ - step_count = _mm_cvtss_f32(step_count4); - } - /* Mix with applying left over gain steps that aren't aligned multiples of 4. */ - for(size_t leftover{min_len&3};leftover;++pos,--leftover) - { - dst[pos] += InSamples[pos] * (gain + step*step_count); - step_count += 1.0f; - } - if(pos == Counter) - gain = *TargetGains; - else - gain += step*step_count; - - /* Mix until pos is aligned with 4 or the mix is done. */ - for(size_t leftover{aligned_len&3};leftover;++pos,--leftover) - dst[pos] += InSamples[pos] * gain; - } - *CurrentGains = gain; - ++CurrentGains; - ++TargetGains; - - if(!(std::abs(gain) > GainSilenceThreshold)) - continue; - if(size_t todo{(InSamples.size()-pos) >> 2}) - { - const __m128 gain4{_mm_set1_ps(gain)}; - do { - const __m128 val4{_mm_load_ps(&InSamples[pos])}; - __m128 dry4{_mm_load_ps(&dst[pos])}; - dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4)); - _mm_store_ps(&dst[pos], dry4); - pos += 4; - } while(--todo); - } - for(size_t leftover{(InSamples.size()-pos)&3};leftover;++pos,--leftover) - dst[pos] += InSamples[pos] * gain; - } -} diff --git a/alc/mixer/mixer_sse2.cpp b/alc/mixer/mixer_sse2.cpp deleted file mode 100644 index 69fac250..00000000 --- a/alc/mixer/mixer_sse2.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 2014 by Timothy Arceri . - * 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 -#include - -#include "alnumeric.h" -#include "defs.h" - -struct SSE2Tag; -struct LerpTag; - - -template<> -const float *Resample_(const InterpState*, const float *RESTRICT src, uint frac, - uint increment, const al::span dst) -{ - const __m128i increment4{_mm_set1_epi32(static_cast(increment*4))}; - const __m128 fracOne4{_mm_set1_ps(1.0f/MixerFracOne)}; - const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)}; - - alignas(16) uint pos_[4], frac_[4]; - InitPosArrays(frac, increment, frac_, pos_, 4); - __m128i frac4{_mm_setr_epi32(static_cast(frac_[0]), static_cast(frac_[1]), - static_cast(frac_[2]), static_cast(frac_[3]))}; - __m128i pos4{_mm_setr_epi32(static_cast(pos_[0]), static_cast(pos_[1]), - static_cast(pos_[2]), static_cast(pos_[3]))}; - - auto dst_iter = dst.begin(); - for(size_t todo{dst.size()>>2};todo;--todo) - { - const int pos0{_mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(0, 0, 0, 0)))}; - const int pos1{_mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(1, 1, 1, 1)))}; - const int pos2{_mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(2, 2, 2, 2)))}; - const int pos3{_mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(3, 3, 3, 3)))}; - const __m128 val1{_mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ])}; - const __m128 val2{_mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1])}; - - /* val1 + (val2-val1)*mu */ - const __m128 r0{_mm_sub_ps(val2, val1)}; - const __m128 mu{_mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4)}; - const __m128 out{_mm_add_ps(val1, _mm_mul_ps(mu, r0))}; - - _mm_store_ps(dst_iter, out); - dst_iter += 4; - - frac4 = _mm_add_epi32(frac4, increment4); - pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, MixerFracBits)); - frac4 = _mm_and_si128(frac4, fracMask4); - } - - if(size_t todo{dst.size()&3}) - { - src += static_cast(_mm_cvtsi128_si32(pos4)); - frac = static_cast(_mm_cvtsi128_si32(frac4)); - - do { - *(dst_iter++) = lerp(src[0], src[1], static_cast(frac) * (1.0f/MixerFracOne)); - - frac += increment; - src += frac>>MixerFracBits; - frac &= MixerFracMask; - } while(--todo); - } - return dst.data(); -} diff --git a/alc/mixer/mixer_sse3.cpp b/alc/mixer/mixer_sse3.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/alc/mixer/mixer_sse41.cpp b/alc/mixer/mixer_sse41.cpp deleted file mode 100644 index cacc9e64..00000000 --- a/alc/mixer/mixer_sse41.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 2014 by Timothy Arceri . - * 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 -#include -#include - -#include "alnumeric.h" -#include "defs.h" - -struct SSE4Tag; -struct LerpTag; - - -template<> -const float *Resample_(const InterpState*, const float *RESTRICT src, uint frac, - uint increment, const al::span dst) -{ - const __m128i increment4{_mm_set1_epi32(static_cast(increment*4))}; - const __m128 fracOne4{_mm_set1_ps(1.0f/MixerFracOne)}; - const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)}; - - alignas(16) uint pos_[4], frac_[4]; - InitPosArrays(frac, increment, frac_, pos_, 4); - __m128i frac4{_mm_setr_epi32(static_cast(frac_[0]), static_cast(frac_[1]), - static_cast(frac_[2]), static_cast(frac_[3]))}; - __m128i pos4{_mm_setr_epi32(static_cast(pos_[0]), static_cast(pos_[1]), - static_cast(pos_[2]), static_cast(pos_[3]))}; - - auto dst_iter = dst.begin(); - for(size_t todo{dst.size()>>2};todo;--todo) - { - const int pos0{_mm_extract_epi32(pos4, 0)}; - const int pos1{_mm_extract_epi32(pos4, 1)}; - const int pos2{_mm_extract_epi32(pos4, 2)}; - const int pos3{_mm_extract_epi32(pos4, 3)}; - const __m128 val1{_mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ])}; - const __m128 val2{_mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1])}; - - /* val1 + (val2-val1)*mu */ - const __m128 r0{_mm_sub_ps(val2, val1)}; - const __m128 mu{_mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4)}; - const __m128 out{_mm_add_ps(val1, _mm_mul_ps(mu, r0))}; - - _mm_store_ps(dst_iter, out); - dst_iter += 4; - - frac4 = _mm_add_epi32(frac4, increment4); - pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, MixerFracBits)); - frac4 = _mm_and_si128(frac4, fracMask4); - } - - if(size_t todo{dst.size()&3}) - { - /* NOTE: These four elements represent the position *after* the last - * four samples, so the lowest element is the next position to - * resample. - */ - src += static_cast(_mm_cvtsi128_si32(pos4)); - frac = static_cast(_mm_cvtsi128_si32(frac4)); - - do { - *(dst_iter++) = lerp(src[0], src[1], static_cast(frac) * (1.0f/MixerFracOne)); - - frac += increment; - src += frac>>MixerFracBits; - frac &= MixerFracMask; - } while(--todo); - } - return dst.data(); -} diff --git a/alc/voice.cpp b/alc/voice.cpp index a188a04d..8aa5a59f 100644 --- a/alc/voice.cpp +++ b/alc/voice.cpp @@ -50,13 +50,13 @@ #include "core/filters/biquad.h" #include "core/filters/nfc.h" #include "core/filters/splitter.h" +#include "core/mixer/defs.h" +#include "core/mixer/hrtfdefs.h" #include "cpu_caps.h" #include "fmt_traits.h" #include "hrtf.h" #include "inprogext.h" #include "logging.h" -#include "mixer/defs.h" -#include "mixer/hrtfdefs.h" #include "opthelpers.h" #include "ringbuffer.h" #include "threads.h" diff --git a/alc/voice.h b/alc/voice.h index ccffaf01..9253be72 100644 --- a/alc/voice.h +++ b/alc/voice.h @@ -11,8 +11,8 @@ #include "core/filters/biquad.h" #include "core/filters/nfc.h" #include "core/filters/splitter.h" +#include "core/mixer/defs.h" #include "hrtf.h" -#include "mixer/defs.h" struct EffectSlot; struct BufferlistItem; diff --git a/core/mixer/defs.h b/core/mixer/defs.h new file mode 100644 index 00000000..9dcf395f --- /dev/null +++ b/core/mixer/defs.h @@ -0,0 +1,100 @@ +#ifndef CORE_MIXER_DEFS_H +#define CORE_MIXER_DEFS_H + +#include +#include + +#include "alspan.h" +#include "core/bufferline.h" + +struct HrtfChannelState; +struct HrtfFilter; +struct MixHrtfFilter; + +using uint = unsigned int; +using float2 = std::array; + + +constexpr int MixerFracBits{12}; +constexpr int MixerFracOne{1 << MixerFracBits}; +constexpr int MixerFracMask{MixerFracOne - 1}; + +/* Maximum number of samples to pad on the ends of a buffer for resampling. + * Note that the padding is symmetric (half at the beginning and half at the + * end)! + */ +constexpr int MaxResamplerPadding{48}; + +constexpr float GainSilenceThreshold{0.00001f}; /* -100dB */ + + +enum class Resampler { + Point, + Linear, + Cubic, + FastBSinc12, + BSinc12, + FastBSinc24, + BSinc24, + + Max = BSinc24 +}; + +/* 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 { + float sf; /* Scale interpolation factor. */ + uint m; /* Coefficient count. */ + uint l; /* Left coefficient offset. */ + /* Filter coefficients, followed by the phase, scale, and scale-phase + * delta coefficients. Starting at phase index 0, each subsequent phase + * index follows contiguously. + */ + const float *filter; +}; + +union InterpState { + BsincState bsinc; +}; + +using ResamplerFunc = const float*(*)(const InterpState *state, const float *RESTRICT src, + uint frac, uint increment, const al::span dst); + +ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState *state); + + +template +const float *Resample_(const InterpState *state, const float *RESTRICT src, uint frac, + uint increment, const al::span dst); + +template +void Mix_(const al::span InSamples, const al::span OutBuffer, + float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos); + +template +void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, + const MixHrtfFilter *hrtfparams, const size_t BufferSize); +template +void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, + const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize); +template +void MixDirectHrtf_(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, + const al::span InSamples, float2 *AccumSamples, + float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize); + +/* Vectorized resampler helpers */ +inline void InitPosArrays(uint frac, uint increment, uint *frac_arr, uint *pos_arr, size_t size) +{ + pos_arr[0] = 0; + frac_arr[0] = frac; + for(size_t i{1};i < size;i++) + { + const uint frac_tmp{frac_arr[i-1] + increment}; + pos_arr[i] = pos_arr[i-1] + (frac_tmp>>MixerFracBits); + frac_arr[i] = frac_tmp&MixerFracMask; + } +} + +#endif /* CORE_MIXER_DEFS_H */ diff --git a/core/mixer/hrtfbase.h b/core/mixer/hrtfbase.h new file mode 100644 index 00000000..8031fe3d --- /dev/null +++ b/core/mixer/hrtfbase.h @@ -0,0 +1,159 @@ +#ifndef CORE_MIXER_HRTFBASE_H +#define CORE_MIXER_HRTFBASE_H + +#include +#include + +#include "almalloc.h" +#include "hrtfdefs.h" +#include "opthelpers.h" + + +using uint = unsigned int; + +using ApplyCoeffsT = void(&)(float2 *RESTRICT Values, const uint_fast32_t irSize, + const HrirArray &Coeffs, const float left, const float right); + +template +inline void MixHrtfBase(const float *InSamples, float2 *RESTRICT AccumSamples, const uint IrSize, + const MixHrtfFilter *hrtfparams, const size_t BufferSize) +{ + ASSUME(BufferSize > 0); + + const HrirArray &Coeffs = *hrtfparams->Coeffs; + const float gainstep{hrtfparams->GainStep}; + const float gain{hrtfparams->Gain}; + + size_t ldelay{HRTF_HISTORY_LENGTH - hrtfparams->Delay[0]}; + size_t rdelay{HRTF_HISTORY_LENGTH - hrtfparams->Delay[1]}; + float stepcount{0.0f}; + for(size_t i{0u};i < BufferSize;++i) + { + const float g{gain + gainstep*stepcount}; + const float left{InSamples[ldelay++] * g}; + const float right{InSamples[rdelay++] * g}; + ApplyCoeffs(AccumSamples+i, IrSize, Coeffs, left, right); + + stepcount += 1.0f; + } +} + +template +inline void MixHrtfBlendBase(const float *InSamples, float2 *RESTRICT AccumSamples, + const uint IrSize, const HrtfFilter *oldparams, const MixHrtfFilter *newparams, + const size_t BufferSize) +{ + ASSUME(BufferSize > 0); + + const auto &OldCoeffs = oldparams->Coeffs; + const float oldGainStep{oldparams->Gain / static_cast(BufferSize)}; + const auto &NewCoeffs = *newparams->Coeffs; + const float newGainStep{newparams->GainStep}; + + if LIKELY(oldparams->Gain > GainSilenceThreshold) + { + size_t ldelay{HRTF_HISTORY_LENGTH - oldparams->Delay[0]}; + size_t rdelay{HRTF_HISTORY_LENGTH - oldparams->Delay[1]}; + auto stepcount = static_cast(BufferSize); + for(size_t i{0u};i < BufferSize;++i) + { + const float g{oldGainStep*stepcount}; + const float left{InSamples[ldelay++] * g}; + const float right{InSamples[rdelay++] * g}; + ApplyCoeffs(AccumSamples+i, IrSize, OldCoeffs, left, right); + + stepcount -= 1.0f; + } + } + + if LIKELY(newGainStep*static_cast(BufferSize) > GainSilenceThreshold) + { + size_t ldelay{HRTF_HISTORY_LENGTH+1 - newparams->Delay[0]}; + size_t rdelay{HRTF_HISTORY_LENGTH+1 - newparams->Delay[1]}; + float stepcount{1.0f}; + for(size_t i{1u};i < BufferSize;++i) + { + const float g{newGainStep*stepcount}; + const float left{InSamples[ldelay++] * g}; + const float right{InSamples[rdelay++] * g}; + ApplyCoeffs(AccumSamples+i, IrSize, NewCoeffs, left, right); + + stepcount += 1.0f; + } + } +} + +template +inline void MixDirectHrtfBase(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, + const al::span InSamples, float2 *RESTRICT AccumSamples, + float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) +{ + ASSUME(BufferSize > 0); + + /* Add the existing signal directly to the accumulation buffer, unfiltered, + * and with a delay to align with the input delay. + */ + for(size_t i{0};i < BufferSize;++i) + { + AccumSamples[HRTF_DIRECT_DELAY+i][0] += LeftOut[i]; + AccumSamples[HRTF_DIRECT_DELAY+i][1] += RightOut[i]; + } + + for(const FloatBufferLine &input : InSamples) + { + /* For dual-band processing, the signal needs extra scaling applied to + * the high frequency response. The band-splitter alone creates a + * frequency-dependent phase shift, which is not ideal. To counteract + * it, combine it with a backwards phase shift. + */ + + /* Load the input signal backwards, into a temp buffer with delay + * padding. The delay serves to reduce the error caused by the IIR + * filter's phase shift on a partial input. + */ + al::span tempbuf{al::assume_aligned<16>(TempBuf), HRTF_DIRECT_DELAY+BufferSize}; + auto tmpiter = std::reverse_copy(input.begin(), input.begin()+BufferSize, tempbuf.begin()); + std::copy(ChanState->mDelay.cbegin(), ChanState->mDelay.cend(), tmpiter); + + /* Save the unfiltered newest input samples for next time. */ + std::copy_n(tempbuf.begin(), ChanState->mDelay.size(), ChanState->mDelay.begin()); + + /* Apply the all-pass on the reversed signal and reverse the resulting + * sample array. This produces the forward response with a backwards + * phase shift (+n degrees becomes -n degrees). + */ + ChanState->mSplitter.applyAllpass(tempbuf); + tempbuf = tempbuf.subspan(); + std::reverse(tempbuf.begin(), tempbuf.end()); + + /* Now apply the HF scale with the band-splitter. This applies the + * forward phase shift, which cancels out with the backwards phase + * shift to get the original phase on the scaled signal. + */ + ChanState->mSplitter.processHfScale(tempbuf, ChanState->mHfScale); + + /* Now apply the HRIR coefficients to this channel. */ + const auto &Coeffs = ChanState->mCoeffs; + for(size_t i{0u};i < BufferSize;++i) + { + const float insample{tempbuf[i]}; + ApplyCoeffs(AccumSamples+i, IrSize, Coeffs, insample, insample); + } + + ++ChanState; + } + + for(size_t i{0u};i < BufferSize;++i) + LeftOut[i] = AccumSamples[i][0]; + for(size_t i{0u};i < BufferSize;++i) + RightOut[i] = AccumSamples[i][1]; + + /* Copy the new in-progress accumulation values to the front and clear the + * following samples for the next mix. + */ + auto accum_iter = std::copy_n(AccumSamples+BufferSize, HRIR_LENGTH+HRTF_DIRECT_DELAY, + AccumSamples); + std::fill_n(accum_iter, BufferSize, float2{}); +} + +#endif /* CORE_MIXER_HRTFBASE_H */ diff --git a/core/mixer/hrtfdefs.h b/core/mixer/hrtfdefs.h new file mode 100644 index 00000000..623e6ec3 --- /dev/null +++ b/core/mixer/hrtfdefs.h @@ -0,0 +1,52 @@ +#ifndef CORE_MIXER_HRTFDEFS_H +#define CORE_MIXER_HRTFDEFS_H + +#include + +#include "core/ambidefs.h" +#include "core/bufferline.h" +#include "core/filters/splitter.h" + + +#define HRTF_HISTORY_BITS 6 +#define HRTF_HISTORY_LENGTH (1<; +using HrirArray = std::array; +using ubyte = unsigned char; +using ubyte2 = std::array; +using ushort = unsigned short; +using uint = unsigned int; + + +struct MixHrtfFilter { + const HrirArray *Coeffs; + std::array Delay; + float Gain; + float GainStep; +}; + +struct HrtfFilter { + alignas(16) HrirArray Coeffs; + std::array Delay; + float Gain; +}; + + +struct HrtfChannelState { + std::array mDelay{}; + BandSplitter mSplitter; + float mHfScale{}; + alignas(16) HrirArray mCoeffs{}; +}; + +#endif /* CORE_MIXER_HRTFDEFS_H */ diff --git a/core/mixer/mixer_c.cpp b/core/mixer/mixer_c.cpp new file mode 100644 index 00000000..24ccd175 --- /dev/null +++ b/core/mixer/mixer_c.cpp @@ -0,0 +1,198 @@ +#include "config.h" + +#include +#include +#include + +#include "alnumeric.h" +#include "core/bsinc_tables.h" +#include "defs.h" +#include "hrtfbase.h" + +struct CTag; +struct CopyTag; +struct PointTag; +struct LerpTag; +struct CubicTag; +struct BSincTag; +struct FastBSincTag; + + +namespace { + +constexpr uint FracPhaseBitDiff{MixerFracBits - BSincPhaseBits}; +constexpr uint FracPhaseDiffOne{1 << FracPhaseBitDiff}; + +inline float do_point(const InterpState&, const float *RESTRICT vals, const uint) +{ return vals[0]; } +inline float do_lerp(const InterpState&, const float *RESTRICT vals, const uint frac) +{ return lerp(vals[0], vals[1], static_cast(frac)*(1.0f/MixerFracOne)); } +inline float do_cubic(const InterpState&, const float *RESTRICT vals, const uint frac) +{ return cubic(vals[0], vals[1], vals[2], vals[3], static_cast(frac)*(1.0f/MixerFracOne)); } +inline float do_bsinc(const InterpState &istate, const float *RESTRICT vals, const uint frac) +{ + const size_t m{istate.bsinc.m}; + + // Calculate the phase index and factor. + const uint pi{frac >> FracPhaseBitDiff}; + const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; + + const float *fil{istate.bsinc.filter + m*pi*4}; + const float *phd{fil + m}; + const float *scd{phd + m}; + const float *spd{scd + m}; + + // Apply the scale and phase interpolated filter. + float r{0.0f}; + for(size_t j_f{0};j_f < m;j_f++) + r += (fil[j_f] + istate.bsinc.sf*scd[j_f] + pf*(phd[j_f] + istate.bsinc.sf*spd[j_f])) * vals[j_f]; + return r; +} +inline float do_fastbsinc(const InterpState &istate, const float *RESTRICT vals, const uint frac) +{ + const size_t m{istate.bsinc.m}; + + // Calculate the phase index and factor. + const uint pi{frac >> FracPhaseBitDiff}; + const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; + + const float *fil{istate.bsinc.filter + m*pi*4}; + const float *phd{fil + m}; + + // Apply the phase interpolated filter. + float r{0.0f}; + for(size_t j_f{0};j_f < m;j_f++) + r += (fil[j_f] + pf*phd[j_f]) * vals[j_f]; + return r; +} + +using SamplerT = float(&)(const InterpState&, const float*RESTRICT, const uint); +template +const float *DoResample(const InterpState *state, const float *RESTRICT src, uint frac, + uint increment, const al::span dst) +{ + const InterpState istate{*state}; + for(float &out : dst) + { + out = Sampler(istate, src, frac); + + frac += increment; + src += frac>>MixerFracBits; + frac &= MixerFracMask; + } + return dst.data(); +} + +inline void ApplyCoeffs(float2 *RESTRICT Values, const uint_fast32_t IrSize, + const HrirArray &Coeffs, const float left, const float right) +{ + ASSUME(IrSize >= MIN_IR_LENGTH); + for(size_t c{0};c < IrSize;++c) + { + Values[c][0] += Coeffs[c][0] * left; + Values[c][1] += Coeffs[c][1] * right; + } +} + +} // namespace + +template<> +const float *Resample_(const InterpState*, const float *RESTRICT src, uint, uint, + const al::span dst) +{ +#if defined(HAVE_SSE) || defined(HAVE_NEON) + /* Avoid copying the source data if it's aligned like the destination. */ + if((reinterpret_cast(src)&15) == (reinterpret_cast(dst.data())&15)) + return src; +#endif + std::copy_n(src, dst.size(), dst.begin()); + return dst.data(); +} + +template<> +const float *Resample_(const InterpState *state, const float *RESTRICT src, + uint frac, uint increment, const al::span dst) +{ return DoResample(state, src, frac, increment, dst); } + +template<> +const float *Resample_(const InterpState *state, const float *RESTRICT src, + uint frac, uint increment, const al::span dst) +{ return DoResample(state, src, frac, increment, dst); } + +template<> +const float *Resample_(const InterpState *state, const float *RESTRICT src, + uint frac, uint increment, const al::span dst) +{ return DoResample(state, src-1, frac, increment, dst); } + +template<> +const float *Resample_(const InterpState *state, const float *RESTRICT src, + uint frac, uint increment, const al::span dst) +{ return DoResample(state, src-state->bsinc.l, frac, increment, dst); } + +template<> +const float *Resample_(const InterpState *state, const float *RESTRICT src, + uint frac, uint increment, const al::span dst) +{ return DoResample(state, src-state->bsinc.l, frac, increment, dst); } + + +template<> +void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, + const MixHrtfFilter *hrtfparams, const size_t BufferSize) +{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, BufferSize); } + +template<> +void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, + const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize) +{ + MixHrtfBlendBase(InSamples, AccumSamples, IrSize, oldparams, newparams, + BufferSize); +} + +template<> +void MixDirectHrtf_(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, + const al::span InSamples, float2 *AccumSamples, + float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) +{ + MixDirectHrtfBase(LeftOut, RightOut, InSamples, AccumSamples, TempBuf, ChanState, + IrSize, BufferSize); +} + + +template<> +void Mix_(const al::span InSamples, const al::span OutBuffer, + float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos) +{ + const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; + const auto min_len = minz(Counter, InSamples.size()); + for(FloatBufferLine &output : OutBuffer) + { + float *RESTRICT dst{al::assume_aligned<16>(output.data()+OutPos)}; + float gain{*CurrentGains}; + const float step{(*TargetGains-gain) * delta}; + + size_t pos{0}; + if(!(std::abs(step) > std::numeric_limits::epsilon())) + gain = *TargetGains; + else + { + float step_count{0.0f}; + for(;pos != min_len;++pos) + { + dst[pos] += InSamples[pos] * (gain + step*step_count); + step_count += 1.0f; + } + if(pos == Counter) + gain = *TargetGains; + else + gain += step*step_count; + } + *CurrentGains = gain; + ++CurrentGains; + ++TargetGains; + + if(!(std::abs(gain) > GainSilenceThreshold)) + continue; + for(;pos != InSamples.size();++pos) + dst[pos] += InSamples[pos] * gain; + } +} diff --git a/core/mixer/mixer_neon.cpp b/core/mixer/mixer_neon.cpp new file mode 100644 index 00000000..af8f6b0c --- /dev/null +++ b/core/mixer/mixer_neon.cpp @@ -0,0 +1,303 @@ +#include "config.h" + +#include + +#include +#include + +#include "alnumeric.h" +#include "core/bsinc_defs.h" +#include "defs.h" +#include "hrtfbase.h" + +struct NEONTag; +struct LerpTag; +struct BSincTag; +struct FastBSincTag; + + +namespace { + +inline float32x4_t set_f4(float l0, float l1, float l2, float l3) +{ + float32x4_t ret{}; + ret = vsetq_lane_f32(l0, ret, 0); + ret = vsetq_lane_f32(l1, ret, 1); + ret = vsetq_lane_f32(l2, ret, 2); + ret = vsetq_lane_f32(l3, ret, 3); + return ret; +} + +constexpr uint FracPhaseBitDiff{MixerFracBits - BSincPhaseBits}; +constexpr uint FracPhaseDiffOne{1 << FracPhaseBitDiff}; + +inline void ApplyCoeffs(float2 *RESTRICT Values, const uint_fast32_t IrSize, + const HrirArray &Coeffs, const float left, const float right) +{ + float32x4_t leftright4; + { + float32x2_t leftright2 = vdup_n_f32(0.0); + leftright2 = vset_lane_f32(left, leftright2, 0); + leftright2 = vset_lane_f32(right, leftright2, 1); + leftright4 = vcombine_f32(leftright2, leftright2); + } + + ASSUME(IrSize >= MIN_IR_LENGTH); + for(size_t c{0};c < IrSize;c += 2) + { + float32x4_t vals = vld1q_f32(&Values[c][0]); + float32x4_t coefs = vld1q_f32(&Coeffs[c][0]); + + vals = vmlaq_f32(vals, coefs, leftright4); + + vst1q_f32(&Values[c][0], vals); + } +} + +} // namespace + +template<> +const float *Resample_(const InterpState*, const float *RESTRICT src, uint frac, + uint increment, const al::span dst) +{ + const int32x4_t increment4 = vdupq_n_s32(static_cast(increment*4)); + const float32x4_t fracOne4 = vdupq_n_f32(1.0f/MixerFracOne); + const int32x4_t fracMask4 = vdupq_n_s32(MixerFracMask); + alignas(16) uint pos_[4], frac_[4]; + int32x4_t pos4, frac4; + + InitPosArrays(frac, increment, frac_, pos_, 4); + frac4 = vld1q_s32(reinterpret_cast(frac_)); + pos4 = vld1q_s32(reinterpret_cast(pos_)); + + auto dst_iter = dst.begin(); + for(size_t todo{dst.size()>>2};todo;--todo) + { + const int pos0{vgetq_lane_s32(pos4, 0)}; + const int pos1{vgetq_lane_s32(pos4, 1)}; + const int pos2{vgetq_lane_s32(pos4, 2)}; + const int pos3{vgetq_lane_s32(pos4, 3)}; + const float32x4_t val1{set_f4(src[pos0], src[pos1], src[pos2], src[pos3])}; + const float32x4_t val2{set_f4(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1])}; + + /* val1 + (val2-val1)*mu */ + const float32x4_t r0{vsubq_f32(val2, val1)}; + const float32x4_t mu{vmulq_f32(vcvtq_f32_s32(frac4), fracOne4)}; + const float32x4_t out{vmlaq_f32(val1, mu, r0)}; + + vst1q_f32(dst_iter, out); + dst_iter += 4; + + frac4 = vaddq_s32(frac4, increment4); + pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, MixerFracBits)); + frac4 = vandq_s32(frac4, fracMask4); + } + + if(size_t todo{dst.size()&3}) + { + src += static_cast(vgetq_lane_s32(pos4, 0)); + frac = static_cast(vgetq_lane_s32(frac4, 0)); + + do { + *(dst_iter++) = lerp(src[0], src[1], static_cast(frac) * (1.0f/MixerFracOne)); + + frac += increment; + src += frac>>MixerFracBits; + frac &= MixerFracMask; + } while(--todo); + } + return dst.data(); +} + +template<> +const float *Resample_(const InterpState *state, const float *RESTRICT src, + uint frac, uint increment, const al::span dst) +{ + const float *const filter{state->bsinc.filter}; + const float32x4_t sf4{vdupq_n_f32(state->bsinc.sf)}; + const size_t m{state->bsinc.m}; + + src -= state->bsinc.l; + for(float &out_sample : dst) + { + // Calculate the phase index and factor. + const uint pi{frac >> FracPhaseBitDiff}; + const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; + + // Apply the scale and phase interpolated filter. + float32x4_t r4{vdupq_n_f32(0.0f)}; + { + const float32x4_t pf4{vdupq_n_f32(pf)}; + const float *fil{filter + m*pi*4}; + const float *phd{fil + m}; + const float *scd{phd + m}; + const float *spd{scd + m}; + size_t td{m >> 2}; + size_t j{0u}; + + do { + /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */ + const float32x4_t f4 = vmlaq_f32( + vmlaq_f32(vld1q_f32(&fil[j]), sf4, vld1q_f32(&scd[j])), + pf4, vmlaq_f32(vld1q_f32(&phd[j]), sf4, vld1q_f32(&spd[j]))); + /* r += f*src */ + r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j])); + j += 4; + } while(--td); + } + r4 = vaddq_f32(r4, vrev64q_f32(r4)); + out_sample = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + + frac += increment; + src += frac>>MixerFracBits; + frac &= MixerFracMask; + } + return dst.data(); +} + +template<> +const float *Resample_(const InterpState *state, + const float *RESTRICT src, uint frac, uint increment, const al::span dst) +{ + const float *const filter{state->bsinc.filter}; + const size_t m{state->bsinc.m}; + + src -= state->bsinc.l; + for(float &out_sample : dst) + { + // Calculate the phase index and factor. + const uint pi{frac >> FracPhaseBitDiff}; + const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; + + // Apply the phase interpolated filter. + float32x4_t r4{vdupq_n_f32(0.0f)}; + { + const float32x4_t pf4{vdupq_n_f32(pf)}; + const float *fil{filter + m*pi*4}; + const float *phd{fil + m}; + size_t td{m >> 2}; + size_t j{0u}; + + do { + /* f = fil + pf*phd */ + const float32x4_t f4 = vmlaq_f32(vld1q_f32(&fil[j]), pf4, vld1q_f32(&phd[j])); + /* r += f*src */ + r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j])); + j += 4; + } while(--td); + } + r4 = vaddq_f32(r4, vrev64q_f32(r4)); + out_sample = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + + frac += increment; + src += frac>>MixerFracBits; + frac &= MixerFracMask; + } + return dst.data(); +} + + +template<> +void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, + const MixHrtfFilter *hrtfparams, const size_t BufferSize) +{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, BufferSize); } + +template<> +void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, + const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize) +{ + MixHrtfBlendBase(InSamples, AccumSamples, IrSize, oldparams, newparams, + BufferSize); +} + +template<> +void MixDirectHrtf_(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, + const al::span InSamples, float2 *AccumSamples, + float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) +{ + MixDirectHrtfBase(LeftOut, RightOut, InSamples, AccumSamples, TempBuf, ChanState, + IrSize, BufferSize); +} + + +template<> +void Mix_(const al::span InSamples, const al::span OutBuffer, + float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos) +{ + const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; + const auto min_len = minz(Counter, InSamples.size()); + const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len; + + for(FloatBufferLine &output : OutBuffer) + { + float *RESTRICT dst{al::assume_aligned<16>(output.data()+OutPos)}; + float gain{*CurrentGains}; + const float step{(*TargetGains-gain) * delta}; + + size_t pos{0}; + if(!(std::abs(step) > std::numeric_limits::epsilon())) + gain = *TargetGains; + else + { + float step_count{0.0f}; + /* Mix with applying gain steps in aligned multiples of 4. */ + if(size_t todo{(min_len-pos) >> 2}) + { + const float32x4_t four4{vdupq_n_f32(4.0f)}; + const float32x4_t step4{vdupq_n_f32(step)}; + const float32x4_t gain4{vdupq_n_f32(gain)}; + float32x4_t step_count4{vdupq_n_f32(0.0f)}; + step_count4 = vsetq_lane_f32(1.0f, step_count4, 1); + step_count4 = vsetq_lane_f32(2.0f, step_count4, 2); + step_count4 = vsetq_lane_f32(3.0f, step_count4, 3); + + do { + const float32x4_t val4 = vld1q_f32(&InSamples[pos]); + float32x4_t dry4 = vld1q_f32(&dst[pos]); + dry4 = vmlaq_f32(dry4, val4, vmlaq_f32(gain4, step4, step_count4)); + step_count4 = vaddq_f32(step_count4, four4); + vst1q_f32(&dst[pos], dry4); + pos += 4; + } while(--todo); + /* NOTE: step_count4 now represents the next four counts after + * the last four mixed samples, so the lowest element + * represents the next step count to apply. + */ + step_count = vgetq_lane_f32(step_count4, 0); + } + /* Mix with applying left over gain steps that aren't aligned multiples of 4. */ + for(size_t leftover{min_len&3};leftover;++pos,--leftover) + { + dst[pos] += InSamples[pos] * (gain + step*step_count); + step_count += 1.0f; + } + if(pos == Counter) + gain = *TargetGains; + else + gain += step*step_count; + + /* Mix until pos is aligned with 4 or the mix is done. */ + for(size_t leftover{aligned_len&3};leftover;++pos,--leftover) + dst[pos] += InSamples[pos] * gain; + } + *CurrentGains = gain; + ++CurrentGains; + ++TargetGains; + + if(!(std::abs(gain) > GainSilenceThreshold)) + continue; + if(size_t todo{(InSamples.size()-pos) >> 2}) + { + const float32x4_t gain4 = vdupq_n_f32(gain); + do { + const float32x4_t val4 = vld1q_f32(&InSamples[pos]); + float32x4_t dry4 = vld1q_f32(&dst[pos]); + dry4 = vmlaq_f32(dry4, val4, gain4); + vst1q_f32(&dst[pos], dry4); + pos += 4; + } while(--todo); + } + for(size_t leftover{(InSamples.size()-pos)&3};leftover;++pos,--leftover) + dst[pos] += InSamples[pos] * gain; + } +} diff --git a/core/mixer/mixer_sse.cpp b/core/mixer/mixer_sse.cpp new file mode 100644 index 00000000..85b2f1ce --- /dev/null +++ b/core/mixer/mixer_sse.cpp @@ -0,0 +1,266 @@ +#include "config.h" + +#include + +#include +#include + +#include "alnumeric.h" +#include "core/bsinc_defs.h" +#include "defs.h" +#include "hrtfbase.h" + +struct SSETag; +struct BSincTag; +struct FastBSincTag; + + +namespace { + +constexpr uint FracPhaseBitDiff{MixerFracBits - BSincPhaseBits}; +constexpr uint FracPhaseDiffOne{1 << FracPhaseBitDiff}; + +#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z)) + +inline void ApplyCoeffs(float2 *RESTRICT Values, const uint_fast32_t IrSize, + const HrirArray &Coeffs, const float left, const float right) +{ + const __m128 lrlr{_mm_setr_ps(left, right, left, right)}; + + ASSUME(IrSize >= MIN_IR_LENGTH); + /* This isn't technically correct to test alignment, but it's true for + * systems that support SSE, which is the only one that needs to know the + * alignment of Values (which alternates between 8- and 16-byte aligned). + */ + if(reinterpret_cast(Values)&0x8) + { + __m128 imp0, imp1; + __m128 coeffs{_mm_load_ps(&Coeffs[0][0])}; + __m128 vals{_mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<__m64*>(&Values[0][0]))}; + imp0 = _mm_mul_ps(lrlr, coeffs); + vals = _mm_add_ps(imp0, vals); + _mm_storel_pi(reinterpret_cast<__m64*>(&Values[0][0]), vals); + uint_fast32_t td{((IrSize+1)>>1) - 1}; + size_t i{1}; + do { + coeffs = _mm_load_ps(&Coeffs[i+1][0]); + vals = _mm_load_ps(&Values[i][0]); + imp1 = _mm_mul_ps(lrlr, coeffs); + imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2)); + vals = _mm_add_ps(imp0, vals); + _mm_store_ps(&Values[i][0], vals); + imp0 = imp1; + i += 2; + } while(--td); + vals = _mm_loadl_pi(vals, reinterpret_cast<__m64*>(&Values[i][0])); + imp0 = _mm_movehl_ps(imp0, imp0); + vals = _mm_add_ps(imp0, vals); + _mm_storel_pi(reinterpret_cast<__m64*>(&Values[i][0]), vals); + } + else + { + for(size_t i{0};i < IrSize;i += 2) + { + const __m128 coeffs{_mm_load_ps(&Coeffs[i][0])}; + __m128 vals{_mm_load_ps(&Values[i][0])}; + vals = MLA4(vals, lrlr, coeffs); + _mm_store_ps(&Values[i][0], vals); + } + } +} + +} // namespace + +template<> +const float *Resample_(const InterpState *state, const float *RESTRICT src, + uint frac, uint increment, const al::span dst) +{ + const float *const filter{state->bsinc.filter}; + const __m128 sf4{_mm_set1_ps(state->bsinc.sf)}; + const size_t m{state->bsinc.m}; + + src -= state->bsinc.l; + for(float &out_sample : dst) + { + // Calculate the phase index and factor. + const uint pi{frac >> FracPhaseBitDiff}; + const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; + + // Apply the scale and phase interpolated filter. + __m128 r4{_mm_setzero_ps()}; + { + const __m128 pf4{_mm_set1_ps(pf)}; + const float *fil{filter + m*pi*4}; + const float *phd{fil + m}; + const float *scd{phd + m}; + const float *spd{scd + m}; + size_t td{m >> 2}; + size_t j{0u}; + + do { + /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */ + const __m128 f4 = MLA4( + MLA4(_mm_load_ps(&fil[j]), sf4, _mm_load_ps(&scd[j])), + pf4, MLA4(_mm_load_ps(&phd[j]), sf4, _mm_load_ps(&spd[j]))); + /* r += f*src */ + r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j])); + j += 4; + } while(--td); + } + r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + out_sample = _mm_cvtss_f32(r4); + + frac += increment; + src += frac>>MixerFracBits; + frac &= MixerFracMask; + } + return dst.data(); +} + +template<> +const float *Resample_(const InterpState *state, const float *RESTRICT src, + uint frac, uint increment, const al::span dst) +{ + const float *const filter{state->bsinc.filter}; + const size_t m{state->bsinc.m}; + + src -= state->bsinc.l; + for(float &out_sample : dst) + { + // Calculate the phase index and factor. + const uint pi{frac >> FracPhaseBitDiff}; + const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; + + // Apply the phase interpolated filter. + __m128 r4{_mm_setzero_ps()}; + { + const __m128 pf4{_mm_set1_ps(pf)}; + const float *fil{filter + m*pi*4}; + const float *phd{fil + m}; + size_t td{m >> 2}; + size_t j{0u}; + + do { + /* f = fil + pf*phd */ + const __m128 f4 = MLA4(_mm_load_ps(&fil[j]), pf4, _mm_load_ps(&phd[j])); + /* r += f*src */ + r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j])); + j += 4; + } while(--td); + } + r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + out_sample = _mm_cvtss_f32(r4); + + frac += increment; + src += frac>>MixerFracBits; + frac &= MixerFracMask; + } + return dst.data(); +} + + +template<> +void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, + const MixHrtfFilter *hrtfparams, const size_t BufferSize) +{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, BufferSize); } + +template<> +void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, + const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize) +{ + MixHrtfBlendBase(InSamples, AccumSamples, IrSize, oldparams, newparams, + BufferSize); +} + +template<> +void MixDirectHrtf_(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, + const al::span InSamples, float2 *AccumSamples, + float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) +{ + MixDirectHrtfBase(LeftOut, RightOut, InSamples, AccumSamples, TempBuf, ChanState, + IrSize, BufferSize); +} + + +template<> +void Mix_(const al::span InSamples, const al::span OutBuffer, + float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos) +{ + const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; + const auto min_len = minz(Counter, InSamples.size()); + const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len; + + for(FloatBufferLine &output : OutBuffer) + { + float *RESTRICT dst{al::assume_aligned<16>(output.data()+OutPos)}; + float gain{*CurrentGains}; + const float step{(*TargetGains-gain) * delta}; + + size_t pos{0}; + if(!(std::abs(step) > std::numeric_limits::epsilon())) + gain = *TargetGains; + else + { + float step_count{0.0f}; + /* Mix with applying gain steps in aligned multiples of 4. */ + if(size_t todo{(min_len-pos) >> 2}) + { + const __m128 four4{_mm_set1_ps(4.0f)}; + const __m128 step4{_mm_set1_ps(step)}; + const __m128 gain4{_mm_set1_ps(gain)}; + __m128 step_count4{_mm_setr_ps(0.0f, 1.0f, 2.0f, 3.0f)}; + do { + const __m128 val4{_mm_load_ps(&InSamples[pos])}; + __m128 dry4{_mm_load_ps(&dst[pos])}; + + /* dry += val * (gain + step*step_count) */ + dry4 = MLA4(dry4, val4, MLA4(gain4, step4, step_count4)); + + _mm_store_ps(&dst[pos], dry4); + step_count4 = _mm_add_ps(step_count4, four4); + pos += 4; + } while(--todo); + /* NOTE: step_count4 now represents the next four counts after + * the last four mixed samples, so the lowest element + * represents the next step count to apply. + */ + step_count = _mm_cvtss_f32(step_count4); + } + /* Mix with applying left over gain steps that aren't aligned multiples of 4. */ + for(size_t leftover{min_len&3};leftover;++pos,--leftover) + { + dst[pos] += InSamples[pos] * (gain + step*step_count); + step_count += 1.0f; + } + if(pos == Counter) + gain = *TargetGains; + else + gain += step*step_count; + + /* Mix until pos is aligned with 4 or the mix is done. */ + for(size_t leftover{aligned_len&3};leftover;++pos,--leftover) + dst[pos] += InSamples[pos] * gain; + } + *CurrentGains = gain; + ++CurrentGains; + ++TargetGains; + + if(!(std::abs(gain) > GainSilenceThreshold)) + continue; + if(size_t todo{(InSamples.size()-pos) >> 2}) + { + const __m128 gain4{_mm_set1_ps(gain)}; + do { + const __m128 val4{_mm_load_ps(&InSamples[pos])}; + __m128 dry4{_mm_load_ps(&dst[pos])}; + dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4)); + _mm_store_ps(&dst[pos], dry4); + pos += 4; + } while(--todo); + } + for(size_t leftover{(InSamples.size()-pos)&3};leftover;++pos,--leftover) + dst[pos] += InSamples[pos] * gain; + } +} diff --git a/core/mixer/mixer_sse2.cpp b/core/mixer/mixer_sse2.cpp new file mode 100644 index 00000000..69fac250 --- /dev/null +++ b/core/mixer/mixer_sse2.cpp @@ -0,0 +1,85 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2014 by Timothy Arceri . + * 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 +#include + +#include "alnumeric.h" +#include "defs.h" + +struct SSE2Tag; +struct LerpTag; + + +template<> +const float *Resample_(const InterpState*, const float *RESTRICT src, uint frac, + uint increment, const al::span dst) +{ + const __m128i increment4{_mm_set1_epi32(static_cast(increment*4))}; + const __m128 fracOne4{_mm_set1_ps(1.0f/MixerFracOne)}; + const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)}; + + alignas(16) uint pos_[4], frac_[4]; + InitPosArrays(frac, increment, frac_, pos_, 4); + __m128i frac4{_mm_setr_epi32(static_cast(frac_[0]), static_cast(frac_[1]), + static_cast(frac_[2]), static_cast(frac_[3]))}; + __m128i pos4{_mm_setr_epi32(static_cast(pos_[0]), static_cast(pos_[1]), + static_cast(pos_[2]), static_cast(pos_[3]))}; + + auto dst_iter = dst.begin(); + for(size_t todo{dst.size()>>2};todo;--todo) + { + const int pos0{_mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(0, 0, 0, 0)))}; + const int pos1{_mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(1, 1, 1, 1)))}; + const int pos2{_mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(2, 2, 2, 2)))}; + const int pos3{_mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(3, 3, 3, 3)))}; + const __m128 val1{_mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ])}; + const __m128 val2{_mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1])}; + + /* val1 + (val2-val1)*mu */ + const __m128 r0{_mm_sub_ps(val2, val1)}; + const __m128 mu{_mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4)}; + const __m128 out{_mm_add_ps(val1, _mm_mul_ps(mu, r0))}; + + _mm_store_ps(dst_iter, out); + dst_iter += 4; + + frac4 = _mm_add_epi32(frac4, increment4); + pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, MixerFracBits)); + frac4 = _mm_and_si128(frac4, fracMask4); + } + + if(size_t todo{dst.size()&3}) + { + src += static_cast(_mm_cvtsi128_si32(pos4)); + frac = static_cast(_mm_cvtsi128_si32(frac4)); + + do { + *(dst_iter++) = lerp(src[0], src[1], static_cast(frac) * (1.0f/MixerFracOne)); + + frac += increment; + src += frac>>MixerFracBits; + frac &= MixerFracMask; + } while(--todo); + } + return dst.data(); +} diff --git a/core/mixer/mixer_sse3.cpp b/core/mixer/mixer_sse3.cpp new file mode 100644 index 00000000..e69de29b diff --git a/core/mixer/mixer_sse41.cpp b/core/mixer/mixer_sse41.cpp new file mode 100644 index 00000000..cacc9e64 --- /dev/null +++ b/core/mixer/mixer_sse41.cpp @@ -0,0 +1,90 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2014 by Timothy Arceri . + * 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 +#include +#include + +#include "alnumeric.h" +#include "defs.h" + +struct SSE4Tag; +struct LerpTag; + + +template<> +const float *Resample_(const InterpState*, const float *RESTRICT src, uint frac, + uint increment, const al::span dst) +{ + const __m128i increment4{_mm_set1_epi32(static_cast(increment*4))}; + const __m128 fracOne4{_mm_set1_ps(1.0f/MixerFracOne)}; + const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)}; + + alignas(16) uint pos_[4], frac_[4]; + InitPosArrays(frac, increment, frac_, pos_, 4); + __m128i frac4{_mm_setr_epi32(static_cast(frac_[0]), static_cast(frac_[1]), + static_cast(frac_[2]), static_cast(frac_[3]))}; + __m128i pos4{_mm_setr_epi32(static_cast(pos_[0]), static_cast(pos_[1]), + static_cast(pos_[2]), static_cast(pos_[3]))}; + + auto dst_iter = dst.begin(); + for(size_t todo{dst.size()>>2};todo;--todo) + { + const int pos0{_mm_extract_epi32(pos4, 0)}; + const int pos1{_mm_extract_epi32(pos4, 1)}; + const int pos2{_mm_extract_epi32(pos4, 2)}; + const int pos3{_mm_extract_epi32(pos4, 3)}; + const __m128 val1{_mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ])}; + const __m128 val2{_mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1])}; + + /* val1 + (val2-val1)*mu */ + const __m128 r0{_mm_sub_ps(val2, val1)}; + const __m128 mu{_mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4)}; + const __m128 out{_mm_add_ps(val1, _mm_mul_ps(mu, r0))}; + + _mm_store_ps(dst_iter, out); + dst_iter += 4; + + frac4 = _mm_add_epi32(frac4, increment4); + pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, MixerFracBits)); + frac4 = _mm_and_si128(frac4, fracMask4); + } + + if(size_t todo{dst.size()&3}) + { + /* NOTE: These four elements represent the position *after* the last + * four samples, so the lowest element is the next position to + * resample. + */ + src += static_cast(_mm_cvtsi128_si32(pos4)); + frac = static_cast(_mm_cvtsi128_si32(frac4)); + + do { + *(dst_iter++) = lerp(src[0], src[1], static_cast(frac) * (1.0f/MixerFracOne)); + + frac += increment; + src += frac>>MixerFracBits; + frac &= MixerFracMask; + } while(--todo); + } + return dst.data(); +} -- cgit v1.2.3 From 783904e414024691d47e76a22d8405ca85dd04f2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 13 Dec 2020 16:49:46 -0800 Subject: Avoid compiling different sources for different targets Simplifies configuration and fixes a potential problem with inline functions. Inline functions that fail to inline will have a callable body generated. If such a body is generated with the SSE4 source, for example, it can generate SSE4 instructions. Calls for that function in other sources can then end up calling the SSE4-generated body outside of any CPU capability check. --- CMakeLists.txt | 38 ++++++-------------------------------- core/mixer/mixer_neon.cpp | 4 ++++ core/mixer/mixer_sse.cpp | 5 +++++ core/mixer/mixer_sse2.cpp | 4 ++++ core/mixer/mixer_sse41.cpp | 4 ++++ 5 files changed, 23 insertions(+), 32 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index a5ae4d43..df5fa66f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -344,8 +344,6 @@ endif() set(SSE2_SWITCH "") -set(SSE3_SWITCH "") -set(SSE4_1_SWITCH "") set(FPU_NEON_SWITCH "") set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) @@ -356,14 +354,6 @@ endif() check_c_compiler_flag(-msse2 HAVE_MSSE2_SWITCH) if(HAVE_MSSE2_SWITCH) set(SSE2_SWITCH "-msse2") - check_c_compiler_flag(-msse3 HAVE_MSSE3_SWITCH) - if(HAVE_MSSE3_SWITCH) - set(SSE3_SWITCH "-msse3") - check_c_compiler_flag(-msse4.1 HAVE_MSSE4_1_SWITCH) - if(HAVE_MSSE4_1_SWITCH) - set(SSE4_1_SWITCH "-msse4.1") - endif() - endif() endif() check_c_compiler_flag(-mfpu=neon HAVE_MFPU_NEON_SWITCH) if(HAVE_MFPU_NEON_SWITCH) @@ -372,11 +362,11 @@ endif() set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS}) unset(OLD_REQUIRED_FLAGS) -check_include_file(xmmintrin.h HAVE_XMMINTRIN_H ${SSE2_SWITCH}) -check_include_file(emmintrin.h HAVE_EMMINTRIN_H ${SSE2_SWITCH}) -check_include_file(pmmintrin.h HAVE_PMMINTRIN_H ${SSE3_SWITCH}) -check_include_file(smmintrin.h HAVE_SMMINTRIN_H ${SSE4_1_SWITCH}) -check_include_file(arm_neon.h HAVE_ARM_NEON_H ${FPU_NEON_SWITCH}) +check_include_file(xmmintrin.h HAVE_XMMINTRIN_H) +check_include_file(emmintrin.h HAVE_EMMINTRIN_H) +check_include_file(pmmintrin.h HAVE_PMMINTRIN_H) +check_include_file(smmintrin.h HAVE_SMMINTRIN_H) +check_include_file(arm_neon.h HAVE_ARM_NEON_H) set(SSE_FLAGS ) set(FPMATH_SET "0") @@ -726,10 +716,6 @@ if(HAVE_XMMINTRIN_H AND HAVE_EMMINTRIN_H) set(HAVE_SSE 1) set(HAVE_SSE2 1) set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse.cpp core/mixer/mixer_sse2.cpp) - if(SSE2_SWITCH) - set_source_files_properties(core/mixer/mixer_sse.cpp core/mixer/mixer_sse2.cpp - PROPERTIES COMPILE_FLAGS "${SSE2_SWITCH}") - endif() set(CPU_EXTS "${CPU_EXTS}, SSE, SSE2") endif() endif() @@ -741,15 +727,11 @@ if(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2) endif() option(ALSOFT_REQUIRE_SSE3 "Require SSE3 support" OFF) -if(HAVE_EMMINTRIN_H) +if(HAVE_PMMINTRIN_H) option(ALSOFT_CPUEXT_SSE3 "Enable SSE3 support" ON) if(HAVE_SSE2 AND ALSOFT_CPUEXT_SSE3) set(HAVE_SSE3 1) set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse3.cpp) - if(SSE2_SWITCH) - set_source_files_properties(core/mixer/mixer_sse3.cpp PROPERTIES - COMPILE_FLAGS "${SSE3_SWITCH}") - endif() set(CPU_EXTS "${CPU_EXTS}, SSE3") endif() endif() @@ -763,10 +745,6 @@ if(HAVE_SMMINTRIN_H) if(HAVE_SSE3 AND ALSOFT_CPUEXT_SSE4_1) set(HAVE_SSE4_1 1) set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse41.cpp) - if(SSE4_1_SWITCH) - set_source_files_properties(core/mixer/mixer_sse41.cpp PROPERTIES - COMPILE_FLAGS "${SSE4_1_SWITCH}") - endif() set(CPU_EXTS "${CPU_EXTS}, SSE4.1") endif() endif() @@ -781,10 +759,6 @@ if(HAVE_ARM_NEON_H) if(ALSOFT_CPUEXT_NEON) set(HAVE_NEON 1) set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_neon.cpp) - if(FPU_NEON_SWITCH) - set_source_files_properties(core/mixer/mixer_neon.cpp PROPERTIES - COMPILE_FLAGS "${FPU_NEON_SWITCH}") - endif() set(CPU_EXTS "${CPU_EXTS}, Neon") endif() endif() diff --git a/core/mixer/mixer_neon.cpp b/core/mixer/mixer_neon.cpp index 3c7ddd4e..cc6dd71d 100644 --- a/core/mixer/mixer_neon.cpp +++ b/core/mixer/mixer_neon.cpp @@ -16,6 +16,10 @@ struct BSincTag; struct FastBSincTag; +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ARM_NEON) +#pragma GCC target("fpu=neon") +#endif + namespace { inline float32x4_t set_f4(float l0, float l1, float l2, float l3) diff --git a/core/mixer/mixer_sse.cpp b/core/mixer/mixer_sse.cpp index ff722c19..f21ec227 100644 --- a/core/mixer/mixer_sse.cpp +++ b/core/mixer/mixer_sse.cpp @@ -15,6 +15,11 @@ struct BSincTag; struct FastBSincTag; +/* SSE2 is required for any SSE support. */ +#if defined(__GNUC__) && !defined(__clang__) && !defined(__SSE2__) +#pragma GCC target("sse2") +#endif + namespace { constexpr uint FracPhaseBitDiff{MixerFracBits - BSincPhaseBits}; diff --git a/core/mixer/mixer_sse2.cpp b/core/mixer/mixer_sse2.cpp index 69fac250..a93a33f9 100644 --- a/core/mixer/mixer_sse2.cpp +++ b/core/mixer/mixer_sse2.cpp @@ -30,6 +30,10 @@ struct SSE2Tag; struct LerpTag; +#if defined(__GNUC__) && !defined(__clang__) && !defined(__SSE2__) +#pragma GCC target("sse2") +#endif + template<> const float *Resample_(const InterpState*, const float *RESTRICT src, uint frac, uint increment, const al::span dst) diff --git a/core/mixer/mixer_sse41.cpp b/core/mixer/mixer_sse41.cpp index cacc9e64..f7839b78 100644 --- a/core/mixer/mixer_sse41.cpp +++ b/core/mixer/mixer_sse41.cpp @@ -31,6 +31,10 @@ struct SSE4Tag; struct LerpTag; +#if defined(__GNUC__) && !defined(__clang__) && !defined(__SSE4_1__) +#pragma GCC target("sse4.1") +#endif + template<> const float *Resample_(const InterpState*, const float *RESTRICT src, uint frac, uint increment, const al::span dst) -- cgit v1.2.3 From 56af137ba0a3ab847b8f57c3c4aeee606aa5403f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 13 Dec 2020 21:12:03 -0800 Subject: Move fmt_traits to core --- CMakeLists.txt | 4 +-- alc/effects/convolution.cpp | 2 +- alc/fmt_traits.cpp | 79 ------------------------------------------- alc/fmt_traits.h | 81 --------------------------------------------- alc/voice.cpp | 2 +- core/fmt_traits.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++ core/fmt_traits.h | 81 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 164 insertions(+), 164 deletions(-) delete mode 100644 alc/fmt_traits.cpp delete mode 100644 alc/fmt_traits.h create mode 100644 core/fmt_traits.cpp create mode 100644 core/fmt_traits.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index df5fa66f..13876def 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -602,6 +602,8 @@ set(CORE_OBJS core/filters/nfc.h core/filters/splitter.cpp core/filters/splitter.h + core/fmt_traits.cpp + core/fmt_traits.h core/mastering.cpp core/mastering.h core/uhjfilter.cpp @@ -683,8 +685,6 @@ set(ALC_OBJS alc/effects/pshifter.cpp alc/effects/reverb.cpp alc/effects/vmorpher.cpp - alc/fmt_traits.cpp - alc/fmt_traits.h alc/fpu_ctrl.cpp alc/fpu_ctrl.h alc/front_stablizer.h diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index b190e75e..677c59ba 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -17,9 +17,9 @@ #include "buffer_storage.h" #include "core/ambidefs.h" #include "core/filters/splitter.h" +#include "core/fmt_traits.h" #include "effects/base.h" #include "effectslot.h" -#include "fmt_traits.h" #include "logging.h" #include "polyphase_resampler.h" diff --git a/alc/fmt_traits.cpp b/alc/fmt_traits.cpp deleted file mode 100644 index 054d8766..00000000 --- a/alc/fmt_traits.cpp +++ /dev/null @@ -1,79 +0,0 @@ - -#include "config.h" - -#include "fmt_traits.h" - - -namespace al { - -const int16_t muLawDecompressionTable[256] = { - -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, - -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, - -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, - -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, - -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, - -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, - -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, - -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, - -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, - -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, - -876, -844, -812, -780, -748, -716, -684, -652, - -620, -588, -556, -524, -492, -460, -428, -396, - -372, -356, -340, -324, -308, -292, -276, -260, - -244, -228, -212, -196, -180, -164, -148, -132, - -120, -112, -104, -96, -88, -80, -72, -64, - -56, -48, -40, -32, -24, -16, -8, 0, - 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, - 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, - 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, - 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, - 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, - 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, - 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, - 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, - 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, - 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, - 876, 844, 812, 780, 748, 716, 684, 652, - 620, 588, 556, 524, 492, 460, 428, 396, - 372, 356, 340, 324, 308, 292, 276, 260, - 244, 228, 212, 196, 180, 164, 148, 132, - 120, 112, 104, 96, 88, 80, 72, 64, - 56, 48, 40, 32, 24, 16, 8, 0 -}; - -const int16_t aLawDecompressionTable[256] = { - -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, - -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, - -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, - -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, - -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, - -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, - -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472, - -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, - -344, -328, -376, -360, -280, -264, -312, -296, - -472, -456, -504, -488, -408, -392, -440, -424, - -88, -72, -120, -104, -24, -8, -56, -40, - -216, -200, -248, -232, -152, -136, -184, -168, - -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, - -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, - -688, -656, -752, -720, -560, -528, -624, -592, - -944, -912, -1008, -976, -816, -784, -880, -848, - 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, - 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, - 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, - 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, - 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, - 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, - 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, - 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, - 344, 328, 376, 360, 280, 264, 312, 296, - 472, 456, 504, 488, 408, 392, 440, 424, - 88, 72, 120, 104, 24, 8, 56, 40, - 216, 200, 248, 232, 152, 136, 184, 168, - 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, - 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, - 688, 656, 752, 720, 560, 528, 624, 592, - 944, 912, 1008, 976, 816, 784, 880, 848 -}; - -} // namespace al diff --git a/alc/fmt_traits.h b/alc/fmt_traits.h deleted file mode 100644 index 9d2a2567..00000000 --- a/alc/fmt_traits.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef ALC_FMT_TRAITS_H -#define ALC_FMT_TRAITS_H - -#include -#include - -#include "albyte.h" -#include "buffer_storage.h" - - -namespace al { - -extern const int16_t muLawDecompressionTable[256]; -extern const int16_t aLawDecompressionTable[256]; - - -template -struct FmtTypeTraits { }; - -template<> -struct FmtTypeTraits { - using Type = uint8_t; - - template - static constexpr inline OutT to(const Type val) noexcept - { return val*OutT{1.0/128.0} - OutT{1.0}; } -}; -template<> -struct FmtTypeTraits { - using Type = int16_t; - - template - static constexpr inline OutT to(const Type val) noexcept { return val*OutT{1.0/32768.0}; } -}; -template<> -struct FmtTypeTraits { - using Type = float; - - template - static constexpr inline OutT to(const Type val) noexcept { return val; } -}; -template<> -struct FmtTypeTraits { - using Type = double; - - template - static constexpr inline OutT to(const Type val) noexcept { return static_cast(val); } -}; -template<> -struct FmtTypeTraits { - using Type = uint8_t; - - template - static constexpr inline OutT to(const Type val) noexcept - { return muLawDecompressionTable[val] * OutT{1.0/32768.0}; } -}; -template<> -struct FmtTypeTraits { - using Type = uint8_t; - - template - static constexpr inline OutT to(const Type val) noexcept - { return aLawDecompressionTable[val] * OutT{1.0/32768.0}; } -}; - - -template -inline void LoadSampleArray(DstT *RESTRICT dst, const al::byte *src, const size_t srcstep, - const size_t samples) noexcept -{ - using TypeTraits = FmtTypeTraits; - using SampleType = typename TypeTraits::Type; - - const SampleType *RESTRICT ssrc{reinterpret_cast(src)}; - for(size_t i{0u};i < samples;i++) - dst[i] = TypeTraits::template to(ssrc[i*srcstep]); -} - -} // namespace al - -#endif /* ALC_FMT_TRAITS_H */ diff --git a/alc/voice.cpp b/alc/voice.cpp index 8aa5a59f..8b4e0c2e 100644 --- a/alc/voice.cpp +++ b/alc/voice.cpp @@ -50,10 +50,10 @@ #include "core/filters/biquad.h" #include "core/filters/nfc.h" #include "core/filters/splitter.h" +#include "core/fmt_traits.h" #include "core/mixer/defs.h" #include "core/mixer/hrtfdefs.h" #include "cpu_caps.h" -#include "fmt_traits.h" #include "hrtf.h" #include "inprogext.h" #include "logging.h" diff --git a/core/fmt_traits.cpp b/core/fmt_traits.cpp new file mode 100644 index 00000000..054d8766 --- /dev/null +++ b/core/fmt_traits.cpp @@ -0,0 +1,79 @@ + +#include "config.h" + +#include "fmt_traits.h" + + +namespace al { + +const int16_t muLawDecompressionTable[256] = { + -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, + -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, + -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, + -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, 0, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 0 +}; + +const int16_t aLawDecompressionTable[256] = { + -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, + -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, + -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, + -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472, + -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, + -344, -328, -376, -360, -280, -264, -312, -296, + -472, -456, -504, -488, -408, -392, -440, -424, + -88, -72, -120, -104, -24, -8, -56, -40, + -216, -200, -248, -232, -152, -136, -184, -168, + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, -592, + -944, -912, -1008, -976, -816, -784, -880, -848, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848 +}; + +} // namespace al diff --git a/core/fmt_traits.h b/core/fmt_traits.h new file mode 100644 index 00000000..f797f836 --- /dev/null +++ b/core/fmt_traits.h @@ -0,0 +1,81 @@ +#ifndef CORE_FMT_TRAITS_H +#define CORE_FMT_TRAITS_H + +#include +#include + +#include "albyte.h" +#include "buffer_storage.h" + + +namespace al { + +extern const int16_t muLawDecompressionTable[256]; +extern const int16_t aLawDecompressionTable[256]; + + +template +struct FmtTypeTraits { }; + +template<> +struct FmtTypeTraits { + using Type = uint8_t; + + template + static constexpr inline OutT to(const Type val) noexcept + { return val*OutT{1.0/128.0} - OutT{1.0}; } +}; +template<> +struct FmtTypeTraits { + using Type = int16_t; + + template + static constexpr inline OutT to(const Type val) noexcept { return val*OutT{1.0/32768.0}; } +}; +template<> +struct FmtTypeTraits { + using Type = float; + + template + static constexpr inline OutT to(const Type val) noexcept { return val; } +}; +template<> +struct FmtTypeTraits { + using Type = double; + + template + static constexpr inline OutT to(const Type val) noexcept { return static_cast(val); } +}; +template<> +struct FmtTypeTraits { + using Type = uint8_t; + + template + static constexpr inline OutT to(const Type val) noexcept + { return muLawDecompressionTable[val] * OutT{1.0/32768.0}; } +}; +template<> +struct FmtTypeTraits { + using Type = uint8_t; + + template + static constexpr inline OutT to(const Type val) noexcept + { return aLawDecompressionTable[val] * OutT{1.0/32768.0}; } +}; + + +template +inline void LoadSampleArray(DstT *RESTRICT dst, const al::byte *src, const size_t srcstep, + const size_t samples) noexcept +{ + using TypeTraits = FmtTypeTraits; + using SampleType = typename TypeTraits::Type; + + const SampleType *RESTRICT ssrc{reinterpret_cast(src)}; + for(size_t i{0u};i < samples;i++) + dst[i] = TypeTraits::template to(ssrc[i*srcstep]); +} + +} // namespace al + +#endif /* CORE_FMT_TRAITS_H */ -- cgit v1.2.3 From 5ad28f8cbaa52f3f6bf4c4cdbfbdbeb3087020e1 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 16 Dec 2020 00:50:50 -0800 Subject: Move VoiceChange to a separate header --- CMakeLists.txt | 3 ++- al/source.cpp | 1 + alc/alc.cpp | 1 + alc/alcontext.h | 13 +------------ alc/alu.cpp | 1 + alc/voice_change.h | 24 ++++++++++++++++++++++++ 6 files changed, 30 insertions(+), 13 deletions(-) create mode 100644 alc/voice_change.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 13876def..551448c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -696,7 +696,8 @@ set(ALC_OBJS alc/panning.cpp alc/uiddefs.cpp alc/voice.cpp - alc/voice.h) + alc/voice.h + alc/voice_change.h) set(CPU_EXTS "Default") diff --git a/al/source.cpp b/al/source.cpp index 762c9c09..22edc8a7 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -69,6 +69,7 @@ #include "opthelpers.h" #include "ringbuffer.h" #include "threads.h" +#include "voice_change.h" namespace { diff --git a/alc/alc.cpp b/alc/alc.cpp index c712c311..38696498 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -100,6 +100,7 @@ #include "threads.h" #include "vecmat.h" #include "vector.h" +#include "voice_change.h" #include "backends/base.h" #include "backends/null.h" diff --git a/alc/alcontext.h b/alc/alcontext.h index 37cd010d..c13930b9 100644 --- a/alc/alcontext.h +++ b/alc/alcontext.h @@ -30,6 +30,7 @@ struct ALsource; struct EffectSlot; struct EffectSlotProps; struct RingBuffer; +struct VoiceChange; enum class DistanceModel { @@ -100,18 +101,6 @@ struct ContextParams { }; -struct VoiceChange { - Voice *mOldVoice{nullptr}; - Voice *mVoice{nullptr}; - ALuint mSourceID{0}; - ALenum mState{0}; - - std::atomic mNext{nullptr}; - - DEF_NEWDEL(VoiceChange) -}; - - struct SourceSubList { uint64_t FreeMask{~0_u64}; ALsource *Sources{nullptr}; /* 64 */ diff --git a/alc/alu.cpp b/alc/alu.cpp index 007a328b..36113b80 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -77,6 +77,7 @@ #include "threads.h" #include "vecmat.h" #include "voice.h" +#include "voice_change.h" struct CTag; #ifdef HAVE_SSE diff --git a/alc/voice_change.h b/alc/voice_change.h new file mode 100644 index 00000000..1ce28f50 --- /dev/null +++ b/alc/voice_change.h @@ -0,0 +1,24 @@ +#ifndef VOICE_CHANGE_H +#define VOICE_CHANGE_H + +#include + +#include "almalloc.h" + +struct Voice; + +using uint = unsigned int; + + +struct VoiceChange { + Voice *mOldVoice{nullptr}; + Voice *mVoice{nullptr}; + uint mSourceID{0}; + int mState{0}; + + std::atomic mNext{nullptr}; + + DEF_NEWDEL(VoiceChange) +}; + +#endif /* VOICE_CHANGE_H */ -- cgit v1.2.3 From efc9c146c3c9c2c08af88a6c26d13da9ec5be074 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 16 Dec 2020 13:58:23 -0800 Subject: Move AsyncEvent to a separate header --- CMakeLists.txt | 1 + al/event.cpp | 1 + al/event.h | 45 --------------------------------------------- alc/alc.cpp | 1 + alc/alu.cpp | 2 +- alc/async_event.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ alc/voice.cpp | 2 +- 7 files changed, 54 insertions(+), 47 deletions(-) create mode 100644 alc/async_event.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 551448c4..dace780b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -659,6 +659,7 @@ set(ALC_OBJS alc/alcontext.h alc/ambdec.cpp alc/ambdec.h + alc/async_event.h alc/bformatdec.cpp alc/bformatdec.h alc/buffer_storage.cpp diff --git a/al/event.cpp b/al/event.cpp index 9db36137..ada92e24 100644 --- a/al/event.cpp +++ b/al/event.cpp @@ -21,6 +21,7 @@ #include "alcontext.h" #include "alexcpt.h" #include "almalloc.h" +#include "async_event.h" #include "effects/base.h" #include "inprogext.h" #include "logging.h" diff --git a/al/event.h b/al/event.h index 53f62dac..83513c51 100644 --- a/al/event.h +++ b/al/event.h @@ -1,52 +1,7 @@ #ifndef AL_EVENT_H #define AL_EVENT_H -#include "almalloc.h" - struct ALCcontext; -struct EffectState; -enum class VChangeState; - -using uint = unsigned int; - - -enum { - /* End event thread processing. */ - EventType_KillThread = 0, - - /* User event types. */ - EventType_SourceStateChange = 1<<0, - EventType_BufferCompleted = 1<<1, - EventType_Disconnected = 1<<2, - - /* Internal events. */ - EventType_ReleaseEffectState = 65536, -}; - -struct AsyncEvent { - uint EnumType{0u}; - union { - char dummy; - struct { - uint id; - VChangeState state; - } srcstate; - struct { - uint id; - uint count; - } bufcomp; - struct { - char msg[244]; - } disconnect; - EffectState *mEffectState; - } u{}; - - AsyncEvent() noexcept = default; - constexpr AsyncEvent(uint type) noexcept : EnumType{type} { } - - DISABLE_ALLOC() -}; - void StartEventThrd(ALCcontext *ctx); void StopEventThrd(ALCcontext *ctx); diff --git a/alc/alc.cpp b/alc/alc.cpp index 38696498..2720033e 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -75,6 +75,7 @@ #include "alspan.h" #include "alstring.h" #include "alu.h" +#include "async_event.h" #include "atomic.h" #include "bformatdec.h" #include "compat.h" diff --git a/alc/alu.cpp b/alc/alu.cpp index 790b8485..6ecafd09 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -44,13 +44,13 @@ #include "AL/alc.h" #include "AL/efx.h" -#include "al/event.h" #include "alcmain.h" #include "alcontext.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" #include "alstring.h" +#include "async_event.h" #include "atomic.h" #include "bformatdec.h" #include "core/ambidefs.h" diff --git a/alc/async_event.h b/alc/async_event.h new file mode 100644 index 00000000..1ee58b10 --- /dev/null +++ b/alc/async_event.h @@ -0,0 +1,49 @@ +#ifndef ALC_EVENT_H +#define ALC_EVENT_H + +#include "almalloc.h" + +struct EffectState; +enum class VChangeState; + +using uint = unsigned int; + + +enum { + /* End event thread processing. */ + EventType_KillThread = 0, + + /* User event types. */ + EventType_SourceStateChange = 1<<0, + EventType_BufferCompleted = 1<<1, + EventType_Disconnected = 1<<2, + + /* Internal events. */ + EventType_ReleaseEffectState = 65536, +}; + +struct AsyncEvent { + uint EnumType{0u}; + union { + char dummy; + struct { + uint id; + VChangeState state; + } srcstate; + struct { + uint id; + uint count; + } bufcomp; + struct { + char msg[244]; + } disconnect; + EffectState *mEffectState; + } u{}; + + AsyncEvent() noexcept = default; + constexpr AsyncEvent(uint type) noexcept : EnumType{type} { } + + DISABLE_ALLOC() +}; + +#endif diff --git a/alc/voice.cpp b/alc/voice.cpp index 6c00a6a2..15240544 100644 --- a/alc/voice.cpp +++ b/alc/voice.cpp @@ -34,7 +34,6 @@ #include #include -#include "al/event.h" #include "alcmain.h" #include "albyte.h" #include "alconfig.h" @@ -44,6 +43,7 @@ #include "alspan.h" #include "alstring.h" #include "alu.h" +#include "async_event.h" #include "buffer_storage.h" #include "core/devformat.h" #include "core/filters/biquad.h" -- cgit v1.2.3 From d578bc6cb1b9bce4954ded9b138d51980163c233 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 17 Dec 2020 01:25:33 -0800 Subject: Move logging to core --- CMakeLists.txt | 3 +- al/auxeffectslot.cpp | 2 +- al/effect.cpp | 2 +- al/error.cpp | 2 +- al/event.cpp | 2 +- al/source.cpp | 2 +- alc/alc.cpp | 2 +- alc/alconfig.cpp | 2 +- alc/ambdec.cpp | 2 +- alc/backends/alsa.cpp | 2 +- alc/backends/base.cpp | 2 +- alc/backends/coreaudio.cpp | 2 +- alc/backends/dsound.cpp | 2 +- alc/backends/jack.cpp | 2 +- alc/backends/null.cpp | 1 - alc/backends/oboe.cpp | 2 +- alc/backends/opensl.cpp | 2 +- alc/backends/oss.cpp | 2 +- alc/backends/portaudio.cpp | 2 +- alc/backends/pulseaudio.cpp | 2 +- alc/backends/sdl2.cpp | 2 +- alc/backends/sndio.cpp | 4 +- alc/backends/solaris.cpp | 2 +- alc/backends/wasapi.cpp | 2 +- alc/backends/wave.cpp | 2 +- alc/backends/winmm.cpp | 2 +- alc/effects/convolution.cpp | 2 +- alc/helpers.cpp | 79 +------------------------------------ alc/hrtf.cpp | 2 +- alc/logging.h | 47 ---------------------- alc/panning.cpp | 2 +- alc/voice.cpp | 2 +- core/logging.cpp | 95 +++++++++++++++++++++++++++++++++++++++++++++ core/logging.h | 47 ++++++++++++++++++++++ 34 files changed, 174 insertions(+), 156 deletions(-) delete mode 100644 alc/logging.h create mode 100644 core/logging.cpp create mode 100644 core/logging.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index dace780b..fa9e7d37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -604,6 +604,8 @@ set(CORE_OBJS core/filters/splitter.h core/fmt_traits.cpp core/fmt_traits.h + core/logging.cpp + core/logging.h core/mastering.cpp core/mastering.h core/uhjfilter.cpp @@ -693,7 +695,6 @@ set(ALC_OBJS alc/hrtf.cpp alc/hrtf.h alc/inprogext.h - alc/logging.h alc/panning.cpp alc/uiddefs.cpp alc/voice.cpp diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp index 46f68374..a69df77a 100644 --- a/al/auxeffectslot.cpp +++ b/al/auxeffectslot.cpp @@ -42,10 +42,10 @@ #include "alspan.h" #include "alu.h" #include "buffer.h" +#include "core/logging.h" #include "effect.h" #include "fpu_ctrl.h" #include "inprogext.h" -#include "logging.h" #include "opthelpers.h" diff --git a/al/effect.cpp b/al/effect.cpp index b45ba00a..3cfccaa3 100644 --- a/al/effect.cpp +++ b/al/effect.cpp @@ -44,8 +44,8 @@ #include "almalloc.h" #include "alnumeric.h" #include "alstring.h" +#include "core/logging.h" #include "effects/base.h" -#include "logging.h" #include "opthelpers.h" #include "vector.h" diff --git a/al/error.cpp b/al/error.cpp index 79bbc56c..f9c408d2 100644 --- a/al/error.cpp +++ b/al/error.cpp @@ -38,7 +38,7 @@ #include "alcontext.h" #include "alexcpt.h" #include "almalloc.h" -#include "logging.h" +#include "core/logging.h" #include "opthelpers.h" #include "vector.h" diff --git a/al/event.cpp b/al/event.cpp index ada92e24..6834253a 100644 --- a/al/event.cpp +++ b/al/event.cpp @@ -22,9 +22,9 @@ #include "alexcpt.h" #include "almalloc.h" #include "async_event.h" +#include "core/logging.h" #include "effects/base.h" #include "inprogext.h" -#include "logging.h" #include "opthelpers.h" #include "ringbuffer.h" #include "threads.h" diff --git a/al/source.cpp b/al/source.cpp index 2cb74589..5fae7b54 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -61,10 +61,10 @@ #include "core/ambidefs.h" #include "core/filters/nfc.h" #include "core/filters/splitter.h" +#include "core/logging.h" #include "event.h" #include "filter.h" #include "inprogext.h" -#include "logging.h" #include "math_defs.h" #include "opthelpers.h" #include "ringbuffer.h" diff --git a/alc/alc.cpp b/alc/alc.cpp index 2720033e..6fd61b44 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -85,6 +85,7 @@ #include "core/mastering.h" #include "core/filters/nfc.h" #include "core/filters/splitter.h" +#include "core/logging.h" #include "core/uhjfilter.h" #include "cpu_caps.h" #include "effects/base.h" @@ -93,7 +94,6 @@ #include "hrtf.h" #include "inprogext.h" #include "intrusive_ptr.h" -#include "logging.h" #include "opthelpers.h" #include "pragmadefs.h" #include "ringbuffer.h" diff --git a/alc/alconfig.cpp b/alc/alconfig.cpp index ac075b54..634679aa 100644 --- a/alc/alconfig.cpp +++ b/alc/alconfig.cpp @@ -41,7 +41,7 @@ #include "alfstream.h" #include "alstring.h" #include "compat.h" -#include "logging.h" +#include "core/logging.h" #include "strutils.h" #include "vector.h" diff --git a/alc/ambdec.cpp b/alc/ambdec.cpp index eaeb9086..a50d6d95 100644 --- a/alc/ambdec.cpp +++ b/alc/ambdec.cpp @@ -11,7 +11,7 @@ #include #include "alfstream.h" -#include "logging.h" +#include "core/logging.h" namespace { diff --git a/alc/backends/alsa.cpp b/alc/backends/alsa.cpp index a07d006b..0166e28c 100644 --- a/alc/backends/alsa.cpp +++ b/alc/backends/alsa.cpp @@ -45,8 +45,8 @@ #include "alnumeric.h" #include "aloptional.h" #include "alu.h" +#include "core/logging.h" #include "dynload.h" -#include "logging.h" #include "ringbuffer.h" #include "threads.h" #include "vector.h" diff --git a/alc/backends/base.cpp b/alc/backends/base.cpp index 04fc4d64..9d8d0c64 100644 --- a/alc/backends/base.cpp +++ b/alc/backends/base.cpp @@ -19,7 +19,7 @@ #include "alnumeric.h" #include "aloptional.h" #include "atomic.h" -#include "logging.h" +#include "core/logging.h" bool BackendBase::reset() diff --git a/alc/backends/coreaudio.cpp b/alc/backends/coreaudio.cpp index 32f8d123..80259667 100644 --- a/alc/backends/coreaudio.cpp +++ b/alc/backends/coreaudio.cpp @@ -34,7 +34,7 @@ #include "alu.h" #include "ringbuffer.h" #include "converter.h" -#include "logging.h" +#include "core/logging.h" #include "backends/base.h" #include diff --git a/alc/backends/dsound.cpp b/alc/backends/dsound.cpp index 9ff5505b..061e9a18 100644 --- a/alc/backends/dsound.cpp +++ b/alc/backends/dsound.cpp @@ -48,8 +48,8 @@ #include "alexcpt.h" #include "alu.h" #include "compat.h" +#include "core/logging.h" #include "dynload.h" -#include "logging.h" #include "ringbuffer.h" #include "strutils.h" #include "threads.h" diff --git a/alc/backends/jack.cpp b/alc/backends/jack.cpp index bc3a2d09..871562ff 100644 --- a/alc/backends/jack.cpp +++ b/alc/backends/jack.cpp @@ -35,8 +35,8 @@ #include "alu.h" #include "alconfig.h" #include "alexcpt.h" +#include "core/logging.h" #include "dynload.h" -#include "logging.h" #include "ringbuffer.h" #include "threads.h" diff --git a/alc/backends/null.cpp b/alc/backends/null.cpp index f78a23a0..8a134a93 100644 --- a/alc/backends/null.cpp +++ b/alc/backends/null.cpp @@ -34,7 +34,6 @@ #include "alexcpt.h" #include "almalloc.h" #include "alu.h" -#include "logging.h" #include "threads.h" diff --git a/alc/backends/oboe.cpp b/alc/backends/oboe.cpp index f4a9877a..1c1b22cd 100644 --- a/alc/backends/oboe.cpp +++ b/alc/backends/oboe.cpp @@ -7,7 +7,7 @@ #include #include "alu.h" -#include "logging.h" +#include "core/logging.h" #include "oboe/Oboe.h" diff --git a/alc/backends/opensl.cpp b/alc/backends/opensl.cpp index 74309a94..185c9999 100644 --- a/alc/backends/opensl.cpp +++ b/alc/backends/opensl.cpp @@ -36,8 +36,8 @@ #include "alexcpt.h" #include "alu.h" #include "compat.h" +#include "core/logging.h" #include "endiantest.h" -#include "logging.h" #include "ringbuffer.h" #include "threads.h" diff --git a/alc/backends/oss.cpp b/alc/backends/oss.cpp index 0f00115e..513f481a 100644 --- a/alc/backends/oss.cpp +++ b/alc/backends/oss.cpp @@ -50,7 +50,7 @@ #include "alnumeric.h" #include "aloptional.h" #include "alu.h" -#include "logging.h" +#include "core/logging.h" #include "ringbuffer.h" #include "threads.h" #include "vector.h" diff --git a/alc/backends/portaudio.cpp b/alc/backends/portaudio.cpp index 42dbb4c9..54fac803 100644 --- a/alc/backends/portaudio.cpp +++ b/alc/backends/portaudio.cpp @@ -30,9 +30,9 @@ #include "alexcpt.h" #include "alu.h" #include "alconfig.h" +#include "core/logging.h" #include "dynload.h" #include "ringbuffer.h" -#include "logging.h" #include diff --git a/alc/backends/pulseaudio.cpp b/alc/backends/pulseaudio.cpp index 5bcd8b5f..73312722 100644 --- a/alc/backends/pulseaudio.cpp +++ b/alc/backends/pulseaudio.cpp @@ -40,8 +40,8 @@ #include "alconfig.h" #include "alexcpt.h" #include "compat.h" +#include "core/logging.h" #include "dynload.h" -#include "logging.h" #include "strutils.h" #include diff --git a/alc/backends/sdl2.cpp b/alc/backends/sdl2.cpp index 30335140..05e8e4e0 100644 --- a/alc/backends/sdl2.cpp +++ b/alc/backends/sdl2.cpp @@ -33,7 +33,7 @@ #include "alexcpt.h" #include "almalloc.h" #include "alu.h" -#include "logging.h" +#include "core/logging.h" #include diff --git a/alc/backends/sndio.cpp b/alc/backends/sndio.cpp index c20ce496..4e9af3bc 100644 --- a/alc/backends/sndio.cpp +++ b/alc/backends/sndio.cpp @@ -32,10 +32,10 @@ #include "alcmain.h" #include "alexcpt.h" #include "alu.h" +#include "core/logging.h" +#include "ringbuffer.h" #include "threads.h" #include "vector.h" -#include "ringbuffer.h" -#include "logging.h" #include diff --git a/alc/backends/solaris.cpp b/alc/backends/solaris.cpp index 6849a820..5134c101 100644 --- a/alc/backends/solaris.cpp +++ b/alc/backends/solaris.cpp @@ -43,7 +43,7 @@ #include "alu.h" #include "alconfig.h" #include "compat.h" -#include "logging.h" +#include "core/logging.h" #include "threads.h" #include "vector.h" diff --git a/alc/backends/wasapi.cpp b/alc/backends/wasapi.cpp index ba0d52d5..154355f2 100644 --- a/alc/backends/wasapi.cpp +++ b/alc/backends/wasapi.cpp @@ -60,7 +60,7 @@ #include "alu.h" #include "compat.h" #include "converter.h" -#include "logging.h" +#include "core/logging.h" #include "ringbuffer.h" #include "strutils.h" #include "threads.h" diff --git a/alc/backends/wave.cpp b/alc/backends/wave.cpp index 473d0314..a9713718 100644 --- a/alc/backends/wave.cpp +++ b/alc/backends/wave.cpp @@ -43,8 +43,8 @@ #include "alnumeric.h" #include "alu.h" #include "compat.h" +#include "core/logging.h" #include "endiantest.h" -#include "logging.h" #include "strutils.h" #include "threads.h" #include "vector.h" diff --git a/alc/backends/winmm.cpp b/alc/backends/winmm.cpp index 274536b3..d4e7c786 100644 --- a/alc/backends/winmm.cpp +++ b/alc/backends/winmm.cpp @@ -42,7 +42,7 @@ #include "alexcpt.h" #include "alu.h" #include "compat.h" -#include "logging.h" +#include "core/logging.h" #include "ringbuffer.h" #include "strutils.h" #include "threads.h" diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index 677c59ba..667d5fb3 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -18,9 +18,9 @@ #include "core/ambidefs.h" #include "core/filters/splitter.h" #include "core/fmt_traits.h" +#include "core/logging.h" #include "effects/base.h" #include "effectslot.h" -#include "logging.h" #include "polyphase_resampler.h" diff --git a/alc/helpers.cpp b/alc/helpers.cpp index 1003ccfa..8c1c8562 100644 --- a/alc/helpers.cpp +++ b/alc/helpers.cpp @@ -35,7 +35,7 @@ #include "alspan.h" #include "alstring.h" #include "compat.h" -#include "logging.h" +#include "core/logging.h" #include "strutils.h" #include "vector.h" @@ -79,36 +79,6 @@ const PathNamePair &GetProcBinary() return ret; } - -void al_print(LogLevel level, FILE *logfile, const char *fmt, ...) -{ - al::vector dynmsg; - char stcmsg[256]; - char *str{stcmsg}; - - va_list args, args2; - va_start(args, fmt); - va_copy(args2, args); - int msglen{std::vsnprintf(str, sizeof(stcmsg), fmt, args)}; - if UNLIKELY(msglen >= 0 && static_cast(msglen) >= sizeof(stcmsg)) - { - dynmsg.resize(static_cast(msglen) + 1u); - str = dynmsg.data(); - msglen = std::vsnprintf(str, dynmsg.size(), fmt, args2); - } - va_end(args2); - va_end(args); - - std::wstring wstr{utf8_to_wstr(str)}; - if(gLogLevel >= level) - { - fputws(wstr.c_str(), logfile); - fflush(logfile); - } - OutputDebugStringW(wstr.c_str()); -} - - namespace { void DirectorySearch(const char *path, const char *ext, al::vector *const results) @@ -225,9 +195,6 @@ void SetRTPriority(void) #ifdef __HAIKU__ #include #endif -#ifdef __ANDROID__ -#include -#endif #ifdef HAVE_PROC_PIDPATH #include #endif @@ -323,50 +290,6 @@ const PathNamePair &GetProcBinary() return ret; } - -void al_print(LogLevel level, FILE *logfile, const char *fmt, ...) -{ - al::vector dynmsg; - char stcmsg[256]; - char *str{stcmsg}; - - va_list args, args2; - va_start(args, fmt); - va_copy(args2, args); - int msglen{std::vsnprintf(str, sizeof(stcmsg), fmt, args)}; - if UNLIKELY(msglen >= 0 && static_cast(msglen) >= sizeof(stcmsg)) - { - dynmsg.resize(static_cast(msglen) + 1u); - str = dynmsg.data(); - msglen = std::vsnprintf(str, dynmsg.size(), fmt, args2); - } - va_end(args2); - va_end(args); - - if(gLogLevel >= level) - { - fputs(str, logfile); - fflush(logfile); - } -#ifdef __ANDROID__ - auto android_severity = [](LogLevel l) noexcept - { - switch(l) - { - case LogLevel::Trace: return ANDROID_LOG_DEBUG; - case LogLevel::Warning: return ANDROID_LOG_WARN; - case LogLevel::Error: return ANDROID_LOG_ERROR; - /* Should not happen. */ - case LogLevel::Disable: - break; - } - return ANDROID_LOG_ERROR; - }; - __android_log_print(android_severity(level), "openal", "%s", str); -#endif -} - - namespace { void DirectorySearch(const char *path, const char *ext, al::vector *const results) diff --git a/alc/hrtf.cpp b/alc/hrtf.cpp index e01cdf97..b13b50cb 100644 --- a/alc/hrtf.cpp +++ b/alc/hrtf.cpp @@ -48,7 +48,7 @@ #include "aloptional.h" #include "alspan.h" #include "core/filters/splitter.h" -#include "logging.h" +#include "core/logging.h" #include "math_defs.h" #include "opthelpers.h" #include "polyphase_resampler.h" diff --git a/alc/logging.h b/alc/logging.h deleted file mode 100644 index 32a877e1..00000000 --- a/alc/logging.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef LOGGING_H -#define LOGGING_H - -#include - -#include "opthelpers.h" - - -enum class LogLevel { - Disable, - Error, - Warning, - Trace -}; -extern LogLevel gLogLevel; - -extern FILE *gLogFile; - - -#if !defined(_WIN32) && !defined(__ANDROID__) -#define TRACE(...) do { \ - if UNLIKELY(gLogLevel >= LogLevel::Trace) \ - fprintf(gLogFile, "[ALSOFT] (II) " __VA_ARGS__); \ -} while(0) - -#define WARN(...) do { \ - if UNLIKELY(gLogLevel >= LogLevel::Warning) \ - fprintf(gLogFile, "[ALSOFT] (WW) " __VA_ARGS__); \ -} while(0) - -#define ERR(...) do { \ - if UNLIKELY(gLogLevel >= LogLevel::Error) \ - fprintf(gLogFile, "[ALSOFT] (EE) " __VA_ARGS__); \ -} while(0) - -#else - -[[gnu::format(printf,3,4)]] void al_print(LogLevel level, FILE *logfile, const char *fmt, ...); - -#define TRACE(...) al_print(LogLevel::Trace, gLogFile, "[ALSOFT] (II) " __VA_ARGS__) - -#define WARN(...) al_print(LogLevel::Warning, gLogFile, "[ALSOFT] (WW) " __VA_ARGS__) - -#define ERR(...) al_print(LogLevel::Error, gLogFile, "[ALSOFT] (EE) " __VA_ARGS__) -#endif - -#endif /* LOGGING_H */ diff --git a/alc/panning.cpp b/alc/panning.cpp index ad297ef7..cb77199f 100644 --- a/alc/panning.cpp +++ b/alc/panning.cpp @@ -52,10 +52,10 @@ #include "core/ambidefs.h" #include "core/bs2b.h" #include "core/devformat.h" +#include "core/logging.h" #include "core/uhjfilter.h" #include "front_stablizer.h" #include "hrtf.h" -#include "logging.h" #include "math_defs.h" #include "opthelpers.h" diff --git a/alc/voice.cpp b/alc/voice.cpp index 15240544..2639bcf8 100644 --- a/alc/voice.cpp +++ b/alc/voice.cpp @@ -50,12 +50,12 @@ #include "core/filters/nfc.h" #include "core/filters/splitter.h" #include "core/fmt_traits.h" +#include "core/logging.h" #include "core/mixer/defs.h" #include "core/mixer/hrtfdefs.h" #include "cpu_caps.h" #include "hrtf.h" #include "inprogext.h" -#include "logging.h" #include "opthelpers.h" #include "ringbuffer.h" #include "threads.h" diff --git a/core/logging.cpp b/core/logging.cpp new file mode 100644 index 00000000..dd7f53c7 --- /dev/null +++ b/core/logging.cpp @@ -0,0 +1,95 @@ + +#include "config.h" + +#include "logging.h" + +#include +#include +#include + +#include "strutils.h" +#include "vector.h" + + +#ifdef _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include + +void al_print(LogLevel level, FILE *logfile, const char *fmt, ...) +{ + al::vector dynmsg; + char stcmsg[256]; + char *str{stcmsg}; + + std::va_list args, args2; + va_start(args, fmt); + va_copy(args2, args); + int msglen{std::vsnprintf(str, sizeof(stcmsg), fmt, args)}; + if UNLIKELY(msglen >= 0 && static_cast(msglen) >= sizeof(stcmsg)) + { + dynmsg.resize(static_cast(msglen) + 1u); + str = dynmsg.data(); + msglen = std::vsnprintf(str, dynmsg.size(), fmt, args2); + } + va_end(args2); + va_end(args); + + std::wstring wstr{utf8_to_wstr(str)}; + if(gLogLevel >= level) + { + fputws(wstr.c_str(), logfile); + fflush(logfile); + } + OutputDebugStringW(wstr.c_str()); +} + +#else + +#ifdef __ANDROID__ +#include +#endif + +void al_print(LogLevel level, FILE *logfile, const char *fmt, ...) +{ + al::vector dynmsg; + char stcmsg[256]; + char *str{stcmsg}; + + std::va_list args, args2; + va_start(args, fmt); + va_copy(args2, args); + int msglen{std::vsnprintf(str, sizeof(stcmsg), fmt, args)}; + if UNLIKELY(msglen >= 0 && static_cast(msglen) >= sizeof(stcmsg)) + { + dynmsg.resize(static_cast(msglen) + 1u); + str = dynmsg.data(); + msglen = std::vsnprintf(str, dynmsg.size(), fmt, args2); + } + va_end(args2); + va_end(args); + + if(gLogLevel >= level) + { + std::fputs(str, logfile); + std::fflush(logfile); + } +#ifdef __ANDROID__ + auto android_severity = [](LogLevel l) noexcept + { + switch(l) + { + case LogLevel::Trace: return ANDROID_LOG_DEBUG; + case LogLevel::Warning: return ANDROID_LOG_WARN; + case LogLevel::Error: return ANDROID_LOG_ERROR; + /* Should not happen. */ + case LogLevel::Disable: + break; + } + return ANDROID_LOG_ERROR; + }; + __android_log_print(android_severity(level), "openal", "%s", str); +#endif +} + +#endif diff --git a/core/logging.h b/core/logging.h new file mode 100644 index 00000000..b931c27e --- /dev/null +++ b/core/logging.h @@ -0,0 +1,47 @@ +#ifndef CORE_LOGGING_H +#define CORE_LOGGING_H + +#include + +#include "opthelpers.h" + + +enum class LogLevel { + Disable, + Error, + Warning, + Trace +}; +extern LogLevel gLogLevel; + +extern FILE *gLogFile; + + +#if !defined(_WIN32) && !defined(__ANDROID__) +#define TRACE(...) do { \ + if UNLIKELY(gLogLevel >= LogLevel::Trace) \ + fprintf(gLogFile, "[ALSOFT] (II) " __VA_ARGS__); \ +} while(0) + +#define WARN(...) do { \ + if UNLIKELY(gLogLevel >= LogLevel::Warning) \ + fprintf(gLogFile, "[ALSOFT] (WW) " __VA_ARGS__); \ +} while(0) + +#define ERR(...) do { \ + if UNLIKELY(gLogLevel >= LogLevel::Error) \ + fprintf(gLogFile, "[ALSOFT] (EE) " __VA_ARGS__); \ +} while(0) + +#else + +[[gnu::format(printf,3,4)]] void al_print(LogLevel level, FILE *logfile, const char *fmt, ...); + +#define TRACE(...) al_print(LogLevel::Trace, gLogFile, "[ALSOFT] (II) " __VA_ARGS__) + +#define WARN(...) al_print(LogLevel::Warning, gLogFile, "[ALSOFT] (WW) " __VA_ARGS__) + +#define ERR(...) al_print(LogLevel::Error, gLogFile, "[ALSOFT] (EE) " __VA_ARGS__) +#endif + +#endif /* CORE_LOGGING_H */ -- cgit v1.2.3 From eedc42890fa1358a6639051a39f7e73f8d4d3b07 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 21 Dec 2020 18:00:43 -0800 Subject: Move alexcpt to core --- CMakeLists.txt | 4 ++-- al/auxeffectslot.cpp | 2 +- al/buffer.cpp | 2 +- al/effect.cpp | 2 +- al/effects/effects.h | 2 +- al/error.cpp | 2 +- al/event.cpp | 2 +- al/extension.cpp | 2 +- al/filter.cpp | 2 +- al/listener.cpp | 2 +- al/source.cpp | 2 +- al/state.cpp | 2 +- alc/alc.cpp | 2 +- alc/backends/alsa.cpp | 1 - alc/backends/base.cpp | 1 - alc/backends/base.h | 2 +- alc/backends/coreaudio.cpp | 1 - alc/backends/dsound.cpp | 1 - alc/backends/jack.cpp | 1 - alc/backends/null.cpp | 1 - alc/backends/opensl.cpp | 1 - alc/backends/oss.cpp | 1 - alc/backends/portaudio.cpp | 1 - alc/backends/pulseaudio.cpp | 1 - alc/backends/sdl2.cpp | 1 - alc/backends/sndio.cpp | 1 - alc/backends/solaris.cpp | 1 - alc/backends/wasapi.cpp | 1 - alc/backends/wave.cpp | 1 - alc/backends/winmm.cpp | 1 - common/alexcpt.cpp | 31 ------------------------------- common/alexcpt.h | 31 ------------------------------- core/except.cpp | 31 +++++++++++++++++++++++++++++++ core/except.h | 31 +++++++++++++++++++++++++++++++ 34 files changed, 77 insertions(+), 93 deletions(-) delete mode 100644 common/alexcpt.cpp delete mode 100644 common/alexcpt.h create mode 100644 core/except.cpp create mode 100644 core/except.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index fa9e7d37..86489991 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -554,8 +554,6 @@ set(COMMON_OBJS common/albyte.h common/alcomplex.cpp common/alcomplex.h - common/alexcpt.cpp - common/alexcpt.h common/alfstream.cpp common/alfstream.h common/almalloc.cpp @@ -596,6 +594,8 @@ set(CORE_OBJS core/bufferline.h core/devformat.cpp core/devformat.h + core/except.cpp + core/except.h core/filters/biquad.h core/filters/biquad.cpp core/filters/nfc.cpp diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp index a69df77a..e3e8a125 100644 --- a/al/auxeffectslot.cpp +++ b/al/auxeffectslot.cpp @@ -36,12 +36,12 @@ #include "alcmain.h" #include "alcontext.h" -#include "alexcpt.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" #include "alu.h" #include "buffer.h" +#include "core/except.h" #include "core/logging.h" #include "effect.h" #include "fpu_ctrl.h" diff --git a/al/buffer.cpp b/al/buffer.cpp index 0ee6b808..cdbedf70 100644 --- a/al/buffer.cpp +++ b/al/buffer.cpp @@ -44,11 +44,11 @@ #include "albyte.h" #include "alcmain.h" #include "alcontext.h" -#include "alexcpt.h" #include "almalloc.h" #include "alnumeric.h" #include "aloptional.h" #include "atomic.h" +#include "core/except.h" #include "inprogext.h" #include "opthelpers.h" diff --git a/al/effect.cpp b/al/effect.cpp index 029ecaa8..7ddf9664 100644 --- a/al/effect.cpp +++ b/al/effect.cpp @@ -40,10 +40,10 @@ #include "alcmain.h" #include "alcontext.h" -#include "alexcpt.h" #include "almalloc.h" #include "alnumeric.h" #include "alstring.h" +#include "core/except.h" #include "core/logging.h" #include "effects/base.h" #include "opthelpers.h" diff --git a/al/effects/effects.h b/al/effects/effects.h index 47b089ba..d6c88c4f 100644 --- a/al/effects/effects.h +++ b/al/effects/effects.h @@ -3,7 +3,7 @@ #include "AL/al.h" -#include "alexcpt.h" +#include "core/except.h" union EffectProps; diff --git a/al/error.cpp b/al/error.cpp index f9c408d2..444b55aa 100644 --- a/al/error.cpp +++ b/al/error.cpp @@ -36,8 +36,8 @@ #include "AL/alc.h" #include "alcontext.h" -#include "alexcpt.h" #include "almalloc.h" +#include "core/except.h" #include "core/logging.h" #include "opthelpers.h" #include "vector.h" diff --git a/al/event.cpp b/al/event.cpp index 6834253a..7267625d 100644 --- a/al/event.cpp +++ b/al/event.cpp @@ -19,9 +19,9 @@ #include "albyte.h" #include "alcontext.h" -#include "alexcpt.h" #include "almalloc.h" #include "async_event.h" +#include "core/except.h" #include "core/logging.h" #include "effects/base.h" #include "inprogext.h" diff --git a/al/extension.cpp b/al/extension.cpp index 35c53136..7346a5c6 100644 --- a/al/extension.cpp +++ b/al/extension.cpp @@ -28,8 +28,8 @@ #include "AL/alc.h" #include "alcontext.h" -#include "alexcpt.h" #include "alstring.h" +#include "core/except.h" #include "opthelpers.h" diff --git a/al/filter.cpp b/al/filter.cpp index 2f49c52e..e549b1c9 100644 --- a/al/filter.cpp +++ b/al/filter.cpp @@ -38,9 +38,9 @@ #include "alcmain.h" #include "alcontext.h" -#include "alexcpt.h" #include "almalloc.h" #include "alnumeric.h" +#include "core/except.h" #include "opthelpers.h" #include "vector.h" diff --git a/al/listener.cpp b/al/listener.cpp index cef96fb0..4bbc145c 100644 --- a/al/listener.cpp +++ b/al/listener.cpp @@ -30,9 +30,9 @@ #include "AL/efx.h" #include "alcontext.h" -#include "alexcpt.h" #include "almalloc.h" #include "atomic.h" +#include "core/except.h" #include "opthelpers.h" diff --git a/al/source.cpp b/al/source.cpp index 5fae7b54..fa7665c4 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -47,7 +47,6 @@ #include "alcmain.h" #include "alcontext.h" -#include "alexcpt.h" #include "almalloc.h" #include "alnumeric.h" #include "aloptional.h" @@ -59,6 +58,7 @@ #include "bformatdec.h" #include "buffer.h" #include "core/ambidefs.h" +#include "core/except.h" #include "core/filters/nfc.h" #include "core/filters/splitter.h" #include "core/logging.h" diff --git a/al/state.cpp b/al/state.cpp index c8eebbe0..026f2808 100644 --- a/al/state.cpp +++ b/al/state.cpp @@ -33,12 +33,12 @@ #include "AL/alext.h" #include "alcontext.h" -#include "alexcpt.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" #include "alu.h" #include "atomic.h" +#include "core/except.h" #include "event.h" #include "inprogext.h" #include "opthelpers.h" diff --git a/alc/alc.cpp b/alc/alc.cpp index 8cda3733..1b06b57f 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -68,7 +68,6 @@ #include "albyte.h" #include "alconfig.h" #include "alcontext.h" -#include "alexcpt.h" #include "almalloc.h" #include "alnumeric.h" #include "aloptional.h" @@ -82,6 +81,7 @@ #include "core/ambidefs.h" #include "core/bs2b.h" #include "core/devformat.h" +#include "core/except.h" #include "core/mastering.h" #include "core/filters/nfc.h" #include "core/filters/splitter.h" diff --git a/alc/backends/alsa.cpp b/alc/backends/alsa.cpp index 4adf64b3..f8b22aaf 100644 --- a/alc/backends/alsa.cpp +++ b/alc/backends/alsa.cpp @@ -40,7 +40,6 @@ #include "albyte.h" #include "alcmain.h" #include "alconfig.h" -#include "alexcpt.h" #include "almalloc.h" #include "alnumeric.h" #include "aloptional.h" diff --git a/alc/backends/base.cpp b/alc/backends/base.cpp index 60d99bff..8642f40c 100644 --- a/alc/backends/base.cpp +++ b/alc/backends/base.cpp @@ -13,7 +13,6 @@ #endif #include "alcmain.h" -#include "alexcpt.h" #include "alnumeric.h" #include "aloptional.h" #include "atomic.h" diff --git a/alc/backends/base.h b/alc/backends/base.h index 963d01a4..df076276 100644 --- a/alc/backends/base.h +++ b/alc/backends/base.h @@ -8,7 +8,7 @@ #include "albyte.h" #include "alcmain.h" -#include "alexcpt.h" +#include "core/except.h" using uint = unsigned int; diff --git a/alc/backends/coreaudio.cpp b/alc/backends/coreaudio.cpp index a4c93819..8e38a777 100644 --- a/alc/backends/coreaudio.cpp +++ b/alc/backends/coreaudio.cpp @@ -30,7 +30,6 @@ #include #include "alcmain.h" -#include "alexcpt.h" #include "alu.h" #include "ringbuffer.h" #include "converter.h" diff --git a/alc/backends/dsound.cpp b/alc/backends/dsound.cpp index a8577dbc..a09def73 100644 --- a/alc/backends/dsound.cpp +++ b/alc/backends/dsound.cpp @@ -45,7 +45,6 @@ #include #include "alcmain.h" -#include "alexcpt.h" #include "alu.h" #include "compat.h" #include "core/logging.h" diff --git a/alc/backends/jack.cpp b/alc/backends/jack.cpp index c70e5b0d..7c9573ce 100644 --- a/alc/backends/jack.cpp +++ b/alc/backends/jack.cpp @@ -34,7 +34,6 @@ #include "alcmain.h" #include "alu.h" #include "alconfig.h" -#include "alexcpt.h" #include "core/logging.h" #include "dynload.h" #include "ringbuffer.h" diff --git a/alc/backends/null.cpp b/alc/backends/null.cpp index 87fb2689..15a36ddd 100644 --- a/alc/backends/null.cpp +++ b/alc/backends/null.cpp @@ -31,7 +31,6 @@ #include #include "alcmain.h" -#include "alexcpt.h" #include "almalloc.h" #include "alu.h" #include "threads.h" diff --git a/alc/backends/opensl.cpp b/alc/backends/opensl.cpp index b41a4d84..8d966fca 100644 --- a/alc/backends/opensl.cpp +++ b/alc/backends/opensl.cpp @@ -33,7 +33,6 @@ #include #include "alcmain.h" -#include "alexcpt.h" #include "alu.h" #include "compat.h" #include "core/logging.h" diff --git a/alc/backends/oss.cpp b/alc/backends/oss.cpp index 1178a3ac..b333432d 100644 --- a/alc/backends/oss.cpp +++ b/alc/backends/oss.cpp @@ -46,7 +46,6 @@ #include "alcmain.h" #include "alconfig.h" #include "albyte.h" -#include "alexcpt.h" #include "almalloc.h" #include "alnumeric.h" #include "aloptional.h" diff --git a/alc/backends/portaudio.cpp b/alc/backends/portaudio.cpp index 4cb9fe0a..d2e6a5a4 100644 --- a/alc/backends/portaudio.cpp +++ b/alc/backends/portaudio.cpp @@ -27,7 +27,6 @@ #include #include "alcmain.h" -#include "alexcpt.h" #include "alu.h" #include "alconfig.h" #include "core/logging.h" diff --git a/alc/backends/pulseaudio.cpp b/alc/backends/pulseaudio.cpp index 9e5424de..8c65460f 100644 --- a/alc/backends/pulseaudio.cpp +++ b/alc/backends/pulseaudio.cpp @@ -38,7 +38,6 @@ #include "alcmain.h" #include "alu.h" #include "alconfig.h" -#include "alexcpt.h" #include "compat.h" #include "core/logging.h" #include "dynload.h" diff --git a/alc/backends/sdl2.cpp b/alc/backends/sdl2.cpp index 300f4934..58045c85 100644 --- a/alc/backends/sdl2.cpp +++ b/alc/backends/sdl2.cpp @@ -30,7 +30,6 @@ #include "AL/al.h" #include "alcmain.h" -#include "alexcpt.h" #include "almalloc.h" #include "alu.h" #include "core/logging.h" diff --git a/alc/backends/sndio.cpp b/alc/backends/sndio.cpp index 258eda19..41bdb73b 100644 --- a/alc/backends/sndio.cpp +++ b/alc/backends/sndio.cpp @@ -30,7 +30,6 @@ #include #include "alcmain.h" -#include "alexcpt.h" #include "alu.h" #include "core/logging.h" #include "ringbuffer.h" diff --git a/alc/backends/solaris.cpp b/alc/backends/solaris.cpp index 6a2c2da0..f2763357 100644 --- a/alc/backends/solaris.cpp +++ b/alc/backends/solaris.cpp @@ -40,7 +40,6 @@ #include "alcmain.h" #include "albyte.h" -#include "alexcpt.h" #include "alu.h" #include "alconfig.h" #include "compat.h" diff --git a/alc/backends/wasapi.cpp b/alc/backends/wasapi.cpp index 45359bf6..a0eb814f 100644 --- a/alc/backends/wasapi.cpp +++ b/alc/backends/wasapi.cpp @@ -56,7 +56,6 @@ #include #include "alcmain.h" -#include "alexcpt.h" #include "alu.h" #include "compat.h" #include "converter.h" diff --git a/alc/backends/wave.cpp b/alc/backends/wave.cpp index 7e076e47..b870ea67 100644 --- a/alc/backends/wave.cpp +++ b/alc/backends/wave.cpp @@ -38,7 +38,6 @@ #include "albyte.h" #include "alcmain.h" #include "alconfig.h" -#include "alexcpt.h" #include "almalloc.h" #include "alnumeric.h" #include "alu.h" diff --git a/alc/backends/winmm.cpp b/alc/backends/winmm.cpp index 2474a316..9b88f12e 100644 --- a/alc/backends/winmm.cpp +++ b/alc/backends/winmm.cpp @@ -39,7 +39,6 @@ #include #include "alcmain.h" -#include "alexcpt.h" #include "alu.h" #include "compat.h" #include "core/logging.h" diff --git a/common/alexcpt.cpp b/common/alexcpt.cpp deleted file mode 100644 index 6fbc63fd..00000000 --- a/common/alexcpt.cpp +++ /dev/null @@ -1,31 +0,0 @@ - -#include "config.h" - -#include "alexcpt.h" - -#include -#include - -#include "opthelpers.h" - - -namespace al { - -/* Defined here to avoid inlining it. */ -base_exception::~base_exception() { } - -void base_exception::setMessage(const char* msg, std::va_list args) -{ - std::va_list args2; - va_copy(args2, args); - int msglen{std::vsnprintf(nullptr, 0, msg, args)}; - if LIKELY(msglen > 0) - { - mMessage.resize(static_cast(msglen)+1); - std::vsnprintf(&mMessage[0], mMessage.length(), msg, args2); - mMessage.pop_back(); - } - va_end(args2); -} - -} // namespace al diff --git a/common/alexcpt.h b/common/alexcpt.h deleted file mode 100644 index e31c50e7..00000000 --- a/common/alexcpt.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef ALEXCPT_H -#define ALEXCPT_H - -#include -#include -#include -#include - - -namespace al { - -class base_exception : public std::exception { - std::string mMessage; - -protected: - base_exception() = default; - virtual ~base_exception(); - - void setMessage(const char *msg, std::va_list args); - -public: - const char *what() const noexcept override { return mMessage.c_str(); } -}; - -} // namespace al - -#define START_API_FUNC try - -#define END_API_FUNC catch(...) { std::terminate(); } - -#endif /* ALEXCPT_H */ diff --git a/core/except.cpp b/core/except.cpp new file mode 100644 index 00000000..07bb410a --- /dev/null +++ b/core/except.cpp @@ -0,0 +1,31 @@ + +#include "config.h" + +#include "except.h" + +#include +#include + +#include "opthelpers.h" + + +namespace al { + +/* Defined here to avoid inlining it. */ +base_exception::~base_exception() { } + +void base_exception::setMessage(const char* msg, std::va_list args) +{ + std::va_list args2; + va_copy(args2, args); + int msglen{std::vsnprintf(nullptr, 0, msg, args)}; + if LIKELY(msglen > 0) + { + mMessage.resize(static_cast(msglen)+1); + std::vsnprintf(&mMessage[0], mMessage.length(), msg, args2); + mMessage.pop_back(); + } + va_end(args2); +} + +} // namespace al diff --git a/core/except.h b/core/except.h new file mode 100644 index 00000000..0e28e9df --- /dev/null +++ b/core/except.h @@ -0,0 +1,31 @@ +#ifndef CORE_EXCEPT_H +#define CORE_EXCEPT_H + +#include +#include +#include +#include + + +namespace al { + +class base_exception : public std::exception { + std::string mMessage; + +protected: + base_exception() = default; + virtual ~base_exception(); + + void setMessage(const char *msg, std::va_list args); + +public: + const char *what() const noexcept override { return mMessage.c_str(); } +}; + +} // namespace al + +#define START_API_FUNC try + +#define END_API_FUNC catch(...) { std::terminate(); } + +#endif /* CORE_EXCEPT_H */ -- cgit v1.2.3 From fe9ec157fd0ea647452c4894209c4016fffac682 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 24 Dec 2020 07:34:29 -0800 Subject: Use an import target for linking OpenSL --- CMakeLists.txt | 3 +-- cmake/FindOpenSL.cmake | 23 +++++++++++------------ 2 files changed, 12 insertions(+), 14 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 86489991..2cbe7850 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1029,8 +1029,7 @@ if(OPENSL_FOUND) set(HAVE_OPENSL 1) set(ALC_OBJS ${ALC_OBJS} alc/backends/opensl.cpp alc/backends/opensl.h) set(BACKENDS "${BACKENDS} OpenSL,") - set(EXTRA_LIBS ${OPENSL_LIBRARIES} ${EXTRA_LIBS}) - set(INC_PATHS ${INC_PATHS} ${OPENSL_INCLUDE_DIRS}) + set(EXTRA_LIBS "OpenSL::OpenSLES" ${EXTRA_LIBS}) endif() endif() if(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL) diff --git a/cmake/FindOpenSL.cmake b/cmake/FindOpenSL.cmake index e4b260bc..00428749 100644 --- a/cmake/FindOpenSL.cmake +++ b/cmake/FindOpenSL.cmake @@ -1,10 +1,9 @@ # - Find OpenSL # Find the OpenSL libraries # -# This module defines the following variables: -# OPENSL_FOUND - True if OPENSL_INCLUDE_DIR & OPENSL_LIBRARY are set -# OPENSL_INCLUDE_DIRS - where to find SLES/OpenSLES.h, etc. -# OPENSL_LIBRARIES - the OpenSL library +# This module defines the following variables and targets: +# OPENSL_FOUND - True if OPENSL was found +# OpenSL::OpenSLES - The OpenSLES target # #============================================================================= @@ -40,15 +39,12 @@ #============================================================================= find_path(OPENSL_INCLUDE_DIR NAMES SLES/OpenSLES.h - DOC "The OpenSL include directory" -) + DOC "The OpenSL include directory") find_path(OPENSL_ANDROID_INCLUDE_DIR NAMES SLES/OpenSLES_Android.h - DOC "The OpenSL Android include directory" -) + DOC "The OpenSL Android include directory") find_library(OPENSL_LIBRARY NAMES OpenSLES - DOC "The OpenSL library" -) + DOC "The OpenSL library") # handle the QUIETLY and REQUIRED arguments and set OPENSL_FOUND to TRUE if # all listed variables are TRUE @@ -57,8 +53,11 @@ find_package_handle_standard_args(OpenSL REQUIRED_VARS OPENSL_LIBRARY OPENSL_INC OPENSL_ANDROID_INCLUDE_DIR) if(OPENSL_FOUND) - set(OPENSL_LIBRARIES ${OPENSL_LIBRARY}) - set(OPENSL_INCLUDE_DIRS ${OPENSL_INCLUDE_DIR} ${OPENSL_ANDROID_INCLUDE_DIR}) + add_library(OpenSL::OpenSLES UNKNOWN IMPORTED) + set_target_properties(OpenSL::OpenSLES PROPERTIES + IMPORTED_LOCATION ${OPENSL_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${OPENSL_INCLUDE_DIR} + INTERFACE_INCLUDE_DIRECTORIES ${OPENSL_ANDROID_INCLUDE_DIR}) endif() mark_as_advanced(OPENSL_INCLUDE_DIR OPENSL_ANDROID_INCLUDE_DIR OPENSL_LIBRARY) -- cgit v1.2.3 From 20820fd01beb265722d8521ad725c3c479800273 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 25 Dec 2020 06:30:47 -0800 Subject: Move the ambdec loader to core --- CMakeLists.txt | 4 +- alc/ambdec.cpp | 434 ----------------------------------------------------- alc/ambdec.h | 48 ------ alc/bformatdec.cpp | 2 +- alc/panning.cpp | 2 +- core/ambdec.cpp | 434 +++++++++++++++++++++++++++++++++++++++++++++++++++++ core/ambdec.h | 48 ++++++ 7 files changed, 486 insertions(+), 486 deletions(-) delete mode 100644 alc/ambdec.cpp delete mode 100644 alc/ambdec.h create mode 100644 core/ambdec.cpp create mode 100644 core/ambdec.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cbe7850..05d04201 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -584,6 +584,8 @@ set(COMMON_OBJS # Core library routines set(CORE_OBJS + core/ambdec.cpp + core/ambdec.h core/ambidefs.cpp core/ambidefs.h core/bs2b.cpp @@ -659,8 +661,6 @@ set(ALC_OBJS alc/alconfig.cpp alc/alconfig.h alc/alcontext.h - alc/ambdec.cpp - alc/ambdec.h alc/async_event.h alc/bformatdec.cpp alc/bformatdec.h diff --git a/alc/ambdec.cpp b/alc/ambdec.cpp deleted file mode 100644 index a50d6d95..00000000 --- a/alc/ambdec.cpp +++ /dev/null @@ -1,434 +0,0 @@ - -#include "config.h" - -#include "ambdec.h" - -#include -#include -#include -#include -#include -#include - -#include "alfstream.h" -#include "core/logging.h" - - -namespace { - -template -constexpr inline std::size_t size(const T(&)[N]) noexcept -{ return N; } - -int readline(std::istream &f, std::string &output) -{ - while(f.good() && f.peek() == '\n') - f.ignore(); - - return std::getline(f, output) && !output.empty(); -} - -bool read_clipped_line(std::istream &f, std::string &buffer) -{ - while(readline(f, buffer)) - { - std::size_t pos{0}; - while(pos < buffer.length() && std::isspace(buffer[pos])) - pos++; - buffer.erase(0, pos); - - std::size_t cmtpos{buffer.find_first_of('#')}; - if(cmtpos < buffer.length()) - buffer.resize(cmtpos); - while(!buffer.empty() && std::isspace(buffer.back())) - buffer.pop_back(); - - if(!buffer.empty()) - return true; - } - return false; -} - - -std::string read_word(std::istream &f) -{ - std::string ret; - f >> ret; - return ret; -} - -bool is_at_end(const std::string &buffer, std::size_t endpos) -{ - while(endpos < buffer.length() && std::isspace(buffer[endpos])) - ++endpos; - return !(endpos < buffer.length()); -} - - -bool load_ambdec_speakers(al::vector &spkrs, const std::size_t num_speakers, std::istream &f, std::string &buffer) -{ - while(spkrs.size() < num_speakers) - { - std::istringstream istr{buffer}; - - std::string cmd{read_word(istr)}; - if(cmd.empty()) - { - if(!read_clipped_line(f, buffer)) - { - ERR("Unexpected end of file\n"); - return false; - } - continue; - } - - if(cmd == "add_spkr") - { - spkrs.emplace_back(); - AmbDecConf::SpeakerConf &spkr = spkrs.back(); - const size_t spkr_num{spkrs.size()}; - - istr >> spkr.Name; - if(istr.fail()) WARN("Name not specified for speaker %zu\n", spkr_num); - istr >> spkr.Distance; - if(istr.fail()) WARN("Distance not specified for speaker %zu\n", spkr_num); - istr >> spkr.Azimuth; - if(istr.fail()) WARN("Azimuth not specified for speaker %zu\n", spkr_num); - istr >> spkr.Elevation; - if(istr.fail()) WARN("Elevation not specified for speaker %zu\n", spkr_num); - istr >> spkr.Connection; - if(istr.fail()) TRACE("Connection not specified for speaker %zu\n", spkr_num); - } - else - { - ERR("Unexpected speakers command: %s\n", cmd.c_str()); - return false; - } - - istr.clear(); - const auto endpos = static_cast(istr.tellg()); - if(!is_at_end(buffer, endpos)) - { - ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); - return false; - } - buffer.clear(); - } - - return true; -} - -bool load_ambdec_matrix(float (&gains)[MaxAmbiOrder+1], al::vector &matrix, const std::size_t maxrow, std::istream &f, std::string &buffer) -{ - bool gotgains{false}; - std::size_t cur{0u}; - while(cur < maxrow) - { - std::istringstream istr{buffer}; - - std::string cmd{read_word(istr)}; - if(cmd.empty()) - { - if(!read_clipped_line(f, buffer)) - { - ERR("Unexpected end of file\n"); - return false; - } - continue; - } - - if(cmd == "order_gain") - { - std::size_t curgain{0u}; - float value; - while(istr.good()) - { - istr >> value; - if(istr.fail()) break; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk on gain %zu: %s\n", curgain+1, - buffer.c_str()+static_cast(istr.tellg())); - return false; - } - if(curgain < size(gains)) - gains[curgain++] = value; - } - std::fill(std::begin(gains)+curgain, std::end(gains), 0.0f); - gotgains = true; - } - else if(cmd == "add_row") - { - matrix.emplace_back(); - AmbDecConf::CoeffArray &mtxrow = matrix.back(); - std::size_t curidx{0u}; - float value{}; - while(istr.good()) - { - istr >> value; - if(istr.fail()) break; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk on matrix element %zux%zu: %s\n", curidx, - matrix.size(), buffer.c_str()+static_cast(istr.tellg())); - matrix.pop_back(); - return false; - } - if(curidx < mtxrow.size()) - mtxrow[curidx++] = value; - } - std::fill(mtxrow.begin()+curidx, mtxrow.end(), 0.0f); - cur++; - } - else - { - ERR("Unexpected matrix command: %s\n", cmd.c_str()); - return false; - } - - istr.clear(); - const auto endpos = static_cast(istr.tellg()); - if(!is_at_end(buffer, endpos)) - { - ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); - return false; - } - buffer.clear(); - } - - if(!gotgains) - { - ERR("Matrix order_gain not specified\n"); - return false; - } - - return true; -} - -} // namespace - -int AmbDecConf::load(const char *fname) noexcept -{ - al::ifstream f{fname}; - if(!f.is_open()) - { - ERR("Failed to open: %s\n", fname); - return 0; - } - - std::size_t num_speakers{0u}; - std::string buffer; - while(read_clipped_line(f, buffer)) - { - std::istringstream istr{buffer}; - - std::string command{read_word(istr)}; - if(command.empty()) - { - ERR("Malformed line: %s\n", buffer.c_str()); - return 0; - } - - if(command == "/description") - istr >> Description; - else if(command == "/version") - { - istr >> Version; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk after version: %s\n", - buffer.c_str()+static_cast(istr.tellg())); - return 0; - } - if(Version != 3) - { - ERR("Unsupported version: %u\n", Version); - return 0; - } - } - else if(command == "/dec/chan_mask") - { - istr >> std::hex >> ChanMask >> std::dec; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk after mask: %s\n", - buffer.c_str()+static_cast(istr.tellg())); - return 0; - } - } - else if(command == "/dec/freq_bands") - { - istr >> FreqBands; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk after freq_bands: %s\n", - buffer.c_str()+static_cast(istr.tellg())); - return 0; - } - if(FreqBands != 1 && FreqBands != 2) - { - ERR("Invalid freq_bands value: %u\n", FreqBands); - return 0; - } - } - else if(command == "/dec/speakers") - { - istr >> num_speakers; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk after speakers: %s\n", - buffer.c_str()+static_cast(istr.tellg())); - return 0; - } - Speakers.reserve(num_speakers); - LFMatrix.reserve(num_speakers); - HFMatrix.reserve(num_speakers); - } - else if(command == "/dec/coeff_scale") - { - std::string scale = read_word(istr); - if(scale == "n3d") CoeffScale = AmbDecScale::N3D; - else if(scale == "sn3d") CoeffScale = AmbDecScale::SN3D; - else if(scale == "fuma") CoeffScale = AmbDecScale::FuMa; - else - { - ERR("Unsupported coeff scale: %s\n", scale.c_str()); - return 0; - } - } - else if(command == "/opt/xover_freq") - { - istr >> XOverFreq; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk after xover_freq: %s\n", - buffer.c_str()+static_cast(istr.tellg())); - return 0; - } - } - else if(command == "/opt/xover_ratio") - { - istr >> XOverRatio; - if(!istr.eof() && !std::isspace(istr.peek())) - { - ERR("Extra junk after xover_ratio: %s\n", - buffer.c_str()+static_cast(istr.tellg())); - return 0; - } - } - else if(command == "/opt/input_scale" || command == "/opt/nfeff_comp" || - command == "/opt/delay_comp" || command == "/opt/level_comp") - { - /* Unused */ - read_word(istr); - } - else if(command == "/speakers/{") - { - const auto endpos = static_cast(istr.tellg()); - if(!is_at_end(buffer, endpos)) - { - ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); - return 0; - } - buffer.clear(); - - if(!load_ambdec_speakers(Speakers, num_speakers, f, buffer)) - return 0; - - if(!read_clipped_line(f, buffer)) - { - ERR("Unexpected end of file\n"); - return 0; - } - std::istringstream istr2{buffer}; - std::string endmark{read_word(istr2)}; - if(endmark != "/}") - { - ERR("Expected /} after speaker definitions, got %s\n", endmark.c_str()); - return 0; - } - istr.swap(istr2); - } - else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{") - { - const auto endpos = static_cast(istr.tellg()); - if(!is_at_end(buffer, endpos)) - { - ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); - return 0; - } - buffer.clear(); - - if(FreqBands == 1) - { - if(command != "/matrix/{") - { - ERR("Unexpected \"%s\" type for a single-band decoder\n", command.c_str()); - return 0; - } - if(!load_ambdec_matrix(HFOrderGain, HFMatrix, num_speakers, f, buffer)) - return 0; - } - else - { - if(command == "/lfmatrix/{") - { - if(!load_ambdec_matrix(LFOrderGain, LFMatrix, num_speakers, f, buffer)) - return 0; - } - else if(command == "/hfmatrix/{") - { - if(!load_ambdec_matrix(HFOrderGain, HFMatrix, num_speakers, f, buffer)) - return 0; - } - else - { - ERR("Unexpected \"%s\" type for a dual-band decoder\n", command.c_str()); - return 0; - } - } - - if(!read_clipped_line(f, buffer)) - { - ERR("Unexpected end of file\n"); - return 0; - } - std::istringstream istr2{buffer}; - std::string endmark{read_word(istr2)}; - if(endmark != "/}") - { - ERR("Expected /} after matrix definitions, got %s\n", endmark.c_str()); - return 0; - } - istr.swap(istr2); - } - else if(command == "/end") - { - const auto endpos = static_cast(istr.tellg()); - if(!is_at_end(buffer, endpos)) - { - ERR("Unexpected junk on end: %s\n", buffer.c_str()+endpos); - return 0; - } - - return 1; - } - else - { - ERR("Unexpected command: %s\n", command.c_str()); - return 0; - } - - istr.clear(); - const auto endpos = static_cast(istr.tellg()); - if(!is_at_end(buffer, endpos)) - { - ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); - return 0; - } - buffer.clear(); - } - ERR("Unexpected end of file\n"); - - return 0; -} diff --git a/alc/ambdec.h b/alc/ambdec.h deleted file mode 100644 index c96ded5b..00000000 --- a/alc/ambdec.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef AMBDEC_H -#define AMBDEC_H - -#include -#include - -#include "core/ambidefs.h" -#include "vector.h" - -/* Helpers to read .ambdec configuration files. */ - -enum class AmbDecScale { - N3D, - SN3D, - FuMa, -}; -struct AmbDecConf { - std::string Description; - int Version{0}; /* Must be 3 */ - - unsigned int ChanMask{0u}; - unsigned int FreqBands{0u}; /* Must be 1 or 2 */ - AmbDecScale CoeffScale{}; - - float XOverFreq{0.0f}; - float XOverRatio{0.0f}; - - struct SpeakerConf { - std::string Name; - float Distance{0.0f}; - float Azimuth{0.0f}; - float Elevation{0.0f}; - std::string Connection; - }; - al::vector Speakers; - - using CoeffArray = std::array; - /* Unused when FreqBands == 1 */ - float LFOrderGain[MaxAmbiOrder+1]{}; - al::vector LFMatrix; - - float HFOrderGain[MaxAmbiOrder+1]{}; - al::vector HFMatrix; - - int load(const char *fname) noexcept; -}; - -#endif /* AMBDEC_H */ diff --git a/alc/bformatdec.cpp b/alc/bformatdec.cpp index c47a042f..b1bd8981 100644 --- a/alc/bformatdec.cpp +++ b/alc/bformatdec.cpp @@ -12,7 +12,7 @@ #include "almalloc.h" #include "alu.h" -#include "ambdec.h" +#include "core/ambdec.h" #include "core/filters/splitter.h" #include "front_stablizer.h" #include "math_defs.h" diff --git a/alc/panning.cpp b/alc/panning.cpp index cb77199f..456f6bef 100644 --- a/alc/panning.cpp +++ b/alc/panning.cpp @@ -47,8 +47,8 @@ #include "alspan.h" #include "alstring.h" #include "alu.h" -#include "ambdec.h" #include "bformatdec.h" +#include "core/ambdec.h" #include "core/ambidefs.h" #include "core/bs2b.h" #include "core/devformat.h" diff --git a/core/ambdec.cpp b/core/ambdec.cpp new file mode 100644 index 00000000..a50d6d95 --- /dev/null +++ b/core/ambdec.cpp @@ -0,0 +1,434 @@ + +#include "config.h" + +#include "ambdec.h" + +#include +#include +#include +#include +#include +#include + +#include "alfstream.h" +#include "core/logging.h" + + +namespace { + +template +constexpr inline std::size_t size(const T(&)[N]) noexcept +{ return N; } + +int readline(std::istream &f, std::string &output) +{ + while(f.good() && f.peek() == '\n') + f.ignore(); + + return std::getline(f, output) && !output.empty(); +} + +bool read_clipped_line(std::istream &f, std::string &buffer) +{ + while(readline(f, buffer)) + { + std::size_t pos{0}; + while(pos < buffer.length() && std::isspace(buffer[pos])) + pos++; + buffer.erase(0, pos); + + std::size_t cmtpos{buffer.find_first_of('#')}; + if(cmtpos < buffer.length()) + buffer.resize(cmtpos); + while(!buffer.empty() && std::isspace(buffer.back())) + buffer.pop_back(); + + if(!buffer.empty()) + return true; + } + return false; +} + + +std::string read_word(std::istream &f) +{ + std::string ret; + f >> ret; + return ret; +} + +bool is_at_end(const std::string &buffer, std::size_t endpos) +{ + while(endpos < buffer.length() && std::isspace(buffer[endpos])) + ++endpos; + return !(endpos < buffer.length()); +} + + +bool load_ambdec_speakers(al::vector &spkrs, const std::size_t num_speakers, std::istream &f, std::string &buffer) +{ + while(spkrs.size() < num_speakers) + { + std::istringstream istr{buffer}; + + std::string cmd{read_word(istr)}; + if(cmd.empty()) + { + if(!read_clipped_line(f, buffer)) + { + ERR("Unexpected end of file\n"); + return false; + } + continue; + } + + if(cmd == "add_spkr") + { + spkrs.emplace_back(); + AmbDecConf::SpeakerConf &spkr = spkrs.back(); + const size_t spkr_num{spkrs.size()}; + + istr >> spkr.Name; + if(istr.fail()) WARN("Name not specified for speaker %zu\n", spkr_num); + istr >> spkr.Distance; + if(istr.fail()) WARN("Distance not specified for speaker %zu\n", spkr_num); + istr >> spkr.Azimuth; + if(istr.fail()) WARN("Azimuth not specified for speaker %zu\n", spkr_num); + istr >> spkr.Elevation; + if(istr.fail()) WARN("Elevation not specified for speaker %zu\n", spkr_num); + istr >> spkr.Connection; + if(istr.fail()) TRACE("Connection not specified for speaker %zu\n", spkr_num); + } + else + { + ERR("Unexpected speakers command: %s\n", cmd.c_str()); + return false; + } + + istr.clear(); + const auto endpos = static_cast(istr.tellg()); + if(!is_at_end(buffer, endpos)) + { + ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); + return false; + } + buffer.clear(); + } + + return true; +} + +bool load_ambdec_matrix(float (&gains)[MaxAmbiOrder+1], al::vector &matrix, const std::size_t maxrow, std::istream &f, std::string &buffer) +{ + bool gotgains{false}; + std::size_t cur{0u}; + while(cur < maxrow) + { + std::istringstream istr{buffer}; + + std::string cmd{read_word(istr)}; + if(cmd.empty()) + { + if(!read_clipped_line(f, buffer)) + { + ERR("Unexpected end of file\n"); + return false; + } + continue; + } + + if(cmd == "order_gain") + { + std::size_t curgain{0u}; + float value; + while(istr.good()) + { + istr >> value; + if(istr.fail()) break; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk on gain %zu: %s\n", curgain+1, + buffer.c_str()+static_cast(istr.tellg())); + return false; + } + if(curgain < size(gains)) + gains[curgain++] = value; + } + std::fill(std::begin(gains)+curgain, std::end(gains), 0.0f); + gotgains = true; + } + else if(cmd == "add_row") + { + matrix.emplace_back(); + AmbDecConf::CoeffArray &mtxrow = matrix.back(); + std::size_t curidx{0u}; + float value{}; + while(istr.good()) + { + istr >> value; + if(istr.fail()) break; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk on matrix element %zux%zu: %s\n", curidx, + matrix.size(), buffer.c_str()+static_cast(istr.tellg())); + matrix.pop_back(); + return false; + } + if(curidx < mtxrow.size()) + mtxrow[curidx++] = value; + } + std::fill(mtxrow.begin()+curidx, mtxrow.end(), 0.0f); + cur++; + } + else + { + ERR("Unexpected matrix command: %s\n", cmd.c_str()); + return false; + } + + istr.clear(); + const auto endpos = static_cast(istr.tellg()); + if(!is_at_end(buffer, endpos)) + { + ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); + return false; + } + buffer.clear(); + } + + if(!gotgains) + { + ERR("Matrix order_gain not specified\n"); + return false; + } + + return true; +} + +} // namespace + +int AmbDecConf::load(const char *fname) noexcept +{ + al::ifstream f{fname}; + if(!f.is_open()) + { + ERR("Failed to open: %s\n", fname); + return 0; + } + + std::size_t num_speakers{0u}; + std::string buffer; + while(read_clipped_line(f, buffer)) + { + std::istringstream istr{buffer}; + + std::string command{read_word(istr)}; + if(command.empty()) + { + ERR("Malformed line: %s\n", buffer.c_str()); + return 0; + } + + if(command == "/description") + istr >> Description; + else if(command == "/version") + { + istr >> Version; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk after version: %s\n", + buffer.c_str()+static_cast(istr.tellg())); + return 0; + } + if(Version != 3) + { + ERR("Unsupported version: %u\n", Version); + return 0; + } + } + else if(command == "/dec/chan_mask") + { + istr >> std::hex >> ChanMask >> std::dec; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk after mask: %s\n", + buffer.c_str()+static_cast(istr.tellg())); + return 0; + } + } + else if(command == "/dec/freq_bands") + { + istr >> FreqBands; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk after freq_bands: %s\n", + buffer.c_str()+static_cast(istr.tellg())); + return 0; + } + if(FreqBands != 1 && FreqBands != 2) + { + ERR("Invalid freq_bands value: %u\n", FreqBands); + return 0; + } + } + else if(command == "/dec/speakers") + { + istr >> num_speakers; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk after speakers: %s\n", + buffer.c_str()+static_cast(istr.tellg())); + return 0; + } + Speakers.reserve(num_speakers); + LFMatrix.reserve(num_speakers); + HFMatrix.reserve(num_speakers); + } + else if(command == "/dec/coeff_scale") + { + std::string scale = read_word(istr); + if(scale == "n3d") CoeffScale = AmbDecScale::N3D; + else if(scale == "sn3d") CoeffScale = AmbDecScale::SN3D; + else if(scale == "fuma") CoeffScale = AmbDecScale::FuMa; + else + { + ERR("Unsupported coeff scale: %s\n", scale.c_str()); + return 0; + } + } + else if(command == "/opt/xover_freq") + { + istr >> XOverFreq; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk after xover_freq: %s\n", + buffer.c_str()+static_cast(istr.tellg())); + return 0; + } + } + else if(command == "/opt/xover_ratio") + { + istr >> XOverRatio; + if(!istr.eof() && !std::isspace(istr.peek())) + { + ERR("Extra junk after xover_ratio: %s\n", + buffer.c_str()+static_cast(istr.tellg())); + return 0; + } + } + else if(command == "/opt/input_scale" || command == "/opt/nfeff_comp" || + command == "/opt/delay_comp" || command == "/opt/level_comp") + { + /* Unused */ + read_word(istr); + } + else if(command == "/speakers/{") + { + const auto endpos = static_cast(istr.tellg()); + if(!is_at_end(buffer, endpos)) + { + ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); + return 0; + } + buffer.clear(); + + if(!load_ambdec_speakers(Speakers, num_speakers, f, buffer)) + return 0; + + if(!read_clipped_line(f, buffer)) + { + ERR("Unexpected end of file\n"); + return 0; + } + std::istringstream istr2{buffer}; + std::string endmark{read_word(istr2)}; + if(endmark != "/}") + { + ERR("Expected /} after speaker definitions, got %s\n", endmark.c_str()); + return 0; + } + istr.swap(istr2); + } + else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{") + { + const auto endpos = static_cast(istr.tellg()); + if(!is_at_end(buffer, endpos)) + { + ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); + return 0; + } + buffer.clear(); + + if(FreqBands == 1) + { + if(command != "/matrix/{") + { + ERR("Unexpected \"%s\" type for a single-band decoder\n", command.c_str()); + return 0; + } + if(!load_ambdec_matrix(HFOrderGain, HFMatrix, num_speakers, f, buffer)) + return 0; + } + else + { + if(command == "/lfmatrix/{") + { + if(!load_ambdec_matrix(LFOrderGain, LFMatrix, num_speakers, f, buffer)) + return 0; + } + else if(command == "/hfmatrix/{") + { + if(!load_ambdec_matrix(HFOrderGain, HFMatrix, num_speakers, f, buffer)) + return 0; + } + else + { + ERR("Unexpected \"%s\" type for a dual-band decoder\n", command.c_str()); + return 0; + } + } + + if(!read_clipped_line(f, buffer)) + { + ERR("Unexpected end of file\n"); + return 0; + } + std::istringstream istr2{buffer}; + std::string endmark{read_word(istr2)}; + if(endmark != "/}") + { + ERR("Expected /} after matrix definitions, got %s\n", endmark.c_str()); + return 0; + } + istr.swap(istr2); + } + else if(command == "/end") + { + const auto endpos = static_cast(istr.tellg()); + if(!is_at_end(buffer, endpos)) + { + ERR("Unexpected junk on end: %s\n", buffer.c_str()+endpos); + return 0; + } + + return 1; + } + else + { + ERR("Unexpected command: %s\n", command.c_str()); + return 0; + } + + istr.clear(); + const auto endpos = static_cast(istr.tellg()); + if(!is_at_end(buffer, endpos)) + { + ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos); + return 0; + } + buffer.clear(); + } + ERR("Unexpected end of file\n"); + + return 0; +} diff --git a/core/ambdec.h b/core/ambdec.h new file mode 100644 index 00000000..9f0db6b5 --- /dev/null +++ b/core/ambdec.h @@ -0,0 +1,48 @@ +#ifndef CORE_AMBDEC_H +#define CORE_AMBDEC_H + +#include +#include + +#include "core/ambidefs.h" +#include "vector.h" + +/* Helpers to read .ambdec configuration files. */ + +enum class AmbDecScale { + N3D, + SN3D, + FuMa, +}; +struct AmbDecConf { + std::string Description; + int Version{0}; /* Must be 3 */ + + unsigned int ChanMask{0u}; + unsigned int FreqBands{0u}; /* Must be 1 or 2 */ + AmbDecScale CoeffScale{}; + + float XOverFreq{0.0f}; + float XOverRatio{0.0f}; + + struct SpeakerConf { + std::string Name; + float Distance{0.0f}; + float Azimuth{0.0f}; + float Elevation{0.0f}; + std::string Connection; + }; + al::vector Speakers; + + using CoeffArray = std::array; + /* Unused when FreqBands == 1 */ + float LFOrderGain[MaxAmbiOrder+1]{}; + al::vector LFMatrix; + + float HFOrderGain[MaxAmbiOrder+1]{}; + al::vector HFMatrix; + + int load(const char *fname) noexcept; +}; + +#endif /* CORE_AMBDEC_H */ -- cgit v1.2.3 From 78f3d8fcd89a422a1412143e6c431efa3ff7a1d7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 31 Dec 2020 11:16:44 -0800 Subject: Check for SSE and NEON earlier --- CMakeLists.txt | 155 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 84 insertions(+), 71 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 05d04201..4f11b2f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -344,7 +344,6 @@ endif() set(SSE2_SWITCH "") -set(FPU_NEON_SWITCH "") set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) if(NOT MSVC) @@ -355,10 +354,6 @@ check_c_compiler_flag(-msse2 HAVE_MSSE2_SWITCH) if(HAVE_MSSE2_SWITCH) set(SSE2_SWITCH "-msse2") endif() -check_c_compiler_flag(-mfpu=neon HAVE_MFPU_NEON_SWITCH) -if(HAVE_MFPU_NEON_SWITCH) - set(FPU_NEON_SWITCH "-mfpu=neon") -endif() set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS}) unset(OLD_REQUIRED_FLAGS) @@ -368,12 +363,79 @@ check_include_file(pmmintrin.h HAVE_PMMINTRIN_H) check_include_file(smmintrin.h HAVE_SMMINTRIN_H) check_include_file(arm_neon.h HAVE_ARM_NEON_H) +set(HAVE_SSE 0) +set(HAVE_SSE2 0) +set(HAVE_SSE3 0) +set(HAVE_SSE4_1 0) +set(HAVE_NEON 0) + +# Check for SSE+SSE2 support +option(ALSOFT_REQUIRE_SSE "Require SSE support" OFF) +option(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF) +if(HAVE_XMMINTRIN_H AND HAVE_EMMINTRIN_H) + option(ALSOFT_CPUEXT_SSE "Enable SSE support" ON) + option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON) + if(ALSOFT_CPUEXT_SSE AND ALSOFT_CPUEXT_SSE2) + set(HAVE_SSE 1) + set(HAVE_SSE2 1) + endif() +endif() +if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE) + message(FATAL_ERROR "Failed to enabled required SSE CPU extensions") +endif() +if(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2) + message(FATAL_ERROR "Failed to enable required SSE2 CPU extensions") +endif() + +option(ALSOFT_REQUIRE_SSE3 "Require SSE3 support" OFF) +if(HAVE_PMMINTRIN_H) + option(ALSOFT_CPUEXT_SSE3 "Enable SSE3 support" ON) + if(HAVE_SSE2 AND ALSOFT_CPUEXT_SSE3) + set(HAVE_SSE3 1) + endif() +endif() +if(ALSOFT_REQUIRE_SSE3 AND NOT HAVE_SSE3) + message(FATAL_ERROR "Failed to enable required SSE3 CPU extensions") +endif() + +option(ALSOFT_REQUIRE_SSE4_1 "Require SSE4.1 support" OFF) +if(HAVE_SMMINTRIN_H) + option(ALSOFT_CPUEXT_SSE4_1 "Enable SSE4.1 support" ON) + if(HAVE_SSE3 AND ALSOFT_CPUEXT_SSE4_1) + set(HAVE_SSE4_1 1) + endif() +endif() +if(ALSOFT_REQUIRE_SSE4_1 AND NOT HAVE_SSE4_1) + message(FATAL_ERROR "Failed to enable required SSE4.1 CPU extensions") +endif() + +# Check for ARM Neon support +option(ALSOFT_REQUIRE_NEON "Require ARM NEON support" OFF) +if(HAVE_ARM_NEON_H) + option(ALSOFT_CPUEXT_NEON "Enable ARM NEON support" ON) + if(ALSOFT_CPUEXT_NEON) + check_c_source_compiles("#include + int main() + { + int32x4_t ret4 = vdupq_n_s32(0); + return vgetq_lane_s32(ret4, 0); + }" HAVE_NEON_INTRINSICS) + if(HAVE_NEON_INTRINSICS) + set(HAVE_NEON 1) + endif() + endif() +endif() +if(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON) + message(FATAL_ERROR "Failed to enabled required ARM NEON CPU extensions") +endif() + + set(SSE_FLAGS ) set(FPMATH_SET "0") -if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND (SSE2_SWITCH OR MSVC)) +if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2) option(ALSOFT_ENABLE_SSE2_CODEGEN "Enable SSE2 code generation instead of x87 for 32-bit targets." TRUE) if(ALSOFT_ENABLE_SSE2_CODEGEN) - if(SSE2_SWITCH) + if(SSE2_SWITCH OR NOT MSVC) check_c_compiler_flag("${SSE2_SWITCH} -mfpmath=sse" HAVE_MFPMATH_SSE_2) if(HAVE_MFPMATH_SSE_2) set(SSE_FLAGS ${SSE_FLAGS} ${SSE2_SWITCH} -mfpmath=sse) @@ -396,14 +458,14 @@ if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND (SSE2_SWITCH OR MSVC)) endif() endif() -if(HAVE_EMMINTRIN_H) +if(HAVE_SSE2) set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) foreach(flag_var ${SSE_FLAGS}) set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${flag_var}") endforeach() check_c_source_compiles("#include - int main() {_mm_pause(); return 0;}" HAVE_SSE_INTRINSICS) + int main() {_mm_pause(); return 0;}" HAVE_SSE_INTRINSICS) set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS}) endif() @@ -701,72 +763,23 @@ set(ALC_OBJS alc/voice.h alc/voice_change.h) - +# Include SIMD mixers set(CPU_EXTS "Default") -set(HAVE_SSE 0) -set(HAVE_SSE2 0) -set(HAVE_SSE3 0) -set(HAVE_SSE4_1 0) -set(HAVE_NEON 0) - -# Check for SSE+SSE2 support -option(ALSOFT_REQUIRE_SSE "Require SSE support" OFF) -option(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF) -if(HAVE_XMMINTRIN_H AND HAVE_EMMINTRIN_H) - option(ALSOFT_CPUEXT_SSE "Enable SSE support" ON) - option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON) - if(ALSOFT_CPUEXT_SSE AND ALSOFT_CPUEXT_SSE2) - set(HAVE_SSE 1) - set(HAVE_SSE2 1) - set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse.cpp core/mixer/mixer_sse2.cpp) - set(CPU_EXTS "${CPU_EXTS}, SSE, SSE2") - endif() +if(HAVE_SSE2) + set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse.cpp core/mixer/mixer_sse2.cpp) + set(CPU_EXTS "${CPU_EXTS}, SSE, SSE2") endif() -if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE) - message(FATAL_ERROR "Failed to enabled required SSE CPU extensions") +if(HAVE_SSE3) + set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse3.cpp) + set(CPU_EXTS "${CPU_EXTS}, SSE3") endif() -if(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2) - message(FATAL_ERROR "Failed to enable required SSE2 CPU extensions") +if(HAVE_SSE4_1) + set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse41.cpp) + set(CPU_EXTS "${CPU_EXTS}, SSE4.1") endif() - -option(ALSOFT_REQUIRE_SSE3 "Require SSE3 support" OFF) -if(HAVE_PMMINTRIN_H) - option(ALSOFT_CPUEXT_SSE3 "Enable SSE3 support" ON) - if(HAVE_SSE2 AND ALSOFT_CPUEXT_SSE3) - set(HAVE_SSE3 1) - set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse3.cpp) - set(CPU_EXTS "${CPU_EXTS}, SSE3") - endif() -endif() -if(ALSOFT_REQUIRE_SSE3 AND NOT HAVE_SSE3) - message(FATAL_ERROR "Failed to enable required SSE3 CPU extensions") -endif() - -option(ALSOFT_REQUIRE_SSE4_1 "Require SSE4.1 support" OFF) -if(HAVE_SMMINTRIN_H) - option(ALSOFT_CPUEXT_SSE4_1 "Enable SSE4.1 support" ON) - if(HAVE_SSE3 AND ALSOFT_CPUEXT_SSE4_1) - set(HAVE_SSE4_1 1) - set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse41.cpp) - set(CPU_EXTS "${CPU_EXTS}, SSE4.1") - endif() -endif() -if(ALSOFT_REQUIRE_SSE4_1 AND NOT HAVE_SSE4_1) - message(FATAL_ERROR "Failed to enable required SSE4.1 CPU extensions") -endif() - -# Check for ARM Neon support -option(ALSOFT_REQUIRE_NEON "Require ARM Neon support" OFF) -if(HAVE_ARM_NEON_H) - option(ALSOFT_CPUEXT_NEON "Enable ARM Neon support" ON) - if(ALSOFT_CPUEXT_NEON) - set(HAVE_NEON 1) - set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_neon.cpp) - set(CPU_EXTS "${CPU_EXTS}, Neon") - endif() -endif() -if(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON) - message(FATAL_ERROR "Failed to enabled required ARM Neon CPU extensions") +if(HAVE_NEON) + set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_neon.cpp) + set(CPU_EXTS "${CPU_EXTS}, Neon") endif() -- cgit v1.2.3 From 20ef8bf390541339f068676f9d14061fe2f5e115 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 31 Dec 2020 16:47:12 -0800 Subject: Move cpu_caps and fpu_ctrl to core --- CMakeLists.txt | 8 +-- al/auxeffectslot.cpp | 2 +- alc/alc.cpp | 4 +- alc/alu.cpp | 4 +- alc/converter.cpp | 2 +- alc/cpu_caps.cpp | 142 --------------------------------------------------- alc/cpu_caps.h | 26 ---------- alc/fpu_ctrl.cpp | 54 -------------------- alc/fpu_ctrl.h | 25 --------- alc/voice.cpp | 2 +- core/cpu_caps.cpp | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++ core/cpu_caps.h | 26 ++++++++++ core/fpu_ctrl.cpp | 54 ++++++++++++++++++++ core/fpu_ctrl.h | 25 +++++++++ 14 files changed, 258 insertions(+), 258 deletions(-) delete mode 100644 alc/cpu_caps.cpp delete mode 100644 alc/cpu_caps.h delete mode 100644 alc/fpu_ctrl.cpp delete mode 100644 alc/fpu_ctrl.h create mode 100644 core/cpu_caps.cpp create mode 100644 core/cpu_caps.h create mode 100644 core/fpu_ctrl.cpp create mode 100644 core/fpu_ctrl.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f11b2f5..bf2310a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -656,6 +656,8 @@ set(CORE_OBJS core/bsinc_tables.cpp core/bsinc_tables.h core/bufferline.h + core/cpu_caps.cpp + core/cpu_caps.h core/devformat.cpp core/devformat.h core/except.cpp @@ -668,6 +670,8 @@ set(CORE_OBJS core/filters/splitter.h core/fmt_traits.cpp core/fmt_traits.h + core/fpu_ctrl.cpp + core/fpu_ctrl.h core/logging.cpp core/logging.h core/mastering.cpp @@ -731,8 +735,6 @@ set(ALC_OBJS alc/compat.h alc/converter.cpp alc/converter.h - alc/cpu_caps.cpp - alc/cpu_caps.h alc/effectslot.cpp alc/effectslot.h alc/effects/base.h @@ -750,8 +752,6 @@ set(ALC_OBJS alc/effects/pshifter.cpp alc/effects/reverb.cpp alc/effects/vmorpher.cpp - alc/fpu_ctrl.cpp - alc/fpu_ctrl.h alc/front_stablizer.h alc/helpers.cpp alc/hrtf.cpp diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp index 61a31774..9a319126 100644 --- a/al/auxeffectslot.cpp +++ b/al/auxeffectslot.cpp @@ -43,9 +43,9 @@ #include "alu.h" #include "buffer.h" #include "core/except.h" +#include "core/fpu_ctrl.h" #include "core/logging.h" #include "effect.h" -#include "fpu_ctrl.h" #include "inprogext.h" #include "opthelpers.h" diff --git a/alc/alc.cpp b/alc/alc.cpp index 9bfc0b48..8cd902ed 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -80,16 +80,16 @@ #include "compat.h" #include "core/ambidefs.h" #include "core/bs2b.h" +#include "core/cpu_caps.h" #include "core/devformat.h" #include "core/except.h" #include "core/mastering.h" #include "core/filters/nfc.h" #include "core/filters/splitter.h" +#include "core/fpu_ctrl.h" #include "core/logging.h" #include "core/uhjfilter.h" -#include "cpu_caps.h" #include "effects/base.h" -#include "fpu_ctrl.h" #include "front_stablizer.h" #include "hrtf.h" #include "inprogext.h" diff --git a/alc/alu.cpp b/alc/alu.cpp index 5128e305..fe4c54a1 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -56,17 +56,17 @@ #include "core/ambidefs.h" #include "core/bs2b.h" #include "core/bsinc_tables.h" +#include "core/cpu_caps.h" #include "core/devformat.h" #include "core/filters/biquad.h" #include "core/filters/nfc.h" #include "core/filters/splitter.h" +#include "core/fpu_ctrl.h" #include "core/mastering.h" #include "core/mixer/defs.h" #include "core/uhjfilter.h" -#include "cpu_caps.h" #include "effects/base.h" #include "effectslot.h" -#include "fpu_ctrl.h" #include "front_stablizer.h" #include "hrtf.h" #include "inprogext.h" diff --git a/alc/converter.cpp b/alc/converter.cpp index 342085c5..5016b373 100644 --- a/alc/converter.cpp +++ b/alc/converter.cpp @@ -11,7 +11,7 @@ #include "albyte.h" #include "alnumeric.h" -#include "fpu_ctrl.h" +#include "core/fpu_ctrl.h" struct CTag; struct CopyTag; diff --git a/alc/cpu_caps.cpp b/alc/cpu_caps.cpp deleted file mode 100644 index dc992663..00000000 --- a/alc/cpu_caps.cpp +++ /dev/null @@ -1,142 +0,0 @@ - -#include "config.h" - -#include "cpu_caps.h" - -#if defined(_WIN32) && (defined(_M_ARM) || defined(_M_ARM64)) -#define WIN32_LEAN_AND_MEAN -#include -#ifndef PF_ARM_NEON_INSTRUCTIONS_AVAILABLE -#define PF_ARM_NEON_INSTRUCTIONS_AVAILABLE 19 -#endif -#endif - -#ifdef HAVE_INTRIN_H -#include -#endif -#ifdef HAVE_CPUID_H -#include -#endif - -#include -#include -#include - - -int CPUCapFlags{0}; - -namespace { - -#if defined(HAVE_GCC_GET_CPUID) \ - && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)) -using reg_type = unsigned int; -inline std::array get_cpuid(unsigned int f) -{ - std::array ret{}; - __get_cpuid(f, &ret[0], &ret[1], &ret[2], &ret[3]); - return ret; -} -#define CAN_GET_CPUID -#elif defined(HAVE_CPUID_INTRINSIC) \ - && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)) -using reg_type = int; -inline std::array get_cpuid(unsigned int f) -{ - std::array ret{}; - (__cpuid)(ret.data(), f); - return ret; -} -#define CAN_GET_CPUID -#endif - -} // namespace - -al::optional GetCPUInfo() -{ - CPUInfo ret; - -#ifdef CAN_GET_CPUID - auto cpuregs = get_cpuid(0); - if(cpuregs[0] == 0) - return al::nullopt; - - const reg_type maxfunc{cpuregs[0]}; - - cpuregs = get_cpuid(0x80000000); - const reg_type maxextfunc{cpuregs[0]}; - - ret.mVendor.append(reinterpret_cast(&cpuregs[1]), 4); - ret.mVendor.append(reinterpret_cast(&cpuregs[3]), 4); - ret.mVendor.append(reinterpret_cast(&cpuregs[2]), 4); - auto iter_end = std::remove(ret.mVendor.begin(), ret.mVendor.end(), '\0'); - while(iter_end != ret.mVendor.begin() && std::isspace(*iter_end)) - --iter_end; - iter_end = std::unique(ret.mVendor.begin(), iter_end, - [](auto&& c0, auto&& c1) { return std::isspace(c0) && std::isspace(c1); }); - ret.mVendor.erase(iter_end, ret.mVendor.end()); - if(!ret.mVendor.empty() && std::isspace(ret.mVendor.front())) - ret.mVendor.erase(ret.mVendor.begin()); - - if(maxextfunc >= 0x80000004) - { - cpuregs = get_cpuid(0x80000002); - ret.mName.append(reinterpret_cast(cpuregs.data()), 16); - cpuregs = get_cpuid(0x80000003); - ret.mName.append(reinterpret_cast(cpuregs.data()), 16); - cpuregs = get_cpuid(0x80000004); - ret.mName.append(reinterpret_cast(cpuregs.data()), 16); - iter_end = std::remove(ret.mName.begin(), ret.mName.end(), '\0'); - while(iter_end != ret.mName.begin() && std::isspace(*iter_end)) - --iter_end; - iter_end = std::unique(ret.mName.begin(), iter_end, - [](auto&& c0, auto&& c1) { return std::isspace(c0) && std::isspace(c1); }); - ret.mName.erase(iter_end, ret.mName.end()); - if(!ret.mName.empty() && std::isspace(ret.mName.front())) - ret.mName.erase(ret.mName.begin()); - } - - if(maxfunc >= 1) - { - cpuregs = get_cpuid(1); - if((cpuregs[3]&(1<<25))) - ret.mCaps |= CPU_CAP_SSE; - if((ret.mCaps&CPU_CAP_SSE) && (cpuregs[3]&(1<<26))) - ret.mCaps |= CPU_CAP_SSE2; - if((ret.mCaps&CPU_CAP_SSE2) && (cpuregs[2]&(1<<0))) - ret.mCaps |= CPU_CAP_SSE3; - if((ret.mCaps&CPU_CAP_SSE3) && (cpuregs[2]&(1<<19))) - ret.mCaps |= CPU_CAP_SSE4_1; - } - -#else - - /* Assume support for whatever's supported if we can't check for it */ -#if defined(HAVE_SSE4_1) -#warning "Assuming SSE 4.1 run-time support!" - ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1; -#elif defined(HAVE_SSE3) -#warning "Assuming SSE 3 run-time support!" - ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3; -#elif defined(HAVE_SSE2) -#warning "Assuming SSE 2 run-time support!" - ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2; -#elif defined(HAVE_SSE) -#warning "Assuming SSE run-time support!" - ret.mCaps |= CPU_CAP_SSE; -#endif -#endif /* CAN_GET_CPUID */ - -#ifdef HAVE_NEON -#ifdef __ARM_NEON - ret.mCaps |= CPU_CAP_NEON; -#elif defined(_WIN32) && (defined(_M_ARM) || defined(_M_ARM64)) - if(IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE)) - ret.mCaps |= CPU_CAP_NEON; -#else -#warning "Assuming NEON run-time support!" - ret.mCaps |= CPU_CAP_NEON; -#endif -#endif - - return al::make_optional(ret); -} diff --git a/alc/cpu_caps.h b/alc/cpu_caps.h deleted file mode 100644 index 190355b2..00000000 --- a/alc/cpu_caps.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef CPU_CAPS_H -#define CPU_CAPS_H - -#include - -#include "aloptional.h" - - -extern int CPUCapFlags; -enum { - CPU_CAP_SSE = 1<<0, - CPU_CAP_SSE2 = 1<<1, - CPU_CAP_SSE3 = 1<<2, - CPU_CAP_SSE4_1 = 1<<3, - CPU_CAP_NEON = 1<<4, -}; - -struct CPUInfo { - std::string mVendor; - std::string mName; - int mCaps{0}; -}; - -al::optional GetCPUInfo(); - -#endif /* CPU_CAPS_H */ diff --git a/alc/fpu_ctrl.cpp b/alc/fpu_ctrl.cpp deleted file mode 100644 index 24021c7d..00000000 --- a/alc/fpu_ctrl.cpp +++ /dev/null @@ -1,54 +0,0 @@ - -#include "config.h" - -#include "fpu_ctrl.h" - -#ifdef HAVE_INTRIN_H -#include -#endif -#ifdef HAVE_SSE_INTRINSICS -#include -#endif - -#include "cpu_caps.h" - - -FPUCtl::FPUCtl() -{ -#if defined(HAVE_SSE_INTRINSICS) - this->sse_state = _mm_getcsr(); - unsigned int sseState = this->sse_state; - sseState |= 0x8000; /* set flush-to-zero */ - sseState |= 0x0040; /* set denormals-are-zero */ - _mm_setcsr(sseState); - -#elif defined(__GNUC__) && defined(HAVE_SSE) - - if((CPUCapFlags&CPU_CAP_SSE)) - { - __asm__ __volatile__("stmxcsr %0" : "=m" (*&this->sse_state)); - unsigned int sseState = this->sse_state; - sseState |= 0x8000; /* set flush-to-zero */ - if((CPUCapFlags&CPU_CAP_SSE2)) - sseState |= 0x0040; /* set denormals-are-zero */ - __asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState)); - } -#endif - - this->in_mode = true; -} - -void FPUCtl::leave() -{ - if(!this->in_mode) return; - -#if defined(HAVE_SSE_INTRINSICS) - _mm_setcsr(this->sse_state); - -#elif defined(__GNUC__) && defined(HAVE_SSE) - - if((CPUCapFlags&CPU_CAP_SSE)) - __asm__ __volatile__("ldmxcsr %0" : : "m" (*&this->sse_state)); -#endif - this->in_mode = false; -} diff --git a/alc/fpu_ctrl.h b/alc/fpu_ctrl.h deleted file mode 100644 index e89bdc29..00000000 --- a/alc/fpu_ctrl.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef FPU_CTRL_H -#define FPU_CTRL_H - -class FPUCtl { -#if defined(HAVE_SSE_INTRINSICS) || (defined(__GNUC__) && defined(HAVE_SSE)) - unsigned int sse_state{}; -#endif - bool in_mode{}; - -public: - FPUCtl(); - /* HACK: 32-bit targets for GCC seem to have a problem here with certain - * noexcept methods (which destructors are) causing an internal compiler - * error. No idea why it's these methods specifically, but this is needed - * to get it to compile. - */ - ~FPUCtl() noexcept(false) { leave(); } - - FPUCtl(const FPUCtl&) = delete; - FPUCtl& operator=(const FPUCtl&) = delete; - - void leave(); -}; - -#endif /* FPU_CTRL_H */ diff --git a/alc/voice.cpp b/alc/voice.cpp index 5618f610..9c6ff6b5 100644 --- a/alc/voice.cpp +++ b/alc/voice.cpp @@ -45,6 +45,7 @@ #include "alu.h" #include "async_event.h" #include "buffer_storage.h" +#include "core/cpu_caps.h" #include "core/devformat.h" #include "core/filters/biquad.h" #include "core/filters/nfc.h" @@ -53,7 +54,6 @@ #include "core/logging.h" #include "core/mixer/defs.h" #include "core/mixer/hrtfdefs.h" -#include "cpu_caps.h" #include "hrtf.h" #include "inprogext.h" #include "opthelpers.h" diff --git a/core/cpu_caps.cpp b/core/cpu_caps.cpp new file mode 100644 index 00000000..dc992663 --- /dev/null +++ b/core/cpu_caps.cpp @@ -0,0 +1,142 @@ + +#include "config.h" + +#include "cpu_caps.h" + +#if defined(_WIN32) && (defined(_M_ARM) || defined(_M_ARM64)) +#define WIN32_LEAN_AND_MEAN +#include +#ifndef PF_ARM_NEON_INSTRUCTIONS_AVAILABLE +#define PF_ARM_NEON_INSTRUCTIONS_AVAILABLE 19 +#endif +#endif + +#ifdef HAVE_INTRIN_H +#include +#endif +#ifdef HAVE_CPUID_H +#include +#endif + +#include +#include +#include + + +int CPUCapFlags{0}; + +namespace { + +#if defined(HAVE_GCC_GET_CPUID) \ + && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)) +using reg_type = unsigned int; +inline std::array get_cpuid(unsigned int f) +{ + std::array ret{}; + __get_cpuid(f, &ret[0], &ret[1], &ret[2], &ret[3]); + return ret; +} +#define CAN_GET_CPUID +#elif defined(HAVE_CPUID_INTRINSIC) \ + && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)) +using reg_type = int; +inline std::array get_cpuid(unsigned int f) +{ + std::array ret{}; + (__cpuid)(ret.data(), f); + return ret; +} +#define CAN_GET_CPUID +#endif + +} // namespace + +al::optional GetCPUInfo() +{ + CPUInfo ret; + +#ifdef CAN_GET_CPUID + auto cpuregs = get_cpuid(0); + if(cpuregs[0] == 0) + return al::nullopt; + + const reg_type maxfunc{cpuregs[0]}; + + cpuregs = get_cpuid(0x80000000); + const reg_type maxextfunc{cpuregs[0]}; + + ret.mVendor.append(reinterpret_cast(&cpuregs[1]), 4); + ret.mVendor.append(reinterpret_cast(&cpuregs[3]), 4); + ret.mVendor.append(reinterpret_cast(&cpuregs[2]), 4); + auto iter_end = std::remove(ret.mVendor.begin(), ret.mVendor.end(), '\0'); + while(iter_end != ret.mVendor.begin() && std::isspace(*iter_end)) + --iter_end; + iter_end = std::unique(ret.mVendor.begin(), iter_end, + [](auto&& c0, auto&& c1) { return std::isspace(c0) && std::isspace(c1); }); + ret.mVendor.erase(iter_end, ret.mVendor.end()); + if(!ret.mVendor.empty() && std::isspace(ret.mVendor.front())) + ret.mVendor.erase(ret.mVendor.begin()); + + if(maxextfunc >= 0x80000004) + { + cpuregs = get_cpuid(0x80000002); + ret.mName.append(reinterpret_cast(cpuregs.data()), 16); + cpuregs = get_cpuid(0x80000003); + ret.mName.append(reinterpret_cast(cpuregs.data()), 16); + cpuregs = get_cpuid(0x80000004); + ret.mName.append(reinterpret_cast(cpuregs.data()), 16); + iter_end = std::remove(ret.mName.begin(), ret.mName.end(), '\0'); + while(iter_end != ret.mName.begin() && std::isspace(*iter_end)) + --iter_end; + iter_end = std::unique(ret.mName.begin(), iter_end, + [](auto&& c0, auto&& c1) { return std::isspace(c0) && std::isspace(c1); }); + ret.mName.erase(iter_end, ret.mName.end()); + if(!ret.mName.empty() && std::isspace(ret.mName.front())) + ret.mName.erase(ret.mName.begin()); + } + + if(maxfunc >= 1) + { + cpuregs = get_cpuid(1); + if((cpuregs[3]&(1<<25))) + ret.mCaps |= CPU_CAP_SSE; + if((ret.mCaps&CPU_CAP_SSE) && (cpuregs[3]&(1<<26))) + ret.mCaps |= CPU_CAP_SSE2; + if((ret.mCaps&CPU_CAP_SSE2) && (cpuregs[2]&(1<<0))) + ret.mCaps |= CPU_CAP_SSE3; + if((ret.mCaps&CPU_CAP_SSE3) && (cpuregs[2]&(1<<19))) + ret.mCaps |= CPU_CAP_SSE4_1; + } + +#else + + /* Assume support for whatever's supported if we can't check for it */ +#if defined(HAVE_SSE4_1) +#warning "Assuming SSE 4.1 run-time support!" + ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1; +#elif defined(HAVE_SSE3) +#warning "Assuming SSE 3 run-time support!" + ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3; +#elif defined(HAVE_SSE2) +#warning "Assuming SSE 2 run-time support!" + ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2; +#elif defined(HAVE_SSE) +#warning "Assuming SSE run-time support!" + ret.mCaps |= CPU_CAP_SSE; +#endif +#endif /* CAN_GET_CPUID */ + +#ifdef HAVE_NEON +#ifdef __ARM_NEON + ret.mCaps |= CPU_CAP_NEON; +#elif defined(_WIN32) && (defined(_M_ARM) || defined(_M_ARM64)) + if(IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE)) + ret.mCaps |= CPU_CAP_NEON; +#else +#warning "Assuming NEON run-time support!" + ret.mCaps |= CPU_CAP_NEON; +#endif +#endif + + return al::make_optional(ret); +} diff --git a/core/cpu_caps.h b/core/cpu_caps.h new file mode 100644 index 00000000..ffd671d0 --- /dev/null +++ b/core/cpu_caps.h @@ -0,0 +1,26 @@ +#ifndef CORE_CPU_CAPS_H +#define CORE_CPU_CAPS_H + +#include + +#include "aloptional.h" + + +extern int CPUCapFlags; +enum { + CPU_CAP_SSE = 1<<0, + CPU_CAP_SSE2 = 1<<1, + CPU_CAP_SSE3 = 1<<2, + CPU_CAP_SSE4_1 = 1<<3, + CPU_CAP_NEON = 1<<4, +}; + +struct CPUInfo { + std::string mVendor; + std::string mName; + int mCaps{0}; +}; + +al::optional GetCPUInfo(); + +#endif /* CORE_CPU_CAPS_H */ diff --git a/core/fpu_ctrl.cpp b/core/fpu_ctrl.cpp new file mode 100644 index 00000000..24021c7d --- /dev/null +++ b/core/fpu_ctrl.cpp @@ -0,0 +1,54 @@ + +#include "config.h" + +#include "fpu_ctrl.h" + +#ifdef HAVE_INTRIN_H +#include +#endif +#ifdef HAVE_SSE_INTRINSICS +#include +#endif + +#include "cpu_caps.h" + + +FPUCtl::FPUCtl() +{ +#if defined(HAVE_SSE_INTRINSICS) + this->sse_state = _mm_getcsr(); + unsigned int sseState = this->sse_state; + sseState |= 0x8000; /* set flush-to-zero */ + sseState |= 0x0040; /* set denormals-are-zero */ + _mm_setcsr(sseState); + +#elif defined(__GNUC__) && defined(HAVE_SSE) + + if((CPUCapFlags&CPU_CAP_SSE)) + { + __asm__ __volatile__("stmxcsr %0" : "=m" (*&this->sse_state)); + unsigned int sseState = this->sse_state; + sseState |= 0x8000; /* set flush-to-zero */ + if((CPUCapFlags&CPU_CAP_SSE2)) + sseState |= 0x0040; /* set denormals-are-zero */ + __asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState)); + } +#endif + + this->in_mode = true; +} + +void FPUCtl::leave() +{ + if(!this->in_mode) return; + +#if defined(HAVE_SSE_INTRINSICS) + _mm_setcsr(this->sse_state); + +#elif defined(__GNUC__) && defined(HAVE_SSE) + + if((CPUCapFlags&CPU_CAP_SSE)) + __asm__ __volatile__("ldmxcsr %0" : : "m" (*&this->sse_state)); +#endif + this->in_mode = false; +} diff --git a/core/fpu_ctrl.h b/core/fpu_ctrl.h new file mode 100644 index 00000000..20bd2772 --- /dev/null +++ b/core/fpu_ctrl.h @@ -0,0 +1,25 @@ +#ifndef CORE_FPU_CTRL_H +#define CORE_FPU_CTRL_H + +class FPUCtl { +#if defined(HAVE_SSE_INTRINSICS) || (defined(__GNUC__) && defined(HAVE_SSE)) + unsigned int sse_state{}; +#endif + bool in_mode{}; + +public: + FPUCtl(); + /* HACK: 32-bit targets for GCC seem to have a problem here with certain + * noexcept methods (which destructors are) causing an internal compiler + * error. No idea why it's these methods specifically, but this is needed + * to get it to compile. + */ + ~FPUCtl() noexcept(false) { leave(); } + + FPUCtl(const FPUCtl&) = delete; + FPUCtl& operator=(const FPUCtl&) = delete; + + void leave(); +}; + +#endif /* CORE_FPU_CTRL_H */ -- cgit v1.2.3 From 13698362f1726326ab60180b04a86df79b518614 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 18 Jan 2021 04:03:16 -0800 Subject: Avoid explicitly searching for the WindowsSDK It's causing problems with various setups. So instead we'll have to assume some things for Windows (namely that winmm exists, and if dsound isn't in DXSDK_DIR, it needs to be in the compiler's default paths to be usable). --- CMakeLists.txt | 48 ++-- cmake/FindDSound.cmake | 41 --- cmake/FindWindowsSDK.cmake | 634 --------------------------------------------- 3 files changed, 23 insertions(+), 700 deletions(-) delete mode 100644 cmake/FindDSound.cmake delete mode 100644 cmake/FindWindowsSDK.cmake (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index bf2310a4..9eb7aa07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -894,38 +894,36 @@ option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF) option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF) option(ALSOFT_REQUIRE_WASAPI "Require WASAPI backend" OFF) if(WIN32) - set(WINSDK_LIB_DIRS ) - set(WINSDK_INCLUDE_DIRS ) - find_package(WindowsSDK) - if(WINDOWSSDK_FOUND) - get_windowssdk_library_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_LIB_DIRS) - get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_INCLUDE_DIRS) - endif() - # Check MMSystem backend - check_include_files("windows.h;mmsystem.h" HAVE_MMSYSTEM_H) - find_library(WINMM_LIBRARY NAMES winmm - PATHS ${WINSDK_LIB_DIRS}) - if(HAVE_MMSYSTEM_H AND WINMM_LIBRARY) - option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON) - if(ALSOFT_BACKEND_WINMM) - set(HAVE_WINMM 1) - set(BACKENDS "${BACKENDS} WinMM,") - set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h) - set(EXTRA_LIBS ${WINMM_LIBRARY} ${EXTRA_LIBS}) - endif() + option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON) + if(ALSOFT_BACKEND_WINMM) + set(HAVE_WINMM 1) + set(BACKENDS "${BACKENDS} WinMM,") + set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h) + # There doesn't seem to be good way to search for winmm.lib for MSVC. + # find_library doesn't find it without being told to look in a specific + # place in the WindowsSDK, but it links anyway. If there ends up being + # Windows targets without this, another means to detect it is needed. + set(EXTRA_LIBS winmm ${EXTRA_LIBS}) endif() # Check DSound backend - find_package(DSound) - if(DSOUND_FOUND) + check_include_file(dsound.h HAVE_DSOUND_H) + if(DXSDK_DIR) + find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h" + PATHS "${DXSDK_DIR}" PATH_SUFFIXES include + DOC "The DirectSound include directory") + endif() + if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR) option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON) if(ALSOFT_BACKEND_DSOUND) set(HAVE_DSOUND 1) - set(BACKENDS "${BACKENDS} DirectSound${IS_LINKED},") - set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h) - add_backend_libs(${DSOUND_LIBRARIES}) - set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIRS}) + set(BACKENDS "${BACKENDS} DirectSound,") + set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h) + + if(NOT HAVE_DSOUND_H) + set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR}) + endif() endif() endif() diff --git a/cmake/FindDSound.cmake b/cmake/FindDSound.cmake deleted file mode 100644 index ecd36078..00000000 --- a/cmake/FindDSound.cmake +++ /dev/null @@ -1,41 +0,0 @@ -# - Find DirectSound includes and libraries -# -# DSOUND_FOUND - True if DSOUND_INCLUDE_DIR & DSOUND_LIBRARY are found -# DSOUND_LIBRARIES - Set when DSOUND_LIBRARY is found -# DSOUND_INCLUDE_DIRS - Set when DSOUND_INCLUDE_DIR is found -# -# DSOUND_INCLUDE_DIR - where to find dsound.h, etc. -# DSOUND_LIBRARY - the dsound library -# - -if (WIN32) - FIND_PACKAGE(WindowsSDK) - if (WINDOWSSDK_FOUND) - get_windowssdk_library_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_LIB_DIRS) - get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_INCLUDE_DIRS) - endif() -endif() - -# DSOUND_INCLUDE_DIR -find_path(DSOUND_INCLUDE_DIR - NAMES "dsound.h" - PATHS "${DXSDK_DIR}" ${WINSDK_INCLUDE_DIRS} - PATH_SUFFIXES include - DOC "The DirectSound include directory") - -# DSOUND_LIBRARY -find_library(DSOUND_LIBRARY - NAMES dsound - PATHS "${DXSDK_DIR}" ${WINSDK_LIB_DIRS} - PATH_SUFFIXES lib lib/x86 lib/x64 - DOC "The DirectSound library") - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(DSound REQUIRED_VARS DSOUND_LIBRARY DSOUND_INCLUDE_DIR) - -if(DSOUND_FOUND) - set(DSOUND_LIBRARIES ${DSOUND_LIBRARY}) - set(DSOUND_INCLUDE_DIRS ${DSOUND_INCLUDE_DIR}) -endif() - -mark_as_advanced(DSOUND_INCLUDE_DIR DSOUND_LIBRARY) diff --git a/cmake/FindWindowsSDK.cmake b/cmake/FindWindowsSDK.cmake deleted file mode 100644 index 15f9a231..00000000 --- a/cmake/FindWindowsSDK.cmake +++ /dev/null @@ -1,634 +0,0 @@ -# - Find the Windows SDK aka Platform SDK -# -# Relevant Wikipedia article: http://en.wikipedia.org/wiki/Microsoft_Windows_SDK -# -# Pass "COMPONENTS tools" to ignore Visual Studio version checks: in case -# you just want the tool binaries to run, rather than the libraries and headers -# for compiling. -# -# Variables: -# WINDOWSSDK_FOUND - if any version of the windows or platform SDK was found that is usable with the current version of visual studio -# WINDOWSSDK_LATEST_DIR -# WINDOWSSDK_LATEST_NAME -# WINDOWSSDK_FOUND_PREFERENCE - if we found an entry indicating a "preferred" SDK listed for this visual studio version -# WINDOWSSDK_PREFERRED_DIR -# WINDOWSSDK_PREFERRED_NAME -# -# WINDOWSSDK_DIRS - contains no duplicates, ordered most recent first. -# WINDOWSSDK_PREFERRED_FIRST_DIRS - contains no duplicates, ordered with preferred first, followed by the rest in descending recency -# -# Functions: -# windowssdk_name_lookup( ) - Find the name corresponding with the SDK directory you pass in, or -# NOTFOUND if not recognized. Your directory must be one of WINDOWSSDK_DIRS for this to work. -# -# windowssdk_build_lookup( ) - Find the build version number corresponding with the SDK directory you pass in, or -# NOTFOUND if not recognized. Your directory must be one of WINDOWSSDK_DIRS for this to work. -# -# get_windowssdk_from_component( ) - Given a library or include dir, -# find the Windows SDK root dir corresponding to it, or NOTFOUND if unrecognized. -# -# get_windowssdk_library_dirs( ) - Find the architecture-appropriate -# library directories corresponding to the SDK directory you pass in (or NOTFOUND if none) -# -# get_windowssdk_library_dirs_multiple( ...) - Find the architecture-appropriate -# library directories corresponding to the SDK directories you pass in, in order, skipping those not found. NOTFOUND if none at all. -# Good for passing WINDOWSSDK_DIRS or WINDOWSSDK_DIRS to if you really just want a file and don't care where from. -# -# get_windowssdk_include_dirs( ) - Find the -# include directories corresponding to the SDK directory you pass in (or NOTFOUND if none) -# -# get_windowssdk_include_dirs_multiple( ...) - Find the -# include directories corresponding to the SDK directories you pass in, in order, skipping those not found. NOTFOUND if none at all. -# Good for passing WINDOWSSDK_DIRS or WINDOWSSDK_DIRS to if you really just want a file and don't care where from. -# -# Requires these CMake modules: -# FindPackageHandleStandardArgs (known included with CMake >=2.6.2) -# -# Original Author: -# 2012 Ryan Pavlik -# http://academic.cleardefinition.com -# Iowa State University HCI Graduate Program/VRAC -# -# Copyright Iowa State University 2012. -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) - -set(_preferred_sdk_dirs) # pre-output -set(_win_sdk_dirs) # pre-output -set(_win_sdk_versanddirs) # pre-output -set(_win_sdk_buildsanddirs) # pre-output -set(_winsdk_vistaonly) # search parameters -set(_winsdk_kits) # search parameters - - -set(_WINDOWSSDK_ANNOUNCE OFF) -if(NOT WINDOWSSDK_FOUND AND (NOT WindowsSDK_FIND_QUIETLY)) - set(_WINDOWSSDK_ANNOUNCE ON) -endif() -macro(_winsdk_announce) - if(_WINSDK_ANNOUNCE) - message(STATUS ${ARGN}) - endif() -endmacro() - -set(_winsdk_win10vers - 10.0.19041.0 # Windows 10 Version 2004 - 10.0.18362.0 # Windows 10 Version 1903 - 10.0.17763.0 # Windows 10 Version 1809 - 10.0.17134.0 # Windows 10 Version 1803 (April 2018 Update) - 10.0.16299.0 # Windows 10 Version 1709 (Fall Creators Update) - 10.0.15063.0 # Windows 10 Version 1703 (Creators Update) - 10.0.14393.0 # Redstone aka Win10 1607 "Anniversary Update" - 10.0.10586.0 # TH2 aka Win10 1511 - 10.0.10240.0 # Win10 RTM - 10.0.10150.0 # just ucrt - 10.0.10056.0 -) - -if(WindowsSDK_FIND_COMPONENTS MATCHES "tools") - set(_WINDOWSSDK_IGNOREMSVC ON) - _winsdk_announce("Checking for tools from Windows/Platform SDKs...") -else() - set(_WINDOWSSDK_IGNOREMSVC OFF) - _winsdk_announce("Checking for Windows/Platform SDKs...") -endif() - -# Appends to the three main pre-output lists used only if the path exists -# and is not already in the list. -function(_winsdk_conditional_append _vername _build _path) - if(("${_path}" MATCHES "registry") OR (NOT EXISTS "${_path}")) - # Path invalid - do not add - return() - endif() - list(FIND _win_sdk_dirs "${_path}" _win_sdk_idx) - if(_win_sdk_idx GREATER -1) - # Path already in list - do not add - return() - endif() - _winsdk_announce( " - ${_vername}, Build ${_build} @ ${_path}") - # Not yet in the list, so we'll add it - list(APPEND _win_sdk_dirs "${_path}") - set(_win_sdk_dirs "${_win_sdk_dirs}" CACHE INTERNAL "" FORCE) - list(APPEND - _win_sdk_versanddirs - "${_vername}" - "${_path}") - set(_win_sdk_versanddirs "${_win_sdk_versanddirs}" CACHE INTERNAL "" FORCE) - list(APPEND - _win_sdk_buildsanddirs - "${_build}" - "${_path}") - set(_win_sdk_buildsanddirs "${_win_sdk_buildsanddirs}" CACHE INTERNAL "" FORCE) -endfunction() - -# Appends to the "preferred SDK" lists only if the path exists -function(_winsdk_conditional_append_preferred _info _path) - if(("${_path}" MATCHES "registry") OR (NOT EXISTS "${_path}")) - # Path invalid - do not add - return() - endif() - - get_filename_component(_path "${_path}" ABSOLUTE) - - list(FIND _win_sdk_preferred_sdk_dirs "${_path}" _win_sdk_idx) - if(_win_sdk_idx GREATER -1) - # Path already in list - do not add - return() - endif() - _winsdk_announce( " - Found \"preferred\" SDK ${_info} @ ${_path}") - # Not yet in the list, so we'll add it - list(APPEND _win_sdk_preferred_sdk_dirs "${_path}") - set(_win_sdk_preferred_sdk_dirs "${_win_sdk_dirs}" CACHE INTERNAL "" FORCE) - - # Just in case we somehow missed it: - _winsdk_conditional_append("${_info}" "" "${_path}") -endfunction() - -# Given a version like v7.0A, looks for an SDK in the registry under "Microsoft SDKs". -# If the given version might be in both HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows -# and HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots aka "Windows Kits", -# use this macro first, since these registry keys usually have more information. -# -# Pass a "default" build number as an extra argument in case we can't find it. -function(_winsdk_check_microsoft_sdks_registry _winsdkver) - set(SDKKEY "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\${_winsdkver}") - get_filename_component(_sdkdir - "[${SDKKEY};InstallationFolder]" - ABSOLUTE) - - set(_sdkname "Windows SDK ${_winsdkver}") - - # Default build number passed as extra argument - set(_build ${ARGN}) - # See if the registry holds a Microsoft-mutilated, err, designated, product name - # (just using get_filename_component to execute the registry lookup) - get_filename_component(_sdkproductname - "[${SDKKEY};ProductName]" - NAME) - if(NOT "${_sdkproductname}" MATCHES "registry") - # Got a product name - set(_sdkname "${_sdkname} (${_sdkproductname})") - endif() - - # try for a version to augment our name - # (just using get_filename_component to execute the registry lookup) - get_filename_component(_sdkver - "[${SDKKEY};ProductVersion]" - NAME) - if(NOT "${_sdkver}" MATCHES "registry" AND NOT MATCHES) - # Got a version - if(NOT "${_sdkver}" MATCHES "\\.\\.") - # and it's not an invalid one with two dots in it: - # use to override the default build - set(_build ${_sdkver}) - if(NOT "${_sdkname}" MATCHES "${_sdkver}") - # Got a version that's not already in the name, let's use it to improve our name. - set(_sdkname "${_sdkname} (${_sdkver})") - endif() - endif() - endif() - _winsdk_conditional_append("${_sdkname}" "${_build}" "${_sdkdir}") -endfunction() - -# Given a name for identification purposes, the build number, and a key (technically a "value name") -# corresponding to a Windows SDK packaged as a "Windows Kit", look for it -# in HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots -# Note that the key or "value name" tends to be something weird like KitsRoot81 - -# no easy way to predict, just have to observe them in the wild. -# Doesn't hurt to also try _winsdk_check_microsoft_sdks_registry for these: -# sometimes you get keys in both parts of the registry (in the wow64 portion especially), -# and the non-"Windows Kits" location is often more descriptive. -function(_winsdk_check_windows_kits_registry _winkit_name _winkit_build _winkit_key) - get_filename_component(_sdkdir - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots;${_winkit_key}]" - ABSOLUTE) - _winsdk_conditional_append("${_winkit_name}" "${_winkit_build}" "${_sdkdir}") -endfunction() - -# Given a name for identification purposes and the build number -# corresponding to a Windows 10 SDK packaged as a "Windows Kit", look for it -# in HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots -# Doesn't hurt to also try _winsdk_check_microsoft_sdks_registry for these: -# sometimes you get keys in both parts of the registry (in the wow64 portion especially), -# and the non-"Windows Kits" location is often more descriptive. -function(_winsdk_check_win10_kits _winkit_build) - get_filename_component(_sdkdir - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots;KitsRoot10]" - ABSOLUTE) - if(("${_sdkdir}" MATCHES "registry") OR (NOT EXISTS "${_sdkdir}")) - return() # not found - endif() - if(EXISTS "${_sdkdir}/Include/${_winkit_build}/um") - _winsdk_conditional_append("Windows Kits 10 (Build ${_winkit_build})" "${_winkit_build}" "${_sdkdir}") - endif() -endfunction() - -# Given a name for indentification purposes, the build number, and the associated package GUID, -# look in the registry under both HKLM and HKCU in \\SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\ -# for that guid and the SDK it points to. -function(_winsdk_check_platformsdk_registry _platformsdkname _build _platformsdkguid) - foreach(_winsdk_hive HKEY_LOCAL_MACHINE HKEY_CURRENT_USER) - get_filename_component(_sdkdir - "[${_winsdk_hive}\\SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\${_platformsdkguid};Install Dir]" - ABSOLUTE) - _winsdk_conditional_append("${_platformsdkname} (${_build})" "${_build}" "${_sdkdir}") - endforeach() -endfunction() - -### -# Detect toolchain information: to know whether it's OK to use Vista+ only SDKs -### -set(_winsdk_vistaonly_ok OFF) -if(MSVC AND NOT _WINDOWSSDK_IGNOREMSVC) - # VC 10 and older has broad target support - if(MSVC_VERSION LESS 1700) - # VC 11 by default targets Vista and later only, so we can add a few more SDKs that (might?) only work on vista+ - elseif("${CMAKE_VS_PLATFORM_TOOLSET}" MATCHES "_xp") - # This is the XP-compatible v110+ toolset - elseif("${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "v100" OR "${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "v90") - # This is the VS2010/VS2008 toolset - else() - # OK, we're VC11 or newer and not using a backlevel or XP-compatible toolset. - # These versions have no XP (and possibly Vista pre-SP1) support - set(_winsdk_vistaonly_ok ON) - if(_WINDOWSSDK_ANNOUNCE AND NOT _WINDOWSSDK_VISTAONLY_PESTERED) - set(_WINDOWSSDK_VISTAONLY_PESTERED ON CACHE INTERNAL "" FORCE) - message(STATUS "FindWindowsSDK: Detected Visual Studio 2012 or newer, not using the _xp toolset variant: including SDK versions that drop XP support in search!") - endif() - endif() -endif() -if(_WINDOWSSDK_IGNOREMSVC) - set(_winsdk_vistaonly_ok ON) -endif() - -### -# MSVC version checks - keeps messy conditionals in one place -# (messy because of _WINDOWSSDK_IGNOREMSVC) -### -set(_winsdk_msvc_greater_1200 OFF) -if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (MSVC_VERSION GREATER 1200))) - set(_winsdk_msvc_greater_1200 ON) -endif() -# Newer than VS .NET/VS Toolkit 2003 -set(_winsdk_msvc_greater_1310 OFF) -if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (MSVC_VERSION GREATER 1310))) - set(_winsdk_msvc_greater_1310 ON) -endif() - -# VS2005/2008 -set(_winsdk_msvc_less_1600 OFF) -if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (MSVC_VERSION LESS 1600))) - set(_winsdk_msvc_less_1600 ON) -endif() - -# VS2013+ -set(_winsdk_msvc_not_less_1800 OFF) -if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (NOT MSVC_VERSION LESS 1800))) - set(_winsdk_msvc_not_less_1800 ON) -endif() - -### -# START body of find module -### -if(_winsdk_msvc_greater_1310) # Newer than VS .NET/VS Toolkit 2003 - ### - # Look for "preferred" SDKs - ### - - # Environment variable for SDK dir - if(EXISTS "$ENV{WindowsSDKDir}" AND (NOT "$ENV{WindowsSDKDir}" STREQUAL "")) - _winsdk_conditional_append_preferred("WindowsSDKDir environment variable" "$ENV{WindowsSDKDir}") - endif() - - if(_winsdk_msvc_less_1600) - # Per-user current Windows SDK for VS2005/2008 - get_filename_component(_sdkdir - "[HKEY_CURRENT_USER\\Software\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]" - ABSOLUTE) - _winsdk_conditional_append_preferred("Per-user current Windows SDK" "${_sdkdir}") - - # System-wide current Windows SDK for VS2005/2008 - get_filename_component(_sdkdir - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]" - ABSOLUTE) - _winsdk_conditional_append_preferred("System-wide current Windows SDK" "${_sdkdir}") - endif() - - ### - # Begin the massive list of SDK searching! - ### - if(_winsdk_vistaonly_ok AND _winsdk_msvc_not_less_1800) - # These require at least Visual Studio 2013 (VC12) - - _winsdk_check_microsoft_sdks_registry(v10.0A) - - # Windows Software Development Kit (SDK) for Windows 10 - # Several different versions living in the same directory - if nothing else we can assume RTM (10240) - _winsdk_check_microsoft_sdks_registry(v10.0 10.0.10240.0) - foreach(_win10build ${_winsdk_win10vers}) - _winsdk_check_win10_kits(${_win10build}) - endforeach() - endif() # vista-only and 2013+ - - # Included in Visual Studio 2013 - # Includes the v120_xp toolset - _winsdk_check_microsoft_sdks_registry(v8.1A 8.1.51636) - - if(_winsdk_vistaonly_ok AND _winsdk_msvc_not_less_1800) - # Windows Software Development Kit (SDK) for Windows 8.1 - # http://msdn.microsoft.com/en-gb/windows/desktop/bg162891 - _winsdk_check_microsoft_sdks_registry(v8.1 8.1.25984.0) - _winsdk_check_windows_kits_registry("Windows Kits 8.1" 8.1.25984.0 KitsRoot81) - endif() # vista-only and 2013+ - - if(_winsdk_vistaonly_ok) - # Included in Visual Studio 2012 - _winsdk_check_microsoft_sdks_registry(v8.0A 8.0.50727) - - # Microsoft Windows SDK for Windows 8 and .NET Framework 4.5 - # This is the first version to also include the DirectX SDK - # http://msdn.microsoft.com/en-US/windows/desktop/hh852363.aspx - _winsdk_check_microsoft_sdks_registry(v8.0 6.2.9200.16384) - _winsdk_check_windows_kits_registry("Windows Kits 8.0" 6.2.9200.16384 KitsRoot) - endif() # vista-only - - # Included with VS 2012 Update 1 or later - # Introduces v110_xp toolset - _winsdk_check_microsoft_sdks_registry(v7.1A 7.1.51106) - if(_winsdk_vistaonly_ok) - # Microsoft Windows SDK for Windows 7 and .NET Framework 4 - # http://www.microsoft.com/downloads/en/details.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b - _winsdk_check_microsoft_sdks_registry(v7.1 7.1.7600.0.30514) - endif() # vista-only - - # Included with VS 2010 - _winsdk_check_microsoft_sdks_registry(v7.0A 6.1.7600.16385) - - # Windows SDK for Windows 7 and .NET Framework 3.5 SP1 - # Works with VC9 - # http://www.microsoft.com/en-us/download/details.aspx?id=18950 - _winsdk_check_microsoft_sdks_registry(v7.0 6.1.7600.16385) - - # Two versions call themselves "v6.1": - # Older: - # Windows Vista Update & .NET 3.0 SDK - # http://www.microsoft.com/en-us/download/details.aspx?id=14477 - - # Newer: - # Windows Server 2008 & .NET 3.5 SDK - # may have broken VS9SP1? they recommend v7.0 instead, or a KB... - # http://www.microsoft.com/en-us/download/details.aspx?id=24826 - _winsdk_check_microsoft_sdks_registry(v6.1 6.1.6000.16384.10) - - # Included in VS 2008 - _winsdk_check_microsoft_sdks_registry(v6.0A 6.1.6723.1) - - # Microsoft Windows Software Development Kit for Windows Vista and .NET Framework 3.0 Runtime Components - # http://blogs.msdn.com/b/stanley/archive/2006/11/08/microsoft-windows-software-development-kit-for-windows-vista-and-net-framework-3-0-runtime-components.aspx - _winsdk_check_microsoft_sdks_registry(v6.0 6.0.6000.16384) -endif() - -# Let's not forget the Platform SDKs, which sometimes are useful! -if(_winsdk_msvc_greater_1200) - _winsdk_check_platformsdk_registry("Microsoft Platform SDK for Windows Server 2003 R2" "5.2.3790.2075.51" "D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1") - _winsdk_check_platformsdk_registry("Microsoft Platform SDK for Windows Server 2003 SP1" "5.2.3790.1830.15" "8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3") -endif() -### -# Finally, look for "preferred" SDKs -### -if(_winsdk_msvc_greater_1310) # Newer than VS .NET/VS Toolkit 2003 - - - # Environment variable for SDK dir - if(EXISTS "$ENV{WindowsSDKDir}" AND (NOT "$ENV{WindowsSDKDir}" STREQUAL "")) - _winsdk_conditional_append_preferred("WindowsSDKDir environment variable" "$ENV{WindowsSDKDir}") - endif() - - if(_winsdk_msvc_less_1600) - # Per-user current Windows SDK for VS2005/2008 - get_filename_component(_sdkdir - "[HKEY_CURRENT_USER\\Software\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]" - ABSOLUTE) - _winsdk_conditional_append_preferred("Per-user current Windows SDK" "${_sdkdir}") - - # System-wide current Windows SDK for VS2005/2008 - get_filename_component(_sdkdir - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]" - ABSOLUTE) - _winsdk_conditional_append_preferred("System-wide current Windows SDK" "${_sdkdir}") - endif() -endif() - - -function(windowssdk_name_lookup _dir _outvar) - list(FIND _win_sdk_versanddirs "${_dir}" _diridx) - math(EXPR _idx "${_diridx} - 1") - if(${_idx} GREATER -1) - list(GET _win_sdk_versanddirs ${_idx} _ret) - else() - set(_ret "NOTFOUND") - endif() - set(${_outvar} "${_ret}" PARENT_SCOPE) -endfunction() - -function(windowssdk_build_lookup _dir _outvar) - list(FIND _win_sdk_buildsanddirs "${_dir}" _diridx) - math(EXPR _idx "${_diridx} - 1") - if(${_idx} GREATER -1) - list(GET _win_sdk_buildsanddirs ${_idx} _ret) - else() - set(_ret "NOTFOUND") - endif() - set(${_outvar} "${_ret}" PARENT_SCOPE) -endfunction() - -# If we found something... -if(_win_sdk_dirs) - list(GET _win_sdk_dirs 0 WINDOWSSDK_LATEST_DIR) - windowssdk_name_lookup("${WINDOWSSDK_LATEST_DIR}" - WINDOWSSDK_LATEST_NAME) - set(WINDOWSSDK_DIRS ${_win_sdk_dirs}) - - # Fallback, in case no preference found. - set(WINDOWSSDK_PREFERRED_DIR "${WINDOWSSDK_LATEST_DIR}") - set(WINDOWSSDK_PREFERRED_NAME "${WINDOWSSDK_LATEST_NAME}") - set(WINDOWSSDK_PREFERRED_FIRST_DIRS ${WINDOWSSDK_DIRS}) - set(WINDOWSSDK_FOUND_PREFERENCE OFF) -endif() - -# If we found indications of a user preference... -if(_win_sdk_preferred_sdk_dirs) - list(GET _win_sdk_preferred_sdk_dirs 0 WINDOWSSDK_PREFERRED_DIR) - windowssdk_name_lookup("${WINDOWSSDK_PREFERRED_DIR}" - WINDOWSSDK_PREFERRED_NAME) - set(WINDOWSSDK_PREFERRED_FIRST_DIRS - ${_win_sdk_preferred_sdk_dirs} - ${_win_sdk_dirs}) - list(REMOVE_DUPLICATES WINDOWSSDK_PREFERRED_FIRST_DIRS) - set(WINDOWSSDK_FOUND_PREFERENCE ON) -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(WindowsSDK - "No compatible version of the Windows SDK or Platform SDK found." - WINDOWSSDK_DIRS) - -if(WINDOWSSDK_FOUND) - # Internal: Architecture-appropriate library directory names. - if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM" OR "${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM64") - if(CMAKE_SIZEOF_VOID_P MATCHES "8") - # Only supported in Win10 SDK and up. - set(_winsdk_archbare ) # what the architecture used to be called in oldest SDKs - set(_winsdk_arch arm64) # what the architecture used to be called - set(_winsdk_arch8 arm64) # what the WDK for Win8+ calls this architecture - else() - set(_winsdk_archbare /arm) # what the architecture used to be called in oldest SDKs - set(_winsdk_arch arm) # what the architecture used to be called - set(_winsdk_arch8 arm) # what the WDK for Win8+ calls this architecture - endif() - else() - if(CMAKE_SIZEOF_VOID_P MATCHES "8") - set(_winsdk_archbare /x64) # what the architecture used to be called in oldest SDKs - set(_winsdk_arch amd64) # what the architecture used to be called - set(_winsdk_arch8 x64) # what the WDK for Win8+ calls this architecture - else() - set(_winsdk_archbare ) # what the architecture used to be called in oldest SDKs - set(_winsdk_arch i386) # what the architecture used to be called - set(_winsdk_arch8 x86) # what the WDK for Win8+ calls this architecture - endif() - endif() - - function(get_windowssdk_from_component _component _var) - get_filename_component(_component "${_component}" ABSOLUTE) - file(TO_CMAKE_PATH "${_component}" _component) - foreach(_sdkdir ${WINDOWSSDK_DIRS}) - get_filename_component(_sdkdir "${_sdkdir}" ABSOLUTE) - string(LENGTH "${_sdkdir}" _sdklen) - file(RELATIVE_PATH _rel "${_sdkdir}" "${_component}") - # If we don't have any "parent directory" items... - if(NOT "${_rel}" MATCHES "[.][.]") - set(${_var} "${_sdkdir}" PARENT_SCOPE) - return() - endif() - endforeach() - # Fail. - set(${_var} "NOTFOUND" PARENT_SCOPE) - endfunction() - function(get_windowssdk_library_dirs _winsdk_dir _var) - set(_dirs) - set(_suffixes - "lib${_winsdk_archbare}" # SDKs like 7.1A - "lib/${_winsdk_arch}" # just because some SDKs have x86 dir and root dir - "lib/w2k/${_winsdk_arch}" # Win2k min requirement - "lib/wxp/${_winsdk_arch}" # WinXP min requirement - "lib/wnet/${_winsdk_arch}" # Win Server 2003 min requirement - "lib/wlh/${_winsdk_arch}" - "lib/wlh/um/${_winsdk_arch8}" # Win Vista ("Long Horn") min requirement - "lib/win7/${_winsdk_arch}" - "lib/win7/um/${_winsdk_arch8}" # Win 7 min requirement - ) - foreach(_ver - wlh # Win Vista ("Long Horn") min requirement - win7 # Win 7 min requirement - win8 # Win 8 min requirement - winv6.3 # Win 8.1 min requirement - ) - - list(APPEND _suffixes - "lib/${_ver}/${_winsdk_arch}" - "lib/${_ver}/um/${_winsdk_arch8}" - "lib/${_ver}/km/${_winsdk_arch8}" - ) - endforeach() - - # Look for WDF libraries in Win10+ SDK - foreach(_mode umdf kmdf) - file(GLOB _wdfdirs RELATIVE "${_winsdk_dir}" "${_winsdk_dir}/lib/wdf/${_mode}/${_winsdk_arch8}/*") - if(_wdfdirs) - list(APPEND _suffixes ${_wdfdirs}) - endif() - endforeach() - - # Look in each Win10+ SDK version for the components - foreach(_win10ver ${_winsdk_win10vers}) - foreach(_component um km ucrt mmos) - list(APPEND _suffixes "lib/${_win10ver}/${_component}/${_winsdk_arch8}") - endforeach() - endforeach() - - foreach(_suffix ${_suffixes}) - # Check to see if a library actually exists here. - file(GLOB _libs "${_winsdk_dir}/${_suffix}/*.lib") - if(_libs) - list(APPEND _dirs "${_winsdk_dir}/${_suffix}") - endif() - endforeach() - if("${_dirs}" STREQUAL "") - set(_dirs NOTFOUND) - else() - list(REMOVE_DUPLICATES _dirs) - endif() - set(${_var} ${_dirs} PARENT_SCOPE) - endfunction() - function(get_windowssdk_include_dirs _winsdk_dir _var) - set(_dirs) - - set(_subdirs shared um winrt km wdf mmos ucrt) - set(_suffixes Include) - - foreach(_dir ${_subdirs}) - list(APPEND _suffixes "Include/${_dir}") - endforeach() - - foreach(_ver ${_winsdk_win10vers}) - foreach(_dir ${_subdirs}) - list(APPEND _suffixes "Include/${_ver}/${_dir}") - endforeach() - endforeach() - - foreach(_suffix ${_suffixes}) - # Check to see if a header file actually exists here. - file(GLOB _headers "${_winsdk_dir}/${_suffix}/*.h") - if(_headers) - list(APPEND _dirs "${_winsdk_dir}/${_suffix}") - endif() - endforeach() - if("${_dirs}" STREQUAL "") - set(_dirs NOTFOUND) - else() - list(REMOVE_DUPLICATES _dirs) - endif() - set(${_var} ${_dirs} PARENT_SCOPE) - endfunction() - function(get_windowssdk_library_dirs_multiple _var) - set(_dirs) - foreach(_sdkdir ${ARGN}) - get_windowssdk_library_dirs("${_sdkdir}" _current_sdk_libdirs) - if(_current_sdk_libdirs) - list(APPEND _dirs ${_current_sdk_libdirs}) - endif() - endforeach() - if("${_dirs}" STREQUAL "") - set(_dirs NOTFOUND) - else() - list(REMOVE_DUPLICATES _dirs) - endif() - set(${_var} ${_dirs} PARENT_SCOPE) - endfunction() - function(get_windowssdk_include_dirs_multiple _var) - set(_dirs) - foreach(_sdkdir ${ARGN}) - get_windowssdk_include_dirs("${_sdkdir}" _current_sdk_incdirs) - if(_current_sdk_libdirs) - list(APPEND _dirs ${_current_sdk_incdirs}) - endif() - endforeach() - if("${_dirs}" STREQUAL "") - set(_dirs NOTFOUND) - else() - list(REMOVE_DUPLICATES _dirs) - endif() - set(${_var} ${_dirs} PARENT_SCOPE) - endfunction() -endif() -- cgit v1.2.3 From 20f5e7c1fafcd40cdee2f7226ae81480ae049d1a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 21 Jan 2021 02:05:55 -0800 Subject: Avoid global constexpr arrays --- CMakeLists.txt | 1 - al/source.cpp | 4 +- alc/alc.cpp | 4 +- alc/alu.cpp | 24 +++--- alc/bformatdec.cpp | 21 +++-- alc/effects/convolution.cpp | 22 ++--- alc/hrtf.cpp | 2 +- alc/panning.cpp | 37 ++++----- core/ambidefs.cpp | 15 ---- core/ambidefs.h | 196 ++++++++++++++++++++++++++------------------ 10 files changed, 172 insertions(+), 154 deletions(-) delete mode 100644 core/ambidefs.cpp (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 9eb7aa07..558b0b4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -648,7 +648,6 @@ set(COMMON_OBJS set(CORE_OBJS core/ambdec.cpp core/ambdec.h - core/ambidefs.cpp core/ambidefs.h core/bs2b.cpp core/bs2b.h diff --git a/al/source.cpp b/al/source.cpp index cf536f2b..770e3778 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -471,8 +471,8 @@ void InitVoice(Voice *voice, ALsource *source, BufferlistItem *BufferList, ALCco if(voice->mAmbiOrder && device->mAmbiOrder > voice->mAmbiOrder) { const uint8_t *OrderFromChan{(voice->mFmtChannels == FmtBFormat2D) ? - AmbiIndex::OrderFrom2DChannel.data() : - AmbiIndex::OrderFromChannel.data()}; + AmbiIndex::OrderFrom2DChannel().data() : + AmbiIndex::OrderFromChannel().data()}; const auto scales = BFormatDec::GetHFOrderScales(voice->mAmbiOrder, device->mAmbiOrder); const BandSplitter splitter{device->mXOverFreq / static_cast(device->Frequency)}; diff --git a/alc/alc.cpp b/alc/alc.cpp index 8cd902ed..2b9eb2df 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -2212,8 +2212,8 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) if(voice->mAmbiOrder && device->mAmbiOrder > voice->mAmbiOrder) { const uint8_t *OrderFromChan{(voice->mFmtChannels == FmtBFormat2D) ? - AmbiIndex::OrderFrom2DChannel.data() : - AmbiIndex::OrderFromChannel.data()}; + AmbiIndex::OrderFrom2DChannel().data() : + AmbiIndex::OrderFromChannel().data()}; const BandSplitter splitter{device->mXOverFreq / static_cast(device->Frequency)}; diff --git a/alc/alu.cpp b/alc/alu.cpp index fe4c54a1..0cbfefec 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -345,23 +345,23 @@ inline uint dither_rng(uint *seed) noexcept } -auto GetAmbiScales(AmbiScaling scaletype) noexcept -> const std::array& +inline auto& GetAmbiScales(AmbiScaling scaletype) noexcept { - if(scaletype == AmbiScaling::FuMa) return AmbiScale::FromFuMa; - if(scaletype == AmbiScaling::SN3D) return AmbiScale::FromSN3D; - return AmbiScale::FromN3D; + if(scaletype == AmbiScaling::FuMa) return AmbiScale::FromFuMa(); + if(scaletype == AmbiScaling::SN3D) return AmbiScale::FromSN3D(); + return AmbiScale::FromN3D(); } -auto GetAmbiLayout(AmbiLayout layouttype) noexcept -> const std::array& +inline auto& GetAmbiLayout(AmbiLayout layouttype) noexcept { - if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa; - return AmbiIndex::FromACN; + if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa(); + return AmbiIndex::FromACN(); } -auto GetAmbi2DLayout(AmbiLayout layouttype) noexcept -> const std::array& +inline auto& GetAmbi2DLayout(AmbiLayout layouttype) noexcept { - if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa2D; - return AmbiIndex::FromACN2D; + if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa2D(); + return AmbiIndex::FromACN2D(); } @@ -855,7 +855,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con std::bind(std::multiplies{}, _1, 1.0f-coverage)); /* NOTE: W needs to be scaled according to channel scaling. */ - const auto &scales = GetAmbiScales(voice->mAmbiScaling); + auto&& scales = GetAmbiScales(voice->mAmbiScaling); ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base*scales[0], voice->mChans[0].mDryParams.Gains.Target); for(uint i{0};i < NumSends;i++) @@ -907,7 +907,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con for(size_t c{1};c < num_channels;c++) { const size_t acn{index_map[c]}; - const size_t order{AmbiIndex::OrderFromChannel[acn]}; + const size_t order{AmbiIndex::OrderFromChannel()[acn]}; const size_t tocopy{ChansPerOrder[order]}; const size_t offset{OrderOffset[order]}; const float scale{scales[acn] * coverage}; diff --git a/alc/bformatdec.cpp b/alc/bformatdec.cpp index 64c45b68..9b2d9049 100644 --- a/alc/bformatdec.cpp +++ b/alc/bformatdec.cpp @@ -31,19 +31,18 @@ constexpr std::array Ambi3DDecoderHFScale3O{{ 5.89792205e-01f, 8.79693856e-01f, 1.00000000e+00f, 1.00000000e+00f }}; -inline auto GetDecoderHFScales(uint order) noexcept -> const std::array& +inline auto& GetDecoderHFScales(uint order) noexcept { if(order >= 3) return Ambi3DDecoderHFScale3O; if(order == 2) return Ambi3DDecoderHFScale2O; return Ambi3DDecoderHFScale; } -inline auto GetAmbiScales(AmbDecScale scaletype) noexcept - -> const std::array& +inline auto& GetAmbiScales(AmbDecScale scaletype) noexcept { - if(scaletype == AmbDecScale::FuMa) return AmbiScale::FromFuMa; - if(scaletype == AmbDecScale::SN3D) return AmbiScale::FromSN3D; - return AmbiScale::FromN3D; + if(scaletype == AmbDecScale::FuMa) return AmbiScale::FromFuMa(); + if(scaletype == AmbDecScale::SN3D) return AmbiScale::FromSN3D(); + return AmbiScale::FromN3D(); } } // namespace @@ -56,15 +55,15 @@ BFormatDec::BFormatDec(const AmbDecConf *conf, const bool allow_2band, const siz , mChannelDec{inchans} { const bool periphonic{(conf->ChanMask&AmbiPeriphonicMask) != 0}; - const std::array &coeff_scale = GetAmbiScales(conf->CoeffScale); + auto&& coeff_scale = GetAmbiScales(conf->CoeffScale); if(!mDualBand) { for(size_t j{0},k{0};j < mChannelDec.size();++j) { - const size_t acn{periphonic ? j : AmbiIndex::FromACN2D[j]}; + const size_t acn{periphonic ? j : AmbiIndex::FromACN2D()[j]}; if(!(conf->ChanMask&(1u<HFOrderGain[order] / coeff_scale[acn]}; for(size_t i{0u};i < conf->NumSpeakers;++i) { @@ -83,9 +82,9 @@ BFormatDec::BFormatDec(const AmbDecConf *conf, const bool allow_2band, const siz const float ratio{std::pow(10.0f, conf->XOverRatio / 40.0f)}; for(size_t j{0},k{0};j < mChannelDec.size();++j) { - const size_t acn{periphonic ? j : AmbiIndex::FromACN2D[j]}; + const size_t acn{periphonic ? j : AmbiIndex::FromACN2D()[j]}; if(!(conf->ChanMask&(1u<HFOrderGain[order] * ratio / coeff_scale[acn]}; const float lfGain{conf->LFOrderGain[order] / ratio / coeff_scale[acn]}; for(size_t i{0u};i < conf->NumSpeakers;++i) diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index 4e10b62a..2dec0dc6 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -76,23 +76,23 @@ void LoadSamples(double *RESTRICT dst, const al::byte *src, const size_t srcstep } -auto GetAmbiScales(AmbiScaling scaletype) noexcept -> const std::array& +inline auto& GetAmbiScales(AmbiScaling scaletype) noexcept { - if(scaletype == AmbiScaling::FuMa) return AmbiScale::FromFuMa; - if(scaletype == AmbiScaling::SN3D) return AmbiScale::FromSN3D; - return AmbiScale::FromN3D; + if(scaletype == AmbiScaling::FuMa) return AmbiScale::FromFuMa(); + if(scaletype == AmbiScaling::SN3D) return AmbiScale::FromSN3D(); + return AmbiScale::FromN3D(); } -auto GetAmbiLayout(AmbiLayout layouttype) noexcept -> const std::array& +inline auto& GetAmbiLayout(AmbiLayout layouttype) noexcept { - if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa; - return AmbiIndex::FromACN; + if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa(); + return AmbiIndex::FromACN(); } -auto GetAmbi2DLayout(AmbiLayout layouttype) noexcept -> const std::array& +inline auto& GetAmbi2DLayout(AmbiLayout layouttype) noexcept { - if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa2D; - return AmbiIndex::FromACN2D; + if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa2D(); + return AmbiIndex::FromACN2D(); } @@ -387,7 +387,7 @@ void ConvolutionState::update(const ALCcontext *context, const EffectSlot *slot, } mOutTarget = target.Main->Buffer; - const auto &scales = GetAmbiScales(mAmbiScaling); + auto&& scales = GetAmbiScales(mAmbiScaling); const uint8_t *index_map{(mChannels == FmtBFormat2D) ? GetAmbi2DLayout(mAmbiLayout).data() : GetAmbiLayout(mAmbiLayout).data()}; diff --git a/alc/hrtf.cpp b/alc/hrtf.cpp index 891f9cb4..4e32a0ba 100644 --- a/alc/hrtf.cpp +++ b/alc/hrtf.cpp @@ -291,7 +291,7 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const double xover_norm{double{XOverFreq} / Hrtf->sampleRate}; for(size_t i{0};i < mChannels.size();++i) { - const size_t order{AmbiIndex::OrderFromChannel[i]}; + const size_t order{AmbiIndex::OrderFromChannel()[i]}; mChannels[i].mSplitter.init(static_cast(xover_norm)); mChannels[i].mHfScale = AmbiOrderHFGain[order]; } diff --git a/alc/panning.cpp b/alc/panning.cpp index c8a314b2..1ac3bb04 100644 --- a/alc/panning.cpp +++ b/alc/panning.cpp @@ -300,17 +300,17 @@ void InitDistanceComp(ALCdevice *device, const AmbDecConf *conf, } -auto GetAmbiScales(DevAmbiScaling scaletype) noexcept -> const std::array& +inline auto& GetAmbiScales(DevAmbiScaling scaletype) noexcept { - if(scaletype == DevAmbiScaling::FuMa) return AmbiScale::FromFuMa; - if(scaletype == DevAmbiScaling::SN3D) return AmbiScale::FromSN3D; - return AmbiScale::FromN3D; + if(scaletype == DevAmbiScaling::FuMa) return AmbiScale::FromFuMa(); + if(scaletype == DevAmbiScaling::SN3D) return AmbiScale::FromSN3D(); + return AmbiScale::FromN3D(); } -auto GetAmbiLayout(DevAmbiLayout layouttype) noexcept -> const std::array& +inline auto& GetAmbiLayout(DevAmbiLayout layouttype) noexcept { - if(layouttype == DevAmbiLayout::FuMa) return AmbiIndex::FromFuMa; - return AmbiIndex::FromACN; + if(layouttype == DevAmbiLayout::FuMa) return AmbiIndex::FromFuMa(); + return AmbiIndex::FromACN(); } @@ -492,8 +492,8 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= if(device->FmtChans == DevFmtAmbi3D) { const char *devname{device->DeviceName.c_str()}; - const std::array &acnmap = GetAmbiLayout(device->mAmbiLayout); - const std::array &n3dscale = GetAmbiScales(device->mAmbiScale); + auto&& acnmap = GetAmbiLayout(device->mAmbiLayout); + auto&& n3dscale = GetAmbiScales(device->mAmbiScale); /* For DevFmtAmbi3D, the ambisonic order is already set. */ const size_t count{AmbiChannelsFromOrder(device->mAmbiOrder)}; @@ -554,7 +554,7 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= /* Built-in speaker decoders are always 2D. */ const size_t ambicount{Ambi2DChannelsFromOrder(decoder.mOrder)}; - std::transform(AmbiIndex::FromACN2D.begin(), AmbiIndex::FromACN2D.begin()+ambicount, + std::transform(AmbiIndex::FromACN2D().begin(), AmbiIndex::FromACN2D().begin()+ambicount, std::begin(device->Dry.AmbiMap), [](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; } ); @@ -611,7 +611,7 @@ void InitCustomPanning(ALCdevice *device, const bool hqdec, const bool stablize, if((conf->ChanMask&AmbiPeriphonicMask)) { count = AmbiChannelsFromOrder(order); - std::transform(AmbiIndex::FromACN.begin(), AmbiIndex::FromACN.begin()+count, + std::transform(AmbiIndex::FromACN().begin(), AmbiIndex::FromACN().begin()+count, std::begin(device->Dry.AmbiMap), [](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; } ); @@ -619,7 +619,7 @@ void InitCustomPanning(ALCdevice *device, const bool hqdec, const bool stablize, else { count = Ambi2DChannelsFromOrder(order); - std::transform(AmbiIndex::FromACN2D.begin(), AmbiIndex::FromACN2D.begin()+count, + std::transform(AmbiIndex::FromACN2D().begin(), AmbiIndex::FromACN2D().begin()+count, std::begin(device->Dry.AmbiMap), [](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; } ); @@ -812,7 +812,7 @@ void InitHrtfPanning(ALCdevice *device) device->mAmbiOrder = ambi_order; const size_t count{AmbiChannelsFromOrder(ambi_order)}; - std::transform(AmbiIndex::FromACN.begin(), AmbiIndex::FromACN.begin()+count, + std::transform(AmbiIndex::FromACN().begin(), AmbiIndex::FromACN().begin()+count, std::begin(device->Dry.AmbiMap), [](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; } ); @@ -834,11 +834,10 @@ void InitUhjPanning(ALCdevice *device) device->mAmbiOrder = 1; - auto acnmap_end = AmbiIndex::FromFuMa.begin() + count; - std::transform(AmbiIndex::FromFuMa.begin(), acnmap_end, std::begin(device->Dry.AmbiMap), + auto acnmap_begin = AmbiIndex::FromFuMa().begin(); + std::transform(acnmap_begin, acnmap_begin + count, std::begin(device->Dry.AmbiMap), [](const uint8_t &acn) noexcept -> BFChannelConfig - { return BFChannelConfig{1.0f/AmbiScale::FromFuMa[acn], acn}; } - ); + { return BFChannelConfig{1.0f/AmbiScale::FromFuMa()[acn], acn}; }); AllocChannels(device, count, device->channelsFromFmt()); } @@ -1092,8 +1091,8 @@ void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context) WetBuffer *wetbuffer{slot->mWetBuffer = wetbuffer_iter->get()}; wetbuffer->mInUse = true; - auto acnmap_end = AmbiIndex::FromACN.begin() + count; - auto iter = std::transform(AmbiIndex::FromACN.begin(), acnmap_end, slot->Wet.AmbiMap.begin(), + auto acnmap_begin = AmbiIndex::FromACN().begin(); + auto iter = std::transform(acnmap_begin, acnmap_begin + count, slot->Wet.AmbiMap.begin(), [](const uint8_t &acn) noexcept -> BFChannelConfig { return BFChannelConfig{1.0f, acn}; }); std::fill(iter, slot->Wet.AmbiMap.end(), BFChannelConfig{}); diff --git a/core/ambidefs.cpp b/core/ambidefs.cpp deleted file mode 100644 index e032830f..00000000 --- a/core/ambidefs.cpp +++ /dev/null @@ -1,15 +0,0 @@ - -#include "config.h" - -#include "ambidefs.h" - - -constexpr std::array AmbiScale::FromN3D; -constexpr std::array AmbiScale::FromSN3D; -constexpr std::array AmbiScale::FromFuMa; -constexpr std::array AmbiIndex::FromFuMa; -constexpr std::array AmbiIndex::FromFuMa2D; -constexpr std::array AmbiIndex::FromACN; -constexpr std::array AmbiIndex::FromACN2D; -constexpr std::array AmbiIndex::OrderFromChannel; -constexpr std::array AmbiIndex::OrderFrom2DChannel; diff --git a/core/ambidefs.h b/core/ambidefs.h index ed8c27fb..a72f7b78 100644 --- a/core/ambidefs.h +++ b/core/ambidefs.h @@ -45,91 +45,127 @@ constexpr size_t MaxAmbi2DChannels{Ambi2DChannelsFromOrder(MaxAmbiOrder)}; * coefficients should be divided by these values to get proper scalings. */ struct AmbiScale { - static constexpr std::array FromN3D{{ - 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f - }}; - static constexpr std::array FromSN3D{{ - 1.000000000f, /* ACN 0, sqrt(1) */ - 1.732050808f, /* ACN 1, sqrt(3) */ - 1.732050808f, /* ACN 2, sqrt(3) */ - 1.732050808f, /* ACN 3, sqrt(3) */ - 2.236067978f, /* ACN 4, sqrt(5) */ - 2.236067978f, /* ACN 5, sqrt(5) */ - 2.236067978f, /* ACN 6, sqrt(5) */ - 2.236067978f, /* ACN 7, sqrt(5) */ - 2.236067978f, /* ACN 8, sqrt(5) */ - 2.645751311f, /* ACN 9, sqrt(7) */ - 2.645751311f, /* ACN 10, sqrt(7) */ - 2.645751311f, /* ACN 11, sqrt(7) */ - 2.645751311f, /* ACN 12, sqrt(7) */ - 2.645751311f, /* ACN 13, sqrt(7) */ - 2.645751311f, /* ACN 14, sqrt(7) */ - 2.645751311f, /* ACN 15, sqrt(7) */ - }}; - static constexpr std::array FromFuMa{{ - 1.414213562f, /* ACN 0 (W), sqrt(2) */ - 1.732050808f, /* ACN 1 (Y), sqrt(3) */ - 1.732050808f, /* ACN 2 (Z), sqrt(3) */ - 1.732050808f, /* ACN 3 (X), sqrt(3) */ - 1.936491673f, /* ACN 4 (V), sqrt(15)/2 */ - 1.936491673f, /* ACN 5 (T), sqrt(15)/2 */ - 2.236067978f, /* ACN 6 (R), sqrt(5) */ - 1.936491673f, /* ACN 7 (S), sqrt(15)/2 */ - 1.936491673f, /* ACN 8 (U), sqrt(15)/2 */ - 2.091650066f, /* ACN 9 (Q), sqrt(35/8) */ - 1.972026594f, /* ACN 10 (O), sqrt(35)/3 */ - 2.231093404f, /* ACN 11 (M), sqrt(224/45) */ - 2.645751311f, /* ACN 12 (K), sqrt(7) */ - 2.231093404f, /* ACN 13 (L), sqrt(224/45) */ - 1.972026594f, /* ACN 14 (N), sqrt(35)/3 */ - 2.091650066f, /* ACN 15 (P), sqrt(35/8) */ - }}; + static auto& FromN3D() noexcept + { + static constexpr const std::array ret{{ + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f + }}; + return ret; + } + static auto& FromSN3D() noexcept + { + static constexpr const std::array ret{{ + 1.000000000f, /* ACN 0, sqrt(1) */ + 1.732050808f, /* ACN 1, sqrt(3) */ + 1.732050808f, /* ACN 2, sqrt(3) */ + 1.732050808f, /* ACN 3, sqrt(3) */ + 2.236067978f, /* ACN 4, sqrt(5) */ + 2.236067978f, /* ACN 5, sqrt(5) */ + 2.236067978f, /* ACN 6, sqrt(5) */ + 2.236067978f, /* ACN 7, sqrt(5) */ + 2.236067978f, /* ACN 8, sqrt(5) */ + 2.645751311f, /* ACN 9, sqrt(7) */ + 2.645751311f, /* ACN 10, sqrt(7) */ + 2.645751311f, /* ACN 11, sqrt(7) */ + 2.645751311f, /* ACN 12, sqrt(7) */ + 2.645751311f, /* ACN 13, sqrt(7) */ + 2.645751311f, /* ACN 14, sqrt(7) */ + 2.645751311f, /* ACN 15, sqrt(7) */ + }}; + return ret; + } + static auto& FromFuMa() noexcept + { + static constexpr const std::array ret{{ + 1.414213562f, /* ACN 0 (W), sqrt(2) */ + 1.732050808f, /* ACN 1 (Y), sqrt(3) */ + 1.732050808f, /* ACN 2 (Z), sqrt(3) */ + 1.732050808f, /* ACN 3 (X), sqrt(3) */ + 1.936491673f, /* ACN 4 (V), sqrt(15)/2 */ + 1.936491673f, /* ACN 5 (T), sqrt(15)/2 */ + 2.236067978f, /* ACN 6 (R), sqrt(5) */ + 1.936491673f, /* ACN 7 (S), sqrt(15)/2 */ + 1.936491673f, /* ACN 8 (U), sqrt(15)/2 */ + 2.091650066f, /* ACN 9 (Q), sqrt(35/8) */ + 1.972026594f, /* ACN 10 (O), sqrt(35)/3 */ + 2.231093404f, /* ACN 11 (M), sqrt(224/45) */ + 2.645751311f, /* ACN 12 (K), sqrt(7) */ + 2.231093404f, /* ACN 13 (L), sqrt(224/45) */ + 1.972026594f, /* ACN 14 (N), sqrt(35)/3 */ + 2.091650066f, /* ACN 15 (P), sqrt(35/8) */ + }}; + return ret; + } }; struct AmbiIndex { - static constexpr std::array FromFuMa{{ - 0, /* W */ - 3, /* X */ - 1, /* Y */ - 2, /* Z */ - 6, /* R */ - 7, /* S */ - 5, /* T */ - 8, /* U */ - 4, /* V */ - 12, /* K */ - 13, /* L */ - 11, /* M */ - 14, /* N */ - 10, /* O */ - 15, /* P */ - 9, /* Q */ - }}; - static constexpr std::array FromFuMa2D{{ - 0, /* W */ - 3, /* X */ - 1, /* Y */ - 8, /* U */ - 4, /* V */ - 15, /* P */ - 9, /* Q */ - }}; + static auto& FromFuMa() noexcept + { + static constexpr const std::array ret{{ + 0, /* W */ + 3, /* X */ + 1, /* Y */ + 2, /* Z */ + 6, /* R */ + 7, /* S */ + 5, /* T */ + 8, /* U */ + 4, /* V */ + 12, /* K */ + 13, /* L */ + 11, /* M */ + 14, /* N */ + 10, /* O */ + 15, /* P */ + 9, /* Q */ + }}; + return ret; + } + static auto& FromFuMa2D() noexcept + { + static constexpr const std::array ret{{ + 0, /* W */ + 3, /* X */ + 1, /* Y */ + 8, /* U */ + 4, /* V */ + 15, /* P */ + 9, /* Q */ + }}; + return ret; + } - static constexpr std::array FromACN{{ - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15 - }}; - static constexpr std::array FromACN2D{{ - 0, 1,3, 4,8, 9,15 - }}; + static auto& FromACN() noexcept + { + static constexpr const std::array ret{{ + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15 + }}; + return ret; + } + static auto& FromACN2D() noexcept + { + static constexpr const std::array ret{{ + 0, 1,3, 4,8, 9,15 + }}; + return ret; + } - static constexpr std::array OrderFromChannel{{ - 0, 1,1,1, 2,2,2,2,2, 3,3,3,3,3,3,3, - }}; - static constexpr std::array OrderFrom2DChannel{{ - 0, 1,1, 2,2, 3,3, - }}; + static auto& OrderFromChannel() noexcept + { + static constexpr const std::array ret{{ + 0, 1,1,1, 2,2,2,2,2, 3,3,3,3,3,3,3, + }}; + return ret; + } + static auto& OrderFrom2DChannel() noexcept + { + static constexpr const std::array ret{{ + 0, 1,1, 2,2, 3,3, + }}; + return ret; + } }; #endif /* CORE_AMBIDEFS_H */ -- cgit v1.2.3 From 5729e1004da8dd8760fe3fa217faec4d3cd80427 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 21 Jan 2021 22:17:40 -0800 Subject: Make the endian test more C++-like --- CMakeLists.txt | 2 +- alc/backends/opensl.cpp | 26 +++++++++++++++----------- alc/backends/wave.cpp | 4 ++-- alc/hrtf.cpp | 4 ++-- common/albit.h | 35 +++++++++++++++++++++++++++++++++++ common/endiantest.h | 15 --------------- 6 files changed, 55 insertions(+), 31 deletions(-) create mode 100644 common/albit.h delete mode 100644 common/endiantest.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 558b0b4e..444049d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -613,6 +613,7 @@ check_symbol_exists(getopt unistd.h HAVE_GETOPT) # Common sources used by both the OpenAL implementation library, the OpenAL # router, and certain tools and examples. set(COMMON_OBJS + common/albit.h common/albyte.h common/alcomplex.cpp common/alcomplex.h @@ -628,7 +629,6 @@ set(COMMON_OBJS common/atomic.h common/dynload.cpp common/dynload.h - common/endiantest.h common/intrusive_ptr.h common/math_defs.h common/opthelpers.h diff --git a/alc/backends/opensl.cpp b/alc/backends/opensl.cpp index e0589b34..926911f0 100644 --- a/alc/backends/opensl.cpp +++ b/alc/backends/opensl.cpp @@ -32,11 +32,11 @@ #include #include +#include "albit.h" #include "alcmain.h" #include "alu.h" #include "compat.h" #include "core/logging.h" -#include "endiantest.h" #include "ringbuffer.h" #include "threads.h" @@ -56,7 +56,7 @@ namespace { constexpr char opensl_device[] = "OpenSL"; -SLuint32 GetChannelMask(DevFmtChannels chans) +constexpr SLuint32 GetChannelMask(DevFmtChannels chans) noexcept { switch(chans) { @@ -83,7 +83,7 @@ SLuint32 GetChannelMask(DevFmtChannels chans) } #ifdef SL_ANDROID_DATAFORMAT_PCM_EX -SLuint32 GetTypeRepresentation(DevFmtType type) +constexpr SLuint32 GetTypeRepresentation(DevFmtType type) noexcept { switch(type) { @@ -102,7 +102,14 @@ SLuint32 GetTypeRepresentation(DevFmtType type) } #endif -const char *res_str(SLresult result) +constexpr SLuint32 GetByteOrderEndianness() noexcept +{ + if /*constexpr*/(al::endian::native == al::endian::little) + return SL_BYTEORDER_LITTLEENDIAN; + return SL_BYTEORDER_BIGENDIAN; +} + +const char *res_str(SLresult result) noexcept { switch(result) { @@ -459,7 +466,7 @@ bool OpenSLPlayback::reset() format_pcm_ex.bitsPerSample = mDevice->bytesFromFmt() * 8; format_pcm_ex.containerSize = format_pcm_ex.bitsPerSample; format_pcm_ex.channelMask = GetChannelMask(mDevice->FmtChans); - format_pcm_ex.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; + format_pcm_ex.endianness = GetByteOrderEndianness(); format_pcm_ex.representation = GetTypeRepresentation(mDevice->FmtType); audioSrc.pLocator = &loc_bufq; @@ -490,8 +497,7 @@ bool OpenSLPlayback::reset() format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8; format_pcm.containerSize = format_pcm.bitsPerSample; format_pcm.channelMask = GetChannelMask(mDevice->FmtChans); - format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN : - SL_BYTEORDER_BIGENDIAN; + format_pcm.endianness = GetByteOrderEndianness(); audioSrc.pLocator = &loc_bufq; audioSrc.pFormat = &format_pcm; @@ -733,8 +739,7 @@ void OpenSLCapture::open(const char* name) format_pcm_ex.bitsPerSample = mDevice->bytesFromFmt() * 8; format_pcm_ex.containerSize = format_pcm_ex.bitsPerSample; format_pcm_ex.channelMask = GetChannelMask(mDevice->FmtChans); - format_pcm_ex.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN : - SL_BYTEORDER_BIGENDIAN; + format_pcm_ex.endianness = GetByteOrderEndianness(); format_pcm_ex.representation = GetTypeRepresentation(mDevice->FmtType); audioSnk.pLocator = &loc_bq; @@ -757,8 +762,7 @@ void OpenSLCapture::open(const char* name) format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8; format_pcm.containerSize = format_pcm.bitsPerSample; format_pcm.channelMask = GetChannelMask(mDevice->FmtChans); - format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN : - SL_BYTEORDER_BIGENDIAN; + format_pcm.endianness = GetByteOrderEndianness(); audioSnk.pLocator = &loc_bq; audioSnk.pFormat = &format_pcm; diff --git a/alc/backends/wave.cpp b/alc/backends/wave.cpp index 3a80b9ca..afff1d56 100644 --- a/alc/backends/wave.cpp +++ b/alc/backends/wave.cpp @@ -33,6 +33,7 @@ #include #include +#include "albit.h" #include "albyte.h" #include "alcmain.h" #include "alconfig.h" @@ -41,7 +42,6 @@ #include "alu.h" #include "compat.h" #include "core/logging.h" -#include "endiantest.h" #include "strutils.h" #include "threads.h" #include "vector.h" @@ -149,7 +149,7 @@ int WaveBackend::mixerProc() mDevice->renderSamples(mBuffer.data(), mDevice->UpdateSize, frameStep); done += mDevice->UpdateSize; - if /*constexpr*/(!IS_LITTLE_ENDIAN) + if /*constexpr*/(al::endian::native != al::endian::little) { const uint bytesize{mDevice->bytesFromFmt()}; diff --git a/alc/hrtf.cpp b/alc/hrtf.cpp index 4e32a0ba..6c40db70 100644 --- a/alc/hrtf.cpp +++ b/alc/hrtf.cpp @@ -40,6 +40,7 @@ #include #include +#include "albit.h" #include "alcmain.h" #include "alconfig.h" #include "alfstream.h" @@ -49,7 +50,6 @@ #include "alspan.h" #include "core/filters/splitter.h" #include "core/logging.h" -#include "endiantest.h" #include "math_defs.h" #include "opthelpers.h" #include "polyphase_resampler.h" @@ -471,7 +471,7 @@ inline T readle(std::istream &data) static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type"); T ret{}; - if(IS_LITTLE_ENDIAN) + if /*constexpr*/(al::endian::native == al::endian::little) { if(!data.read(reinterpret_cast(&ret), num_bits/8)) return static_cast(EOF); diff --git a/common/albit.h b/common/albit.h new file mode 100644 index 00000000..225c0b89 --- /dev/null +++ b/common/albit.h @@ -0,0 +1,35 @@ +#ifndef AL_BIT_H +#define AL_BIT_H + +namespace al { + +#ifdef __BYTE_ORDER__ +enum class endian { + little = __ORDER_LITTLE_ENDIAN__, + big = __ORDER_BIG_ENDIAN__, + native = __BYTE_ORDER__ +}; + +#else + +/* This doesn't support mixed-endian. */ +namespace detail_ { +constexpr inline bool EndianTest() noexcept +{ + static_assert(sizeof(char) < sizeof(int), "char is too big"); + + constexpr int test_val{1}; + return static_cast(test_val); +} +} // namespace detail_ + +enum class endian { + little = 0, + big = 1, + native = detail_::EndianTest() ? little : big +}; +#endif + +} // namespace al + +#endif /* AL_BIT_H */ diff --git a/common/endiantest.h b/common/endiantest.h deleted file mode 100644 index 893653bd..00000000 --- a/common/endiantest.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef AL_ENDIANTEST_H -#define AL_ENDIANTEST_H - -#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) -#define IS_LITTLE_ENDIAN (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) -#else -constexpr inline bool EndianTest() noexcept -{ - constexpr int test_val{1}; - return static_cast(test_val); -} -#define IS_LITTLE_ENDIAN (EndianTest()) -#endif - -#endif /* AL_ENDIANTEST_H */ -- cgit v1.2.3 From f9f8b52a4012e52f551cd96da1ab53f9a137f915 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 22 Jan 2021 08:17:45 -0800 Subject: Ensure the correct standard is set for cmake checks --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 444049d2..2c04c73c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,9 +153,11 @@ set(EXPORT_DECL "") # Require C++14 set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} $<$:${CMAKE_CXX14_EXTENSION_COMPILE_OPTION}>") # Prefer C11, but support C99 and C90 too. set(CMAKE_C_STANDARD 11) +set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} $<$:${CMAKE_C11_EXTENSION_COMPILE_OPTION}>") if(NOT WIN32) # Check if _POSIX_C_SOURCE and _XOPEN_SOURCE needs to be set for POSIX functions -- cgit v1.2.3 From 1dcc6361fe5c06dfd3ec30cfb9bce04e0c522805 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 22 Jan 2021 08:42:02 -0800 Subject: Fix setting the correct standards flag --- CMakeLists.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c04c73c..651f84e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,11 +153,19 @@ set(EXPORT_DECL "") # Require C++14 set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} $<$:${CMAKE_CXX14_EXTENSION_COMPILE_OPTION}>") +if(CMAKE_CXX14_EXTENSION_COMPILE_OPTION) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX14_EXTENSION_COMPILE_OPTION}") +elseif(CMAKE_CXX14_STANDARD_COMPILE_OPTION) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX14_STANDARD_COMPILE_OPTION}") +endif() # Prefer C11, but support C99 and C90 too. set(CMAKE_C_STANDARD 11) -set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} $<$:${CMAKE_C11_EXTENSION_COMPILE_OPTION}>") +if(CMAKE_C11_EXTENSION_COMPILE_OPTION) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C11_EXTENSION_COMPILE_OPTION}") +elseif(CMAKE_C11_STANDARD_COMPILE_OPTION) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C11_STANDARD_COMPILE_OPTION}") +endif() if(NOT WIN32) # Check if _POSIX_C_SOURCE and _XOPEN_SOURCE needs to be set for POSIX functions -- cgit v1.2.3 From f576a353638236775ecb4f4e8ba93cb1716fbe2d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 22 Jan 2021 08:53:44 -0800 Subject: Don't bother checking for std::aligned_alloc --- CMakeLists.txt | 17 ----------------- common/almalloc.cpp | 7 ++----- config.h.in | 3 --- 3 files changed, 2 insertions(+), 25 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 651f84e8..1f355508 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,19 +153,9 @@ set(EXPORT_DECL "") # Require C++14 set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -if(CMAKE_CXX14_EXTENSION_COMPILE_OPTION) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX14_EXTENSION_COMPILE_OPTION}") -elseif(CMAKE_CXX14_STANDARD_COMPILE_OPTION) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX14_STANDARD_COMPILE_OPTION}") -endif() # Prefer C11, but support C99 and C90 too. set(CMAKE_C_STANDARD 11) -if(CMAKE_C11_EXTENSION_COMPILE_OPTION) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C11_EXTENSION_COMPILE_OPTION}") -elseif(CMAKE_C11_STANDARD_COMPILE_OPTION) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C11_STANDARD_COMPILE_OPTION}") -endif() if(NOT WIN32) # Check if _POSIX_C_SOURCE and _XOPEN_SOURCE needs to be set for POSIX functions @@ -534,13 +524,6 @@ if(HAVE_INTRIN_H) }" HAVE_CPUID_INTRINSIC) endif() -check_cxx_source_compiles("#include -int main() -{ - void *ptr{std::aligned_alloc(alignof(int), sizeof(int))}; - std::free(ptr); - return 0; -}" HAVE_STD_ALIGNED_ALLOC) check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN) check_symbol_exists(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC) check_symbol_exists(proc_pidpath libproc.h HAVE_PROC_PIDPATH) diff --git a/common/almalloc.cpp b/common/almalloc.cpp index 4d7ff62c..ad1dc6be 100644 --- a/common/almalloc.cpp +++ b/common/almalloc.cpp @@ -18,10 +18,7 @@ void *al_malloc(size_t alignment, size_t size) assert((alignment & (alignment-1)) == 0); alignment = std::max(alignment, alignof(std::max_align_t)); -#if defined(HAVE_STD_ALIGNED_ALLOC) - size = (size+(alignment-1))&~(alignment-1); - return std::aligned_alloc(alignment, size); -#elif defined(HAVE_POSIX_MEMALIGN) +#if defined(HAVE_POSIX_MEMALIGN) void *ret{}; if(posix_memalign(&ret, alignment, size) == 0) return ret; @@ -53,7 +50,7 @@ void *al_calloc(size_t alignment, size_t size) void al_free(void *ptr) noexcept { -#if defined(HAVE_STD_ALIGNED_ALLOC) || defined(HAVE_POSIX_MEMALIGN) +#if defined(HAVE_POSIX_MEMALIGN) std::free(ptr); #elif defined(HAVE__ALIGNED_MALLOC) _aligned_free(ptr); diff --git a/config.h.in b/config.h.in index f7e1542e..a28204ef 100644 --- a/config.h.in +++ b/config.h.in @@ -5,9 +5,6 @@ /* Define if HRTF data is embedded in the library */ #cmakedefine ALSOFT_EMBED_HRTF_DATA -/* Define if we have the std::aligned_alloc function */ -#cmakedefine HAVE_STD_ALIGNED_ALLOC - /* Define if we have the posix_memalign function */ #cmakedefine HAVE_POSIX_MEMALIGN -- cgit v1.2.3 From ac5d40e40a0155351fe1be4aab30017b6a13a859 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 25 Jan 2021 09:24:10 -0800 Subject: Move al::deque to a common header --- CMakeLists.txt | 1 + al/source.h | 8 +------- common/aldeque.h | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 common/aldeque.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f355508..ca72f610 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -610,6 +610,7 @@ set(COMMON_OBJS common/albyte.h common/alcomplex.cpp common/alcomplex.h + common/aldeque.h common/alfstream.cpp common/alfstream.h common/almalloc.cpp diff --git a/al/source.h b/al/source.h index f17395c3..6572864f 100644 --- a/al/source.h +++ b/al/source.h @@ -12,6 +12,7 @@ #include "AL/alc.h" #include "alcontext.h" +#include "aldeque.h" #include "almalloc.h" #include "alnumeric.h" #include "alu.h" @@ -22,13 +23,6 @@ struct ALbuffer; struct ALeffectslot; -namespace al { - -template -using deque = std::deque>; - -} // namespace al - #define DEFAULT_SENDS 2 diff --git a/common/aldeque.h b/common/aldeque.h new file mode 100644 index 00000000..3f99bf00 --- /dev/null +++ b/common/aldeque.h @@ -0,0 +1,16 @@ +#ifndef ALDEQUE_H +#define ALDEQUE_H + +#include + +#include "almalloc.h" + + +namespace al { + +template +using deque = std::deque>; + +} // namespace al + +#endif /* ALDEQUE_H */ -- cgit v1.2.3 From ae4eacf147e2c2340cc4e02a790df04c793ed0a9 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 4 Feb 2021 11:09:06 -0800 Subject: Release 1.21.1 --- CMakeLists.txt | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index ca72f610..0cf0613d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,7 +143,7 @@ endif() set(LIB_MAJOR_VERSION "1") set(LIB_MINOR_VERSION "21") -set(LIB_REVISION "0") +set(LIB_REVISION "1") set(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}") set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0) diff --git a/appveyor.yml b/appveyor.yml index e54760b2..96082741 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 1.21.0.{build} +version: 1.21.1.{build} environment: APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 -- cgit v1.2.3 From 1d57db6836fd577e66bafc84095d672d288e4552 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 8 Mar 2021 22:47:50 -0800 Subject: Move the ComPtr wrapper to a common header --- CMakeLists.txt | 1 + alc/backends/dsound.cpp | 64 +------------------------------------------- alc/backends/wasapi.cpp | 64 +------------------------------------------- common/comptr.h | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 126 deletions(-) create mode 100644 common/comptr.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cf0613d..cd9e6d9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -621,6 +621,7 @@ set(COMMON_OBJS common/alstring.cpp common/alstring.h common/atomic.h + common/comptr.h common/dynload.cpp common/dynload.h common/intrusive_ptr.h diff --git a/alc/backends/dsound.cpp b/alc/backends/dsound.cpp index 34fe25f4..72f9c47a 100644 --- a/alc/backends/dsound.cpp +++ b/alc/backends/dsound.cpp @@ -47,6 +47,7 @@ #include "alcmain.h" #include "alu.h" #include "compat.h" +#include "comptr.h" #include "core/logging.h" #include "dynload.h" #include "ringbuffer.h" @@ -107,69 +108,6 @@ HRESULT (WINAPI *pDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallbac #endif -template -class ComPtr { - T *mPtr{nullptr}; - -public: - ComPtr() noexcept = default; - ComPtr(const ComPtr &rhs) : mPtr{rhs.mPtr} { if(mPtr) mPtr->AddRef(); } - ComPtr(ComPtr&& rhs) noexcept : mPtr{rhs.mPtr} { rhs.mPtr = nullptr; } - ComPtr(std::nullptr_t) noexcept { } - explicit ComPtr(T *ptr) noexcept : mPtr{ptr} { } - ~ComPtr() { if(mPtr) mPtr->Release(); } - - ComPtr& operator=(const ComPtr &rhs) - { - if(!rhs.mPtr) - { - if(mPtr) - mPtr->Release(); - mPtr = nullptr; - } - else - { - rhs.mPtr->AddRef(); - try { - if(mPtr) - mPtr->Release(); - mPtr = rhs.mPtr; - } - catch(...) { - rhs.mPtr->Release(); - throw; - } - } - return *this; - } - ComPtr& operator=(ComPtr&& rhs) - { - if(mPtr) - mPtr->Release(); - mPtr = rhs.mPtr; - rhs.mPtr = nullptr; - return *this; - } - - operator bool() const noexcept { return mPtr != nullptr; } - - T& operator*() const noexcept { return *mPtr; } - T* operator->() const noexcept { return mPtr; } - T* get() const noexcept { return mPtr; } - T** getPtr() noexcept { return &mPtr; } - - T* release() noexcept - { - T *ret{mPtr}; - mPtr = nullptr; - return ret; - } - - void swap(ComPtr &rhs) noexcept { std::swap(mPtr, rhs.mPtr); } - void swap(ComPtr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); } -}; - - #define MONO SPEAKER_FRONT_CENTER #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT) #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT) diff --git a/alc/backends/wasapi.cpp b/alc/backends/wasapi.cpp index b594aebe..3dd08613 100644 --- a/alc/backends/wasapi.cpp +++ b/alc/backends/wasapi.cpp @@ -59,6 +59,7 @@ #include "alcmain.h" #include "alu.h" #include "compat.h" +#include "comptr.h" #include "converter.h" #include "core/logging.h" #include "ringbuffer.h" @@ -129,69 +130,6 @@ inline uint RefTime2Samples(const ReferenceTime &val, uint srate) } -template -class ComPtr { - T *mPtr{nullptr}; - -public: - ComPtr() noexcept = default; - ComPtr(const ComPtr &rhs) : mPtr{rhs.mPtr} { if(mPtr) mPtr->AddRef(); } - ComPtr(ComPtr&& rhs) noexcept : mPtr{rhs.mPtr} { rhs.mPtr = nullptr; } - ComPtr(std::nullptr_t) noexcept { } - explicit ComPtr(T *ptr) noexcept : mPtr{ptr} { } - ~ComPtr() { if(mPtr) mPtr->Release(); } - - ComPtr& operator=(const ComPtr &rhs) - { - if(!rhs.mPtr) - { - if(mPtr) - mPtr->Release(); - mPtr = nullptr; - } - else - { - rhs.mPtr->AddRef(); - try { - if(mPtr) - mPtr->Release(); - mPtr = rhs.mPtr; - } - catch(...) { - rhs.mPtr->Release(); - throw; - } - } - return *this; - } - ComPtr& operator=(ComPtr&& rhs) - { - if(mPtr) - mPtr->Release(); - mPtr = rhs.mPtr; - rhs.mPtr = nullptr; - return *this; - } - - operator bool() const noexcept { return mPtr != nullptr; } - - T& operator*() const noexcept { return *mPtr; } - T* operator->() const noexcept { return mPtr; } - T* get() const noexcept { return mPtr; } - T** getPtr() noexcept { return &mPtr; } - - T* release() noexcept - { - T *ret{mPtr}; - mPtr = nullptr; - return ret; - } - - void swap(ComPtr &rhs) noexcept { std::swap(mPtr, rhs.mPtr); } - void swap(ComPtr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); } -}; - - class GuidPrinter { char mMsg[64]; diff --git a/common/comptr.h b/common/comptr.h new file mode 100644 index 00000000..c238991a --- /dev/null +++ b/common/comptr.h @@ -0,0 +1,70 @@ +#ifndef COMMON_COMPTR_H +#define COMMON_COMPTR_H + +#include +#include + + +template +class ComPtr { + T *mPtr{nullptr}; + +public: + ComPtr() noexcept = default; + ComPtr(const ComPtr &rhs) : mPtr{rhs.mPtr} { if(mPtr) mPtr->AddRef(); } + ComPtr(ComPtr&& rhs) noexcept : mPtr{rhs.mPtr} { rhs.mPtr = nullptr; } + ComPtr(std::nullptr_t) noexcept { } + explicit ComPtr(T *ptr) noexcept : mPtr{ptr} { } + ~ComPtr() { if(mPtr) mPtr->Release(); } + + ComPtr& operator=(const ComPtr &rhs) + { + if(!rhs.mPtr) + { + if(mPtr) + mPtr->Release(); + mPtr = nullptr; + } + else + { + rhs.mPtr->AddRef(); + try { + if(mPtr) + mPtr->Release(); + mPtr = rhs.mPtr; + } + catch(...) { + rhs.mPtr->Release(); + throw; + } + } + return *this; + } + ComPtr& operator=(ComPtr&& rhs) + { + if(mPtr) + mPtr->Release(); + mPtr = rhs.mPtr; + rhs.mPtr = nullptr; + return *this; + } + + operator bool() const noexcept { return mPtr != nullptr; } + + T& operator*() const noexcept { return *mPtr; } + T* operator->() const noexcept { return mPtr; } + T* get() const noexcept { return mPtr; } + T** getPtr() noexcept { return &mPtr; } + + T* release() noexcept + { + T *ret{mPtr}; + mPtr = nullptr; + return ret; + } + + void swap(ComPtr &rhs) noexcept { std::swap(mPtr, rhs.mPtr); } + void swap(ComPtr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); } +}; + +#endif -- cgit v1.2.3 From c3ee678945872bd895cf4bc3437bd166d0c3b3b6 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 21 Mar 2021 01:47:24 -0700 Subject: Add a utility to decode UHJ sound files to AMB Currently only supports 2-channel UHJ, and the produced .amb files shouldn't be played as normal B-Format (decoded 2-channel UHJ needs to use different shelf filters). --- CMakeLists.txt | 20 +- utils/uhjdecoder.cpp | 515 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 531 insertions(+), 4 deletions(-) create mode 100644 utils/uhjdecoder.cpp (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index cd9e6d9b..f255e942 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1149,10 +1149,13 @@ if(ALSOFT_EMBED_HRTF_DATA) endif() -if(ALSOFT_UTILS AND NOT ALSOFT_NO_CONFIG_UTIL) - find_package(Qt5Widgets) +if(ALSOFT_UTILS) + find_package(MySOFA) + if(NOT ALSOFT_NO_CONFIG_UTIL) + find_package(Qt5Widgets) + endif() endif() -if(ALSOFT_EXAMPLES) +if(ALSOFT_UTILS OR ALSOFT_EXAMPLES) find_package(SndFile) find_package(SDL2) if(SDL2_FOUND) @@ -1430,7 +1433,16 @@ if(ALSOFT_UTILS) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} openal-info) endif() - find_package(MySOFA) + if(SNDFILE_FOUND) + add_executable(uhjdecoder utils/uhjdecoder.cpp) + target_compile_definitions(uhjdecoder PRIVATE ${CPP_DEFS}) + target_include_directories(uhjdecoder + PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common) + target_compile_options(uhjdecoder PRIVATE ${C_FLAGS}) + target_link_libraries(uhjdecoder PUBLIC common + PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG}) + endif() + if(MYSOFA_FOUND) set(SOFA_SUPPORT_SRCS utils/sofa-support.cpp diff --git a/utils/uhjdecoder.cpp b/utils/uhjdecoder.cpp new file mode 100644 index 00000000..3de2e40a --- /dev/null +++ b/utils/uhjdecoder.cpp @@ -0,0 +1,515 @@ +/* + * 2-channel UHJ Decoder + * + * Copyright (c) Chris Robinson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config.h" + +#ifdef HAVE_SSE_INTRINSICS +#include +#elif defined(HAVE_NEON) +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "albit.h" +#include "albyte.h" +#include "alcomplex.h" +#include "almalloc.h" +#include "alspan.h" +#include "opthelpers.h" + +#include "sndfile.h" + +#include "win_main_utf8.h" + + +struct FileDeleter { + void operator()(FILE *file) { fclose(file); } +}; +using FilePtr = std::unique_ptr; + +struct SndFileDeleter { + void operator()(SNDFILE *sndfile) { sf_close(sndfile); } +}; +using SndFilePtr = std::unique_ptr; + + +using ubyte = unsigned char; +using ushort = unsigned short; +using uint = unsigned int; +using complex_d = std::complex; + +using byte4 = std::array; + + +constexpr ubyte SUBTYPE_BFORMAT_FLOAT[]{ + 0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, + 0xca, 0x00, 0x00, 0x00 +}; + +void fwrite16le(ushort val, FILE *f) +{ + ubyte data[2]{ static_cast(val&0xff), static_cast((val>>8)&0xff) }; + fwrite(data, 1, 2, f); +} + +void fwrite32le(uint val, FILE *f) +{ + ubyte data[4]{ static_cast(val&0xff), static_cast((val>>8)&0xff), + static_cast((val>>16)&0xff), static_cast((val>>24)&0xff) }; + fwrite(data, 1, 4, f); +} + +template +byte4 f32AsLEBytes(const float &value) = delete; + +template<> +byte4 f32AsLEBytes(const float &value) +{ + byte4 ret{}; + std::memcpy(ret.data(), &value, 4); + return ret; +} +template<> +byte4 f32AsLEBytes(const float &value) +{ + byte4 ret{}; + std::memcpy(ret.data(), &value, 4); + std::swap(ret[0], ret[3]); + std::swap(ret[1], ret[2]); + return ret; +} + + +constexpr uint BufferLineSize{1024}; + +using FloatBufferLine = std::array; +using FloatBufferSpan = al::span; + + +struct UhjDecoder { + constexpr static size_t sFilterSize{128}; + + alignas(16) std::array mS{}; + alignas(16) std::array mD{}; + + /* History for the FIR filter. */ + alignas(16) std::array mDTHistory{}; + alignas(16) std::array mSHistory{}; + + alignas(16) std::array mTemp{}; + + void decode2(const float *RESTRICT InSamples, FloatBufferLine *OutSamples, + const size_t SamplesToDo); + + DEF_NEWDEL(UhjDecoder) +}; + +/* Same basic filter design as in core/uhjfilter.cpp. */ +template +struct PhaseShifterT { + static_assert((FilterSize&(FilterSize-1)) == 0, "FilterSize needs to be power-of-two"); + + alignas(16) std::array Coeffs{}; + + PhaseShifterT() + { + constexpr size_t fft_size{FilterSize * 2}; + constexpr size_t half_size{fft_size / 2}; + + auto fftBuffer = std::make_unique(fft_size); + std::fill_n(fftBuffer.get(), fft_size, complex_d{}); + fftBuffer[half_size] = 1.0; + + forward_fft({fftBuffer.get(), fft_size}); + for(size_t i{0};i < half_size+1;++i) + fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()}; + for(size_t i{half_size+1};i < fft_size;++i) + fftBuffer[i] = std::conj(fftBuffer[fft_size - i]); + inverse_fft({fftBuffer.get(), fft_size}); + + auto fftiter = fftBuffer.get() + half_size + (FilterSize-1); + for(float &coeff : Coeffs) + { + coeff = static_cast(fftiter->real() / double{fft_size}); + fftiter -= 2; + } + } +}; +const PhaseShifterT PShift{}; + +/* Mostly the same as in core/uhjfilter.cpp, except this overwrites the output + * instead of adding to it. + */ +void allpass_process(al::span dst, const float *RESTRICT src) +{ +#ifdef HAVE_SSE_INTRINSICS + if(size_t todo{dst.size()>>1}) + { + auto *out = reinterpret_cast<__m64*>(dst.data()); + do { + __m128 r04{_mm_setzero_ps()}; + __m128 r14{_mm_setzero_ps()}; + for(size_t j{0};j < PShift.Coeffs.size();j+=4) + { + const __m128 coeffs{_mm_load_ps(&PShift.Coeffs[j])}; + const __m128 s0{_mm_loadu_ps(&src[j*2])}; + const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])}; + + __m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))}; + r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs)); + + s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1)); + r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs)); + } + src += 2; + + __m128 r4{_mm_add_ps(_mm_unpackhi_ps(r04, r14), _mm_unpacklo_ps(r04, r14))}; + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + + _mm_storel_pi(out, r4); + ++out; + } while(--todo); + } + if((dst.size()&1)) + { + __m128 r4{_mm_setzero_ps()}; + for(size_t j{0};j < PShift.Coeffs.size();j+=4) + { + const __m128 coeffs{_mm_load_ps(&PShift.Coeffs[j])}; + const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; + r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs)); + } + r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + + dst.back() = _mm_cvtss_f32(r4); + } + +#elif defined(HAVE_NEON) + + size_t pos{0}; + if(size_t todo{dst.size()>>1}) + { + auto shuffle_2020 = [](float32x4_t a, float32x4_t b) + { + float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 0))}; + ret = vsetq_lane_f32(vgetq_lane_f32(a, 2), ret, 1); + ret = vsetq_lane_f32(vgetq_lane_f32(b, 0), ret, 2); + ret = vsetq_lane_f32(vgetq_lane_f32(b, 2), ret, 3); + return ret; + }; + auto shuffle_3131 = [](float32x4_t a, float32x4_t b) + { + float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 1))}; + ret = vsetq_lane_f32(vgetq_lane_f32(a, 3), ret, 1); + ret = vsetq_lane_f32(vgetq_lane_f32(b, 1), ret, 2); + ret = vsetq_lane_f32(vgetq_lane_f32(b, 3), ret, 3); + return ret; + }; + auto unpacklo = [](float32x4_t a, float32x4_t b) + { + float32x2x2_t result{vzip_f32(vget_low_f32(a), vget_low_f32(b))}; + return vcombine_f32(result.val[0], result.val[1]); + }; + auto unpackhi = [](float32x4_t a, float32x4_t b) + { + float32x2x2_t result{vzip_f32(vget_high_f32(a), vget_high_f32(b))}; + return vcombine_f32(result.val[0], result.val[1]); + }; + do { + float32x4_t r04{vdupq_n_f32(0.0f)}; + float32x4_t r14{vdupq_n_f32(0.0f)}; + for(size_t j{0};j < PShift.Coeffs.size();j+=4) + { + const float32x4_t coeffs{vld1q_f32(&PShift.Coeffs[j])}; + const float32x4_t s0{vld1q_f32(&src[j*2])}; + const float32x4_t s1{vld1q_f32(&src[j*2 + 4])}; + + r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs); + r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs); + } + src += 2; + + float32x4_t r4{vaddq_f32(unpackhi(r04, r14), unpacklo(r04, r14))}; + float32x2_t r2{vadd_f32(vget_low_f32(r4), vget_high_f32(r4))}; + + vst1_f32(&dst[pos], r2); + pos += 2; + } while(--todo); + } + if((dst.size()&1)) + { + auto load4 = [](float32_t a, float32_t b, float32_t c, float32_t d) + { + float32x4_t ret{vmovq_n_f32(a)}; + ret = vsetq_lane_f32(b, ret, 1); + ret = vsetq_lane_f32(c, ret, 2); + ret = vsetq_lane_f32(d, ret, 3); + return ret; + }; + float32x4_t r4{vdupq_n_f32(0.0f)}; + for(size_t j{0};j < PShift.Coeffs.size();j+=4) + { + const float32x4_t coeffs{vld1q_f32(&PShift.Coeffs[j])}; + const float32x4_t s{load4(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; + r4 = vmlaq_f32(r4, s, coeffs); + } + r4 = vaddq_f32(r4, vrev64q_f32(r4)); + dst[pos] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + } + +#else + + for(float &output : dst) + { + float ret{0.0f}; + for(size_t j{0};j < PShift.Coeffs.size();++j) + ret += src[j*2] * PShift.Coeffs[j]; + + output = ret; + ++src; + } +#endif +} + + +/* There is a difference with decoding 2-channel UHJ compared to 3-channel, due + * to 2-channel having lost some of the original signal (which can be recovered + * with 3-channel). The B-Format signal reconstructed from 2-channel UHJ should + * not be run through a normal B-Format decoder, as it needs different shelf + * filters (none? if I understand right, it should do an energy-optimized + * decode only). + * + * 2-channel UHJ decoding is done as: + * + * S = (Left + Right)/2.0 + * D = (Left - Right)/2.0 + * + * W = 0.982*S + j*0.164*D + * X = 0.419*S - j*0.828*D + * Y = 0.763*D + j*0.385*S + * + * where j is a +90 degree phase shift. + */ +void UhjDecoder::decode2(const float *RESTRICT InSamples, FloatBufferLine *OutSamples, + const size_t SamplesToDo) +{ + ASSUME(SamplesToDo > 0); + + float *woutput{OutSamples[0].data()}; + float *xoutput{OutSamples[1].data()}; + float *youtput{OutSamples[2].data()}; + + /* Add a delay to the input mid/side channels, to align it with the + * all-passed signal. + */ + + /* S = (Left + Right)/2.0 */ + for(size_t i{0};i < SamplesToDo;++i) + mS[sFilterSize+i] = (InSamples[i*2 + 0] + InSamples[i*2 + 1]) * 0.5f; + + /* D = (Left - Right)/2.0 */ + for(size_t i{0};i < SamplesToDo;++i) + mD[sFilterSize+i] = (InSamples[i*2 + 0] - InSamples[i*2 + 1]) * 0.5f; + + /* Precompute j*D and store in xoutput. */ + auto tmpiter = std::copy(mDTHistory.cbegin(), mDTHistory.cend(), mTemp.begin()); + std::copy_n(mD.cbegin(), SamplesToDo+sFilterSize, tmpiter); + std::copy_n(mTemp.cbegin()+SamplesToDo, mDTHistory.size(), mDTHistory.begin()); + allpass_process({xoutput, SamplesToDo}, mTemp.data()); + + for(size_t i{0};i < SamplesToDo;++i) + { + /* W = 0.982*S + j*0.164*D */ + woutput[i] = 0.982f*mS[i] + 0.164f*xoutput[i]; + /* X = 0.419*S - j*0.828*D */ + xoutput[i] = 0.419f*mS[i] - 0.828f*xoutput[i]; + } + + /* Precompute j*S and store in youtput. */ + tmpiter = std::copy(mSHistory.cbegin(), mSHistory.cend(), mTemp.begin()); + std::copy_n(mS.cbegin(), SamplesToDo+sFilterSize, tmpiter); + std::copy_n(mTemp.cbegin()+SamplesToDo, mSHistory.size(), mSHistory.begin()); + allpass_process({youtput, SamplesToDo}, mTemp.data()); + + for(size_t i{0};i < SamplesToDo;++i) + { + /* Y = 0.763*D + j*0.385*S */ + youtput[i] = 0.763f*mD[i] + 0.385f*youtput[i]; + } + + std::copy(mS.begin()+SamplesToDo, mS.begin()+SamplesToDo+sFilterSize, mS.begin()); + std::copy(mD.begin()+SamplesToDo, mD.begin()+SamplesToDo+sFilterSize, mD.begin()); +} + + +int main(int argc, char **argv) +{ + if(argc < 2 || std::strcmp(argv[1], "-h") == 0 || std::strcmp(argv[1], "--help") == 0) + { + printf("Usage: %s \n", argv[0]); + return 1; + } + + size_t num_files{0}, num_decoded{0}; + for(int fidx{1};fidx < argc;++fidx) + { + ++num_files; + SF_INFO ininfo{}; + SndFilePtr infile{sf_open(argv[fidx], SFM_READ, &ininfo)}; + if(!infile) + { + fprintf(stderr, "Failed to open %s\n", argv[fidx]); + continue; + } + if(ininfo.channels != 2) + { + fprintf(stderr, "%s is not a stereo file\n", argv[fidx]); + continue; + } + printf("Converting %s...\n", argv[fidx]); + + std::string outname{argv[fidx]}; + auto lastslash = outname.find_last_of('/'); + if(lastslash != std::string::npos) + outname.erase(0, lastslash+1); + auto lastdot = outname.find_last_of('.'); + if(lastdot != std::string::npos) + outname.resize(lastdot+1); + outname += "amb"; + + FilePtr outfile{fopen(outname.c_str(), "wb")}; + if(!outfile) + { + fprintf(stderr, "Failed to create %s\n", outname.c_str()); + continue; + } + + fputs("RIFF", outfile.get()); + fwrite32le(0xFFFFFFFF, outfile.get()); // 'RIFF' header len; filled in at close + + fputs("WAVE", outfile.get()); + + fputs("fmt ", outfile.get()); + fwrite32le(40, outfile.get()); // 'fmt ' header len; 40 bytes for EXTENSIBLE + + // 16-bit val, format type id (extensible: 0xFFFE) + fwrite16le(0xFFFE, outfile.get()); + // 16-bit val, channel count + fwrite16le(static_cast(3), outfile.get()); + // 32-bit val, frequency + fwrite32le(static_cast(ininfo.samplerate), outfile.get()); + // 32-bit val, bytes per second + fwrite32le(static_cast(ininfo.samplerate) * sizeof(float) * 3, outfile.get()); + // 16-bit val, frame size + fwrite16le(static_cast(sizeof(float) * 3), outfile.get()); + // 16-bit val, bits per sample + fwrite16le(static_cast(sizeof(float) * 8), outfile.get()); + // 16-bit val, extra byte count + fwrite16le(22, outfile.get()); + // 16-bit val, valid bits per sample + fwrite16le(static_cast(sizeof(float) * 8), outfile.get()); + // 32-bit val, channel mask + fwrite32le(0, outfile.get()); + // 16 byte GUID, sub-type format + fwrite(SUBTYPE_BFORMAT_FLOAT, 1, 16, outfile.get()); + + fputs("data", outfile.get()); + fwrite32le(0xFFFFFFFF, outfile.get()); // 'data' header len; filled in at close + if(ferror(outfile.get())) + { + fprintf(stderr, "Error writing wave file header: %s (%d)\n", strerror(errno), errno); + continue; + } + + auto DataStart = ftell(outfile.get()); + + auto decoder = std::make_unique(); + auto inmem = std::make_unique(BufferLineSize*static_cast(ininfo.channels)); + auto decmem = std::make_unique[]>(3); + auto outmem = std::make_unique(BufferLineSize*3); + + /* The all-pass filter has a lead-in of 127 samples, and a lead-out of + * 128 samples. So after reading the last samples from the input, an + * additional 255 samples of silence need to be fed through the decoder + * for it to finish. + */ + sf_count_t LeadOut{UhjDecoder::sFilterSize*2 - 1}; + while(LeadOut > 0) + { + sf_count_t sgot{sf_readf_float(infile.get(), inmem.get(), BufferLineSize)}; + sgot = std::max(sgot, 0); + if(sgot < BufferLineSize) + { + const sf_count_t remaining{std::min(BufferLineSize - sgot, LeadOut)}; + std::fill_n(inmem.get() + sgot*2, remaining*2, 0.0f); + sgot += remaining; + LeadOut -= remaining; + } + + auto got = static_cast(sgot); + decoder->decode2(inmem.get(), decmem.get(), got); + for(size_t i{0};i < got;++i) + { + outmem[i*3 + 0] = f32AsLEBytes(decmem[0][i]); + outmem[i*3 + 1] = f32AsLEBytes(decmem[1][i]); + outmem[i*3 + 2] = f32AsLEBytes(decmem[2][i]); + } + + size_t wrote{fwrite(outmem.get(), sizeof(byte4)*3, got, outfile.get())}; + if(wrote < got) + { + fprintf(stderr, "Error writing wave data: %s (%d)\n", strerror(errno), errno); + break; + } + } + + auto DataEnd = ftell(outfile.get()); + if(DataEnd > DataStart) + { + long dataLen{DataEnd - DataStart}; + if(fseek(outfile.get(), 4, SEEK_SET) == 0) + fwrite32le(static_cast(DataEnd-8), outfile.get()); // 'WAVE' header len + if(fseek(outfile.get(), DataStart-4, SEEK_SET) == 0) + fwrite32le(static_cast(dataLen), outfile.get()); // 'data' header len + } + fflush(outfile.get()); + ++num_decoded; + } + if(num_decoded == 0) + fprintf(stderr, "Failed to decode any input files\n"); + else if(num_decoded < num_files) + fprintf(stderr, "Decoded %zu of %zu files\n", num_decoded, num_files); + else + printf("Decoded %zu file%s\n", num_decoded, (num_decoded==1)?"":"s"); + return 0; +} -- cgit v1.2.3 From 819e0297fff72fab0745985db8640d2652437189 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 28 Mar 2021 01:47:27 -0700 Subject: Add the export definitions to the library projects Instead of the config.h header. --- CMakeLists.txt | 6 ++++-- config.h.in | 4 ---- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index f255e942..954753f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1236,7 +1236,8 @@ else() router/al.cpp ) target_compile_definitions(OpenAL - PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES ${CPP_DEFS}) + PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" + "AL_API=${EXPORT_DECL}" ${CPP_DEFS}) target_compile_options(OpenAL PRIVATE ${C_FLAGS}) target_link_libraries(OpenAL PRIVATE common ${LINKER_FLAGS}) target_include_directories(OpenAL @@ -1321,7 +1322,8 @@ set_target_properties(${IMPL_TARGET} PROPERTIES OUTPUT_NAME ${LIBNAME} SOVERSION ${LIB_MAJOR_VERSION} ) target_compile_definitions(${IMPL_TARGET} - PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES ${CPP_DEFS}) + PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}" + ${CPP_DEFS}) target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS}) if(TARGET build_version) diff --git a/config.h.in b/config.h.in index a28204ef..f43b3f73 100644 --- a/config.h.in +++ b/config.h.in @@ -1,7 +1,3 @@ -/* API declaration export attribute */ -#define AL_API ${EXPORT_DECL} -#define ALC_API ${EXPORT_DECL} - /* Define if HRTF data is embedded in the library */ #cmakedefine ALSOFT_EMBED_HRTF_DATA -- cgit v1.2.3 From 8ab5e5dba253d1609423b8e3625655d7b1937584 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 28 Mar 2021 05:43:10 -0700 Subject: Move the UHJ phase shifter to a common header --- CMakeLists.txt | 1 + alc/alc.cpp | 2 +- common/phase_shifter.h | 347 +++++++++++++++++++++++++++++++++++++++++++++++++ core/uhjfilter.cpp | 202 +--------------------------- core/uhjfilter.h | 15 +-- utils/uhjdecoder.cpp | 227 +++++--------------------------- 6 files changed, 394 insertions(+), 400 deletions(-) create mode 100644 common/phase_shifter.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 954753f9..7e4f06ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -627,6 +627,7 @@ set(COMMON_OBJS common/intrusive_ptr.h common/math_defs.h common/opthelpers.h + common/phase_shifter.h common/polyphase_resampler.cpp common/polyphase_resampler.h common/pragmadefs.h diff --git a/alc/alc.cpp b/alc/alc.cpp index 0cd20f62..2de0b47e 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -2001,7 +2001,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) nanoseconds::rep sample_delay{0}; if(device->Uhj_Encoder) - sample_delay += Uhj2Encoder::sFilterSize; + sample_delay += Uhj2Encoder::sFilterDelay; if(device->mHrtfState) sample_delay += HrtfDirectDelay; if(auto *ambidec = device->AmbiDecoder.get()) diff --git a/common/phase_shifter.h b/common/phase_shifter.h new file mode 100644 index 00000000..18ab34c7 --- /dev/null +++ b/common/phase_shifter.h @@ -0,0 +1,347 @@ +#ifndef PHASE_SHIFTER_H +#define PHASE_SHIFTER_H + +#ifdef HAVE_SSE_INTRINSICS +#include +#elif defined(HAVE_NEON) +#include +#endif + +#include +#include + +#include "alcomplex.h" +#include "alspan.h" + + +/* Implements a wide-band +90 degree phase-shift. Note that this should be + * given one sample less of a delay (FilterSize/2 - 1) compared to the direct + * signal delay (FilterSize/2) to properly align. + */ +template +struct PhaseShifterT { + static_assert(FilterSize >= 16, "FilterSize needs to be at least 16"); + static_assert((FilterSize&(FilterSize-1)) == 0, "FilterSize needs to be power-of-two"); + + alignas(16) std::array mCoeffs{}; + + /* Some notes on this filter construction. + * + * A wide-band phase-shift filter needs a delay to maintain linearity. A + * dirac impulse in the center of a time-domain buffer represents a filter + * passing all frequencies through as-is with a pure delay. Converting that + * to the frequency domain, adjusting the phase of each frequency bin by + * +90 degrees, then converting back to the time domain, results in a FIR + * filter that applies a +90 degree wide-band phase-shift. + * + * A particularly notable aspect of the time-domain filter response is that + * every other coefficient is 0. This allows doubling the effective size of + * the filter, by storing only the non-0 coefficients and double-stepping + * over the input to apply it. + * + * Additionally, the resulting filter is independent of the sample rate. + * The same filter can be applied regardless of the device's sample rate + * and achieve the same effect. + */ + PhaseShifterT() + { + using complex_d = std::complex; + constexpr size_t fft_size{FilterSize}; + constexpr size_t half_size{fft_size / 2}; + + auto fftBuffer = std::make_unique(fft_size); + std::fill_n(fftBuffer.get(), fft_size, complex_d{}); + fftBuffer[half_size] = 1.0; + + forward_fft({fftBuffer.get(), fft_size}); + for(size_t i{0};i < half_size+1;++i) + fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()}; + for(size_t i{half_size+1};i < fft_size;++i) + fftBuffer[i] = std::conj(fftBuffer[fft_size - i]); + inverse_fft({fftBuffer.get(), fft_size}); + + auto fftiter = fftBuffer.get() + half_size + (FilterSize/2 - 1); + for(float &coeff : mCoeffs) + { + coeff = static_cast(fftiter->real() / double{fft_size}); + fftiter -= 2; + } + } + + void process(al::span dst, const float *RESTRICT src) const; + void processAccum(al::span dst, const float *RESTRICT src) const; +}; + +template +inline void PhaseShifterT::process(al::span dst, const float *RESTRICT src) const +{ +#ifdef HAVE_SSE_INTRINSICS + if(size_t todo{dst.size()>>1}) + { + auto *out = reinterpret_cast<__m64*>(dst.data()); + do { + __m128 r04{_mm_setzero_ps()}; + __m128 r14{_mm_setzero_ps()}; + for(size_t j{0};j < mCoeffs.size();j+=4) + { + const __m128 coeffs{_mm_load_ps(&mCoeffs[j])}; + const __m128 s0{_mm_loadu_ps(&src[j*2])}; + const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])}; + + __m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))}; + r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs)); + + s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1)); + r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs)); + } + src += 2; + + __m128 r4{_mm_add_ps(_mm_unpackhi_ps(r04, r14), _mm_unpacklo_ps(r04, r14))}; + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + + _mm_storel_pi(out, r4); + ++out; + } while(--todo); + } + if((dst.size()&1)) + { + __m128 r4{_mm_setzero_ps()}; + for(size_t j{0};j < mCoeffs.size();j+=4) + { + const __m128 coeffs{_mm_load_ps(&mCoeffs[j])}; + const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; + r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs)); + } + r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + + dst.back() = _mm_cvtss_f32(r4); + } + +#elif defined(HAVE_NEON) + + size_t pos{0}; + if(size_t todo{dst.size()>>1}) + { + /* There doesn't seem to be NEON intrinsics to do this kind of stipple + * shuffling, so there's two custom methods for it. + */ + auto shuffle_2020 = [](float32x4_t a, float32x4_t b) + { + float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 0))}; + ret = vsetq_lane_f32(vgetq_lane_f32(a, 2), ret, 1); + ret = vsetq_lane_f32(vgetq_lane_f32(b, 0), ret, 2); + ret = vsetq_lane_f32(vgetq_lane_f32(b, 2), ret, 3); + return ret; + }; + auto shuffle_3131 = [](float32x4_t a, float32x4_t b) + { + float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 1))}; + ret = vsetq_lane_f32(vgetq_lane_f32(a, 3), ret, 1); + ret = vsetq_lane_f32(vgetq_lane_f32(b, 1), ret, 2); + ret = vsetq_lane_f32(vgetq_lane_f32(b, 3), ret, 3); + return ret; + }; + auto unpacklo = [](float32x4_t a, float32x4_t b) + { + float32x2x2_t result{vzip_f32(vget_low_f32(a), vget_low_f32(b))}; + return vcombine_f32(result.val[0], result.val[1]); + }; + auto unpackhi = [](float32x4_t a, float32x4_t b) + { + float32x2x2_t result{vzip_f32(vget_high_f32(a), vget_high_f32(b))}; + return vcombine_f32(result.val[0], result.val[1]); + }; + do { + float32x4_t r04{vdupq_n_f32(0.0f)}; + float32x4_t r14{vdupq_n_f32(0.0f)}; + for(size_t j{0};j < mCoeffs.size();j+=4) + { + const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])}; + const float32x4_t s0{vld1q_f32(&src[j*2])}; + const float32x4_t s1{vld1q_f32(&src[j*2 + 4])}; + + r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs); + r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs); + } + src += 2; + + float32x4_t r4{vaddq_f32(unpackhi(r04, r14), unpacklo(r04, r14))}; + float32x2_t r2{vadd_f32(vget_low_f32(r4), vget_high_f32(r4))}; + + vst1_f32(&dst[pos], r2); + pos += 2; + } while(--todo); + } + if((dst.size()&1)) + { + auto load4 = [](float32_t a, float32_t b, float32_t c, float32_t d) + { + float32x4_t ret{vmovq_n_f32(a)}; + ret = vsetq_lane_f32(b, ret, 1); + ret = vsetq_lane_f32(c, ret, 2); + ret = vsetq_lane_f32(d, ret, 3); + return ret; + }; + float32x4_t r4{vdupq_n_f32(0.0f)}; + for(size_t j{0};j < mCoeffs.size();j+=4) + { + const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])}; + const float32x4_t s{load4(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; + r4 = vmlaq_f32(r4, s, coeffs); + } + r4 = vaddq_f32(r4, vrev64q_f32(r4)); + dst[pos] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + } + +#else + + for(float &output : dst) + { + float ret{0.0f}; + for(size_t j{0};j < mCoeffs.size();++j) + ret += src[j*2] * mCoeffs[j]; + + output = ret; + ++src; + } +#endif +} + +template +inline void PhaseShifterT::processAccum(al::span dst, const float *RESTRICT src) const +{ +#ifdef HAVE_SSE_INTRINSICS + if(size_t todo{dst.size()>>1}) + { + auto *out = reinterpret_cast<__m64*>(dst.data()); + do { + __m128 r04{_mm_setzero_ps()}; + __m128 r14{_mm_setzero_ps()}; + for(size_t j{0};j < mCoeffs.size();j+=4) + { + const __m128 coeffs{_mm_load_ps(&mCoeffs[j])}; + const __m128 s0{_mm_loadu_ps(&src[j*2])}; + const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])}; + + __m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))}; + r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs)); + + s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1)); + r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs)); + } + src += 2; + + __m128 r4{_mm_add_ps(_mm_unpackhi_ps(r04, r14), _mm_unpacklo_ps(r04, r14))}; + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + + _mm_storel_pi(out, _mm_add_ps(_mm_loadl_pi(_mm_undefined_ps(), out), r4)); + ++out; + } while(--todo); + } + if((dst.size()&1)) + { + __m128 r4{_mm_setzero_ps()}; + for(size_t j{0};j < mCoeffs.size();j+=4) + { + const __m128 coeffs{_mm_load_ps(&mCoeffs[j])}; + /* NOTE: This could alternatively be done with two unaligned loads + * and a shuffle. Which would be better? + */ + const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; + r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs)); + } + r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + + dst.back() += _mm_cvtss_f32(r4); + } + +#elif defined(HAVE_NEON) + + size_t pos{0}; + if(size_t todo{dst.size()>>1}) + { + auto shuffle_2020 = [](float32x4_t a, float32x4_t b) + { + float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 0))}; + ret = vsetq_lane_f32(vgetq_lane_f32(a, 2), ret, 1); + ret = vsetq_lane_f32(vgetq_lane_f32(b, 0), ret, 2); + ret = vsetq_lane_f32(vgetq_lane_f32(b, 2), ret, 3); + return ret; + }; + auto shuffle_3131 = [](float32x4_t a, float32x4_t b) + { + float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 1))}; + ret = vsetq_lane_f32(vgetq_lane_f32(a, 3), ret, 1); + ret = vsetq_lane_f32(vgetq_lane_f32(b, 1), ret, 2); + ret = vsetq_lane_f32(vgetq_lane_f32(b, 3), ret, 3); + return ret; + }; + auto unpacklo = [](float32x4_t a, float32x4_t b) + { + float32x2x2_t result{vzip_f32(vget_low_f32(a), vget_low_f32(b))}; + return vcombine_f32(result.val[0], result.val[1]); + }; + auto unpackhi = [](float32x4_t a, float32x4_t b) + { + float32x2x2_t result{vzip_f32(vget_high_f32(a), vget_high_f32(b))}; + return vcombine_f32(result.val[0], result.val[1]); + }; + do { + float32x4_t r04{vdupq_n_f32(0.0f)}; + float32x4_t r14{vdupq_n_f32(0.0f)}; + for(size_t j{0};j < mCoeffs.size();j+=4) + { + const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])}; + const float32x4_t s0{vld1q_f32(&src[j*2])}; + const float32x4_t s1{vld1q_f32(&src[j*2 + 4])}; + + r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs); + r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs); + } + src += 2; + + float32x4_t r4{vaddq_f32(unpackhi(r04, r14), unpacklo(r04, r14))}; + float32x2_t r2{vadd_f32(vget_low_f32(r4), vget_high_f32(r4))}; + + vst1_f32(&dst[pos], vadd_f32(vld1_f32(&dst[pos]), r2)); + pos += 2; + } while(--todo); + } + if((dst.size()&1)) + { + auto load4 = [](float32_t a, float32_t b, float32_t c, float32_t d) + { + float32x4_t ret{vmovq_n_f32(a)}; + ret = vsetq_lane_f32(b, ret, 1); + ret = vsetq_lane_f32(c, ret, 2); + ret = vsetq_lane_f32(d, ret, 3); + return ret; + }; + float32x4_t r4{vdupq_n_f32(0.0f)}; + for(size_t j{0};j < mCoeffs.size();j+=4) + { + const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])}; + const float32x4_t s{load4(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; + r4 = vmlaq_f32(r4, s, coeffs); + } + r4 = vaddq_f32(r4, vrev64q_f32(r4)); + dst[pos] += vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + } + +#else + + for(float &output : dst) + { + float ret{0.0f}; + for(size_t j{0};j < mCoeffs.size();++j) + ret += src[j*2] * mCoeffs[j]; + + output += ret; + ++src; + } +#endif +} + +#endif /* PHASE_SHIFTER_H */ diff --git a/core/uhjfilter.cpp b/core/uhjfilter.cpp index 5cb7d0ab..353cb545 100644 --- a/core/uhjfilter.cpp +++ b/core/uhjfilter.cpp @@ -15,202 +15,14 @@ #include "alcomplex.h" #include "alnumeric.h" #include "opthelpers.h" +#include "phase_shifter.h" namespace { using complex_d = std::complex; -struct PhaseShifterT { - alignas(16) std::array Coeffs; - - /* Some notes on this filter construction. - * - * A wide-band phase-shift filter needs a delay to maintain linearity. A - * dirac impulse in the center of a time-domain buffer represents a filter - * passing all frequencies through as-is with a pure delay. Converting that - * to the frequency domain, adjusting the phase of each frequency bin by - * +90 degrees, then converting back to the time domain, results in a FIR - * filter that applies a +90 degree wide-band phase-shift. - * - * A particularly notable aspect of the time-domain filter response is that - * every other coefficient is 0. This allows doubling the effective size of - * the filter, by storing only the non-0 coefficients and double-stepping - * over the input to apply it. - * - * Additionally, the resulting filter is independent of the sample rate. - * The same filter can be applied regardless of the device's sample rate - * and achieve the same effect. - */ - PhaseShifterT() - { - constexpr size_t fft_size{Uhj2Encoder::sFilterSize * 2}; - constexpr size_t half_size{fft_size / 2}; - - /* Generate a frequency domain impulse with a +90 degree phase offset. - * Reconstruct the mirrored frequencies to convert to the time domain. - */ - auto fftBuffer = std::make_unique(fft_size); - std::fill_n(fftBuffer.get(), fft_size, complex_d{}); - fftBuffer[half_size] = 1.0; - - forward_fft({fftBuffer.get(), fft_size}); - for(size_t i{0};i < half_size+1;++i) - fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()}; - for(size_t i{half_size+1};i < fft_size;++i) - fftBuffer[i] = std::conj(fftBuffer[fft_size - i]); - inverse_fft({fftBuffer.get(), fft_size}); - - /* Reverse the filter for simpler processing, and store only the non-0 - * coefficients. - */ - auto fftiter = fftBuffer.get() + half_size + (Uhj2Encoder::sFilterSize-1); - for(float &coeff : Coeffs) - { - coeff = static_cast(fftiter->real() / double{fft_size}); - fftiter -= 2; - } - } -}; -const PhaseShifterT PShift{}; - -void allpass_process(al::span dst, const float *RESTRICT src) -{ -#ifdef HAVE_SSE_INTRINSICS - if(size_t todo{dst.size()>>1}) - { - auto *out = reinterpret_cast<__m64*>(dst.data()); - do { - __m128 r04{_mm_setzero_ps()}; - __m128 r14{_mm_setzero_ps()}; - for(size_t j{0};j < PShift.Coeffs.size();j+=4) - { - const __m128 coeffs{_mm_load_ps(&PShift.Coeffs[j])}; - const __m128 s0{_mm_loadu_ps(&src[j*2])}; - const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])}; - - __m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))}; - r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs)); - - s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1)); - r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs)); - } - src += 2; - - __m128 r4{_mm_add_ps(_mm_unpackhi_ps(r04, r14), _mm_unpacklo_ps(r04, r14))}; - r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - - _mm_storel_pi(out, _mm_add_ps(_mm_loadl_pi(_mm_undefined_ps(), out), r4)); - ++out; - } while(--todo); - } - if((dst.size()&1)) - { - __m128 r4{_mm_setzero_ps()}; - for(size_t j{0};j < PShift.Coeffs.size();j+=4) - { - const __m128 coeffs{_mm_load_ps(&PShift.Coeffs[j])}; - /* NOTE: This could alternatively be done with two unaligned loads - * and a shuffle. Which would be better? - */ - const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; - r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs)); - } - r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); - r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - - dst.back() += _mm_cvtss_f32(r4); - } - -#elif defined(HAVE_NEON) - - size_t pos{0}; - if(size_t todo{dst.size()>>1}) - { - /* There doesn't seem to be NEON intrinsics to do this kind of stipple - * shuffling, so there's two custom methods for it. - */ - auto shuffle_2020 = [](float32x4_t a, float32x4_t b) - { - float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 0))}; - ret = vsetq_lane_f32(vgetq_lane_f32(a, 2), ret, 1); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 0), ret, 2); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 2), ret, 3); - return ret; - }; - auto shuffle_3131 = [](float32x4_t a, float32x4_t b) - { - float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 1))}; - ret = vsetq_lane_f32(vgetq_lane_f32(a, 3), ret, 1); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 1), ret, 2); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 3), ret, 3); - return ret; - }; - auto unpacklo = [](float32x4_t a, float32x4_t b) - { - float32x2x2_t result{vzip_f32(vget_low_f32(a), vget_low_f32(b))}; - return vcombine_f32(result.val[0], result.val[1]); - }; - auto unpackhi = [](float32x4_t a, float32x4_t b) - { - float32x2x2_t result{vzip_f32(vget_high_f32(a), vget_high_f32(b))}; - return vcombine_f32(result.val[0], result.val[1]); - }; - do { - float32x4_t r04{vdupq_n_f32(0.0f)}; - float32x4_t r14{vdupq_n_f32(0.0f)}; - for(size_t j{0};j < PShift.Coeffs.size();j+=4) - { - const float32x4_t coeffs{vld1q_f32(&PShift.Coeffs[j])}; - const float32x4_t s0{vld1q_f32(&src[j*2])}; - const float32x4_t s1{vld1q_f32(&src[j*2 + 4])}; - - r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs); - r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs); - } - src += 2; - - float32x4_t r4{vaddq_f32(unpackhi(r04, r14), unpacklo(r04, r14))}; - float32x2_t r2{vadd_f32(vget_low_f32(r4), vget_high_f32(r4))}; - - vst1_f32(&dst[pos], vadd_f32(vld1_f32(&dst[pos]), r2)); - pos += 2; - } while(--todo); - } - if((dst.size()&1)) - { - auto load4 = [](float32_t a, float32_t b, float32_t c, float32_t d) - { - float32x4_t ret{vmovq_n_f32(a)}; - ret = vsetq_lane_f32(b, ret, 1); - ret = vsetq_lane_f32(c, ret, 2); - ret = vsetq_lane_f32(d, ret, 3); - return ret; - }; - float32x4_t r4{vdupq_n_f32(0.0f)}; - for(size_t j{0};j < PShift.Coeffs.size();j+=4) - { - const float32x4_t coeffs{vld1q_f32(&PShift.Coeffs[j])}; - const float32x4_t s{load4(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; - r4 = vmlaq_f32(r4, s, coeffs); - } - r4 = vaddq_f32(r4, vrev64q_f32(r4)); - dst[pos] += vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); - } - -#else - - for(float &output : dst) - { - float ret{0.0f}; - for(size_t j{0};j < PShift.Coeffs.size();++j) - ret += src[j*2] * PShift.Coeffs[j]; - - output += ret; - ++src; - } -#endif -} +const PhaseShifterT PShift{}; } // namespace @@ -247,13 +59,13 @@ void Uhj2Encoder::encode(const FloatBufferSpan LeftOut, const FloatBufferSpan Ri /* Combine the previously delayed mid/side signal with the input. */ /* S = 0.9396926*W + 0.1855740*X */ - auto miditer = mMid.begin() + sFilterSize; + auto miditer = mMid.begin() + sFilterDelay; std::transform(winput, winput+SamplesToDo, xinput, miditer, [](const float w, const float x) noexcept -> float { return 0.9396926f*w + 0.1855740f*x; }); /* D = 0.6554516*Y */ - auto sideiter = mSide.begin() + sFilterSize; + auto sideiter = mSide.begin() + sFilterDelay; std::transform(yinput, yinput+SamplesToDo, sideiter, [](const float y) noexcept -> float { return 0.6554516f*y; }); @@ -271,7 +83,7 @@ void Uhj2Encoder::encode(const FloatBufferSpan LeftOut, const FloatBufferSpan Ri [](const float w, const float x) noexcept -> float { return -0.3420201f*w + 0.5098604f*x; }); std::copy_n(mTemp.cbegin()+SamplesToDo, mSideHistory.size(), mSideHistory.begin()); - allpass_process({mSide.data(), SamplesToDo}, mTemp.data()); + PShift.processAccum({mSide.data(), SamplesToDo}, mTemp.data()); /* Left = (S + D)/2.0 */ for(size_t i{0};i < SamplesToDo;i++) @@ -281,6 +93,6 @@ void Uhj2Encoder::encode(const FloatBufferSpan LeftOut, const FloatBufferSpan Ri right[i] = (mMid[i] - mSide[i]) * 0.5f; /* Copy the future samples to the front for next time. */ - std::copy(mMid.cbegin()+SamplesToDo, mMid.cbegin()+SamplesToDo+sFilterSize, mMid.begin()); - std::copy(mSide.cbegin()+SamplesToDo, mSide.cbegin()+SamplesToDo+sFilterSize, mSide.begin()); + std::copy(mMid.cbegin()+SamplesToDo, mMid.cbegin()+SamplesToDo+sFilterDelay, mMid.begin()); + std::copy(mSide.cbegin()+SamplesToDo, mSide.cbegin()+SamplesToDo+sFilterDelay, mSide.begin()); } diff --git a/core/uhjfilter.h b/core/uhjfilter.h index ff794355..d432cec5 100644 --- a/core/uhjfilter.h +++ b/core/uhjfilter.h @@ -8,20 +8,19 @@ struct Uhj2Encoder { - /* A particular property of the filter allows it to cover nearly twice its - * length, so the filter size is also the effective delay (despite being - * center-aligned). + /* The filter delay is half it's effective size, so a delay of 128 has a + * FIR length of 256. */ - constexpr static size_t sFilterSize{128}; + constexpr static size_t sFilterDelay{128}; /* Delays and processing storage for the unfiltered signal. */ - alignas(16) std::array mMid{}; - alignas(16) std::array mSide{}; + alignas(16) std::array mMid{}; + alignas(16) std::array mSide{}; /* History for the FIR filter. */ - alignas(16) std::array mSideHistory{}; + alignas(16) std::array mSideHistory{}; - alignas(16) std::array mTemp{}; + alignas(16) std::array mTemp{}; /** * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input diff --git a/utils/uhjdecoder.cpp b/utils/uhjdecoder.cpp index 5572b690..1efed0dd 100644 --- a/utils/uhjdecoder.cpp +++ b/utils/uhjdecoder.cpp @@ -46,6 +46,7 @@ #include "alspan.h" #include "vector.h" #include "opthelpers.h" +#include "phase_shifter.h" #include "sndfile.h" @@ -117,18 +118,18 @@ using FloatBufferSpan = al::span; struct UhjDecoder { - constexpr static size_t sFilterSize{128}; + constexpr static size_t sFilterDelay{128}; - alignas(16) std::array mS{}; - alignas(16) std::array mD{}; - alignas(16) std::array mT{}; - alignas(16) std::array mQ{}; + alignas(16) std::array mS{}; + alignas(16) std::array mD{}; + alignas(16) std::array mT{}; + alignas(16) std::array mQ{}; /* History for the FIR filter. */ - alignas(16) std::array mDTHistory{}; - alignas(16) std::array mSHistory{}; + alignas(16) std::array mDTHistory{}; + alignas(16) std::array mSHistory{}; - alignas(16) std::array mTemp{}; + alignas(16) std::array mTemp{}; void decode(const float *RESTRICT InSamples, const size_t InChannels, const al::span OutSamples, const size_t SamplesToDo); @@ -138,173 +139,7 @@ struct UhjDecoder { DEF_NEWDEL(UhjDecoder) }; -/* Same basic filter design as in core/uhjfilter.cpp. */ -template -struct PhaseShifterT { - static_assert((FilterSize&(FilterSize-1)) == 0, "FilterSize needs to be power-of-two"); - - alignas(16) std::array Coeffs{}; - - PhaseShifterT() - { - constexpr size_t fft_size{FilterSize * 2}; - constexpr size_t half_size{fft_size / 2}; - - auto fftBuffer = std::make_unique(fft_size); - std::fill_n(fftBuffer.get(), fft_size, complex_d{}); - fftBuffer[half_size] = 1.0; - - forward_fft({fftBuffer.get(), fft_size}); - for(size_t i{0};i < half_size+1;++i) - fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()}; - for(size_t i{half_size+1};i < fft_size;++i) - fftBuffer[i] = std::conj(fftBuffer[fft_size - i]); - inverse_fft({fftBuffer.get(), fft_size}); - - auto fftiter = fftBuffer.get() + half_size + (FilterSize-1); - for(float &coeff : Coeffs) - { - coeff = static_cast(fftiter->real() / double{fft_size}); - fftiter -= 2; - } - } -}; -const PhaseShifterT PShift{}; - -/* Mostly the same as in core/uhjfilter.cpp, except this overwrites the output - * instead of adding to it. - */ -void allpass_process(al::span dst, const float *RESTRICT src) -{ -#ifdef HAVE_SSE_INTRINSICS - if(size_t todo{dst.size()>>1}) - { - auto *out = reinterpret_cast<__m64*>(dst.data()); - do { - __m128 r04{_mm_setzero_ps()}; - __m128 r14{_mm_setzero_ps()}; - for(size_t j{0};j < PShift.Coeffs.size();j+=4) - { - const __m128 coeffs{_mm_load_ps(&PShift.Coeffs[j])}; - const __m128 s0{_mm_loadu_ps(&src[j*2])}; - const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])}; - - __m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))}; - r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs)); - - s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1)); - r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs)); - } - src += 2; - - __m128 r4{_mm_add_ps(_mm_unpackhi_ps(r04, r14), _mm_unpacklo_ps(r04, r14))}; - r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - - _mm_storel_pi(out, r4); - ++out; - } while(--todo); - } - if((dst.size()&1)) - { - __m128 r4{_mm_setzero_ps()}; - for(size_t j{0};j < PShift.Coeffs.size();j+=4) - { - const __m128 coeffs{_mm_load_ps(&PShift.Coeffs[j])}; - const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; - r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs)); - } - r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); - r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - - dst.back() = _mm_cvtss_f32(r4); - } - -#elif defined(HAVE_NEON) - - size_t pos{0}; - if(size_t todo{dst.size()>>1}) - { - auto shuffle_2020 = [](float32x4_t a, float32x4_t b) - { - float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 0))}; - ret = vsetq_lane_f32(vgetq_lane_f32(a, 2), ret, 1); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 0), ret, 2); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 2), ret, 3); - return ret; - }; - auto shuffle_3131 = [](float32x4_t a, float32x4_t b) - { - float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 1))}; - ret = vsetq_lane_f32(vgetq_lane_f32(a, 3), ret, 1); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 1), ret, 2); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 3), ret, 3); - return ret; - }; - auto unpacklo = [](float32x4_t a, float32x4_t b) - { - float32x2x2_t result{vzip_f32(vget_low_f32(a), vget_low_f32(b))}; - return vcombine_f32(result.val[0], result.val[1]); - }; - auto unpackhi = [](float32x4_t a, float32x4_t b) - { - float32x2x2_t result{vzip_f32(vget_high_f32(a), vget_high_f32(b))}; - return vcombine_f32(result.val[0], result.val[1]); - }; - do { - float32x4_t r04{vdupq_n_f32(0.0f)}; - float32x4_t r14{vdupq_n_f32(0.0f)}; - for(size_t j{0};j < PShift.Coeffs.size();j+=4) - { - const float32x4_t coeffs{vld1q_f32(&PShift.Coeffs[j])}; - const float32x4_t s0{vld1q_f32(&src[j*2])}; - const float32x4_t s1{vld1q_f32(&src[j*2 + 4])}; - - r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs); - r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs); - } - src += 2; - - float32x4_t r4{vaddq_f32(unpackhi(r04, r14), unpacklo(r04, r14))}; - float32x2_t r2{vadd_f32(vget_low_f32(r4), vget_high_f32(r4))}; - - vst1_f32(&dst[pos], r2); - pos += 2; - } while(--todo); - } - if((dst.size()&1)) - { - auto load4 = [](float32_t a, float32_t b, float32_t c, float32_t d) - { - float32x4_t ret{vmovq_n_f32(a)}; - ret = vsetq_lane_f32(b, ret, 1); - ret = vsetq_lane_f32(c, ret, 2); - ret = vsetq_lane_f32(d, ret, 3); - return ret; - }; - float32x4_t r4{vdupq_n_f32(0.0f)}; - for(size_t j{0};j < PShift.Coeffs.size();j+=4) - { - const float32x4_t coeffs{vld1q_f32(&PShift.Coeffs[j])}; - const float32x4_t s{load4(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; - r4 = vmlaq_f32(r4, s, coeffs); - } - r4 = vaddq_f32(r4, vrev64q_f32(r4)); - dst[pos] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); - } - -#else - - for(float &output : dst) - { - float ret{0.0f}; - for(size_t j{0};j < PShift.Coeffs.size();++j) - ret += src[j*2] * PShift.Coeffs[j]; - - output = ret; - ++src; - } -#endif -} +const PhaseShifterT PShift{}; /* Decoding UHJ is done as: @@ -395,31 +230,31 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels /* S = Left + Right */ for(size_t i{0};i < SamplesToDo;++i) - mS[sFilterSize+i] = InSamples[i*InChannels + 0] + InSamples[i*InChannels + 1]; + mS[sFilterDelay+i] = InSamples[i*InChannels + 0] + InSamples[i*InChannels + 1]; /* D = Left - Right */ for(size_t i{0};i < SamplesToDo;++i) - mD[sFilterSize+i] = InSamples[i*InChannels + 0] - InSamples[i*InChannels + 1]; + mD[sFilterDelay+i] = InSamples[i*InChannels + 0] - InSamples[i*InChannels + 1]; if(InChannels > 2) { /* T */ for(size_t i{0};i < SamplesToDo;++i) - mT[sFilterSize+i] = InSamples[i*InChannels + 2]; + mT[sFilterDelay+i] = InSamples[i*InChannels + 2]; } if(InChannels > 3) { /* Q */ for(size_t i{0};i < SamplesToDo;++i) - mQ[sFilterSize+i] = InSamples[i*InChannels + 3]; + mQ[sFilterDelay+i] = InSamples[i*InChannels + 3]; } /* Precompute j(0.828347*D + 0.767835*T) and store in xoutput. */ auto tmpiter = std::copy(mDTHistory.cbegin(), mDTHistory.cend(), mTemp.begin()); - std::transform(mD.cbegin(), mD.cbegin()+SamplesToDo+sFilterSize, mT.cbegin(), tmpiter, + std::transform(mD.cbegin(), mD.cbegin()+SamplesToDo+sFilterDelay, mT.cbegin(), tmpiter, [](const float d, const float t) noexcept { return 0.828347f*d + 0.767835f*t; }); std::copy_n(mTemp.cbegin()+SamplesToDo, mDTHistory.size(), mDTHistory.begin()); - allpass_process({xoutput, SamplesToDo}, mTemp.data()); + PShift.process({xoutput, SamplesToDo}, mTemp.data()); for(size_t i{0};i < SamplesToDo;++i) { @@ -431,9 +266,9 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels /* Precompute j*S and store in youtput. */ tmpiter = std::copy(mSHistory.cbegin(), mSHistory.cend(), mTemp.begin()); - std::copy_n(mS.cbegin(), SamplesToDo+sFilterSize, tmpiter); + std::copy_n(mS.cbegin(), SamplesToDo+sFilterDelay, tmpiter); std::copy_n(mTemp.cbegin()+SamplesToDo, mSHistory.size(), mSHistory.begin()); - allpass_process({youtput, SamplesToDo}, mTemp.data()); + PShift.process({youtput, SamplesToDo}, mTemp.data()); for(size_t i{0};i < SamplesToDo;++i) { @@ -449,10 +284,10 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels zoutput[i] = 1.023332f*mQ[i]; } - std::copy(mS.begin()+SamplesToDo, mS.begin()+SamplesToDo+sFilterSize, mS.begin()); - std::copy(mD.begin()+SamplesToDo, mD.begin()+SamplesToDo+sFilterSize, mD.begin()); - std::copy(mT.begin()+SamplesToDo, mT.begin()+SamplesToDo+sFilterSize, mT.begin()); - std::copy(mQ.begin()+SamplesToDo, mQ.begin()+SamplesToDo+sFilterSize, mQ.begin()); + std::copy(mS.begin()+SamplesToDo, mS.begin()+SamplesToDo+sFilterDelay, mS.begin()); + std::copy(mD.begin()+SamplesToDo, mD.begin()+SamplesToDo+sFilterDelay, mD.begin()); + std::copy(mT.begin()+SamplesToDo, mT.begin()+SamplesToDo+sFilterDelay, mT.begin()); + std::copy(mQ.begin()+SamplesToDo, mQ.begin()+SamplesToDo+sFilterDelay, mQ.begin()); } /* This is an alternative equation for decoding 2-channel UHJ. Not sure what @@ -485,17 +320,17 @@ void UhjDecoder::decode2(const float *RESTRICT InSamples, /* S = Left + Right */ for(size_t i{0};i < SamplesToDo;++i) - mS[sFilterSize+i] = InSamples[i*2 + 0] + InSamples[i*2 + 1]; + mS[sFilterDelay+i] = InSamples[i*2 + 0] + InSamples[i*2 + 1]; /* D = Left - Right */ for(size_t i{0};i < SamplesToDo;++i) - mD[sFilterSize+i] = InSamples[i*2 + 0] - InSamples[i*2 + 1]; + mD[sFilterDelay+i] = InSamples[i*2 + 0] - InSamples[i*2 + 1]; /* Precompute j*D and store in xoutput. */ auto tmpiter = std::copy(mDTHistory.cbegin(), mDTHistory.cend(), mTemp.begin()); - std::copy_n(mD.cbegin(), SamplesToDo+sFilterSize, tmpiter); + std::copy_n(mD.cbegin(), SamplesToDo+sFilterDelay, tmpiter); std::copy_n(mTemp.cbegin()+SamplesToDo, mDTHistory.size(), mDTHistory.begin()); - allpass_process({xoutput, SamplesToDo}, mTemp.data()); + PShift.process({xoutput, SamplesToDo}, mTemp.data()); for(size_t i{0};i < SamplesToDo;++i) { @@ -507,9 +342,9 @@ void UhjDecoder::decode2(const float *RESTRICT InSamples, /* Precompute j*S and store in youtput. */ tmpiter = std::copy(mSHistory.cbegin(), mSHistory.cend(), mTemp.begin()); - std::copy_n(mS.cbegin(), SamplesToDo+sFilterSize, tmpiter); + std::copy_n(mS.cbegin(), SamplesToDo+sFilterDelay, tmpiter); std::copy_n(mTemp.cbegin()+SamplesToDo, mSHistory.size(), mSHistory.begin()); - allpass_process({youtput, SamplesToDo}, mTemp.data()); + PShift.process({youtput, SamplesToDo}, mTemp.data()); for(size_t i{0};i < SamplesToDo;++i) { @@ -517,8 +352,8 @@ void UhjDecoder::decode2(const float *RESTRICT InSamples, youtput[i] = 0.762956f*mD[i] + 0.384230f*youtput[i]; } - std::copy(mS.begin()+SamplesToDo, mS.begin()+SamplesToDo+sFilterSize, mS.begin()); - std::copy(mD.begin()+SamplesToDo, mD.begin()+SamplesToDo+sFilterSize, mD.begin()); + std::copy(mS.begin()+SamplesToDo, mS.begin()+SamplesToDo+sFilterDelay, mS.begin()); + std::copy(mD.begin()+SamplesToDo, mD.begin()+SamplesToDo+sFilterDelay, mD.begin()); } @@ -643,7 +478,7 @@ int main(int argc, char **argv) * additional 255 samples of silence need to be fed through the decoder * for it to finish. */ - sf_count_t LeadOut{UhjDecoder::sFilterSize*2 - 1}; + sf_count_t LeadOut{UhjDecoder::sFilterDelay*2 - 1}; while(LeadOut > 0) { sf_count_t sgot{sf_readf_float(infile.get(), inmem.get(), BufferLineSize)}; -- cgit v1.2.3 From 35a0f2665f834c107e39ec2dcfc3d9ae0a0b33ce Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 31 Mar 2021 09:37:30 -0700 Subject: Decode UHJ buffers to B-Format for mixing This should also have an adjustment for the shelf filter. Although it's not clear what the appropriate adjustments should be. --- CMakeLists.txt | 1 + al/buffer.cpp | 9 ++-- al/source.cpp | 8 +-- alc/alu.cpp | 17 +++--- alc/voice.cpp | 140 ++++++++++++++++++++++++++++-------------------- alc/voice.h | 15 +++++- core/mixer/defs.h | 7 +-- core/resampler_limits.h | 12 +++++ core/uhjfilter.cpp | 67 +++++++++++++++++++++++ core/uhjfilter.h | 20 +++++++ 10 files changed, 214 insertions(+), 82 deletions(-) create mode 100644 core/resampler_limits.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e4f06ef..bd64c9d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -671,6 +671,7 @@ set(CORE_OBJS core/logging.h core/mastering.cpp core/mastering.h + core/resampler_limits.h core/uhjfilter.cpp core/uhjfilter.h core/mixer/defs.h diff --git a/al/buffer.cpp b/al/buffer.cpp index f63d5c71..436be9ae 100644 --- a/al/buffer.cpp +++ b/al/buffer.cpp @@ -51,7 +51,9 @@ #include "atomic.h" #include "core/except.h" #include "inprogext.h" +#include "core/logging.h" #include "opthelpers.h" +#include "voice.h" namespace { @@ -503,7 +505,7 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, unpackalign, NameFromUserFmtType(SrcType)); const ALuint ambiorder{(DstChannels == FmtBFormat2D || DstChannels == FmtBFormat3D) ? - ALBuf->UnpackAmbiOrder : 0}; + ALBuf->UnpackAmbiOrder : ((DstChannels == FmtUHJ2) ? 1 : 0)}; if((access&AL_PRESERVE_DATA_BIT_SOFT)) { @@ -646,10 +648,11 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, SETERR_RETURN(context, AL_INVALID_ENUM,, "Unsupported callback format"); const ALuint ambiorder{(DstChannels == FmtBFormat2D || DstChannels == FmtBFormat3D) ? - ALBuf->UnpackAmbiOrder : 0}; + ALBuf->UnpackAmbiOrder : ((DstChannels == FmtUHJ2) ? 1 : 0)}; + constexpr uint line_size{BufferLineSize + MaxPostVoiceLoad}; al::vector(FrameSizeFromFmt(DstChannels, DstType, ambiorder) * - size_t{BufferLineSize + (MaxResamplerPadding>>1)}).swap(ALBuf->mData); + size_t{line_size}).swap(ALBuf->mData); ALBuf->mCallback = callback; ALBuf->mUserData = userptr; diff --git a/al/source.cpp b/al/source.cpp index b2bb0d75..dd9e6cc7 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -439,13 +439,13 @@ void InitVoice(Voice *voice, ALsource *source, ALbufferQueueItem *BufferList, AL std::memory_order_relaxed); ALbuffer *buffer{BufferList->mBuffer}; - ALuint num_channels{buffer->channelsFromFmt()}; + ALuint num_channels{(buffer->mChannels==FmtUHJ2) ? 3 : buffer->channelsFromFmt()}; voice->mFrequency = buffer->mSampleRate; voice->mFmtChannels = buffer->mChannels; voice->mFmtType = buffer->mType; - voice->mSampleSize = buffer->bytesFromFmt(); - voice->mAmbiLayout = buffer->mAmbiLayout; - voice->mAmbiScaling = buffer->mAmbiScaling; + voice->mFrameSize = buffer->frameSizeFromFmt(); + voice->mAmbiLayout = (buffer->mChannels==FmtUHJ2) ? AmbiLayout::FuMa : buffer->mAmbiLayout; + voice->mAmbiScaling = (buffer->mChannels==FmtUHJ2) ? AmbiScaling::FuMa : buffer->mAmbiScaling; voice->mAmbiOrder = buffer->mAmbiOrder; if(buffer->mCallback) voice->mFlags |= VoiceIsCallback; diff --git a/alc/alu.cpp b/alc/alu.cpp index 67bb8ebc..86cbefa5 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -789,23 +789,18 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con case FmtBFormat2D: case FmtBFormat3D: - DirectChannels = DirectMode::Off; - break; - - /* TODO: UHJ2 should be treated as BFormat2D for panning. */ case FmtUHJ2: DirectChannels = DirectMode::Off; - chans = StereoMap; - downmix_gain = 1.0f / 2.0f; break; } voice->mFlags &= ~(VoiceHasHrtf | VoiceHasNfc); - if(voice->mFmtChannels == FmtBFormat2D || voice->mFmtChannels == FmtBFormat3D) + if(voice->mFmtChannels == FmtBFormat2D || voice->mFmtChannels == FmtBFormat3D + || voice->mFmtChannels == FmtUHJ2) { /* Special handling for B-Format sources. */ - if(Device->AvgSpeakerDist > 0.0f) + if(Device->AvgSpeakerDist > 0.0f && voice->mFmtChannels != FmtUHJ2) { if(!(Distance > std::numeric_limits::epsilon())) { @@ -904,7 +899,8 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con /* Convert the rotation matrix for input ordering and scaling, and * whether input is 2D or 3D. */ - const uint8_t *index_map{(voice->mFmtChannels == FmtBFormat2D) ? + const uint8_t *index_map{ + (voice->mFmtChannels == FmtBFormat2D || voice->mFmtChannels == FmtUHJ2) ? GetAmbi2DLayout(voice->mAmbiLayout).data() : GetAmbiLayout(voice->mAmbiLayout).data()}; @@ -1561,7 +1557,8 @@ void CalcSourceParams(Voice *voice, ALCcontext *context, bool force) } if((voice->mProps.DirectChannels != DirectMode::Off && voice->mFmtChannels != FmtMono - && voice->mFmtChannels != FmtBFormat2D && voice->mFmtChannels != FmtBFormat3D) + && voice->mFmtChannels != FmtBFormat2D && voice->mFmtChannels != FmtBFormat3D + && voice->mFmtChannels != FmtUHJ2) || voice->mProps.mSpatializeMode==SpatializeMode::Off || (voice->mProps.mSpatializeMode==SpatializeMode::Auto && voice->mFmtChannels != FmtMono)) CalcNonAttnSourceParams(voice, &voice->mProps, context); diff --git a/alc/voice.cpp b/alc/voice.cpp index f9eca51c..c3e3dca2 100644 --- a/alc/voice.cpp +++ b/alc/voice.cpp @@ -55,6 +55,7 @@ #include "core/logging.h" #include "core/mixer/defs.h" #include "core/mixer/hrtfdefs.h" +#include "core/resampler_limits.h" #include "hrtf.h" #include "inprogext.h" #include "opthelpers.h" @@ -81,8 +82,6 @@ MixerFunc MixSamples{Mix_}; namespace { -constexpr uint ResamplerPrePadding{MaxResamplerPadding / 2}; - using HrtfMixerFunc = void(*)(const float *InSamples, float2 *AccumSamples, const uint IrSize, const MixHrtfFilter *hrtfparams, const size_t BufferSize); using HrtfMixerBlendFunc = void(*)(const float *InSamples, float2 *AccumSamples, @@ -224,17 +223,32 @@ const float *DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, float *ds void LoadSamples(const al::span dstSamples, const size_t dstOffset, - const al::byte *src, const size_t srcOffset, const size_t srcstep, FmtType srctype, + const al::byte *src, const size_t srcOffset, const FmtType srctype, const FmtChannels srcchans, const size_t samples) noexcept { #define HANDLE_FMT(T) case T: \ { \ constexpr size_t sampleSize{sizeof(al::FmtTypeTraits::Type)}; \ - src += srcOffset*srcstep*sampleSize; \ - for(auto &dst : dstSamples) \ + if(srcchans == FmtUHJ2) \ + { \ + constexpr size_t srcstep{2u}; \ + src += srcOffset*srcstep*sampleSize; \ + al::LoadSampleArray(dstSamples[0].data() + dstOffset, src, \ + srcstep, samples); \ + al::LoadSampleArray(dstSamples[1].data() + dstOffset, \ + src + sampleSize, srcstep, samples); \ + std::fill_n(dstSamples[2].data() + dstOffset, samples, 0.0f); \ + } \ + else \ { \ - al::LoadSampleArray(dst.data() + dstOffset, src, srcstep, samples); \ - src += sampleSize; \ + const size_t srcstep{dstSamples.size()}; \ + src += srcOffset*srcstep*sampleSize; \ + for(auto &dst : dstSamples) \ + { \ + al::LoadSampleArray(dst.data() + dstOffset, src, srcstep, \ + samples); \ + src += sampleSize; \ + } \ } \ } \ break @@ -252,10 +266,9 @@ void LoadSamples(const al::span dstSamples, const size_t dstO } void LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, - const size_t dataPosInt, const FmtType sampleType, const size_t samplesToLoad, - const al::span voiceSamples) + const size_t dataPosInt, const FmtType sampleType, const FmtChannels sampleChannels, + const size_t samplesToLoad, const al::span voiceSamples) { - const size_t numChannels{voiceSamples.size()}; const uint loopStart{buffer->mLoopStart}; const uint loopEnd{buffer->mLoopEnd}; ASSUME(loopEnd > loopStart); @@ -265,14 +278,14 @@ void LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, { /* Load what's left to play from the buffer */ const size_t remaining{minz(samplesToLoad, buffer->mSampleLen-dataPosInt)}; - LoadSamples(voiceSamples, ResamplerPrePadding, buffer->mSamples, dataPosInt, numChannels, - sampleType, remaining); + LoadSamples(voiceSamples, MaxResamplerEdge, buffer->mSamples, dataPosInt, sampleType, + sampleChannels, remaining); if(const size_t toFill{samplesToLoad - remaining}) { for(auto &chanbuffer : voiceSamples) { - auto srcsamples = chanbuffer.data() + ResamplerPrePadding - 1 + remaining; + auto srcsamples = chanbuffer.data() + MaxResamplerEdge - 1 + remaining; std::fill_n(srcsamples + 1, toFill, *srcsamples); } } @@ -281,46 +294,44 @@ void LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, { /* Load what's left of this loop iteration */ const size_t remaining{minz(samplesToLoad, loopEnd-dataPosInt)}; - LoadSamples(voiceSamples, ResamplerPrePadding, buffer->mSamples, dataPosInt, numChannels, - sampleType, remaining); + LoadSamples(voiceSamples, MaxResamplerEdge, buffer->mSamples, dataPosInt, sampleType, + sampleChannels, remaining); /* Load repeats of the loop to fill the buffer. */ const auto loopSize = static_cast(loopEnd - loopStart); size_t samplesLoaded{remaining}; while(const size_t toFill{minz(samplesToLoad - samplesLoaded, loopSize)}) { - LoadSamples(voiceSamples, ResamplerPrePadding + samplesLoaded, buffer->mSamples, - loopStart, numChannels, sampleType, toFill); + LoadSamples(voiceSamples, MaxResamplerEdge + samplesLoaded, buffer->mSamples, + loopStart, sampleType, sampleChannels, toFill); samplesLoaded += toFill; } } } void LoadBufferCallback(VoiceBufferItem *buffer, const size_t numCallbackSamples, - const FmtType sampleType, const size_t samplesToLoad, + const FmtType sampleType, const FmtChannels sampleChannels, const size_t samplesToLoad, const al::span voiceSamples) { - const size_t numChannels{voiceSamples.size()}; /* Load what's left to play from the buffer */ const size_t remaining{minz(samplesToLoad, numCallbackSamples)}; - LoadSamples(voiceSamples, ResamplerPrePadding, buffer->mSamples, 0, numChannels, sampleType, + LoadSamples(voiceSamples, MaxResamplerEdge, buffer->mSamples, 0, sampleType, sampleChannels, remaining); if(const size_t toFill{samplesToLoad - remaining}) { for(auto &chanbuffer : voiceSamples) { - auto srcsamples = chanbuffer.data() + ResamplerPrePadding - 1 + remaining; + auto srcsamples = chanbuffer.data() + MaxResamplerEdge - 1 + remaining; std::fill_n(srcsamples + 1, toFill, *srcsamples); } } } void LoadBufferQueue(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, - size_t dataPosInt, const FmtType sampleType, const size_t samplesToLoad, - const al::span voiceSamples) + size_t dataPosInt, const FmtType sampleType, const FmtChannels sampleChannels, + const size_t samplesToLoad, const al::span voiceSamples) { - const size_t numChannels{voiceSamples.size()}; /* Crawl the buffer queue to fill in the temp buffer */ size_t samplesLoaded{0}; while(buffer && samplesLoaded != samplesToLoad) @@ -334,8 +345,8 @@ void LoadBufferQueue(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, } const size_t remaining{minz(samplesToLoad-samplesLoaded, buffer->mSampleLen-dataPosInt)}; - LoadSamples(voiceSamples, ResamplerPrePadding+samplesLoaded, buffer->mSamples, dataPosInt, - numChannels, sampleType, remaining); + LoadSamples(voiceSamples, MaxResamplerEdge+samplesLoaded, buffer->mSamples, dataPosInt, + sampleType, sampleChannels, remaining); samplesLoaded += remaining; if(samplesLoaded == samplesToLoad) @@ -350,7 +361,7 @@ void LoadBufferQueue(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, size_t chanidx{0}; for(auto &chanbuffer : voiceSamples) { - auto srcsamples = chanbuffer.data() + ResamplerPrePadding - 1 + samplesLoaded; + auto srcsamples = chanbuffer.data() + MaxResamplerEdge - 1 + samplesLoaded; std::fill_n(srcsamples + 1, toFill, *srcsamples); ++chanidx; } @@ -517,6 +528,8 @@ void Voice::mix(const State vstate, ALCcontext *Context, const uint SamplesToDo) else if UNLIKELY(!BufferListItem) Counter = std::min(Counter, 64u); + const uint PostPadding{MaxResamplerEdge + + ((mFmtChannels==FmtUHJ2) ? uint{UhjDecoder::sFilterDelay} : 0u)}; uint buffers_done{0u}; uint OutPos{0u}; do { @@ -531,7 +544,7 @@ void Voice::mix(const State vstate, ALCcontext *Context, const uint SamplesToDo) /* Calculate the last read src sample pos. */ DataSize64 = (DataSize64*increment + DataPosFrac) >> MixerFracBits; /* +1 to get the src sample count, include padding. */ - DataSize64 += 1 + ResamplerPrePadding; + DataSize64 += 1 + PostPadding; /* Result is guaranteed to be <= BufferLineSize+ResamplerPrePadding * since we won't use more src samples than dst samples+padding. @@ -543,18 +556,18 @@ void Voice::mix(const State vstate, ALCcontext *Context, const uint SamplesToDo) uint64_t DataSize64{DstBufferSize}; /* Calculate the end src sample pos, include padding. */ DataSize64 = (DataSize64*increment + DataPosFrac) >> MixerFracBits; - DataSize64 += ResamplerPrePadding; + DataSize64 += PostPadding; - if(DataSize64 <= BufferLineSize + ResamplerPrePadding) + if(DataSize64 <= LineSize - MaxResamplerEdge) SrcBufferSize = static_cast(DataSize64); else { /* If the source size got saturated, we can't fill the desired * dst size. Figure out how many samples we can actually mix. */ - SrcBufferSize = BufferLineSize + ResamplerPrePadding; + SrcBufferSize = LineSize - MaxResamplerEdge; - DataSize64 = SrcBufferSize - ResamplerPrePadding; + DataSize64 = SrcBufferSize - PostPadding; DataSize64 = ((DataSize64<(DataSize64) & ~3u; } + ASSUME(DstBufferSize > 0); } } @@ -570,11 +584,8 @@ void Voice::mix(const State vstate, ALCcontext *Context, const uint SamplesToDo) { if(SrcBufferSize > mNumCallbackSamples) { - const size_t FrameSize{mChans.size() * mSampleSize}; - ASSUME(FrameSize > 0); - - const size_t byteOffset{mNumCallbackSamples*FrameSize}; - const size_t needBytes{SrcBufferSize*FrameSize - byteOffset}; + const size_t byteOffset{mNumCallbackSamples*mFrameSize}; + const size_t needBytes{SrcBufferSize*mFrameSize - byteOffset}; const int gotBytes{BufferListItem->mCallback(BufferListItem->mUserData, &BufferListItem->mSamples[byteOffset], static_cast(needBytes))}; @@ -584,7 +595,7 @@ void Voice::mix(const State vstate, ALCcontext *Context, const uint SamplesToDo) { mFlags |= VoiceCallbackStopped; mNumCallbackSamples += static_cast(static_cast(gotBytes) / - FrameSize); + mFrameSize); } else mNumCallbackSamples = SrcBufferSize; @@ -595,7 +606,8 @@ void Voice::mix(const State vstate, ALCcontext *Context, const uint SamplesToDo) { for(auto &chanbuffer : mVoiceSamples) { - auto srciter = chanbuffer.data() + ResamplerPrePadding; + auto srciter = chanbuffer.data() + MaxResamplerEdge; + auto srcend = chanbuffer.data() + MaxResamplerPadding; /* When loading from a voice that ended prematurely, only take * the samples that get closest to 0 amplitude. This helps @@ -603,29 +615,41 @@ void Voice::mix(const State vstate, ALCcontext *Context, const uint SamplesToDo) */ auto abs_lt = [](const float lhs, const float rhs) noexcept -> bool { return std::abs(lhs) < std::abs(rhs); }; - srciter = std::min_element(srciter, srciter+(MaxResamplerPadding>>1), abs_lt); + srciter = std::min_element(srciter, srcend, abs_lt); - std::fill(srciter+1, chanbuffer.data() + ResamplerPrePadding + SrcBufferSize, - *srciter); + SrcBufferSize = SrcBufferSize - PostPadding + MaxResamplerPadding; + std::fill(srciter+1, chanbuffer.data() + SrcBufferSize, *srciter); } } - else if((mFlags&VoiceIsStatic)) - LoadBufferStatic(BufferListItem, BufferLoopItem, DataPosInt, mFmtType, SrcBufferSize, - mVoiceSamples); - else if((mFlags&VoiceIsCallback)) - LoadBufferCallback(BufferListItem, mNumCallbackSamples, mFmtType, SrcBufferSize, - mVoiceSamples); else - LoadBufferQueue(BufferListItem, BufferLoopItem, DataPosInt, mFmtType, SrcBufferSize, - mVoiceSamples); + { + if((mFlags&VoiceIsStatic)) + LoadBufferStatic(BufferListItem, BufferLoopItem, DataPosInt, mFmtType, mFmtChannels, + SrcBufferSize, mVoiceSamples); + else if((mFlags&VoiceIsCallback)) + LoadBufferCallback(BufferListItem, mNumCallbackSamples, mFmtType, mFmtChannels, + SrcBufferSize, mVoiceSamples); + else + LoadBufferQueue(BufferListItem, BufferLoopItem, DataPosInt, mFmtType, mFmtChannels, + SrcBufferSize, mVoiceSamples); + + if(mDecoder) + { + std::array samples{{mVoiceSamples[0].data() + MaxResamplerEdge, + mVoiceSamples[1].data() + MaxResamplerEdge, + mVoiceSamples[2].data() + MaxResamplerEdge}}; + const size_t srcOffset{(increment*DstBufferSize + DataPosFrac)>>MixerFracBits}; + SrcBufferSize = SrcBufferSize - PostPadding + MaxResamplerEdge; + mDecoder->decode(samples, SrcBufferSize, srcOffset); + } + } - ASSUME(DstBufferSize > 0); auto voiceSamples = mVoiceSamples.begin(); for(auto &chandata : mChans) { /* Resample, then apply ambisonic upsampling as needed. */ float *ResampledData{Resample(&mResampleState, - voiceSamples->data() + ResamplerPrePadding, DataPosFrac, increment, + voiceSamples->data() + MaxResamplerEdge, DataPosFrac, increment, {Device->ResampledData, DstBufferSize})}; if((mFlags&VoiceIsAmbisonic)) chandata.mAmbiSplitter.processHfScale({ResampledData, DstBufferSize}, @@ -720,11 +744,8 @@ void Voice::mix(const State vstate, ALCcontext *Context, const uint SamplesToDo) { if(SrcSamplesDone < mNumCallbackSamples) { - const size_t FrameSize{mChans.size() * mSampleSize}; - ASSUME(FrameSize > 0); - - const size_t byteOffset{SrcSamplesDone*FrameSize}; - const size_t byteEnd{mNumCallbackSamples*FrameSize}; + const size_t byteOffset{SrcSamplesDone*mFrameSize}; + const size_t byteEnd{mNumCallbackSamples*mFrameSize}; al::byte *data{BufferListItem->mSamples}; std::copy(data+byteOffset, data+byteEnd, data); mNumCallbackSamples -= SrcSamplesDone; @@ -802,6 +823,11 @@ void Voice::mix(const State vstate, ALCcontext *Context, const uint SamplesToDo) void Voice::prepare(ALCdevice *device) { + if(mFmtChannels == FmtUHJ2 && !mDecoder) + mDecoder = std::make_unique(); + else if(mFmtChannels != FmtUHJ2) + mDecoder = nullptr; + /* Clear the stepping value explicitly so the mixer knows not to mix this * until the update gets applied. */ diff --git a/alc/voice.h b/alc/voice.h index 96975efa..8f3476f1 100644 --- a/alc/voice.h +++ b/alc/voice.h @@ -15,6 +15,7 @@ #include "core/filters/splitter.h" #include "core/mixer/defs.h" #include "core/mixer/hrtfdefs.h" +#include "core/uhjfilter.h" #include "vector.h" struct ALCcontext; @@ -37,6 +38,12 @@ enum class DirectMode : unsigned char { }; +/* Maximum number of extra source samples that may need to be loaded, for + * resampling or conversion purposes. + */ +constexpr uint MaxPostVoiceLoad{MaxResamplerEdge + UhjDecoder::sFilterDelay}; + + enum { AF_None = 0, AF_LowPass = 1, @@ -191,11 +198,13 @@ struct Voice { FmtChannels mFmtChannels; FmtType mFmtType; uint mFrequency; - uint mSampleSize; + uint mFrameSize; AmbiLayout mAmbiLayout; AmbiScaling mAmbiScaling; uint mAmbiOrder; + std::unique_ptr mDecoder; + /** Current target parameters used for mixing. */ uint mStep{0}; @@ -218,7 +227,9 @@ struct Voice { * now current (which may be overwritten if the buffer data is still * available). */ - using BufferLine = std::array; + static constexpr size_t LineSize{BufferLineSize + MaxResamplerPadding + + UhjDecoder::sFilterDelay}; + using BufferLine = std::array; al::vector mVoiceSamples{2}; struct ChannelData { diff --git a/core/mixer/defs.h b/core/mixer/defs.h index e8e7be6c..ba304f22 100644 --- a/core/mixer/defs.h +++ b/core/mixer/defs.h @@ -6,6 +6,7 @@ #include "alspan.h" #include "core/bufferline.h" +#include "core/resampler_limits.h" struct HrtfChannelState; struct HrtfFilter; @@ -19,12 +20,6 @@ constexpr int MixerFracBits{12}; constexpr int MixerFracOne{1 << MixerFracBits}; constexpr int MixerFracMask{MixerFracOne - 1}; -/* Maximum number of samples to pad on the ends of a buffer for resampling. - * Note that the padding is symmetric (half at the beginning and half at the - * end)! - */ -constexpr int MaxResamplerPadding{48}; - constexpr float GainSilenceThreshold{0.00001f}; /* -100dB */ diff --git a/core/resampler_limits.h b/core/resampler_limits.h new file mode 100644 index 00000000..9d4cefda --- /dev/null +++ b/core/resampler_limits.h @@ -0,0 +1,12 @@ +#ifndef CORE_RESAMPLER_LIMITS_H +#define CORE_RESAMPLER_LIMITS_H + +/* Maximum number of samples to pad on the ends of a buffer for resampling. + * Note that the padding is symmetric (half at the beginning and half at the + * end)! + */ +constexpr int MaxResamplerPadding{48}; + +constexpr int MaxResamplerEdge{MaxResamplerPadding >> 1}; + +#endif /* CORE_RESAMPLER_LIMITS_H */ diff --git a/core/uhjfilter.cpp b/core/uhjfilter.cpp index bfcb83a3..d535522c 100644 --- a/core/uhjfilter.cpp +++ b/core/uhjfilter.cpp @@ -14,6 +14,8 @@ namespace { +static_assert(Uhj2Encoder::sFilterDelay==UhjDecoder::sFilterDelay, "UHJ filter delays mismatch"); + using complex_d = std::complex; const PhaseShifterT PShift{}; @@ -90,3 +92,68 @@ void Uhj2Encoder::encode(const FloatBufferSpan LeftOut, const FloatBufferSpan Ri std::copy(mS.cbegin()+SamplesToDo, mS.cbegin()+SamplesToDo+sFilterDelay, mS.begin()); std::copy(mD.cbegin()+SamplesToDo, mD.cbegin()+SamplesToDo+sFilterDelay, mD.begin()); } + + +/* Decoding UHJ is done as: + * + * S = Left + Right + * D = Left - Right + * + * W = 0.981530*S + 0.197484*j(0.828347*D + 0.767835*T) + * X = 0.418504*S - j(0.828347*D + 0.767835*T) + * Y = 0.795954*D - 0.676406*T + j(0.186626*S) + * Z = 1.023332*Q + * + * where j is a +90 degree phase shift. 3-channel UHJ excludes Q, while 2- + * channel excludes Q and T. The B-Format signal reconstructed from 2-channel + * UHJ should not be run through a normal B-Format decoder, as it needs + * different shelf filters. + */ +void UhjDecoder::decode(const al::span Samples, const size_t SamplesToDo, + const size_t ForwardSamples) +{ + ASSUME(SamplesToDo > 0); + + /* S = Left + Right */ + for(size_t i{0};i < SamplesToDo+sFilterDelay;++i) + mS[i] = Samples[0][i] + Samples[1][i]; + + /* D = Left - Right */ + for(size_t i{0};i < SamplesToDo+sFilterDelay;++i) + mD[i] = Samples[0][i] - Samples[1][i]; + + /* T */ + for(size_t i{0};i < SamplesToDo+sFilterDelay;++i) + mT[i] = Samples[2][i]; + + float *woutput{Samples[0]}; + float *xoutput{Samples[1]}; + float *youtput{Samples[2]}; + + /* Precompute j(0.828347*D + 0.767835*T) and store in xoutput. */ + auto tmpiter = std::copy(mDTHistory.cbegin(), mDTHistory.cend(), mTemp.begin()); + std::transform(mD.cbegin(), mD.cbegin()+SamplesToDo+sFilterDelay, mT.cbegin(), tmpiter, + [](const float d, const float t) noexcept { return 0.828347f*d + 0.767835f*t; }); + std::copy_n(mTemp.cbegin()+ForwardSamples, mDTHistory.size(), mDTHistory.begin()); + PShift.process({xoutput, SamplesToDo}, mTemp.data()); + + for(size_t i{0};i < SamplesToDo;++i) + { + /* W = 0.981530*S + 0.197484*j(0.828347*D + 0.767835*T) */ + woutput[i] = 0.981530f*mS[i] + 0.197484f*xoutput[i]; + /* X = 0.418504*S - j(0.828347*D + 0.767835*T) */ + xoutput[i] = 0.418504f*mS[i] - xoutput[i]; + } + + /* Precompute j*S and store in youtput. */ + tmpiter = std::copy(mSHistory.cbegin(), mSHistory.cend(), mTemp.begin()); + std::copy_n(mS.cbegin(), SamplesToDo+sFilterDelay, tmpiter); + std::copy_n(mTemp.cbegin()+ForwardSamples, mSHistory.size(), mSHistory.begin()); + PShift.process({youtput, SamplesToDo}, mTemp.data()); + + for(size_t i{0};i < SamplesToDo;++i) + { + /* Y = 0.795954*D - 0.676406*T + j(0.186626*S) */ + youtput[i] = 0.795954f*mD[i] - 0.676406f*mT[i] + 0.186626f*youtput[i]; + } +} diff --git a/core/uhjfilter.h b/core/uhjfilter.h index 13beea1e..b07488e5 100644 --- a/core/uhjfilter.h +++ b/core/uhjfilter.h @@ -5,6 +5,7 @@ #include "almalloc.h" #include "bufferline.h" +#include "resampler_limits.h" struct Uhj2Encoder { @@ -32,4 +33,23 @@ struct Uhj2Encoder { DEF_NEWDEL(Uhj2Encoder) }; + +struct UhjDecoder { + constexpr static size_t sFilterDelay{128}; + + alignas(16) std::array mS{}; + alignas(16) std::array mD{}; + alignas(16) std::array mT{}; + + alignas(16) std::array mDTHistory{}; + alignas(16) std::array mSHistory{}; + + alignas(16) std::array mTemp{}; + + void decode(const al::span Samples, const size_t SamplesToDo, + const size_t ForwardSamples); + + DEF_NEWDEL(UhjDecoder) +}; + #endif /* CORE_UHJFILTER_H */ -- cgit v1.2.3 From 5165b29b1945e1cff5e8c042bd371a5b2da9b492 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 18 Apr 2021 00:34:36 -0700 Subject: Optionally use RTKit/D-Bus to set elevated priority If pthread_setschedparam fails or is unavailable. --- CMakeLists.txt | 25 +++++- alc/helpers.cpp | 77 +++++++++++++---- config.h.in | 3 + core/dbus_wrap.cpp | 46 ++++++++++ core/dbus_wrap.h | 75 +++++++++++++++++ core/rtkit.cpp | 240 +++++++++++++++++++++++++++++++++++++++++++++++++++++ core/rtkit.h | 80 ++++++++++++++++++ 7 files changed, 528 insertions(+), 18 deletions(-) create mode 100644 core/dbus_wrap.cpp create mode 100644 core/dbus_wrap.h create mode 100644 core/rtkit.cpp create mode 100644 core/rtkit.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index bd64c9d9..3f1894fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -673,7 +673,30 @@ set(CORE_OBJS core/mastering.h core/resampler_limits.h core/uhjfilter.cpp - core/uhjfilter.h + core/uhjfilter.h) + +set(HAVE_RTKIT 0) +option(ALSOFT_REQUIRE_RTKIT "Require RTKit/D-Bus support" FALSE) +find_package(DBus1) +if(DBus1_FOUND) + option(ALSOFT_RTKIT "Enable RTKit support" ON) + if(ALSOFT_RTKIT) + set(HAVE_RTKIT 1) + set(CORE_OBJS ${CORE_OBJS} core/dbus_wrap.cpp core/dbus_wrap.h core/rtkit.cpp core/rtkit.h) + if(WIN32 OR HAVE_DLFCN_H) + set(INC_PATHS ${INC_PATHS} ${DBus1_INCLUDE_DIRS}) + set(CPP_DEFS ${CPP_DEFS} ${DBus1_DEFINITIONS}) + else() + set(EXTRA_LIBS ${EXTRA_LIBS} ${DBus1_LIBRARIES}) + endif() + endif() +endif() +if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT) + message(FATAL_ERROR "Failed to enabled required RTKit support") +endif() + +# Default mixers, always available +set(CORE_OBJS ${CORE_OBJS} core/mixer/defs.h core/mixer/hrtfbase.h core/mixer/hrtfdefs.h diff --git a/alc/helpers.cpp b/alc/helpers.cpp index 6dbe4787..671a1ae5 100644 --- a/alc/helpers.cpp +++ b/alc/helpers.cpp @@ -203,6 +203,9 @@ void SetRTPriority(void) #include #include #endif +#ifdef HAVE_RTKIT +#include "core/rtkit.h" +#endif const PathNamePair &GetProcBinary() { @@ -409,28 +412,68 @@ al::vector SearchDataFiles(const char *ext, const char *subdir) void SetRTPriority() { + if(RTPrioLevel <= 0) + return; + + int err{-ENOTSUP}; #if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__) - if(RTPrioLevel > 0) - { - struct sched_param param{}; - /* Use the minimum real-time priority possible for now (on Linux this - * should be 1 for SCHED_RR). - */ - param.sched_priority = sched_get_priority_min(SCHED_RR); - int err; + struct sched_param param{}; + /* Use the minimum real-time priority possible for now (on Linux this + * should be 1 for SCHED_RR). + */ + param.sched_priority = sched_get_priority_min(SCHED_RR); #ifdef SCHED_RESET_ON_FORK - err = pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, ¶m); - if(err == EINVAL) + err = pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, ¶m); + if(err == EINVAL) +#endif + err = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); + if(err == 0) return; + + WARN("pthread_setschedparam failed: %s (%d)\n", std::strerror(err), err); #endif - err = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); - if(err != 0) - ERR("Failed to set real-time priority for thread: %s (%d)\n", std::strerror(err), err); +#ifdef HAVE_RTKIT + if(HasDBus()) + { + dbus::Error error; + if(dbus::ConnectionPtr conn{(*pdbus_bus_get)(DBUS_BUS_SYSTEM, &error.get())}) + { + /* Don't stupidly exit if the connection dies while doing this. */ + (*pdbus_connection_set_exit_on_disconnect)(conn.get(), false); + + int nicemin{}; + rtkit_get_min_nice_level(conn.get(), &nicemin); + int rtmax{rtkit_get_max_realtime_priority(conn.get())}; + TRACE("Maximum real-time priority: %d, minimum niceness: %d\n", rtmax, nicemin); + + if(rtmax > 0) + { + /* Use half the maximum real-time priority allowed. */ + TRACE("Making real-time with priority %d\n", (rtmax+1)/2); + err = rtkit_make_realtime(conn.get(), 0, (rtmax+1)/2); + if(err != 0) + { + err = std::abs(err); + WARN("Failed to set real-time priority: %s (%d)\n", std::strerror(err), err); + } + } + if(err != 0 && nicemin < 0) + { + TRACE("Making high priority with niceness %d\n", nicemin); + err = rtkit_make_high_priority(conn.get(), 0, nicemin); + if(err != 0) + { + err = std::abs(err); + WARN("Failed to set high priority: %s (%d)\n", std::strerror(err), err); + } + } + } + else + WARN("D-Bus connection failed with %s: %s\n", error->name, error->message); } -#else - /* Real-time priority not available */ - if(RTPrioLevel > 0) - ERR("Cannot set priority level for thread\n"); + else + WARN("D-Bus not available\n"); #endif + ERR("Could not set elevated priority: %s (%d)\n", std::strerror(err), err); } #endif diff --git a/config.h.in b/config.h.in index f43b3f73..5cc78ae5 100644 --- a/config.h.in +++ b/config.h.in @@ -13,6 +13,9 @@ /* Define if we have the getopt function */ #cmakedefine HAVE_GETOPT +/* Define if we have DBus/RTKit */ +#cmakedefine HAVE_RTKIT + /* Define if we have SSE CPU extensions */ #cmakedefine HAVE_SSE #cmakedefine HAVE_SSE2 diff --git a/core/dbus_wrap.cpp b/core/dbus_wrap.cpp new file mode 100644 index 00000000..506dd815 --- /dev/null +++ b/core/dbus_wrap.cpp @@ -0,0 +1,46 @@ + +#include "config.h" + +#include "dbus_wrap.h" + +#ifdef HAVE_DYNLOAD + +#include +#include + +#include "logging.h" + + +void *dbus_handle{nullptr}; +#define DECL_FUNC(x) decltype(x) *p##x{}; +DBUS_FUNCTIONS(DECL_FUNC) +#undef DECL_FUNC + +void PrepareDBus() +{ + static constexpr char libname[] = "libdbus-1.so.3"; + + auto load_func = [](auto &f, const char *name) -> void + { f = reinterpret_cast>(GetSymbol(dbus_handle, name)); }; +#define LOAD_FUNC(x) do { \ + load_func(p##x, #x); \ + if(!p##x) \ + { \ + WARN("Failed to load function %s\n", #x); \ + CloseLib(dbus_handle); \ + dbus_handle = nullptr; \ + return; \ + } \ +} while(0); + + dbus_handle = LoadLib(libname); + if(!dbus_handle) + { + WARN("Failed to load %s\n", libname); + return; + } + +DBUS_FUNCTIONS(LOAD_FUNC) +#undef LOAD_FUNC +} +#endif diff --git a/core/dbus_wrap.h b/core/dbus_wrap.h new file mode 100644 index 00000000..61dbb971 --- /dev/null +++ b/core/dbus_wrap.h @@ -0,0 +1,75 @@ +#ifndef CORE_DBUS_WRAP_H +#define CORE_DBUS_WRAP_H + +#include + +#include + +#include "dynload.h" + + +#define DBUS_FUNCTIONS(MAGIC) \ +MAGIC(dbus_error_init) \ +MAGIC(dbus_error_free) \ +MAGIC(dbus_bus_get) \ +MAGIC(dbus_connection_set_exit_on_disconnect) \ +MAGIC(dbus_connection_unref) \ +MAGIC(dbus_connection_send_with_reply_and_block) \ +MAGIC(dbus_message_unref) \ +MAGIC(dbus_message_new_method_call) \ +MAGIC(dbus_message_append_args) \ +MAGIC(dbus_message_iter_init) \ +MAGIC(dbus_message_iter_next) \ +MAGIC(dbus_message_iter_recurse) \ +MAGIC(dbus_message_iter_get_arg_type) \ +MAGIC(dbus_message_iter_get_basic) \ +MAGIC(dbus_set_error_from_message) + +#ifdef HAVE_DYNLOAD + +#include + +extern void *dbus_handle; +#define DECL_FUNC(x) extern decltype(x) *p##x; +DBUS_FUNCTIONS(DECL_FUNC) +#undef DECL_FUNC + +void PrepareDBus(); + +inline auto HasDBus() +{ + static std::once_flag init_dbus{}; + std::call_once(init_dbus, PrepareDBus); + return dbus_handle; +} + +#else + +#define DECL_FUNC(x) constexpr auto p##x = &x; +DBUS_FUNCTIONS(DECL_FUNC) +#undef DECL_FUNC + +constexpr bool HasDBus() noexcept { return true; } +#endif /* HAVE_DYNLOAD */ + + +namespace dbus { + +struct Error { + Error() { (*pdbus_error_init)(&mError); } + ~Error() { (*pdbus_error_free)(&mError); } + DBusError* operator->() { return &mError; } + DBusError &get() { return mError; } +private: + DBusError mError{}; +}; + +struct ConnectionDeleter { + void operator()(DBusConnection *c) { (*pdbus_connection_unref)(c); } +}; +using ConnectionPtr = std::unique_ptr; + +} // namespace dbus + + +#endif /* CORE_DBUS_WRAP_H */ diff --git a/core/rtkit.cpp b/core/rtkit.cpp new file mode 100644 index 00000000..8b489e71 --- /dev/null +++ b/core/rtkit.cpp @@ -0,0 +1,240 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + Copyright 2009 Lennart Poettering + Copyright 2010 David Henningsson + Copyright 2021 Chris Robinson + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ + +#include "config.h" + +#include "rtkit.h" + +#include + +#ifdef __linux__ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include + + +namespace dbus { + constexpr int TypeString{'s'}; + constexpr int TypeVariant{'v'}; + constexpr int TypeInt32{'i'}; + constexpr int TypeUInt32{'u'}; + constexpr int TypeInt64{'x'}; + constexpr int TypeUInt64{'t'}; + constexpr int TypeInvalid{'\0'}; + + struct MessageDeleter { + void operator()(DBusMessage *m) { (*pdbus_message_unref)(m); } + }; + using MessagePtr = std::unique_ptr; +} // namespace dbus + +namespace { + +inline pid_t _gettid() +{ return static_cast(syscall(SYS_gettid)); } + +int translate_error(const char *name) +{ + if(strcmp(name, DBUS_ERROR_NO_MEMORY) == 0) + return -ENOMEM; + if(strcmp(name, DBUS_ERROR_SERVICE_UNKNOWN) == 0 + || strcmp(name, DBUS_ERROR_NAME_HAS_NO_OWNER) == 0) + return -ENOENT; + if(strcmp(name, DBUS_ERROR_ACCESS_DENIED) == 0 + || strcmp(name, DBUS_ERROR_AUTH_FAILED) == 0) + return -EACCES; + return -EIO; +} + +int rtkit_get_int_property(DBusConnection *connection, const char *propname, long long *propval) +{ + dbus::MessagePtr m{(*pdbus_message_new_method_call)(RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH, + "org.freedesktop.DBus.Properties", "Get")}; + if(!m) return -ENOMEM; + + const char *interfacestr = RTKIT_SERVICE_NAME; + auto ready = (*pdbus_message_append_args)(m.get(), + dbus::TypeString, &interfacestr, + dbus::TypeString, &propname, + dbus::TypeInvalid); + if(!ready) return -ENOMEM; + + dbus::Error error; + dbus::MessagePtr r{(*pdbus_connection_send_with_reply_and_block)(connection, m.get(), -1, + &error.get())}; + if(!r) return translate_error(error->name); + + if((*pdbus_set_error_from_message)(&error.get(), r.get())) + return translate_error(error->name); + + int ret{-EBADMSG}; + DBusMessageIter iter{}; + (*pdbus_message_iter_init)(r.get(), &iter); + while(int curtype{(*pdbus_message_iter_get_arg_type)(&iter)}) + { + if(curtype == dbus::TypeVariant) + { + DBusMessageIter subiter{}; + (*pdbus_message_iter_recurse)(&iter, &subiter); + + while((curtype=(*pdbus_message_iter_get_arg_type)(&subiter)) != dbus::TypeInvalid) + { + if(curtype == dbus::TypeInt32) + { + dbus_int32_t i32{}; + (*pdbus_message_iter_get_basic)(&subiter, &i32); + *propval = i32; + ret = 0; + } + + if(curtype == dbus::TypeInt64) + { + dbus_int64_t i64{}; + (*pdbus_message_iter_get_basic)(&subiter, &i64); + *propval = i64; + ret = 0; + } + + (*pdbus_message_iter_next)(&subiter); + } + } + (*pdbus_message_iter_next)(&iter); + } + + return ret; +} + +} // namespace + +extern "C" { +int rtkit_get_max_realtime_priority(DBusConnection *connection) +{ + long long retval{}; + int err{rtkit_get_int_property(connection, "MaxRealtimePriority", &retval)}; + return err < 0 ? err : static_cast(retval); +} + +int rtkit_get_min_nice_level(DBusConnection *connection, int *min_nice_level) +{ + long long retval{}; + int err{rtkit_get_int_property(connection, "MinNiceLevel", &retval)}; + if(err >= 0) *min_nice_level = static_cast(retval); + return err; +} + +long long rtkit_get_rttime_usec_max(DBusConnection *connection) +{ + long long retval{}; + int err{rtkit_get_int_property(connection, "RTTimeUSecMax", &retval)}; + return err < 0 ? err : retval; +} + +int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) +{ + if(thread == 0) + thread = _gettid(); + + dbus::MessagePtr m{(*pdbus_message_new_method_call)(RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH, + "org.freedesktop.RealtimeKit1", "MakeThreadRealtime")}; + if(!m) return -ENOMEM; + + auto u64 = static_cast(thread); + auto u32 = static_cast(priority); + auto ready = (*pdbus_message_append_args)(m.get(), + dbus::TypeUInt64, &u64, + dbus::TypeUInt32, &u32, + dbus::TypeInvalid); + if(!ready) return -ENOMEM; + + dbus::Error error; + dbus::MessagePtr r{(*pdbus_connection_send_with_reply_and_block)(connection, m.get(), -1, + &error.get())}; + if(!r) return translate_error(error->name); + + if((*pdbus_set_error_from_message)(&error.get(), r.get())) + return translate_error(error->name); + + return 0; +} + +int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) +{ + if(thread == 0) + thread = _gettid(); + + dbus::MessagePtr m{(*pdbus_message_new_method_call)(RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH, + "org.freedesktop.RealtimeKit1", "MakeThreadHighPriority")}; + if(!m) return -ENOMEM; + + auto u64 = static_cast(thread); + auto s32 = static_cast(nice_level); + auto ready = (*pdbus_message_append_args)(m.get(), + dbus::TypeUInt64, &u64, + dbus::TypeInt32, &s32, + dbus::TypeInvalid); + if(!ready) return -ENOMEM; + + dbus::Error error; + dbus::MessagePtr r{(*pdbus_connection_send_with_reply_and_block)(connection, m.get(), -1, + &error.get())}; + if(!r) return translate_error(error->name); + + if((*pdbus_set_error_from_message)(&error.get(), r.get())) + return translate_error(error->name); + + return 0; +} +} // extern "C" + +#else + +extern "C" { +int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) +{ return -ENOTSUP; } + +int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) +{ return -ENOTSUP; } + +int rtkit_get_max_realtime_priority(DBusConnection *connection) +{ return -ENOTSUP; } + +int rtkit_get_min_nice_level(DBusConnection *connection, int *min_nice_level) +{ return -ENOTSUP; } + +long long rtkit_get_rttime_usec_max(DBusConnection *connection) +{ return -ENOTSUP; } +} // extern "C" + +#endif diff --git a/core/rtkit.h b/core/rtkit.h new file mode 100644 index 00000000..96e81d4a --- /dev/null +++ b/core/rtkit.h @@ -0,0 +1,80 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foortkithfoo +#define foortkithfoo + +/*** + Copyright 2009 Lennart Poettering + Copyright 2010 David Henningsson + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ + +#include + +#include "dbus_wrap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is the reference implementation for a client for + * RealtimeKit. You don't have to use this, but if do, just copy these + * sources into your repository */ + +#define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1" +#define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1" + +/* This is mostly equivalent to sched_setparam(thread, SCHED_RR, { + * .sched_priority = priority }). 'thread' needs to be a kernel thread + * id as returned by gettid(), not a pthread_t! If 'thread' is 0 the + * current thread is used. The returned value is a negative errno + * style error code, or 0 on success. */ +int rtkit_make_realtime(DBusConnection *system_bus, pid_t thread, int priority); + +/* This is mostly equivalent to setpriority(PRIO_PROCESS, thread, + * nice_level). 'thread' needs to be a kernel thread id as returned by + * gettid(), not a pthread_t! If 'thread' is 0 the current thread is + * used. The returned value is a negative errno style error code, or 0 + * on success.*/ +int rtkit_make_high_priority(DBusConnection *system_bus, pid_t thread, int nice_level); + +/* Return the maximum value of realtime priority available. Realtime requests + * above this value will fail. A negative value is an errno style error code. + */ +int rtkit_get_max_realtime_priority(DBusConnection *system_bus); + +/* Retreive the minimum value of nice level available. High prio requests + * below this value will fail. The returned value is a negative errno + * style error code, or 0 on success.*/ +int rtkit_get_min_nice_level(DBusConnection *system_bus, int *min_nice_level); + +/* Return the maximum value of RLIMIT_RTTIME to set before attempting a + * realtime request. A negative value is an errno style error code. + */ +long long rtkit_get_rttime_usec_max(DBusConnection *system_bus); + + +#ifdef __cplusplus +} +#endif + +#endif -- cgit v1.2.3 From d2f587ee23f3e538d6d2744b9acae2e15e047db8 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 22 Apr 2021 03:14:31 -0700 Subject: Move helpers.cpp to core --- CMakeLists.txt | 4 +- alc/alc.cpp | 2 +- alc/alconfig.cpp | 2 +- alc/backends/alsa.cpp | 2 +- alc/backends/dsound.cpp | 2 +- alc/backends/jack.cpp | 2 +- alc/backends/null.cpp | 2 +- alc/backends/opensl.cpp | 2 +- alc/backends/oss.cpp | 2 +- alc/backends/pulseaudio.cpp | 2 +- alc/backends/sndio.cpp | 2 +- alc/backends/solaris.cpp | 2 +- alc/backends/wasapi.cpp | 2 +- alc/backends/wave.cpp | 2 +- alc/backends/winmm.cpp | 2 +- alc/compat.h | 18 -- alc/helpers.cpp | 528 -------------------------------------------- alc/hrtf.cpp | 2 +- core/helpers.cpp | 510 ++++++++++++++++++++++++++++++++++++++++++ core/helpers.h | 18 ++ 20 files changed, 545 insertions(+), 563 deletions(-) delete mode 100644 alc/compat.h delete mode 100644 alc/helpers.cpp create mode 100644 core/helpers.cpp create mode 100644 core/helpers.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f1894fa..1b5f8395 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -667,6 +667,8 @@ set(CORE_OBJS core/fmt_traits.h core/fpu_ctrl.cpp core/fpu_ctrl.h + core/helpers.cpp + core/helpers.h core/logging.cpp core/logging.h core/mastering.cpp @@ -751,7 +753,6 @@ set(ALC_OBJS alc/bformatdec.h alc/buffer_storage.cpp alc/buffer_storage.h - alc/compat.h alc/converter.cpp alc/converter.h alc/effectslot.cpp @@ -772,7 +773,6 @@ set(ALC_OBJS alc/effects/reverb.cpp alc/effects/vmorpher.cpp alc/front_stablizer.h - alc/helpers.cpp alc/hrtf.cpp alc/hrtf.h alc/inprogext.h diff --git a/alc/alc.cpp b/alc/alc.cpp index b79ea9c8..c4e2d61c 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -79,12 +79,12 @@ #include "async_event.h" #include "atomic.h" #include "bformatdec.h" -#include "compat.h" #include "core/ambidefs.h" #include "core/bs2b.h" #include "core/cpu_caps.h" #include "core/devformat.h" #include "core/except.h" +#include "core/helpers.h" #include "core/mastering.h" #include "core/filters/nfc.h" #include "core/filters/splitter.h" diff --git a/alc/alconfig.cpp b/alc/alconfig.cpp index 634679aa..5e00c054 100644 --- a/alc/alconfig.cpp +++ b/alc/alconfig.cpp @@ -40,7 +40,7 @@ #include "alfstream.h" #include "alstring.h" -#include "compat.h" +#include "core/helpers.h" #include "core/logging.h" #include "strutils.h" #include "vector.h" diff --git a/alc/backends/alsa.cpp b/alc/backends/alsa.cpp index fe19be04..1153b99a 100644 --- a/alc/backends/alsa.cpp +++ b/alc/backends/alsa.cpp @@ -42,7 +42,7 @@ #include "alnumeric.h" #include "aloptional.h" #include "alu.h" -#include "compat.h" +#include "core/helpers.h" #include "core/logging.h" #include "dynload.h" #include "ringbuffer.h" diff --git a/alc/backends/dsound.cpp b/alc/backends/dsound.cpp index 72f9c47a..3c6c5615 100644 --- a/alc/backends/dsound.cpp +++ b/alc/backends/dsound.cpp @@ -46,8 +46,8 @@ #include "alcmain.h" #include "alu.h" -#include "compat.h" #include "comptr.h" +#include "core/helpers.h" #include "core/logging.h" #include "dynload.h" #include "ringbuffer.h" diff --git a/alc/backends/jack.cpp b/alc/backends/jack.cpp index cf52e7a4..fe86f08c 100644 --- a/alc/backends/jack.cpp +++ b/alc/backends/jack.cpp @@ -34,7 +34,7 @@ #include "alcmain.h" #include "alu.h" #include "alconfig.h" -#include "compat.h" +#include "core/helpers.h" #include "core/logging.h" #include "dynload.h" #include "ringbuffer.h" diff --git a/alc/backends/null.cpp b/alc/backends/null.cpp index bf267163..d3dfc3a4 100644 --- a/alc/backends/null.cpp +++ b/alc/backends/null.cpp @@ -33,7 +33,7 @@ #include "alcmain.h" #include "almalloc.h" #include "alu.h" -#include "compat.h" +#include "core/helpers.h" #include "threads.h" diff --git a/alc/backends/opensl.cpp b/alc/backends/opensl.cpp index cae17ce1..8e5c2783 100644 --- a/alc/backends/opensl.cpp +++ b/alc/backends/opensl.cpp @@ -35,7 +35,7 @@ #include "albit.h" #include "alcmain.h" #include "alu.h" -#include "compat.h" +#include "core/helpers.h" #include "core/logging.h" #include "opthelpers.h" #include "ringbuffer.h" diff --git a/alc/backends/oss.cpp b/alc/backends/oss.cpp index 3db80961..22289b74 100644 --- a/alc/backends/oss.cpp +++ b/alc/backends/oss.cpp @@ -48,7 +48,7 @@ #include "alnumeric.h" #include "aloptional.h" #include "alu.h" -#include "compat.h" +#include "core/helpers.h" #include "core/logging.h" #include "ringbuffer.h" #include "threads.h" diff --git a/alc/backends/pulseaudio.cpp b/alc/backends/pulseaudio.cpp index 0011c1c7..bf3fc18e 100644 --- a/alc/backends/pulseaudio.cpp +++ b/alc/backends/pulseaudio.cpp @@ -38,7 +38,7 @@ #include "alcmain.h" #include "alu.h" #include "alconfig.h" -#include "compat.h" +#include "core/helpers.h" #include "core/logging.h" #include "dynload.h" #include "strutils.h" diff --git a/alc/backends/sndio.cpp b/alc/backends/sndio.cpp index f5dbdcb2..35b7e6c8 100644 --- a/alc/backends/sndio.cpp +++ b/alc/backends/sndio.cpp @@ -31,7 +31,7 @@ #include "alcmain.h" #include "alu.h" -#include "compat.h" +#include "core/helpers.h" #include "core/logging.h" #include "ringbuffer.h" #include "threads.h" diff --git a/alc/backends/solaris.cpp b/alc/backends/solaris.cpp index 68ab814c..7739cba7 100644 --- a/alc/backends/solaris.cpp +++ b/alc/backends/solaris.cpp @@ -43,7 +43,7 @@ #include "albyte.h" #include "alu.h" #include "alconfig.h" -#include "compat.h" +#include "core/helpers.h" #include "core/logging.h" #include "threads.h" #include "vector.h" diff --git a/alc/backends/wasapi.cpp b/alc/backends/wasapi.cpp index 6e54fa48..4df64e73 100644 --- a/alc/backends/wasapi.cpp +++ b/alc/backends/wasapi.cpp @@ -58,9 +58,9 @@ #include "albit.h" #include "alcmain.h" #include "alu.h" -#include "compat.h" #include "comptr.h" #include "converter.h" +#include "core/helpers.h" #include "core/logging.h" #include "ringbuffer.h" #include "strutils.h" diff --git a/alc/backends/wave.cpp b/alc/backends/wave.cpp index e80fb3ae..00ff6ec6 100644 --- a/alc/backends/wave.cpp +++ b/alc/backends/wave.cpp @@ -40,7 +40,7 @@ #include "almalloc.h" #include "alnumeric.h" #include "alu.h" -#include "compat.h" +#include "core/helpers.h" #include "core/logging.h" #include "opthelpers.h" #include "strutils.h" diff --git a/alc/backends/winmm.cpp b/alc/backends/winmm.cpp index 14fda45b..693c4664 100644 --- a/alc/backends/winmm.cpp +++ b/alc/backends/winmm.cpp @@ -40,7 +40,7 @@ #include "alcmain.h" #include "alu.h" -#include "compat.h" +#include "core/helpers.h" #include "core/logging.h" #include "ringbuffer.h" #include "strutils.h" diff --git a/alc/compat.h b/alc/compat.h deleted file mode 100644 index d858e5c9..00000000 --- a/alc/compat.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef AL_COMPAT_H -#define AL_COMPAT_H - -#include - -#include "vector.h" - - -struct PathNamePair { std::string path, fname; }; -const PathNamePair &GetProcBinary(void); - -extern int RTPrioLevel; -extern bool AllowRTTimeLimit; -void SetRTPriority(void); - -al::vector SearchDataFiles(const char *match, const char *subdir); - -#endif /* AL_COMPAT_H */ diff --git a/alc/helpers.cpp b/alc/helpers.cpp deleted file mode 100644 index ae9ba4ba..00000000 --- a/alc/helpers.cpp +++ /dev/null @@ -1,528 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 2011 by authors. - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include "alcmain.h" -#include "almalloc.h" -#include "alfstream.h" -#include "aloptional.h" -#include "alspan.h" -#include "alstring.h" -#include "compat.h" -#include "core/logging.h" -#include "strutils.h" -#include "vector.h" - - -/* Mixing thread piority level */ -int RTPrioLevel{1}; - -/* Allow reducing the process's RTTime limit for RTKit. */ -bool AllowRTTimeLimit{true}; - - -#ifdef _WIN32 - -#include - -const PathNamePair &GetProcBinary() -{ - static al::optional procbin; - if(procbin) return *procbin; - - auto fullpath = al::vector(256); - DWORD len; - while((len=GetModuleFileNameW(nullptr, fullpath.data(), static_cast(fullpath.size()))) == fullpath.size()) - fullpath.resize(fullpath.size() << 1); - if(len == 0) - { - ERR("Failed to get process name: error %lu\n", GetLastError()); - procbin = al::make_optional(); - return *procbin; - } - - fullpath.resize(len); - if(fullpath.back() != 0) - fullpath.push_back(0); - - auto sep = std::find(fullpath.rbegin()+1, fullpath.rend(), '\\'); - sep = std::find(fullpath.rbegin()+1, sep, '/'); - if(sep != fullpath.rend()) - { - *sep = 0; - procbin = al::make_optional(wstr_to_utf8(fullpath.data()), - wstr_to_utf8(&*sep + 1)); - } - else - procbin = al::make_optional(std::string{}, wstr_to_utf8(fullpath.data())); - - TRACE("Got binary: %s, %s\n", procbin->path.c_str(), procbin->fname.c_str()); - return *procbin; -} - -namespace { - -void DirectorySearch(const char *path, const char *ext, al::vector *const results) -{ - std::string pathstr{path}; - pathstr += "\\*"; - pathstr += ext; - TRACE("Searching %s\n", pathstr.c_str()); - - std::wstring wpath{utf8_to_wstr(pathstr.c_str())}; - WIN32_FIND_DATAW fdata; - HANDLE hdl{FindFirstFileW(wpath.c_str(), &fdata)}; - if(hdl == INVALID_HANDLE_VALUE) return; - - const auto base = results->size(); - - do { - results->emplace_back(); - std::string &str = results->back(); - str = path; - str += '\\'; - str += wstr_to_utf8(fdata.cFileName); - } while(FindNextFileW(hdl, &fdata)); - FindClose(hdl); - - const al::span newlist{results->data()+base, results->size()-base}; - std::sort(newlist.begin(), newlist.end()); - for(const auto &name : newlist) - TRACE(" got %s\n", name.c_str()); -} - -} // namespace - -al::vector SearchDataFiles(const char *ext, const char *subdir) -{ - auto is_slash = [](int c) noexcept -> int { return (c == '\\' || c == '/'); }; - - static std::mutex search_lock; - std::lock_guard _{search_lock}; - - /* If the path is absolute, use it directly. */ - al::vector results; - if(isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2])) - { - std::string path{subdir}; - std::replace(path.begin(), path.end(), '/', '\\'); - DirectorySearch(path.c_str(), ext, &results); - return results; - } - if(subdir[0] == '\\' && subdir[1] == '\\' && subdir[2] == '?' && subdir[3] == '\\') - { - DirectorySearch(subdir, ext, &results); - return results; - } - - std::string path; - - /* Search the app-local directory. */ - if(auto localpath = al::getenv(L"ALSOFT_LOCAL_PATH")) - { - path = wstr_to_utf8(localpath->c_str()); - if(is_slash(path.back())) - path.pop_back(); - } - else if(WCHAR *cwdbuf{_wgetcwd(nullptr, 0)}) - { - path = wstr_to_utf8(cwdbuf); - if(is_slash(path.back())) - path.pop_back(); - free(cwdbuf); - } - else - path = "."; - std::replace(path.begin(), path.end(), '/', '\\'); - DirectorySearch(path.c_str(), ext, &results); - - /* Search the local and global data dirs. */ - static const int ids[2]{ CSIDL_APPDATA, CSIDL_COMMON_APPDATA }; - for(int id : ids) - { - WCHAR buffer[MAX_PATH]; - if(SHGetSpecialFolderPathW(nullptr, buffer, id, FALSE) == FALSE) - continue; - - path = wstr_to_utf8(buffer); - if(!is_slash(path.back())) - path += '\\'; - path += subdir; - std::replace(path.begin(), path.end(), '/', '\\'); - - DirectorySearch(path.c_str(), ext, &results); - } - - return results; -} - -void SetRTPriority(void) -{ - if(RTPrioLevel > 0) - { - if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) - ERR("Failed to set priority level for thread\n"); - } -} - -#else - -#include -#include -#include -#ifdef __FreeBSD__ -#include -#endif -#ifdef __HAIKU__ -#include -#endif -#ifdef HAVE_PROC_PIDPATH -#include -#endif -#if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__) -#include -#include -#endif -#ifdef HAVE_RTKIT -#include -#include - -#include "core/rtkit.h" -#ifndef RLIMIT_RTTIME -#define RLIMIT_RTTIME 15 -#endif -#endif - -const PathNamePair &GetProcBinary() -{ - static al::optional procbin; - if(procbin) return *procbin; - - al::vector pathname; -#ifdef __FreeBSD__ - size_t pathlen; - int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; - if(sysctl(mib, 4, nullptr, &pathlen, nullptr, 0) == -1) - WARN("Failed to sysctl kern.proc.pathname: %s\n", strerror(errno)); - else - { - pathname.resize(pathlen + 1); - sysctl(mib, 4, pathname.data(), &pathlen, nullptr, 0); - pathname.resize(pathlen); - } -#endif -#ifdef HAVE_PROC_PIDPATH - if(pathname.empty()) - { - char procpath[PROC_PIDPATHINFO_MAXSIZE]{}; - const pid_t pid{getpid()}; - if(proc_pidpath(pid, procpath, sizeof(procpath)) < 1) - ERR("proc_pidpath(%d, ...) failed: %s\n", pid, strerror(errno)); - else - pathname.insert(pathname.end(), procpath, procpath+strlen(procpath)); - } -#endif -#ifdef __HAIKU__ - if(pathname.empty()) - { - char procpath[PATH_MAX]; - if(find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, procpath, sizeof(procpath)) == B_OK) - pathname.insert(pathname.end(), procpath, procpath+strlen(procpath)); - } -#endif -#ifndef __SWITCH__ - if(pathname.empty()) - { - static const char SelfLinkNames[][32]{ - "/proc/self/exe", - "/proc/self/file", - "/proc/curproc/exe", - "/proc/curproc/file" - }; - - pathname.resize(256); - - const char *selfname{}; - ssize_t len{}; - for(const char *name : SelfLinkNames) - { - selfname = name; - len = readlink(selfname, pathname.data(), pathname.size()); - if(len >= 0 || errno != ENOENT) break; - } - - while(len > 0 && static_cast(len) == pathname.size()) - { - pathname.resize(pathname.size() << 1); - len = readlink(selfname, pathname.data(), pathname.size()); - } - if(len <= 0) - { - WARN("Failed to readlink %s: %s\n", selfname, strerror(errno)); - len = 0; - } - - pathname.resize(static_cast(len)); - } -#endif - while(!pathname.empty() && pathname.back() == 0) - pathname.pop_back(); - - auto sep = std::find(pathname.crbegin(), pathname.crend(), '/'); - if(sep != pathname.crend()) - procbin = al::make_optional(std::string(pathname.cbegin(), sep.base()-1), - std::string(sep.base(), pathname.cend())); - else - procbin = al::make_optional(std::string{}, - std::string(pathname.cbegin(), pathname.cend())); - - TRACE("Got binary: \"%s\", \"%s\"\n", procbin->path.c_str(), procbin->fname.c_str()); - return *procbin; -} - -namespace { - -void DirectorySearch(const char *path, const char *ext, al::vector *const results) -{ - TRACE("Searching %s for *%s\n", path, ext); - DIR *dir{opendir(path)}; - if(!dir) return; - - const auto base = results->size(); - const size_t extlen{strlen(ext)}; - - while(struct dirent *dirent{readdir(dir)}) - { - if(strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) - continue; - - const size_t len{strlen(dirent->d_name)}; - if(len <= extlen) continue; - if(al::strcasecmp(dirent->d_name+len-extlen, ext) != 0) - continue; - - results->emplace_back(); - std::string &str = results->back(); - str = path; - if(str.back() != '/') - str.push_back('/'); - str += dirent->d_name; - } - closedir(dir); - - const al::span newlist{results->data()+base, results->size()-base}; - std::sort(newlist.begin(), newlist.end()); - for(const auto &name : newlist) - TRACE(" got %s\n", name.c_str()); -} - -} // namespace - -al::vector SearchDataFiles(const char *ext, const char *subdir) -{ - static std::mutex search_lock; - std::lock_guard _{search_lock}; - - al::vector results; - if(subdir[0] == '/') - { - DirectorySearch(subdir, ext, &results); - return results; - } - - /* Search the app-local directory. */ - if(auto localpath = al::getenv("ALSOFT_LOCAL_PATH")) - DirectorySearch(localpath->c_str(), ext, &results); - else - { - al::vector cwdbuf(256); - while(!getcwd(cwdbuf.data(), cwdbuf.size())) - { - if(errno != ERANGE) - { - cwdbuf.clear(); - break; - } - cwdbuf.resize(cwdbuf.size() << 1); - } - if(cwdbuf.empty()) - DirectorySearch(".", ext, &results); - else - { - DirectorySearch(cwdbuf.data(), ext, &results); - cwdbuf.clear(); - } - } - - // Search local data dir - if(auto datapath = al::getenv("XDG_DATA_HOME")) - { - std::string &path = *datapath; - if(path.back() != '/') - path += '/'; - path += subdir; - DirectorySearch(path.c_str(), ext, &results); - } - else if(auto homepath = al::getenv("HOME")) - { - std::string &path = *homepath; - if(path.back() == '/') - path.pop_back(); - path += "/.local/share/"; - path += subdir; - DirectorySearch(path.c_str(), ext, &results); - } - - // Search global data dirs - std::string datadirs{al::getenv("XDG_DATA_DIRS").value_or("/usr/local/share/:/usr/share/")}; - - size_t curpos{0u}; - while(curpos < datadirs.size()) - { - size_t nextpos{datadirs.find(':', curpos)}; - - std::string path{(nextpos != std::string::npos) ? - datadirs.substr(curpos, nextpos++ - curpos) : datadirs.substr(curpos)}; - curpos = nextpos; - - if(path.empty()) continue; - if(path.back() != '/') - path += '/'; - path += subdir; - - DirectorySearch(path.c_str(), ext, &results); - } - - return results; -} - -void SetRTPriority() -{ - if(RTPrioLevel <= 0) - return; - - int err{-ENOTSUP}; -#if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__) - struct sched_param param{}; - /* Use the minimum real-time priority possible for now (on Linux this - * should be 1 for SCHED_RR). - */ - param.sched_priority = sched_get_priority_min(SCHED_RR); -#ifdef SCHED_RESET_ON_FORK - err = pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, ¶m); - if(err == EINVAL) -#endif - err = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); - if(err == 0) return; - - WARN("pthread_setschedparam failed: %s (%d)\n", std::strerror(err), err); -#endif -#ifdef HAVE_RTKIT - if(HasDBus()) - { - dbus::Error error; - if(dbus::ConnectionPtr conn{(*pdbus_bus_get)(DBUS_BUS_SYSTEM, &error.get())}) - { - using ulonglong = unsigned long long; - auto limit_rttime = [](DBusConnection *conn) -> int - { - long long maxrttime{rtkit_get_rttime_usec_max(conn)}; - if(maxrttime <= 0) return static_cast(std::abs(maxrttime)); - const ulonglong umaxtime{static_cast(maxrttime)}; - - struct rlimit rlim{}; - if(getrlimit(RLIMIT_RTTIME, &rlim) != 0) - return errno; - TRACE("RTTime max: %llu (hard: %llu, soft: %llu)\n", umaxtime, - ulonglong{rlim.rlim_max}, ulonglong{rlim.rlim_cur}); - if(rlim.rlim_max > umaxtime) - { - rlim.rlim_max = static_cast(std::min(umaxtime, - std::numeric_limits::max())); - rlim.rlim_cur = std::min(rlim.rlim_cur, rlim.rlim_max); - if(setrlimit(RLIMIT_RTTIME, &rlim) != 0) - return errno; - } - return 0; - }; - - /* Don't stupidly exit if the connection dies while doing this. */ - (*pdbus_connection_set_exit_on_disconnect)(conn.get(), false); - - int nicemin{}; - err = rtkit_get_min_nice_level(conn.get(), &nicemin); - if(err == -ENOENT) - { - err = std::abs(err); - ERR("Could not query RTKit: %s (%d)\n", std::strerror(err), err); - return; - } - int rtmax{rtkit_get_max_realtime_priority(conn.get())}; - TRACE("Maximum real-time priority: %d, minimum niceness: %d\n", rtmax, nicemin); - - err = EINVAL; - if(rtmax > 0) - { - if(AllowRTTimeLimit) - { - err = limit_rttime(conn.get()); - if(err != 0) - WARN("Failed to set RLIMIT_RTTIME for RTKit: %s (%d)\n", - std::strerror(err), err); - } - - /* Use half the maximum real-time priority allowed. */ - TRACE("Making real-time with priority %d\n", (rtmax+1)/2); - err = rtkit_make_realtime(conn.get(), 0, (rtmax+1)/2); - if(err == 0) return; - - err = std::abs(err); - WARN("Failed to set real-time priority: %s (%d)\n", std::strerror(err), err); - } - if(nicemin < 0) - { - TRACE("Making high priority with niceness %d\n", nicemin); - err = rtkit_make_high_priority(conn.get(), 0, nicemin); - if(err == 0) return; - - err = std::abs(err); - WARN("Failed to set high priority: %s (%d)\n", std::strerror(err), err); - } - } - else - WARN("D-Bus connection failed with %s: %s\n", error->name, error->message); - } - else - WARN("D-Bus not available\n"); -#endif - ERR("Could not set elevated priority: %s (%d)\n", std::strerror(err), err); -} - -#endif diff --git a/alc/hrtf.cpp b/alc/hrtf.cpp index 6d7bc949..6030bb65 100644 --- a/alc/hrtf.cpp +++ b/alc/hrtf.cpp @@ -48,9 +48,9 @@ #include "alnumeric.h" #include "aloptional.h" #include "alspan.h" -#include "compat.h" #include "core/ambidefs.h" #include "core/filters/splitter.h" +#include "core/helpers.h" #include "core/logging.h" #include "math_defs.h" #include "opthelpers.h" diff --git a/core/helpers.cpp b/core/helpers.cpp new file mode 100644 index 00000000..c03c3b62 --- /dev/null +++ b/core/helpers.cpp @@ -0,0 +1,510 @@ + +#include "config.h" + +#include "helpers.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "alcmain.h" +#include "almalloc.h" +#include "alfstream.h" +#include "aloptional.h" +#include "alspan.h" +#include "alstring.h" +#include "logging.h" +#include "strutils.h" +#include "vector.h" + + +/* Mixing thread piority level */ +int RTPrioLevel{1}; + +/* Allow reducing the process's RTTime limit for RTKit. */ +bool AllowRTTimeLimit{true}; + + +#ifdef _WIN32 + +#include + +const PathNamePair &GetProcBinary() +{ + static al::optional procbin; + if(procbin) return *procbin; + + auto fullpath = al::vector(256); + DWORD len; + while((len=GetModuleFileNameW(nullptr, fullpath.data(), static_cast(fullpath.size()))) == fullpath.size()) + fullpath.resize(fullpath.size() << 1); + if(len == 0) + { + ERR("Failed to get process name: error %lu\n", GetLastError()); + procbin = al::make_optional(); + return *procbin; + } + + fullpath.resize(len); + if(fullpath.back() != 0) + fullpath.push_back(0); + + auto sep = std::find(fullpath.rbegin()+1, fullpath.rend(), '\\'); + sep = std::find(fullpath.rbegin()+1, sep, '/'); + if(sep != fullpath.rend()) + { + *sep = 0; + procbin = al::make_optional(wstr_to_utf8(fullpath.data()), + wstr_to_utf8(&*sep + 1)); + } + else + procbin = al::make_optional(std::string{}, wstr_to_utf8(fullpath.data())); + + TRACE("Got binary: %s, %s\n", procbin->path.c_str(), procbin->fname.c_str()); + return *procbin; +} + +namespace { + +void DirectorySearch(const char *path, const char *ext, al::vector *const results) +{ + std::string pathstr{path}; + pathstr += "\\*"; + pathstr += ext; + TRACE("Searching %s\n", pathstr.c_str()); + + std::wstring wpath{utf8_to_wstr(pathstr.c_str())}; + WIN32_FIND_DATAW fdata; + HANDLE hdl{FindFirstFileW(wpath.c_str(), &fdata)}; + if(hdl == INVALID_HANDLE_VALUE) return; + + const auto base = results->size(); + + do { + results->emplace_back(); + std::string &str = results->back(); + str = path; + str += '\\'; + str += wstr_to_utf8(fdata.cFileName); + } while(FindNextFileW(hdl, &fdata)); + FindClose(hdl); + + const al::span newlist{results->data()+base, results->size()-base}; + std::sort(newlist.begin(), newlist.end()); + for(const auto &name : newlist) + TRACE(" got %s\n", name.c_str()); +} + +} // namespace + +al::vector SearchDataFiles(const char *ext, const char *subdir) +{ + auto is_slash = [](int c) noexcept -> int { return (c == '\\' || c == '/'); }; + + static std::mutex search_lock; + std::lock_guard _{search_lock}; + + /* If the path is absolute, use it directly. */ + al::vector results; + if(isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2])) + { + std::string path{subdir}; + std::replace(path.begin(), path.end(), '/', '\\'); + DirectorySearch(path.c_str(), ext, &results); + return results; + } + if(subdir[0] == '\\' && subdir[1] == '\\' && subdir[2] == '?' && subdir[3] == '\\') + { + DirectorySearch(subdir, ext, &results); + return results; + } + + std::string path; + + /* Search the app-local directory. */ + if(auto localpath = al::getenv(L"ALSOFT_LOCAL_PATH")) + { + path = wstr_to_utf8(localpath->c_str()); + if(is_slash(path.back())) + path.pop_back(); + } + else if(WCHAR *cwdbuf{_wgetcwd(nullptr, 0)}) + { + path = wstr_to_utf8(cwdbuf); + if(is_slash(path.back())) + path.pop_back(); + free(cwdbuf); + } + else + path = "."; + std::replace(path.begin(), path.end(), '/', '\\'); + DirectorySearch(path.c_str(), ext, &results); + + /* Search the local and global data dirs. */ + static const int ids[2]{ CSIDL_APPDATA, CSIDL_COMMON_APPDATA }; + for(int id : ids) + { + WCHAR buffer[MAX_PATH]; + if(SHGetSpecialFolderPathW(nullptr, buffer, id, FALSE) == FALSE) + continue; + + path = wstr_to_utf8(buffer); + if(!is_slash(path.back())) + path += '\\'; + path += subdir; + std::replace(path.begin(), path.end(), '/', '\\'); + + DirectorySearch(path.c_str(), ext, &results); + } + + return results; +} + +void SetRTPriority(void) +{ + if(RTPrioLevel > 0) + { + if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) + ERR("Failed to set priority level for thread\n"); + } +} + +#else + +#include +#include +#include +#ifdef __FreeBSD__ +#include +#endif +#ifdef __HAIKU__ +#include +#endif +#ifdef HAVE_PROC_PIDPATH +#include +#endif +#if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__) +#include +#include +#endif +#ifdef HAVE_RTKIT +#include +#include + +#include "core/rtkit.h" +#ifndef RLIMIT_RTTIME +#define RLIMIT_RTTIME 15 +#endif +#endif + +const PathNamePair &GetProcBinary() +{ + static al::optional procbin; + if(procbin) return *procbin; + + al::vector pathname; +#ifdef __FreeBSD__ + size_t pathlen; + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; + if(sysctl(mib, 4, nullptr, &pathlen, nullptr, 0) == -1) + WARN("Failed to sysctl kern.proc.pathname: %s\n", strerror(errno)); + else + { + pathname.resize(pathlen + 1); + sysctl(mib, 4, pathname.data(), &pathlen, nullptr, 0); + pathname.resize(pathlen); + } +#endif +#ifdef HAVE_PROC_PIDPATH + if(pathname.empty()) + { + char procpath[PROC_PIDPATHINFO_MAXSIZE]{}; + const pid_t pid{getpid()}; + if(proc_pidpath(pid, procpath, sizeof(procpath)) < 1) + ERR("proc_pidpath(%d, ...) failed: %s\n", pid, strerror(errno)); + else + pathname.insert(pathname.end(), procpath, procpath+strlen(procpath)); + } +#endif +#ifdef __HAIKU__ + if(pathname.empty()) + { + char procpath[PATH_MAX]; + if(find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, procpath, sizeof(procpath)) == B_OK) + pathname.insert(pathname.end(), procpath, procpath+strlen(procpath)); + } +#endif +#ifndef __SWITCH__ + if(pathname.empty()) + { + static const char SelfLinkNames[][32]{ + "/proc/self/exe", + "/proc/self/file", + "/proc/curproc/exe", + "/proc/curproc/file" + }; + + pathname.resize(256); + + const char *selfname{}; + ssize_t len{}; + for(const char *name : SelfLinkNames) + { + selfname = name; + len = readlink(selfname, pathname.data(), pathname.size()); + if(len >= 0 || errno != ENOENT) break; + } + + while(len > 0 && static_cast(len) == pathname.size()) + { + pathname.resize(pathname.size() << 1); + len = readlink(selfname, pathname.data(), pathname.size()); + } + if(len <= 0) + { + WARN("Failed to readlink %s: %s\n", selfname, strerror(errno)); + len = 0; + } + + pathname.resize(static_cast(len)); + } +#endif + while(!pathname.empty() && pathname.back() == 0) + pathname.pop_back(); + + auto sep = std::find(pathname.crbegin(), pathname.crend(), '/'); + if(sep != pathname.crend()) + procbin = al::make_optional(std::string(pathname.cbegin(), sep.base()-1), + std::string(sep.base(), pathname.cend())); + else + procbin = al::make_optional(std::string{}, + std::string(pathname.cbegin(), pathname.cend())); + + TRACE("Got binary: \"%s\", \"%s\"\n", procbin->path.c_str(), procbin->fname.c_str()); + return *procbin; +} + +namespace { + +void DirectorySearch(const char *path, const char *ext, al::vector *const results) +{ + TRACE("Searching %s for *%s\n", path, ext); + DIR *dir{opendir(path)}; + if(!dir) return; + + const auto base = results->size(); + const size_t extlen{strlen(ext)}; + + while(struct dirent *dirent{readdir(dir)}) + { + if(strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) + continue; + + const size_t len{strlen(dirent->d_name)}; + if(len <= extlen) continue; + if(al::strcasecmp(dirent->d_name+len-extlen, ext) != 0) + continue; + + results->emplace_back(); + std::string &str = results->back(); + str = path; + if(str.back() != '/') + str.push_back('/'); + str += dirent->d_name; + } + closedir(dir); + + const al::span newlist{results->data()+base, results->size()-base}; + std::sort(newlist.begin(), newlist.end()); + for(const auto &name : newlist) + TRACE(" got %s\n", name.c_str()); +} + +} // namespace + +al::vector SearchDataFiles(const char *ext, const char *subdir) +{ + static std::mutex search_lock; + std::lock_guard _{search_lock}; + + al::vector results; + if(subdir[0] == '/') + { + DirectorySearch(subdir, ext, &results); + return results; + } + + /* Search the app-local directory. */ + if(auto localpath = al::getenv("ALSOFT_LOCAL_PATH")) + DirectorySearch(localpath->c_str(), ext, &results); + else + { + al::vector cwdbuf(256); + while(!getcwd(cwdbuf.data(), cwdbuf.size())) + { + if(errno != ERANGE) + { + cwdbuf.clear(); + break; + } + cwdbuf.resize(cwdbuf.size() << 1); + } + if(cwdbuf.empty()) + DirectorySearch(".", ext, &results); + else + { + DirectorySearch(cwdbuf.data(), ext, &results); + cwdbuf.clear(); + } + } + + // Search local data dir + if(auto datapath = al::getenv("XDG_DATA_HOME")) + { + std::string &path = *datapath; + if(path.back() != '/') + path += '/'; + path += subdir; + DirectorySearch(path.c_str(), ext, &results); + } + else if(auto homepath = al::getenv("HOME")) + { + std::string &path = *homepath; + if(path.back() == '/') + path.pop_back(); + path += "/.local/share/"; + path += subdir; + DirectorySearch(path.c_str(), ext, &results); + } + + // Search global data dirs + std::string datadirs{al::getenv("XDG_DATA_DIRS").value_or("/usr/local/share/:/usr/share/")}; + + size_t curpos{0u}; + while(curpos < datadirs.size()) + { + size_t nextpos{datadirs.find(':', curpos)}; + + std::string path{(nextpos != std::string::npos) ? + datadirs.substr(curpos, nextpos++ - curpos) : datadirs.substr(curpos)}; + curpos = nextpos; + + if(path.empty()) continue; + if(path.back() != '/') + path += '/'; + path += subdir; + + DirectorySearch(path.c_str(), ext, &results); + } + + return results; +} + +void SetRTPriority() +{ + if(RTPrioLevel <= 0) + return; + + int err{-ENOTSUP}; +#if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__) + struct sched_param param{}; + /* Use the minimum real-time priority possible for now (on Linux this + * should be 1 for SCHED_RR). + */ + param.sched_priority = sched_get_priority_min(SCHED_RR); +#ifdef SCHED_RESET_ON_FORK + err = pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, ¶m); + if(err == EINVAL) +#endif + err = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); + if(err == 0) return; + + WARN("pthread_setschedparam failed: %s (%d)\n", std::strerror(err), err); +#endif +#ifdef HAVE_RTKIT + if(HasDBus()) + { + dbus::Error error; + if(dbus::ConnectionPtr conn{(*pdbus_bus_get)(DBUS_BUS_SYSTEM, &error.get())}) + { + using ulonglong = unsigned long long; + auto limit_rttime = [](DBusConnection *conn) -> int + { + long long maxrttime{rtkit_get_rttime_usec_max(conn)}; + if(maxrttime <= 0) return static_cast(std::abs(maxrttime)); + const ulonglong umaxtime{static_cast(maxrttime)}; + + struct rlimit rlim{}; + if(getrlimit(RLIMIT_RTTIME, &rlim) != 0) + return errno; + TRACE("RTTime max: %llu (hard: %llu, soft: %llu)\n", umaxtime, + ulonglong{rlim.rlim_max}, ulonglong{rlim.rlim_cur}); + if(rlim.rlim_max > umaxtime) + { + rlim.rlim_max = static_cast(std::min(umaxtime, + std::numeric_limits::max())); + rlim.rlim_cur = std::min(rlim.rlim_cur, rlim.rlim_max); + if(setrlimit(RLIMIT_RTTIME, &rlim) != 0) + return errno; + } + return 0; + }; + + /* Don't stupidly exit if the connection dies while doing this. */ + (*pdbus_connection_set_exit_on_disconnect)(conn.get(), false); + + int nicemin{}; + err = rtkit_get_min_nice_level(conn.get(), &nicemin); + if(err == -ENOENT) + { + err = std::abs(err); + ERR("Could not query RTKit: %s (%d)\n", std::strerror(err), err); + return; + } + int rtmax{rtkit_get_max_realtime_priority(conn.get())}; + TRACE("Maximum real-time priority: %d, minimum niceness: %d\n", rtmax, nicemin); + + err = EINVAL; + if(rtmax > 0) + { + if(AllowRTTimeLimit) + { + err = limit_rttime(conn.get()); + if(err != 0) + WARN("Failed to set RLIMIT_RTTIME for RTKit: %s (%d)\n", + std::strerror(err), err); + } + + /* Use half the maximum real-time priority allowed. */ + TRACE("Making real-time with priority %d\n", (rtmax+1)/2); + err = rtkit_make_realtime(conn.get(), 0, (rtmax+1)/2); + if(err == 0) return; + + err = std::abs(err); + WARN("Failed to set real-time priority: %s (%d)\n", std::strerror(err), err); + } + if(nicemin < 0) + { + TRACE("Making high priority with niceness %d\n", nicemin); + err = rtkit_make_high_priority(conn.get(), 0, nicemin); + if(err == 0) return; + + err = std::abs(err); + WARN("Failed to set high priority: %s (%d)\n", std::strerror(err), err); + } + } + else + WARN("D-Bus connection failed with %s: %s\n", error->name, error->message); + } + else + WARN("D-Bus not available\n"); +#endif + ERR("Could not set elevated priority: %s (%d)\n", std::strerror(err), err); +} + +#endif diff --git a/core/helpers.h b/core/helpers.h new file mode 100644 index 00000000..f0bfcf1b --- /dev/null +++ b/core/helpers.h @@ -0,0 +1,18 @@ +#ifndef CORE_HELPERS_H +#define CORE_HELPERS_H + +#include + +#include "vector.h" + + +struct PathNamePair { std::string path, fname; }; +const PathNamePair &GetProcBinary(void); + +extern int RTPrioLevel; +extern bool AllowRTTimeLimit; +void SetRTPriority(void); + +al::vector SearchDataFiles(const char *match, const char *subdir); + +#endif /* CORE_HELPERS_H */ -- cgit v1.2.3 From 65b85f7cb9cdac5cbad2cefbe2141141717c80cb Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 22 Apr 2021 10:26:20 -0700 Subject: Move hrtf.cpp/h to core --- CMakeLists.txt | 4 +- alc/alc.cpp | 2 +- alc/alcmain.h | 2 +- alc/alu.cpp | 2 +- alc/hrtf.cpp | 1468 ------------------------------------------------------- alc/hrtf.h | 91 ---- alc/panning.cpp | 2 +- alc/voice.cpp | 2 +- core/hrtf.cpp | 1447 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ core/hrtf.h | 91 ++++ 10 files changed, 1545 insertions(+), 1566 deletions(-) delete mode 100644 alc/hrtf.cpp delete mode 100644 alc/hrtf.h create mode 100644 core/hrtf.cpp create mode 100644 core/hrtf.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b5f8395..15feb2b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -669,6 +669,8 @@ set(CORE_OBJS core/fpu_ctrl.h core/helpers.cpp core/helpers.h + core/hrtf.cpp + core/hrtf.h core/logging.cpp core/logging.h core/mastering.cpp @@ -773,8 +775,6 @@ set(ALC_OBJS alc/effects/reverb.cpp alc/effects/vmorpher.cpp alc/front_stablizer.h - alc/hrtf.cpp - alc/hrtf.h alc/inprogext.h alc/panning.cpp alc/uiddefs.cpp diff --git a/alc/alc.cpp b/alc/alc.cpp index 86dac583..cc168b6a 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -89,11 +89,11 @@ #include "core/filters/nfc.h" #include "core/filters/splitter.h" #include "core/fpu_ctrl.h" +#include "core/hrtf.h" #include "core/logging.h" #include "core/uhjfilter.h" #include "effects/base.h" #include "front_stablizer.h" -#include "hrtf.h" #include "inprogext.h" #include "intrusive_ptr.h" #include "opthelpers.h" diff --git a/alc/alcmain.h b/alc/alcmain.h index d0f9d6e3..69f05bfe 100644 --- a/alc/alcmain.h +++ b/alc/alcmain.h @@ -26,8 +26,8 @@ #include "core/bufferline.h" #include "core/devformat.h" #include "core/filters/splitter.h" +#include "core/hrtf.h" #include "core/mixer/defs.h" -#include "hrtf.h" #include "inprogext.h" #include "intrusive_ptr.h" #include "vector.h" diff --git a/alc/alu.cpp b/alc/alu.cpp index 8dd36fb3..b2b0834a 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -62,13 +62,13 @@ #include "core/filters/nfc.h" #include "core/filters/splitter.h" #include "core/fpu_ctrl.h" +#include "core/hrtf.h" #include "core/mastering.h" #include "core/mixer/defs.h" #include "core/uhjfilter.h" #include "effects/base.h" #include "effectslot.h" #include "front_stablizer.h" -#include "hrtf.h" #include "inprogext.h" #include "math_defs.h" #include "opthelpers.h" diff --git a/alc/hrtf.cpp b/alc/hrtf.cpp deleted file mode 100644 index 3ab36622..00000000 --- a/alc/hrtf.cpp +++ /dev/null @@ -1,1468 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 2011 by Chris Robinson - * 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 "hrtf.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "albit.h" -#include "albyte.h" -#include "alfstream.h" -#include "almalloc.h" -#include "alnumeric.h" -#include "aloptional.h" -#include "alspan.h" -#include "core/ambidefs.h" -#include "core/filters/splitter.h" -#include "core/helpers.h" -#include "core/logging.h" -#include "math_defs.h" -#include "opthelpers.h" -#include "polyphase_resampler.h" - - -namespace { - -using namespace std::placeholders; - -struct HrtfEntry { - std::string mDispName; - std::string mFilename; -}; - -struct LoadedHrtf { - std::string mFilename; - std::unique_ptr mEntry; -}; - -/* Data set limits must be the same as or more flexible than those defined in - * the makemhr utility. - */ -constexpr uint MinFdCount{1}; -constexpr uint MaxFdCount{16}; - -constexpr uint MinFdDistance{50}; -constexpr uint MaxFdDistance{2500}; - -constexpr uint MinEvCount{5}; -constexpr uint MaxEvCount{181}; - -constexpr uint MinAzCount{1}; -constexpr uint MaxAzCount{255}; - -constexpr uint MaxHrirDelay{HrtfHistoryLength - 1}; - -constexpr uint HrirDelayFracBits{2}; -constexpr uint HrirDelayFracOne{1 << HrirDelayFracBits}; -constexpr uint HrirDelayFracHalf{HrirDelayFracOne >> 1}; - -static_assert(MaxHrirDelay*HrirDelayFracOne < 256, "MAX_HRIR_DELAY or DELAY_FRAC too large"); - -constexpr char magicMarker00[8]{'M','i','n','P','H','R','0','0'}; -constexpr char magicMarker01[8]{'M','i','n','P','H','R','0','1'}; -constexpr char magicMarker02[8]{'M','i','n','P','H','R','0','2'}; -constexpr char magicMarker03[8]{'M','i','n','P','H','R','0','3'}; - -/* First value for pass-through coefficients (remaining are 0), used for omni- - * directional sounds. */ -constexpr float PassthruCoeff{0.707106781187f/*sqrt(0.5)*/}; - -std::mutex LoadedHrtfLock; -al::vector LoadedHrtfs; - -std::mutex EnumeratedHrtfLock; -al::vector EnumeratedHrtfs; - - -class databuf final : public std::streambuf { - int_type underflow() override - { return traits_type::eof(); } - - pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override - { - if((mode&std::ios_base::out) || !(mode&std::ios_base::in)) - return traits_type::eof(); - - char_type *cur; - switch(whence) - { - case std::ios_base::beg: - if(offset < 0 || offset > egptr()-eback()) - return traits_type::eof(); - cur = eback() + offset; - break; - - case std::ios_base::cur: - if((offset >= 0 && offset > egptr()-gptr()) || - (offset < 0 && -offset > gptr()-eback())) - return traits_type::eof(); - cur = gptr() + offset; - break; - - case std::ios_base::end: - if(offset > 0 || -offset > egptr()-eback()) - return traits_type::eof(); - cur = egptr() + offset; - break; - - default: - return traits_type::eof(); - } - - setg(eback(), cur, egptr()); - return cur - eback(); - } - - pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override - { - // Simplified version of seekoff - if((mode&std::ios_base::out) || !(mode&std::ios_base::in)) - return traits_type::eof(); - - if(pos < 0 || pos > egptr()-eback()) - return traits_type::eof(); - - setg(eback(), eback() + static_cast(pos), egptr()); - return pos; - } - -public: - databuf(const char_type *start_, const char_type *end_) noexcept - { - setg(const_cast(start_), const_cast(start_), - const_cast(end_)); - } -}; - -class idstream final : public std::istream { - databuf mStreamBuf; - -public: - idstream(const char *start_, const char *end_) - : std::istream{nullptr}, mStreamBuf{start_, end_} - { init(&mStreamBuf); } -}; - - -struct IdxBlend { uint idx; float blend; }; -/* Calculate the elevation index given the polar elevation in radians. This - * will return an index between 0 and (evcount - 1). - */ -IdxBlend CalcEvIndex(uint evcount, float ev) -{ - ev = (al::MathDefs::Pi()*0.5f + ev) * static_cast(evcount-1) / - al::MathDefs::Pi(); - uint idx{float2uint(ev)}; - - return IdxBlend{minu(idx, evcount-1), ev-static_cast(idx)}; -} - -/* Calculate the azimuth index given the polar azimuth in radians. This will - * return an index between 0 and (azcount - 1). - */ -IdxBlend CalcAzIndex(uint azcount, float az) -{ - az = (al::MathDefs::Tau()+az) * static_cast(azcount) / - al::MathDefs::Tau(); - uint idx{float2uint(az)}; - - return IdxBlend{idx%azcount, az-static_cast(idx)}; -} - -} // namespace - - -/* Calculates static HRIR coefficients and delays for the given polar elevation - * and azimuth in radians. The coefficients are normalized. - */ -void GetHrtfCoeffs(const HrtfStore *Hrtf, float elevation, float azimuth, float distance, - float spread, HrirArray &coeffs, const al::span delays) -{ - const float dirfact{1.0f - (spread / al::MathDefs::Tau())}; - - const auto *field = Hrtf->field; - const auto *field_end = field + Hrtf->fdCount-1; - size_t ebase{0}; - while(distance < field->distance && field != field_end) - { - ebase += field->evCount; - ++field; - } - - /* Calculate the elevation indices. */ - const auto elev0 = CalcEvIndex(field->evCount, elevation); - const size_t elev1_idx{minu(elev0.idx+1, field->evCount-1)}; - const size_t ir0offset{Hrtf->elev[ebase + elev0.idx].irOffset}; - const size_t ir1offset{Hrtf->elev[ebase + elev1_idx].irOffset}; - - /* Calculate azimuth indices. */ - const auto az0 = CalcAzIndex(Hrtf->elev[ebase + elev0.idx].azCount, azimuth); - const auto az1 = CalcAzIndex(Hrtf->elev[ebase + elev1_idx].azCount, azimuth); - - /* Calculate the HRIR indices to blend. */ - const size_t idx[4]{ - ir0offset + az0.idx, - ir0offset + ((az0.idx+1) % Hrtf->elev[ebase + elev0.idx].azCount), - ir1offset + az1.idx, - ir1offset + ((az1.idx+1) % Hrtf->elev[ebase + elev1_idx].azCount) - }; - - /* Calculate bilinear blending weights, attenuated according to the - * directional panning factor. - */ - const float blend[4]{ - (1.0f-elev0.blend) * (1.0f-az0.blend) * dirfact, - (1.0f-elev0.blend) * ( az0.blend) * dirfact, - ( elev0.blend) * (1.0f-az1.blend) * dirfact, - ( elev0.blend) * ( az1.blend) * dirfact - }; - - /* Calculate the blended HRIR delays. */ - float d{Hrtf->delays[idx[0]][0]*blend[0] + Hrtf->delays[idx[1]][0]*blend[1] + - Hrtf->delays[idx[2]][0]*blend[2] + Hrtf->delays[idx[3]][0]*blend[3]}; - delays[0] = fastf2u(d * float{1.0f/HrirDelayFracOne}); - d = Hrtf->delays[idx[0]][1]*blend[0] + Hrtf->delays[idx[1]][1]*blend[1] + - Hrtf->delays[idx[2]][1]*blend[2] + Hrtf->delays[idx[3]][1]*blend[3]; - delays[1] = fastf2u(d * float{1.0f/HrirDelayFracOne}); - - /* Calculate the blended HRIR coefficients. */ - float *coeffout{al::assume_aligned<16>(&coeffs[0][0])}; - coeffout[0] = PassthruCoeff * (1.0f-dirfact); - coeffout[1] = PassthruCoeff * (1.0f-dirfact); - std::fill_n(coeffout+2, size_t{HrirLength-1}*2, 0.0f); - for(size_t c{0};c < 4;c++) - { - const float *srccoeffs{al::assume_aligned<16>(Hrtf->coeffs[idx[c]][0].data())}; - const float mult{blend[c]}; - auto blend_coeffs = [mult](const float src, const float coeff) noexcept -> float - { return src*mult + coeff; }; - std::transform(srccoeffs, srccoeffs + HrirLength*2, coeffout, coeffout, blend_coeffs); - } -} - - -std::unique_ptr DirectHrtfState::Create(size_t num_chans) -{ return std::unique_ptr{new(FamCount(num_chans)) DirectHrtfState{num_chans}}; } - -void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, - const al::span AmbiPoints, const float (*AmbiMatrix)[MaxAmbiChannels], - const float XOverFreq, const al::span AmbiOrderHFGain) -{ - using double2 = std::array; - struct ImpulseResponse { - const ConstHrirSpan hrir; - uint ldelay, rdelay; - }; - - const double xover_norm{double{XOverFreq} / Hrtf->sampleRate}; - for(size_t i{0};i < mChannels.size();++i) - { - const size_t order{AmbiIndex::OrderFromChannel()[i]}; - mChannels[i].mSplitter.init(static_cast(xover_norm)); - mChannels[i].mHfScale = AmbiOrderHFGain[order]; - } - - uint min_delay{HrtfHistoryLength*HrirDelayFracOne}, max_delay{0}; - al::vector impres; impres.reserve(AmbiPoints.size()); - auto calc_res = [Hrtf,&max_delay,&min_delay](const AngularPoint &pt) -> ImpulseResponse - { - auto &field = Hrtf->field[0]; - const auto elev0 = CalcEvIndex(field.evCount, pt.Elev.value); - const size_t elev1_idx{minu(elev0.idx+1, field.evCount-1)}; - const size_t ir0offset{Hrtf->elev[elev0.idx].irOffset}; - const size_t ir1offset{Hrtf->elev[elev1_idx].irOffset}; - - const auto az0 = CalcAzIndex(Hrtf->elev[elev0.idx].azCount, pt.Azim.value); - const auto az1 = CalcAzIndex(Hrtf->elev[elev1_idx].azCount, pt.Azim.value); - - const size_t idx[4]{ - ir0offset + az0.idx, - ir0offset + ((az0.idx+1) % Hrtf->elev[elev0.idx].azCount), - ir1offset + az1.idx, - ir1offset + ((az1.idx+1) % Hrtf->elev[elev1_idx].azCount) - }; - - const std::array blend{{ - (1.0-elev0.blend) * (1.0-az0.blend), - (1.0-elev0.blend) * ( az0.blend), - ( elev0.blend) * (1.0-az1.blend), - ( elev0.blend) * ( az1.blend) - }}; - - /* The largest blend factor serves as the closest HRIR. */ - const size_t irOffset{idx[std::max_element(blend.begin(), blend.end()) - blend.begin()]}; - ImpulseResponse res{Hrtf->coeffs[irOffset], - Hrtf->delays[irOffset][0], Hrtf->delays[irOffset][1]}; - - min_delay = minu(min_delay, minu(res.ldelay, res.rdelay)); - max_delay = maxu(max_delay, maxu(res.ldelay, res.rdelay)); - - return res; - }; - std::transform(AmbiPoints.begin(), AmbiPoints.end(), std::back_inserter(impres), calc_res); - auto hrir_delay_round = [](const uint d) noexcept -> uint - { return (d+HrirDelayFracHalf) >> HrirDelayFracBits; }; - - TRACE("Min delay: %.2f, max delay: %.2f, FIR length: %u\n", - min_delay/double{HrirDelayFracOne}, max_delay/double{HrirDelayFracOne}, irSize); - - const bool per_hrir_min{mChannels.size() > AmbiChannelsFromOrder(1)}; - auto tmpres = al::vector>(mChannels.size()); - max_delay = 0; - for(size_t c{0u};c < AmbiPoints.size();++c) - { - const ConstHrirSpan hrir{impres[c].hrir}; - const uint base_delay{per_hrir_min ? minu(impres[c].ldelay, impres[c].rdelay) : min_delay}; - const uint ldelay{hrir_delay_round(impres[c].ldelay - base_delay)}; - const uint rdelay{hrir_delay_round(impres[c].rdelay - base_delay)}; - max_delay = maxu(max_delay, maxu(impres[c].ldelay, impres[c].rdelay) - base_delay); - - for(size_t i{0u};i < mChannels.size();++i) - { - const double mult{AmbiMatrix[c][i]}; - const size_t numirs{HrirLength - maxz(ldelay, rdelay)}; - size_t lidx{ldelay}, ridx{rdelay}; - for(size_t j{0};j < numirs;++j) - { - tmpres[i][lidx++][0] += hrir[j][0] * mult; - tmpres[i][ridx++][1] += hrir[j][1] * mult; - } - } - } - impres.clear(); - - for(size_t i{0u};i < mChannels.size();++i) - { - auto copy_arr = [](const double2 &in) noexcept -> float2 - { return float2{{static_cast(in[0]), static_cast(in[1])}}; }; - std::transform(tmpres[i].cbegin(), tmpres[i].cend(), mChannels[i].mCoeffs.begin(), - copy_arr); - } - tmpres.clear(); - - const uint max_length{minu(hrir_delay_round(max_delay) + irSize, HrirLength)}; - TRACE("New max delay: %.2f, FIR length: %u\n", max_delay/double{HrirDelayFracOne}, - max_length); - mIrSize = max_length; -} - - -namespace { - -std::unique_ptr CreateHrtfStore(uint rate, ushort irSize, - const al::span fields, - const al::span elevs, const HrirArray *coeffs, - const ubyte2 *delays, const char *filename) -{ - std::unique_ptr Hrtf; - - const size_t irCount{size_t{elevs.back().azCount} + elevs.back().irOffset}; - size_t total{sizeof(HrtfStore)}; - total = RoundUp(total, alignof(HrtfStore::Field)); /* Align for field infos */ - total += sizeof(HrtfStore::Field)*fields.size(); - total = RoundUp(total, alignof(HrtfStore::Elevation)); /* Align for elevation infos */ - total += sizeof(Hrtf->elev[0])*elevs.size(); - total = RoundUp(total, 16); /* Align for coefficients using SIMD */ - total += sizeof(Hrtf->coeffs[0])*irCount; - total += sizeof(Hrtf->delays[0])*irCount; - - Hrtf.reset(new (al_calloc(16, total)) HrtfStore{}); - if(!Hrtf) - ERR("Out of memory allocating storage for %s.\n", filename); - else - { - InitRef(Hrtf->mRef, 1u); - Hrtf->sampleRate = rate; - Hrtf->irSize = irSize; - Hrtf->fdCount = static_cast(fields.size()); - - /* Set up pointers to storage following the main HRTF struct. */ - char *base = reinterpret_cast(Hrtf.get()); - size_t offset{sizeof(HrtfStore)}; - - offset = RoundUp(offset, alignof(HrtfStore::Field)); /* Align for field infos */ - auto field_ = reinterpret_cast(base + offset); - offset += sizeof(field_[0])*fields.size(); - - offset = RoundUp(offset, alignof(HrtfStore::Elevation)); /* Align for elevation infos */ - auto elev_ = reinterpret_cast(base + offset); - offset += sizeof(elev_[0])*elevs.size(); - - offset = RoundUp(offset, 16); /* Align for coefficients using SIMD */ - auto coeffs_ = reinterpret_cast(base + offset); - offset += sizeof(coeffs_[0])*irCount; - - auto delays_ = reinterpret_cast(base + offset); - offset += sizeof(delays_[0])*irCount; - - assert(offset == total); - - /* Copy input data to storage. */ - std::copy(fields.cbegin(), fields.cend(), field_); - std::copy(elevs.cbegin(), elevs.cend(), elev_); - std::copy_n(coeffs, irCount, coeffs_); - std::copy_n(delays, irCount, delays_); - - /* Finally, assign the storage pointers. */ - Hrtf->field = field_; - Hrtf->elev = elev_; - Hrtf->coeffs = coeffs_; - Hrtf->delays = delays_; - } - - return Hrtf; -} - -void MirrorLeftHrirs(const al::span elevs, HrirArray *coeffs, - ubyte2 *delays) -{ - for(const auto &elev : elevs) - { - const ushort evoffset{elev.irOffset}; - const ushort azcount{elev.azCount}; - for(size_t j{0};j < azcount;j++) - { - const size_t lidx{evoffset + j}; - const size_t ridx{evoffset + ((azcount-j) % azcount)}; - - const size_t irSize{coeffs[ridx].size()}; - for(size_t k{0};k < irSize;k++) - coeffs[ridx][k][1] = coeffs[lidx][k][0]; - delays[ridx][1] = delays[lidx][0]; - } - } -} - - -template -inline T readle(std::istream &data) -{ - static_assert((num_bits&7) == 0, "num_bits must be a multiple of 8"); - static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type"); - - T ret{}; - if_constexpr(al::endian::native == al::endian::little) - { - if(!data.read(reinterpret_cast(&ret), num_bits/8)) - return static_cast(EOF); - } - else - { - al::byte b[sizeof(T)]{}; - if(!data.read(reinterpret_cast(b), num_bits/8)) - return static_cast(EOF); - std::reverse_copy(std::begin(b), std::end(b), reinterpret_cast(&ret)); - } - - if_constexpr(std::is_signed::value && num_bits < sizeof(T)*8) - { - constexpr auto signbit = static_cast(1u << (num_bits-1)); - return static_cast((ret^signbit) - signbit); - } - return ret; -} - -template<> -inline uint8_t readle(std::istream &data) -{ return static_cast(data.get()); } - - -std::unique_ptr LoadHrtf00(std::istream &data, const char *filename) -{ - uint rate{readle(data)}; - ushort irCount{readle(data)}; - ushort irSize{readle(data)}; - ubyte evCount{readle(data)}; - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - - if(irSize < MinIrLength || irSize > HrirLength) - { - ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength); - return nullptr; - } - if(evCount < MinEvCount || evCount > MaxEvCount) - { - ERR("Unsupported elevation count: evCount=%d (%d to %d)\n", - evCount, MinEvCount, MaxEvCount); - return nullptr; - } - - auto elevs = al::vector(evCount); - for(auto &elev : elevs) - elev.irOffset = readle(data); - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - for(size_t i{1};i < evCount;i++) - { - if(elevs[i].irOffset <= elevs[i-1].irOffset) - { - ERR("Invalid evOffset: evOffset[%zu]=%d (last=%d)\n", i, elevs[i].irOffset, - elevs[i-1].irOffset); - return nullptr; - } - } - if(irCount <= elevs.back().irOffset) - { - ERR("Invalid evOffset: evOffset[%zu]=%d (irCount=%d)\n", - elevs.size()-1, elevs.back().irOffset, irCount); - return nullptr; - } - - for(size_t i{1};i < evCount;i++) - { - elevs[i-1].azCount = static_cast(elevs[i].irOffset - elevs[i-1].irOffset); - if(elevs[i-1].azCount < MinAzCount || elevs[i-1].azCount > MaxAzCount) - { - ERR("Unsupported azimuth count: azCount[%zd]=%d (%d to %d)\n", - i-1, elevs[i-1].azCount, MinAzCount, MaxAzCount); - return nullptr; - } - } - elevs.back().azCount = static_cast(irCount - elevs.back().irOffset); - if(elevs.back().azCount < MinAzCount || elevs.back().azCount > MaxAzCount) - { - ERR("Unsupported azimuth count: azCount[%zu]=%d (%d to %d)\n", - elevs.size()-1, elevs.back().azCount, MinAzCount, MaxAzCount); - return nullptr; - } - - auto coeffs = al::vector(irCount, HrirArray{}); - auto delays = al::vector(irCount); - for(auto &hrir : coeffs) - { - for(auto &val : al::span{hrir.data(), irSize}) - val[0] = readle(data) / 32768.0f; - } - for(auto &val : delays) - val[0] = readle(data); - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - for(size_t i{0};i < irCount;i++) - { - if(delays[i][0] > MaxHrirDelay) - { - ERR("Invalid delays[%zd]: %d (%d)\n", i, delays[i][0], MaxHrirDelay); - return nullptr; - } - delays[i][0] <<= HrirDelayFracBits; - } - - /* Mirror the left ear responses to the right ear. */ - MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data()); - - const HrtfStore::Field field[1]{{0.0f, evCount}}; - return CreateHrtfStore(rate, irSize, field, {elevs.data(), elevs.size()}, coeffs.data(), - delays.data(), filename); -} - -std::unique_ptr LoadHrtf01(std::istream &data, const char *filename) -{ - uint rate{readle(data)}; - ushort irSize{readle(data)}; - ubyte evCount{readle(data)}; - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - - if(irSize < MinIrLength || irSize > HrirLength) - { - ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength); - return nullptr; - } - if(evCount < MinEvCount || evCount > MaxEvCount) - { - ERR("Unsupported elevation count: evCount=%d (%d to %d)\n", - evCount, MinEvCount, MaxEvCount); - return nullptr; - } - - auto elevs = al::vector(evCount); - for(auto &elev : elevs) - elev.azCount = readle(data); - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - for(size_t i{0};i < evCount;++i) - { - if(elevs[i].azCount < MinAzCount || elevs[i].azCount > MaxAzCount) - { - ERR("Unsupported azimuth count: azCount[%zd]=%d (%d to %d)\n", i, elevs[i].azCount, - MinAzCount, MaxAzCount); - return nullptr; - } - } - - elevs[0].irOffset = 0; - for(size_t i{1};i < evCount;i++) - elevs[i].irOffset = static_cast(elevs[i-1].irOffset + elevs[i-1].azCount); - const ushort irCount{static_cast(elevs.back().irOffset + elevs.back().azCount)}; - - auto coeffs = al::vector(irCount, HrirArray{}); - auto delays = al::vector(irCount); - for(auto &hrir : coeffs) - { - for(auto &val : al::span{hrir.data(), irSize}) - val[0] = readle(data) / 32768.0f; - } - for(auto &val : delays) - val[0] = readle(data); - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - for(size_t i{0};i < irCount;i++) - { - if(delays[i][0] > MaxHrirDelay) - { - ERR("Invalid delays[%zd]: %d (%d)\n", i, delays[i][0], MaxHrirDelay); - return nullptr; - } - delays[i][0] <<= HrirDelayFracBits; - } - - /* Mirror the left ear responses to the right ear. */ - MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data()); - - const HrtfStore::Field field[1]{{0.0f, evCount}}; - return CreateHrtfStore(rate, irSize, field, {elevs.data(), elevs.size()}, coeffs.data(), - delays.data(), filename); -} - -std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) -{ - constexpr ubyte SampleType_S16{0}; - constexpr ubyte SampleType_S24{1}; - constexpr ubyte ChanType_LeftOnly{0}; - constexpr ubyte ChanType_LeftRight{1}; - - uint rate{readle(data)}; - ubyte sampleType{readle(data)}; - ubyte channelType{readle(data)}; - ushort irSize{readle(data)}; - ubyte fdCount{readle(data)}; - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - - if(sampleType > SampleType_S24) - { - ERR("Unsupported sample type: %d\n", sampleType); - return nullptr; - } - if(channelType > ChanType_LeftRight) - { - ERR("Unsupported channel type: %d\n", channelType); - return nullptr; - } - - if(irSize < MinIrLength || irSize > HrirLength) - { - ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength); - return nullptr; - } - if(fdCount < 1 || fdCount > MaxFdCount) - { - ERR("Unsupported number of field-depths: fdCount=%d (%d to %d)\n", fdCount, MinFdCount, - MaxFdCount); - return nullptr; - } - - auto fields = al::vector(fdCount); - auto elevs = al::vector{}; - for(size_t f{0};f < fdCount;f++) - { - const ushort distance{readle(data)}; - const ubyte evCount{readle(data)}; - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - - if(distance < MinFdDistance || distance > MaxFdDistance) - { - ERR("Unsupported field distance[%zu]=%d (%d to %d millimeters)\n", f, distance, - MinFdDistance, MaxFdDistance); - return nullptr; - } - if(evCount < MinEvCount || evCount > MaxEvCount) - { - ERR("Unsupported elevation count: evCount[%zu]=%d (%d to %d)\n", f, evCount, - MinEvCount, MaxEvCount); - return nullptr; - } - - fields[f].distance = distance / 1000.0f; - fields[f].evCount = evCount; - if(f > 0 && fields[f].distance <= fields[f-1].distance) - { - ERR("Field distance[%zu] is not after previous (%f > %f)\n", f, fields[f].distance, - fields[f-1].distance); - return nullptr; - } - - const size_t ebase{elevs.size()}; - elevs.resize(ebase + evCount); - for(auto &elev : al::span(elevs.data()+ebase, evCount)) - elev.azCount = readle(data); - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - - for(size_t e{0};e < evCount;e++) - { - if(elevs[ebase+e].azCount < MinAzCount || elevs[ebase+e].azCount > MaxAzCount) - { - ERR("Unsupported azimuth count: azCount[%zu][%zu]=%d (%d to %d)\n", f, e, - elevs[ebase+e].azCount, MinAzCount, MaxAzCount); - return nullptr; - } - } - } - - elevs[0].irOffset = 0; - std::partial_sum(elevs.cbegin(), elevs.cend(), elevs.begin(), - [](const HrtfStore::Elevation &last, const HrtfStore::Elevation &cur) - -> HrtfStore::Elevation - { - return HrtfStore::Elevation{cur.azCount, - static_cast(last.azCount + last.irOffset)}; - }); - const auto irTotal = static_cast(elevs.back().azCount + elevs.back().irOffset); - - auto coeffs = al::vector(irTotal, HrirArray{}); - auto delays = al::vector(irTotal); - if(channelType == ChanType_LeftOnly) - { - if(sampleType == SampleType_S16) - { - for(auto &hrir : coeffs) - { - for(auto &val : al::span{hrir.data(), irSize}) - val[0] = readle(data) / 32768.0f; - } - } - else if(sampleType == SampleType_S24) - { - for(auto &hrir : coeffs) - { - for(auto &val : al::span{hrir.data(), irSize}) - val[0] = static_cast(readle(data)) / 8388608.0f; - } - } - for(auto &val : delays) - val[0] = readle(data); - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - for(size_t i{0};i < irTotal;++i) - { - if(delays[i][0] > MaxHrirDelay) - { - ERR("Invalid delays[%zu][0]: %d (%d)\n", i, delays[i][0], MaxHrirDelay); - return nullptr; - } - delays[i][0] <<= HrirDelayFracBits; - } - - /* Mirror the left ear responses to the right ear. */ - MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data()); - } - else if(channelType == ChanType_LeftRight) - { - if(sampleType == SampleType_S16) - { - for(auto &hrir : coeffs) - { - for(auto &val : al::span{hrir.data(), irSize}) - { - val[0] = readle(data) / 32768.0f; - val[1] = readle(data) / 32768.0f; - } - } - } - else if(sampleType == SampleType_S24) - { - for(auto &hrir : coeffs) - { - for(auto &val : al::span{hrir.data(), irSize}) - { - val[0] = static_cast(readle(data)) / 8388608.0f; - val[1] = static_cast(readle(data)) / 8388608.0f; - } - } - } - for(auto &val : delays) - { - val[0] = readle(data); - val[1] = readle(data); - } - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - - for(size_t i{0};i < irTotal;++i) - { - if(delays[i][0] > MaxHrirDelay) - { - ERR("Invalid delays[%zu][0]: %d (%d)\n", i, delays[i][0], MaxHrirDelay); - return nullptr; - } - if(delays[i][1] > MaxHrirDelay) - { - ERR("Invalid delays[%zu][1]: %d (%d)\n", i, delays[i][1], MaxHrirDelay); - return nullptr; - } - delays[i][0] <<= HrirDelayFracBits; - delays[i][1] <<= HrirDelayFracBits; - } - } - - if(fdCount > 1) - { - auto fields_ = al::vector(fields.size()); - auto elevs_ = al::vector(elevs.size()); - auto coeffs_ = al::vector(coeffs.size()); - auto delays_ = al::vector(delays.size()); - - /* Simple reverse for the per-field elements. */ - std::reverse_copy(fields.cbegin(), fields.cend(), fields_.begin()); - - /* Each field has a group of elevations, which each have an azimuth - * count. Reverse the order of the groups, keeping the relative order - * of per-group azimuth counts. - */ - auto elevs__end = elevs_.end(); - auto copy_azs = [&elevs,&elevs__end](const ptrdiff_t ebase, const HrtfStore::Field &field) - -> ptrdiff_t - { - auto elevs_src = elevs.begin()+ebase; - elevs__end = std::copy_backward(elevs_src, elevs_src+field.evCount, elevs__end); - return ebase + field.evCount; - }; - (void)std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_azs); - assert(elevs_.begin() == elevs__end); - - /* Reestablish the IR offset for each elevation index, given the new - * ordering of elevations. - */ - elevs_[0].irOffset = 0; - std::partial_sum(elevs_.cbegin(), elevs_.cend(), elevs_.begin(), - [](const HrtfStore::Elevation &last, const HrtfStore::Elevation &cur) - -> HrtfStore::Elevation - { - return HrtfStore::Elevation{cur.azCount, - static_cast(last.azCount + last.irOffset)}; - }); - - /* Reverse the order of each field's group of IRs. */ - auto coeffs_end = coeffs_.end(); - auto delays_end = delays_.end(); - auto copy_irs = [&elevs,&coeffs,&delays,&coeffs_end,&delays_end]( - const ptrdiff_t ebase, const HrtfStore::Field &field) -> ptrdiff_t - { - auto accum_az = [](int count, const HrtfStore::Elevation &elev) noexcept -> int - { return count + elev.azCount; }; - const auto elevs_mid = elevs.cbegin() + ebase; - const auto elevs_end = elevs_mid + field.evCount; - const int abase{std::accumulate(elevs.cbegin(), elevs_mid, 0, accum_az)}; - const int num_azs{std::accumulate(elevs_mid, elevs_end, 0, accum_az)}; - - coeffs_end = std::copy_backward(coeffs.cbegin() + abase, - coeffs.cbegin() + (abase+num_azs), coeffs_end); - delays_end = std::copy_backward(delays.cbegin() + abase, - delays.cbegin() + (abase+num_azs), delays_end); - - return ebase + field.evCount; - }; - (void)std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_irs); - assert(coeffs_.begin() == coeffs_end); - assert(delays_.begin() == delays_end); - - fields = std::move(fields_); - elevs = std::move(elevs_); - coeffs = std::move(coeffs_); - delays = std::move(delays_); - } - - return CreateHrtfStore(rate, irSize, {fields.data(), fields.size()}, - {elevs.data(), elevs.size()}, coeffs.data(), delays.data(), filename); -} - -std::unique_ptr LoadHrtf03(std::istream &data, const char *filename) -{ - constexpr ubyte ChanType_LeftOnly{0}; - constexpr ubyte ChanType_LeftRight{1}; - - uint rate{readle(data)}; - ubyte channelType{readle(data)}; - ushort irSize{readle(data)}; - ubyte fdCount{readle(data)}; - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - - if(channelType > ChanType_LeftRight) - { - ERR("Unsupported channel type: %d\n", channelType); - return nullptr; - } - - if(irSize < MinIrLength || irSize > HrirLength) - { - ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength); - return nullptr; - } - if(fdCount < 1 || fdCount > MaxFdCount) - { - ERR("Unsupported number of field-depths: fdCount=%d (%d to %d)\n", fdCount, MinFdCount, - MaxFdCount); - return nullptr; - } - - auto fields = al::vector(fdCount); - auto elevs = al::vector{}; - for(size_t f{0};f < fdCount;f++) - { - const ushort distance{readle(data)}; - const ubyte evCount{readle(data)}; - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - - if(distance < MinFdDistance || distance > MaxFdDistance) - { - ERR("Unsupported field distance[%zu]=%d (%d to %d millimeters)\n", f, distance, - MinFdDistance, MaxFdDistance); - return nullptr; - } - if(evCount < MinEvCount || evCount > MaxEvCount) - { - ERR("Unsupported elevation count: evCount[%zu]=%d (%d to %d)\n", f, evCount, - MinEvCount, MaxEvCount); - return nullptr; - } - - fields[f].distance = distance / 1000.0f; - fields[f].evCount = evCount; - if(f > 0 && fields[f].distance > fields[f-1].distance) - { - ERR("Field distance[%zu] is not before previous (%f <= %f)\n", f, fields[f].distance, - fields[f-1].distance); - return nullptr; - } - - const size_t ebase{elevs.size()}; - elevs.resize(ebase + evCount); - for(auto &elev : al::span(elevs.data()+ebase, evCount)) - elev.azCount = readle(data); - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - - for(size_t e{0};e < evCount;e++) - { - if(elevs[ebase+e].azCount < MinAzCount || elevs[ebase+e].azCount > MaxAzCount) - { - ERR("Unsupported azimuth count: azCount[%zu][%zu]=%d (%d to %d)\n", f, e, - elevs[ebase+e].azCount, MinAzCount, MaxAzCount); - return nullptr; - } - } - } - - elevs[0].irOffset = 0; - std::partial_sum(elevs.cbegin(), elevs.cend(), elevs.begin(), - [](const HrtfStore::Elevation &last, const HrtfStore::Elevation &cur) - -> HrtfStore::Elevation - { - return HrtfStore::Elevation{cur.azCount, - static_cast(last.azCount + last.irOffset)}; - }); - const auto irTotal = static_cast(elevs.back().azCount + elevs.back().irOffset); - - auto coeffs = al::vector(irTotal, HrirArray{}); - auto delays = al::vector(irTotal); - if(channelType == ChanType_LeftOnly) - { - for(auto &hrir : coeffs) - { - for(auto &val : al::span{hrir.data(), irSize}) - val[0] = static_cast(readle(data)) / 8388608.0f; - } - for(auto &val : delays) - val[0] = readle(data); - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - for(size_t i{0};i < irTotal;++i) - { - if(delays[i][0] > MaxHrirDelay<{hrir.data(), irSize}) - { - val[0] = static_cast(readle(data)) / 8388608.0f; - val[1] = static_cast(readle(data)) / 8388608.0f; - } - } - for(auto &val : delays) - { - val[0] = readle(data); - val[1] = readle(data); - } - if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } - - for(size_t i{0};i < irTotal;++i) - { - if(delays[i][0] > MaxHrirDelay< MaxHrirDelay< bool { return name == entry.mDispName; }; - auto &enum_names = EnumeratedHrtfs; - return std::find_if(enum_names.cbegin(), enum_names.cend(), match_name) != enum_names.cend(); -} - -void AddFileEntry(const std::string &filename) -{ - /* Check if this file has already been enumerated. */ - auto enum_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(), - [&filename](const HrtfEntry &entry) -> bool - { return entry.mFilename == filename; }); - if(enum_iter != EnumeratedHrtfs.cend()) - { - TRACE("Skipping duplicate file entry %s\n", filename.c_str()); - return; - } - - /* TODO: Get a human-readable name from the HRTF data (possibly coming in a - * format update). */ - size_t namepos{filename.find_last_of('/')+1}; - if(!namepos) namepos = filename.find_last_of('\\')+1; - - size_t extpos{filename.find_last_of('.')}; - if(extpos <= namepos) extpos = std::string::npos; - - const std::string basename{(extpos == std::string::npos) ? - filename.substr(namepos) : filename.substr(namepos, extpos-namepos)}; - std::string newname{basename}; - int count{1}; - while(checkName(newname)) - { - newname = basename; - newname += " #"; - newname += std::to_string(++count); - } - EnumeratedHrtfs.emplace_back(HrtfEntry{newname, filename}); - const HrtfEntry &entry = EnumeratedHrtfs.back(); - - TRACE("Adding file entry \"%s\"\n", entry.mFilename.c_str()); -} - -/* Unfortunate that we have to duplicate AddFileEntry to take a memory buffer - * for input instead of opening the given filename. - */ -void AddBuiltInEntry(const std::string &dispname, uint residx) -{ - const std::string filename{'!'+std::to_string(residx)+'_'+dispname}; - - auto enum_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(), - [&filename](const HrtfEntry &entry) -> bool - { return entry.mFilename == filename; }); - if(enum_iter != EnumeratedHrtfs.cend()) - { - TRACE("Skipping duplicate file entry %s\n", filename.c_str()); - return; - } - - /* TODO: Get a human-readable name from the HRTF data (possibly coming in a - * format update). */ - - std::string newname{dispname}; - int count{1}; - while(checkName(newname)) - { - newname = dispname; - newname += " #"; - newname += std::to_string(++count); - } - EnumeratedHrtfs.emplace_back(HrtfEntry{newname, filename}); - const HrtfEntry &entry = EnumeratedHrtfs.back(); - - TRACE("Adding built-in entry \"%s\"\n", entry.mFilename.c_str()); -} - - -#define IDR_DEFAULT_HRTF_MHR 1 - -#ifndef ALSOFT_EMBED_HRTF_DATA - -al::span GetResource(int /*name*/) -{ return {}; } - -#else - -#include "hrtf_default.h" - -al::span GetResource(int name) -{ - if(name == IDR_DEFAULT_HRTF_MHR) - return {reinterpret_cast(hrtf_default), sizeof(hrtf_default)}; - return {}; -} -#endif - -} // namespace - - -al::vector EnumerateHrtf(al::optional pathopt) -{ - std::lock_guard _{EnumeratedHrtfLock}; - EnumeratedHrtfs.clear(); - - bool usedefaults{true}; - if(pathopt) - { - const char *pathlist{pathopt->c_str()}; - while(pathlist && *pathlist) - { - const char *next, *end; - - while(isspace(*pathlist) || *pathlist == ',') - pathlist++; - if(*pathlist == '\0') - continue; - - next = strchr(pathlist, ','); - if(next) - end = next++; - else - { - end = pathlist + strlen(pathlist); - usedefaults = false; - } - - while(end != pathlist && isspace(*(end-1))) - --end; - if(end != pathlist) - { - const std::string pname{pathlist, end}; - for(const auto &fname : SearchDataFiles(".mhr", pname.c_str())) - AddFileEntry(fname); - } - - pathlist = next; - } - } - - if(usedefaults) - { - for(const auto &fname : SearchDataFiles(".mhr", "openal/hrtf")) - AddFileEntry(fname); - - if(!GetResource(IDR_DEFAULT_HRTF_MHR).empty()) - AddBuiltInEntry("Built-In HRTF", IDR_DEFAULT_HRTF_MHR); - } - - al::vector list; - list.reserve(EnumeratedHrtfs.size()); - for(auto &entry : EnumeratedHrtfs) - list.emplace_back(entry.mDispName); - - return list; -} - -HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) -{ - std::lock_guard _{EnumeratedHrtfLock}; - auto entry_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(), - [&name](const HrtfEntry &entry) -> bool { return entry.mDispName == name; }); - if(entry_iter == EnumeratedHrtfs.cend()) - return nullptr; - const std::string &fname = entry_iter->mFilename; - - std::lock_guard __{LoadedHrtfLock}; - auto hrtf_lt_fname = [](LoadedHrtf &hrtf, const std::string &filename) -> bool - { return hrtf.mFilename < filename; }; - auto handle = std::lower_bound(LoadedHrtfs.begin(), LoadedHrtfs.end(), fname, hrtf_lt_fname); - while(handle != LoadedHrtfs.end() && handle->mFilename == fname) - { - HrtfStore *hrtf{handle->mEntry.get()}; - if(hrtf && hrtf->sampleRate == devrate) - { - hrtf->add_ref(); - return HrtfStorePtr{hrtf}; - } - ++handle; - } - - std::unique_ptr stream; - int residx{}; - char ch{}; - if(sscanf(fname.c_str(), "!%d%c", &residx, &ch) == 2 && ch == '_') - { - TRACE("Loading %s...\n", fname.c_str()); - al::span res{GetResource(residx)}; - if(res.empty()) - { - ERR("Could not get resource %u, %s\n", residx, name.c_str()); - return nullptr; - } - stream = std::make_unique(res.begin(), res.end()); - } - else - { - TRACE("Loading %s...\n", fname.c_str()); - auto fstr = std::make_unique(fname.c_str(), std::ios::binary); - if(!fstr->is_open()) - { - ERR("Could not open %s\n", fname.c_str()); - return nullptr; - } - stream = std::move(fstr); - } - - std::unique_ptr hrtf; - char magic[sizeof(magicMarker03)]; - stream->read(magic, sizeof(magic)); - if(stream->gcount() < static_cast(sizeof(magicMarker03))) - ERR("%s data is too short (%zu bytes)\n", name.c_str(), stream->gcount()); - else if(memcmp(magic, magicMarker03, sizeof(magicMarker03)) == 0) - { - TRACE("Detected data set format v3\n"); - hrtf = LoadHrtf03(*stream, name.c_str()); - } - else if(memcmp(magic, magicMarker02, sizeof(magicMarker02)) == 0) - { - TRACE("Detected data set format v2\n"); - hrtf = LoadHrtf02(*stream, name.c_str()); - } - else if(memcmp(magic, magicMarker01, sizeof(magicMarker01)) == 0) - { - TRACE("Detected data set format v1\n"); - hrtf = LoadHrtf01(*stream, name.c_str()); - } - else if(memcmp(magic, magicMarker00, sizeof(magicMarker00)) == 0) - { - TRACE("Detected data set format v0\n"); - hrtf = LoadHrtf00(*stream, name.c_str()); - } - else - ERR("Invalid header in %s: \"%.8s\"\n", name.c_str(), magic); - stream.reset(); - - if(!hrtf) - { - ERR("Failed to load %s\n", name.c_str()); - return nullptr; - } - - if(hrtf->sampleRate != devrate) - { - TRACE("Resampling HRTF %s (%uhz -> %uhz)\n", name.c_str(), hrtf->sampleRate, devrate); - - /* Calculate the last elevation's index and get the total IR count. */ - const size_t lastEv{std::accumulate(hrtf->field, hrtf->field+hrtf->fdCount, size_t{0}, - [](const size_t curval, const HrtfStore::Field &field) noexcept -> size_t - { return curval + field.evCount; } - ) - 1}; - const size_t irCount{size_t{hrtf->elev[lastEv].irOffset} + hrtf->elev[lastEv].azCount}; - - /* Resample all the IRs. */ - std::array,2> inout; - PPhaseResampler rs; - rs.init(hrtf->sampleRate, devrate); - for(size_t i{0};i < irCount;++i) - { - HrirArray &coeffs = const_cast(hrtf->coeffs[i]); - for(size_t j{0};j < 2;++j) - { - std::transform(coeffs.cbegin(), coeffs.cend(), inout[0].begin(), - [j](const float2 &in) noexcept -> double { return in[j]; }); - rs.process(HrirLength, inout[0].data(), HrirLength, inout[1].data()); - for(size_t k{0};k < HrirLength;++k) - coeffs[k][j] = static_cast(inout[1][k]); - } - } - rs = {}; - - /* Scale the delays for the new sample rate. */ - float max_delay{0.0f}; - auto new_delays = al::vector(irCount); - const float rate_scale{static_cast(devrate)/static_cast(hrtf->sampleRate)}; - for(size_t i{0};i < irCount;++i) - { - for(size_t j{0};j < 2;++j) - { - const float new_delay{std::round(hrtf->delays[i][j] * rate_scale) / - float{HrirDelayFracOne}}; - max_delay = maxf(max_delay, new_delay); - new_delays[i][j] = new_delay; - } - } - - /* If the new delays exceed the max, scale it down to fit (essentially - * shrinking the head radius; not ideal but better than a per-delay - * clamp). - */ - float delay_scale{HrirDelayFracOne}; - if(max_delay > MaxHrirDelay) - { - WARN("Resampled delay exceeds max (%.2f > %d)\n", max_delay, MaxHrirDelay); - delay_scale *= float{MaxHrirDelay} / max_delay; - } - - for(size_t i{0};i < irCount;++i) - { - ubyte2 &delays = const_cast(hrtf->delays[i]); - for(size_t j{0};j < 2;++j) - delays[j] = static_cast(float2int(new_delays[i][j]*delay_scale + 0.5f)); - } - - /* Scale the IR size for the new sample rate and update the stored - * sample rate. - */ - const float newIrSize{std::round(static_cast(hrtf->irSize) * rate_scale)}; - hrtf->irSize = static_cast(minf(HrirLength, newIrSize)); - hrtf->sampleRate = devrate; - } - - TRACE("Loaded HRTF %s for sample rate %uhz, %u-sample filter\n", name.c_str(), - hrtf->sampleRate, hrtf->irSize); - handle = LoadedHrtfs.emplace(handle, LoadedHrtf{fname, std::move(hrtf)}); - - return HrtfStorePtr{handle->mEntry.get()}; -} - - -void HrtfStore::add_ref() -{ - auto ref = IncrementRef(mRef); - TRACE("HrtfStore %p increasing refcount to %u\n", decltype(std::declval()){this}, ref); -} - -void HrtfStore::release() -{ - auto ref = DecrementRef(mRef); - TRACE("HrtfStore %p decreasing refcount to %u\n", decltype(std::declval()){this}, ref); - if(ref == 0) - { - std::lock_guard _{LoadedHrtfLock}; - - /* Go through and remove all unused HRTFs. */ - auto remove_unused = [](LoadedHrtf &hrtf) -> bool - { - HrtfStore *entry{hrtf.mEntry.get()}; - if(entry && ReadRef(entry->mRef) == 0) - { - TRACE("Unloading unused HRTF %s\n", hrtf.mFilename.data()); - hrtf.mEntry = nullptr; - return true; - } - return false; - }; - auto iter = std::remove_if(LoadedHrtfs.begin(), LoadedHrtfs.end(), remove_unused); - LoadedHrtfs.erase(iter, LoadedHrtfs.end()); - } -} diff --git a/alc/hrtf.h b/alc/hrtf.h deleted file mode 100644 index d60377df..00000000 --- a/alc/hrtf.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef ALC_HRTF_H -#define ALC_HRTF_H - -#include -#include -#include -#include - -#include "almalloc.h" -#include "aloptional.h" -#include "alspan.h" -#include "atomic.h" -#include "core/ambidefs.h" -#include "core/bufferline.h" -#include "core/filters/splitter.h" -#include "core/mixer/hrtfdefs.h" -#include "intrusive_ptr.h" -#include "vector.h" - - -struct HrtfStore { - RefCount mRef; - - uint sampleRate; - uint irSize; - - struct Field { - float distance; - ubyte evCount; - }; - /* NOTE: Fields are stored *backwards*. field[0] is the farthest field, and - * field[fdCount-1] is the nearest. - */ - uint fdCount; - const Field *field; - - struct Elevation { - ushort azCount; - ushort irOffset; - }; - Elevation *elev; - const HrirArray *coeffs; - const ubyte2 *delays; - - void add_ref(); - void release(); - - DEF_PLACE_NEWDEL() -}; -using HrtfStorePtr = al::intrusive_ptr; - - -struct EvRadians { float value; }; -struct AzRadians { float value; }; -struct AngularPoint { - EvRadians Elev; - AzRadians Azim; -}; - - -struct DirectHrtfState { - std::array mTemp; - - /* HRTF filter state for dry buffer content */ - uint mIrSize{0}; - al::FlexArray mChannels; - - DirectHrtfState(size_t numchans) : mChannels{numchans} { } - /** - * Produces HRTF filter coefficients for decoding B-Format, given a set of - * virtual speaker positions, a matching decoding matrix, and per-order - * high-frequency gains for the decoder. The calculated impulse responses - * are ordered and scaled according to the matrix input. - */ - void build(const HrtfStore *Hrtf, const uint irSize, - const al::span AmbiPoints, const float (*AmbiMatrix)[MaxAmbiChannels], - const float XOverFreq, const al::span AmbiOrderHFGain); - - static std::unique_ptr Create(size_t num_chans); - - DEF_FAM_NEWDEL(DirectHrtfState, mChannels) -}; - - -al::vector EnumerateHrtf(al::optional pathopt); -HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate); - -void GetHrtfCoeffs(const HrtfStore *Hrtf, float elevation, float azimuth, float distance, - float spread, HrirArray &coeffs, const al::span delays); - -#endif /* ALC_HRTF_H */ diff --git a/alc/panning.cpp b/alc/panning.cpp index 98060d7e..4307a8d1 100644 --- a/alc/panning.cpp +++ b/alc/panning.cpp @@ -52,10 +52,10 @@ #include "core/ambidefs.h" #include "core/bs2b.h" #include "core/devformat.h" +#include "core/hrtf.h" #include "core/logging.h" #include "core/uhjfilter.h" #include "front_stablizer.h" -#include "hrtf.h" #include "math_defs.h" #include "opthelpers.h" diff --git a/alc/voice.cpp b/alc/voice.cpp index 891bc3dc..afc22a9b 100644 --- a/alc/voice.cpp +++ b/alc/voice.cpp @@ -52,11 +52,11 @@ #include "core/filters/nfc.h" #include "core/filters/splitter.h" #include "core/fmt_traits.h" +#include "core/hrtf.h" #include "core/logging.h" #include "core/mixer/defs.h" #include "core/mixer/hrtfdefs.h" #include "core/resampler_limits.h" -#include "hrtf.h" #include "inprogext.h" #include "opthelpers.h" #include "ringbuffer.h" diff --git a/core/hrtf.cpp b/core/hrtf.cpp new file mode 100644 index 00000000..0a065be2 --- /dev/null +++ b/core/hrtf.cpp @@ -0,0 +1,1447 @@ + +#include "config.h" + +#include "hrtf.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "albit.h" +#include "albyte.h" +#include "alfstream.h" +#include "almalloc.h" +#include "alnumeric.h" +#include "aloptional.h" +#include "alspan.h" +#include "ambidefs.h" +#include "filters/splitter.h" +#include "helpers.h" +#include "logging.h" +#include "math_defs.h" +#include "opthelpers.h" +#include "polyphase_resampler.h" + + +namespace { + +struct HrtfEntry { + std::string mDispName; + std::string mFilename; +}; + +struct LoadedHrtf { + std::string mFilename; + std::unique_ptr mEntry; +}; + +/* Data set limits must be the same as or more flexible than those defined in + * the makemhr utility. + */ +constexpr uint MinFdCount{1}; +constexpr uint MaxFdCount{16}; + +constexpr uint MinFdDistance{50}; +constexpr uint MaxFdDistance{2500}; + +constexpr uint MinEvCount{5}; +constexpr uint MaxEvCount{181}; + +constexpr uint MinAzCount{1}; +constexpr uint MaxAzCount{255}; + +constexpr uint MaxHrirDelay{HrtfHistoryLength - 1}; + +constexpr uint HrirDelayFracBits{2}; +constexpr uint HrirDelayFracOne{1 << HrirDelayFracBits}; +constexpr uint HrirDelayFracHalf{HrirDelayFracOne >> 1}; + +static_assert(MaxHrirDelay*HrirDelayFracOne < 256, "MAX_HRIR_DELAY or DELAY_FRAC too large"); + +constexpr char magicMarker00[8]{'M','i','n','P','H','R','0','0'}; +constexpr char magicMarker01[8]{'M','i','n','P','H','R','0','1'}; +constexpr char magicMarker02[8]{'M','i','n','P','H','R','0','2'}; +constexpr char magicMarker03[8]{'M','i','n','P','H','R','0','3'}; + +/* First value for pass-through coefficients (remaining are 0), used for omni- + * directional sounds. */ +constexpr float PassthruCoeff{0.707106781187f/*sqrt(0.5)*/}; + +std::mutex LoadedHrtfLock; +al::vector LoadedHrtfs; + +std::mutex EnumeratedHrtfLock; +al::vector EnumeratedHrtfs; + + +class databuf final : public std::streambuf { + int_type underflow() override + { return traits_type::eof(); } + + pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override + { + if((mode&std::ios_base::out) || !(mode&std::ios_base::in)) + return traits_type::eof(); + + char_type *cur; + switch(whence) + { + case std::ios_base::beg: + if(offset < 0 || offset > egptr()-eback()) + return traits_type::eof(); + cur = eback() + offset; + break; + + case std::ios_base::cur: + if((offset >= 0 && offset > egptr()-gptr()) || + (offset < 0 && -offset > gptr()-eback())) + return traits_type::eof(); + cur = gptr() + offset; + break; + + case std::ios_base::end: + if(offset > 0 || -offset > egptr()-eback()) + return traits_type::eof(); + cur = egptr() + offset; + break; + + default: + return traits_type::eof(); + } + + setg(eback(), cur, egptr()); + return cur - eback(); + } + + pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override + { + // Simplified version of seekoff + if((mode&std::ios_base::out) || !(mode&std::ios_base::in)) + return traits_type::eof(); + + if(pos < 0 || pos > egptr()-eback()) + return traits_type::eof(); + + setg(eback(), eback() + static_cast(pos), egptr()); + return pos; + } + +public: + databuf(const char_type *start_, const char_type *end_) noexcept + { + setg(const_cast(start_), const_cast(start_), + const_cast(end_)); + } +}; + +class idstream final : public std::istream { + databuf mStreamBuf; + +public: + idstream(const char *start_, const char *end_) + : std::istream{nullptr}, mStreamBuf{start_, end_} + { init(&mStreamBuf); } +}; + + +struct IdxBlend { uint idx; float blend; }; +/* Calculate the elevation index given the polar elevation in radians. This + * will return an index between 0 and (evcount - 1). + */ +IdxBlend CalcEvIndex(uint evcount, float ev) +{ + ev = (al::MathDefs::Pi()*0.5f + ev) * static_cast(evcount-1) / + al::MathDefs::Pi(); + uint idx{float2uint(ev)}; + + return IdxBlend{minu(idx, evcount-1), ev-static_cast(idx)}; +} + +/* Calculate the azimuth index given the polar azimuth in radians. This will + * return an index between 0 and (azcount - 1). + */ +IdxBlend CalcAzIndex(uint azcount, float az) +{ + az = (al::MathDefs::Tau()+az) * static_cast(azcount) / + al::MathDefs::Tau(); + uint idx{float2uint(az)}; + + return IdxBlend{idx%azcount, az-static_cast(idx)}; +} + +} // namespace + + +/* Calculates static HRIR coefficients and delays for the given polar elevation + * and azimuth in radians. The coefficients are normalized. + */ +void GetHrtfCoeffs(const HrtfStore *Hrtf, float elevation, float azimuth, float distance, + float spread, HrirArray &coeffs, const al::span delays) +{ + const float dirfact{1.0f - (spread / al::MathDefs::Tau())}; + + const auto *field = Hrtf->field; + const auto *field_end = field + Hrtf->fdCount-1; + size_t ebase{0}; + while(distance < field->distance && field != field_end) + { + ebase += field->evCount; + ++field; + } + + /* Calculate the elevation indices. */ + const auto elev0 = CalcEvIndex(field->evCount, elevation); + const size_t elev1_idx{minu(elev0.idx+1, field->evCount-1)}; + const size_t ir0offset{Hrtf->elev[ebase + elev0.idx].irOffset}; + const size_t ir1offset{Hrtf->elev[ebase + elev1_idx].irOffset}; + + /* Calculate azimuth indices. */ + const auto az0 = CalcAzIndex(Hrtf->elev[ebase + elev0.idx].azCount, azimuth); + const auto az1 = CalcAzIndex(Hrtf->elev[ebase + elev1_idx].azCount, azimuth); + + /* Calculate the HRIR indices to blend. */ + const size_t idx[4]{ + ir0offset + az0.idx, + ir0offset + ((az0.idx+1) % Hrtf->elev[ebase + elev0.idx].azCount), + ir1offset + az1.idx, + ir1offset + ((az1.idx+1) % Hrtf->elev[ebase + elev1_idx].azCount) + }; + + /* Calculate bilinear blending weights, attenuated according to the + * directional panning factor. + */ + const float blend[4]{ + (1.0f-elev0.blend) * (1.0f-az0.blend) * dirfact, + (1.0f-elev0.blend) * ( az0.blend) * dirfact, + ( elev0.blend) * (1.0f-az1.blend) * dirfact, + ( elev0.blend) * ( az1.blend) * dirfact + }; + + /* Calculate the blended HRIR delays. */ + float d{Hrtf->delays[idx[0]][0]*blend[0] + Hrtf->delays[idx[1]][0]*blend[1] + + Hrtf->delays[idx[2]][0]*blend[2] + Hrtf->delays[idx[3]][0]*blend[3]}; + delays[0] = fastf2u(d * float{1.0f/HrirDelayFracOne}); + d = Hrtf->delays[idx[0]][1]*blend[0] + Hrtf->delays[idx[1]][1]*blend[1] + + Hrtf->delays[idx[2]][1]*blend[2] + Hrtf->delays[idx[3]][1]*blend[3]; + delays[1] = fastf2u(d * float{1.0f/HrirDelayFracOne}); + + /* Calculate the blended HRIR coefficients. */ + float *coeffout{al::assume_aligned<16>(&coeffs[0][0])}; + coeffout[0] = PassthruCoeff * (1.0f-dirfact); + coeffout[1] = PassthruCoeff * (1.0f-dirfact); + std::fill_n(coeffout+2, size_t{HrirLength-1}*2, 0.0f); + for(size_t c{0};c < 4;c++) + { + const float *srccoeffs{al::assume_aligned<16>(Hrtf->coeffs[idx[c]][0].data())}; + const float mult{blend[c]}; + auto blend_coeffs = [mult](const float src, const float coeff) noexcept -> float + { return src*mult + coeff; }; + std::transform(srccoeffs, srccoeffs + HrirLength*2, coeffout, coeffout, blend_coeffs); + } +} + + +std::unique_ptr DirectHrtfState::Create(size_t num_chans) +{ return std::unique_ptr{new(FamCount(num_chans)) DirectHrtfState{num_chans}}; } + +void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, + const al::span AmbiPoints, const float (*AmbiMatrix)[MaxAmbiChannels], + const float XOverFreq, const al::span AmbiOrderHFGain) +{ + using double2 = std::array; + struct ImpulseResponse { + const ConstHrirSpan hrir; + uint ldelay, rdelay; + }; + + const double xover_norm{double{XOverFreq} / Hrtf->sampleRate}; + for(size_t i{0};i < mChannels.size();++i) + { + const size_t order{AmbiIndex::OrderFromChannel()[i]}; + mChannels[i].mSplitter.init(static_cast(xover_norm)); + mChannels[i].mHfScale = AmbiOrderHFGain[order]; + } + + uint min_delay{HrtfHistoryLength*HrirDelayFracOne}, max_delay{0}; + al::vector impres; impres.reserve(AmbiPoints.size()); + auto calc_res = [Hrtf,&max_delay,&min_delay](const AngularPoint &pt) -> ImpulseResponse + { + auto &field = Hrtf->field[0]; + const auto elev0 = CalcEvIndex(field.evCount, pt.Elev.value); + const size_t elev1_idx{minu(elev0.idx+1, field.evCount-1)}; + const size_t ir0offset{Hrtf->elev[elev0.idx].irOffset}; + const size_t ir1offset{Hrtf->elev[elev1_idx].irOffset}; + + const auto az0 = CalcAzIndex(Hrtf->elev[elev0.idx].azCount, pt.Azim.value); + const auto az1 = CalcAzIndex(Hrtf->elev[elev1_idx].azCount, pt.Azim.value); + + const size_t idx[4]{ + ir0offset + az0.idx, + ir0offset + ((az0.idx+1) % Hrtf->elev[elev0.idx].azCount), + ir1offset + az1.idx, + ir1offset + ((az1.idx+1) % Hrtf->elev[elev1_idx].azCount) + }; + + const std::array blend{{ + (1.0-elev0.blend) * (1.0-az0.blend), + (1.0-elev0.blend) * ( az0.blend), + ( elev0.blend) * (1.0-az1.blend), + ( elev0.blend) * ( az1.blend) + }}; + + /* The largest blend factor serves as the closest HRIR. */ + const size_t irOffset{idx[std::max_element(blend.begin(), blend.end()) - blend.begin()]}; + ImpulseResponse res{Hrtf->coeffs[irOffset], + Hrtf->delays[irOffset][0], Hrtf->delays[irOffset][1]}; + + min_delay = minu(min_delay, minu(res.ldelay, res.rdelay)); + max_delay = maxu(max_delay, maxu(res.ldelay, res.rdelay)); + + return res; + }; + std::transform(AmbiPoints.begin(), AmbiPoints.end(), std::back_inserter(impres), calc_res); + auto hrir_delay_round = [](const uint d) noexcept -> uint + { return (d+HrirDelayFracHalf) >> HrirDelayFracBits; }; + + TRACE("Min delay: %.2f, max delay: %.2f, FIR length: %u\n", + min_delay/double{HrirDelayFracOne}, max_delay/double{HrirDelayFracOne}, irSize); + + const bool per_hrir_min{mChannels.size() > AmbiChannelsFromOrder(1)}; + auto tmpres = al::vector>(mChannels.size()); + max_delay = 0; + for(size_t c{0u};c < AmbiPoints.size();++c) + { + const ConstHrirSpan hrir{impres[c].hrir}; + const uint base_delay{per_hrir_min ? minu(impres[c].ldelay, impres[c].rdelay) : min_delay}; + const uint ldelay{hrir_delay_round(impres[c].ldelay - base_delay)}; + const uint rdelay{hrir_delay_round(impres[c].rdelay - base_delay)}; + max_delay = maxu(max_delay, maxu(impres[c].ldelay, impres[c].rdelay) - base_delay); + + for(size_t i{0u};i < mChannels.size();++i) + { + const double mult{AmbiMatrix[c][i]}; + const size_t numirs{HrirLength - maxz(ldelay, rdelay)}; + size_t lidx{ldelay}, ridx{rdelay}; + for(size_t j{0};j < numirs;++j) + { + tmpres[i][lidx++][0] += hrir[j][0] * mult; + tmpres[i][ridx++][1] += hrir[j][1] * mult; + } + } + } + impres.clear(); + + for(size_t i{0u};i < mChannels.size();++i) + { + auto copy_arr = [](const double2 &in) noexcept -> float2 + { return float2{{static_cast(in[0]), static_cast(in[1])}}; }; + std::transform(tmpres[i].cbegin(), tmpres[i].cend(), mChannels[i].mCoeffs.begin(), + copy_arr); + } + tmpres.clear(); + + const uint max_length{minu(hrir_delay_round(max_delay) + irSize, HrirLength)}; + TRACE("New max delay: %.2f, FIR length: %u\n", max_delay/double{HrirDelayFracOne}, + max_length); + mIrSize = max_length; +} + + +namespace { + +std::unique_ptr CreateHrtfStore(uint rate, ushort irSize, + const al::span fields, + const al::span elevs, const HrirArray *coeffs, + const ubyte2 *delays, const char *filename) +{ + std::unique_ptr Hrtf; + + const size_t irCount{size_t{elevs.back().azCount} + elevs.back().irOffset}; + size_t total{sizeof(HrtfStore)}; + total = RoundUp(total, alignof(HrtfStore::Field)); /* Align for field infos */ + total += sizeof(HrtfStore::Field)*fields.size(); + total = RoundUp(total, alignof(HrtfStore::Elevation)); /* Align for elevation infos */ + total += sizeof(Hrtf->elev[0])*elevs.size(); + total = RoundUp(total, 16); /* Align for coefficients using SIMD */ + total += sizeof(Hrtf->coeffs[0])*irCount; + total += sizeof(Hrtf->delays[0])*irCount; + + Hrtf.reset(new (al_calloc(16, total)) HrtfStore{}); + if(!Hrtf) + ERR("Out of memory allocating storage for %s.\n", filename); + else + { + InitRef(Hrtf->mRef, 1u); + Hrtf->sampleRate = rate; + Hrtf->irSize = irSize; + Hrtf->fdCount = static_cast(fields.size()); + + /* Set up pointers to storage following the main HRTF struct. */ + char *base = reinterpret_cast(Hrtf.get()); + size_t offset{sizeof(HrtfStore)}; + + offset = RoundUp(offset, alignof(HrtfStore::Field)); /* Align for field infos */ + auto field_ = reinterpret_cast(base + offset); + offset += sizeof(field_[0])*fields.size(); + + offset = RoundUp(offset, alignof(HrtfStore::Elevation)); /* Align for elevation infos */ + auto elev_ = reinterpret_cast(base + offset); + offset += sizeof(elev_[0])*elevs.size(); + + offset = RoundUp(offset, 16); /* Align for coefficients using SIMD */ + auto coeffs_ = reinterpret_cast(base + offset); + offset += sizeof(coeffs_[0])*irCount; + + auto delays_ = reinterpret_cast(base + offset); + offset += sizeof(delays_[0])*irCount; + + assert(offset == total); + + /* Copy input data to storage. */ + std::copy(fields.cbegin(), fields.cend(), field_); + std::copy(elevs.cbegin(), elevs.cend(), elev_); + std::copy_n(coeffs, irCount, coeffs_); + std::copy_n(delays, irCount, delays_); + + /* Finally, assign the storage pointers. */ + Hrtf->field = field_; + Hrtf->elev = elev_; + Hrtf->coeffs = coeffs_; + Hrtf->delays = delays_; + } + + return Hrtf; +} + +void MirrorLeftHrirs(const al::span elevs, HrirArray *coeffs, + ubyte2 *delays) +{ + for(const auto &elev : elevs) + { + const ushort evoffset{elev.irOffset}; + const ushort azcount{elev.azCount}; + for(size_t j{0};j < azcount;j++) + { + const size_t lidx{evoffset + j}; + const size_t ridx{evoffset + ((azcount-j) % azcount)}; + + const size_t irSize{coeffs[ridx].size()}; + for(size_t k{0};k < irSize;k++) + coeffs[ridx][k][1] = coeffs[lidx][k][0]; + delays[ridx][1] = delays[lidx][0]; + } + } +} + + +template +inline T readle(std::istream &data) +{ + static_assert((num_bits&7) == 0, "num_bits must be a multiple of 8"); + static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type"); + + T ret{}; + if_constexpr(al::endian::native == al::endian::little) + { + if(!data.read(reinterpret_cast(&ret), num_bits/8)) + return static_cast(EOF); + } + else + { + al::byte b[sizeof(T)]{}; + if(!data.read(reinterpret_cast(b), num_bits/8)) + return static_cast(EOF); + std::reverse_copy(std::begin(b), std::end(b), reinterpret_cast(&ret)); + } + + if_constexpr(std::is_signed::value && num_bits < sizeof(T)*8) + { + constexpr auto signbit = static_cast(1u << (num_bits-1)); + return static_cast((ret^signbit) - signbit); + } + return ret; +} + +template<> +inline uint8_t readle(std::istream &data) +{ return static_cast(data.get()); } + + +std::unique_ptr LoadHrtf00(std::istream &data, const char *filename) +{ + uint rate{readle(data)}; + ushort irCount{readle(data)}; + ushort irSize{readle(data)}; + ubyte evCount{readle(data)}; + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + + if(irSize < MinIrLength || irSize > HrirLength) + { + ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength); + return nullptr; + } + if(evCount < MinEvCount || evCount > MaxEvCount) + { + ERR("Unsupported elevation count: evCount=%d (%d to %d)\n", + evCount, MinEvCount, MaxEvCount); + return nullptr; + } + + auto elevs = al::vector(evCount); + for(auto &elev : elevs) + elev.irOffset = readle(data); + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + for(size_t i{1};i < evCount;i++) + { + if(elevs[i].irOffset <= elevs[i-1].irOffset) + { + ERR("Invalid evOffset: evOffset[%zu]=%d (last=%d)\n", i, elevs[i].irOffset, + elevs[i-1].irOffset); + return nullptr; + } + } + if(irCount <= elevs.back().irOffset) + { + ERR("Invalid evOffset: evOffset[%zu]=%d (irCount=%d)\n", + elevs.size()-1, elevs.back().irOffset, irCount); + return nullptr; + } + + for(size_t i{1};i < evCount;i++) + { + elevs[i-1].azCount = static_cast(elevs[i].irOffset - elevs[i-1].irOffset); + if(elevs[i-1].azCount < MinAzCount || elevs[i-1].azCount > MaxAzCount) + { + ERR("Unsupported azimuth count: azCount[%zd]=%d (%d to %d)\n", + i-1, elevs[i-1].azCount, MinAzCount, MaxAzCount); + return nullptr; + } + } + elevs.back().azCount = static_cast(irCount - elevs.back().irOffset); + if(elevs.back().azCount < MinAzCount || elevs.back().azCount > MaxAzCount) + { + ERR("Unsupported azimuth count: azCount[%zu]=%d (%d to %d)\n", + elevs.size()-1, elevs.back().azCount, MinAzCount, MaxAzCount); + return nullptr; + } + + auto coeffs = al::vector(irCount, HrirArray{}); + auto delays = al::vector(irCount); + for(auto &hrir : coeffs) + { + for(auto &val : al::span{hrir.data(), irSize}) + val[0] = readle(data) / 32768.0f; + } + for(auto &val : delays) + val[0] = readle(data); + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + for(size_t i{0};i < irCount;i++) + { + if(delays[i][0] > MaxHrirDelay) + { + ERR("Invalid delays[%zd]: %d (%d)\n", i, delays[i][0], MaxHrirDelay); + return nullptr; + } + delays[i][0] <<= HrirDelayFracBits; + } + + /* Mirror the left ear responses to the right ear. */ + MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data()); + + const HrtfStore::Field field[1]{{0.0f, evCount}}; + return CreateHrtfStore(rate, irSize, field, {elevs.data(), elevs.size()}, coeffs.data(), + delays.data(), filename); +} + +std::unique_ptr LoadHrtf01(std::istream &data, const char *filename) +{ + uint rate{readle(data)}; + ushort irSize{readle(data)}; + ubyte evCount{readle(data)}; + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + + if(irSize < MinIrLength || irSize > HrirLength) + { + ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength); + return nullptr; + } + if(evCount < MinEvCount || evCount > MaxEvCount) + { + ERR("Unsupported elevation count: evCount=%d (%d to %d)\n", + evCount, MinEvCount, MaxEvCount); + return nullptr; + } + + auto elevs = al::vector(evCount); + for(auto &elev : elevs) + elev.azCount = readle(data); + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + for(size_t i{0};i < evCount;++i) + { + if(elevs[i].azCount < MinAzCount || elevs[i].azCount > MaxAzCount) + { + ERR("Unsupported azimuth count: azCount[%zd]=%d (%d to %d)\n", i, elevs[i].azCount, + MinAzCount, MaxAzCount); + return nullptr; + } + } + + elevs[0].irOffset = 0; + for(size_t i{1};i < evCount;i++) + elevs[i].irOffset = static_cast(elevs[i-1].irOffset + elevs[i-1].azCount); + const ushort irCount{static_cast(elevs.back().irOffset + elevs.back().azCount)}; + + auto coeffs = al::vector(irCount, HrirArray{}); + auto delays = al::vector(irCount); + for(auto &hrir : coeffs) + { + for(auto &val : al::span{hrir.data(), irSize}) + val[0] = readle(data) / 32768.0f; + } + for(auto &val : delays) + val[0] = readle(data); + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + for(size_t i{0};i < irCount;i++) + { + if(delays[i][0] > MaxHrirDelay) + { + ERR("Invalid delays[%zd]: %d (%d)\n", i, delays[i][0], MaxHrirDelay); + return nullptr; + } + delays[i][0] <<= HrirDelayFracBits; + } + + /* Mirror the left ear responses to the right ear. */ + MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data()); + + const HrtfStore::Field field[1]{{0.0f, evCount}}; + return CreateHrtfStore(rate, irSize, field, {elevs.data(), elevs.size()}, coeffs.data(), + delays.data(), filename); +} + +std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) +{ + constexpr ubyte SampleType_S16{0}; + constexpr ubyte SampleType_S24{1}; + constexpr ubyte ChanType_LeftOnly{0}; + constexpr ubyte ChanType_LeftRight{1}; + + uint rate{readle(data)}; + ubyte sampleType{readle(data)}; + ubyte channelType{readle(data)}; + ushort irSize{readle(data)}; + ubyte fdCount{readle(data)}; + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + + if(sampleType > SampleType_S24) + { + ERR("Unsupported sample type: %d\n", sampleType); + return nullptr; + } + if(channelType > ChanType_LeftRight) + { + ERR("Unsupported channel type: %d\n", channelType); + return nullptr; + } + + if(irSize < MinIrLength || irSize > HrirLength) + { + ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength); + return nullptr; + } + if(fdCount < 1 || fdCount > MaxFdCount) + { + ERR("Unsupported number of field-depths: fdCount=%d (%d to %d)\n", fdCount, MinFdCount, + MaxFdCount); + return nullptr; + } + + auto fields = al::vector(fdCount); + auto elevs = al::vector{}; + for(size_t f{0};f < fdCount;f++) + { + const ushort distance{readle(data)}; + const ubyte evCount{readle(data)}; + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + + if(distance < MinFdDistance || distance > MaxFdDistance) + { + ERR("Unsupported field distance[%zu]=%d (%d to %d millimeters)\n", f, distance, + MinFdDistance, MaxFdDistance); + return nullptr; + } + if(evCount < MinEvCount || evCount > MaxEvCount) + { + ERR("Unsupported elevation count: evCount[%zu]=%d (%d to %d)\n", f, evCount, + MinEvCount, MaxEvCount); + return nullptr; + } + + fields[f].distance = distance / 1000.0f; + fields[f].evCount = evCount; + if(f > 0 && fields[f].distance <= fields[f-1].distance) + { + ERR("Field distance[%zu] is not after previous (%f > %f)\n", f, fields[f].distance, + fields[f-1].distance); + return nullptr; + } + + const size_t ebase{elevs.size()}; + elevs.resize(ebase + evCount); + for(auto &elev : al::span(elevs.data()+ebase, evCount)) + elev.azCount = readle(data); + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + + for(size_t e{0};e < evCount;e++) + { + if(elevs[ebase+e].azCount < MinAzCount || elevs[ebase+e].azCount > MaxAzCount) + { + ERR("Unsupported azimuth count: azCount[%zu][%zu]=%d (%d to %d)\n", f, e, + elevs[ebase+e].azCount, MinAzCount, MaxAzCount); + return nullptr; + } + } + } + + elevs[0].irOffset = 0; + std::partial_sum(elevs.cbegin(), elevs.cend(), elevs.begin(), + [](const HrtfStore::Elevation &last, const HrtfStore::Elevation &cur) + -> HrtfStore::Elevation + { + return HrtfStore::Elevation{cur.azCount, + static_cast(last.azCount + last.irOffset)}; + }); + const auto irTotal = static_cast(elevs.back().azCount + elevs.back().irOffset); + + auto coeffs = al::vector(irTotal, HrirArray{}); + auto delays = al::vector(irTotal); + if(channelType == ChanType_LeftOnly) + { + if(sampleType == SampleType_S16) + { + for(auto &hrir : coeffs) + { + for(auto &val : al::span{hrir.data(), irSize}) + val[0] = readle(data) / 32768.0f; + } + } + else if(sampleType == SampleType_S24) + { + for(auto &hrir : coeffs) + { + for(auto &val : al::span{hrir.data(), irSize}) + val[0] = static_cast(readle(data)) / 8388608.0f; + } + } + for(auto &val : delays) + val[0] = readle(data); + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + for(size_t i{0};i < irTotal;++i) + { + if(delays[i][0] > MaxHrirDelay) + { + ERR("Invalid delays[%zu][0]: %d (%d)\n", i, delays[i][0], MaxHrirDelay); + return nullptr; + } + delays[i][0] <<= HrirDelayFracBits; + } + + /* Mirror the left ear responses to the right ear. */ + MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data()); + } + else if(channelType == ChanType_LeftRight) + { + if(sampleType == SampleType_S16) + { + for(auto &hrir : coeffs) + { + for(auto &val : al::span{hrir.data(), irSize}) + { + val[0] = readle(data) / 32768.0f; + val[1] = readle(data) / 32768.0f; + } + } + } + else if(sampleType == SampleType_S24) + { + for(auto &hrir : coeffs) + { + for(auto &val : al::span{hrir.data(), irSize}) + { + val[0] = static_cast(readle(data)) / 8388608.0f; + val[1] = static_cast(readle(data)) / 8388608.0f; + } + } + } + for(auto &val : delays) + { + val[0] = readle(data); + val[1] = readle(data); + } + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + + for(size_t i{0};i < irTotal;++i) + { + if(delays[i][0] > MaxHrirDelay) + { + ERR("Invalid delays[%zu][0]: %d (%d)\n", i, delays[i][0], MaxHrirDelay); + return nullptr; + } + if(delays[i][1] > MaxHrirDelay) + { + ERR("Invalid delays[%zu][1]: %d (%d)\n", i, delays[i][1], MaxHrirDelay); + return nullptr; + } + delays[i][0] <<= HrirDelayFracBits; + delays[i][1] <<= HrirDelayFracBits; + } + } + + if(fdCount > 1) + { + auto fields_ = al::vector(fields.size()); + auto elevs_ = al::vector(elevs.size()); + auto coeffs_ = al::vector(coeffs.size()); + auto delays_ = al::vector(delays.size()); + + /* Simple reverse for the per-field elements. */ + std::reverse_copy(fields.cbegin(), fields.cend(), fields_.begin()); + + /* Each field has a group of elevations, which each have an azimuth + * count. Reverse the order of the groups, keeping the relative order + * of per-group azimuth counts. + */ + auto elevs__end = elevs_.end(); + auto copy_azs = [&elevs,&elevs__end](const ptrdiff_t ebase, const HrtfStore::Field &field) + -> ptrdiff_t + { + auto elevs_src = elevs.begin()+ebase; + elevs__end = std::copy_backward(elevs_src, elevs_src+field.evCount, elevs__end); + return ebase + field.evCount; + }; + (void)std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_azs); + assert(elevs_.begin() == elevs__end); + + /* Reestablish the IR offset for each elevation index, given the new + * ordering of elevations. + */ + elevs_[0].irOffset = 0; + std::partial_sum(elevs_.cbegin(), elevs_.cend(), elevs_.begin(), + [](const HrtfStore::Elevation &last, const HrtfStore::Elevation &cur) + -> HrtfStore::Elevation + { + return HrtfStore::Elevation{cur.azCount, + static_cast(last.azCount + last.irOffset)}; + }); + + /* Reverse the order of each field's group of IRs. */ + auto coeffs_end = coeffs_.end(); + auto delays_end = delays_.end(); + auto copy_irs = [&elevs,&coeffs,&delays,&coeffs_end,&delays_end]( + const ptrdiff_t ebase, const HrtfStore::Field &field) -> ptrdiff_t + { + auto accum_az = [](int count, const HrtfStore::Elevation &elev) noexcept -> int + { return count + elev.azCount; }; + const auto elevs_mid = elevs.cbegin() + ebase; + const auto elevs_end = elevs_mid + field.evCount; + const int abase{std::accumulate(elevs.cbegin(), elevs_mid, 0, accum_az)}; + const int num_azs{std::accumulate(elevs_mid, elevs_end, 0, accum_az)}; + + coeffs_end = std::copy_backward(coeffs.cbegin() + abase, + coeffs.cbegin() + (abase+num_azs), coeffs_end); + delays_end = std::copy_backward(delays.cbegin() + abase, + delays.cbegin() + (abase+num_azs), delays_end); + + return ebase + field.evCount; + }; + (void)std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_irs); + assert(coeffs_.begin() == coeffs_end); + assert(delays_.begin() == delays_end); + + fields = std::move(fields_); + elevs = std::move(elevs_); + coeffs = std::move(coeffs_); + delays = std::move(delays_); + } + + return CreateHrtfStore(rate, irSize, {fields.data(), fields.size()}, + {elevs.data(), elevs.size()}, coeffs.data(), delays.data(), filename); +} + +std::unique_ptr LoadHrtf03(std::istream &data, const char *filename) +{ + constexpr ubyte ChanType_LeftOnly{0}; + constexpr ubyte ChanType_LeftRight{1}; + + uint rate{readle(data)}; + ubyte channelType{readle(data)}; + ushort irSize{readle(data)}; + ubyte fdCount{readle(data)}; + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + + if(channelType > ChanType_LeftRight) + { + ERR("Unsupported channel type: %d\n", channelType); + return nullptr; + } + + if(irSize < MinIrLength || irSize > HrirLength) + { + ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength); + return nullptr; + } + if(fdCount < 1 || fdCount > MaxFdCount) + { + ERR("Unsupported number of field-depths: fdCount=%d (%d to %d)\n", fdCount, MinFdCount, + MaxFdCount); + return nullptr; + } + + auto fields = al::vector(fdCount); + auto elevs = al::vector{}; + for(size_t f{0};f < fdCount;f++) + { + const ushort distance{readle(data)}; + const ubyte evCount{readle(data)}; + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + + if(distance < MinFdDistance || distance > MaxFdDistance) + { + ERR("Unsupported field distance[%zu]=%d (%d to %d millimeters)\n", f, distance, + MinFdDistance, MaxFdDistance); + return nullptr; + } + if(evCount < MinEvCount || evCount > MaxEvCount) + { + ERR("Unsupported elevation count: evCount[%zu]=%d (%d to %d)\n", f, evCount, + MinEvCount, MaxEvCount); + return nullptr; + } + + fields[f].distance = distance / 1000.0f; + fields[f].evCount = evCount; + if(f > 0 && fields[f].distance > fields[f-1].distance) + { + ERR("Field distance[%zu] is not before previous (%f <= %f)\n", f, fields[f].distance, + fields[f-1].distance); + return nullptr; + } + + const size_t ebase{elevs.size()}; + elevs.resize(ebase + evCount); + for(auto &elev : al::span(elevs.data()+ebase, evCount)) + elev.azCount = readle(data); + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + + for(size_t e{0};e < evCount;e++) + { + if(elevs[ebase+e].azCount < MinAzCount || elevs[ebase+e].azCount > MaxAzCount) + { + ERR("Unsupported azimuth count: azCount[%zu][%zu]=%d (%d to %d)\n", f, e, + elevs[ebase+e].azCount, MinAzCount, MaxAzCount); + return nullptr; + } + } + } + + elevs[0].irOffset = 0; + std::partial_sum(elevs.cbegin(), elevs.cend(), elevs.begin(), + [](const HrtfStore::Elevation &last, const HrtfStore::Elevation &cur) + -> HrtfStore::Elevation + { + return HrtfStore::Elevation{cur.azCount, + static_cast(last.azCount + last.irOffset)}; + }); + const auto irTotal = static_cast(elevs.back().azCount + elevs.back().irOffset); + + auto coeffs = al::vector(irTotal, HrirArray{}); + auto delays = al::vector(irTotal); + if(channelType == ChanType_LeftOnly) + { + for(auto &hrir : coeffs) + { + for(auto &val : al::span{hrir.data(), irSize}) + val[0] = static_cast(readle(data)) / 8388608.0f; + } + for(auto &val : delays) + val[0] = readle(data); + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + for(size_t i{0};i < irTotal;++i) + { + if(delays[i][0] > MaxHrirDelay<{hrir.data(), irSize}) + { + val[0] = static_cast(readle(data)) / 8388608.0f; + val[1] = static_cast(readle(data)) / 8388608.0f; + } + } + for(auto &val : delays) + { + val[0] = readle(data); + val[1] = readle(data); + } + if(!data || data.eof()) + { + ERR("Failed reading %s\n", filename); + return nullptr; + } + + for(size_t i{0};i < irTotal;++i) + { + if(delays[i][0] > MaxHrirDelay< MaxHrirDelay< bool { return name == entry.mDispName; }; + auto &enum_names = EnumeratedHrtfs; + return std::find_if(enum_names.cbegin(), enum_names.cend(), match_name) != enum_names.cend(); +} + +void AddFileEntry(const std::string &filename) +{ + /* Check if this file has already been enumerated. */ + auto enum_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(), + [&filename](const HrtfEntry &entry) -> bool + { return entry.mFilename == filename; }); + if(enum_iter != EnumeratedHrtfs.cend()) + { + TRACE("Skipping duplicate file entry %s\n", filename.c_str()); + return; + } + + /* TODO: Get a human-readable name from the HRTF data (possibly coming in a + * format update). */ + size_t namepos{filename.find_last_of('/')+1}; + if(!namepos) namepos = filename.find_last_of('\\')+1; + + size_t extpos{filename.find_last_of('.')}; + if(extpos <= namepos) extpos = std::string::npos; + + const std::string basename{(extpos == std::string::npos) ? + filename.substr(namepos) : filename.substr(namepos, extpos-namepos)}; + std::string newname{basename}; + int count{1}; + while(checkName(newname)) + { + newname = basename; + newname += " #"; + newname += std::to_string(++count); + } + EnumeratedHrtfs.emplace_back(HrtfEntry{newname, filename}); + const HrtfEntry &entry = EnumeratedHrtfs.back(); + + TRACE("Adding file entry \"%s\"\n", entry.mFilename.c_str()); +} + +/* Unfortunate that we have to duplicate AddFileEntry to take a memory buffer + * for input instead of opening the given filename. + */ +void AddBuiltInEntry(const std::string &dispname, uint residx) +{ + const std::string filename{'!'+std::to_string(residx)+'_'+dispname}; + + auto enum_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(), + [&filename](const HrtfEntry &entry) -> bool + { return entry.mFilename == filename; }); + if(enum_iter != EnumeratedHrtfs.cend()) + { + TRACE("Skipping duplicate file entry %s\n", filename.c_str()); + return; + } + + /* TODO: Get a human-readable name from the HRTF data (possibly coming in a + * format update). */ + + std::string newname{dispname}; + int count{1}; + while(checkName(newname)) + { + newname = dispname; + newname += " #"; + newname += std::to_string(++count); + } + EnumeratedHrtfs.emplace_back(HrtfEntry{newname, filename}); + const HrtfEntry &entry = EnumeratedHrtfs.back(); + + TRACE("Adding built-in entry \"%s\"\n", entry.mFilename.c_str()); +} + + +#define IDR_DEFAULT_HRTF_MHR 1 + +#ifndef ALSOFT_EMBED_HRTF_DATA + +al::span GetResource(int /*name*/) +{ return {}; } + +#else + +#include "hrtf_default.h" + +al::span GetResource(int name) +{ + if(name == IDR_DEFAULT_HRTF_MHR) + return {reinterpret_cast(hrtf_default), sizeof(hrtf_default)}; + return {}; +} +#endif + +} // namespace + + +al::vector EnumerateHrtf(al::optional pathopt) +{ + std::lock_guard _{EnumeratedHrtfLock}; + EnumeratedHrtfs.clear(); + + bool usedefaults{true}; + if(pathopt) + { + const char *pathlist{pathopt->c_str()}; + while(pathlist && *pathlist) + { + const char *next, *end; + + while(isspace(*pathlist) || *pathlist == ',') + pathlist++; + if(*pathlist == '\0') + continue; + + next = strchr(pathlist, ','); + if(next) + end = next++; + else + { + end = pathlist + strlen(pathlist); + usedefaults = false; + } + + while(end != pathlist && isspace(*(end-1))) + --end; + if(end != pathlist) + { + const std::string pname{pathlist, end}; + for(const auto &fname : SearchDataFiles(".mhr", pname.c_str())) + AddFileEntry(fname); + } + + pathlist = next; + } + } + + if(usedefaults) + { + for(const auto &fname : SearchDataFiles(".mhr", "openal/hrtf")) + AddFileEntry(fname); + + if(!GetResource(IDR_DEFAULT_HRTF_MHR).empty()) + AddBuiltInEntry("Built-In HRTF", IDR_DEFAULT_HRTF_MHR); + } + + al::vector list; + list.reserve(EnumeratedHrtfs.size()); + for(auto &entry : EnumeratedHrtfs) + list.emplace_back(entry.mDispName); + + return list; +} + +HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) +{ + std::lock_guard _{EnumeratedHrtfLock}; + auto entry_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(), + [&name](const HrtfEntry &entry) -> bool { return entry.mDispName == name; }); + if(entry_iter == EnumeratedHrtfs.cend()) + return nullptr; + const std::string &fname = entry_iter->mFilename; + + std::lock_guard __{LoadedHrtfLock}; + auto hrtf_lt_fname = [](LoadedHrtf &hrtf, const std::string &filename) -> bool + { return hrtf.mFilename < filename; }; + auto handle = std::lower_bound(LoadedHrtfs.begin(), LoadedHrtfs.end(), fname, hrtf_lt_fname); + while(handle != LoadedHrtfs.end() && handle->mFilename == fname) + { + HrtfStore *hrtf{handle->mEntry.get()}; + if(hrtf && hrtf->sampleRate == devrate) + { + hrtf->add_ref(); + return HrtfStorePtr{hrtf}; + } + ++handle; + } + + std::unique_ptr stream; + int residx{}; + char ch{}; + if(sscanf(fname.c_str(), "!%d%c", &residx, &ch) == 2 && ch == '_') + { + TRACE("Loading %s...\n", fname.c_str()); + al::span res{GetResource(residx)}; + if(res.empty()) + { + ERR("Could not get resource %u, %s\n", residx, name.c_str()); + return nullptr; + } + stream = std::make_unique(res.begin(), res.end()); + } + else + { + TRACE("Loading %s...\n", fname.c_str()); + auto fstr = std::make_unique(fname.c_str(), std::ios::binary); + if(!fstr->is_open()) + { + ERR("Could not open %s\n", fname.c_str()); + return nullptr; + } + stream = std::move(fstr); + } + + std::unique_ptr hrtf; + char magic[sizeof(magicMarker03)]; + stream->read(magic, sizeof(magic)); + if(stream->gcount() < static_cast(sizeof(magicMarker03))) + ERR("%s data is too short (%zu bytes)\n", name.c_str(), stream->gcount()); + else if(memcmp(magic, magicMarker03, sizeof(magicMarker03)) == 0) + { + TRACE("Detected data set format v3\n"); + hrtf = LoadHrtf03(*stream, name.c_str()); + } + else if(memcmp(magic, magicMarker02, sizeof(magicMarker02)) == 0) + { + TRACE("Detected data set format v2\n"); + hrtf = LoadHrtf02(*stream, name.c_str()); + } + else if(memcmp(magic, magicMarker01, sizeof(magicMarker01)) == 0) + { + TRACE("Detected data set format v1\n"); + hrtf = LoadHrtf01(*stream, name.c_str()); + } + else if(memcmp(magic, magicMarker00, sizeof(magicMarker00)) == 0) + { + TRACE("Detected data set format v0\n"); + hrtf = LoadHrtf00(*stream, name.c_str()); + } + else + ERR("Invalid header in %s: \"%.8s\"\n", name.c_str(), magic); + stream.reset(); + + if(!hrtf) + { + ERR("Failed to load %s\n", name.c_str()); + return nullptr; + } + + if(hrtf->sampleRate != devrate) + { + TRACE("Resampling HRTF %s (%uhz -> %uhz)\n", name.c_str(), hrtf->sampleRate, devrate); + + /* Calculate the last elevation's index and get the total IR count. */ + const size_t lastEv{std::accumulate(hrtf->field, hrtf->field+hrtf->fdCount, size_t{0}, + [](const size_t curval, const HrtfStore::Field &field) noexcept -> size_t + { return curval + field.evCount; } + ) - 1}; + const size_t irCount{size_t{hrtf->elev[lastEv].irOffset} + hrtf->elev[lastEv].azCount}; + + /* Resample all the IRs. */ + std::array,2> inout; + PPhaseResampler rs; + rs.init(hrtf->sampleRate, devrate); + for(size_t i{0};i < irCount;++i) + { + HrirArray &coeffs = const_cast(hrtf->coeffs[i]); + for(size_t j{0};j < 2;++j) + { + std::transform(coeffs.cbegin(), coeffs.cend(), inout[0].begin(), + [j](const float2 &in) noexcept -> double { return in[j]; }); + rs.process(HrirLength, inout[0].data(), HrirLength, inout[1].data()); + for(size_t k{0};k < HrirLength;++k) + coeffs[k][j] = static_cast(inout[1][k]); + } + } + rs = {}; + + /* Scale the delays for the new sample rate. */ + float max_delay{0.0f}; + auto new_delays = al::vector(irCount); + const float rate_scale{static_cast(devrate)/static_cast(hrtf->sampleRate)}; + for(size_t i{0};i < irCount;++i) + { + for(size_t j{0};j < 2;++j) + { + const float new_delay{std::round(hrtf->delays[i][j] * rate_scale) / + float{HrirDelayFracOne}}; + max_delay = maxf(max_delay, new_delay); + new_delays[i][j] = new_delay; + } + } + + /* If the new delays exceed the max, scale it down to fit (essentially + * shrinking the head radius; not ideal but better than a per-delay + * clamp). + */ + float delay_scale{HrirDelayFracOne}; + if(max_delay > MaxHrirDelay) + { + WARN("Resampled delay exceeds max (%.2f > %d)\n", max_delay, MaxHrirDelay); + delay_scale *= float{MaxHrirDelay} / max_delay; + } + + for(size_t i{0};i < irCount;++i) + { + ubyte2 &delays = const_cast(hrtf->delays[i]); + for(size_t j{0};j < 2;++j) + delays[j] = static_cast(float2int(new_delays[i][j]*delay_scale + 0.5f)); + } + + /* Scale the IR size for the new sample rate and update the stored + * sample rate. + */ + const float newIrSize{std::round(static_cast(hrtf->irSize) * rate_scale)}; + hrtf->irSize = static_cast(minf(HrirLength, newIrSize)); + hrtf->sampleRate = devrate; + } + + TRACE("Loaded HRTF %s for sample rate %uhz, %u-sample filter\n", name.c_str(), + hrtf->sampleRate, hrtf->irSize); + handle = LoadedHrtfs.emplace(handle, LoadedHrtf{fname, std::move(hrtf)}); + + return HrtfStorePtr{handle->mEntry.get()}; +} + + +void HrtfStore::add_ref() +{ + auto ref = IncrementRef(mRef); + TRACE("HrtfStore %p increasing refcount to %u\n", decltype(std::declval()){this}, ref); +} + +void HrtfStore::release() +{ + auto ref = DecrementRef(mRef); + TRACE("HrtfStore %p decreasing refcount to %u\n", decltype(std::declval()){this}, ref); + if(ref == 0) + { + std::lock_guard _{LoadedHrtfLock}; + + /* Go through and remove all unused HRTFs. */ + auto remove_unused = [](LoadedHrtf &hrtf) -> bool + { + HrtfStore *entry{hrtf.mEntry.get()}; + if(entry && ReadRef(entry->mRef) == 0) + { + TRACE("Unloading unused HRTF %s\n", hrtf.mFilename.data()); + hrtf.mEntry = nullptr; + return true; + } + return false; + }; + auto iter = std::remove_if(LoadedHrtfs.begin(), LoadedHrtfs.end(), remove_unused); + LoadedHrtfs.erase(iter, LoadedHrtfs.end()); + } +} diff --git a/core/hrtf.h b/core/hrtf.h new file mode 100644 index 00000000..f2dfc832 --- /dev/null +++ b/core/hrtf.h @@ -0,0 +1,91 @@ +#ifndef CORE_HRTF_H +#define CORE_HRTF_H + +#include +#include +#include +#include + +#include "almalloc.h" +#include "aloptional.h" +#include "alspan.h" +#include "atomic.h" +#include "ambidefs.h" +#include "bufferline.h" +#include "filters/splitter.h" +#include "mixer/hrtfdefs.h" +#include "intrusive_ptr.h" +#include "vector.h" + + +struct HrtfStore { + RefCount mRef; + + uint sampleRate; + uint irSize; + + struct Field { + float distance; + ubyte evCount; + }; + /* NOTE: Fields are stored *backwards*. field[0] is the farthest field, and + * field[fdCount-1] is the nearest. + */ + uint fdCount; + const Field *field; + + struct Elevation { + ushort azCount; + ushort irOffset; + }; + Elevation *elev; + const HrirArray *coeffs; + const ubyte2 *delays; + + void add_ref(); + void release(); + + DEF_PLACE_NEWDEL() +}; +using HrtfStorePtr = al::intrusive_ptr; + + +struct EvRadians { float value; }; +struct AzRadians { float value; }; +struct AngularPoint { + EvRadians Elev; + AzRadians Azim; +}; + + +struct DirectHrtfState { + std::array mTemp; + + /* HRTF filter state for dry buffer content */ + uint mIrSize{0}; + al::FlexArray mChannels; + + DirectHrtfState(size_t numchans) : mChannels{numchans} { } + /** + * Produces HRTF filter coefficients for decoding B-Format, given a set of + * virtual speaker positions, a matching decoding matrix, and per-order + * high-frequency gains for the decoder. The calculated impulse responses + * are ordered and scaled according to the matrix input. + */ + void build(const HrtfStore *Hrtf, const uint irSize, + const al::span AmbiPoints, const float (*AmbiMatrix)[MaxAmbiChannels], + const float XOverFreq, const al::span AmbiOrderHFGain); + + static std::unique_ptr Create(size_t num_chans); + + DEF_FAM_NEWDEL(DirectHrtfState, mChannels) +}; + + +al::vector EnumerateHrtf(al::optional pathopt); +HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate); + +void GetHrtfCoeffs(const HrtfStore *Hrtf, float elevation, float azimuth, float distance, + float spread, HrirArray &coeffs, const al::span delays); + +#endif /* CORE_HRTF_H */ -- cgit v1.2.3 From 519672c8e54585bc6d827dd3efed943e06b3e6cd Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 24 Apr 2021 03:47:23 -0700 Subject: Move some more sources to core --- CMakeLists.txt | 8 +- alc/backends/coreaudio.cpp | 2 +- alc/backends/wasapi.cpp | 2 +- alc/converter.cpp | 371 --------------------------------------------- alc/converter.h | 59 ------- alc/uiddefs.cpp | 37 ----- core/converter.cpp | 371 +++++++++++++++++++++++++++++++++++++++++++++ core/converter.h | 59 +++++++ core/uiddefs.cpp | 37 +++++ 9 files changed, 473 insertions(+), 473 deletions(-) delete mode 100644 alc/converter.cpp delete mode 100644 alc/converter.h delete mode 100644 alc/uiddefs.cpp create mode 100644 core/converter.cpp create mode 100644 core/converter.h create mode 100644 core/uiddefs.cpp (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 15feb2b9..13d7ee54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -651,6 +651,8 @@ set(CORE_OBJS core/bsinc_tables.cpp core/bsinc_tables.h core/bufferline.h + core/converter.cpp + core/converter.h core/cpu_caps.cpp core/cpu_caps.h core/devformat.cpp @@ -677,7 +679,8 @@ set(CORE_OBJS core/mastering.h core/resampler_limits.h core/uhjfilter.cpp - core/uhjfilter.h) + core/uhjfilter.h + core/uiddefs.cpp) set(HAVE_RTKIT 0) option(ALSOFT_REQUIRE_RTKIT "Require RTKit/D-Bus support" FALSE) @@ -755,8 +758,6 @@ set(ALC_OBJS alc/bformatdec.h alc/buffer_storage.cpp alc/buffer_storage.h - alc/converter.cpp - alc/converter.h alc/effectslot.cpp alc/effectslot.h alc/effects/base.h @@ -777,7 +778,6 @@ set(ALC_OBJS alc/front_stablizer.h alc/inprogext.h alc/panning.cpp - alc/uiddefs.cpp alc/voice.cpp alc/voice.h alc/voice_change.h) diff --git a/alc/backends/coreaudio.cpp b/alc/backends/coreaudio.cpp index 43c78063..0dd8e766 100644 --- a/alc/backends/coreaudio.cpp +++ b/alc/backends/coreaudio.cpp @@ -32,7 +32,7 @@ #include "alcmain.h" #include "alu.h" #include "ringbuffer.h" -#include "converter.h" +#include "core/converter.h" #include "core/logging.h" #include "backends/base.h" diff --git a/alc/backends/wasapi.cpp b/alc/backends/wasapi.cpp index 4df64e73..76c6d009 100644 --- a/alc/backends/wasapi.cpp +++ b/alc/backends/wasapi.cpp @@ -59,7 +59,7 @@ #include "alcmain.h" #include "alu.h" #include "comptr.h" -#include "converter.h" +#include "core/converter.h" #include "core/helpers.h" #include "core/logging.h" #include "ringbuffer.h" diff --git a/alc/converter.cpp b/alc/converter.cpp deleted file mode 100644 index f7d4fc46..00000000 --- a/alc/converter.cpp +++ /dev/null @@ -1,371 +0,0 @@ - -#include "config.h" - -#include "converter.h" - -#include -#include -#include -#include -#include - -#include "albit.h" -#include "albyte.h" -#include "alnumeric.h" -#include "core/fpu_ctrl.h" - -struct CTag; -struct CopyTag; - - -namespace { - -constexpr uint MaxPitch{10}; - -static_assert((BufferLineSize-1)/MaxPitch > 0, "MaxPitch is too large for BufferLineSize!"); -static_assert((INT_MAX>>MixerFracBits)/MaxPitch > BufferLineSize, - "MaxPitch and/or BufferLineSize are too large for MixerFracBits!"); - -/* Base template left undefined. Should be marked =delete, but Clang 3.8.1 - * chokes on that given the inline specializations. - */ -template -inline float LoadSample(DevFmtType_t val) noexcept; - -template<> inline float LoadSample(DevFmtType_t val) noexcept -{ return val * (1.0f/128.0f); } -template<> inline float LoadSample(DevFmtType_t val) noexcept -{ return val * (1.0f/32768.0f); } -template<> inline float LoadSample(DevFmtType_t val) noexcept -{ return static_cast(val) * (1.0f/2147483648.0f); } -template<> inline float LoadSample(DevFmtType_t val) noexcept -{ return val; } - -template<> inline float LoadSample(DevFmtType_t val) noexcept -{ return LoadSample(static_cast(val - 128)); } -template<> inline float LoadSample(DevFmtType_t val) noexcept -{ return LoadSample(static_cast(val - 32768)); } -template<> inline float LoadSample(DevFmtType_t val) noexcept -{ return LoadSample(static_cast(val - 2147483648u)); } - - -template -inline void LoadSampleArray(float *RESTRICT dst, const void *src, const size_t srcstep, - const size_t samples) noexcept -{ - const DevFmtType_t *ssrc = static_cast*>(src); - for(size_t i{0u};i < samples;i++) - dst[i] = LoadSample(ssrc[i*srcstep]); -} - -void LoadSamples(float *dst, const void *src, const size_t srcstep, const DevFmtType srctype, - const size_t samples) noexcept -{ -#define HANDLE_FMT(T) \ - case T: LoadSampleArray(dst, src, srcstep, samples); break - switch(srctype) - { - HANDLE_FMT(DevFmtByte); - HANDLE_FMT(DevFmtUByte); - HANDLE_FMT(DevFmtShort); - HANDLE_FMT(DevFmtUShort); - HANDLE_FMT(DevFmtInt); - HANDLE_FMT(DevFmtUInt); - HANDLE_FMT(DevFmtFloat); - } -#undef HANDLE_FMT -} - - -template -inline DevFmtType_t StoreSample(float) noexcept; - -template<> inline float StoreSample(float val) noexcept -{ return val; } -template<> inline int32_t StoreSample(float val) noexcept -{ return fastf2i(clampf(val*2147483648.0f, -2147483648.0f, 2147483520.0f)); } -template<> inline int16_t StoreSample(float val) noexcept -{ return static_cast(fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f))); } -template<> inline int8_t StoreSample(float val) noexcept -{ return static_cast(fastf2i(clampf(val*128.0f, -128.0f, 127.0f))); } - -/* Define unsigned output variations. */ -template<> inline uint32_t StoreSample(float val) noexcept -{ return static_cast(StoreSample(val)) + 2147483648u; } -template<> inline uint16_t StoreSample(float val) noexcept -{ return static_cast(StoreSample(val) + 32768); } -template<> inline uint8_t StoreSample(float val) noexcept -{ return static_cast(StoreSample(val) + 128); } - -template -inline void StoreSampleArray(void *dst, const float *RESTRICT src, const size_t dststep, - const size_t samples) noexcept -{ - DevFmtType_t *sdst = static_cast*>(dst); - for(size_t i{0u};i < samples;i++) - sdst[i*dststep] = StoreSample(src[i]); -} - - -void StoreSamples(void *dst, const float *src, const size_t dststep, const DevFmtType dsttype, - const size_t samples) noexcept -{ -#define HANDLE_FMT(T) \ - case T: StoreSampleArray(dst, src, dststep, samples); break - switch(dsttype) - { - HANDLE_FMT(DevFmtByte); - HANDLE_FMT(DevFmtUByte); - HANDLE_FMT(DevFmtShort); - HANDLE_FMT(DevFmtUShort); - HANDLE_FMT(DevFmtInt); - HANDLE_FMT(DevFmtUInt); - HANDLE_FMT(DevFmtFloat); - } -#undef HANDLE_FMT -} - - -template -void Mono2Stereo(float *RESTRICT dst, const void *src, const size_t frames) noexcept -{ - const DevFmtType_t *ssrc = static_cast*>(src); - for(size_t i{0u};i < frames;i++) - dst[i*2 + 1] = dst[i*2 + 0] = LoadSample(ssrc[i]) * 0.707106781187f; -} - -template -void Multi2Mono(uint chanmask, const size_t step, const float scale, float *RESTRICT dst, - const void *src, const size_t frames) noexcept -{ - const DevFmtType_t *ssrc = static_cast*>(src); - std::fill_n(dst, frames, 0.0f); - for(size_t c{0};chanmask;++c) - { - if LIKELY((chanmask&1)) - { - for(size_t i{0u};i < frames;i++) - dst[i] += LoadSample(ssrc[i*step + c]); - } - chanmask >>= 1; - } - for(size_t i{0u};i < frames;i++) - dst[i] *= scale; -} - -} // namespace - -SampleConverterPtr CreateSampleConverter(DevFmtType srcType, DevFmtType dstType, size_t numchans, - uint srcRate, uint dstRate, Resampler resampler) -{ - if(numchans < 1 || srcRate < 1 || dstRate < 1) - return nullptr; - - SampleConverterPtr converter{new(FamCount(numchans)) SampleConverter{numchans}}; - converter->mSrcType = srcType; - converter->mDstType = dstType; - converter->mSrcTypeSize = BytesFromDevFmt(srcType); - converter->mDstTypeSize = BytesFromDevFmt(dstType); - - converter->mSrcPrepCount = 0; - converter->mFracOffset = 0; - - /* Have to set the mixer FPU mode since that's what the resampler code expects. */ - FPUCtl mixer_mode{}; - auto step = static_cast( - mind(srcRate*double{MixerFracOne}/dstRate + 0.5, MaxPitch*MixerFracOne)); - converter->mIncrement = maxu(step, 1); - if(converter->mIncrement == MixerFracOne) - converter->mResample = Resample_; - else - converter->mResample = PrepareResampler(resampler, converter->mIncrement, - &converter->mState); - - return converter; -} - -uint SampleConverter::availableOut(uint srcframes) const -{ - int prepcount{mSrcPrepCount}; - if(prepcount < 0) - { - /* Negative prepcount means we need to skip that many input samples. */ - if(static_cast(-prepcount) >= srcframes) - return 0; - srcframes -= static_cast(-prepcount); - prepcount = 0; - } - - if(srcframes < 1) - { - /* No output samples if there's no input samples. */ - return 0; - } - - if(prepcount < MaxResamplerPadding - && static_cast(MaxResamplerPadding - prepcount) >= srcframes) - { - /* Not enough input samples to generate an output sample. */ - return 0; - } - - auto DataSize64 = static_cast(prepcount); - DataSize64 += srcframes; - DataSize64 -= MaxResamplerPadding; - DataSize64 <<= MixerFracBits; - DataSize64 -= mFracOffset; - - /* If we have a full prep, we can generate at least one sample. */ - return static_cast(clampu64((DataSize64 + mIncrement-1)/mIncrement, 1, - std::numeric_limits::max())); -} - -uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint dstframes) -{ - const uint SrcFrameSize{static_cast(mChan.size()) * mSrcTypeSize}; - const uint DstFrameSize{static_cast(mChan.size()) * mDstTypeSize}; - const uint increment{mIncrement}; - auto SamplesIn = static_cast(*src); - uint NumSrcSamples{*srcframes}; - - FPUCtl mixer_mode{}; - uint pos{0}; - while(pos < dstframes && NumSrcSamples > 0) - { - int prepcount{mSrcPrepCount}; - if(prepcount < 0) - { - /* Negative prepcount means we need to skip that many input samples. */ - if(static_cast(-prepcount) >= NumSrcSamples) - { - mSrcPrepCount = static_cast(NumSrcSamples) + prepcount; - NumSrcSamples = 0; - break; - } - SamplesIn += SrcFrameSize*static_cast(-prepcount); - NumSrcSamples -= static_cast(-prepcount); - mSrcPrepCount = 0; - continue; - } - const uint toread{minu(NumSrcSamples, BufferLineSize - MaxResamplerPadding)}; - - if(prepcount < MaxResamplerPadding - && static_cast(MaxResamplerPadding - prepcount) >= toread) - { - /* Not enough input samples to generate an output sample. Store - * what we're given for later. - */ - for(size_t chan{0u};chan < mChan.size();chan++) - LoadSamples(&mChan[chan].PrevSamples[prepcount], SamplesIn + mSrcTypeSize*chan, - mChan.size(), mSrcType, toread); - - mSrcPrepCount = prepcount + static_cast(toread); - NumSrcSamples = 0; - break; - } - - float *RESTRICT SrcData{mSrcSamples}; - float *RESTRICT DstData{mDstSamples}; - uint DataPosFrac{mFracOffset}; - auto DataSize64 = static_cast(prepcount); - DataSize64 += toread; - DataSize64 -= MaxResamplerPadding; - DataSize64 <<= MixerFracBits; - DataSize64 -= DataPosFrac; - - /* If we have a full prep, we can generate at least one sample. */ - auto DstSize = static_cast( - clampu64((DataSize64 + increment-1)/increment, 1, BufferLineSize)); - DstSize = minu(DstSize, dstframes-pos); - - for(size_t chan{0u};chan < mChan.size();chan++) - { - const al::byte *SrcSamples{SamplesIn + mSrcTypeSize*chan}; - al::byte *DstSamples = static_cast(dst) + mDstTypeSize*chan; - - /* Load the previous samples into the source data first, then the - * new samples from the input buffer. - */ - std::copy_n(mChan[chan].PrevSamples, prepcount, SrcData); - LoadSamples(SrcData + prepcount, SrcSamples, mChan.size(), mSrcType, toread); - - /* Store as many prep samples for next time as possible, given the - * number of output samples being generated. - */ - uint SrcDataEnd{(DstSize*increment + DataPosFrac)>>MixerFracBits}; - if(SrcDataEnd >= static_cast(prepcount)+toread) - std::fill(std::begin(mChan[chan].PrevSamples), - std::end(mChan[chan].PrevSamples), 0.0f); - else - { - const size_t len{minz(al::size(mChan[chan].PrevSamples), - static_cast(prepcount)+toread-SrcDataEnd)}; - std::copy_n(SrcData+SrcDataEnd, len, mChan[chan].PrevSamples); - std::fill(std::begin(mChan[chan].PrevSamples)+len, - std::end(mChan[chan].PrevSamples), 0.0f); - } - - /* Now resample, and store the result in the output buffer. */ - const float *ResampledData{mResample(&mState, SrcData+(MaxResamplerPadding>>1), - DataPosFrac, increment, {DstData, DstSize})}; - - StoreSamples(DstSamples, ResampledData, mChan.size(), mDstType, DstSize); - } - - /* Update the number of prep samples still available, as well as the - * fractional offset. - */ - DataPosFrac += increment*DstSize; - mSrcPrepCount = mini(prepcount + static_cast(toread - (DataPosFrac>>MixerFracBits)), - MaxResamplerPadding); - mFracOffset = DataPosFrac & MixerFracMask; - - /* Update the src and dst pointers in case there's still more to do. */ - SamplesIn += SrcFrameSize*(DataPosFrac>>MixerFracBits); - NumSrcSamples -= minu(NumSrcSamples, (DataPosFrac>>MixerFracBits)); - - dst = static_cast(dst) + DstFrameSize*DstSize; - pos += DstSize; - } - - *src = SamplesIn; - *srcframes = NumSrcSamples; - - return pos; -} - - -void ChannelConverter::convert(const void *src, float *dst, uint frames) const -{ - if(mDstChans == DevFmtMono) - { - const float scale{std::sqrt(1.0f / static_cast(al::popcount(mChanMask)))}; - switch(mSrcType) - { -#define HANDLE_FMT(T) case T: Multi2Mono(mChanMask, mSrcStep, scale, dst, src, frames); break - HANDLE_FMT(DevFmtByte); - HANDLE_FMT(DevFmtUByte); - HANDLE_FMT(DevFmtShort); - HANDLE_FMT(DevFmtUShort); - HANDLE_FMT(DevFmtInt); - HANDLE_FMT(DevFmtUInt); - HANDLE_FMT(DevFmtFloat); -#undef HANDLE_FMT - } - } - else if(mChanMask == 0x1 && mDstChans == DevFmtStereo) - { - switch(mSrcType) - { -#define HANDLE_FMT(T) case T: Mono2Stereo(dst, src, frames); break - HANDLE_FMT(DevFmtByte); - HANDLE_FMT(DevFmtUByte); - HANDLE_FMT(DevFmtShort); - HANDLE_FMT(DevFmtUShort); - HANDLE_FMT(DevFmtInt); - HANDLE_FMT(DevFmtUInt); - HANDLE_FMT(DevFmtFloat); -#undef HANDLE_FMT - } - } -} diff --git a/alc/converter.h b/alc/converter.h deleted file mode 100644 index ffcfbc18..00000000 --- a/alc/converter.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef CONVERTER_H -#define CONVERTER_H - -#include -#include - -#include "almalloc.h" -#include "core/devformat.h" -#include "core/mixer/defs.h" - -using uint = unsigned int; - - -struct SampleConverter { - DevFmtType mSrcType{}; - DevFmtType mDstType{}; - uint mSrcTypeSize{}; - uint mDstTypeSize{}; - - int mSrcPrepCount{}; - - uint mFracOffset{}; - uint mIncrement{}; - InterpState mState{}; - ResamplerFunc mResample{}; - - alignas(16) float mSrcSamples[BufferLineSize]{}; - alignas(16) float mDstSamples[BufferLineSize]{}; - - struct ChanSamples { - alignas(16) float PrevSamples[MaxResamplerPadding]; - }; - al::FlexArray mChan; - - SampleConverter(size_t numchans) : mChan{numchans} { } - - uint convert(const void **src, uint *srcframes, void *dst, uint dstframes); - uint availableOut(uint srcframes) const; - - DEF_FAM_NEWDEL(SampleConverter, mChan) -}; -using SampleConverterPtr = std::unique_ptr; - -SampleConverterPtr CreateSampleConverter(DevFmtType srcType, DevFmtType dstType, size_t numchans, - uint srcRate, uint dstRate, Resampler resampler); - - -struct ChannelConverter { - DevFmtType mSrcType{}; - uint mSrcStep{}; - uint mChanMask{}; - DevFmtChannels mDstChans{}; - - bool is_active() const noexcept { return mChanMask != 0; } - - void convert(const void *src, float *dst, uint frames) const; -}; - -#endif /* CONVERTER_H */ diff --git a/alc/uiddefs.cpp b/alc/uiddefs.cpp deleted file mode 100644 index 244c01a5..00000000 --- a/alc/uiddefs.cpp +++ /dev/null @@ -1,37 +0,0 @@ - -#include "config.h" - - -#ifndef AL_NO_UID_DEFS - -#if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H) -#define INITGUID -#include -#ifdef HAVE_GUIDDEF_H -#include -#else -#include -#endif - -DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71); -DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71); - -DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x00,0xa0,0xc9,0x25,0xcd,0x16); - -DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e); -DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6); -DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2); -DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2); -DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17); - -#ifdef HAVE_WASAPI -#include -#include -#include -DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14); -DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0); -DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 ); -#endif -#endif - -#endif /* AL_NO_UID_DEFS */ diff --git a/core/converter.cpp b/core/converter.cpp new file mode 100644 index 00000000..6a06b464 --- /dev/null +++ b/core/converter.cpp @@ -0,0 +1,371 @@ + +#include "config.h" + +#include "converter.h" + +#include +#include +#include +#include +#include + +#include "albit.h" +#include "albyte.h" +#include "alnumeric.h" +#include "fpu_ctrl.h" + +struct CTag; +struct CopyTag; + + +namespace { + +constexpr uint MaxPitch{10}; + +static_assert((BufferLineSize-1)/MaxPitch > 0, "MaxPitch is too large for BufferLineSize!"); +static_assert((INT_MAX>>MixerFracBits)/MaxPitch > BufferLineSize, + "MaxPitch and/or BufferLineSize are too large for MixerFracBits!"); + +/* Base template left undefined. Should be marked =delete, but Clang 3.8.1 + * chokes on that given the inline specializations. + */ +template +inline float LoadSample(DevFmtType_t val) noexcept; + +template<> inline float LoadSample(DevFmtType_t val) noexcept +{ return val * (1.0f/128.0f); } +template<> inline float LoadSample(DevFmtType_t val) noexcept +{ return val * (1.0f/32768.0f); } +template<> inline float LoadSample(DevFmtType_t val) noexcept +{ return static_cast(val) * (1.0f/2147483648.0f); } +template<> inline float LoadSample(DevFmtType_t val) noexcept +{ return val; } + +template<> inline float LoadSample(DevFmtType_t val) noexcept +{ return LoadSample(static_cast(val - 128)); } +template<> inline float LoadSample(DevFmtType_t val) noexcept +{ return LoadSample(static_cast(val - 32768)); } +template<> inline float LoadSample(DevFmtType_t val) noexcept +{ return LoadSample(static_cast(val - 2147483648u)); } + + +template +inline void LoadSampleArray(float *RESTRICT dst, const void *src, const size_t srcstep, + const size_t samples) noexcept +{ + const DevFmtType_t *ssrc = static_cast*>(src); + for(size_t i{0u};i < samples;i++) + dst[i] = LoadSample(ssrc[i*srcstep]); +} + +void LoadSamples(float *dst, const void *src, const size_t srcstep, const DevFmtType srctype, + const size_t samples) noexcept +{ +#define HANDLE_FMT(T) \ + case T: LoadSampleArray(dst, src, srcstep, samples); break + switch(srctype) + { + HANDLE_FMT(DevFmtByte); + HANDLE_FMT(DevFmtUByte); + HANDLE_FMT(DevFmtShort); + HANDLE_FMT(DevFmtUShort); + HANDLE_FMT(DevFmtInt); + HANDLE_FMT(DevFmtUInt); + HANDLE_FMT(DevFmtFloat); + } +#undef HANDLE_FMT +} + + +template +inline DevFmtType_t StoreSample(float) noexcept; + +template<> inline float StoreSample(float val) noexcept +{ return val; } +template<> inline int32_t StoreSample(float val) noexcept +{ return fastf2i(clampf(val*2147483648.0f, -2147483648.0f, 2147483520.0f)); } +template<> inline int16_t StoreSample(float val) noexcept +{ return static_cast(fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f))); } +template<> inline int8_t StoreSample(float val) noexcept +{ return static_cast(fastf2i(clampf(val*128.0f, -128.0f, 127.0f))); } + +/* Define unsigned output variations. */ +template<> inline uint32_t StoreSample(float val) noexcept +{ return static_cast(StoreSample(val)) + 2147483648u; } +template<> inline uint16_t StoreSample(float val) noexcept +{ return static_cast(StoreSample(val) + 32768); } +template<> inline uint8_t StoreSample(float val) noexcept +{ return static_cast(StoreSample(val) + 128); } + +template +inline void StoreSampleArray(void *dst, const float *RESTRICT src, const size_t dststep, + const size_t samples) noexcept +{ + DevFmtType_t *sdst = static_cast*>(dst); + for(size_t i{0u};i < samples;i++) + sdst[i*dststep] = StoreSample(src[i]); +} + + +void StoreSamples(void *dst, const float *src, const size_t dststep, const DevFmtType dsttype, + const size_t samples) noexcept +{ +#define HANDLE_FMT(T) \ + case T: StoreSampleArray(dst, src, dststep, samples); break + switch(dsttype) + { + HANDLE_FMT(DevFmtByte); + HANDLE_FMT(DevFmtUByte); + HANDLE_FMT(DevFmtShort); + HANDLE_FMT(DevFmtUShort); + HANDLE_FMT(DevFmtInt); + HANDLE_FMT(DevFmtUInt); + HANDLE_FMT(DevFmtFloat); + } +#undef HANDLE_FMT +} + + +template +void Mono2Stereo(float *RESTRICT dst, const void *src, const size_t frames) noexcept +{ + const DevFmtType_t *ssrc = static_cast*>(src); + for(size_t i{0u};i < frames;i++) + dst[i*2 + 1] = dst[i*2 + 0] = LoadSample(ssrc[i]) * 0.707106781187f; +} + +template +void Multi2Mono(uint chanmask, const size_t step, const float scale, float *RESTRICT dst, + const void *src, const size_t frames) noexcept +{ + const DevFmtType_t *ssrc = static_cast*>(src); + std::fill_n(dst, frames, 0.0f); + for(size_t c{0};chanmask;++c) + { + if LIKELY((chanmask&1)) + { + for(size_t i{0u};i < frames;i++) + dst[i] += LoadSample(ssrc[i*step + c]); + } + chanmask >>= 1; + } + for(size_t i{0u};i < frames;i++) + dst[i] *= scale; +} + +} // namespace + +SampleConverterPtr CreateSampleConverter(DevFmtType srcType, DevFmtType dstType, size_t numchans, + uint srcRate, uint dstRate, Resampler resampler) +{ + if(numchans < 1 || srcRate < 1 || dstRate < 1) + return nullptr; + + SampleConverterPtr converter{new(FamCount(numchans)) SampleConverter{numchans}}; + converter->mSrcType = srcType; + converter->mDstType = dstType; + converter->mSrcTypeSize = BytesFromDevFmt(srcType); + converter->mDstTypeSize = BytesFromDevFmt(dstType); + + converter->mSrcPrepCount = 0; + converter->mFracOffset = 0; + + /* Have to set the mixer FPU mode since that's what the resampler code expects. */ + FPUCtl mixer_mode{}; + auto step = static_cast( + mind(srcRate*double{MixerFracOne}/dstRate + 0.5, MaxPitch*MixerFracOne)); + converter->mIncrement = maxu(step, 1); + if(converter->mIncrement == MixerFracOne) + converter->mResample = Resample_; + else + converter->mResample = PrepareResampler(resampler, converter->mIncrement, + &converter->mState); + + return converter; +} + +uint SampleConverter::availableOut(uint srcframes) const +{ + int prepcount{mSrcPrepCount}; + if(prepcount < 0) + { + /* Negative prepcount means we need to skip that many input samples. */ + if(static_cast(-prepcount) >= srcframes) + return 0; + srcframes -= static_cast(-prepcount); + prepcount = 0; + } + + if(srcframes < 1) + { + /* No output samples if there's no input samples. */ + return 0; + } + + if(prepcount < MaxResamplerPadding + && static_cast(MaxResamplerPadding - prepcount) >= srcframes) + { + /* Not enough input samples to generate an output sample. */ + return 0; + } + + auto DataSize64 = static_cast(prepcount); + DataSize64 += srcframes; + DataSize64 -= MaxResamplerPadding; + DataSize64 <<= MixerFracBits; + DataSize64 -= mFracOffset; + + /* If we have a full prep, we can generate at least one sample. */ + return static_cast(clampu64((DataSize64 + mIncrement-1)/mIncrement, 1, + std::numeric_limits::max())); +} + +uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint dstframes) +{ + const uint SrcFrameSize{static_cast(mChan.size()) * mSrcTypeSize}; + const uint DstFrameSize{static_cast(mChan.size()) * mDstTypeSize}; + const uint increment{mIncrement}; + auto SamplesIn = static_cast(*src); + uint NumSrcSamples{*srcframes}; + + FPUCtl mixer_mode{}; + uint pos{0}; + while(pos < dstframes && NumSrcSamples > 0) + { + int prepcount{mSrcPrepCount}; + if(prepcount < 0) + { + /* Negative prepcount means we need to skip that many input samples. */ + if(static_cast(-prepcount) >= NumSrcSamples) + { + mSrcPrepCount = static_cast(NumSrcSamples) + prepcount; + NumSrcSamples = 0; + break; + } + SamplesIn += SrcFrameSize*static_cast(-prepcount); + NumSrcSamples -= static_cast(-prepcount); + mSrcPrepCount = 0; + continue; + } + const uint toread{minu(NumSrcSamples, BufferLineSize - MaxResamplerPadding)}; + + if(prepcount < MaxResamplerPadding + && static_cast(MaxResamplerPadding - prepcount) >= toread) + { + /* Not enough input samples to generate an output sample. Store + * what we're given for later. + */ + for(size_t chan{0u};chan < mChan.size();chan++) + LoadSamples(&mChan[chan].PrevSamples[prepcount], SamplesIn + mSrcTypeSize*chan, + mChan.size(), mSrcType, toread); + + mSrcPrepCount = prepcount + static_cast(toread); + NumSrcSamples = 0; + break; + } + + float *RESTRICT SrcData{mSrcSamples}; + float *RESTRICT DstData{mDstSamples}; + uint DataPosFrac{mFracOffset}; + auto DataSize64 = static_cast(prepcount); + DataSize64 += toread; + DataSize64 -= MaxResamplerPadding; + DataSize64 <<= MixerFracBits; + DataSize64 -= DataPosFrac; + + /* If we have a full prep, we can generate at least one sample. */ + auto DstSize = static_cast( + clampu64((DataSize64 + increment-1)/increment, 1, BufferLineSize)); + DstSize = minu(DstSize, dstframes-pos); + + for(size_t chan{0u};chan < mChan.size();chan++) + { + const al::byte *SrcSamples{SamplesIn + mSrcTypeSize*chan}; + al::byte *DstSamples = static_cast(dst) + mDstTypeSize*chan; + + /* Load the previous samples into the source data first, then the + * new samples from the input buffer. + */ + std::copy_n(mChan[chan].PrevSamples, prepcount, SrcData); + LoadSamples(SrcData + prepcount, SrcSamples, mChan.size(), mSrcType, toread); + + /* Store as many prep samples for next time as possible, given the + * number of output samples being generated. + */ + uint SrcDataEnd{(DstSize*increment + DataPosFrac)>>MixerFracBits}; + if(SrcDataEnd >= static_cast(prepcount)+toread) + std::fill(std::begin(mChan[chan].PrevSamples), + std::end(mChan[chan].PrevSamples), 0.0f); + else + { + const size_t len{minz(al::size(mChan[chan].PrevSamples), + static_cast(prepcount)+toread-SrcDataEnd)}; + std::copy_n(SrcData+SrcDataEnd, len, mChan[chan].PrevSamples); + std::fill(std::begin(mChan[chan].PrevSamples)+len, + std::end(mChan[chan].PrevSamples), 0.0f); + } + + /* Now resample, and store the result in the output buffer. */ + const float *ResampledData{mResample(&mState, SrcData+(MaxResamplerPadding>>1), + DataPosFrac, increment, {DstData, DstSize})}; + + StoreSamples(DstSamples, ResampledData, mChan.size(), mDstType, DstSize); + } + + /* Update the number of prep samples still available, as well as the + * fractional offset. + */ + DataPosFrac += increment*DstSize; + mSrcPrepCount = mini(prepcount + static_cast(toread - (DataPosFrac>>MixerFracBits)), + MaxResamplerPadding); + mFracOffset = DataPosFrac & MixerFracMask; + + /* Update the src and dst pointers in case there's still more to do. */ + SamplesIn += SrcFrameSize*(DataPosFrac>>MixerFracBits); + NumSrcSamples -= minu(NumSrcSamples, (DataPosFrac>>MixerFracBits)); + + dst = static_cast(dst) + DstFrameSize*DstSize; + pos += DstSize; + } + + *src = SamplesIn; + *srcframes = NumSrcSamples; + + return pos; +} + + +void ChannelConverter::convert(const void *src, float *dst, uint frames) const +{ + if(mDstChans == DevFmtMono) + { + const float scale{std::sqrt(1.0f / static_cast(al::popcount(mChanMask)))}; + switch(mSrcType) + { +#define HANDLE_FMT(T) case T: Multi2Mono(mChanMask, mSrcStep, scale, dst, src, frames); break + HANDLE_FMT(DevFmtByte); + HANDLE_FMT(DevFmtUByte); + HANDLE_FMT(DevFmtShort); + HANDLE_FMT(DevFmtUShort); + HANDLE_FMT(DevFmtInt); + HANDLE_FMT(DevFmtUInt); + HANDLE_FMT(DevFmtFloat); +#undef HANDLE_FMT + } + } + else if(mChanMask == 0x1 && mDstChans == DevFmtStereo) + { + switch(mSrcType) + { +#define HANDLE_FMT(T) case T: Mono2Stereo(dst, src, frames); break + HANDLE_FMT(DevFmtByte); + HANDLE_FMT(DevFmtUByte); + HANDLE_FMT(DevFmtShort); + HANDLE_FMT(DevFmtUShort); + HANDLE_FMT(DevFmtInt); + HANDLE_FMT(DevFmtUInt); + HANDLE_FMT(DevFmtFloat); +#undef HANDLE_FMT + } + } +} diff --git a/core/converter.h b/core/converter.h new file mode 100644 index 00000000..2d22ae38 --- /dev/null +++ b/core/converter.h @@ -0,0 +1,59 @@ +#ifndef CORE_CONVERTER_H +#define CORE_CONVERTER_H + +#include +#include + +#include "almalloc.h" +#include "devformat.h" +#include "mixer/defs.h" + +using uint = unsigned int; + + +struct SampleConverter { + DevFmtType mSrcType{}; + DevFmtType mDstType{}; + uint mSrcTypeSize{}; + uint mDstTypeSize{}; + + int mSrcPrepCount{}; + + uint mFracOffset{}; + uint mIncrement{}; + InterpState mState{}; + ResamplerFunc mResample{}; + + alignas(16) float mSrcSamples[BufferLineSize]{}; + alignas(16) float mDstSamples[BufferLineSize]{}; + + struct ChanSamples { + alignas(16) float PrevSamples[MaxResamplerPadding]; + }; + al::FlexArray mChan; + + SampleConverter(size_t numchans) : mChan{numchans} { } + + uint convert(const void **src, uint *srcframes, void *dst, uint dstframes); + uint availableOut(uint srcframes) const; + + DEF_FAM_NEWDEL(SampleConverter, mChan) +}; +using SampleConverterPtr = std::unique_ptr; + +SampleConverterPtr CreateSampleConverter(DevFmtType srcType, DevFmtType dstType, size_t numchans, + uint srcRate, uint dstRate, Resampler resampler); + + +struct ChannelConverter { + DevFmtType mSrcType{}; + uint mSrcStep{}; + uint mChanMask{}; + DevFmtChannels mDstChans{}; + + bool is_active() const noexcept { return mChanMask != 0; } + + void convert(const void *src, float *dst, uint frames) const; +}; + +#endif /* CORE_CONVERTER_H */ diff --git a/core/uiddefs.cpp b/core/uiddefs.cpp new file mode 100644 index 00000000..244c01a5 --- /dev/null +++ b/core/uiddefs.cpp @@ -0,0 +1,37 @@ + +#include "config.h" + + +#ifndef AL_NO_UID_DEFS + +#if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H) +#define INITGUID +#include +#ifdef HAVE_GUIDDEF_H +#include +#else +#include +#endif + +DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71); +DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71); + +DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x00,0xa0,0xc9,0x25,0xcd,0x16); + +DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e); +DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6); +DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2); +DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2); +DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17); + +#ifdef HAVE_WASAPI +#include +#include +#include +DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14); +DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0); +DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 ); +#endif +#endif + +#endif /* AL_NO_UID_DEFS */ -- cgit v1.2.3 From 6d5dfbd09b6657c15ccc93f81ac556041bbd8d5f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 24 Apr 2021 10:46:32 -0700 Subject: Move the DeviceBase declaraction to core --- CMakeLists.txt | 2 + alc/alc.cpp | 16 ++-- alc/alcmain.h | 241 +------------------------------------------------ core/device.cpp | 7 ++ core/device.h | 274 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 289 insertions(+), 251 deletions(-) create mode 100644 core/device.cpp create mode 100644 core/device.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 13d7ee54..b323fa12 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -657,6 +657,8 @@ set(CORE_OBJS core/cpu_caps.h core/devformat.cpp core/devformat.h + core/device.cpp + core/device.h core/except.cpp core/except.h core/filters/biquad.h diff --git a/alc/alc.cpp b/alc/alc.cpp index bbb9a45c..87c8bbf4 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -980,12 +980,6 @@ constexpr int alcEFXMajorVersion{1}; constexpr int alcEFXMinorVersion{0}; -/* To avoid extraneous allocations, a 0-sized FlexArray is - * defined globally as a sharable object. - */ -al::FlexArray EmptyContextArray{0u}; - - using DeviceRef = al::intrusive_ptr; @@ -2301,14 +2295,14 @@ static bool ResetDeviceParams(ALCdevice *device, const int *attrList) } -DeviceBase::DeviceBase(DeviceType type) : Type{type}, mContexts{&EmptyContextArray} +DeviceBase::DeviceBase(DeviceType type) : Type{type}, mContexts{&sEmptyContextArray} { } DeviceBase::~DeviceBase() { auto *oldarray = mContexts.exchange(nullptr, std::memory_order_relaxed); - if(oldarray != &EmptyContextArray) delete oldarray; + if(oldarray != &sEmptyContextArray) delete oldarray; } ALCdevice::~ALCdevice() @@ -2547,7 +2541,7 @@ bool ALCcontext::deinit() using ContextArray = al::FlexArray; auto alloc_ctx_array = [](const size_t count) -> ContextArray* { - if(count == 0) return &EmptyContextArray; + if(count == 0) return &DeviceBase::sEmptyContextArray; return ContextArray::Create(count).release(); }; auto *newarray = alloc_ctx_array(oldarray->size() - toremove); @@ -2562,7 +2556,7 @@ bool ALCcontext::deinit() * to finish before deleting the old array. */ mDevice->mContexts.store(newarray); - if(oldarray != &EmptyContextArray) + if(oldarray != &DeviceBase::sEmptyContextArray) { mDevice->waitForMix(); delete oldarray; @@ -3393,7 +3387,7 @@ START_API_FUNC * to finish before deleting the old array. */ dev->mContexts.store(newarray.release()); - if(oldarray != &EmptyContextArray) + if(oldarray != &DeviceBase::sEmptyContextArray) { dev->waitForMix(); delete oldarray; diff --git a/alc/alcmain.h b/alc/alcmain.h index 025ba3d4..e8875184 100644 --- a/alc/alcmain.h +++ b/alc/alcmain.h @@ -25,6 +25,7 @@ #include "core/ambidefs.h" #include "core/bufferline.h" #include "core/devformat.h" +#include "core/device.h" #include "core/filters/splitter.h" #include "core/hrtf.h" #include "core/mixer/defs.h" @@ -32,253 +33,13 @@ #include "intrusive_ptr.h" #include "vector.h" -class BFormatDec; struct ALbuffer; struct ALeffect; struct ALfilter; -struct BackendBase; -struct Compressor; -struct ContextBase; -struct EffectState; -struct UhjEncoder; -struct bs2b; using uint = unsigned int; -#define MIN_OUTPUT_RATE 8000 -#define MAX_OUTPUT_RATE 192000 -#define DEFAULT_OUTPUT_RATE 44100 - -#define DEFAULT_UPDATE_SIZE 882 /* 20ms */ -#define DEFAULT_NUM_UPDATES 3 - - -enum class DeviceType : unsigned char { - Playback, - Capture, - Loopback -}; - - -enum class RenderMode : unsigned char { - Normal, - Pairwise, - Hrtf -}; - - -struct InputRemixMap { - struct TargetMix { Channel channel; float mix; }; - - Channel channel; - std::array targets; -}; - - -/* Maximum delay in samples for speaker distance compensation. */ -#define MAX_DELAY_LENGTH 1024 - -struct DistanceComp { - struct ChanData { - float Gain{1.0f}; - uint Length{0u}; /* Valid range is [0...MAX_DELAY_LENGTH). */ - float *Buffer{nullptr}; - }; - - std::array mChannels; - al::FlexArray mSamples; - - DistanceComp(size_t count) : mSamples{count} { } - - static std::unique_ptr Create(size_t numsamples) - { return std::unique_ptr{new(FamCount(numsamples)) DistanceComp{numsamples}}; } - - DEF_FAM_NEWDEL(DistanceComp, mSamples) -}; - - -struct BFChannelConfig { - float Scale; - uint Index; -}; - - -struct MixParams { - /* Coefficient channel mapping for mixing to the buffer. */ - std::array AmbiMap{}; - - al::span Buffer; -}; - -struct RealMixParams { - al::span RemixMap; - std::array ChannelIndex{}; - - al::span Buffer; -}; - -enum { - // Frequency was requested by the app or config file - FrequencyRequest, - // Channel configuration was requested by the config file - ChannelsRequest, - // Sample type was requested by the config file - SampleTypeRequest, - - // Specifies if the DSP is paused at user request - DevicePaused, - // Specifies if the device is currently running - DeviceRunning, - - DeviceFlagsCount -}; - -struct DeviceBase { - std::atomic Connected{true}; - const DeviceType Type{}; - - uint Frequency{}; - uint UpdateSize{}; - uint BufferSize{}; - - DevFmtChannels FmtChans{}; - DevFmtType FmtType{}; - bool IsHeadphones{false}; - uint mAmbiOrder{0}; - float mXOverFreq{400.0f}; - /* For DevFmtAmbi* output only, specifies the channel order and - * normalization. - */ - DevAmbiLayout mAmbiLayout{DevAmbiLayout::Default}; - DevAmbiScaling mAmbiScale{DevAmbiScaling::Default}; - - std::string DeviceName; - - // Device flags - std::bitset Flags{}; - - uint NumAuxSends{}; - - /* Rendering mode. */ - RenderMode mRenderMode{RenderMode::Normal}; - - /* The average speaker distance as determined by the ambdec configuration, - * HRTF data set, or the NFC-HOA reference delay. Only used for NFC. - */ - float AvgSpeakerDist{0.0f}; - - uint SamplesDone{0u}; - std::chrono::nanoseconds ClockBase{0}; - std::chrono::nanoseconds FixedLatency{0}; - - /* Temp storage used for mixer processing. */ - alignas(16) float ResampledData[BufferLineSize]; - alignas(16) float FilteredData[BufferLineSize]; - union { - alignas(16) float HrtfSourceData[BufferLineSize + HrtfHistoryLength]; - alignas(16) float NfcSampleData[BufferLineSize]; - }; - - /* Persistent storage for HRTF mixing. */ - alignas(16) float2 HrtfAccumData[BufferLineSize + HrirLength + HrtfDirectDelay]; - - /* Mixing buffer used by the Dry mix and Real output. */ - al::vector MixBuffer; - - /* The "dry" path corresponds to the main output. */ - MixParams Dry; - uint NumChannelsPerOrder[MaxAmbiOrder+1]{}; - - /* "Real" output, which will be written to the device buffer. May alias the - * dry buffer. - */ - RealMixParams RealOut; - - /* HRTF state and info */ - std::unique_ptr mHrtfState; - al::intrusive_ptr mHrtf; - uint mIrSize{0}; - - /* Ambisonic-to-UHJ encoder */ - std::unique_ptr mUhjEncoder; - - /* Ambisonic decoder for speakers */ - std::unique_ptr AmbiDecoder; - - /* Stereo-to-binaural filter */ - std::unique_ptr Bs2b; - - using PostProc = void(DeviceBase::*)(const size_t SamplesToDo); - PostProc PostProcess{nullptr}; - - std::unique_ptr Limiter; - - /* Delay buffers used to compensate for speaker distances. */ - std::unique_ptr ChannelDelays; - - /* Dithering control. */ - float DitherDepth{0.0f}; - uint DitherSeed{0u}; - - /* Running count of the mixer invocations, in 31.1 fixed point. This - * actually increments *twice* when mixing, first at the start and then at - * the end, so the bottom bit indicates if the device is currently mixing - * and the upper bits indicates how many mixes have been done. - */ - RefCount MixCount{0u}; - - // Contexts created on this device - std::atomic*> mContexts{nullptr}; - - /* This lock protects the device state (format, update size, etc) from - * being from being changed in multiple threads, or being accessed while - * being changed. It's also used to serialize calls to the backend. - */ - std::mutex StateLock; - std::unique_ptr Backend; - - - DeviceBase(DeviceType type); - DeviceBase(const DeviceBase&) = delete; - DeviceBase& operator=(const DeviceBase&) = delete; - ~DeviceBase(); - - uint bytesFromFmt() const noexcept { return BytesFromDevFmt(FmtType); } - uint channelsFromFmt() const noexcept { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); } - uint frameSizeFromFmt() const noexcept { return bytesFromFmt() * channelsFromFmt(); } - - uint waitForMix() const noexcept - { - uint refcount; - while((refcount=MixCount.load(std::memory_order_acquire))&1) { - } - return refcount; - } - - void ProcessHrtf(const size_t SamplesToDo); - void ProcessAmbiDec(const size_t SamplesToDo); - void ProcessAmbiDecStablized(const size_t SamplesToDo); - void ProcessUhj(const size_t SamplesToDo); - void ProcessBs2b(const size_t SamplesToDo); - - inline void postProcess(const size_t SamplesToDo) - { if LIKELY(PostProcess) (this->*PostProcess)(SamplesToDo); } - - void renderSamples(void *outBuffer, const uint numSamples, const size_t frameStep); - - /* Caller must lock the device state, and the mixer must not be running. */ -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf,2,3)]] -#else - [[gnu::format(printf,2,3)]] -#endif - void handleDisconnect(const char *msg, ...); - - DISABLE_ALLOC() -}; - - struct BufferSubList { uint64_t FreeMask{~0_u64}; ALbuffer *Buffers{nullptr}; /* 64 */ diff --git a/core/device.cpp b/core/device.cpp new file mode 100644 index 00000000..9705c0ac --- /dev/null +++ b/core/device.cpp @@ -0,0 +1,7 @@ + +#include "config.h" + +#include "device.h" + + +al::FlexArray DeviceBase::sEmptyContextArray{0u}; diff --git a/core/device.h b/core/device.h new file mode 100644 index 00000000..bc6bae52 --- /dev/null +++ b/core/device.h @@ -0,0 +1,274 @@ +#ifndef CORE_DEVICE_H +#define CORE_DEVICE_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "almalloc.h" +#include "alspan.h" +#include "ambidefs.h" +#include "atomic.h" +#include "core/bufferline.h" +#include "devformat.h" +#include "intrusive_ptr.h" +#include "mixer/hrtfdefs.h" +#include "opthelpers.h" +#include "vector.h" + +struct BackendBase; +class BFormatDec; +struct bs2b; +struct Compressor; +struct ContextBase; +struct DirectHrtfState; +struct HrtfStore; +struct UhjEncoder; + +using uint = unsigned int; + + +#define MIN_OUTPUT_RATE 8000 +#define MAX_OUTPUT_RATE 192000 +#define DEFAULT_OUTPUT_RATE 44100 + +#define DEFAULT_UPDATE_SIZE 882 /* 20ms */ +#define DEFAULT_NUM_UPDATES 3 + + +enum class DeviceType : unsigned char { + Playback, + Capture, + Loopback +}; + + +enum class RenderMode : unsigned char { + Normal, + Pairwise, + Hrtf +}; + + +struct InputRemixMap { + struct TargetMix { Channel channel; float mix; }; + + Channel channel; + std::array targets; +}; + + +/* Maximum delay in samples for speaker distance compensation. */ +#define MAX_DELAY_LENGTH 1024 + +struct DistanceComp { + struct ChanData { + float Gain{1.0f}; + uint Length{0u}; /* Valid range is [0...MAX_DELAY_LENGTH). */ + float *Buffer{nullptr}; + }; + + std::array mChannels; + al::FlexArray mSamples; + + DistanceComp(size_t count) : mSamples{count} { } + + static std::unique_ptr Create(size_t numsamples) + { return std::unique_ptr{new(FamCount(numsamples)) DistanceComp{numsamples}}; } + + DEF_FAM_NEWDEL(DistanceComp, mSamples) +}; + + +struct BFChannelConfig { + float Scale; + uint Index; +}; + + +struct MixParams { + /* Coefficient channel mapping for mixing to the buffer. */ + std::array AmbiMap{}; + + al::span Buffer; +}; + +struct RealMixParams { + al::span RemixMap; + std::array ChannelIndex{}; + + al::span Buffer; +}; + +enum { + // Frequency was requested by the app or config file + FrequencyRequest, + // Channel configuration was requested by the config file + ChannelsRequest, + // Sample type was requested by the config file + SampleTypeRequest, + + // Specifies if the DSP is paused at user request + DevicePaused, + // Specifies if the device is currently running + DeviceRunning, + + DeviceFlagsCount +}; + +struct DeviceBase { + /* To avoid extraneous allocations, a 0-sized FlexArray is + * defined globally as a sharable object. + */ + static al::FlexArray sEmptyContextArray; + + std::atomic Connected{true}; + const DeviceType Type{}; + + uint Frequency{}; + uint UpdateSize{}; + uint BufferSize{}; + + DevFmtChannels FmtChans{}; + DevFmtType FmtType{}; + bool IsHeadphones{false}; + uint mAmbiOrder{0}; + float mXOverFreq{400.0f}; + /* For DevFmtAmbi* output only, specifies the channel order and + * normalization. + */ + DevAmbiLayout mAmbiLayout{DevAmbiLayout::Default}; + DevAmbiScaling mAmbiScale{DevAmbiScaling::Default}; + + std::string DeviceName; + + // Device flags + std::bitset Flags{}; + + uint NumAuxSends{}; + + /* Rendering mode. */ + RenderMode mRenderMode{RenderMode::Normal}; + + /* The average speaker distance as determined by the ambdec configuration, + * HRTF data set, or the NFC-HOA reference delay. Only used for NFC. + */ + float AvgSpeakerDist{0.0f}; + + uint SamplesDone{0u}; + std::chrono::nanoseconds ClockBase{0}; + std::chrono::nanoseconds FixedLatency{0}; + + /* Temp storage used for mixer processing. */ + alignas(16) float ResampledData[BufferLineSize]; + alignas(16) float FilteredData[BufferLineSize]; + union { + alignas(16) float HrtfSourceData[BufferLineSize + HrtfHistoryLength]; + alignas(16) float NfcSampleData[BufferLineSize]; + }; + + /* Persistent storage for HRTF mixing. */ + alignas(16) float2 HrtfAccumData[BufferLineSize + HrirLength + HrtfDirectDelay]; + + /* Mixing buffer used by the Dry mix and Real output. */ + al::vector MixBuffer; + + /* The "dry" path corresponds to the main output. */ + MixParams Dry; + uint NumChannelsPerOrder[MaxAmbiOrder+1]{}; + + /* "Real" output, which will be written to the device buffer. May alias the + * dry buffer. + */ + RealMixParams RealOut; + + /* HRTF state and info */ + std::unique_ptr mHrtfState; + al::intrusive_ptr mHrtf; + uint mIrSize{0}; + + /* Ambisonic-to-UHJ encoder */ + std::unique_ptr mUhjEncoder; + + /* Ambisonic decoder for speakers */ + std::unique_ptr AmbiDecoder; + + /* Stereo-to-binaural filter */ + std::unique_ptr Bs2b; + + using PostProc = void(DeviceBase::*)(const size_t SamplesToDo); + PostProc PostProcess{nullptr}; + + std::unique_ptr Limiter; + + /* Delay buffers used to compensate for speaker distances. */ + std::unique_ptr ChannelDelays; + + /* Dithering control. */ + float DitherDepth{0.0f}; + uint DitherSeed{0u}; + + /* Running count of the mixer invocations, in 31.1 fixed point. This + * actually increments *twice* when mixing, first at the start and then at + * the end, so the bottom bit indicates if the device is currently mixing + * and the upper bits indicates how many mixes have been done. + */ + RefCount MixCount{0u}; + + // Contexts created on this device + std::atomic*> mContexts{nullptr}; + + /* This lock protects the device state (format, update size, etc) from + * being from being changed in multiple threads, or being accessed while + * being changed. It's also used to serialize calls to the backend. + */ + std::mutex StateLock; + std::unique_ptr Backend; + + + DeviceBase(DeviceType type); + DeviceBase(const DeviceBase&) = delete; + DeviceBase& operator=(const DeviceBase&) = delete; + ~DeviceBase(); + + uint bytesFromFmt() const noexcept { return BytesFromDevFmt(FmtType); } + uint channelsFromFmt() const noexcept { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); } + uint frameSizeFromFmt() const noexcept { return bytesFromFmt() * channelsFromFmt(); } + + uint waitForMix() const noexcept + { + uint refcount; + while((refcount=MixCount.load(std::memory_order_acquire))&1) { + } + return refcount; + } + + void ProcessHrtf(const size_t SamplesToDo); + void ProcessAmbiDec(const size_t SamplesToDo); + void ProcessAmbiDecStablized(const size_t SamplesToDo); + void ProcessUhj(const size_t SamplesToDo); + void ProcessBs2b(const size_t SamplesToDo); + + inline void postProcess(const size_t SamplesToDo) + { if LIKELY(PostProcess) (this->*PostProcess)(SamplesToDo); } + + void renderSamples(void *outBuffer, const uint numSamples, const size_t frameStep); + + /* Caller must lock the device state, and the mixer must not be running. */ +#ifdef __USE_MINGW_ANSI_STDIO + [[gnu::format(gnu_printf,2,3)]] +#else + [[gnu::format(printf,2,3)]] +#endif + void handleDisconnect(const char *msg, ...); + + DISABLE_ALLOC() +}; + +#endif /* CORE_DEVICE_H */ -- cgit v1.2.3 From 440ce71dcedb39e827660d0925b6cc2046c6d0f0 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 24 Apr 2021 13:46:52 -0700 Subject: Move GetHFOrderScales to a more appropriate place --- CMakeLists.txt | 1 + alc/bformatdec.cpp | 33 --------------------------------- alc/bformatdec.h | 4 ---- alc/effects/convolution.cpp | 3 +-- alc/effects/reverb.cpp | 4 ++-- alc/voice.cpp | 3 +-- core/ambidefs.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ core/ambidefs.h | 4 ++++ 8 files changed, 53 insertions(+), 43 deletions(-) create mode 100644 core/ambidefs.cpp (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index b323fa12..b3bafbb0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -644,6 +644,7 @@ set(COMMON_OBJS set(CORE_OBJS core/ambdec.cpp core/ambdec.h + core/ambidefs.cpp core/ambidefs.h core/bs2b.cpp core/bs2b.h diff --git a/alc/bformatdec.cpp b/alc/bformatdec.cpp index 9b2d9049..36b8d50e 100644 --- a/alc/bformatdec.cpp +++ b/alc/bformatdec.cpp @@ -21,23 +21,6 @@ namespace { -constexpr std::array Ambi3DDecoderHFScale{{ - 1.00000000e+00f, 1.00000000e+00f -}}; -constexpr std::array Ambi3DDecoderHFScale2O{{ - 7.45355990e-01f, 1.00000000e+00f, 1.00000000e+00f -}}; -constexpr std::array Ambi3DDecoderHFScale3O{{ - 5.89792205e-01f, 8.79693856e-01f, 1.00000000e+00f, 1.00000000e+00f -}}; - -inline auto& GetDecoderHFScales(uint order) noexcept -{ - if(order >= 3) return Ambi3DDecoderHFScale3O; - if(order == 2) return Ambi3DDecoderHFScale2O; - return Ambi3DDecoderHFScale; -} - inline auto& GetAmbiScales(AmbDecScale scaletype) noexcept { if(scaletype == AmbDecScale::FuMa) return AmbiScale::FromFuMa(); @@ -266,22 +249,6 @@ void BFormatDec::processStablize(const al::span OutBuffer, } -auto BFormatDec::GetHFOrderScales(const uint in_order, const uint out_order) noexcept - -> std::array -{ - std::array ret{}; - - assert(out_order >= in_order); - - const auto &target = GetDecoderHFScales(out_order); - const auto &input = GetDecoderHFScales(in_order); - - for(size_t i{0};i < in_order+1;++i) - ret[i] = input[i] / target[i]; - - return ret; -} - std::unique_ptr BFormatDec::Create(const AmbDecConf *conf, const bool allow_2band, const size_t inchans, const uint srate, const uint (&chanmap)[MAX_OUTPUT_CHANNELS], std::unique_ptr stablizer) diff --git a/alc/bformatdec.h b/alc/bformatdec.h index 7715d364..bb39f709 100644 --- a/alc/bformatdec.h +++ b/alc/bformatdec.h @@ -58,10 +58,6 @@ public: const FloatBufferLine *InSamples, const size_t lidx, const size_t ridx, const size_t cidx, const size_t SamplesToDo); - /* Retrieves per-order HF scaling factors for "upsampling" ambisonic data. */ - static std::array GetHFOrderScales(const uint in_order, - const uint out_order) noexcept; - static std::unique_ptr Create(const AmbDecConf *conf, const bool allow_2band, const size_t inchans, const uint srate, const uint (&chanmap)[MAX_OUTPUT_CHANNELS], std::unique_ptr stablizer); diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index 0a1bd214..072bc034 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -14,7 +14,6 @@ #include "alcontext.h" #include "almalloc.h" #include "alspan.h" -#include "bformatdec.h" #include "buffer_storage.h" #include "core/ambidefs.h" #include "core/filters/splitter.h" @@ -398,7 +397,7 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot if(device->mAmbiOrder > mAmbiOrder) { mMix = &ConvolutionState::UpsampleMix; - const auto scales = BFormatDec::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder); + const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder); (*mChans)[0].mHfScale = scales[0]; for(size_t i{1};i < mChans->size();++i) (*mChans)[i].mHfScale = scales[1]; diff --git a/alc/effects/reverb.cpp b/alc/effects/reverb.cpp index 1260748c..2af7472d 100644 --- a/alc/effects/reverb.cpp +++ b/alc/effects/reverb.cpp @@ -32,7 +32,7 @@ #include "alcmain.h" #include "alcontext.h" #include "alnumeric.h" -#include "bformatdec.h" +#include "core/ambidefs.h" #include "core/filters/biquad.h" #include "effectslot.h" #include "vector.h" @@ -665,7 +665,7 @@ void ReverbState::deviceUpdate(const DeviceBase *device, const Buffer&) if(device->mAmbiOrder > 1) { mMixOut = &ReverbState::MixOutAmbiUp; - mOrderScales = BFormatDec::GetHFOrderScales(1, device->mAmbiOrder); + mOrderScales = AmbiScale::GetHFOrderScales(1, device->mAmbiOrder); } else { diff --git a/alc/voice.cpp b/alc/voice.cpp index 206444d9..79c47588 100644 --- a/alc/voice.cpp +++ b/alc/voice.cpp @@ -44,7 +44,6 @@ #include "alstring.h" #include "alu.h" #include "async_event.h" -#include "bformatdec.h" #include "buffer_storage.h" #include "core/ambidefs.h" #include "core/cpu_caps.h" @@ -840,7 +839,7 @@ void Voice::prepare(DeviceBase *device) { const uint8_t *OrderFromChan{(mFmtChannels == FmtBFormat2D) ? AmbiIndex::OrderFrom2DChannel().data() : AmbiIndex::OrderFromChannel().data()}; - const auto scales = BFormatDec::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder); + const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder); const BandSplitter splitter{device->mXOverFreq / static_cast(device->Frequency)}; for(auto &chandata : mChans) diff --git a/core/ambidefs.cpp b/core/ambidefs.cpp new file mode 100644 index 00000000..2725748e --- /dev/null +++ b/core/ambidefs.cpp @@ -0,0 +1,44 @@ + +#include "config.h" + +#include "ambidefs.h" + +#include + + +namespace { + +constexpr std::array Ambi3DDecoderHFScale{{ + 1.00000000e+00f, 1.00000000e+00f +}}; +constexpr std::array Ambi3DDecoderHFScale2O{{ + 7.45355990e-01f, 1.00000000e+00f, 1.00000000e+00f +}}; +constexpr std::array Ambi3DDecoderHFScale3O{{ + 5.89792205e-01f, 8.79693856e-01f, 1.00000000e+00f, 1.00000000e+00f +}}; + +inline auto& GetDecoderHFScales(uint order) noexcept +{ + if(order >= 3) return Ambi3DDecoderHFScale3O; + if(order == 2) return Ambi3DDecoderHFScale2O; + return Ambi3DDecoderHFScale; +} + +} // namespace + +auto AmbiScale::GetHFOrderScales(const uint in_order, const uint out_order) noexcept + -> std::array +{ + std::array ret{}; + + assert(out_order >= in_order); + + const auto &target = GetDecoderHFScales(out_order); + const auto &input = GetDecoderHFScales(in_order); + + for(size_t i{0};i < in_order+1;++i) + ret[i] = input[i] / target[i]; + + return ret; +} diff --git a/core/ambidefs.h b/core/ambidefs.h index a72f7b78..22739359 100644 --- a/core/ambidefs.h +++ b/core/ambidefs.h @@ -97,6 +97,10 @@ struct AmbiScale { }}; return ret; } + + /* Retrieves per-order HF scaling factors for "upsampling" ambisonic data. */ + static std::array GetHFOrderScales(const uint in_order, + const uint out_order) noexcept; }; struct AmbiIndex { -- cgit v1.2.3 From 0fe38c053d8dd827e774fbe0aef121e7aa0a0f28 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 25 Apr 2021 11:36:37 -0700 Subject: Move some functions to core And clean up more includes --- CMakeLists.txt | 2 + al/effects/chorus.cpp | 3 ++ alc/alu.cpp | 1 + alc/alu.h | 85 ------------------------------ alc/bformatdec.cpp | 6 +-- alc/effects/autowah.cpp | 22 +++++--- alc/effects/base.h | 6 +++ alc/effects/chorus.cpp | 18 ++++--- alc/effects/compressor.cpp | 22 ++++++-- alc/effects/convolution.cpp | 18 ++++++- alc/effects/dedicated.cpp | 19 +++++-- alc/effects/distortion.cpp | 15 +++++- alc/effects/echo.cpp | 22 ++++++-- alc/effects/equalizer.cpp | 18 +++++-- alc/effects/fshifter.cpp | 20 +++++-- alc/effects/modulator.cpp | 22 +++++--- alc/effects/pshifter.cpp | 22 +++++--- alc/effects/reverb.cpp | 83 +++++++++++++++++------------ alc/effects/vmorpher.cpp | 19 +++++-- alc/panning.cpp | 110 -------------------------------------- alc/voice.cpp | 3 +- core/mixer.cpp | 126 ++++++++++++++++++++++++++++++++++++++++++++ core/mixer.h | 101 +++++++++++++++++++++++++++++++++++ 23 files changed, 474 insertions(+), 289 deletions(-) create mode 100644 core/mixer.cpp create mode 100644 core/mixer.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index b3bafbb0..ba04aa28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -680,6 +680,8 @@ set(CORE_OBJS core/logging.h core/mastering.cpp core/mastering.h + core/mixer.cpp + core/mixer.h core/resampler_limits.h core/uhjfilter.cpp core/uhjfilter.h diff --git a/al/effects/chorus.cpp b/al/effects/chorus.cpp index b2395283..60152755 100644 --- a/al/effects/chorus.cpp +++ b/al/effects/chorus.cpp @@ -12,6 +12,9 @@ namespace { +static_assert(ChorusMaxDelay >= AL_CHORUS_MAX_DELAY, "Chorus max delay too small"); +static_assert(FlangerMaxDelay >= AL_FLANGER_MAX_DELAY, "Flanger max delay too small"); + 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"); diff --git a/alc/alu.cpp b/alc/alu.cpp index d5103852..7221ce27 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -64,6 +64,7 @@ #include "core/fpu_ctrl.h" #include "core/hrtf.h" #include "core/mastering.h" +#include "core/mixer.h" #include "core/mixer/defs.h" #include "core/uhjfilter.h" #include "effects/base.h" diff --git a/alc/alu.h b/alc/alu.h index b75c040e..ac33de27 100644 --- a/alc/alu.h +++ b/alc/alu.h @@ -22,13 +22,6 @@ struct MixParams; #define MAX_SENDS 6 -using MixerFunc = void(*)(const al::span InSamples, - const al::span OutBuffer, float *CurrentGains, const float *TargetGains, - const size_t Counter, const size_t OutPos); - -extern MixerFunc MixSamples; - - constexpr float GainMixMax{1000.0f}; /* +60dB */ constexpr float SpeedOfSoundMetersPerSec{343.3f}; @@ -55,84 +48,6 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, HrtfRequestMode hrtf_appreq void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context); -/** - * 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. - */ -std::array CalcAmbiCoeffs(const float y, const float z, const float x, - const float spread); - -/** - * CalcDirectionCoeffs - * - * 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). - */ -inline std::array CalcDirectionCoeffs(const float (&dir)[3], - const float spread) -{ - /* Convert from OpenAL coords to Ambisonics. */ - return CalcAmbiCoeffs(-dir[0], dir[1], -dir[2], spread); -} - -/** - * CalcAngleCoeffs - * - * Calculates ambisonic coefficients based on azimuth and elevation. The - * azimuth and elevation parameters are in radians, going right and up - * respectively. - */ -inline std::array CalcAngleCoeffs(const float azimuth, - const float elevation, const float spread) -{ - const float x{-std::sin(azimuth) * std::cos(elevation)}; - const float y{ std::sin(elevation)}; - const float z{ std::cos(azimuth) * std::cos(elevation)}; - - return CalcAmbiCoeffs(x, y, z, spread); -} - - -/** - * ComputePanGains - * - * Computes panning gains using the given channel decoder coefficients and the - * 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. - */ -void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain, - const al::span gains); - - -/** Helper to set an identity/pass-through panning for ambisonic mixing (3D input). */ -template -auto SetAmbiPanIdentity(T iter, I count, F func) -> std::enable_if_t::value> -{ - if(count < 1) return; - - std::array coeffs{{1.0f}}; - func(*iter, coeffs); - ++iter; - for(I i{1};i < count;++i,++iter) - { - coeffs[i-1] = 0.0f; - coeffs[i ] = 1.0f; - func(*iter, coeffs); - } -} - extern const float ConeScale; extern const float ZScale; diff --git a/alc/bformatdec.cpp b/alc/bformatdec.cpp index 36b8d50e..e29af045 100644 --- a/alc/bformatdec.cpp +++ b/alc/bformatdec.cpp @@ -5,15 +5,13 @@ #include #include -#include #include -#include -#include +#include #include "almalloc.h" -#include "alu.h" #include "core/ambdec.h" #include "core/filters/splitter.h" +#include "core/mixer.h" #include "front_stablizer.h" #include "math_defs.h" #include "opthelpers.h" diff --git a/alc/effects/autowah.cpp b/alc/effects/autowah.cpp index a21d2a01..3df19eb2 100644 --- a/alc/effects/autowah.cpp +++ b/alc/effects/autowah.cpp @@ -20,16 +20,26 @@ #include "config.h" -#include -#include - #include +#include +#include +#include +#include -#include "alcmain.h" #include "alcontext.h" -#include "core/filters/biquad.h" +#include "almalloc.h" +#include "alnumeric.h" +#include "alspan.h" +#include "core/ambidefs.h" +#include "core/bufferline.h" +#include "core/devformat.h" +#include "core/device.h" +#include "core/mixer.h" +#include "effects/base.h" #include "effectslot.h" -#include "vecmat.h" +#include "intrusive_ptr.h" +#include "math_defs.h" + namespace { diff --git a/alc/effects/base.h b/alc/effects/base.h index 09c722f5..6c31ae0c 100644 --- a/alc/effects/base.h +++ b/alc/effects/base.h @@ -18,11 +18,17 @@ struct BufferStorage; /** Target gain for the reverb decay feedback reaching the decay time. */ constexpr float ReverbDecayGain{0.001f}; /* -60 dB */ +constexpr float ReverbMaxReflectionsDelay{0.3f}; +constexpr float ReverbMaxLateReverbDelay{0.1f}; + enum class ChorusWaveform { Sinusoid, Triangle }; +constexpr float ChorusMaxDelay{0.016f}; +constexpr float FlangerMaxDelay{0.004f}; + constexpr float EchoMaxDelay{0.207f}; constexpr float EchoMaxLRDelay{0.404f}; diff --git a/alc/effects/chorus.cpp b/alc/effects/chorus.cpp index 805c57d5..4171cef8 100644 --- a/alc/effects/chorus.cpp +++ b/alc/effects/chorus.cpp @@ -21,20 +21,24 @@ #include "config.h" #include +#include #include -#include #include #include -#include "alcmain.h" #include "alcontext.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" -#include "alu.h" -#include "core/ambidefs.h" +#include "core/bufferline.h" +#include "core/devformat.h" +#include "core/device.h" +#include "core/mixer.h" +#include "core/mixer/defs.h" +#include "core/resampler_limits.h" #include "effects/base.h" #include "effectslot.h" +#include "intrusive_ptr.h" #include "math_defs.h" #include "opthelpers.h" #include "vector.h" @@ -42,6 +46,8 @@ namespace { +using uint = unsigned int; + #define MAX_UPDATE_SAMPLES 256 struct ChorusState final : public EffectState { @@ -79,7 +85,7 @@ struct ChorusState final : public EffectState { void ChorusState::deviceUpdate(const DeviceBase *Device, const Buffer&) { - constexpr float max_delay{maxf(AL_CHORUS_MAX_DELAY, AL_FLANGER_MAX_DELAY)}; + constexpr float max_delay{maxf(ChorusMaxDelay, FlangerMaxDelay)}; const auto frequency = static_cast(Device->Frequency); const size_t maxlen{NextPowerOf2(float2uint(max_delay*2.0f*frequency) + 1u)}; @@ -247,7 +253,7 @@ void ChorusState::process(const size_t samplesToDo, const al::span #include - -#include "alcmain.h" -#include "alcontext.h" -#include "alu.h" +#include +#include + +#include "almalloc.h" +#include "alnumeric.h" +#include "alspan.h" +#include "core/ambidefs.h" +#include "core/bufferline.h" +#include "core/devformat.h" +#include "core/device.h" +#include "core/mixer.h" +#include "core/mixer/defs.h" +#include "effects/base.h" #include "effectslot.h" -#include "vecmat.h" +#include "intrusive_ptr.h" + +struct ContextBase; namespace { diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index 072bc034..efc1c4c7 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -1,7 +1,15 @@ #include "config.h" +#include +#include +#include +#include +#include +#include +#include #include +#include #ifdef HAVE_SSE_INTRINSICS #include @@ -9,20 +17,28 @@ #include #endif +#include "albyte.h" #include "alcmain.h" #include "alcomplex.h" #include "alcontext.h" #include "almalloc.h" +#include "alnumeric.h" #include "alspan.h" #include "buffer_storage.h" +#include "config.h" #include "core/ambidefs.h" +#include "core/bufferline.h" +#include "core/devformat.h" +#include "core/device.h" #include "core/filters/splitter.h" #include "core/fmt_traits.h" -#include "core/logging.h" +#include "core/mixer.h" #include "effects/base.h" #include "effectslot.h" +#include "intrusive_ptr.h" #include "math_defs.h" #include "polyphase_resampler.h" +#include "vector.h" namespace { diff --git a/alc/effects/dedicated.cpp b/alc/effects/dedicated.cpp index f29458d2..78663053 100644 --- a/alc/effects/dedicated.cpp +++ b/alc/effects/dedicated.cpp @@ -20,18 +20,29 @@ #include "config.h" -#include -#include #include +#include +#include +#include #include "alcmain.h" -#include "alcontext.h" -#include "alu.h" +#include "almalloc.h" +#include "alspan.h" +#include "core/bufferline.h" +#include "core/devformat.h" +#include "core/device.h" +#include "core/mixer.h" +#include "effects/base.h" #include "effectslot.h" +#include "intrusive_ptr.h" + +struct ContextBase; namespace { +using uint = unsigned int; + struct DedicatedState final : public EffectState { float mCurrentGains[MAX_OUTPUT_CHANNELS]; float mTargetGains[MAX_OUTPUT_CHANNELS]; diff --git a/alc/effects/distortion.cpp b/alc/effects/distortion.cpp index a9ac8293..d5d0fd32 100644 --- a/alc/effects/distortion.cpp +++ b/alc/effects/distortion.cpp @@ -21,13 +21,24 @@ #include "config.h" #include -#include +#include #include +#include -#include "alcmain.h" #include "alcontext.h" +#include "almalloc.h" +#include "alnumeric.h" +#include "alspan.h" +#include "core/bufferline.h" +#include "core/devformat.h" +#include "core/device.h" #include "core/filters/biquad.h" +#include "core/mixer.h" +#include "core/mixer/defs.h" +#include "effects/base.h" #include "effectslot.h" +#include "intrusive_ptr.h" +#include "math_defs.h" namespace { diff --git a/alc/effects/echo.cpp b/alc/effects/echo.cpp index 38183460..8ee1723c 100644 --- a/alc/effects/echo.cpp +++ b/alc/effects/echo.cpp @@ -20,20 +20,32 @@ #include "config.h" -#include -#include - #include +#include +#include +#include +#include -#include "alcmain.h" #include "alcontext.h" +#include "almalloc.h" +#include "alnumeric.h" +#include "alspan.h" +#include "core/bufferline.h" +#include "core/devformat.h" +#include "core/device.h" #include "core/filters/biquad.h" +#include "core/mixer.h" +#include "effects/base.h" #include "effectslot.h" +#include "intrusive_ptr.h" +#include "opthelpers.h" #include "vector.h" namespace { +using uint = unsigned int; + constexpr float LowpassFreqRef{5000.0f}; struct EchoState final : public EffectState { @@ -148,7 +160,7 @@ void EchoState::process(const size_t samplesToDo, const al::span -#include - #include +#include +#include #include +#include +#include -#include "alcmain.h" #include "alcontext.h" +#include "almalloc.h" +#include "alspan.h" +#include "core/ambidefs.h" +#include "core/bufferline.h" +#include "core/devformat.h" +#include "core/device.h" #include "core/filters/biquad.h" +#include "core/mixer.h" +#include "effects/base.h" #include "effectslot.h" -#include "vecmat.h" +#include "intrusive_ptr.h" namespace { diff --git a/alc/effects/fshifter.cpp b/alc/effects/fshifter.cpp index e378a267..aae4b72c 100644 --- a/alc/effects/fshifter.cpp +++ b/alc/effects/fshifter.cpp @@ -20,22 +20,32 @@ #include "config.h" -#include -#include +#include #include +#include #include -#include +#include +#include -#include "alcmain.h" #include "alcomplex.h" #include "alcontext.h" -#include "alu.h" +#include "almalloc.h" +#include "alnumeric.h" +#include "alspan.h" +#include "core/bufferline.h" +#include "core/devformat.h" +#include "core/device.h" +#include "core/mixer.h" +#include "core/mixer/defs.h" +#include "effects/base.h" #include "effectslot.h" +#include "intrusive_ptr.h" #include "math_defs.h" namespace { +using uint = unsigned int; using complex_d = std::complex; #define HIL_SIZE 1024 diff --git a/alc/effects/modulator.cpp b/alc/effects/modulator.cpp index 380d9809..735163a4 100644 --- a/alc/effects/modulator.cpp +++ b/alc/effects/modulator.cpp @@ -20,21 +20,31 @@ #include "config.h" -#include -#include - -#include #include +#include +#include +#include -#include "alcmain.h" #include "alcontext.h" +#include "almalloc.h" +#include "alnumeric.h" +#include "alspan.h" +#include "core/ambidefs.h" +#include "core/bufferline.h" +#include "core/devformat.h" +#include "core/device.h" #include "core/filters/biquad.h" +#include "core/mixer.h" +#include "effects/base.h" #include "effectslot.h" -#include "vecmat.h" +#include "intrusive_ptr.h" +#include "math_defs.h" namespace { +using uint = unsigned int; + #define MAX_UPDATE_SAMPLES 128 #define WAVEFORM_FRACBITS 24 diff --git a/alc/effects/pshifter.cpp b/alc/effects/pshifter.cpp index 1cf4861f..5bf813e5 100644 --- a/alc/effects/pshifter.cpp +++ b/alc/effects/pshifter.cpp @@ -20,23 +20,33 @@ #include "config.h" -#include -#include +#include #include +#include #include -#include +#include +#include -#include "alcmain.h" #include "alcomplex.h" -#include "alcontext.h" +#include "almalloc.h" #include "alnumeric.h" -#include "alu.h" +#include "alspan.h" +#include "core/bufferline.h" +#include "core/devformat.h" +#include "core/device.h" +#include "core/mixer.h" +#include "core/mixer/defs.h" +#include "effects/base.h" #include "effectslot.h" +#include "intrusive_ptr.h" #include "math_defs.h" +struct ContextBase; + namespace { +using uint = unsigned int; using complex_d = std::complex; #define STFT_SIZE 1024 diff --git a/alc/effects/reverb.cpp b/alc/effects/reverb.cpp index 2af7472d..ccb4f544 100644 --- a/alc/effects/reverb.cpp +++ b/alc/effects/reverb.cpp @@ -20,23 +20,34 @@ #include "config.h" -#include -#include -#include - -#include -#include #include +#include +#include #include +#include +#include +#include -#include "alcmain.h" #include "alcontext.h" +#include "almalloc.h" #include "alnumeric.h" +#include "alspan.h" +#include "alu.h" #include "core/ambidefs.h" +#include "core/bufferline.h" +#include "core/devformat.h" +#include "core/device.h" #include "core/filters/biquad.h" +#include "core/filters/splitter.h" +#include "core/mixer.h" +#include "core/mixer/defs.h" +#include "effects/base.h" #include "effectslot.h" -#include "vector.h" +#include "intrusive_ptr.h" +#include "math_defs.h" +#include "opthelpers.h" #include "vecmat.h" +#include "vector.h" /* This is a user config option for modifying the overall output of the reverb * effect. @@ -45,6 +56,11 @@ float ReverbBoost = 1.0f; namespace { +using uint = unsigned int; + +constexpr float MaxModulationTime{4.0f}; +constexpr float DefaultModulationTime{0.25f}; + #define MOD_FRACBITS 24 #define MOD_FRACONE (1<= AL_EAXREVERB_DEFAULT_MODULATION_TIME) + if(modTime >= DefaultModulationTime) { /* To cancel the effects of a long period modulation on the late * reverberation, the amount of pitch should be varied (decreased) * according to the modulation time. The natural form is varying * inversely, in fact resulting in an invariant. */ - Depth[1] = MODULATION_DEPTH_COEFF / 4.0f * AL_EAXREVERB_DEFAULT_MODULATION_TIME * - modDepth * frequency; + Depth[1] = MODULATION_DEPTH_COEFF / 4.0f * DefaultModulationTime * modDepth * frequency; } else Depth[1] = MODULATION_DEPTH_COEFF / 4.0f * modTime * modDepth * frequency; @@ -835,7 +851,8 @@ void LateReverb::updateLines(const float density_mult, const float diffusion, /* Scaling factor to convert the normalized reference frequencies from * representing 0...freq to 0...max_reference. */ - const float norm_weight_factor{frequency / AL_EAXREVERB_MAX_HFREFERENCE}; + constexpr float MaxHFReference{20000.0f}; + const float norm_weight_factor{frequency / MaxHFReference}; const float late_allpass_avg{ std::accumulate(LATE_ALLPASS_LENGTHS.begin(), LATE_ALLPASS_LENGTHS.end(), 0.0f) / @@ -1024,10 +1041,10 @@ void ReverbState::update(const ContextBase *Context, const EffectSlot *Slot, props->Reverb.DecayTime); /* Calculate the LF/HF decay times. */ - const float lfDecayTime{clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio, - AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME)}; - const float hfDecayTime{clampf(props->Reverb.DecayTime * hfRatio, - AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME)}; + constexpr float MinDecayTime{0.1f}, MaxDecayTime{20.0f}; + const float lfDecayTime{clampf(props->Reverb.DecayTime*props->Reverb.DecayLFRatio, + MinDecayTime, MaxDecayTime)}; + const float hfDecayTime{clampf(props->Reverb.DecayTime*hfRatio, MinDecayTime, MaxDecayTime)}; /* Update the modulator rate and depth. */ mLate.Mod.updateModulator(props->Reverb.ModulationTime, props->Reverb.ModulationDepth, diff --git a/alc/effects/vmorpher.cpp b/alc/effects/vmorpher.cpp index 332df159..f3ed7aad 100644 --- a/alc/effects/vmorpher.cpp +++ b/alc/effects/vmorpher.cpp @@ -20,20 +20,31 @@ #include "config.h" -#include -#include #include +#include +#include #include +#include -#include "alcmain.h" #include "alcontext.h" -#include "alu.h" +#include "almalloc.h" +#include "alnumeric.h" +#include "alspan.h" +#include "core/ambidefs.h" +#include "core/bufferline.h" +#include "core/devformat.h" +#include "core/device.h" +#include "core/mixer.h" +#include "effects/base.h" #include "effectslot.h" +#include "intrusive_ptr.h" #include "math_defs.h" namespace { +using uint = unsigned int; + #define MAX_UPDATE_SAMPLES 256 #define NUM_FORMANTS 4 #define NUM_FILTERS 2 diff --git a/alc/panning.cpp b/alc/panning.cpp index 39f4f4c3..ce6ba29c 100644 --- a/alc/panning.cpp +++ b/alc/panning.cpp @@ -1090,113 +1090,3 @@ void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context) std::fill(iter, slot->Wet.AmbiMap.end(), BFChannelConfig{}); slot->Wet.Buffer = wetbuffer->mBuffer; } - - -std::array CalcAmbiCoeffs(const float y, const float z, const float x, - const float spread) -{ - std::array coeffs; - - /* Zeroth-order */ - coeffs[0] = 1.0f; /* ACN 0 = 1 */ - /* First-order */ - coeffs[1] = 1.732050808f * y; /* ACN 1 = sqrt(3) * Y */ - coeffs[2] = 1.732050808f * z; /* ACN 2 = sqrt(3) * Z */ - coeffs[3] = 1.732050808f * x; /* ACN 3 = sqrt(3) * X */ - /* Second-order */ - const float xx{x*x}, yy{y*y}, zz{z*z}, xy{x*y}, yz{y*z}, xz{x*z}; - coeffs[4] = 3.872983346f * xy; /* ACN 4 = sqrt(15) * X * Y */ - coeffs[5] = 3.872983346f * yz; /* ACN 5 = sqrt(15) * Y * Z */ - coeffs[6] = 1.118033989f * (3.0f*zz - 1.0f); /* ACN 6 = sqrt(5)/2 * (3*Z*Z - 1) */ - coeffs[7] = 3.872983346f * xz; /* ACN 7 = sqrt(15) * X * Z */ - coeffs[8] = 1.936491673f * (xx - yy); /* ACN 8 = sqrt(15)/2 * (X*X - Y*Y) */ - /* Third-order */ - coeffs[9] = 2.091650066f * (y*(3.0f*xx - yy)); /* ACN 9 = sqrt(35/8) * Y * (3*X*X - Y*Y) */ - coeffs[10] = 10.246950766f * (z*xy); /* ACN 10 = sqrt(105) * Z * X * Y */ - coeffs[11] = 1.620185175f * (y*(5.0f*zz - 1.0f)); /* ACN 11 = sqrt(21/8) * Y * (5*Z*Z - 1) */ - coeffs[12] = 1.322875656f * (z*(5.0f*zz - 3.0f)); /* ACN 12 = sqrt(7)/2 * Z * (5*Z*Z - 3) */ - coeffs[13] = 1.620185175f * (x*(5.0f*zz - 1.0f)); /* ACN 13 = sqrt(21/8) * X * (5*Z*Z - 1) */ - coeffs[14] = 5.123475383f * (z*(xx - yy)); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */ - coeffs[15] = 2.091650066f * (x*(xx - 3.0f*yy)); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */ - /* Fourth-order */ - /* ACN 16 = sqrt(35)*3/2 * X * Y * (X*X - Y*Y) */ - /* ACN 17 = sqrt(35/2)*3/2 * (3*X*X - Y*Y) * Y * Z */ - /* ACN 18 = sqrt(5)*3/2 * X * Y * (7*Z*Z - 1) */ - /* ACN 19 = sqrt(5/2)*3/2 * Y * Z * (7*Z*Z - 3) */ - /* ACN 20 = 3/8 * (35*Z*Z*Z*Z - 30*Z*Z + 3) */ - /* ACN 21 = sqrt(5/2)*3/2 * X * Z * (7*Z*Z - 3) */ - /* ACN 22 = sqrt(5)*3/4 * (X*X - Y*Y) * (7*Z*Z - 1) */ - /* ACN 23 = sqrt(35/2)*3/2 * (X*X - 3*Y*Y) * X * Z */ - /* ACN 24 = sqrt(35)*3/8 * (X*X*X*X - 6*X*X*Y*Y + Y*Y*Y*Y) */ - - if(spread > 0.0f) - { - /* Implement the spread by using a spherical source that subtends the - * angle spread. See: - * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3 - * - * When adjusted for N3D normalization instead of SN3D, these - * calculations are: - * - * ZH0 = -sqrt(pi) * (-1+ca); - * ZH1 = 0.5*sqrt(pi) * sa*sa; - * ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1); - * ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1); - * ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3); - * ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1); - * - * The gain of the source is compensated for size, so that the - * loudness doesn't depend on the spread. Thus: - * - * ZH0 = 1.0f; - * ZH1 = 0.5f * (ca+1.0f); - * ZH2 = 0.5f * (ca+1.0f)*ca; - * ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f); - * ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca; - * ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f); - */ - const float ca{std::cos(spread * 0.5f)}; - /* Increase the source volume by up to +3dB for a full spread. */ - const float scale{std::sqrt(1.0f + spread/al::MathDefs::Tau())}; - - const float ZH0_norm{scale}; - const float ZH1_norm{scale * 0.5f * (ca+1.f)}; - const float ZH2_norm{scale * 0.5f * (ca+1.f)*ca}; - const float ZH3_norm{scale * 0.125f * (ca+1.f)*(5.f*ca*ca-1.f)}; - - /* Zeroth-order */ - coeffs[0] *= ZH0_norm; - /* First-order */ - coeffs[1] *= ZH1_norm; - coeffs[2] *= ZH1_norm; - coeffs[3] *= ZH1_norm; - /* Second-order */ - coeffs[4] *= ZH2_norm; - coeffs[5] *= ZH2_norm; - coeffs[6] *= ZH2_norm; - coeffs[7] *= ZH2_norm; - coeffs[8] *= ZH2_norm; - /* Third-order */ - coeffs[9] *= ZH3_norm; - coeffs[10] *= ZH3_norm; - coeffs[11] *= ZH3_norm; - coeffs[12] *= ZH3_norm; - coeffs[13] *= ZH3_norm; - coeffs[14] *= ZH3_norm; - coeffs[15] *= ZH3_norm; - } - - return coeffs; -} - -void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain, - const al::span gains) -{ - auto ambimap = mix->AmbiMap.cbegin(); - - auto iter = std::transform(ambimap, ambimap+mix->Buffer.size(), gains.begin(), - [coeffs,ingain](const BFChannelConfig &chanmap) noexcept -> float - { return chanmap.Scale * coeffs[chanmap.Index] * ingain; } - ); - std::fill(iter, gains.end(), 0.0f); -} diff --git a/alc/voice.cpp b/alc/voice.cpp index 6368d5c4..8782e916 100644 --- a/alc/voice.cpp +++ b/alc/voice.cpp @@ -53,6 +53,7 @@ #include "core/filters/splitter.h" #include "core/fmt_traits.h" #include "core/logging.h" +#include "core/mixer.h" #include "core/mixer/defs.h" #include "core/mixer/hrtfdefs.h" #include "core/resampler_limits.h" @@ -75,8 +76,6 @@ static_assert(!(sizeof(Voice::BufferLine)&15), "Voice::BufferLine must be a mult Resampler ResamplerDefault{Resampler::Linear}; -MixerFunc MixSamples{Mix_}; - namespace { using HrtfMixerFunc = void(*)(const float *InSamples, float2 *AccumSamples, const uint IrSize, diff --git a/core/mixer.cpp b/core/mixer.cpp new file mode 100644 index 00000000..71e48fe3 --- /dev/null +++ b/core/mixer.cpp @@ -0,0 +1,126 @@ + +#include "config.h" + +#include "mixer.h" + +#include + +#include "devformat.h" +#include "device.h" +#include "math_defs.h" +#include "mixer/defs.h" + +struct CTag; + + +MixerFunc MixSamples{Mix_}; + + +std::array CalcAmbiCoeffs(const float y, const float z, const float x, + const float spread) +{ + std::array coeffs; + + /* Zeroth-order */ + coeffs[0] = 1.0f; /* ACN 0 = 1 */ + /* First-order */ + coeffs[1] = 1.732050808f * y; /* ACN 1 = sqrt(3) * Y */ + coeffs[2] = 1.732050808f * z; /* ACN 2 = sqrt(3) * Z */ + coeffs[3] = 1.732050808f * x; /* ACN 3 = sqrt(3) * X */ + /* Second-order */ + const float xx{x*x}, yy{y*y}, zz{z*z}, xy{x*y}, yz{y*z}, xz{x*z}; + coeffs[4] = 3.872983346f * xy; /* ACN 4 = sqrt(15) * X * Y */ + coeffs[5] = 3.872983346f * yz; /* ACN 5 = sqrt(15) * Y * Z */ + coeffs[6] = 1.118033989f * (3.0f*zz - 1.0f); /* ACN 6 = sqrt(5)/2 * (3*Z*Z - 1) */ + coeffs[7] = 3.872983346f * xz; /* ACN 7 = sqrt(15) * X * Z */ + coeffs[8] = 1.936491673f * (xx - yy); /* ACN 8 = sqrt(15)/2 * (X*X - Y*Y) */ + /* Third-order */ + coeffs[9] = 2.091650066f * (y*(3.0f*xx - yy)); /* ACN 9 = sqrt(35/8) * Y * (3*X*X - Y*Y) */ + coeffs[10] = 10.246950766f * (z*xy); /* ACN 10 = sqrt(105) * Z * X * Y */ + coeffs[11] = 1.620185175f * (y*(5.0f*zz - 1.0f)); /* ACN 11 = sqrt(21/8) * Y * (5*Z*Z - 1) */ + coeffs[12] = 1.322875656f * (z*(5.0f*zz - 3.0f)); /* ACN 12 = sqrt(7)/2 * Z * (5*Z*Z - 3) */ + coeffs[13] = 1.620185175f * (x*(5.0f*zz - 1.0f)); /* ACN 13 = sqrt(21/8) * X * (5*Z*Z - 1) */ + coeffs[14] = 5.123475383f * (z*(xx - yy)); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */ + coeffs[15] = 2.091650066f * (x*(xx - 3.0f*yy)); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */ + /* Fourth-order */ + /* ACN 16 = sqrt(35)*3/2 * X * Y * (X*X - Y*Y) */ + /* ACN 17 = sqrt(35/2)*3/2 * (3*X*X - Y*Y) * Y * Z */ + /* ACN 18 = sqrt(5)*3/2 * X * Y * (7*Z*Z - 1) */ + /* ACN 19 = sqrt(5/2)*3/2 * Y * Z * (7*Z*Z - 3) */ + /* ACN 20 = 3/8 * (35*Z*Z*Z*Z - 30*Z*Z + 3) */ + /* ACN 21 = sqrt(5/2)*3/2 * X * Z * (7*Z*Z - 3) */ + /* ACN 22 = sqrt(5)*3/4 * (X*X - Y*Y) * (7*Z*Z - 1) */ + /* ACN 23 = sqrt(35/2)*3/2 * (X*X - 3*Y*Y) * X * Z */ + /* ACN 24 = sqrt(35)*3/8 * (X*X*X*X - 6*X*X*Y*Y + Y*Y*Y*Y) */ + + if(spread > 0.0f) + { + /* Implement the spread by using a spherical source that subtends the + * angle spread. See: + * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3 + * + * When adjusted for N3D normalization instead of SN3D, these + * calculations are: + * + * ZH0 = -sqrt(pi) * (-1+ca); + * ZH1 = 0.5*sqrt(pi) * sa*sa; + * ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1); + * ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1); + * ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3); + * ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1); + * + * The gain of the source is compensated for size, so that the + * loudness doesn't depend on the spread. Thus: + * + * ZH0 = 1.0f; + * ZH1 = 0.5f * (ca+1.0f); + * ZH2 = 0.5f * (ca+1.0f)*ca; + * ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f); + * ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca; + * ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f); + */ + const float ca{std::cos(spread * 0.5f)}; + /* Increase the source volume by up to +3dB for a full spread. */ + const float scale{std::sqrt(1.0f + spread/al::MathDefs::Tau())}; + + const float ZH0_norm{scale}; + const float ZH1_norm{scale * 0.5f * (ca+1.f)}; + const float ZH2_norm{scale * 0.5f * (ca+1.f)*ca}; + const float ZH3_norm{scale * 0.125f * (ca+1.f)*(5.f*ca*ca-1.f)}; + + /* Zeroth-order */ + coeffs[0] *= ZH0_norm; + /* First-order */ + coeffs[1] *= ZH1_norm; + coeffs[2] *= ZH1_norm; + coeffs[3] *= ZH1_norm; + /* Second-order */ + coeffs[4] *= ZH2_norm; + coeffs[5] *= ZH2_norm; + coeffs[6] *= ZH2_norm; + coeffs[7] *= ZH2_norm; + coeffs[8] *= ZH2_norm; + /* Third-order */ + coeffs[9] *= ZH3_norm; + coeffs[10] *= ZH3_norm; + coeffs[11] *= ZH3_norm; + coeffs[12] *= ZH3_norm; + coeffs[13] *= ZH3_norm; + coeffs[14] *= ZH3_norm; + coeffs[15] *= ZH3_norm; + } + + return coeffs; +} + +void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain, + const al::span gains) +{ + auto ambimap = mix->AmbiMap.cbegin(); + + auto iter = std::transform(ambimap, ambimap+mix->Buffer.size(), gains.begin(), + [coeffs,ingain](const BFChannelConfig &chanmap) noexcept -> float + { return chanmap.Scale * coeffs[chanmap.Index] * ingain; } + ); + std::fill(iter, gains.end(), 0.0f); +} diff --git a/core/mixer.h b/core/mixer.h new file mode 100644 index 00000000..309f4224 --- /dev/null +++ b/core/mixer.h @@ -0,0 +1,101 @@ +#ifndef CORE_MIXER_H +#define CORE_MIXER_H + +#include +#include +#include +#include + +#include "alspan.h" +#include "ambidefs.h" +#include "bufferline.h" +#include "devformat.h" + +struct MixParams; + +using MixerFunc = void(*)(const al::span InSamples, + const al::span OutBuffer, float *CurrentGains, const float *TargetGains, + const size_t Counter, const size_t OutPos); + +extern MixerFunc MixSamples; + + +/** + * 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. + */ +std::array CalcAmbiCoeffs(const float y, const float z, const float x, + const float spread); + +/** + * CalcDirectionCoeffs + * + * 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). + */ +inline std::array CalcDirectionCoeffs(const float (&dir)[3], + const float spread) +{ + /* Convert from OpenAL coords to Ambisonics. */ + return CalcAmbiCoeffs(-dir[0], dir[1], -dir[2], spread); +} + +/** + * CalcAngleCoeffs + * + * Calculates ambisonic coefficients based on azimuth and elevation. The + * azimuth and elevation parameters are in radians, going right and up + * respectively. + */ +inline std::array CalcAngleCoeffs(const float azimuth, + const float elevation, const float spread) +{ + const float x{-std::sin(azimuth) * std::cos(elevation)}; + const float y{ std::sin(elevation)}; + const float z{ std::cos(azimuth) * std::cos(elevation)}; + + return CalcAmbiCoeffs(x, y, z, spread); +} + + +/** + * ComputePanGains + * + * Computes panning gains using the given channel decoder coefficients and the + * 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. + */ +void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain, + const al::span gains); + + +/** Helper to set an identity/pass-through panning for ambisonic mixing (3D input). */ +template +auto SetAmbiPanIdentity(T iter, I count, F func) -> std::enable_if_t::value> +{ + if(count < 1) return; + + std::array coeffs{{1.0f}}; + func(*iter, coeffs); + ++iter; + for(I i{1};i < count;++i,++iter) + { + coeffs[i-1] = 0.0f; + coeffs[i ] = 1.0f; + func(*iter, coeffs); + } +} + +#endif /* CORE_MIXER_H */ -- cgit v1.2.3 From 8d09d03ed363ab1735b1933588d8242ba85ddf10 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 25 Apr 2021 14:29:21 -0700 Subject: Move async_event.h to core --- CMakeLists.txt | 2 +- al/event.cpp | 13 +++++-------- alc/alc.cpp | 2 +- alc/alu.cpp | 21 +++++++++++++++++++-- alc/async_event.h | 49 ------------------------------------------------ alc/voice.cpp | 4 ++-- core/async_event.h | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 83 insertions(+), 63 deletions(-) delete mode 100644 alc/async_event.h create mode 100644 core/async_event.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index ba04aa28..58d512e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -646,6 +646,7 @@ set(CORE_OBJS core/ambdec.h core/ambidefs.cpp core/ambidefs.h + core/async_event.h core/bs2b.cpp core/bs2b.h core/bsinc_defs.h @@ -758,7 +759,6 @@ set(ALC_OBJS alc/alconfig.cpp alc/alconfig.h alc/alcontext.h - alc/async_event.h alc/bformatdec.cpp alc/bformatdec.h alc/buffer_storage.cpp diff --git a/al/event.cpp b/al/event.cpp index a5d7be38..0bba4280 100644 --- a/al/event.cpp +++ b/al/event.cpp @@ -20,7 +20,7 @@ #include "albyte.h" #include "alcontext.h" #include "almalloc.h" -#include "async_event.h" +#include "core/async_event.h" #include "core/except.h" #include "core/logging.h" #include "effects/base.h" @@ -75,25 +75,22 @@ static int EventThread(ALCcontext *context) msg += " state has changed to "; switch(evt.u.srcstate.state) { - case VChangeState::Reset: + case AsyncEvent::SrcState::Reset: msg += "AL_INITIAL"; state = AL_INITIAL; break; - case VChangeState::Stop: + case AsyncEvent::SrcState::Stop: msg += "AL_STOPPED"; state = AL_STOPPED; break; - case VChangeState::Play: + case AsyncEvent::SrcState::Play: msg += "AL_PLAYING"; state = AL_PLAYING; break; - case VChangeState::Pause: + case AsyncEvent::SrcState::Pause: msg += "AL_PAUSED"; state = AL_PAUSED; break; - /* Shouldn't happen */ - case VChangeState::Restart: - break; } context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id, state, static_cast(msg.length()), msg.c_str(), context->mEventParam); diff --git a/alc/alc.cpp b/alc/alc.cpp index 1d96cef0..8ba1c8a2 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -76,10 +76,10 @@ #include "alspan.h" #include "alstring.h" #include "alu.h" -#include "async_event.h" #include "atomic.h" #include "bformatdec.h" #include "core/ambidefs.h" +#include "core/async_event.h" #include "core/bs2b.h" #include "core/cpu_caps.h" #include "core/devformat.h" diff --git a/alc/alu.cpp b/alc/alu.cpp index 7221ce27..a97bc18b 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -50,10 +50,10 @@ #include "alnumeric.h" #include "alspan.h" #include "alstring.h" -#include "async_event.h" #include "atomic.h" #include "bformatdec.h" #include "core/ambidefs.h" +#include "core/async_event.h" #include "core/bs2b.h" #include "core/bsinc_tables.h" #include "core/cpu_caps.h" @@ -1581,7 +1581,24 @@ void SendSourceStateEvent(ContextBase *context, uint id, VChangeState state) AsyncEvent *evt{::new(evt_vec.first.buf) AsyncEvent{EventType_SourceStateChange}}; evt->u.srcstate.id = id; - evt->u.srcstate.state = state; + switch(state) + { + case VChangeState::Reset: + evt->u.srcstate.state = AsyncEvent::SrcState::Reset; + break; + case VChangeState::Stop: + evt->u.srcstate.state = AsyncEvent::SrcState::Stop; + break; + case VChangeState::Play: + evt->u.srcstate.state = AsyncEvent::SrcState::Play; + break; + case VChangeState::Pause: + evt->u.srcstate.state = AsyncEvent::SrcState::Pause; + break; + /* Shouldn't happen. */ + case VChangeState::Restart: + ASSUME(0); + } ring->writeAdvance(1); } diff --git a/alc/async_event.h b/alc/async_event.h deleted file mode 100644 index 1ee58b10..00000000 --- a/alc/async_event.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef ALC_EVENT_H -#define ALC_EVENT_H - -#include "almalloc.h" - -struct EffectState; -enum class VChangeState; - -using uint = unsigned int; - - -enum { - /* End event thread processing. */ - EventType_KillThread = 0, - - /* User event types. */ - EventType_SourceStateChange = 1<<0, - EventType_BufferCompleted = 1<<1, - EventType_Disconnected = 1<<2, - - /* Internal events. */ - EventType_ReleaseEffectState = 65536, -}; - -struct AsyncEvent { - uint EnumType{0u}; - union { - char dummy; - struct { - uint id; - VChangeState state; - } srcstate; - struct { - uint id; - uint count; - } bufcomp; - struct { - char msg[244]; - } disconnect; - EffectState *mEffectState; - } u{}; - - AsyncEvent() noexcept = default; - constexpr AsyncEvent(uint type) noexcept : EnumType{type} { } - - DISABLE_ALLOC() -}; - -#endif diff --git a/alc/voice.cpp b/alc/voice.cpp index 8782e916..6abfcf59 100644 --- a/alc/voice.cpp +++ b/alc/voice.cpp @@ -42,9 +42,9 @@ #include "alspan.h" #include "alstring.h" #include "alu.h" -#include "async_event.h" #include "buffer_storage.h" #include "core/ambidefs.h" +#include "core/async_event.h" #include "core/cpu_caps.h" #include "core/devformat.h" #include "core/device.h" @@ -185,7 +185,7 @@ void SendSourceStoppedEvent(ContextBase *context, uint id) AsyncEvent *evt{::new(evt_vec.first.buf) AsyncEvent{EventType_SourceStateChange}}; evt->u.srcstate.id = id; - evt->u.srcstate.state = VChangeState::Stop; + evt->u.srcstate.state = AsyncEvent::SrcState::Stop; ring->writeAdvance(1); } diff --git a/core/async_event.h b/core/async_event.h new file mode 100644 index 00000000..054f0563 --- /dev/null +++ b/core/async_event.h @@ -0,0 +1,55 @@ +#ifndef CORE_EVENT_H +#define CORE_EVENT_H + +#include "almalloc.h" + +struct EffectState; + +using uint = unsigned int; + + +enum { + /* End event thread processing. */ + EventType_KillThread = 0, + + /* User event types. */ + EventType_SourceStateChange = 1<<0, + EventType_BufferCompleted = 1<<1, + EventType_Disconnected = 1<<2, + + /* Internal events. */ + EventType_ReleaseEffectState = 65536, +}; + +struct AsyncEvent { + enum class SrcState { + Reset, + Stop, + Play, + Pause + }; + + uint EnumType{0u}; + union { + char dummy; + struct { + uint id; + SrcState state; + } srcstate; + struct { + uint id; + uint count; + } bufcomp; + struct { + char msg[244]; + } disconnect; + EffectState *mEffectState; + } u{}; + + AsyncEvent() noexcept = default; + constexpr AsyncEvent(uint type) noexcept : EnumType{type} { } + + DISABLE_ALLOC() +}; + +#endif -- cgit v1.2.3 From 2479483645bf719233fa5d39bc91682f12d71350 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 25 Apr 2021 18:08:08 -0700 Subject: Move bformatdec to core --- CMakeLists.txt | 6 +- al/source.cpp | 2 +- alc/alc.cpp | 4 +- alc/alu.cpp | 4 +- alc/bformatdec.cpp | 263 ------------------------------------------------- alc/bformatdec.h | 71 ------------- alc/front_stablizer.h | 36 ------- alc/panning.cpp | 4 +- core/bformatdec.cpp | 263 +++++++++++++++++++++++++++++++++++++++++++++++++ core/bformatdec.h | 71 +++++++++++++ core/front_stablizer.h | 36 +++++++ 11 files changed, 380 insertions(+), 380 deletions(-) delete mode 100644 alc/bformatdec.cpp delete mode 100644 alc/bformatdec.h delete mode 100644 alc/front_stablizer.h create mode 100644 core/bformatdec.cpp create mode 100644 core/bformatdec.h create mode 100644 core/front_stablizer.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 58d512e0..3a1f6a09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -647,6 +647,8 @@ set(CORE_OBJS core/ambidefs.cpp core/ambidefs.h core/async_event.h + core/bformatdec.cpp + core/bformatdec.h core/bs2b.cpp core/bs2b.h core/bsinc_defs.h @@ -673,6 +675,7 @@ set(CORE_OBJS core/fmt_traits.h core/fpu_ctrl.cpp core/fpu_ctrl.h + core/front_stablizer.h core/helpers.cpp core/helpers.h core/hrtf.cpp @@ -759,8 +762,6 @@ set(ALC_OBJS alc/alconfig.cpp alc/alconfig.h alc/alcontext.h - alc/bformatdec.cpp - alc/bformatdec.h alc/buffer_storage.cpp alc/buffer_storage.h alc/effectslot.cpp @@ -780,7 +781,6 @@ set(ALC_OBJS alc/effects/pshifter.cpp alc/effects/reverb.cpp alc/effects/vmorpher.cpp - alc/front_stablizer.h alc/inprogext.h alc/panning.cpp alc/voice.cpp diff --git a/al/source.cpp b/al/source.cpp index 69120bac..4855b72d 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -56,9 +56,9 @@ #include "atomic.h" #include "auxeffectslot.h" #include "backends/base.h" -#include "bformatdec.h" #include "buffer.h" #include "core/ambidefs.h" +#include "core/bformatdec.h" #include "core/except.h" #include "core/filters/nfc.h" #include "core/filters/splitter.h" diff --git a/alc/alc.cpp b/alc/alc.cpp index 8ba1c8a2..d06ca067 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -77,9 +77,9 @@ #include "alstring.h" #include "alu.h" #include "atomic.h" -#include "bformatdec.h" #include "core/ambidefs.h" #include "core/async_event.h" +#include "core/bformatdec.h" #include "core/bs2b.h" #include "core/cpu_caps.h" #include "core/devformat.h" @@ -89,11 +89,11 @@ #include "core/filters/nfc.h" #include "core/filters/splitter.h" #include "core/fpu_ctrl.h" +#include "core/front_stablizer.h" #include "core/hrtf.h" #include "core/logging.h" #include "core/uhjfilter.h" #include "effects/base.h" -#include "front_stablizer.h" #include "inprogext.h" #include "intrusive_ptr.h" #include "opthelpers.h" diff --git a/alc/alu.cpp b/alc/alu.cpp index a97bc18b..14ffd8b5 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -51,9 +51,9 @@ #include "alspan.h" #include "alstring.h" #include "atomic.h" -#include "bformatdec.h" #include "core/ambidefs.h" #include "core/async_event.h" +#include "core/bformatdec.h" #include "core/bs2b.h" #include "core/bsinc_tables.h" #include "core/cpu_caps.h" @@ -62,6 +62,7 @@ #include "core/filters/nfc.h" #include "core/filters/splitter.h" #include "core/fpu_ctrl.h" +#include "core/front_stablizer.h" #include "core/hrtf.h" #include "core/mastering.h" #include "core/mixer.h" @@ -69,7 +70,6 @@ #include "core/uhjfilter.h" #include "effects/base.h" #include "effectslot.h" -#include "front_stablizer.h" #include "inprogext.h" #include "math_defs.h" #include "opthelpers.h" diff --git a/alc/bformatdec.cpp b/alc/bformatdec.cpp deleted file mode 100644 index e29af045..00000000 --- a/alc/bformatdec.cpp +++ /dev/null @@ -1,263 +0,0 @@ - -#include "config.h" - -#include "bformatdec.h" - -#include -#include -#include -#include - -#include "almalloc.h" -#include "core/ambdec.h" -#include "core/filters/splitter.h" -#include "core/mixer.h" -#include "front_stablizer.h" -#include "math_defs.h" -#include "opthelpers.h" - - -namespace { - -inline auto& GetAmbiScales(AmbDecScale scaletype) noexcept -{ - if(scaletype == AmbDecScale::FuMa) return AmbiScale::FromFuMa(); - if(scaletype == AmbDecScale::SN3D) return AmbiScale::FromSN3D(); - return AmbiScale::FromN3D(); -} - -} // namespace - - -BFormatDec::BFormatDec(const AmbDecConf *conf, const bool allow_2band, const size_t inchans, - const uint srate, const uint (&chanmap)[MAX_OUTPUT_CHANNELS], - std::unique_ptr stablizer) - : mStablizer{std::move(stablizer)}, mDualBand{allow_2band && (conf->FreqBands == 2)} - , mChannelDec{inchans} -{ - const bool periphonic{(conf->ChanMask&AmbiPeriphonicMask) != 0}; - auto&& coeff_scale = GetAmbiScales(conf->CoeffScale); - - if(!mDualBand) - { - for(size_t j{0},k{0};j < mChannelDec.size();++j) - { - const size_t acn{periphonic ? j : AmbiIndex::FromACN2D()[j]}; - if(!(conf->ChanMask&(1u<HFOrderGain[order] / coeff_scale[acn]}; - for(size_t i{0u};i < conf->NumSpeakers;++i) - { - const size_t chanidx{chanmap[i]}; - mChannelDec[j].mGains.Single[chanidx] = conf->Matrix[i][k] * gain; - } - ++k; - } - } - else - { - mChannelDec[0].mXOver.init(conf->XOverFreq / static_cast(srate)); - for(size_t j{1};j < mChannelDec.size();++j) - mChannelDec[j].mXOver = mChannelDec[0].mXOver; - - const float ratio{std::pow(10.0f, conf->XOverRatio / 40.0f)}; - for(size_t j{0},k{0};j < mChannelDec.size();++j) - { - const size_t acn{periphonic ? j : AmbiIndex::FromACN2D()[j]}; - if(!(conf->ChanMask&(1u<HFOrderGain[order] * ratio / coeff_scale[acn]}; - const float lfGain{conf->LFOrderGain[order] / ratio / coeff_scale[acn]}; - for(size_t i{0u};i < conf->NumSpeakers;++i) - { - const size_t chanidx{chanmap[i]}; - mChannelDec[j].mGains.Dual[sHFBand][chanidx] = conf->HFMatrix[i][k] * hfGain; - mChannelDec[j].mGains.Dual[sLFBand][chanidx] = conf->LFMatrix[i][k] * lfGain; - } - ++k; - } - } -} - -BFormatDec::BFormatDec(const size_t inchans, const al::span coeffs, - const al::span coeffslf, std::unique_ptr stablizer) - : mStablizer{std::move(stablizer)}, mDualBand{!coeffslf.empty()}, mChannelDec{inchans} -{ - if(!mDualBand) - { - for(size_t j{0};j < mChannelDec.size();++j) - { - float *outcoeffs{mChannelDec[j].mGains.Single}; - for(const ChannelDec &incoeffs : coeffs) - *(outcoeffs++) = incoeffs[j]; - } - } - else - { - for(size_t j{0};j < mChannelDec.size();++j) - { - float *outcoeffs{mChannelDec[j].mGains.Dual[sHFBand]}; - for(const ChannelDec &incoeffs : coeffs) - *(outcoeffs++) = incoeffs[j]; - - outcoeffs = mChannelDec[j].mGains.Dual[sLFBand]; - for(const ChannelDec &incoeffs : coeffslf) - *(outcoeffs++) = incoeffs[j]; - } - } -} - - -void BFormatDec::process(const al::span OutBuffer, - const FloatBufferLine *InSamples, const size_t SamplesToDo) -{ - ASSUME(SamplesToDo > 0); - - if(mDualBand) - { - const al::span hfSamples{mSamples[sHFBand].data(), SamplesToDo}; - const al::span lfSamples{mSamples[sLFBand].data(), SamplesToDo}; - for(auto &chandec : mChannelDec) - { - chandec.mXOver.process({InSamples->data(), SamplesToDo}, hfSamples.data(), - lfSamples.data()); - MixSamples(hfSamples, OutBuffer, chandec.mGains.Dual[sHFBand], - chandec.mGains.Dual[sHFBand], 0, 0); - MixSamples(lfSamples, OutBuffer, chandec.mGains.Dual[sLFBand], - chandec.mGains.Dual[sLFBand], 0, 0); - ++InSamples; - } - } - else - { - for(auto &chandec : mChannelDec) - { - MixSamples({InSamples->data(), SamplesToDo}, OutBuffer, chandec.mGains.Single, - chandec.mGains.Single, 0, 0); - ++InSamples; - } - } -} - -void BFormatDec::processStablize(const al::span OutBuffer, - const FloatBufferLine *InSamples, const size_t lidx, const size_t ridx, const size_t cidx, - const size_t SamplesToDo) -{ - ASSUME(SamplesToDo > 0); - - /* Move the existing direct L/R signal out so it doesn't get processed by - * the stablizer. Add a delay to it so it stays aligned with the stablizer - * delay. - */ - float *RESTRICT mid{al::assume_aligned<16>(mStablizer->MidDirect.data())}; - float *RESTRICT side{al::assume_aligned<16>(mStablizer->Side.data())}; - for(size_t i{0};i < SamplesToDo;++i) - { - mid[FrontStablizer::DelayLength+i] = OutBuffer[lidx][i] + OutBuffer[ridx][i]; - side[FrontStablizer::DelayLength+i] = OutBuffer[lidx][i] - OutBuffer[ridx][i]; - } - std::fill_n(OutBuffer[lidx].begin(), SamplesToDo, 0.0f); - std::fill_n(OutBuffer[ridx].begin(), SamplesToDo, 0.0f); - - /* Decode the B-Format input to OutBuffer. */ - process(OutBuffer, InSamples, SamplesToDo); - - /* Apply a delay to all channels, except the front-left and front-right, so - * they maintain correct timing. - */ - const size_t NumChannels{OutBuffer.size()}; - for(size_t i{0u};i < NumChannels;i++) - { - if(i == lidx || i == ridx) - continue; - - auto &DelayBuf = mStablizer->DelayBuf[i]; - auto buffer_end = OutBuffer[i].begin() + SamplesToDo; - if LIKELY(SamplesToDo >= FrontStablizer::DelayLength) - { - auto delay_end = std::rotate(OutBuffer[i].begin(), - buffer_end - FrontStablizer::DelayLength, buffer_end); - std::swap_ranges(OutBuffer[i].begin(), delay_end, DelayBuf.begin()); - } - else - { - auto delay_start = std::swap_ranges(OutBuffer[i].begin(), buffer_end, - DelayBuf.begin()); - std::rotate(DelayBuf.begin(), delay_start, DelayBuf.end()); - } - } - - /* Include the side signal for what was just decoded. */ - for(size_t i{0};i < SamplesToDo;++i) - side[FrontStablizer::DelayLength+i] += OutBuffer[lidx][i] - OutBuffer[ridx][i]; - - /* Combine the delayed mid signal with the decoded mid signal. Note that - * the samples are stored and combined in reverse, so the newest samples - * are at the front and the oldest at the back. - */ - al::span tmpbuf{mStablizer->TempBuf.data(), SamplesToDo+FrontStablizer::DelayLength}; - auto tmpiter = tmpbuf.begin() + SamplesToDo; - std::copy(mStablizer->MidDelay.cbegin(), mStablizer->MidDelay.cend(), tmpiter); - for(size_t i{0};i < SamplesToDo;++i) - *--tmpiter = OutBuffer[lidx][i] + OutBuffer[ridx][i]; - /* Save the newest samples for next time. */ - std::copy_n(tmpbuf.cbegin(), mStablizer->MidDelay.size(), mStablizer->MidDelay.begin()); - - /* Apply an all-pass on the reversed signal, then reverse the samples to - * get the forward signal with a reversed phase shift. The future samples - * are included with the all-pass to reduce the error in the output - * samples (the smaller the delay, the more error is introduced). - */ - mStablizer->MidFilter.applyAllpass(tmpbuf); - tmpbuf = tmpbuf.subspan(); - std::reverse(tmpbuf.begin(), tmpbuf.end()); - - /* Now apply the band-splitter, combining its phase shift with the reversed - * phase shift, restoring the original phase on the split signal. - */ - mStablizer->MidFilter.process(tmpbuf, mStablizer->MidHF.data(), mStablizer->MidLF.data()); - - /* This pans the separate low- and high-frequency signals between being on - * the center channel and the left+right channels. The low-frequency signal - * is panned 1/3rd toward center and the high-frequency signal is panned - * 1/4th toward center. These values can be tweaked. - */ - const float cos_lf{std::cos(1.0f/3.0f * (al::MathDefs::Pi()*0.5f))}; - const float cos_hf{std::cos(1.0f/4.0f * (al::MathDefs::Pi()*0.5f))}; - const float sin_lf{std::sin(1.0f/3.0f * (al::MathDefs::Pi()*0.5f))}; - const float sin_hf{std::sin(1.0f/4.0f * (al::MathDefs::Pi()*0.5f))}; - for(size_t i{0};i < SamplesToDo;i++) - { - const float m{mStablizer->MidLF[i]*cos_lf + mStablizer->MidHF[i]*cos_hf + mid[i]}; - const float c{mStablizer->MidLF[i]*sin_lf + mStablizer->MidHF[i]*sin_hf}; - const float s{side[i]}; - - /* The generated center channel signal adds to the existing signal, - * while the modified left and right channels replace. - */ - OutBuffer[lidx][i] = (m + s) * 0.5f; - OutBuffer[ridx][i] = (m - s) * 0.5f; - OutBuffer[cidx][i] += c * 0.5f; - } - /* Move the delayed mid/side samples to the front for next time. */ - auto mid_end = mStablizer->MidDirect.cbegin() + SamplesToDo; - std::copy(mid_end, mid_end+FrontStablizer::DelayLength, mStablizer->MidDirect.begin()); - auto side_end = mStablizer->Side.cbegin() + SamplesToDo; - std::copy(side_end, side_end+FrontStablizer::DelayLength, mStablizer->Side.begin()); -} - - -std::unique_ptr BFormatDec::Create(const AmbDecConf *conf, const bool allow_2band, - const size_t inchans, const uint srate, const uint (&chanmap)[MAX_OUTPUT_CHANNELS], - std::unique_ptr stablizer) -{ - return std::unique_ptr{new(FamCount(inchans)) - BFormatDec{conf, allow_2band, inchans, srate, chanmap, std::move(stablizer)}}; -} -std::unique_ptr BFormatDec::Create(const size_t inchans, - const al::span coeffs, const al::span coeffslf, - std::unique_ptr stablizer) -{ - return std::unique_ptr{new(FamCount(inchans)) - BFormatDec{inchans, coeffs, coeffslf, std::move(stablizer)}}; -} diff --git a/alc/bformatdec.h b/alc/bformatdec.h deleted file mode 100644 index bb39f709..00000000 --- a/alc/bformatdec.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef BFORMATDEC_H -#define BFORMATDEC_H - -#include -#include -#include - -#include "almalloc.h" -#include "alspan.h" -#include "core/ambidefs.h" -#include "core/bufferline.h" -#include "core/devformat.h" -#include "core/filters/splitter.h" - -struct AmbDecConf; -struct FrontStablizer; - - -using ChannelDec = std::array; - -class BFormatDec { - static constexpr size_t sHFBand{0}; - static constexpr size_t sLFBand{1}; - static constexpr size_t sNumBands{2}; - - struct ChannelDecoder { - union MatrixU { - float Dual[sNumBands][MAX_OUTPUT_CHANNELS]; - float Single[MAX_OUTPUT_CHANNELS]; - } mGains{}; - - /* NOTE: BandSplitter filter is unused with single-band decoding. */ - BandSplitter mXOver; - }; - - alignas(16) std::array mSamples; - - const std::unique_ptr mStablizer; - const bool mDualBand{false}; - - al::FlexArray mChannelDec; - -public: - BFormatDec(const AmbDecConf *conf, const bool allow_2band, const size_t inchans, - const uint srate, const uint (&chanmap)[MAX_OUTPUT_CHANNELS], - std::unique_ptr stablizer); - BFormatDec(const size_t inchans, const al::span coeffs, - const al::span coeffslf, std::unique_ptr stablizer); - - bool hasStablizer() const noexcept { return mStablizer != nullptr; }; - - /* Decodes the ambisonic input to the given output channels. */ - void process(const al::span OutBuffer, const FloatBufferLine *InSamples, - const size_t SamplesToDo); - - /* Decodes the ambisonic input to the given output channels with stablization. */ - void processStablize(const al::span OutBuffer, - const FloatBufferLine *InSamples, const size_t lidx, const size_t ridx, const size_t cidx, - const size_t SamplesToDo); - - static std::unique_ptr Create(const AmbDecConf *conf, const bool allow_2band, - const size_t inchans, const uint srate, const uint (&chanmap)[MAX_OUTPUT_CHANNELS], - std::unique_ptr stablizer); - static std::unique_ptr Create(const size_t inchans, - const al::span coeffs, const al::span coeffslf, - std::unique_ptr stablizer); - - DEF_FAM_NEWDEL(BFormatDec, mChannelDec) -}; - -#endif /* BFORMATDEC_H */ diff --git a/alc/front_stablizer.h b/alc/front_stablizer.h deleted file mode 100644 index 0fedeb50..00000000 --- a/alc/front_stablizer.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef ALC_FRONT_STABLIZER_H -#define ALC_FRONT_STABLIZER_H - -#include -#include - -#include "almalloc.h" -#include "core/bufferline.h" -#include "core/filters/splitter.h" - - -struct FrontStablizer { - static constexpr size_t DelayLength{256u}; - - FrontStablizer(size_t numchans) : DelayBuf{numchans} { } - - alignas(16) std::array Side{}; - alignas(16) std::array MidDirect{}; - alignas(16) std::array MidDelay{}; - - alignas(16) std::array TempBuf{}; - - BandSplitter MidFilter; - alignas(16) FloatBufferLine MidLF{}; - alignas(16) FloatBufferLine MidHF{}; - - using DelayLine = std::array; - al::FlexArray DelayBuf; - - static std::unique_ptr Create(size_t numchans) - { return std::unique_ptr{new(FamCount(numchans)) FrontStablizer{numchans}}; } - - DEF_FAM_NEWDEL(FrontStablizer, DelayBuf) -}; - -#endif /* ALC_FRONT_STABLIZER_H */ diff --git a/alc/panning.cpp b/alc/panning.cpp index ce6ba29c..5e263111 100644 --- a/alc/panning.cpp +++ b/alc/panning.cpp @@ -47,15 +47,15 @@ #include "alspan.h" #include "alstring.h" #include "alu.h" -#include "bformatdec.h" #include "core/ambdec.h" #include "core/ambidefs.h" +#include "core/bformatdec.h" #include "core/bs2b.h" #include "core/devformat.h" +#include "core/front_stablizer.h" #include "core/hrtf.h" #include "core/logging.h" #include "core/uhjfilter.h" -#include "front_stablizer.h" #include "math_defs.h" #include "opthelpers.h" diff --git a/core/bformatdec.cpp b/core/bformatdec.cpp new file mode 100644 index 00000000..6bf85ec9 --- /dev/null +++ b/core/bformatdec.cpp @@ -0,0 +1,263 @@ + +#include "config.h" + +#include "bformatdec.h" + +#include +#include +#include +#include + +#include "almalloc.h" +#include "ambdec.h" +#include "filters/splitter.h" +#include "front_stablizer.h" +#include "math_defs.h" +#include "mixer.h" +#include "opthelpers.h" + + +namespace { + +inline auto& GetAmbiScales(AmbDecScale scaletype) noexcept +{ + if(scaletype == AmbDecScale::FuMa) return AmbiScale::FromFuMa(); + if(scaletype == AmbDecScale::SN3D) return AmbiScale::FromSN3D(); + return AmbiScale::FromN3D(); +} + +} // namespace + + +BFormatDec::BFormatDec(const AmbDecConf *conf, const bool allow_2band, const size_t inchans, + const uint srate, const uint (&chanmap)[MAX_OUTPUT_CHANNELS], + std::unique_ptr stablizer) + : mStablizer{std::move(stablizer)}, mDualBand{allow_2band && (conf->FreqBands == 2)} + , mChannelDec{inchans} +{ + const bool periphonic{(conf->ChanMask&AmbiPeriphonicMask) != 0}; + auto&& coeff_scale = GetAmbiScales(conf->CoeffScale); + + if(!mDualBand) + { + for(size_t j{0},k{0};j < mChannelDec.size();++j) + { + const size_t acn{periphonic ? j : AmbiIndex::FromACN2D()[j]}; + if(!(conf->ChanMask&(1u<HFOrderGain[order] / coeff_scale[acn]}; + for(size_t i{0u};i < conf->NumSpeakers;++i) + { + const size_t chanidx{chanmap[i]}; + mChannelDec[j].mGains.Single[chanidx] = conf->Matrix[i][k] * gain; + } + ++k; + } + } + else + { + mChannelDec[0].mXOver.init(conf->XOverFreq / static_cast(srate)); + for(size_t j{1};j < mChannelDec.size();++j) + mChannelDec[j].mXOver = mChannelDec[0].mXOver; + + const float ratio{std::pow(10.0f, conf->XOverRatio / 40.0f)}; + for(size_t j{0},k{0};j < mChannelDec.size();++j) + { + const size_t acn{periphonic ? j : AmbiIndex::FromACN2D()[j]}; + if(!(conf->ChanMask&(1u<HFOrderGain[order] * ratio / coeff_scale[acn]}; + const float lfGain{conf->LFOrderGain[order] / ratio / coeff_scale[acn]}; + for(size_t i{0u};i < conf->NumSpeakers;++i) + { + const size_t chanidx{chanmap[i]}; + mChannelDec[j].mGains.Dual[sHFBand][chanidx] = conf->HFMatrix[i][k] * hfGain; + mChannelDec[j].mGains.Dual[sLFBand][chanidx] = conf->LFMatrix[i][k] * lfGain; + } + ++k; + } + } +} + +BFormatDec::BFormatDec(const size_t inchans, const al::span coeffs, + const al::span coeffslf, std::unique_ptr stablizer) + : mStablizer{std::move(stablizer)}, mDualBand{!coeffslf.empty()}, mChannelDec{inchans} +{ + if(!mDualBand) + { + for(size_t j{0};j < mChannelDec.size();++j) + { + float *outcoeffs{mChannelDec[j].mGains.Single}; + for(const ChannelDec &incoeffs : coeffs) + *(outcoeffs++) = incoeffs[j]; + } + } + else + { + for(size_t j{0};j < mChannelDec.size();++j) + { + float *outcoeffs{mChannelDec[j].mGains.Dual[sHFBand]}; + for(const ChannelDec &incoeffs : coeffs) + *(outcoeffs++) = incoeffs[j]; + + outcoeffs = mChannelDec[j].mGains.Dual[sLFBand]; + for(const ChannelDec &incoeffs : coeffslf) + *(outcoeffs++) = incoeffs[j]; + } + } +} + + +void BFormatDec::process(const al::span OutBuffer, + const FloatBufferLine *InSamples, const size_t SamplesToDo) +{ + ASSUME(SamplesToDo > 0); + + if(mDualBand) + { + const al::span hfSamples{mSamples[sHFBand].data(), SamplesToDo}; + const al::span lfSamples{mSamples[sLFBand].data(), SamplesToDo}; + for(auto &chandec : mChannelDec) + { + chandec.mXOver.process({InSamples->data(), SamplesToDo}, hfSamples.data(), + lfSamples.data()); + MixSamples(hfSamples, OutBuffer, chandec.mGains.Dual[sHFBand], + chandec.mGains.Dual[sHFBand], 0, 0); + MixSamples(lfSamples, OutBuffer, chandec.mGains.Dual[sLFBand], + chandec.mGains.Dual[sLFBand], 0, 0); + ++InSamples; + } + } + else + { + for(auto &chandec : mChannelDec) + { + MixSamples({InSamples->data(), SamplesToDo}, OutBuffer, chandec.mGains.Single, + chandec.mGains.Single, 0, 0); + ++InSamples; + } + } +} + +void BFormatDec::processStablize(const al::span OutBuffer, + const FloatBufferLine *InSamples, const size_t lidx, const size_t ridx, const size_t cidx, + const size_t SamplesToDo) +{ + ASSUME(SamplesToDo > 0); + + /* Move the existing direct L/R signal out so it doesn't get processed by + * the stablizer. Add a delay to it so it stays aligned with the stablizer + * delay. + */ + float *RESTRICT mid{al::assume_aligned<16>(mStablizer->MidDirect.data())}; + float *RESTRICT side{al::assume_aligned<16>(mStablizer->Side.data())}; + for(size_t i{0};i < SamplesToDo;++i) + { + mid[FrontStablizer::DelayLength+i] = OutBuffer[lidx][i] + OutBuffer[ridx][i]; + side[FrontStablizer::DelayLength+i] = OutBuffer[lidx][i] - OutBuffer[ridx][i]; + } + std::fill_n(OutBuffer[lidx].begin(), SamplesToDo, 0.0f); + std::fill_n(OutBuffer[ridx].begin(), SamplesToDo, 0.0f); + + /* Decode the B-Format input to OutBuffer. */ + process(OutBuffer, InSamples, SamplesToDo); + + /* Apply a delay to all channels, except the front-left and front-right, so + * they maintain correct timing. + */ + const size_t NumChannels{OutBuffer.size()}; + for(size_t i{0u};i < NumChannels;i++) + { + if(i == lidx || i == ridx) + continue; + + auto &DelayBuf = mStablizer->DelayBuf[i]; + auto buffer_end = OutBuffer[i].begin() + SamplesToDo; + if LIKELY(SamplesToDo >= FrontStablizer::DelayLength) + { + auto delay_end = std::rotate(OutBuffer[i].begin(), + buffer_end - FrontStablizer::DelayLength, buffer_end); + std::swap_ranges(OutBuffer[i].begin(), delay_end, DelayBuf.begin()); + } + else + { + auto delay_start = std::swap_ranges(OutBuffer[i].begin(), buffer_end, + DelayBuf.begin()); + std::rotate(DelayBuf.begin(), delay_start, DelayBuf.end()); + } + } + + /* Include the side signal for what was just decoded. */ + for(size_t i{0};i < SamplesToDo;++i) + side[FrontStablizer::DelayLength+i] += OutBuffer[lidx][i] - OutBuffer[ridx][i]; + + /* Combine the delayed mid signal with the decoded mid signal. Note that + * the samples are stored and combined in reverse, so the newest samples + * are at the front and the oldest at the back. + */ + al::span tmpbuf{mStablizer->TempBuf.data(), SamplesToDo+FrontStablizer::DelayLength}; + auto tmpiter = tmpbuf.begin() + SamplesToDo; + std::copy(mStablizer->MidDelay.cbegin(), mStablizer->MidDelay.cend(), tmpiter); + for(size_t i{0};i < SamplesToDo;++i) + *--tmpiter = OutBuffer[lidx][i] + OutBuffer[ridx][i]; + /* Save the newest samples for next time. */ + std::copy_n(tmpbuf.cbegin(), mStablizer->MidDelay.size(), mStablizer->MidDelay.begin()); + + /* Apply an all-pass on the reversed signal, then reverse the samples to + * get the forward signal with a reversed phase shift. The future samples + * are included with the all-pass to reduce the error in the output + * samples (the smaller the delay, the more error is introduced). + */ + mStablizer->MidFilter.applyAllpass(tmpbuf); + tmpbuf = tmpbuf.subspan(); + std::reverse(tmpbuf.begin(), tmpbuf.end()); + + /* Now apply the band-splitter, combining its phase shift with the reversed + * phase shift, restoring the original phase on the split signal. + */ + mStablizer->MidFilter.process(tmpbuf, mStablizer->MidHF.data(), mStablizer->MidLF.data()); + + /* This pans the separate low- and high-frequency signals between being on + * the center channel and the left+right channels. The low-frequency signal + * is panned 1/3rd toward center and the high-frequency signal is panned + * 1/4th toward center. These values can be tweaked. + */ + const float cos_lf{std::cos(1.0f/3.0f * (al::MathDefs::Pi()*0.5f))}; + const float cos_hf{std::cos(1.0f/4.0f * (al::MathDefs::Pi()*0.5f))}; + const float sin_lf{std::sin(1.0f/3.0f * (al::MathDefs::Pi()*0.5f))}; + const float sin_hf{std::sin(1.0f/4.0f * (al::MathDefs::Pi()*0.5f))}; + for(size_t i{0};i < SamplesToDo;i++) + { + const float m{mStablizer->MidLF[i]*cos_lf + mStablizer->MidHF[i]*cos_hf + mid[i]}; + const float c{mStablizer->MidLF[i]*sin_lf + mStablizer->MidHF[i]*sin_hf}; + const float s{side[i]}; + + /* The generated center channel signal adds to the existing signal, + * while the modified left and right channels replace. + */ + OutBuffer[lidx][i] = (m + s) * 0.5f; + OutBuffer[ridx][i] = (m - s) * 0.5f; + OutBuffer[cidx][i] += c * 0.5f; + } + /* Move the delayed mid/side samples to the front for next time. */ + auto mid_end = mStablizer->MidDirect.cbegin() + SamplesToDo; + std::copy(mid_end, mid_end+FrontStablizer::DelayLength, mStablizer->MidDirect.begin()); + auto side_end = mStablizer->Side.cbegin() + SamplesToDo; + std::copy(side_end, side_end+FrontStablizer::DelayLength, mStablizer->Side.begin()); +} + + +std::unique_ptr BFormatDec::Create(const AmbDecConf *conf, const bool allow_2band, + const size_t inchans, const uint srate, const uint (&chanmap)[MAX_OUTPUT_CHANNELS], + std::unique_ptr stablizer) +{ + return std::unique_ptr{new(FamCount(inchans)) + BFormatDec{conf, allow_2band, inchans, srate, chanmap, std::move(stablizer)}}; +} +std::unique_ptr BFormatDec::Create(const size_t inchans, + const al::span coeffs, const al::span coeffslf, + std::unique_ptr stablizer) +{ + return std::unique_ptr{new(FamCount(inchans)) + BFormatDec{inchans, coeffs, coeffslf, std::move(stablizer)}}; +} diff --git a/core/bformatdec.h b/core/bformatdec.h new file mode 100644 index 00000000..a0ae3f27 --- /dev/null +++ b/core/bformatdec.h @@ -0,0 +1,71 @@ +#ifndef CORE_BFORMATDEC_H +#define CORE_BFORMATDEC_H + +#include +#include +#include + +#include "almalloc.h" +#include "alspan.h" +#include "ambidefs.h" +#include "bufferline.h" +#include "devformat.h" +#include "filters/splitter.h" + +struct AmbDecConf; +struct FrontStablizer; + + +using ChannelDec = std::array; + +class BFormatDec { + static constexpr size_t sHFBand{0}; + static constexpr size_t sLFBand{1}; + static constexpr size_t sNumBands{2}; + + struct ChannelDecoder { + union MatrixU { + float Dual[sNumBands][MAX_OUTPUT_CHANNELS]; + float Single[MAX_OUTPUT_CHANNELS]; + } mGains{}; + + /* NOTE: BandSplitter filter is unused with single-band decoding. */ + BandSplitter mXOver; + }; + + alignas(16) std::array mSamples; + + const std::unique_ptr mStablizer; + const bool mDualBand{false}; + + al::FlexArray mChannelDec; + +public: + BFormatDec(const AmbDecConf *conf, const bool allow_2band, const size_t inchans, + const uint srate, const uint (&chanmap)[MAX_OUTPUT_CHANNELS], + std::unique_ptr stablizer); + BFormatDec(const size_t inchans, const al::span coeffs, + const al::span coeffslf, std::unique_ptr stablizer); + + bool hasStablizer() const noexcept { return mStablizer != nullptr; }; + + /* Decodes the ambisonic input to the given output channels. */ + void process(const al::span OutBuffer, const FloatBufferLine *InSamples, + const size_t SamplesToDo); + + /* Decodes the ambisonic input to the given output channels with stablization. */ + void processStablize(const al::span OutBuffer, + const FloatBufferLine *InSamples, const size_t lidx, const size_t ridx, const size_t cidx, + const size_t SamplesToDo); + + static std::unique_ptr Create(const AmbDecConf *conf, const bool allow_2band, + const size_t inchans, const uint srate, const uint (&chanmap)[MAX_OUTPUT_CHANNELS], + std::unique_ptr stablizer); + static std::unique_ptr Create(const size_t inchans, + const al::span coeffs, const al::span coeffslf, + std::unique_ptr stablizer); + + DEF_FAM_NEWDEL(BFormatDec, mChannelDec) +}; + +#endif /* CORE_BFORMATDEC_H */ diff --git a/core/front_stablizer.h b/core/front_stablizer.h new file mode 100644 index 00000000..3d328a8d --- /dev/null +++ b/core/front_stablizer.h @@ -0,0 +1,36 @@ +#ifndef CORE_FRONT_STABLIZER_H +#define CORE_FRONT_STABLIZER_H + +#include +#include + +#include "almalloc.h" +#include "bufferline.h" +#include "filters/splitter.h" + + +struct FrontStablizer { + static constexpr size_t DelayLength{256u}; + + FrontStablizer(size_t numchans) : DelayBuf{numchans} { } + + alignas(16) std::array Side{}; + alignas(16) std::array MidDirect{}; + alignas(16) std::array MidDelay{}; + + alignas(16) std::array TempBuf{}; + + BandSplitter MidFilter; + alignas(16) FloatBufferLine MidLF{}; + alignas(16) FloatBufferLine MidHF{}; + + using DelayLine = std::array; + al::FlexArray DelayBuf; + + static std::unique_ptr Create(size_t numchans) + { return std::unique_ptr{new(FamCount(numchans)) FrontStablizer{numchans}}; } + + DEF_FAM_NEWDEL(FrontStablizer, DelayBuf) +}; + +#endif /* CORE_FRONT_STABLIZER_H */ -- cgit v1.2.3 From 99157f149f180cfcc2e4be6a3d2a54843411e87a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 27 Apr 2021 08:04:09 -0700 Subject: Move ContextBase and VoiceChange to core --- CMakeLists.txt | 8 ++- al/event.cpp | 2 +- al/source.cpp | 2 +- alc/alc.cpp | 2 +- alc/alcontext.h | 141 +------------------------------------------ alc/alu.cpp | 4 +- alc/alu.h | 10 --- alc/voice.cpp | 10 +-- alc/voice.h | 8 ++- alc/voice_change.h | 31 ---------- core/context.cpp | 5 ++ core/context.h | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++ core/voice_change.h | 31 ++++++++++ 13 files changed, 230 insertions(+), 195 deletions(-) delete mode 100644 alc/voice_change.h create mode 100644 core/context.cpp create mode 100644 core/context.h create mode 100644 core/voice_change.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a1f6a09..ef4ef720 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -655,6 +655,8 @@ set(CORE_OBJS core/bsinc_tables.cpp core/bsinc_tables.h core/bufferline.h + core/context.cpp + core/context.h core/converter.cpp core/converter.h core/cpu_caps.cpp @@ -689,7 +691,8 @@ set(CORE_OBJS core/resampler_limits.h core/uhjfilter.cpp core/uhjfilter.h - core/uiddefs.cpp) + core/uiddefs.cpp + core/voice_change.h) set(HAVE_RTKIT 0) option(ALSOFT_REQUIRE_RTKIT "Require RTKit/D-Bus support" FALSE) @@ -784,8 +787,7 @@ set(ALC_OBJS alc/inprogext.h alc/panning.cpp alc/voice.cpp - alc/voice.h - alc/voice_change.h) + alc/voice.h) # Include SIMD mixers set(CPU_EXTS "Default") diff --git a/al/event.cpp b/al/event.cpp index 0bba4280..843a90e1 100644 --- a/al/event.cpp +++ b/al/event.cpp @@ -23,12 +23,12 @@ #include "core/async_event.h" #include "core/except.h" #include "core/logging.h" +#include "core/voice_change.h" #include "effects/base.h" #include "inprogext.h" #include "opthelpers.h" #include "ringbuffer.h" #include "threads.h" -#include "voice_change.h" static int EventThread(ALCcontext *context) diff --git a/al/source.cpp b/al/source.cpp index 27eeff9c..ca234a05 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -63,6 +63,7 @@ #include "core/filters/nfc.h" #include "core/filters/splitter.h" #include "core/logging.h" +#include "core/voice_change.h" #include "event.h" #include "filter.h" #include "inprogext.h" @@ -70,7 +71,6 @@ #include "opthelpers.h" #include "ringbuffer.h" #include "threads.h" -#include "voice_change.h" namespace { diff --git a/alc/alc.cpp b/alc/alc.cpp index 47b77758..858fe278 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -93,6 +93,7 @@ #include "core/hrtf.h" #include "core/logging.h" #include "core/uhjfilter.h" +#include "core/voice_change.h" #include "effects/base.h" #include "inprogext.h" #include "intrusive_ptr.h" @@ -103,7 +104,6 @@ #include "threads.h" #include "vecmat.h" #include "vector.h" -#include "voice_change.h" #include "backends/base.h" #include "backends/null.h" diff --git a/alc/alcontext.h b/alc/alcontext.h index 075e6a55..2b38dd70 100644 --- a/alc/alcontext.h +++ b/alc/alcontext.h @@ -19,6 +19,7 @@ #include "alu.h" #include "atomic.h" #include "core/bufferline.h" +#include "core/context.h" #include "inprogext.h" #include "intrusive_ptr.h" #include "threads.h" @@ -38,146 +39,6 @@ struct VoicePropsItem; using uint = unsigned int; -enum class DistanceModel : unsigned char { - Disable, - Inverse, InverseClamped, - Linear, LinearClamped, - Exponent, ExponentClamped, - - Default = InverseClamped -}; - - -struct WetBuffer { - bool mInUse; - al::FlexArray mBuffer; - - WetBuffer(size_t count) : mBuffer{count} { } - - DEF_FAM_NEWDEL(WetBuffer, mBuffer) -}; -using WetBufferPtr = std::unique_ptr; - - -struct ContextProps { - float DopplerFactor; - float DopplerVelocity; - float SpeedOfSound; - bool SourceDistanceModel; - DistanceModel mDistanceModel; - - std::atomic next; - - DEF_NEWDEL(ContextProps) -}; - -struct ListenerProps { - std::array Position; - std::array Velocity; - std::array OrientAt; - std::array OrientUp; - float Gain; - float MetersPerUnit; - - std::atomic next; - - DEF_NEWDEL(ListenerProps) -}; - -struct ContextParams { - /* Pointer to the most recent property values that are awaiting an update. */ - std::atomic ContextUpdate{nullptr}; - std::atomic ListenerUpdate{nullptr}; - - alu::Matrix Matrix{alu::Matrix::Identity()}; - alu::Vector Velocity{}; - - float Gain{1.0f}; - float MetersPerUnit{1.0f}; - - float DopplerFactor{1.0f}; - float SpeedOfSound{343.3f}; /* in units per sec! */ - - bool SourceDistanceModel{false}; - DistanceModel mDistanceModel{}; -}; - -struct ContextBase { - DeviceBase *const mDevice; - - /* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit - * indicates if updates are currently happening). - */ - RefCount mUpdateCount{0u}; - std::atomic mHoldUpdates{false}; - std::atomic mStopVoicesOnDisconnect{true}; - - float mGainBoost{1.0f}; - - /* Linked lists of unused property containers, free to use for future - * updates. - */ - std::atomic mFreeContextProps{nullptr}; - std::atomic mFreeListenerProps{nullptr}; - std::atomic mFreeVoiceProps{nullptr}; - std::atomic mFreeEffectslotProps{nullptr}; - - /* The voice change tail is the beginning of the "free" elements, up to and - * *excluding* the current. If tail==current, there's no free elements and - * new ones need to be allocated. The current voice change is the element - * last processed, and any after are pending. - */ - VoiceChange *mVoiceChangeTail{}; - std::atomic mCurrentVoiceChange{}; - - void allocVoiceChanges(size_t addcount); - - - ContextParams mParams; - - using VoiceArray = al::FlexArray; - std::atomic mVoices{}; - std::atomic mActiveVoiceCount{}; - - void allocVoices(size_t addcount); - al::span getVoicesSpan() const noexcept - { - return {mVoices.load(std::memory_order_relaxed)->data(), - mActiveVoiceCount.load(std::memory_order_relaxed)}; - } - al::span getVoicesSpanAcquired() const noexcept - { - return {mVoices.load(std::memory_order_acquire)->data(), - mActiveVoiceCount.load(std::memory_order_acquire)}; - } - - - using EffectSlotArray = al::FlexArray; - std::atomic mActiveAuxSlots{nullptr}; - - std::thread mEventThread; - al::semaphore mEventSem; - std::unique_ptr mAsyncEvents; - std::atomic mEnabledEvts{0u}; - - /* Asynchronous voice change actions are processed as a linked list of - * VoiceChange objects by the mixer, which is atomically appended to. - * However, to avoid allocating each object individually, they're allocated - * in clusters that are stored in a vector for easy automatic cleanup. - */ - using VoiceChangeCluster = std::unique_ptr; - al::vector mVoiceChangeClusters; - - using VoiceCluster = std::unique_ptr; - al::vector mVoiceClusters; - - - ContextBase(DeviceBase *device); - ContextBase(const ContextBase&) = delete; - ContextBase& operator=(const ContextBase&) = delete; - ~ContextBase(); -}; - struct SourceSubList { uint64_t FreeMask{~0_u64}; ALsource *Sources{nullptr}; /* 64 */ diff --git a/alc/alu.cpp b/alc/alu.cpp index d7f4410f..250d4ee4 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -39,7 +39,6 @@ #include #include -#include "alcontext.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -53,6 +52,7 @@ #include "core/bsinc_defs.h" #include "core/bsinc_tables.h" #include "core/bufferline.h" +#include "core/context.h" #include "core/cpu_caps.h" #include "core/devformat.h" #include "core/device.h" @@ -66,6 +66,7 @@ #include "core/mixer/hrtfdefs.h" #include "core/resampler_limits.h" #include "core/uhjfilter.h" +#include "core/voice_change.h" #include "effects/base.h" #include "effectslot.h" #include "intrusive_ptr.h" @@ -77,7 +78,6 @@ #include "vecmat.h" #include "vector.h" #include "voice.h" -#include "voice_change.h" struct CTag; #ifdef HAVE_SSE diff --git a/alc/alu.h b/alc/alu.h index 3ca0c6b6..67c7c410 100644 --- a/alc/alu.h +++ b/alc/alu.h @@ -1,21 +1,13 @@ #ifndef ALU_H #define ALU_H -#include - -#include "aloptional.h" - struct ALCcontext; struct ALCdevice; struct EffectSlot; -#define MAX_SENDS 6 - - constexpr float GainMixMax{1000.0f}; /* +60dB */ -constexpr float SpeedOfSoundMetersPerSec{343.3f}; constexpr float AirAbsorbGainHF{0.99426f}; /* -0.05dB */ @@ -27,8 +19,6 @@ enum HrtfRequestMode { void aluInit(void); -void aluInitMixer(al::optional resampler); - /* aluInitRenderer * * Set up the appropriate panning method and mixing method given the device diff --git a/alc/voice.cpp b/alc/voice.cpp index 6abfcf59..686f4c9b 100644 --- a/alc/voice.cpp +++ b/alc/voice.cpp @@ -22,8 +22,6 @@ #include "voice.h" -#include - #include #include #include @@ -32,19 +30,19 @@ #include #include #include +#include #include #include #include "albyte.h" -#include "alcontext.h" #include "alnumeric.h" #include "aloptional.h" #include "alspan.h" #include "alstring.h" -#include "alu.h" #include "buffer_storage.h" #include "core/ambidefs.h" #include "core/async_event.h" +#include "core/context.h" #include "core/cpu_caps.h" #include "core/devformat.h" #include "core/device.h" @@ -57,10 +55,10 @@ #include "core/mixer/defs.h" #include "core/mixer/hrtfdefs.h" #include "core/resampler_limits.h" +#include "core/voice_change.h" #include "opthelpers.h" #include "ringbuffer.h" #include "vector.h" -#include "voice_change.h" struct CTag; #ifdef HAVE_SSE @@ -78,6 +76,8 @@ Resampler ResamplerDefault{Resampler::Linear}; namespace { +using uint = unsigned int; + using HrtfMixerFunc = void(*)(const float *InSamples, float2 *AccumSamples, const uint IrSize, const MixHrtfFilter *hrtfparams, const size_t BufferSize); using HrtfMixerBlendFunc = void(*)(const float *InSamples, float2 *AccumSamples, diff --git a/alc/voice.h b/alc/voice.h index dfeffec1..937294e3 100644 --- a/alc/voice.h +++ b/alc/voice.h @@ -6,11 +6,12 @@ #include #include #include +#include #include "albyte.h" #include "almalloc.h" +#include "aloptional.h" #include "alspan.h" -#include "alu.h" #include "buffer_storage.h" #include "core/bufferline.h" #include "core/devformat.h" @@ -31,6 +32,9 @@ enum class DistanceModel : unsigned char; using uint = unsigned int; +#define MAX_SENDS 6 + + enum class SpatializeMode : unsigned char { Off, On, @@ -262,4 +266,6 @@ struct Voice { extern Resampler ResamplerDefault; +void aluInitMixer(al::optional resampler); + #endif /* VOICE_H */ diff --git a/alc/voice_change.h b/alc/voice_change.h deleted file mode 100644 index ddc6186f..00000000 --- a/alc/voice_change.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef VOICE_CHANGE_H -#define VOICE_CHANGE_H - -#include - -#include "almalloc.h" - -struct Voice; - -using uint = unsigned int; - - -enum class VChangeState { - Reset, - Stop, - Play, - Pause, - Restart -}; -struct VoiceChange { - Voice *mOldVoice{nullptr}; - Voice *mVoice{nullptr}; - uint mSourceID{0}; - VChangeState mState{}; - - std::atomic mNext{nullptr}; - - DEF_NEWDEL(VoiceChange) -}; - -#endif /* VOICE_CHANGE_H */ diff --git a/core/context.cpp b/core/context.cpp new file mode 100644 index 00000000..f1c310aa --- /dev/null +++ b/core/context.cpp @@ -0,0 +1,5 @@ + +#include "config.h" + +#include "context.h" + diff --git a/core/context.h b/core/context.h new file mode 100644 index 00000000..bf439053 --- /dev/null +++ b/core/context.h @@ -0,0 +1,171 @@ +#ifndef CORE_CONTEXT_H +#define CORE_CONTEXT_H + +#include +#include +#include +#include +#include + +#include "almalloc.h" +#include "alspan.h" +#include "atomic.h" +#include "core/bufferline.h" +#include "threads.h" +#include "vecmat.h" +#include "vector.h" + +struct DeviceBase; +struct EffectSlot; +struct EffectSlotProps; +struct RingBuffer; +struct Voice; +struct VoiceChange; +struct VoicePropsItem; + +using uint = unsigned int; + + +constexpr float SpeedOfSoundMetersPerSec{343.3f}; + +enum class DistanceModel : unsigned char { + Disable, + Inverse, InverseClamped, + Linear, LinearClamped, + Exponent, ExponentClamped, + + Default = InverseClamped +}; + + +struct WetBuffer { + bool mInUse; + al::FlexArray mBuffer; + + WetBuffer(size_t count) : mBuffer{count} { } + + DEF_FAM_NEWDEL(WetBuffer, mBuffer) +}; +using WetBufferPtr = std::unique_ptr; + + +struct ContextProps { + float DopplerFactor; + float DopplerVelocity; + float SpeedOfSound; + bool SourceDistanceModel; + DistanceModel mDistanceModel; + + std::atomic next; + + DEF_NEWDEL(ContextProps) +}; + +struct ListenerProps { + std::array Position; + std::array Velocity; + std::array OrientAt; + std::array OrientUp; + float Gain; + float MetersPerUnit; + + std::atomic next; + + DEF_NEWDEL(ListenerProps) +}; + +struct ContextParams { + /* Pointer to the most recent property values that are awaiting an update. */ + std::atomic ContextUpdate{nullptr}; + std::atomic ListenerUpdate{nullptr}; + + alu::Matrix Matrix{alu::Matrix::Identity()}; + alu::Vector Velocity{}; + + float Gain{1.0f}; + float MetersPerUnit{1.0f}; + + float DopplerFactor{1.0f}; + float SpeedOfSound{343.3f}; /* in units per sec! */ + + bool SourceDistanceModel{false}; + DistanceModel mDistanceModel{}; +}; + +struct ContextBase { + DeviceBase *const mDevice; + + /* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit + * indicates if updates are currently happening). + */ + RefCount mUpdateCount{0u}; + std::atomic mHoldUpdates{false}; + std::atomic mStopVoicesOnDisconnect{true}; + + float mGainBoost{1.0f}; + + /* Linked lists of unused property containers, free to use for future + * updates. + */ + std::atomic mFreeContextProps{nullptr}; + std::atomic mFreeListenerProps{nullptr}; + std::atomic mFreeVoiceProps{nullptr}; + std::atomic mFreeEffectslotProps{nullptr}; + + /* The voice change tail is the beginning of the "free" elements, up to and + * *excluding* the current. If tail==current, there's no free elements and + * new ones need to be allocated. The current voice change is the element + * last processed, and any after are pending. + */ + VoiceChange *mVoiceChangeTail{}; + std::atomic mCurrentVoiceChange{}; + + void allocVoiceChanges(size_t addcount); + + + ContextParams mParams; + + using VoiceArray = al::FlexArray; + std::atomic mVoices{}; + std::atomic mActiveVoiceCount{}; + + void allocVoices(size_t addcount); + al::span getVoicesSpan() const noexcept + { + return {mVoices.load(std::memory_order_relaxed)->data(), + mActiveVoiceCount.load(std::memory_order_relaxed)}; + } + al::span getVoicesSpanAcquired() const noexcept + { + return {mVoices.load(std::memory_order_acquire)->data(), + mActiveVoiceCount.load(std::memory_order_acquire)}; + } + + + using EffectSlotArray = al::FlexArray; + std::atomic mActiveAuxSlots{nullptr}; + + std::thread mEventThread; + al::semaphore mEventSem; + std::unique_ptr mAsyncEvents; + std::atomic mEnabledEvts{0u}; + + /* Asynchronous voice change actions are processed as a linked list of + * VoiceChange objects by the mixer, which is atomically appended to. + * However, to avoid allocating each object individually, they're allocated + * in clusters that are stored in a vector for easy automatic cleanup. + */ + using VoiceChangeCluster = std::unique_ptr; + al::vector mVoiceChangeClusters; + + using VoiceCluster = std::unique_ptr; + al::vector mVoiceClusters; + + + ContextBase(DeviceBase *device); + ContextBase(const ContextBase&) = delete; + ContextBase& operator=(const ContextBase&) = delete; + ~ContextBase(); +}; + +#endif /* CORE_CONTEXT_H */ diff --git a/core/voice_change.h b/core/voice_change.h new file mode 100644 index 00000000..ddc6186f --- /dev/null +++ b/core/voice_change.h @@ -0,0 +1,31 @@ +#ifndef VOICE_CHANGE_H +#define VOICE_CHANGE_H + +#include + +#include "almalloc.h" + +struct Voice; + +using uint = unsigned int; + + +enum class VChangeState { + Reset, + Stop, + Play, + Pause, + Restart +}; +struct VoiceChange { + Voice *mOldVoice{nullptr}; + Voice *mVoice{nullptr}; + uint mSourceID{0}; + VChangeState mState{}; + + std::atomic mNext{nullptr}; + + DEF_NEWDEL(VoiceChange) +}; + +#endif /* VOICE_CHANGE_H */ -- cgit v1.2.3 From ff380298e4086490584707b8ffde44c5ad64830f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 27 Apr 2021 08:26:42 -0700 Subject: Move BufferStorage and Voice to core --- CMakeLists.txt | 10 +- al/buffer.cpp | 4 +- al/buffer.h | 2 +- al/source.h | 2 +- al/state.cpp | 2 +- alc/alc.cpp | 2 +- alc/alu.cpp | 4 +- alc/buffer_storage.cpp | 41 --- alc/buffer_storage.h | 75 ---- alc/effects/convolution.cpp | 2 +- alc/voice.cpp | 869 -------------------------------------------- alc/voice.h | 271 -------------- core/buffer_storage.cpp | 41 +++ core/buffer_storage.h | 75 ++++ core/voice.cpp | 849 +++++++++++++++++++++++++++++++++++++++++++ core/voice.h | 270 ++++++++++++++ 16 files changed, 1249 insertions(+), 1270 deletions(-) delete mode 100644 alc/buffer_storage.cpp delete mode 100644 alc/buffer_storage.h delete mode 100644 alc/voice.cpp delete mode 100644 alc/voice.h create mode 100644 core/buffer_storage.cpp create mode 100644 core/buffer_storage.h create mode 100644 core/voice.cpp create mode 100644 core/voice.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index ef4ef720..11c3975b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -655,6 +655,8 @@ set(CORE_OBJS core/bsinc_tables.cpp core/bsinc_tables.h core/bufferline.h + core/buffer_storage.cpp + core/buffer_storage.h core/context.cpp core/context.h core/converter.cpp @@ -692,6 +694,8 @@ set(CORE_OBJS core/uhjfilter.cpp core/uhjfilter.h core/uiddefs.cpp + core/voice.cpp + core/voice.h core/voice_change.h) set(HAVE_RTKIT 0) @@ -765,8 +769,6 @@ set(ALC_OBJS alc/alconfig.cpp alc/alconfig.h alc/alcontext.h - alc/buffer_storage.cpp - alc/buffer_storage.h alc/effectslot.cpp alc/effectslot.h alc/effects/base.h @@ -785,9 +787,7 @@ set(ALC_OBJS alc/effects/reverb.cpp alc/effects/vmorpher.cpp alc/inprogext.h - alc/panning.cpp - alc/voice.cpp - alc/voice.h) + alc/panning.cpp) # Include SIMD mixers set(CPU_EXTS "Default") diff --git a/al/buffer.cpp b/al/buffer.cpp index 906b40fc..21f46fc8 100644 --- a/al/buffer.cpp +++ b/al/buffer.cpp @@ -50,10 +50,10 @@ #include "aloptional.h" #include "atomic.h" #include "core/except.h" -#include "inprogext.h" #include "core/logging.h" +#include "core/voice.h" +#include "inprogext.h" #include "opthelpers.h" -#include "voice.h" namespace { diff --git a/al/buffer.h b/al/buffer.h index a9bc0e71..fe37b0af 100644 --- a/al/buffer.h +++ b/al/buffer.h @@ -8,7 +8,7 @@ #include "albyte.h" #include "almalloc.h" #include "atomic.h" -#include "buffer_storage.h" +#include "core/buffer_storage.h" #include "inprogext.h" #include "vector.h" diff --git a/al/source.h b/al/source.h index 4f4832e5..2063068a 100644 --- a/al/source.h +++ b/al/source.h @@ -17,9 +17,9 @@ #include "alnumeric.h" #include "alu.h" #include "atomic.h" +#include "core/voice.h" #include "math_defs.h" #include "vector.h" -#include "voice.h" struct ALbuffer; struct ALeffectslot; diff --git a/al/state.cpp b/al/state.cpp index 86a85b4d..950d64fc 100644 --- a/al/state.cpp +++ b/al/state.cpp @@ -40,11 +40,11 @@ #include "alu.h" #include "atomic.h" #include "core/except.h" +#include "core/voice.h" #include "event.h" #include "inprogext.h" #include "opthelpers.h" #include "strutils.h" -#include "voice.h" namespace { diff --git a/alc/alc.cpp b/alc/alc.cpp index 858fe278..1f7b999d 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -1131,7 +1131,7 @@ void alc_initconfig(void) AllowRTTimeLimit = *limopt; aluInit(); - aluInitMixer(ConfigValueStr(nullptr, nullptr, "resampler")); + Voice::InitMixer(ConfigValueStr(nullptr, nullptr, "resampler")); auto traperr = al::getenv("ALSOFT_TRAP_ERROR"); if(traperr && (al::strcasecmp(traperr->c_str(), "true") == 0 diff --git a/alc/alu.cpp b/alc/alu.cpp index 250d4ee4..d370aba7 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -44,7 +44,6 @@ #include "alspan.h" #include "alstring.h" #include "atomic.h" -#include "buffer_storage.h" #include "core/ambidefs.h" #include "core/async_event.h" #include "core/bformatdec.h" @@ -52,6 +51,7 @@ #include "core/bsinc_defs.h" #include "core/bsinc_tables.h" #include "core/bufferline.h" +#include "core/buffer_storage.h" #include "core/context.h" #include "core/cpu_caps.h" #include "core/devformat.h" @@ -66,6 +66,7 @@ #include "core/mixer/hrtfdefs.h" #include "core/resampler_limits.h" #include "core/uhjfilter.h" +#include "core/voice.h" #include "core/voice_change.h" #include "effects/base.h" #include "effectslot.h" @@ -77,7 +78,6 @@ #include "threads.h" #include "vecmat.h" #include "vector.h" -#include "voice.h" struct CTag; #ifdef HAVE_SSE diff --git a/alc/buffer_storage.cpp b/alc/buffer_storage.cpp deleted file mode 100644 index 3eb1e28a..00000000 --- a/alc/buffer_storage.cpp +++ /dev/null @@ -1,41 +0,0 @@ - -#include "config.h" - -#include "buffer_storage.h" - -#include - - -uint BytesFromFmt(FmtType type) noexcept -{ - switch(type) - { - case FmtUByte: return sizeof(uint8_t); - case FmtShort: return sizeof(int16_t); - case FmtFloat: return sizeof(float); - case FmtDouble: return sizeof(double); - case FmtMulaw: return sizeof(uint8_t); - case FmtAlaw: return sizeof(uint8_t); - } - return 0; -} - -uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept -{ - switch(chans) - { - case FmtMono: return 1; - case FmtStereo: return 2; - case FmtRear: return 2; - case FmtQuad: return 4; - case FmtX51: return 6; - case FmtX61: return 7; - case FmtX71: return 8; - case FmtBFormat2D: return (ambiorder*2) + 1; - case FmtBFormat3D: return (ambiorder+1) * (ambiorder+1); - case FmtUHJ2: return 2; - case FmtUHJ3: return 3; - case FmtUHJ4: return 4; - } - return 0; -} diff --git a/alc/buffer_storage.h b/alc/buffer_storage.h deleted file mode 100644 index 733a62e3..00000000 --- a/alc/buffer_storage.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef ALC_BUFFER_STORAGE_H -#define ALC_BUFFER_STORAGE_H - -#include - -#include "albyte.h" - - -using uint = unsigned int; - -/* Storable formats */ -enum FmtType : unsigned char { - FmtUByte, - FmtShort, - FmtFloat, - FmtDouble, - FmtMulaw, - FmtAlaw, -}; -enum FmtChannels : unsigned char { - FmtMono, - FmtStereo, - FmtRear, - FmtQuad, - FmtX51, /* (WFX order) */ - FmtX61, /* (WFX order) */ - FmtX71, /* (WFX order) */ - FmtBFormat2D, - FmtBFormat3D, - FmtUHJ2, /* 2-channel UHJ, aka "BHJ", stereo-compatible */ - FmtUHJ3, /* 3-channel UHJ, aka "THJ", first-two channels are stereo-compatible */ - FmtUHJ4, /* 4-channel UHJ, aka "PHJ", first-two channels are stereo-compatible */ -}; - -enum class AmbiLayout : unsigned char { - FuMa, - ACN, -}; -enum class AmbiScaling : unsigned char { - FuMa, - SN3D, - N3D, -}; - -uint BytesFromFmt(FmtType type) noexcept; -uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept; -inline uint FrameSizeFromFmt(FmtChannels chans, FmtType type, uint ambiorder) noexcept -{ return ChannelsFromFmt(chans, ambiorder) * BytesFromFmt(type); } - - -using CallbackType = int(*)(void*, void*, int); - -struct BufferStorage { - CallbackType mCallback{nullptr}; - void *mUserData{nullptr}; - - uint mSampleRate{0u}; - FmtChannels mChannels{FmtMono}; - FmtType mType{FmtShort}; - uint mSampleLen{0u}; - - AmbiLayout mAmbiLayout{AmbiLayout::FuMa}; - AmbiScaling mAmbiScaling{AmbiScaling::FuMa}; - uint mAmbiOrder{0u}; - - inline uint bytesFromFmt() const noexcept { return BytesFromFmt(mType); } - inline uint channelsFromFmt() const noexcept - { return ChannelsFromFmt(mChannels, mAmbiOrder); } - inline uint frameSizeFromFmt() const noexcept { return channelsFromFmt() * bytesFromFmt(); } - - inline bool isBFormat() const noexcept - { return mChannels == FmtBFormat2D || mChannels == FmtBFormat3D; } -}; - -#endif /* ALC_BUFFER_STORAGE_H */ diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index 90907a40..b460904b 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -23,10 +23,10 @@ #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" -#include "buffer_storage.h" #include "config.h" #include "core/ambidefs.h" #include "core/bufferline.h" +#include "core/buffer_storage.h" #include "core/devformat.h" #include "core/device.h" #include "core/filters/splitter.h" diff --git a/alc/voice.cpp b/alc/voice.cpp deleted file mode 100644 index 686f4c9b..00000000 --- a/alc/voice.cpp +++ /dev/null @@ -1,869 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 1999-2007 by authors. - * 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 "voice.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "albyte.h" -#include "alnumeric.h" -#include "aloptional.h" -#include "alspan.h" -#include "alstring.h" -#include "buffer_storage.h" -#include "core/ambidefs.h" -#include "core/async_event.h" -#include "core/context.h" -#include "core/cpu_caps.h" -#include "core/devformat.h" -#include "core/device.h" -#include "core/filters/biquad.h" -#include "core/filters/nfc.h" -#include "core/filters/splitter.h" -#include "core/fmt_traits.h" -#include "core/logging.h" -#include "core/mixer.h" -#include "core/mixer/defs.h" -#include "core/mixer/hrtfdefs.h" -#include "core/resampler_limits.h" -#include "core/voice_change.h" -#include "opthelpers.h" -#include "ringbuffer.h" -#include "vector.h" - -struct CTag; -#ifdef HAVE_SSE -struct SSETag; -#endif -#ifdef HAVE_NEON -struct NEONTag; -#endif -struct CopyTag; - - -static_assert(!(sizeof(Voice::BufferLine)&15), "Voice::BufferLine must be a multiple of 16 bytes"); - -Resampler ResamplerDefault{Resampler::Linear}; - -namespace { - -using uint = unsigned int; - -using HrtfMixerFunc = void(*)(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize); -using HrtfMixerBlendFunc = void(*)(const float *InSamples, float2 *AccumSamples, - const uint IrSize, const HrtfFilter *oldparams, const MixHrtfFilter *newparams, - const size_t BufferSize); - -HrtfMixerFunc MixHrtfSamples{MixHrtf_}; -HrtfMixerBlendFunc MixHrtfBlendSamples{MixHrtfBlend_}; - -inline MixerFunc SelectMixer() -{ -#ifdef HAVE_NEON - if((CPUCapFlags&CPU_CAP_NEON)) - return Mix_; -#endif -#ifdef HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) - return Mix_; -#endif - return Mix_; -} - -inline HrtfMixerFunc SelectHrtfMixer() -{ -#ifdef HAVE_NEON - if((CPUCapFlags&CPU_CAP_NEON)) - return MixHrtf_; -#endif -#ifdef HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) - return MixHrtf_; -#endif - return MixHrtf_; -} - -inline HrtfMixerBlendFunc SelectHrtfBlendMixer() -{ -#ifdef HAVE_NEON - if((CPUCapFlags&CPU_CAP_NEON)) - return MixHrtfBlend_; -#endif -#ifdef HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) - return MixHrtfBlend_; -#endif - return MixHrtfBlend_; -} - -} // namespace - - -void aluInitMixer(al::optional resampler) -{ - if(resampler) - { - struct ResamplerEntry { - const char name[16]; - const Resampler resampler; - }; - constexpr ResamplerEntry ResamplerList[]{ - { "none", Resampler::Point }, - { "point", Resampler::Point }, - { "linear", Resampler::Linear }, - { "cubic", Resampler::Cubic }, - { "bsinc12", Resampler::BSinc12 }, - { "fast_bsinc12", Resampler::FastBSinc12 }, - { "bsinc24", Resampler::BSinc24 }, - { "fast_bsinc24", Resampler::FastBSinc24 }, - }; - - const char *str{resampler->c_str()}; - if(al::strcasecmp(str, "bsinc") == 0) - { - WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str); - str = "bsinc12"; - } - else if(al::strcasecmp(str, "sinc4") == 0 || al::strcasecmp(str, "sinc8") == 0) - { - WARN("Resampler option \"%s\" is deprecated, using cubic\n", str); - str = "cubic"; - } - - auto iter = std::find_if(std::begin(ResamplerList), std::end(ResamplerList), - [str](const ResamplerEntry &entry) -> bool - { return al::strcasecmp(str, entry.name) == 0; }); - if(iter == std::end(ResamplerList)) - ERR("Invalid resampler: %s\n", str); - else - ResamplerDefault = iter->resampler; - } - - MixSamples = SelectMixer(); - MixHrtfBlendSamples = SelectHrtfBlendMixer(); - MixHrtfSamples = SelectHrtfMixer(); -} - - -namespace { - -void SendSourceStoppedEvent(ContextBase *context, uint id) -{ - RingBuffer *ring{context->mAsyncEvents.get()}; - auto evt_vec = ring->getWriteVector(); - if(evt_vec.first.len < 1) return; - - AsyncEvent *evt{::new(evt_vec.first.buf) AsyncEvent{EventType_SourceStateChange}}; - evt->u.srcstate.id = id; - evt->u.srcstate.state = AsyncEvent::SrcState::Stop; - - ring->writeAdvance(1); -} - - -const float *DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, float *dst, - const al::span src, int type) -{ - switch(type) - { - case AF_None: - lpfilter.clear(); - hpfilter.clear(); - break; - - case AF_LowPass: - lpfilter.process(src, dst); - hpfilter.clear(); - return dst; - case AF_HighPass: - lpfilter.clear(); - hpfilter.process(src, dst); - return dst; - - case AF_BandPass: - DualBiquad{lpfilter, hpfilter}.process(src, dst); - return dst; - } - return src.data(); -} - - -void LoadSamples(const al::span dstSamples, const size_t dstOffset, - const al::byte *src, const size_t srcOffset, const FmtType srctype, const FmtChannels srcchans, - const size_t samples) noexcept -{ -#define HANDLE_FMT(T) case T: \ - { \ - constexpr size_t sampleSize{sizeof(al::FmtTypeTraits::Type)}; \ - if(srcchans == FmtUHJ2) \ - { \ - constexpr size_t srcstep{2u}; \ - src += srcOffset*srcstep*sampleSize; \ - al::LoadSampleArray(dstSamples[0].data() + dstOffset, src, \ - srcstep, samples); \ - al::LoadSampleArray(dstSamples[1].data() + dstOffset, \ - src + sampleSize, srcstep, samples); \ - std::fill_n(dstSamples[2].data() + dstOffset, samples, 0.0f); \ - } \ - else \ - { \ - const size_t srcstep{dstSamples.size()}; \ - src += srcOffset*srcstep*sampleSize; \ - for(auto &dst : dstSamples) \ - { \ - al::LoadSampleArray(dst.data() + dstOffset, src, srcstep, \ - samples); \ - src += sampleSize; \ - } \ - } \ - } \ - break - - switch(srctype) - { - HANDLE_FMT(FmtUByte); - HANDLE_FMT(FmtShort); - HANDLE_FMT(FmtFloat); - HANDLE_FMT(FmtDouble); - HANDLE_FMT(FmtMulaw); - HANDLE_FMT(FmtAlaw); - } -#undef HANDLE_FMT -} - -void LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, - const size_t dataPosInt, const FmtType sampleType, const FmtChannels sampleChannels, - const size_t samplesToLoad, const al::span voiceSamples) -{ - const uint loopStart{buffer->mLoopStart}; - const uint loopEnd{buffer->mLoopEnd}; - ASSUME(loopEnd > loopStart); - - /* If current pos is beyond the loop range, do not loop */ - if(!bufferLoopItem || dataPosInt >= loopEnd) - { - /* Load what's left to play from the buffer */ - const size_t remaining{minz(samplesToLoad, buffer->mSampleLen-dataPosInt)}; - LoadSamples(voiceSamples, MaxResamplerEdge, buffer->mSamples, dataPosInt, sampleType, - sampleChannels, remaining); - - if(const size_t toFill{samplesToLoad - remaining}) - { - for(auto &chanbuffer : voiceSamples) - { - auto srcsamples = chanbuffer.data() + MaxResamplerEdge - 1 + remaining; - std::fill_n(srcsamples + 1, toFill, *srcsamples); - } - } - } - else - { - /* Load what's left of this loop iteration */ - const size_t remaining{minz(samplesToLoad, loopEnd-dataPosInt)}; - LoadSamples(voiceSamples, MaxResamplerEdge, buffer->mSamples, dataPosInt, sampleType, - sampleChannels, remaining); - - /* Load repeats of the loop to fill the buffer. */ - const auto loopSize = static_cast(loopEnd - loopStart); - size_t samplesLoaded{remaining}; - while(const size_t toFill{minz(samplesToLoad - samplesLoaded, loopSize)}) - { - LoadSamples(voiceSamples, MaxResamplerEdge + samplesLoaded, buffer->mSamples, - loopStart, sampleType, sampleChannels, toFill); - samplesLoaded += toFill; - } - } -} - -void LoadBufferCallback(VoiceBufferItem *buffer, const size_t numCallbackSamples, - const FmtType sampleType, const FmtChannels sampleChannels, const size_t samplesToLoad, - const al::span voiceSamples) -{ - /* Load what's left to play from the buffer */ - const size_t remaining{minz(samplesToLoad, numCallbackSamples)}; - LoadSamples(voiceSamples, MaxResamplerEdge, buffer->mSamples, 0, sampleType, sampleChannels, - remaining); - - if(const size_t toFill{samplesToLoad - remaining}) - { - for(auto &chanbuffer : voiceSamples) - { - auto srcsamples = chanbuffer.data() + MaxResamplerEdge - 1 + remaining; - std::fill_n(srcsamples + 1, toFill, *srcsamples); - } - } -} - -void LoadBufferQueue(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, - size_t dataPosInt, const FmtType sampleType, const FmtChannels sampleChannels, - const size_t samplesToLoad, const al::span voiceSamples) -{ - /* Crawl the buffer queue to fill in the temp buffer */ - size_t samplesLoaded{0}; - while(buffer && samplesLoaded != samplesToLoad) - { - if(dataPosInt >= buffer->mSampleLen) - { - dataPosInt -= buffer->mSampleLen; - buffer = buffer->mNext.load(std::memory_order_acquire); - if(!buffer) buffer = bufferLoopItem; - continue; - } - - const size_t remaining{minz(samplesToLoad-samplesLoaded, buffer->mSampleLen-dataPosInt)}; - LoadSamples(voiceSamples, MaxResamplerEdge+samplesLoaded, buffer->mSamples, dataPosInt, - sampleType, sampleChannels, remaining); - - samplesLoaded += remaining; - if(samplesLoaded == samplesToLoad) - break; - - dataPosInt = 0; - buffer = buffer->mNext.load(std::memory_order_acquire); - if(!buffer) buffer = bufferLoopItem; - } - if(const size_t toFill{samplesToLoad - samplesLoaded}) - { - size_t chanidx{0}; - for(auto &chanbuffer : voiceSamples) - { - auto srcsamples = chanbuffer.data() + MaxResamplerEdge - 1 + samplesLoaded; - std::fill_n(srcsamples + 1, toFill, *srcsamples); - ++chanidx; - } - } -} - - -void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &parms, - const float TargetGain, const uint Counter, uint OutPos, DeviceBase *Device) -{ - const uint IrSize{Device->mIrSize}; - auto &HrtfSamples = Device->HrtfSourceData; - /* Source HRTF mixing needs to include the direct delay so it remains - * aligned with the direct mix's HRTF filtering. - */ - float2 *AccumSamples{Device->HrtfAccumData + HrtfDirectDelay}; - - /* Copy the HRTF history and new input samples into a temp buffer. */ - auto src_iter = std::copy(parms.Hrtf.History.begin(), parms.Hrtf.History.end(), - std::begin(HrtfSamples)); - std::copy_n(samples, DstBufferSize, src_iter); - /* Copy the last used samples back into the history buffer for later. */ - std::copy_n(std::begin(HrtfSamples) + DstBufferSize, parms.Hrtf.History.size(), - parms.Hrtf.History.begin()); - - /* If fading and this is the first mixing pass, fade between the IRs. */ - uint fademix{0u}; - if(Counter && OutPos == 0) - { - fademix = minu(DstBufferSize, Counter); - - float gain{TargetGain}; - - /* The new coefficients need to fade in completely since they're - * replacing the old ones. To keep the gain fading consistent, - * interpolate between the old and new target gains given how much of - * the fade time this mix handles. - */ - if(Counter > fademix) - { - const float a{static_cast(fademix) / static_cast(Counter)}; - gain = lerp(parms.Hrtf.Old.Gain, TargetGain, a); - } - - MixHrtfFilter hrtfparams{ - parms.Hrtf.Target.Coeffs, - parms.Hrtf.Target.Delay, - 0.0f, gain / static_cast(fademix)}; - MixHrtfBlendSamples(HrtfSamples, AccumSamples+OutPos, IrSize, &parms.Hrtf.Old, &hrtfparams, - fademix); - - /* Update the old parameters with the result. */ - parms.Hrtf.Old = parms.Hrtf.Target; - parms.Hrtf.Old.Gain = gain; - OutPos += fademix; - } - - if(fademix < DstBufferSize) - { - const uint todo{DstBufferSize - fademix}; - float gain{TargetGain}; - - /* Interpolate the target gain if the gain fading lasts longer than - * this mix. - */ - if(Counter > DstBufferSize) - { - const float a{static_cast(todo) / static_cast(Counter-fademix)}; - gain = lerp(parms.Hrtf.Old.Gain, TargetGain, a); - } - - MixHrtfFilter hrtfparams{ - parms.Hrtf.Target.Coeffs, - parms.Hrtf.Target.Delay, - parms.Hrtf.Old.Gain, - (gain - parms.Hrtf.Old.Gain) / static_cast(todo)}; - MixHrtfSamples(HrtfSamples+fademix, AccumSamples+OutPos, IrSize, &hrtfparams, todo); - - /* Store the now-current gain for next time. */ - parms.Hrtf.Old.Gain = gain; - } -} - -void DoNfcMix(const al::span samples, FloatBufferLine *OutBuffer, DirectParams &parms, - const float *TargetGains, const uint Counter, const uint OutPos, DeviceBase *Device) -{ - using FilterProc = void (NfcFilter::*)(const al::span, float*); - static constexpr FilterProc NfcProcess[MaxAmbiOrder+1]{ - nullptr, &NfcFilter::process1, &NfcFilter::process2, &NfcFilter::process3}; - - float *CurrentGains{parms.Gains.Current.data()}; - MixSamples(samples, {OutBuffer, 1u}, CurrentGains, TargetGains, Counter, OutPos); - ++OutBuffer; - ++CurrentGains; - ++TargetGains; - - const al::span nfcsamples{Device->NfcSampleData, samples.size()}; - size_t order{1}; - while(const size_t chancount{Device->NumChannelsPerOrder[order]}) - { - (parms.NFCtrlFilter.*NfcProcess[order])(samples, nfcsamples.data()); - MixSamples(nfcsamples, {OutBuffer, chancount}, CurrentGains, TargetGains, Counter, OutPos); - OutBuffer += chancount; - CurrentGains += chancount; - TargetGains += chancount; - if(++order == MaxAmbiOrder+1) - break; - } -} - -} // namespace - -void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo) -{ - static constexpr std::array SilentTarget{}; - - ASSUME(SamplesToDo > 0); - - /* Get voice info */ - uint DataPosInt{mPosition.load(std::memory_order_relaxed)}; - uint DataPosFrac{mPositionFrac.load(std::memory_order_relaxed)}; - VoiceBufferItem *BufferListItem{mCurrentBuffer.load(std::memory_order_relaxed)}; - VoiceBufferItem *BufferLoopItem{mLoopBuffer.load(std::memory_order_relaxed)}; - const uint increment{mStep}; - if UNLIKELY(increment < 1) - { - /* If the voice is supposed to be stopping but can't be mixed, just - * stop it before bailing. - */ - if(vstate == Stopping) - mPlayState.store(Stopped, std::memory_order_release); - return; - } - - DeviceBase *Device{Context->mDevice}; - const uint NumSends{Device->NumAuxSends}; - - ResamplerFunc Resample{(increment == MixerFracOne && DataPosFrac == 0) ? - Resample_ : mResampler}; - - uint Counter{(mFlags&VoiceIsFading) ? SamplesToDo : 0}; - if(!Counter) - { - /* No fading, just overwrite the old/current params. */ - for(auto &chandata : mChans) - { - { - DirectParams &parms = chandata.mDryParams; - if(!(mFlags&VoiceHasHrtf)) - parms.Gains.Current = parms.Gains.Target; - else - parms.Hrtf.Old = parms.Hrtf.Target; - } - for(uint send{0};send < NumSends;++send) - { - if(mSend[send].Buffer.empty()) - continue; - - SendParams &parms = chandata.mWetParams[send]; - parms.Gains.Current = parms.Gains.Target; - } - } - } - else if UNLIKELY(!BufferListItem) - Counter = std::min(Counter, 64u); - - const uint PostPadding{MaxResamplerEdge + - ((mFmtChannels==FmtUHJ2 || mFmtChannels==FmtUHJ3 || mFmtChannels==FmtUHJ4) - ? uint{UhjDecoder::sFilterDelay} : 0u)}; - uint buffers_done{0u}; - uint OutPos{0u}; - do { - /* Figure out how many buffer samples will be needed */ - uint DstBufferSize{SamplesToDo - OutPos}; - uint SrcBufferSize; - - if(increment <= MixerFracOne) - { - /* Calculate the last written dst sample pos. */ - uint64_t DataSize64{DstBufferSize - 1}; - /* Calculate the last read src sample pos. */ - DataSize64 = (DataSize64*increment + DataPosFrac) >> MixerFracBits; - /* +1 to get the src sample count, include padding. */ - DataSize64 += 1 + PostPadding; - - /* Result is guaranteed to be <= BufferLineSize+ResamplerPrePadding - * since we won't use more src samples than dst samples+padding. - */ - SrcBufferSize = static_cast(DataSize64); - } - else - { - uint64_t DataSize64{DstBufferSize}; - /* Calculate the end src sample pos, include padding. */ - DataSize64 = (DataSize64*increment + DataPosFrac) >> MixerFracBits; - DataSize64 += PostPadding; - - if(DataSize64 <= LineSize - MaxResamplerEdge) - SrcBufferSize = static_cast(DataSize64); - else - { - /* If the source size got saturated, we can't fill the desired - * dst size. Figure out how many samples we can actually mix. - */ - SrcBufferSize = LineSize - MaxResamplerEdge; - - DataSize64 = SrcBufferSize - PostPadding; - DataSize64 = ((DataSize64<(DataSize64) & ~3u; - } - ASSUME(DstBufferSize > 0); - } - } - - if((mFlags&(VoiceIsCallback|VoiceCallbackStopped)) == VoiceIsCallback && BufferListItem) - { - if(SrcBufferSize > mNumCallbackSamples) - { - const size_t byteOffset{mNumCallbackSamples*mFrameSize}; - const size_t needBytes{SrcBufferSize*mFrameSize - byteOffset}; - - const int gotBytes{BufferListItem->mCallback(BufferListItem->mUserData, - &BufferListItem->mSamples[byteOffset], static_cast(needBytes))}; - if(gotBytes < 0) - mFlags |= VoiceCallbackStopped; - else if(static_cast(gotBytes) < needBytes) - { - mFlags |= VoiceCallbackStopped; - mNumCallbackSamples += static_cast(static_cast(gotBytes) / - mFrameSize); - } - else - mNumCallbackSamples = SrcBufferSize; - } - } - - if UNLIKELY(!BufferListItem) - { - for(auto &chanbuffer : mVoiceSamples) - { - auto srciter = chanbuffer.data() + MaxResamplerEdge; - auto srcend = chanbuffer.data() + MaxResamplerPadding; - - /* When loading from a voice that ended prematurely, only take - * the samples that get closest to 0 amplitude. This helps - * certain sounds fade out better. - */ - auto abs_lt = [](const float lhs, const float rhs) noexcept -> bool - { return std::abs(lhs) < std::abs(rhs); }; - srciter = std::min_element(srciter, srcend, abs_lt); - - SrcBufferSize = SrcBufferSize - PostPadding + MaxResamplerPadding; - std::fill(srciter+1, chanbuffer.data() + SrcBufferSize, *srciter); - } - } - else - { - if((mFlags&VoiceIsStatic)) - LoadBufferStatic(BufferListItem, BufferLoopItem, DataPosInt, mFmtType, mFmtChannels, - SrcBufferSize, mVoiceSamples); - else if((mFlags&VoiceIsCallback)) - LoadBufferCallback(BufferListItem, mNumCallbackSamples, mFmtType, mFmtChannels, - SrcBufferSize, mVoiceSamples); - else - LoadBufferQueue(BufferListItem, BufferLoopItem, DataPosInt, mFmtType, mFmtChannels, - SrcBufferSize, mVoiceSamples); - - if(mDecoder) - { - const size_t srcOffset{(increment*DstBufferSize + DataPosFrac)>>MixerFracBits}; - SrcBufferSize = SrcBufferSize - PostPadding + MaxResamplerEdge; - mDecoder->decode(mVoiceSamples, MaxResamplerEdge, SrcBufferSize, srcOffset); - } - } - - auto voiceSamples = mVoiceSamples.begin(); - for(auto &chandata : mChans) - { - /* Resample, then apply ambisonic upsampling as needed. */ - float *ResampledData{Resample(&mResampleState, - voiceSamples->data() + MaxResamplerEdge, DataPosFrac, increment, - {Device->ResampledData, DstBufferSize})}; - if((mFlags&VoiceIsAmbisonic)) - chandata.mAmbiSplitter.processHfScale({ResampledData, DstBufferSize}, - chandata.mAmbiScale); - - /* Now filter and mix to the appropriate outputs. */ - const al::span FilterBuf{Device->FilteredData}; - { - DirectParams &parms = chandata.mDryParams; - const float *samples{DoFilters(parms.LowPass, parms.HighPass, FilterBuf.data(), - {ResampledData, DstBufferSize}, mDirect.FilterType)}; - - if((mFlags&VoiceHasHrtf)) - { - const float TargetGain{UNLIKELY(vstate == Stopping) ? 0.0f : - parms.Hrtf.Target.Gain}; - DoHrtfMix(samples, DstBufferSize, parms, TargetGain, Counter, OutPos, Device); - } - else if((mFlags&VoiceHasNfc)) - { - const float *TargetGains{UNLIKELY(vstate == Stopping) ? SilentTarget.data() - : parms.Gains.Target.data()}; - DoNfcMix({samples, DstBufferSize}, mDirect.Buffer.data(), parms, TargetGains, - Counter, OutPos, Device); - } - else - { - const float *TargetGains{UNLIKELY(vstate == Stopping) ? SilentTarget.data() - : parms.Gains.Target.data()}; - MixSamples({samples, DstBufferSize}, mDirect.Buffer, - parms.Gains.Current.data(), TargetGains, Counter, OutPos); - } - } - - for(uint send{0};send < NumSends;++send) - { - if(mSend[send].Buffer.empty()) - continue; - - SendParams &parms = chandata.mWetParams[send]; - const float *samples{DoFilters(parms.LowPass, parms.HighPass, FilterBuf.data(), - {ResampledData, DstBufferSize}, mSend[send].FilterType)}; - - const float *TargetGains{UNLIKELY(vstate == Stopping) ? SilentTarget.data() - : parms.Gains.Target.data()}; - MixSamples({samples, DstBufferSize}, mSend[send].Buffer, - parms.Gains.Current.data(), TargetGains, Counter, OutPos); - } - - /* Store the last source samples used for next time. */ - const size_t srcOffset{(increment*DstBufferSize + DataPosFrac)>>MixerFracBits}; - std::copy_n(voiceSamples->data()+srcOffset, MaxResamplerPadding, voiceSamples->data()); - ++voiceSamples; - } - /* Update positions */ - DataPosFrac += increment*DstBufferSize; - const uint SrcSamplesDone{DataPosFrac>>MixerFracBits}; - DataPosInt += SrcSamplesDone; - DataPosFrac &= MixerFracMask; - - OutPos += DstBufferSize; - Counter = maxu(DstBufferSize, Counter) - DstBufferSize; - - if UNLIKELY(!BufferListItem) - { - /* Do nothing extra when there's no buffers. */ - } - else if((mFlags&VoiceIsStatic)) - { - if(BufferLoopItem) - { - /* Handle looping static source */ - const uint LoopStart{BufferListItem->mLoopStart}; - const uint LoopEnd{BufferListItem->mLoopEnd}; - if(DataPosInt >= LoopEnd) - { - assert(LoopEnd > LoopStart); - DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart; - } - } - else - { - /* Handle non-looping static source */ - if(DataPosInt >= BufferListItem->mSampleLen) - { - BufferListItem = nullptr; - break; - } - } - } - else if((mFlags&VoiceIsCallback)) - { - if(SrcSamplesDone < mNumCallbackSamples) - { - const size_t byteOffset{SrcSamplesDone*mFrameSize}; - const size_t byteEnd{mNumCallbackSamples*mFrameSize}; - al::byte *data{BufferListItem->mSamples}; - std::copy(data+byteOffset, data+byteEnd, data); - mNumCallbackSamples -= SrcSamplesDone; - } - else - { - BufferListItem = nullptr; - mNumCallbackSamples = 0; - } - } - else - { - /* Handle streaming source */ - do { - if(BufferListItem->mSampleLen > DataPosInt) - break; - - DataPosInt -= BufferListItem->mSampleLen; - - ++buffers_done; - BufferListItem = BufferListItem->mNext.load(std::memory_order_relaxed); - if(!BufferListItem) BufferListItem = BufferLoopItem; - } while(BufferListItem); - } - } while(OutPos < SamplesToDo); - - mFlags |= VoiceIsFading; - - /* Don't update positions and buffers if we were stopping. */ - if UNLIKELY(vstate == Stopping) - { - mPlayState.store(Stopped, std::memory_order_release); - return; - } - - /* Capture the source ID in case it's reset for stopping. */ - const uint SourceID{mSourceID.load(std::memory_order_relaxed)}; - - /* Update voice info */ - mPosition.store(DataPosInt, std::memory_order_relaxed); - mPositionFrac.store(DataPosFrac, std::memory_order_relaxed); - mCurrentBuffer.store(BufferListItem, std::memory_order_relaxed); - if(!BufferListItem) - { - mLoopBuffer.store(nullptr, std::memory_order_relaxed); - mSourceID.store(0u, std::memory_order_relaxed); - } - std::atomic_thread_fence(std::memory_order_release); - - /* Send any events now, after the position/buffer info was updated. */ - const uint enabledevt{Context->mEnabledEvts.load(std::memory_order_acquire)}; - if(buffers_done > 0 && (enabledevt&EventType_BufferCompleted)) - { - RingBuffer *ring{Context->mAsyncEvents.get()}; - auto evt_vec = ring->getWriteVector(); - if(evt_vec.first.len > 0) - { - AsyncEvent *evt{::new(evt_vec.first.buf) AsyncEvent{EventType_BufferCompleted}}; - evt->u.bufcomp.id = SourceID; - evt->u.bufcomp.count = buffers_done; - ring->writeAdvance(1); - } - } - - if(!BufferListItem) - { - /* If the voice just ended, set it to Stopping so the next render - * ensures any residual noise fades to 0 amplitude. - */ - mPlayState.store(Stopping, std::memory_order_release); - if((enabledevt&EventType_SourceStateChange)) - SendSourceStoppedEvent(Context, SourceID); - } -} - -void Voice::prepare(DeviceBase *device) -{ - if((mFmtChannels == FmtUHJ2 || mFmtChannels == FmtUHJ3 || mFmtChannels==FmtUHJ4) && !mDecoder) - mDecoder = std::make_unique(); - else if(mFmtChannels != FmtUHJ2 && mFmtChannels != FmtUHJ3 && mFmtChannels != FmtUHJ4) - mDecoder = nullptr; - - /* Clear the stepping value explicitly so the mixer knows not to mix this - * until the update gets applied. - */ - mStep = 0; - - /* Make sure the sample history is cleared. */ - std::fill(mVoiceSamples.begin(), mVoiceSamples.end(), BufferLine{}); - - /* Don't need to set the VoiceIsAmbisonic flag if the device is not higher - * order than the voice. No HF scaling is necessary to mix it. - */ - if(mAmbiOrder && device->mAmbiOrder > mAmbiOrder) - { - const uint8_t *OrderFromChan{(mFmtChannels == FmtBFormat2D) ? - AmbiIndex::OrderFrom2DChannel().data() : AmbiIndex::OrderFromChannel().data()}; - const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder); - - const BandSplitter splitter{device->mXOverFreq / static_cast(device->Frequency)}; - for(auto &chandata : mChans) - { - chandata.mAmbiScale = scales[*(OrderFromChan++)]; - chandata.mAmbiSplitter = splitter; - chandata.mDryParams = DirectParams{}; - std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{}); - } - mFlags |= VoiceIsAmbisonic; - } - else - { - for(auto &chandata : mChans) - { - chandata.mDryParams = DirectParams{}; - std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{}); - } - mFlags &= ~VoiceIsAmbisonic; - } - - if(device->AvgSpeakerDist > 0.0f) - { - const float w1{SpeedOfSoundMetersPerSec / - (device->AvgSpeakerDist * static_cast(device->Frequency))}; - for(auto &chandata : mChans) - chandata.mDryParams.NFCtrlFilter.init(w1); - } -} diff --git a/alc/voice.h b/alc/voice.h deleted file mode 100644 index 937294e3..00000000 --- a/alc/voice.h +++ /dev/null @@ -1,271 +0,0 @@ -#ifndef VOICE_H -#define VOICE_H - -#include - -#include -#include -#include -#include - -#include "albyte.h" -#include "almalloc.h" -#include "aloptional.h" -#include "alspan.h" -#include "buffer_storage.h" -#include "core/bufferline.h" -#include "core/devformat.h" -#include "core/filters/biquad.h" -#include "core/filters/nfc.h" -#include "core/filters/splitter.h" -#include "core/mixer/defs.h" -#include "core/mixer/hrtfdefs.h" -#include "core/resampler_limits.h" -#include "core/uhjfilter.h" -#include "vector.h" - -struct ContextBase; -struct DeviceBase; -struct EffectSlot; -enum class DistanceModel : unsigned char; - -using uint = unsigned int; - - -#define MAX_SENDS 6 - - -enum class SpatializeMode : unsigned char { - Off, - On, - Auto -}; - -enum class DirectMode : unsigned char { - Off, - DropMismatch, - RemixMismatch -}; - - -/* Maximum number of extra source samples that may need to be loaded, for - * resampling or conversion purposes. - */ -constexpr uint MaxPostVoiceLoad{MaxResamplerEdge + UhjDecoder::sFilterDelay}; - - -enum { - AF_None = 0, - AF_LowPass = 1, - AF_HighPass = 2, - AF_BandPass = AF_LowPass | AF_HighPass -}; - - -struct DirectParams { - BiquadFilter LowPass; - BiquadFilter HighPass; - - NfcFilter NFCtrlFilter; - - struct { - HrtfFilter Old; - HrtfFilter Target; - alignas(16) std::array History; - } Hrtf; - - struct { - std::array Current; - std::array Target; - } Gains; -}; - -struct SendParams { - BiquadFilter LowPass; - BiquadFilter HighPass; - - struct { - std::array Current; - std::array Target; - } Gains; -}; - - -struct VoiceBufferItem { - std::atomic mNext{nullptr}; - - CallbackType mCallback{nullptr}; - void *mUserData{nullptr}; - - uint mSampleLen{0u}; - uint mLoopStart{0u}; - uint mLoopEnd{0u}; - - al::byte *mSamples{nullptr}; -}; - - -struct VoiceProps { - float Pitch; - float Gain; - float OuterGain; - float MinGain; - float MaxGain; - float InnerAngle; - float OuterAngle; - float RefDistance; - float MaxDistance; - float RolloffFactor; - std::array Position; - std::array Velocity; - std::array Direction; - std::array OrientAt; - std::array OrientUp; - bool HeadRelative; - DistanceModel mDistanceModel; - Resampler mResampler; - DirectMode DirectChannels; - SpatializeMode mSpatializeMode; - - bool DryGainHFAuto; - bool WetGainAuto; - bool WetGainHFAuto; - float OuterGainHF; - - float AirAbsorptionFactor; - float RoomRolloffFactor; - float DopplerFactor; - - std::array StereoPan; - - float Radius; - - /** Direct filter and auxiliary send info. */ - struct { - float Gain; - float GainHF; - float HFReference; - float GainLF; - float LFReference; - } Direct; - struct SendData { - EffectSlot *Slot; - float Gain; - float GainHF; - float HFReference; - float GainLF; - float LFReference; - } Send[MAX_SENDS]; -}; - -struct VoicePropsItem : public VoiceProps { - std::atomic next{nullptr}; - - DEF_NEWDEL(VoicePropsItem) -}; - -constexpr uint VoiceIsStatic{ 1u<<0}; -constexpr uint VoiceIsCallback{ 1u<<1}; -constexpr uint VoiceIsAmbisonic{ 1u<<2}; /* Needs HF scaling for ambisonic upsampling. */ -constexpr uint VoiceCallbackStopped{1u<<3}; -constexpr uint VoiceIsFading{ 1u<<4}; /* Use gain stepping for smooth transitions. */ -constexpr uint VoiceHasHrtf{ 1u<<5}; -constexpr uint VoiceHasNfc{ 1u<<6}; - -struct Voice { - enum State { - Stopped, - Playing, - Stopping, - Pending - }; - - std::atomic mUpdate{nullptr}; - - VoiceProps mProps; - - std::atomic mSourceID{0u}; - std::atomic mPlayState{Stopped}; - std::atomic mPendingChange{false}; - - /** - * Source offset in samples, relative to the currently playing buffer, NOT - * the whole queue. - */ - std::atomic mPosition; - /** Fractional (fixed-point) offset to the next sample. */ - std::atomic mPositionFrac; - - /* Current buffer queue item being played. */ - std::atomic mCurrentBuffer; - - /* Buffer queue item to loop to at end of queue (will be NULL for non- - * looping voices). - */ - std::atomic mLoopBuffer; - - /* Properties for the attached buffer(s). */ - FmtChannels mFmtChannels; - FmtType mFmtType; - uint mFrequency; - uint mFrameSize; - AmbiLayout mAmbiLayout; - AmbiScaling mAmbiScaling; - uint mAmbiOrder; - - std::unique_ptr mDecoder; - - /** Current target parameters used for mixing. */ - uint mStep{0}; - - ResamplerFunc mResampler; - - InterpState mResampleState; - - uint mFlags{}; - uint mNumCallbackSamples{0}; - - struct TargetData { - int FilterType; - al::span Buffer; - }; - TargetData mDirect; - std::array mSend; - - /* The first MaxResamplerPadding/2 elements are the sample history from the - * previous mix, with an additional MaxResamplerPadding/2 elements that are - * now current (which may be overwritten if the buffer data is still - * available). - */ - static constexpr size_t LineSize{BufferLineSize + MaxResamplerPadding + - UhjDecoder::sFilterDelay}; - using BufferLine = std::array; - al::vector mVoiceSamples{2}; - - struct ChannelData { - float mAmbiScale; - BandSplitter mAmbiSplitter; - - DirectParams mDryParams; - std::array mWetParams; - }; - al::vector mChans{2}; - - Voice() = default; - ~Voice() { delete mUpdate.exchange(nullptr, std::memory_order_acq_rel); } - - Voice(const Voice&) = delete; - Voice& operator=(const Voice&) = delete; - - void mix(const State vstate, ContextBase *Context, const uint SamplesToDo); - - void prepare(DeviceBase *device); - - DEF_NEWDEL(Voice) -}; - -extern Resampler ResamplerDefault; - -void aluInitMixer(al::optional resampler); - -#endif /* VOICE_H */ diff --git a/core/buffer_storage.cpp b/core/buffer_storage.cpp new file mode 100644 index 00000000..5179db13 --- /dev/null +++ b/core/buffer_storage.cpp @@ -0,0 +1,41 @@ + +#include "config.h" + +#include "buffer_storage.h" + +#include + + +uint BytesFromFmt(FmtType type) noexcept +{ + switch(type) + { + case FmtUByte: return sizeof(uint8_t); + case FmtShort: return sizeof(int16_t); + case FmtFloat: return sizeof(float); + case FmtDouble: return sizeof(double); + case FmtMulaw: return sizeof(uint8_t); + case FmtAlaw: return sizeof(uint8_t); + } + return 0; +} + +uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept +{ + switch(chans) + { + case FmtMono: return 1; + case FmtStereo: return 2; + case FmtRear: return 2; + case FmtQuad: return 4; + case FmtX51: return 6; + case FmtX61: return 7; + case FmtX71: return 8; + case FmtBFormat2D: return (ambiorder*2) + 1; + case FmtBFormat3D: return (ambiorder+1) * (ambiorder+1); + case FmtUHJ2: return 2; + case FmtUHJ3: return 3; + case FmtUHJ4: return 4; + } + return 0; +} diff --git a/core/buffer_storage.h b/core/buffer_storage.h new file mode 100644 index 00000000..59280354 --- /dev/null +++ b/core/buffer_storage.h @@ -0,0 +1,75 @@ +#ifndef CORE_BUFFER_STORAGE_H +#define CORE_BUFFER_STORAGE_H + +#include + +#include "albyte.h" + + +using uint = unsigned int; + +/* Storable formats */ +enum FmtType : unsigned char { + FmtUByte, + FmtShort, + FmtFloat, + FmtDouble, + FmtMulaw, + FmtAlaw, +}; +enum FmtChannels : unsigned char { + FmtMono, + FmtStereo, + FmtRear, + FmtQuad, + FmtX51, /* (WFX order) */ + FmtX61, /* (WFX order) */ + FmtX71, /* (WFX order) */ + FmtBFormat2D, + FmtBFormat3D, + FmtUHJ2, /* 2-channel UHJ, aka "BHJ", stereo-compatible */ + FmtUHJ3, /* 3-channel UHJ, aka "THJ" */ + FmtUHJ4, /* 4-channel UHJ, aka "PHJ" */ +}; + +enum class AmbiLayout : unsigned char { + FuMa, + ACN, +}; +enum class AmbiScaling : unsigned char { + FuMa, + SN3D, + N3D, +}; + +uint BytesFromFmt(FmtType type) noexcept; +uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept; +inline uint FrameSizeFromFmt(FmtChannels chans, FmtType type, uint ambiorder) noexcept +{ return ChannelsFromFmt(chans, ambiorder) * BytesFromFmt(type); } + + +using CallbackType = int(*)(void*, void*, int); + +struct BufferStorage { + CallbackType mCallback{nullptr}; + void *mUserData{nullptr}; + + uint mSampleRate{0u}; + FmtChannels mChannels{FmtMono}; + FmtType mType{FmtShort}; + uint mSampleLen{0u}; + + AmbiLayout mAmbiLayout{AmbiLayout::FuMa}; + AmbiScaling mAmbiScaling{AmbiScaling::FuMa}; + uint mAmbiOrder{0u}; + + inline uint bytesFromFmt() const noexcept { return BytesFromFmt(mType); } + inline uint channelsFromFmt() const noexcept + { return ChannelsFromFmt(mChannels, mAmbiOrder); } + inline uint frameSizeFromFmt() const noexcept { return channelsFromFmt() * bytesFromFmt(); } + + inline bool isBFormat() const noexcept + { return mChannels == FmtBFormat2D || mChannels == FmtBFormat3D; } +}; + +#endif /* CORE_BUFFER_STORAGE_H */ diff --git a/core/voice.cpp b/core/voice.cpp new file mode 100644 index 00000000..c764a277 --- /dev/null +++ b/core/voice.cpp @@ -0,0 +1,849 @@ + +#include "config.h" + +#include "voice.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "albyte.h" +#include "alnumeric.h" +#include "aloptional.h" +#include "alspan.h" +#include "alstring.h" +#include "ambidefs.h" +#include "async_event.h" +#include "buffer_storage.h" +#include "context.h" +#include "cpu_caps.h" +#include "devformat.h" +#include "device.h" +#include "filters/biquad.h" +#include "filters/nfc.h" +#include "filters/splitter.h" +#include "fmt_traits.h" +#include "logging.h" +#include "mixer.h" +#include "mixer/defs.h" +#include "mixer/hrtfdefs.h" +#include "opthelpers.h" +#include "resampler_limits.h" +#include "ringbuffer.h" +#include "vector.h" +#include "voice_change.h" + +struct CTag; +#ifdef HAVE_SSE +struct SSETag; +#endif +#ifdef HAVE_NEON +struct NEONTag; +#endif +struct CopyTag; + + +static_assert(!(sizeof(Voice::BufferLine)&15), "Voice::BufferLine must be a multiple of 16 bytes"); + +Resampler ResamplerDefault{Resampler::Linear}; + +namespace { + +using uint = unsigned int; + +using HrtfMixerFunc = void(*)(const float *InSamples, float2 *AccumSamples, const uint IrSize, + const MixHrtfFilter *hrtfparams, const size_t BufferSize); +using HrtfMixerBlendFunc = void(*)(const float *InSamples, float2 *AccumSamples, + const uint IrSize, const HrtfFilter *oldparams, const MixHrtfFilter *newparams, + const size_t BufferSize); + +HrtfMixerFunc MixHrtfSamples{MixHrtf_}; +HrtfMixerBlendFunc MixHrtfBlendSamples{MixHrtfBlend_}; + +inline MixerFunc SelectMixer() +{ +#ifdef HAVE_NEON + if((CPUCapFlags&CPU_CAP_NEON)) + return Mix_; +#endif +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + return Mix_; +#endif + return Mix_; +} + +inline HrtfMixerFunc SelectHrtfMixer() +{ +#ifdef HAVE_NEON + if((CPUCapFlags&CPU_CAP_NEON)) + return MixHrtf_; +#endif +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + return MixHrtf_; +#endif + return MixHrtf_; +} + +inline HrtfMixerBlendFunc SelectHrtfBlendMixer() +{ +#ifdef HAVE_NEON + if((CPUCapFlags&CPU_CAP_NEON)) + return MixHrtfBlend_; +#endif +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + return MixHrtfBlend_; +#endif + return MixHrtfBlend_; +} + +} // namespace + +void Voice::InitMixer(al::optional resampler) +{ + if(resampler) + { + struct ResamplerEntry { + const char name[16]; + const Resampler resampler; + }; + constexpr ResamplerEntry ResamplerList[]{ + { "none", Resampler::Point }, + { "point", Resampler::Point }, + { "linear", Resampler::Linear }, + { "cubic", Resampler::Cubic }, + { "bsinc12", Resampler::BSinc12 }, + { "fast_bsinc12", Resampler::FastBSinc12 }, + { "bsinc24", Resampler::BSinc24 }, + { "fast_bsinc24", Resampler::FastBSinc24 }, + }; + + const char *str{resampler->c_str()}; + if(al::strcasecmp(str, "bsinc") == 0) + { + WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str); + str = "bsinc12"; + } + else if(al::strcasecmp(str, "sinc4") == 0 || al::strcasecmp(str, "sinc8") == 0) + { + WARN("Resampler option \"%s\" is deprecated, using cubic\n", str); + str = "cubic"; + } + + auto iter = std::find_if(std::begin(ResamplerList), std::end(ResamplerList), + [str](const ResamplerEntry &entry) -> bool + { return al::strcasecmp(str, entry.name) == 0; }); + if(iter == std::end(ResamplerList)) + ERR("Invalid resampler: %s\n", str); + else + ResamplerDefault = iter->resampler; + } + + MixSamples = SelectMixer(); + MixHrtfBlendSamples = SelectHrtfBlendMixer(); + MixHrtfSamples = SelectHrtfMixer(); +} + + +namespace { + +void SendSourceStoppedEvent(ContextBase *context, uint id) +{ + RingBuffer *ring{context->mAsyncEvents.get()}; + auto evt_vec = ring->getWriteVector(); + if(evt_vec.first.len < 1) return; + + AsyncEvent *evt{::new(evt_vec.first.buf) AsyncEvent{EventType_SourceStateChange}}; + evt->u.srcstate.id = id; + evt->u.srcstate.state = AsyncEvent::SrcState::Stop; + + ring->writeAdvance(1); +} + + +const float *DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, float *dst, + const al::span src, int type) +{ + switch(type) + { + case AF_None: + lpfilter.clear(); + hpfilter.clear(); + break; + + case AF_LowPass: + lpfilter.process(src, dst); + hpfilter.clear(); + return dst; + case AF_HighPass: + lpfilter.clear(); + hpfilter.process(src, dst); + return dst; + + case AF_BandPass: + DualBiquad{lpfilter, hpfilter}.process(src, dst); + return dst; + } + return src.data(); +} + + +void LoadSamples(const al::span dstSamples, const size_t dstOffset, + const al::byte *src, const size_t srcOffset, const FmtType srctype, const FmtChannels srcchans, + const size_t samples) noexcept +{ +#define HANDLE_FMT(T) case T: \ + { \ + constexpr size_t sampleSize{sizeof(al::FmtTypeTraits::Type)}; \ + if(srcchans == FmtUHJ2) \ + { \ + constexpr size_t srcstep{2u}; \ + src += srcOffset*srcstep*sampleSize; \ + al::LoadSampleArray(dstSamples[0].data() + dstOffset, src, \ + srcstep, samples); \ + al::LoadSampleArray(dstSamples[1].data() + dstOffset, \ + src + sampleSize, srcstep, samples); \ + std::fill_n(dstSamples[2].data() + dstOffset, samples, 0.0f); \ + } \ + else \ + { \ + const size_t srcstep{dstSamples.size()}; \ + src += srcOffset*srcstep*sampleSize; \ + for(auto &dst : dstSamples) \ + { \ + al::LoadSampleArray(dst.data() + dstOffset, src, srcstep, \ + samples); \ + src += sampleSize; \ + } \ + } \ + } \ + break + + switch(srctype) + { + HANDLE_FMT(FmtUByte); + HANDLE_FMT(FmtShort); + HANDLE_FMT(FmtFloat); + HANDLE_FMT(FmtDouble); + HANDLE_FMT(FmtMulaw); + HANDLE_FMT(FmtAlaw); + } +#undef HANDLE_FMT +} + +void LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, + const size_t dataPosInt, const FmtType sampleType, const FmtChannels sampleChannels, + const size_t samplesToLoad, const al::span voiceSamples) +{ + const uint loopStart{buffer->mLoopStart}; + const uint loopEnd{buffer->mLoopEnd}; + ASSUME(loopEnd > loopStart); + + /* If current pos is beyond the loop range, do not loop */ + if(!bufferLoopItem || dataPosInt >= loopEnd) + { + /* Load what's left to play from the buffer */ + const size_t remaining{minz(samplesToLoad, buffer->mSampleLen-dataPosInt)}; + LoadSamples(voiceSamples, MaxResamplerEdge, buffer->mSamples, dataPosInt, sampleType, + sampleChannels, remaining); + + if(const size_t toFill{samplesToLoad - remaining}) + { + for(auto &chanbuffer : voiceSamples) + { + auto srcsamples = chanbuffer.data() + MaxResamplerEdge - 1 + remaining; + std::fill_n(srcsamples + 1, toFill, *srcsamples); + } + } + } + else + { + /* Load what's left of this loop iteration */ + const size_t remaining{minz(samplesToLoad, loopEnd-dataPosInt)}; + LoadSamples(voiceSamples, MaxResamplerEdge, buffer->mSamples, dataPosInt, sampleType, + sampleChannels, remaining); + + /* Load repeats of the loop to fill the buffer. */ + const auto loopSize = static_cast(loopEnd - loopStart); + size_t samplesLoaded{remaining}; + while(const size_t toFill{minz(samplesToLoad - samplesLoaded, loopSize)}) + { + LoadSamples(voiceSamples, MaxResamplerEdge + samplesLoaded, buffer->mSamples, + loopStart, sampleType, sampleChannels, toFill); + samplesLoaded += toFill; + } + } +} + +void LoadBufferCallback(VoiceBufferItem *buffer, const size_t numCallbackSamples, + const FmtType sampleType, const FmtChannels sampleChannels, const size_t samplesToLoad, + const al::span voiceSamples) +{ + /* Load what's left to play from the buffer */ + const size_t remaining{minz(samplesToLoad, numCallbackSamples)}; + LoadSamples(voiceSamples, MaxResamplerEdge, buffer->mSamples, 0, sampleType, sampleChannels, + remaining); + + if(const size_t toFill{samplesToLoad - remaining}) + { + for(auto &chanbuffer : voiceSamples) + { + auto srcsamples = chanbuffer.data() + MaxResamplerEdge - 1 + remaining; + std::fill_n(srcsamples + 1, toFill, *srcsamples); + } + } +} + +void LoadBufferQueue(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, + size_t dataPosInt, const FmtType sampleType, const FmtChannels sampleChannels, + const size_t samplesToLoad, const al::span voiceSamples) +{ + /* Crawl the buffer queue to fill in the temp buffer */ + size_t samplesLoaded{0}; + while(buffer && samplesLoaded != samplesToLoad) + { + if(dataPosInt >= buffer->mSampleLen) + { + dataPosInt -= buffer->mSampleLen; + buffer = buffer->mNext.load(std::memory_order_acquire); + if(!buffer) buffer = bufferLoopItem; + continue; + } + + const size_t remaining{minz(samplesToLoad-samplesLoaded, buffer->mSampleLen-dataPosInt)}; + LoadSamples(voiceSamples, MaxResamplerEdge+samplesLoaded, buffer->mSamples, dataPosInt, + sampleType, sampleChannels, remaining); + + samplesLoaded += remaining; + if(samplesLoaded == samplesToLoad) + break; + + dataPosInt = 0; + buffer = buffer->mNext.load(std::memory_order_acquire); + if(!buffer) buffer = bufferLoopItem; + } + if(const size_t toFill{samplesToLoad - samplesLoaded}) + { + size_t chanidx{0}; + for(auto &chanbuffer : voiceSamples) + { + auto srcsamples = chanbuffer.data() + MaxResamplerEdge - 1 + samplesLoaded; + std::fill_n(srcsamples + 1, toFill, *srcsamples); + ++chanidx; + } + } +} + + +void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &parms, + const float TargetGain, const uint Counter, uint OutPos, DeviceBase *Device) +{ + const uint IrSize{Device->mIrSize}; + auto &HrtfSamples = Device->HrtfSourceData; + /* Source HRTF mixing needs to include the direct delay so it remains + * aligned with the direct mix's HRTF filtering. + */ + float2 *AccumSamples{Device->HrtfAccumData + HrtfDirectDelay}; + + /* Copy the HRTF history and new input samples into a temp buffer. */ + auto src_iter = std::copy(parms.Hrtf.History.begin(), parms.Hrtf.History.end(), + std::begin(HrtfSamples)); + std::copy_n(samples, DstBufferSize, src_iter); + /* Copy the last used samples back into the history buffer for later. */ + std::copy_n(std::begin(HrtfSamples) + DstBufferSize, parms.Hrtf.History.size(), + parms.Hrtf.History.begin()); + + /* If fading and this is the first mixing pass, fade between the IRs. */ + uint fademix{0u}; + if(Counter && OutPos == 0) + { + fademix = minu(DstBufferSize, Counter); + + float gain{TargetGain}; + + /* The new coefficients need to fade in completely since they're + * replacing the old ones. To keep the gain fading consistent, + * interpolate between the old and new target gains given how much of + * the fade time this mix handles. + */ + if(Counter > fademix) + { + const float a{static_cast(fademix) / static_cast(Counter)}; + gain = lerp(parms.Hrtf.Old.Gain, TargetGain, a); + } + + MixHrtfFilter hrtfparams{ + parms.Hrtf.Target.Coeffs, + parms.Hrtf.Target.Delay, + 0.0f, gain / static_cast(fademix)}; + MixHrtfBlendSamples(HrtfSamples, AccumSamples+OutPos, IrSize, &parms.Hrtf.Old, &hrtfparams, + fademix); + + /* Update the old parameters with the result. */ + parms.Hrtf.Old = parms.Hrtf.Target; + parms.Hrtf.Old.Gain = gain; + OutPos += fademix; + } + + if(fademix < DstBufferSize) + { + const uint todo{DstBufferSize - fademix}; + float gain{TargetGain}; + + /* Interpolate the target gain if the gain fading lasts longer than + * this mix. + */ + if(Counter > DstBufferSize) + { + const float a{static_cast(todo) / static_cast(Counter-fademix)}; + gain = lerp(parms.Hrtf.Old.Gain, TargetGain, a); + } + + MixHrtfFilter hrtfparams{ + parms.Hrtf.Target.Coeffs, + parms.Hrtf.Target.Delay, + parms.Hrtf.Old.Gain, + (gain - parms.Hrtf.Old.Gain) / static_cast(todo)}; + MixHrtfSamples(HrtfSamples+fademix, AccumSamples+OutPos, IrSize, &hrtfparams, todo); + + /* Store the now-current gain for next time. */ + parms.Hrtf.Old.Gain = gain; + } +} + +void DoNfcMix(const al::span samples, FloatBufferLine *OutBuffer, DirectParams &parms, + const float *TargetGains, const uint Counter, const uint OutPos, DeviceBase *Device) +{ + using FilterProc = void (NfcFilter::*)(const al::span, float*); + static constexpr FilterProc NfcProcess[MaxAmbiOrder+1]{ + nullptr, &NfcFilter::process1, &NfcFilter::process2, &NfcFilter::process3}; + + float *CurrentGains{parms.Gains.Current.data()}; + MixSamples(samples, {OutBuffer, 1u}, CurrentGains, TargetGains, Counter, OutPos); + ++OutBuffer; + ++CurrentGains; + ++TargetGains; + + const al::span nfcsamples{Device->NfcSampleData, samples.size()}; + size_t order{1}; + while(const size_t chancount{Device->NumChannelsPerOrder[order]}) + { + (parms.NFCtrlFilter.*NfcProcess[order])(samples, nfcsamples.data()); + MixSamples(nfcsamples, {OutBuffer, chancount}, CurrentGains, TargetGains, Counter, OutPos); + OutBuffer += chancount; + CurrentGains += chancount; + TargetGains += chancount; + if(++order == MaxAmbiOrder+1) + break; + } +} + +} // namespace + +void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo) +{ + static constexpr std::array SilentTarget{}; + + ASSUME(SamplesToDo > 0); + + /* Get voice info */ + uint DataPosInt{mPosition.load(std::memory_order_relaxed)}; + uint DataPosFrac{mPositionFrac.load(std::memory_order_relaxed)}; + VoiceBufferItem *BufferListItem{mCurrentBuffer.load(std::memory_order_relaxed)}; + VoiceBufferItem *BufferLoopItem{mLoopBuffer.load(std::memory_order_relaxed)}; + const uint increment{mStep}; + if UNLIKELY(increment < 1) + { + /* If the voice is supposed to be stopping but can't be mixed, just + * stop it before bailing. + */ + if(vstate == Stopping) + mPlayState.store(Stopped, std::memory_order_release); + return; + } + + DeviceBase *Device{Context->mDevice}; + const uint NumSends{Device->NumAuxSends}; + + ResamplerFunc Resample{(increment == MixerFracOne && DataPosFrac == 0) ? + Resample_ : mResampler}; + + uint Counter{(mFlags&VoiceIsFading) ? SamplesToDo : 0}; + if(!Counter) + { + /* No fading, just overwrite the old/current params. */ + for(auto &chandata : mChans) + { + { + DirectParams &parms = chandata.mDryParams; + if(!(mFlags&VoiceHasHrtf)) + parms.Gains.Current = parms.Gains.Target; + else + parms.Hrtf.Old = parms.Hrtf.Target; + } + for(uint send{0};send < NumSends;++send) + { + if(mSend[send].Buffer.empty()) + continue; + + SendParams &parms = chandata.mWetParams[send]; + parms.Gains.Current = parms.Gains.Target; + } + } + } + else if UNLIKELY(!BufferListItem) + Counter = std::min(Counter, 64u); + + const uint PostPadding{MaxResamplerEdge + + ((mFmtChannels==FmtUHJ2 || mFmtChannels==FmtUHJ3 || mFmtChannels==FmtUHJ4) + ? uint{UhjDecoder::sFilterDelay} : 0u)}; + uint buffers_done{0u}; + uint OutPos{0u}; + do { + /* Figure out how many buffer samples will be needed */ + uint DstBufferSize{SamplesToDo - OutPos}; + uint SrcBufferSize; + + if(increment <= MixerFracOne) + { + /* Calculate the last written dst sample pos. */ + uint64_t DataSize64{DstBufferSize - 1}; + /* Calculate the last read src sample pos. */ + DataSize64 = (DataSize64*increment + DataPosFrac) >> MixerFracBits; + /* +1 to get the src sample count, include padding. */ + DataSize64 += 1 + PostPadding; + + /* Result is guaranteed to be <= BufferLineSize+ResamplerPrePadding + * since we won't use more src samples than dst samples+padding. + */ + SrcBufferSize = static_cast(DataSize64); + } + else + { + uint64_t DataSize64{DstBufferSize}; + /* Calculate the end src sample pos, include padding. */ + DataSize64 = (DataSize64*increment + DataPosFrac) >> MixerFracBits; + DataSize64 += PostPadding; + + if(DataSize64 <= LineSize - MaxResamplerEdge) + SrcBufferSize = static_cast(DataSize64); + else + { + /* If the source size got saturated, we can't fill the desired + * dst size. Figure out how many samples we can actually mix. + */ + SrcBufferSize = LineSize - MaxResamplerEdge; + + DataSize64 = SrcBufferSize - PostPadding; + DataSize64 = ((DataSize64<(DataSize64) & ~3u; + } + ASSUME(DstBufferSize > 0); + } + } + + if((mFlags&(VoiceIsCallback|VoiceCallbackStopped)) == VoiceIsCallback && BufferListItem) + { + if(SrcBufferSize > mNumCallbackSamples) + { + const size_t byteOffset{mNumCallbackSamples*mFrameSize}; + const size_t needBytes{SrcBufferSize*mFrameSize - byteOffset}; + + const int gotBytes{BufferListItem->mCallback(BufferListItem->mUserData, + &BufferListItem->mSamples[byteOffset], static_cast(needBytes))}; + if(gotBytes < 0) + mFlags |= VoiceCallbackStopped; + else if(static_cast(gotBytes) < needBytes) + { + mFlags |= VoiceCallbackStopped; + mNumCallbackSamples += static_cast(static_cast(gotBytes) / + mFrameSize); + } + else + mNumCallbackSamples = SrcBufferSize; + } + } + + if UNLIKELY(!BufferListItem) + { + for(auto &chanbuffer : mVoiceSamples) + { + auto srciter = chanbuffer.data() + MaxResamplerEdge; + auto srcend = chanbuffer.data() + MaxResamplerPadding; + + /* When loading from a voice that ended prematurely, only take + * the samples that get closest to 0 amplitude. This helps + * certain sounds fade out better. + */ + auto abs_lt = [](const float lhs, const float rhs) noexcept -> bool + { return std::abs(lhs) < std::abs(rhs); }; + srciter = std::min_element(srciter, srcend, abs_lt); + + SrcBufferSize = SrcBufferSize - PostPadding + MaxResamplerPadding; + std::fill(srciter+1, chanbuffer.data() + SrcBufferSize, *srciter); + } + } + else + { + if((mFlags&VoiceIsStatic)) + LoadBufferStatic(BufferListItem, BufferLoopItem, DataPosInt, mFmtType, mFmtChannels, + SrcBufferSize, mVoiceSamples); + else if((mFlags&VoiceIsCallback)) + LoadBufferCallback(BufferListItem, mNumCallbackSamples, mFmtType, mFmtChannels, + SrcBufferSize, mVoiceSamples); + else + LoadBufferQueue(BufferListItem, BufferLoopItem, DataPosInt, mFmtType, mFmtChannels, + SrcBufferSize, mVoiceSamples); + + if(mDecoder) + { + const size_t srcOffset{(increment*DstBufferSize + DataPosFrac)>>MixerFracBits}; + SrcBufferSize = SrcBufferSize - PostPadding + MaxResamplerEdge; + mDecoder->decode(mVoiceSamples, MaxResamplerEdge, SrcBufferSize, srcOffset); + } + } + + auto voiceSamples = mVoiceSamples.begin(); + for(auto &chandata : mChans) + { + /* Resample, then apply ambisonic upsampling as needed. */ + float *ResampledData{Resample(&mResampleState, + voiceSamples->data() + MaxResamplerEdge, DataPosFrac, increment, + {Device->ResampledData, DstBufferSize})}; + if((mFlags&VoiceIsAmbisonic)) + chandata.mAmbiSplitter.processHfScale({ResampledData, DstBufferSize}, + chandata.mAmbiScale); + + /* Now filter and mix to the appropriate outputs. */ + const al::span FilterBuf{Device->FilteredData}; + { + DirectParams &parms = chandata.mDryParams; + const float *samples{DoFilters(parms.LowPass, parms.HighPass, FilterBuf.data(), + {ResampledData, DstBufferSize}, mDirect.FilterType)}; + + if((mFlags&VoiceHasHrtf)) + { + const float TargetGain{UNLIKELY(vstate == Stopping) ? 0.0f : + parms.Hrtf.Target.Gain}; + DoHrtfMix(samples, DstBufferSize, parms, TargetGain, Counter, OutPos, Device); + } + else if((mFlags&VoiceHasNfc)) + { + const float *TargetGains{UNLIKELY(vstate == Stopping) ? SilentTarget.data() + : parms.Gains.Target.data()}; + DoNfcMix({samples, DstBufferSize}, mDirect.Buffer.data(), parms, TargetGains, + Counter, OutPos, Device); + } + else + { + const float *TargetGains{UNLIKELY(vstate == Stopping) ? SilentTarget.data() + : parms.Gains.Target.data()}; + MixSamples({samples, DstBufferSize}, mDirect.Buffer, + parms.Gains.Current.data(), TargetGains, Counter, OutPos); + } + } + + for(uint send{0};send < NumSends;++send) + { + if(mSend[send].Buffer.empty()) + continue; + + SendParams &parms = chandata.mWetParams[send]; + const float *samples{DoFilters(parms.LowPass, parms.HighPass, FilterBuf.data(), + {ResampledData, DstBufferSize}, mSend[send].FilterType)}; + + const float *TargetGains{UNLIKELY(vstate == Stopping) ? SilentTarget.data() + : parms.Gains.Target.data()}; + MixSamples({samples, DstBufferSize}, mSend[send].Buffer, + parms.Gains.Current.data(), TargetGains, Counter, OutPos); + } + + /* Store the last source samples used for next time. */ + const size_t srcOffset{(increment*DstBufferSize + DataPosFrac)>>MixerFracBits}; + std::copy_n(voiceSamples->data()+srcOffset, MaxResamplerPadding, voiceSamples->data()); + ++voiceSamples; + } + /* Update positions */ + DataPosFrac += increment*DstBufferSize; + const uint SrcSamplesDone{DataPosFrac>>MixerFracBits}; + DataPosInt += SrcSamplesDone; + DataPosFrac &= MixerFracMask; + + OutPos += DstBufferSize; + Counter = maxu(DstBufferSize, Counter) - DstBufferSize; + + if UNLIKELY(!BufferListItem) + { + /* Do nothing extra when there's no buffers. */ + } + else if((mFlags&VoiceIsStatic)) + { + if(BufferLoopItem) + { + /* Handle looping static source */ + const uint LoopStart{BufferListItem->mLoopStart}; + const uint LoopEnd{BufferListItem->mLoopEnd}; + if(DataPosInt >= LoopEnd) + { + assert(LoopEnd > LoopStart); + DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart; + } + } + else + { + /* Handle non-looping static source */ + if(DataPosInt >= BufferListItem->mSampleLen) + { + BufferListItem = nullptr; + break; + } + } + } + else if((mFlags&VoiceIsCallback)) + { + if(SrcSamplesDone < mNumCallbackSamples) + { + const size_t byteOffset{SrcSamplesDone*mFrameSize}; + const size_t byteEnd{mNumCallbackSamples*mFrameSize}; + al::byte *data{BufferListItem->mSamples}; + std::copy(data+byteOffset, data+byteEnd, data); + mNumCallbackSamples -= SrcSamplesDone; + } + else + { + BufferListItem = nullptr; + mNumCallbackSamples = 0; + } + } + else + { + /* Handle streaming source */ + do { + if(BufferListItem->mSampleLen > DataPosInt) + break; + + DataPosInt -= BufferListItem->mSampleLen; + + ++buffers_done; + BufferListItem = BufferListItem->mNext.load(std::memory_order_relaxed); + if(!BufferListItem) BufferListItem = BufferLoopItem; + } while(BufferListItem); + } + } while(OutPos < SamplesToDo); + + mFlags |= VoiceIsFading; + + /* Don't update positions and buffers if we were stopping. */ + if UNLIKELY(vstate == Stopping) + { + mPlayState.store(Stopped, std::memory_order_release); + return; + } + + /* Capture the source ID in case it's reset for stopping. */ + const uint SourceID{mSourceID.load(std::memory_order_relaxed)}; + + /* Update voice info */ + mPosition.store(DataPosInt, std::memory_order_relaxed); + mPositionFrac.store(DataPosFrac, std::memory_order_relaxed); + mCurrentBuffer.store(BufferListItem, std::memory_order_relaxed); + if(!BufferListItem) + { + mLoopBuffer.store(nullptr, std::memory_order_relaxed); + mSourceID.store(0u, std::memory_order_relaxed); + } + std::atomic_thread_fence(std::memory_order_release); + + /* Send any events now, after the position/buffer info was updated. */ + const uint enabledevt{Context->mEnabledEvts.load(std::memory_order_acquire)}; + if(buffers_done > 0 && (enabledevt&EventType_BufferCompleted)) + { + RingBuffer *ring{Context->mAsyncEvents.get()}; + auto evt_vec = ring->getWriteVector(); + if(evt_vec.first.len > 0) + { + AsyncEvent *evt{::new(evt_vec.first.buf) AsyncEvent{EventType_BufferCompleted}}; + evt->u.bufcomp.id = SourceID; + evt->u.bufcomp.count = buffers_done; + ring->writeAdvance(1); + } + } + + if(!BufferListItem) + { + /* If the voice just ended, set it to Stopping so the next render + * ensures any residual noise fades to 0 amplitude. + */ + mPlayState.store(Stopping, std::memory_order_release); + if((enabledevt&EventType_SourceStateChange)) + SendSourceStoppedEvent(Context, SourceID); + } +} + +void Voice::prepare(DeviceBase *device) +{ + if((mFmtChannels == FmtUHJ2 || mFmtChannels == FmtUHJ3 || mFmtChannels==FmtUHJ4) && !mDecoder) + mDecoder = std::make_unique(); + else if(mFmtChannels != FmtUHJ2 && mFmtChannels != FmtUHJ3 && mFmtChannels != FmtUHJ4) + mDecoder = nullptr; + + /* Clear the stepping value explicitly so the mixer knows not to mix this + * until the update gets applied. + */ + mStep = 0; + + /* Make sure the sample history is cleared. */ + std::fill(mVoiceSamples.begin(), mVoiceSamples.end(), BufferLine{}); + + /* Don't need to set the VoiceIsAmbisonic flag if the device is not higher + * order than the voice. No HF scaling is necessary to mix it. + */ + if(mAmbiOrder && device->mAmbiOrder > mAmbiOrder) + { + const uint8_t *OrderFromChan{(mFmtChannels == FmtBFormat2D) ? + AmbiIndex::OrderFrom2DChannel().data() : AmbiIndex::OrderFromChannel().data()}; + const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder); + + const BandSplitter splitter{device->mXOverFreq / static_cast(device->Frequency)}; + for(auto &chandata : mChans) + { + chandata.mAmbiScale = scales[*(OrderFromChan++)]; + chandata.mAmbiSplitter = splitter; + chandata.mDryParams = DirectParams{}; + std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{}); + } + mFlags |= VoiceIsAmbisonic; + } + else + { + for(auto &chandata : mChans) + { + chandata.mDryParams = DirectParams{}; + std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{}); + } + mFlags &= ~VoiceIsAmbisonic; + } + + if(device->AvgSpeakerDist > 0.0f) + { + const float w1{SpeedOfSoundMetersPerSec / + (device->AvgSpeakerDist * static_cast(device->Frequency))}; + for(auto &chandata : mChans) + chandata.mDryParams.NFCtrlFilter.init(w1); + } +} diff --git a/core/voice.h b/core/voice.h new file mode 100644 index 00000000..c3347cda --- /dev/null +++ b/core/voice.h @@ -0,0 +1,270 @@ +#ifndef CORE_VOICE_H +#define CORE_VOICE_H + +#include +#include +#include +#include +#include + +#include "albyte.h" +#include "almalloc.h" +#include "aloptional.h" +#include "alspan.h" +#include "bufferline.h" +#include "buffer_storage.h" +#include "devformat.h" +#include "filters/biquad.h" +#include "filters/nfc.h" +#include "filters/splitter.h" +#include "mixer/defs.h" +#include "mixer/hrtfdefs.h" +#include "resampler_limits.h" +#include "uhjfilter.h" +#include "vector.h" + +struct ContextBase; +struct DeviceBase; +struct EffectSlot; +enum class DistanceModel : unsigned char; + +using uint = unsigned int; + + +#define MAX_SENDS 6 + + +enum class SpatializeMode : unsigned char { + Off, + On, + Auto +}; + +enum class DirectMode : unsigned char { + Off, + DropMismatch, + RemixMismatch +}; + + +/* Maximum number of extra source samples that may need to be loaded, for + * resampling or conversion purposes. + */ +constexpr uint MaxPostVoiceLoad{MaxResamplerEdge + UhjDecoder::sFilterDelay}; + + +enum { + AF_None = 0, + AF_LowPass = 1, + AF_HighPass = 2, + AF_BandPass = AF_LowPass | AF_HighPass +}; + + +struct DirectParams { + BiquadFilter LowPass; + BiquadFilter HighPass; + + NfcFilter NFCtrlFilter; + + struct { + HrtfFilter Old; + HrtfFilter Target; + alignas(16) std::array History; + } Hrtf; + + struct { + std::array Current; + std::array Target; + } Gains; +}; + +struct SendParams { + BiquadFilter LowPass; + BiquadFilter HighPass; + + struct { + std::array Current; + std::array Target; + } Gains; +}; + + +struct VoiceBufferItem { + std::atomic mNext{nullptr}; + + CallbackType mCallback{nullptr}; + void *mUserData{nullptr}; + + uint mSampleLen{0u}; + uint mLoopStart{0u}; + uint mLoopEnd{0u}; + + al::byte *mSamples{nullptr}; +}; + + +struct VoiceProps { + float Pitch; + float Gain; + float OuterGain; + float MinGain; + float MaxGain; + float InnerAngle; + float OuterAngle; + float RefDistance; + float MaxDistance; + float RolloffFactor; + std::array Position; + std::array Velocity; + std::array Direction; + std::array OrientAt; + std::array OrientUp; + bool HeadRelative; + DistanceModel mDistanceModel; + Resampler mResampler; + DirectMode DirectChannels; + SpatializeMode mSpatializeMode; + + bool DryGainHFAuto; + bool WetGainAuto; + bool WetGainHFAuto; + float OuterGainHF; + + float AirAbsorptionFactor; + float RoomRolloffFactor; + float DopplerFactor; + + std::array StereoPan; + + float Radius; + + /** Direct filter and auxiliary send info. */ + struct { + float Gain; + float GainHF; + float HFReference; + float GainLF; + float LFReference; + } Direct; + struct SendData { + EffectSlot *Slot; + float Gain; + float GainHF; + float HFReference; + float GainLF; + float LFReference; + } Send[MAX_SENDS]; +}; + +struct VoicePropsItem : public VoiceProps { + std::atomic next{nullptr}; + + DEF_NEWDEL(VoicePropsItem) +}; + +constexpr uint VoiceIsStatic{ 1u<<0}; +constexpr uint VoiceIsCallback{ 1u<<1}; +constexpr uint VoiceIsAmbisonic{ 1u<<2}; /* Needs HF scaling for ambisonic upsampling. */ +constexpr uint VoiceCallbackStopped{1u<<3}; +constexpr uint VoiceIsFading{ 1u<<4}; /* Use gain stepping for smooth transitions. */ +constexpr uint VoiceHasHrtf{ 1u<<5}; +constexpr uint VoiceHasNfc{ 1u<<6}; + +struct Voice { + enum State { + Stopped, + Playing, + Stopping, + Pending + }; + + std::atomic mUpdate{nullptr}; + + VoiceProps mProps; + + std::atomic mSourceID{0u}; + std::atomic mPlayState{Stopped}; + std::atomic mPendingChange{false}; + + /** + * Source offset in samples, relative to the currently playing buffer, NOT + * the whole queue. + */ + std::atomic mPosition; + /** Fractional (fixed-point) offset to the next sample. */ + std::atomic mPositionFrac; + + /* Current buffer queue item being played. */ + std::atomic mCurrentBuffer; + + /* Buffer queue item to loop to at end of queue (will be NULL for non- + * looping voices). + */ + std::atomic mLoopBuffer; + + /* Properties for the attached buffer(s). */ + FmtChannels mFmtChannels; + FmtType mFmtType; + uint mFrequency; + uint mFrameSize; + AmbiLayout mAmbiLayout; + AmbiScaling mAmbiScaling; + uint mAmbiOrder; + + std::unique_ptr mDecoder; + + /** Current target parameters used for mixing. */ + uint mStep{0}; + + ResamplerFunc mResampler; + + InterpState mResampleState; + + uint mFlags{}; + uint mNumCallbackSamples{0}; + + struct TargetData { + int FilterType; + al::span Buffer; + }; + TargetData mDirect; + std::array mSend; + + /* The first MaxResamplerPadding/2 elements are the sample history from the + * previous mix, with an additional MaxResamplerPadding/2 elements that are + * now current (which may be overwritten if the buffer data is still + * available). + */ + static constexpr size_t LineSize{BufferLineSize + MaxResamplerPadding + + UhjDecoder::sFilterDelay}; + using BufferLine = std::array; + al::vector mVoiceSamples{2}; + + struct ChannelData { + float mAmbiScale; + BandSplitter mAmbiSplitter; + + DirectParams mDryParams; + std::array mWetParams; + }; + al::vector mChans{2}; + + Voice() = default; + ~Voice() { delete mUpdate.exchange(nullptr, std::memory_order_acq_rel); } + + Voice(const Voice&) = delete; + Voice& operator=(const Voice&) = delete; + + void mix(const State vstate, ContextBase *Context, const uint SamplesToDo); + + void prepare(DeviceBase *device); + + static void InitMixer(al::optional resampler); + + DEF_NEWDEL(Voice) +}; + +extern Resampler ResamplerDefault; + +#endif /* CORE_VOICE_H */ -- cgit v1.2.3 From 061148072f6bbcda20d57a4512e414c833129479 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 27 Apr 2021 16:04:54 -0700 Subject: Update include headers Don't add alc/ to the include paths. --- CMakeLists.txt | 1 - al/auxeffectslot.cpp | 8 ++++---- al/auxeffectslot.h | 6 +++--- al/buffer.cpp | 6 +++--- al/buffer.h | 2 +- al/effect.cpp | 6 +++--- al/effects/autowah.cpp | 2 +- al/effects/chorus.cpp | 2 +- al/effects/compressor.cpp | 2 +- al/effects/convolution.cpp | 4 ++-- al/effects/dedicated.cpp | 4 ++-- al/effects/distortion.cpp | 2 +- al/effects/echo.cpp | 2 +- al/effects/equalizer.cpp | 2 +- al/effects/fshifter.cpp | 2 +- al/effects/modulator.cpp | 2 +- al/effects/null.cpp | 2 +- al/effects/pshifter.cpp | 2 +- al/effects/reverb.cpp | 2 +- al/effects/vmorpher.cpp | 2 +- al/error.cpp | 2 +- al/event.cpp | 6 +++--- al/extension.cpp | 2 +- al/filter.cpp | 4 ++-- al/listener.cpp | 2 +- al/source.cpp | 10 +++++----- al/source.h | 4 ++-- al/state.cpp | 8 ++++---- alc/backends/alsa.cpp | 7 +++---- alc/backends/alsa.h | 2 +- alc/backends/coreaudio.cpp | 13 ++++++------- alc/backends/coreaudio.h | 2 +- alc/backends/dsound.cpp | 8 ++++---- alc/backends/dsound.h | 2 +- alc/backends/jack.cpp | 8 ++++---- alc/backends/jack.h | 2 +- alc/backends/loopback.cpp | 9 ++++----- alc/backends/loopback.h | 2 +- alc/backends/null.cpp | 5 ++--- alc/backends/null.h | 2 +- alc/backends/oboe.h | 2 +- alc/backends/opensl.cpp | 6 +++--- alc/backends/opensl.h | 2 +- alc/backends/oss.cpp | 7 +++---- alc/backends/oss.h | 2 +- alc/backends/portaudio.cpp | 8 ++++---- alc/backends/portaudio.h | 2 +- alc/backends/pulseaudio.cpp | 4 ++-- alc/backends/pulseaudio.h | 2 +- alc/backends/sndio.cpp | 6 +++--- alc/backends/sndio.h | 2 +- alc/backends/wasapi.cpp | 6 +++--- alc/backends/wasapi.h | 2 +- alc/backends/wave.cpp | 7 +++---- alc/backends/wave.h | 2 +- alc/backends/winmm.cpp | 6 +++--- alc/backends/winmm.h | 2 +- alc/effects/autowah.cpp | 4 ++-- alc/effects/base.h | 9 ++++++--- alc/effects/chorus.cpp | 4 ++-- alc/effects/compressor.cpp | 4 ++-- alc/effects/convolution.cpp | 4 ++-- alc/effects/dedicated.cpp | 4 ++-- alc/effects/distortion.cpp | 4 ++-- alc/effects/echo.cpp | 4 ++-- alc/effects/equalizer.cpp | 4 ++-- alc/effects/fshifter.cpp | 4 ++-- alc/effects/modulator.cpp | 4 ++-- alc/effects/null.cpp | 2 +- alc/effects/pshifter.cpp | 4 ++-- alc/effects/reverb.cpp | 4 ++-- alc/effects/vmorpher.cpp | 4 ++-- alc/effectslot.h | 5 ++--- core/helpers.cpp | 5 +++-- 74 files changed, 149 insertions(+), 153 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 11c3975b..8b286781 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1345,7 +1345,6 @@ target_include_directories(${IMPL_TARGET} ${INC_PATHS} ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR} - ${OpenAL_SOURCE_DIR}/alc ${OpenAL_SOURCE_DIR}/common ) diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp index 3ee0a46e..eee274b1 100644 --- a/al/auxeffectslot.cpp +++ b/al/auxeffectslot.cpp @@ -36,18 +36,18 @@ #include "AL/efx.h" #include "albit.h" -#include "alcmain.h" -#include "alcontext.h" +#include "alc/alcmain.h" +#include "alc/alcontext.h" +#include "alc/alu.h" +#include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" -#include "alu.h" #include "buffer.h" #include "core/except.h" #include "core/fpu_ctrl.h" #include "core/logging.h" #include "effect.h" -#include "inprogext.h" #include "opthelpers.h" diff --git a/al/auxeffectslot.h b/al/auxeffectslot.h index 8d7e396b..ca73c757 100644 --- a/al/auxeffectslot.h +++ b/al/auxeffectslot.h @@ -8,11 +8,11 @@ #include "AL/alc.h" #include "AL/efx.h" -#include "alcmain.h" +#include "alc/alcmain.h" +#include "alc/effectslot.h" +#include "alc/effects/base.h" #include "almalloc.h" #include "atomic.h" -#include "effectslot.h" -#include "effects/base.h" #include "intrusive_ptr.h" #include "vector.h" diff --git a/al/buffer.cpp b/al/buffer.cpp index 21f46fc8..aaffddf1 100644 --- a/al/buffer.cpp +++ b/al/buffer.cpp @@ -43,8 +43,9 @@ #include "albit.h" #include "albyte.h" -#include "alcmain.h" -#include "alcontext.h" +#include "alc/alcmain.h" +#include "alc/alcontext.h" +#include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" #include "aloptional.h" @@ -52,7 +53,6 @@ #include "core/except.h" #include "core/logging.h" #include "core/voice.h" -#include "inprogext.h" #include "opthelpers.h" diff --git a/al/buffer.h b/al/buffer.h index fe37b0af..a78c65c6 100644 --- a/al/buffer.h +++ b/al/buffer.h @@ -6,10 +6,10 @@ #include "AL/al.h" #include "albyte.h" +#include "alc/inprogext.h" #include "almalloc.h" #include "atomic.h" #include "core/buffer_storage.h" -#include "inprogext.h" #include "vector.h" diff --git a/al/effect.cpp b/al/effect.cpp index 645e41df..2c39394f 100644 --- a/al/effect.cpp +++ b/al/effect.cpp @@ -39,14 +39,14 @@ #include "AL/efx.h" #include "albit.h" -#include "alcmain.h" -#include "alcontext.h" +#include "alc/alcmain.h" +#include "alc/alcontext.h" +#include "alc/effects/base.h" #include "almalloc.h" #include "alnumeric.h" #include "alstring.h" #include "core/except.h" #include "core/logging.h" -#include "effects/base.h" #include "opthelpers.h" #include "vector.h" diff --git a/al/effects/autowah.cpp b/al/effects/autowah.cpp index 65d4b702..9abef1f7 100644 --- a/al/effects/autowah.cpp +++ b/al/effects/autowah.cpp @@ -8,7 +8,7 @@ #include "AL/efx.h" -#include "effects/base.h" +#include "alc/effects/base.h" #include "effects.h" namespace { diff --git a/al/effects/chorus.cpp b/al/effects/chorus.cpp index 60152755..704d7230 100644 --- a/al/effects/chorus.cpp +++ b/al/effects/chorus.cpp @@ -4,10 +4,10 @@ #include "AL/al.h" #include "AL/efx.h" +#include "alc/effects/base.h" #include "aloptional.h" #include "core/logging.h" #include "effects.h" -#include "effects/base.h" namespace { diff --git a/al/effects/compressor.cpp b/al/effects/compressor.cpp index 94e07431..f5db2a6f 100644 --- a/al/effects/compressor.cpp +++ b/al/effects/compressor.cpp @@ -4,8 +4,8 @@ #include "AL/al.h" #include "AL/efx.h" +#include "alc/effects/base.h" #include "effects.h" -#include "effects/base.h" namespace { diff --git a/al/effects/convolution.cpp b/al/effects/convolution.cpp index 4d87b1c4..8e850fd3 100644 --- a/al/effects/convolution.cpp +++ b/al/effects/convolution.cpp @@ -2,10 +2,10 @@ #include "config.h" #include "AL/al.h" -#include "inprogext.h" +#include "alc/inprogext.h" +#include "alc/effects/base.h" #include "effects.h" -#include "effects/base.h" namespace { diff --git a/al/effects/dedicated.cpp b/al/effects/dedicated.cpp index 334d9e56..db57003c 100644 --- a/al/effects/dedicated.cpp +++ b/al/effects/dedicated.cpp @@ -4,10 +4,10 @@ #include #include "AL/al.h" -#include "AL/efx.h" +#include "AL/alext.h" +#include "alc/effects/base.h" #include "effects.h" -#include "effects/base.h" namespace { diff --git a/al/effects/distortion.cpp b/al/effects/distortion.cpp index 8961a4d9..f5d64a9a 100644 --- a/al/effects/distortion.cpp +++ b/al/effects/distortion.cpp @@ -4,8 +4,8 @@ #include "AL/al.h" #include "AL/efx.h" +#include "alc/effects/base.h" #include "effects.h" -#include "effects/base.h" namespace { diff --git a/al/effects/echo.cpp b/al/effects/echo.cpp index 79a60521..65f691c6 100644 --- a/al/effects/echo.cpp +++ b/al/effects/echo.cpp @@ -4,8 +4,8 @@ #include "AL/al.h" #include "AL/efx.h" +#include "alc/effects/base.h" #include "effects.h" -#include "effects/base.h" namespace { diff --git a/al/effects/equalizer.cpp b/al/effects/equalizer.cpp index 3a7c0a8f..3c039688 100644 --- a/al/effects/equalizer.cpp +++ b/al/effects/equalizer.cpp @@ -4,8 +4,8 @@ #include "AL/al.h" #include "AL/efx.h" +#include "alc/effects/base.h" #include "effects.h" -#include "effects/base.h" namespace { diff --git a/al/effects/fshifter.cpp b/al/effects/fshifter.cpp index 444b0260..63621aa6 100644 --- a/al/effects/fshifter.cpp +++ b/al/effects/fshifter.cpp @@ -4,9 +4,9 @@ #include "AL/al.h" #include "AL/efx.h" +#include "alc/effects/base.h" #include "aloptional.h" #include "effects.h" -#include "effects/base.h" namespace { diff --git a/al/effects/modulator.cpp b/al/effects/modulator.cpp index 89dcc209..4c4ee485 100644 --- a/al/effects/modulator.cpp +++ b/al/effects/modulator.cpp @@ -4,9 +4,9 @@ #include "AL/al.h" #include "AL/efx.h" +#include "alc/effects/base.h" #include "aloptional.h" #include "effects.h" -#include "effects/base.h" namespace { diff --git a/al/effects/null.cpp b/al/effects/null.cpp index 0ac5278f..516446db 100644 --- a/al/effects/null.cpp +++ b/al/effects/null.cpp @@ -4,8 +4,8 @@ #include "AL/al.h" #include "AL/efx.h" +#include "alc/effects/base.h" #include "effects.h" -#include "effects/base.h" namespace { diff --git a/al/effects/pshifter.cpp b/al/effects/pshifter.cpp index e6b0b3b0..56059a3c 100644 --- a/al/effects/pshifter.cpp +++ b/al/effects/pshifter.cpp @@ -4,8 +4,8 @@ #include "AL/al.h" #include "AL/efx.h" +#include "alc/effects/base.h" #include "effects.h" -#include "effects/base.h" namespace { diff --git a/al/effects/reverb.cpp b/al/effects/reverb.cpp index caa0c81e..3f234b93 100644 --- a/al/effects/reverb.cpp +++ b/al/effects/reverb.cpp @@ -6,8 +6,8 @@ #include "AL/al.h" #include "AL/efx.h" +#include "alc/effects/base.h" #include "effects.h" -#include "effects/base.h" namespace { diff --git a/al/effects/vmorpher.cpp b/al/effects/vmorpher.cpp index 03eb2c62..076776a9 100644 --- a/al/effects/vmorpher.cpp +++ b/al/effects/vmorpher.cpp @@ -4,9 +4,9 @@ #include "AL/al.h" #include "AL/efx.h" +#include "alc/effects/base.h" #include "aloptional.h" #include "effects.h" -#include "effects/base.h" namespace { diff --git a/al/error.cpp b/al/error.cpp index 444b55aa..b6489a86 100644 --- a/al/error.cpp +++ b/al/error.cpp @@ -35,7 +35,7 @@ #include "AL/al.h" #include "AL/alc.h" -#include "alcontext.h" +#include "alc/alcontext.h" #include "almalloc.h" #include "core/except.h" #include "core/logging.h" diff --git a/al/event.cpp b/al/event.cpp index 843a90e1..5b6c1976 100644 --- a/al/event.cpp +++ b/al/event.cpp @@ -18,14 +18,14 @@ #include "AL/alc.h" #include "albyte.h" -#include "alcontext.h" +#include "alc/alcontext.h" +#include "alc/effects/base.h" +#include "alc/inprogext.h" #include "almalloc.h" #include "core/async_event.h" #include "core/except.h" #include "core/logging.h" #include "core/voice_change.h" -#include "effects/base.h" -#include "inprogext.h" #include "opthelpers.h" #include "ringbuffer.h" #include "threads.h" diff --git a/al/extension.cpp b/al/extension.cpp index 7346a5c6..bd903a6f 100644 --- a/al/extension.cpp +++ b/al/extension.cpp @@ -27,7 +27,7 @@ #include "AL/al.h" #include "AL/alc.h" -#include "alcontext.h" +#include "alc/alcontext.h" #include "alstring.h" #include "core/except.h" #include "opthelpers.h" diff --git a/al/filter.cpp b/al/filter.cpp index c3391193..09249c81 100644 --- a/al/filter.cpp +++ b/al/filter.cpp @@ -37,8 +37,8 @@ #include "AL/efx.h" #include "albit.h" -#include "alcmain.h" -#include "alcontext.h" +#include "alc/alcmain.h" +#include "alc/alcontext.h" #include "almalloc.h" #include "alnumeric.h" #include "core/except.h" diff --git a/al/listener.cpp b/al/listener.cpp index edc1ed06..00626a20 100644 --- a/al/listener.cpp +++ b/al/listener.cpp @@ -29,7 +29,7 @@ #include "AL/alc.h" #include "AL/efx.h" -#include "alcontext.h" +#include "alc/alcontext.h" #include "almalloc.h" #include "atomic.h" #include "core/except.h" diff --git a/al/source.cpp b/al/source.cpp index ca234a05..25d84f9f 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -46,16 +46,17 @@ #include "AL/efx.h" #include "albit.h" -#include "alcmain.h" -#include "alcontext.h" +#include "alc/alcmain.h" +#include "alc/alcontext.h" +#include "alc/alu.h" +#include "alc/backends/base.h" +#include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" #include "aloptional.h" #include "alspan.h" -#include "alu.h" #include "atomic.h" #include "auxeffectslot.h" -#include "backends/base.h" #include "buffer.h" #include "core/ambidefs.h" #include "core/bformatdec.h" @@ -66,7 +67,6 @@ #include "core/voice_change.h" #include "event.h" #include "filter.h" -#include "inprogext.h" #include "math_defs.h" #include "opthelpers.h" #include "ringbuffer.h" diff --git a/al/source.h b/al/source.h index 2063068a..dd3f56a7 100644 --- a/al/source.h +++ b/al/source.h @@ -11,11 +11,11 @@ #include "AL/al.h" #include "AL/alc.h" -#include "alcontext.h" +#include "alc/alcontext.h" +#include "alc/alu.h" #include "aldeque.h" #include "almalloc.h" #include "alnumeric.h" -#include "alu.h" #include "atomic.h" #include "core/voice.h" #include "math_defs.h" diff --git a/al/state.cpp b/al/state.cpp index 950d64fc..b0b05972 100644 --- a/al/state.cpp +++ b/al/state.cpp @@ -32,17 +32,17 @@ #include "AL/alc.h" #include "AL/alext.h" -#include "alcmain.h" -#include "alcontext.h" +#include "alc/alcmain.h" +#include "alc/alcontext.h" +#include "alc/alu.h" +#include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" -#include "alu.h" #include "atomic.h" #include "core/except.h" #include "core/voice.h" #include "event.h" -#include "inprogext.h" #include "opthelpers.h" #include "strutils.h" diff --git a/alc/backends/alsa.cpp b/alc/backends/alsa.cpp index a6c7e178..f6f318eb 100644 --- a/alc/backends/alsa.cpp +++ b/alc/backends/alsa.cpp @@ -20,7 +20,7 @@ #include "config.h" -#include "backends/alsa.h" +#include "alsa.h" #include #include @@ -36,12 +36,11 @@ #include #include "albyte.h" -#include "alcmain.h" -#include "alconfig.h" +#include "alc/alconfig.h" #include "almalloc.h" #include "alnumeric.h" #include "aloptional.h" -#include "alu.h" +#include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "dynload.h" diff --git a/alc/backends/alsa.h b/alc/backends/alsa.h index 1a28e6b9..b256dcf5 100644 --- a/alc/backends/alsa.h +++ b/alc/backends/alsa.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_ALSA_H #define BACKENDS_ALSA_H -#include "backends/base.h" +#include "base.h" struct AlsaBackendFactory final : public BackendFactory { public: diff --git a/alc/backends/coreaudio.cpp b/alc/backends/coreaudio.cpp index 45c814bf..81ce32bc 100644 --- a/alc/backends/coreaudio.cpp +++ b/alc/backends/coreaudio.cpp @@ -20,23 +20,22 @@ #include "config.h" -#include "backends/coreaudio.h" +#include "coreaudio.h" -#include +#include #include #include #include +#include #include -#include "alcmain.h" -#include "alu.h" -#include "ringbuffer.h" +#include "alnumeric.h" #include "core/converter.h" +#include "core/device.h" #include "core/logging.h" -#include "backends/base.h" +#include "ringbuffer.h" -#include #include #include diff --git a/alc/backends/coreaudio.h b/alc/backends/coreaudio.h index 19e50eab..1252edde 100644 --- a/alc/backends/coreaudio.h +++ b/alc/backends/coreaudio.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_COREAUDIO_H #define BACKENDS_COREAUDIO_H -#include "backends/base.h" +#include "base.h" struct CoreAudioBackendFactory final : public BackendFactory { public: diff --git a/alc/backends/dsound.cpp b/alc/backends/dsound.cpp index 928ff507..401f8165 100644 --- a/alc/backends/dsound.cpp +++ b/alc/backends/dsound.cpp @@ -20,7 +20,7 @@ #include "config.h" -#include "backends/dsound.h" +#include "dsound.h" #define WIN32_LEAN_AND_MEAN #include @@ -44,9 +44,9 @@ #include #include -#include "alcmain.h" -#include "alu.h" +#include "alnumeric.h" #include "comptr.h" +#include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "dynload.h" @@ -175,7 +175,7 @@ struct DSoundPlayback final : public BackendBase { int mixerProc(); - void open(const ALCchar *name) override; + void open(const char *name) override; bool reset() override; void start() override; void stop() override; diff --git a/alc/backends/dsound.h b/alc/backends/dsound.h index 3b5b344b..787f227a 100644 --- a/alc/backends/dsound.h +++ b/alc/backends/dsound.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_DSOUND_H #define BACKENDS_DSOUND_H -#include "backends/base.h" +#include "base.h" struct DSoundBackendFactory final : public BackendFactory { public: diff --git a/alc/backends/jack.cpp b/alc/backends/jack.cpp index b7b35217..54bd19e6 100644 --- a/alc/backends/jack.cpp +++ b/alc/backends/jack.cpp @@ -20,7 +20,7 @@ #include "config.h" -#include "backends/jack.h" +#include "jack.h" #include #include @@ -31,9 +31,9 @@ #include #include -#include "alcmain.h" -#include "alu.h" -#include "alconfig.h" +#include "alc/alconfig.h" +#include "alnumeric.h" +#include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "dynload.h" diff --git a/alc/backends/jack.h b/alc/backends/jack.h index 6f81a209..b83f24dd 100644 --- a/alc/backends/jack.h +++ b/alc/backends/jack.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_JACK_H #define BACKENDS_JACK_H -#include "backends/base.h" +#include "base.h" struct JackBackendFactory final : public BackendFactory { public: diff --git a/alc/backends/loopback.cpp b/alc/backends/loopback.cpp index bafe8cc8..bf4ab246 100644 --- a/alc/backends/loopback.cpp +++ b/alc/backends/loopback.cpp @@ -20,10 +20,9 @@ #include "config.h" -#include "backends/loopback.h" +#include "loopback.h" -#include "alcmain.h" -#include "alu.h" +#include "core/device.h" namespace { @@ -31,7 +30,7 @@ namespace { struct LoopbackBackend final : public BackendBase { LoopbackBackend(DeviceBase *device) noexcept : BackendBase{device} { } - void open(const ALCchar *name) override; + void open(const char *name) override; bool reset() override; void start() override; void stop() override; @@ -40,7 +39,7 @@ struct LoopbackBackend final : public BackendBase { }; -void LoopbackBackend::open(const ALCchar *name) +void LoopbackBackend::open(const char *name) { mDevice->DeviceName = name; } diff --git a/alc/backends/loopback.h b/alc/backends/loopback.h index 1a031a1f..cb42b3c8 100644 --- a/alc/backends/loopback.h +++ b/alc/backends/loopback.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_LOOPBACK_H #define BACKENDS_LOOPBACK_H -#include "backends/base.h" +#include "base.h" struct LoopbackBackendFactory final : public BackendFactory { public: diff --git a/alc/backends/null.cpp b/alc/backends/null.cpp index 763d4d2e..5a8fc255 100644 --- a/alc/backends/null.cpp +++ b/alc/backends/null.cpp @@ -20,7 +20,7 @@ #include "config.h" -#include "backends/null.h" +#include "null.h" #include #include @@ -30,9 +30,8 @@ #include #include -#include "alcmain.h" +#include "core/device.h" #include "almalloc.h" -#include "alu.h" #include "core/helpers.h" #include "threads.h" diff --git a/alc/backends/null.h b/alc/backends/null.h index c9d1b3b3..7048cad6 100644 --- a/alc/backends/null.h +++ b/alc/backends/null.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_NULL_H #define BACKENDS_NULL_H -#include "backends/base.h" +#include "base.h" struct NullBackendFactory final : public BackendFactory { public: diff --git a/alc/backends/oboe.h b/alc/backends/oboe.h index 228ec807..a39c2445 100644 --- a/alc/backends/oboe.h +++ b/alc/backends/oboe.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_OBOE_H #define BACKENDS_OBOE_H -#include "backends/base.h" +#include "base.h" struct OboeBackendFactory final : public BackendFactory { public: diff --git a/alc/backends/opensl.cpp b/alc/backends/opensl.cpp index af656470..7be7f031 100644 --- a/alc/backends/opensl.cpp +++ b/alc/backends/opensl.cpp @@ -21,7 +21,7 @@ #include "config.h" -#include "backends/opensl.h" +#include "opensl.h" #include #include @@ -33,8 +33,8 @@ #include #include "albit.h" -#include "alcmain.h" -#include "alu.h" +#include "alnumeric.h" +#include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "opthelpers.h" diff --git a/alc/backends/opensl.h b/alc/backends/opensl.h index 36586bbb..b8162447 100644 --- a/alc/backends/opensl.h +++ b/alc/backends/opensl.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_OSL_H #define BACKENDS_OSL_H -#include "backends/base.h" +#include "base.h" struct OSLBackendFactory final : public BackendFactory { public: diff --git a/alc/backends/oss.cpp b/alc/backends/oss.cpp index d769a1eb..6d4fa261 100644 --- a/alc/backends/oss.cpp +++ b/alc/backends/oss.cpp @@ -20,7 +20,7 @@ #include "config.h" -#include "backends/oss.h" +#include "oss.h" #include #include @@ -41,13 +41,12 @@ #include #include -#include "alcmain.h" -#include "alconfig.h" #include "albyte.h" +#include "alc/alconfig.h" #include "almalloc.h" #include "alnumeric.h" #include "aloptional.h" -#include "alu.h" +#include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "ringbuffer.h" diff --git a/alc/backends/oss.h b/alc/backends/oss.h index 798da456..4f2c00b9 100644 --- a/alc/backends/oss.h +++ b/alc/backends/oss.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_OSS_H #define BACKENDS_OSS_H -#include "backends/base.h" +#include "base.h" struct OSSBackendFactory final : public BackendFactory { public: diff --git a/alc/backends/portaudio.cpp b/alc/backends/portaudio.cpp index 8f41b12a..2d5cd36d 100644 --- a/alc/backends/portaudio.cpp +++ b/alc/backends/portaudio.cpp @@ -20,15 +20,15 @@ #include "config.h" -#include "backends/portaudio.h" +#include "portaudio.h" #include #include #include -#include "alcmain.h" -#include "alu.h" -#include "alconfig.h" +#include "alc/alconfig.h" +#include "alnumeric.h" +#include "core/device.h" #include "core/logging.h" #include "dynload.h" #include "ringbuffer.h" diff --git a/alc/backends/portaudio.h b/alc/backends/portaudio.h index 34bb8d1b..c35ccff2 100644 --- a/alc/backends/portaudio.h +++ b/alc/backends/portaudio.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_PORTAUDIO_H #define BACKENDS_PORTAUDIO_H -#include "backends/base.h" +#include "base.h" struct PortBackendFactory final : public BackendFactory { public: diff --git a/alc/backends/pulseaudio.cpp b/alc/backends/pulseaudio.cpp index da1ffcb0..23bcecad 100644 --- a/alc/backends/pulseaudio.cpp +++ b/alc/backends/pulseaudio.cpp @@ -21,7 +21,7 @@ #include "config.h" -#include "backends/pulseaudio.h" +#include "pulseaudio.h" #include #include @@ -43,7 +43,7 @@ #include #include "albyte.h" -#include "alconfig.h" +#include "alc/alconfig.h" #include "almalloc.h" #include "alnumeric.h" #include "aloptional.h" diff --git a/alc/backends/pulseaudio.h b/alc/backends/pulseaudio.h index 60ebbc36..6690fe8a 100644 --- a/alc/backends/pulseaudio.h +++ b/alc/backends/pulseaudio.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_PULSEAUDIO_H #define BACKENDS_PULSEAUDIO_H -#include "backends/base.h" +#include "base.h" class PulseBackendFactory final : public BackendFactory { public: diff --git a/alc/backends/sndio.cpp b/alc/backends/sndio.cpp index c7c42210..2d8b424c 100644 --- a/alc/backends/sndio.cpp +++ b/alc/backends/sndio.cpp @@ -20,7 +20,7 @@ #include "config.h" -#include "backends/sndio.h" +#include "sndio.h" #include #include @@ -29,8 +29,8 @@ #include #include -#include "alcmain.h" -#include "alu.h" +#include "alnumeric.h" +#include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "ringbuffer.h" diff --git a/alc/backends/sndio.h b/alc/backends/sndio.h index 9b93199f..d9433191 100644 --- a/alc/backends/sndio.h +++ b/alc/backends/sndio.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_SNDIO_H #define BACKENDS_SNDIO_H -#include "backends/base.h" +#include "base.h" struct SndIOBackendFactory final : public BackendFactory { public: diff --git a/alc/backends/wasapi.cpp b/alc/backends/wasapi.cpp index 114c69e0..d38fc25d 100644 --- a/alc/backends/wasapi.cpp +++ b/alc/backends/wasapi.cpp @@ -20,7 +20,7 @@ #include "config.h" -#include "backends/wasapi.h" +#include "wasapi.h" #define WIN32_LEAN_AND_MEAN #include @@ -56,10 +56,10 @@ #include #include "albit.h" -#include "alcmain.h" -#include "alu.h" +#include "alnumeric.h" #include "comptr.h" #include "core/converter.h" +#include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "ringbuffer.h" diff --git a/alc/backends/wasapi.h b/alc/backends/wasapi.h index fa4b811e..bb2671ee 100644 --- a/alc/backends/wasapi.h +++ b/alc/backends/wasapi.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_WASAPI_H #define BACKENDS_WASAPI_H -#include "backends/base.h" +#include "base.h" struct WasapiBackendFactory final : public BackendFactory { public: diff --git a/alc/backends/wave.cpp b/alc/backends/wave.cpp index 434a5fb1..259cbc62 100644 --- a/alc/backends/wave.cpp +++ b/alc/backends/wave.cpp @@ -20,7 +20,7 @@ #include "config.h" -#include "backends/wave.h" +#include "wave.h" #include #include @@ -35,11 +35,10 @@ #include "albit.h" #include "albyte.h" -#include "alcmain.h" -#include "alconfig.h" +#include "alc/alconfig.h" #include "almalloc.h" #include "alnumeric.h" -#include "alu.h" +#include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "opthelpers.h" diff --git a/alc/backends/wave.h b/alc/backends/wave.h index 926e2198..e768d336 100644 --- a/alc/backends/wave.h +++ b/alc/backends/wave.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_WAVE_H #define BACKENDS_WAVE_H -#include "backends/base.h" +#include "base.h" struct WaveBackendFactory final : public BackendFactory { public: diff --git a/alc/backends/winmm.cpp b/alc/backends/winmm.cpp index c346a828..42aee313 100644 --- a/alc/backends/winmm.cpp +++ b/alc/backends/winmm.cpp @@ -20,7 +20,7 @@ #include "config.h" -#include "backends/winmm.h" +#include "winmm.h" #include #include @@ -38,8 +38,8 @@ #include #include -#include "alcmain.h" -#include "alu.h" +#include "alnumeric.h" +#include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "ringbuffer.h" diff --git a/alc/backends/winmm.h b/alc/backends/winmm.h index a376b8a4..45a706aa 100644 --- a/alc/backends/winmm.h +++ b/alc/backends/winmm.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_WINMM_H #define BACKENDS_WINMM_H -#include "backends/base.h" +#include "base.h" struct WinMMBackendFactory final : public BackendFactory { public: diff --git a/alc/effects/autowah.cpp b/alc/effects/autowah.cpp index 6016b8ab..9c2ec335 100644 --- a/alc/effects/autowah.cpp +++ b/alc/effects/autowah.cpp @@ -26,6 +26,8 @@ #include #include +#include "alc/effects/base.h" +#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -35,8 +37,6 @@ #include "core/devformat.h" #include "core/device.h" #include "core/mixer.h" -#include "effects/base.h" -#include "effectslot.h" #include "intrusive_ptr.h" #include "math_defs.h" diff --git a/alc/effects/base.h b/alc/effects/base.h index 6c31ae0c..1fb339aa 100644 --- a/alc/effects/base.h +++ b/alc/effects/base.h @@ -1,18 +1,21 @@ #ifndef EFFECTS_BASE_H #define EFFECTS_BASE_H -#include +#include #include "albyte.h" -#include "alcmain.h" #include "almalloc.h" #include "alspan.h" #include "atomic.h" +#include "core/bufferline.h" #include "intrusive_ptr.h" +struct BufferStorage; struct ContextBase; +struct DeviceBase; struct EffectSlot; -struct BufferStorage; +struct MixParams; +struct RealMixParams; /** Target gain for the reverb decay feedback reaching the decay time. */ diff --git a/alc/effects/chorus.cpp b/alc/effects/chorus.cpp index 50cf1e40..3a1b9ae4 100644 --- a/alc/effects/chorus.cpp +++ b/alc/effects/chorus.cpp @@ -26,6 +26,8 @@ #include #include +#include "alc/effects/base.h" +#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -36,8 +38,6 @@ #include "core/mixer.h" #include "core/mixer/defs.h" #include "core/resampler_limits.h" -#include "effects/base.h" -#include "effectslot.h" #include "intrusive_ptr.h" #include "math_defs.h" #include "opthelpers.h" diff --git a/alc/effects/compressor.cpp b/alc/effects/compressor.cpp index 9a6f9b5d..030bfe08 100644 --- a/alc/effects/compressor.cpp +++ b/alc/effects/compressor.cpp @@ -37,6 +37,8 @@ #include #include +#include "alc/effects/base.h" +#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -46,8 +48,6 @@ #include "core/device.h" #include "core/mixer.h" #include "core/mixer/defs.h" -#include "effects/base.h" -#include "effectslot.h" #include "intrusive_ptr.h" struct ContextBase; diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index fd68ea41..ca5a7321 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -19,9 +19,11 @@ #include "albyte.h" #include "alcomplex.h" +#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" +#include "base.h" #include "core/ambidefs.h" #include "core/bufferline.h" #include "core/buffer_storage.h" @@ -31,8 +33,6 @@ #include "core/filters/splitter.h" #include "core/fmt_traits.h" #include "core/mixer.h" -#include "effects/base.h" -#include "effectslot.h" #include "intrusive_ptr.h" #include "math_defs.h" #include "polyphase_resampler.h" diff --git a/alc/effects/dedicated.cpp b/alc/effects/dedicated.cpp index 6afd7baa..e7ea89e0 100644 --- a/alc/effects/dedicated.cpp +++ b/alc/effects/dedicated.cpp @@ -25,14 +25,14 @@ #include #include +#include "alc/effects/base.h" +#include "alc/effectslot.h" #include "almalloc.h" #include "alspan.h" #include "core/bufferline.h" #include "core/devformat.h" #include "core/device.h" #include "core/mixer.h" -#include "effects/base.h" -#include "effectslot.h" #include "intrusive_ptr.h" struct ContextBase; diff --git a/alc/effects/distortion.cpp b/alc/effects/distortion.cpp index a3f287c0..26b4df8e 100644 --- a/alc/effects/distortion.cpp +++ b/alc/effects/distortion.cpp @@ -25,6 +25,8 @@ #include #include +#include "alc/effects/base.h" +#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -35,8 +37,6 @@ #include "core/filters/biquad.h" #include "core/mixer.h" #include "core/mixer/defs.h" -#include "effects/base.h" -#include "effectslot.h" #include "intrusive_ptr.h" #include "math_defs.h" diff --git a/alc/effects/echo.cpp b/alc/effects/echo.cpp index 4fccabfe..4cdef37c 100644 --- a/alc/effects/echo.cpp +++ b/alc/effects/echo.cpp @@ -26,6 +26,8 @@ #include #include +#include "alc/effects/base.h" +#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -35,8 +37,6 @@ #include "core/device.h" #include "core/filters/biquad.h" #include "core/mixer.h" -#include "effects/base.h" -#include "effectslot.h" #include "intrusive_ptr.h" #include "opthelpers.h" #include "vector.h" diff --git a/alc/effects/equalizer.cpp b/alc/effects/equalizer.cpp index aedf537e..800330a3 100644 --- a/alc/effects/equalizer.cpp +++ b/alc/effects/equalizer.cpp @@ -27,6 +27,8 @@ #include #include +#include "alc/effects/base.h" +#include "alc/effectslot.h" #include "almalloc.h" #include "alspan.h" #include "core/ambidefs.h" @@ -36,8 +38,6 @@ #include "core/device.h" #include "core/filters/biquad.h" #include "core/mixer.h" -#include "effects/base.h" -#include "effectslot.h" #include "intrusive_ptr.h" diff --git a/alc/effects/fshifter.cpp b/alc/effects/fshifter.cpp index e19809bb..c25aab82 100644 --- a/alc/effects/fshifter.cpp +++ b/alc/effects/fshifter.cpp @@ -27,6 +27,8 @@ #include #include +#include "alc/effects/base.h" +#include "alc/effectslot.h" #include "alcomplex.h" #include "almalloc.h" #include "alnumeric.h" @@ -37,8 +39,6 @@ #include "core/device.h" #include "core/mixer.h" #include "core/mixer/defs.h" -#include "effects/base.h" -#include "effectslot.h" #include "intrusive_ptr.h" #include "math_defs.h" diff --git a/alc/effects/modulator.cpp b/alc/effects/modulator.cpp index ded35805..a518ff63 100644 --- a/alc/effects/modulator.cpp +++ b/alc/effects/modulator.cpp @@ -25,6 +25,8 @@ #include #include +#include "alc/effects/base.h" +#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -35,8 +37,6 @@ #include "core/device.h" #include "core/filters/biquad.h" #include "core/mixer.h" -#include "effects/base.h" -#include "effectslot.h" #include "intrusive_ptr.h" #include "math_defs.h" diff --git a/alc/effects/null.cpp b/alc/effects/null.cpp index 1413909f..cda1420e 100644 --- a/alc/effects/null.cpp +++ b/alc/effects/null.cpp @@ -5,8 +5,8 @@ #include "almalloc.h" #include "alspan.h" +#include "base.h" #include "core/bufferline.h" -#include "effects/base.h" #include "intrusive_ptr.h" struct ContextBase; diff --git a/alc/effects/pshifter.cpp b/alc/effects/pshifter.cpp index 5bf813e5..26115605 100644 --- a/alc/effects/pshifter.cpp +++ b/alc/effects/pshifter.cpp @@ -27,6 +27,8 @@ #include #include +#include "alc/effects/base.h" +#include "alc/effectslot.h" #include "alcomplex.h" #include "almalloc.h" #include "alnumeric.h" @@ -36,8 +38,6 @@ #include "core/device.h" #include "core/mixer.h" #include "core/mixer/defs.h" -#include "effects/base.h" -#include "effectslot.h" #include "intrusive_ptr.h" #include "math_defs.h" diff --git a/alc/effects/reverb.cpp b/alc/effects/reverb.cpp index 0db27210..d6f1dbbf 100644 --- a/alc/effects/reverb.cpp +++ b/alc/effects/reverb.cpp @@ -28,6 +28,8 @@ #include #include +#include "alc/effects/base.h" +#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -40,8 +42,6 @@ #include "core/filters/splitter.h" #include "core/mixer.h" #include "core/mixer/defs.h" -#include "effects/base.h" -#include "effectslot.h" #include "intrusive_ptr.h" #include "math_defs.h" #include "opthelpers.h" diff --git a/alc/effects/vmorpher.cpp b/alc/effects/vmorpher.cpp index c3790e30..6c419ba2 100644 --- a/alc/effects/vmorpher.cpp +++ b/alc/effects/vmorpher.cpp @@ -38,6 +38,8 @@ #include #include +#include "alc/effects/base.h" +#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -47,8 +49,6 @@ #include "core/devformat.h" #include "core/device.h" #include "core/mixer.h" -#include "effects/base.h" -#include "effectslot.h" #include "intrusive_ptr.h" #include "math_defs.h" diff --git a/alc/effectslot.h b/alc/effectslot.h index c1eb1cc3..cbb1a2f5 100644 --- a/alc/effectslot.h +++ b/alc/effectslot.h @@ -1,14 +1,13 @@ #ifndef EFFECTSLOT_H #define EFFECTSLOT_H -#include +#include #include "almalloc.h" -#include "alcmain.h" +#include "core/device.h" #include "effects/base.h" #include "intrusive_ptr.h" - struct EffectSlot; struct WetBuffer; diff --git a/core/helpers.cpp b/core/helpers.cpp index a2f1a6c9..94219544 100644 --- a/core/helpers.cpp +++ b/core/helpers.cpp @@ -10,9 +10,9 @@ #include #include #include +#include #include -#include "alcmain.h" #include "almalloc.h" #include "alfstream.h" #include "aloptional.h" @@ -199,7 +199,8 @@ void SetRTPriority(void) #include #include -#include "core/rtkit.h" +#include "dbus_wrap.h" +#include "rtkit.h" #ifndef RLIMIT_RTTIME #define RLIMIT_RTTIME 15 #endif -- cgit v1.2.3 From 06dcfc3fcfe7a93e9ec53406077b3211579cc92f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 27 Apr 2021 16:40:22 -0700 Subject: Rename alcmain.h to device.h --- CMakeLists.txt | 3 +- al/auxeffectslot.cpp | 2 +- al/auxeffectslot.h | 2 +- al/buffer.cpp | 2 +- al/effect.cpp | 2 +- al/filter.cpp | 2 +- al/source.cpp | 2 +- al/state.cpp | 12 ++--- alc/alc.cpp | 2 +- alc/alcmain.h | 127 ----------------------------------------------- alc/backends/sdl2.cpp | 5 +- alc/backends/sdl2.h | 2 +- alc/backends/solaris.cpp | 7 ++- alc/backends/solaris.h | 2 +- alc/device.cpp | 4 ++ alc/device.h | 110 ++++++++++++++++++++++++++++++++++++++++ alc/panning.cpp | 2 +- 17 files changed, 137 insertions(+), 151 deletions(-) delete mode 100644 alc/alcmain.h create mode 100644 alc/device.cpp create mode 100644 alc/device.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b286781..ac1e31d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -763,12 +763,13 @@ set(OPENAL_OBJS # ALC and related routines set(ALC_OBJS alc/alc.cpp - alc/alcmain.h alc/alu.cpp alc/alu.h alc/alconfig.cpp alc/alconfig.h alc/alcontext.h + alc/device.cpp + alc/device.h alc/effectslot.cpp alc/effectslot.h alc/effects/base.h diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp index eee274b1..83904a44 100644 --- a/al/auxeffectslot.cpp +++ b/al/auxeffectslot.cpp @@ -36,9 +36,9 @@ #include "AL/efx.h" #include "albit.h" -#include "alc/alcmain.h" #include "alc/alcontext.h" #include "alc/alu.h" +#include "alc/device.h" #include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" diff --git a/al/auxeffectslot.h b/al/auxeffectslot.h index ca73c757..27423830 100644 --- a/al/auxeffectslot.h +++ b/al/auxeffectslot.h @@ -8,7 +8,7 @@ #include "AL/alc.h" #include "AL/efx.h" -#include "alc/alcmain.h" +#include "alc/device.h" #include "alc/effectslot.h" #include "alc/effects/base.h" #include "almalloc.h" diff --git a/al/buffer.cpp b/al/buffer.cpp index aaffddf1..4623ae30 100644 --- a/al/buffer.cpp +++ b/al/buffer.cpp @@ -43,8 +43,8 @@ #include "albit.h" #include "albyte.h" -#include "alc/alcmain.h" #include "alc/alcontext.h" +#include "alc/device.h" #include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" diff --git a/al/effect.cpp b/al/effect.cpp index 2c39394f..5b10df2e 100644 --- a/al/effect.cpp +++ b/al/effect.cpp @@ -39,8 +39,8 @@ #include "AL/efx.h" #include "albit.h" -#include "alc/alcmain.h" #include "alc/alcontext.h" +#include "alc/device.h" #include "alc/effects/base.h" #include "almalloc.h" #include "alnumeric.h" diff --git a/al/filter.cpp b/al/filter.cpp index 09249c81..0c4e647f 100644 --- a/al/filter.cpp +++ b/al/filter.cpp @@ -37,8 +37,8 @@ #include "AL/efx.h" #include "albit.h" -#include "alc/alcmain.h" #include "alc/alcontext.h" +#include "alc/device.h" #include "almalloc.h" #include "alnumeric.h" #include "core/except.h" diff --git a/al/source.cpp b/al/source.cpp index 25d84f9f..9c45ebda 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -46,10 +46,10 @@ #include "AL/efx.h" #include "albit.h" -#include "alc/alcmain.h" #include "alc/alcontext.h" #include "alc/alu.h" #include "alc/backends/base.h" +#include "alc/device.h" #include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" diff --git a/al/state.cpp b/al/state.cpp index b0b05972..396c15c7 100644 --- a/al/state.cpp +++ b/al/state.cpp @@ -24,25 +24,25 @@ #include #include -#include -#include #include +#include +#include #include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" -#include "alc/alcmain.h" #include "alc/alcontext.h" #include "alc/alu.h" #include "alc/inprogext.h" -#include "almalloc.h" #include "alnumeric.h" -#include "alspan.h" +#include "aloptional.h" #include "atomic.h" +#include "core/context.h" #include "core/except.h" +#include "core/mixer/defs.h" #include "core/voice.h" -#include "event.h" +#include "intrusive_ptr.h" #include "opthelpers.h" #include "strutils.h" diff --git a/alc/alc.cpp b/alc/alc.cpp index 1f7b999d..9eface89 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -66,7 +66,6 @@ #include "al/listener.h" #include "al/source.h" #include "albit.h" -#include "alcmain.h" #include "albyte.h" #include "alconfig.h" #include "alcontext.h" @@ -94,6 +93,7 @@ #include "core/logging.h" #include "core/uhjfilter.h" #include "core/voice_change.h" +#include "device.h" #include "effects/base.h" #include "inprogext.h" #include "intrusive_ptr.h" diff --git a/alc/alcmain.h b/alc/alcmain.h deleted file mode 100644 index eb25b596..00000000 --- a/alc/alcmain.h +++ /dev/null @@ -1,127 +0,0 @@ -#ifndef ALC_MAIN_H -#define ALC_MAIN_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "AL/al.h" -#include "AL/alc.h" -#include "AL/alext.h" - -#include "almalloc.h" -#include "alnumeric.h" -#include "alspan.h" -#include "atomic.h" -#include "core/ambidefs.h" -#include "core/bufferline.h" -#include "core/devformat.h" -#include "core/device.h" -#include "core/filters/splitter.h" -#include "core/hrtf.h" -#include "core/mixer/defs.h" -#include "inprogext.h" -#include "intrusive_ptr.h" -#include "vector.h" - -struct ALbuffer; -struct ALeffect; -struct ALfilter; - -using uint = unsigned int; - - -struct BufferSubList { - uint64_t FreeMask{~0_u64}; - ALbuffer *Buffers{nullptr}; /* 64 */ - - BufferSubList() noexcept = default; - BufferSubList(const BufferSubList&) = delete; - BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers} - { rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; } - ~BufferSubList(); - - BufferSubList& operator=(const BufferSubList&) = delete; - BufferSubList& operator=(BufferSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; } -}; - -struct EffectSubList { - uint64_t FreeMask{~0_u64}; - ALeffect *Effects{nullptr}; /* 64 */ - - EffectSubList() noexcept = default; - EffectSubList(const EffectSubList&) = delete; - EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects} - { rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; } - ~EffectSubList(); - - EffectSubList& operator=(const EffectSubList&) = delete; - EffectSubList& operator=(EffectSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; } -}; - -struct FilterSubList { - uint64_t FreeMask{~0_u64}; - ALfilter *Filters{nullptr}; /* 64 */ - - FilterSubList() noexcept = default; - FilterSubList(const FilterSubList&) = delete; - FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters} - { rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; } - ~FilterSubList(); - - FilterSubList& operator=(const FilterSubList&) = delete; - FilterSubList& operator=(FilterSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; } -}; - - -struct ALCdevice : public al::intrusive_ref, DeviceBase { - ALCuint NumMonoSources{}; - ALCuint NumStereoSources{}; - - // Maximum number of sources that can be created - uint SourcesMax{}; - // Maximum number of slots that can be created - uint AuxiliaryEffectSlotMax{}; - - std::string mHrtfName; - al::vector mHrtfList; - ALCenum mHrtfStatus{ALC_FALSE}; - - ALCenum LimiterState{ALC_DONT_CARE_SOFT}; - - std::atomic LastError{ALC_NO_ERROR}; - - // Map of Buffers for this device - std::mutex BufferLock; - al::vector BufferList; - - // Map of Effects for this device - std::mutex EffectLock; - al::vector EffectList; - - // Map of Filters for this device - std::mutex FilterLock; - al::vector FilterList; - - - ALCdevice(DeviceType type) : DeviceBase{type} { } - ~ALCdevice(); - - void enumerateHrtfs(); - - DEF_NEWDEL(ALCdevice) -}; - -#endif diff --git a/alc/backends/sdl2.cpp b/alc/backends/sdl2.cpp index 800ae935..fe8ece31 100644 --- a/alc/backends/sdl2.cpp +++ b/alc/backends/sdl2.cpp @@ -20,16 +20,15 @@ #include "config.h" -#include "backends/sdl2.h" +#include "sdl2.h" #include #include #include #include -#include "alcmain.h" #include "almalloc.h" -#include "alu.h" +#include "core/device.h" #include "core/logging.h" #include diff --git a/alc/backends/sdl2.h b/alc/backends/sdl2.h index 3844b8bf..3bd8df86 100644 --- a/alc/backends/sdl2.h +++ b/alc/backends/sdl2.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_SDL2_H #define BACKENDS_SDL2_H -#include "backends/base.h" +#include "base.h" struct SDL2BackendFactory final : public BackendFactory { public: diff --git a/alc/backends/solaris.cpp b/alc/backends/solaris.cpp index 20414543..26f5a5c5 100644 --- a/alc/backends/solaris.cpp +++ b/alc/backends/solaris.cpp @@ -20,7 +20,7 @@ #include "config.h" -#include "backends/solaris.h" +#include "solaris.h" #include #include @@ -39,10 +39,9 @@ #include #include -#include "alcmain.h" #include "albyte.h" -#include "alu.h" -#include "alconfig.h" +#include "alc/alconfig.h" +#include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "threads.h" diff --git a/alc/backends/solaris.h b/alc/backends/solaris.h index 4e80cf9a..5da6ad3a 100644 --- a/alc/backends/solaris.h +++ b/alc/backends/solaris.h @@ -1,7 +1,7 @@ #ifndef BACKENDS_SOLARIS_H #define BACKENDS_SOLARIS_H -#include "backends/base.h" +#include "base.h" struct SolarisBackendFactory final : public BackendFactory { public: diff --git a/alc/device.cpp b/alc/device.cpp new file mode 100644 index 00000000..954a790b --- /dev/null +++ b/alc/device.cpp @@ -0,0 +1,4 @@ + +#include "config.h" + +#include "device.h" diff --git a/alc/device.h b/alc/device.h new file mode 100644 index 00000000..2f465b16 --- /dev/null +++ b/alc/device.h @@ -0,0 +1,110 @@ +#ifndef ALC_DEVICE_H +#define ALC_DEVICE_H + +#include +#include +#include +#include +#include + +#include "AL/alc.h" +#include "AL/alext.h" + +#include "almalloc.h" +#include "alnumeric.h" +#include "core/device.h" +#include "intrusive_ptr.h" +#include "vector.h" + +struct ALbuffer; +struct ALeffect; +struct ALfilter; + +using uint = unsigned int; + + +struct BufferSubList { + uint64_t FreeMask{~0_u64}; + ALbuffer *Buffers{nullptr}; /* 64 */ + + BufferSubList() noexcept = default; + BufferSubList(const BufferSubList&) = delete; + BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers} + { rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; } + ~BufferSubList(); + + BufferSubList& operator=(const BufferSubList&) = delete; + BufferSubList& operator=(BufferSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; } +}; + +struct EffectSubList { + uint64_t FreeMask{~0_u64}; + ALeffect *Effects{nullptr}; /* 64 */ + + EffectSubList() noexcept = default; + EffectSubList(const EffectSubList&) = delete; + EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects} + { rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; } + ~EffectSubList(); + + EffectSubList& operator=(const EffectSubList&) = delete; + EffectSubList& operator=(EffectSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; } +}; + +struct FilterSubList { + uint64_t FreeMask{~0_u64}; + ALfilter *Filters{nullptr}; /* 64 */ + + FilterSubList() noexcept = default; + FilterSubList(const FilterSubList&) = delete; + FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters} + { rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; } + ~FilterSubList(); + + FilterSubList& operator=(const FilterSubList&) = delete; + FilterSubList& operator=(FilterSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; } +}; + + +struct ALCdevice : public al::intrusive_ref, DeviceBase { + ALCuint NumMonoSources{}; + ALCuint NumStereoSources{}; + + // Maximum number of sources that can be created + uint SourcesMax{}; + // Maximum number of slots that can be created + uint AuxiliaryEffectSlotMax{}; + + std::string mHrtfName; + al::vector mHrtfList; + ALCenum mHrtfStatus{ALC_FALSE}; + + ALCenum LimiterState{ALC_DONT_CARE_SOFT}; + + std::atomic LastError{ALC_NO_ERROR}; + + // Map of Buffers for this device + std::mutex BufferLock; + al::vector BufferList; + + // Map of Effects for this device + std::mutex EffectLock; + al::vector EffectList; + + // Map of Filters for this device + std::mutex FilterLock; + al::vector FilterList; + + + ALCdevice(DeviceType type) : DeviceBase{type} { } + ~ALCdevice(); + + void enumerateHrtfs(); + + DEF_NEWDEL(ALCdevice) +}; + +#endif diff --git a/alc/panning.cpp b/alc/panning.cpp index 5e263111..00fe9023 100644 --- a/alc/panning.cpp +++ b/alc/panning.cpp @@ -38,7 +38,6 @@ #include "AL/alext.h" #include "al/auxeffectslot.h" -#include "alcmain.h" #include "alconfig.h" #include "alcontext.h" #include "almalloc.h" @@ -56,6 +55,7 @@ #include "core/hrtf.h" #include "core/logging.h" #include "core/uhjfilter.h" +#include "device.h" #include "math_defs.h" #include "opthelpers.h" -- cgit v1.2.3 From e3c0b60cc6cd5c0c0ff8e5dd672c947db3aeeeef Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 27 Apr 2021 18:31:20 -0700 Subject: Rename alcontext.h and move some functions to context.cpp --- CMakeLists.txt | 3 +- al/auxeffectslot.cpp | 2 +- al/buffer.cpp | 2 +- al/effect.cpp | 3 +- al/error.cpp | 2 +- al/event.cpp | 2 +- al/extension.cpp | 2 +- al/filter.cpp | 2 +- al/listener.cpp | 2 +- al/source.cpp | 2 +- al/source.h | 2 +- al/state.cpp | 2 +- alc/alc.cpp | 488 ++++++--------------------------------------------- alc/alcontext.h | 162 ----------------- alc/context.cpp | 387 ++++++++++++++++++++++++++++++++++++++++ alc/context.h | 167 ++++++++++++++++++ alc/effectslot.cpp | 2 +- alc/panning.cpp | 2 +- 18 files changed, 627 insertions(+), 607 deletions(-) delete mode 100644 alc/alcontext.h create mode 100644 alc/context.cpp create mode 100644 alc/context.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index ac1e31d3..292f6a6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -767,7 +767,8 @@ set(ALC_OBJS alc/alu.h alc/alconfig.cpp alc/alconfig.h - alc/alcontext.h + alc/context.cpp + alc/context.h alc/device.cpp alc/device.h alc/effectslot.cpp diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp index 83904a44..d1ab29ee 100644 --- a/al/auxeffectslot.cpp +++ b/al/auxeffectslot.cpp @@ -36,8 +36,8 @@ #include "AL/efx.h" #include "albit.h" -#include "alc/alcontext.h" #include "alc/alu.h" +#include "alc/context.h" #include "alc/device.h" #include "alc/inprogext.h" #include "almalloc.h" diff --git a/al/buffer.cpp b/al/buffer.cpp index 4623ae30..aa997130 100644 --- a/al/buffer.cpp +++ b/al/buffer.cpp @@ -43,7 +43,7 @@ #include "albit.h" #include "albyte.h" -#include "alc/alcontext.h" +#include "alc/context.h" #include "alc/device.h" #include "alc/inprogext.h" #include "almalloc.h" diff --git a/al/effect.cpp b/al/effect.cpp index 5b10df2e..a15194fd 100644 --- a/al/effect.cpp +++ b/al/effect.cpp @@ -39,9 +39,10 @@ #include "AL/efx.h" #include "albit.h" -#include "alc/alcontext.h" +#include "alc/context.h" #include "alc/device.h" #include "alc/effects/base.h" +#include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" #include "alstring.h" diff --git a/al/error.cpp b/al/error.cpp index b6489a86..b5b6f430 100644 --- a/al/error.cpp +++ b/al/error.cpp @@ -35,7 +35,7 @@ #include "AL/al.h" #include "AL/alc.h" -#include "alc/alcontext.h" +#include "alc/context.h" #include "almalloc.h" #include "core/except.h" #include "core/logging.h" diff --git a/al/event.cpp b/al/event.cpp index 5b6c1976..fd4667f7 100644 --- a/al/event.cpp +++ b/al/event.cpp @@ -18,7 +18,7 @@ #include "AL/alc.h" #include "albyte.h" -#include "alc/alcontext.h" +#include "alc/context.h" #include "alc/effects/base.h" #include "alc/inprogext.h" #include "almalloc.h" diff --git a/al/extension.cpp b/al/extension.cpp index bd903a6f..d47bb576 100644 --- a/al/extension.cpp +++ b/al/extension.cpp @@ -27,7 +27,7 @@ #include "AL/al.h" #include "AL/alc.h" -#include "alc/alcontext.h" +#include "alc/context.h" #include "alstring.h" #include "core/except.h" #include "opthelpers.h" diff --git a/al/filter.cpp b/al/filter.cpp index 0c4e647f..21a8fee7 100644 --- a/al/filter.cpp +++ b/al/filter.cpp @@ -37,7 +37,7 @@ #include "AL/efx.h" #include "albit.h" -#include "alc/alcontext.h" +#include "alc/context.h" #include "alc/device.h" #include "almalloc.h" #include "alnumeric.h" diff --git a/al/listener.cpp b/al/listener.cpp index 00626a20..0997ff95 100644 --- a/al/listener.cpp +++ b/al/listener.cpp @@ -29,7 +29,7 @@ #include "AL/alc.h" #include "AL/efx.h" -#include "alc/alcontext.h" +#include "alc/context.h" #include "almalloc.h" #include "atomic.h" #include "core/except.h" diff --git a/al/source.cpp b/al/source.cpp index 9c45ebda..67cfacb6 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -46,9 +46,9 @@ #include "AL/efx.h" #include "albit.h" -#include "alc/alcontext.h" #include "alc/alu.h" #include "alc/backends/base.h" +#include "alc/context.h" #include "alc/device.h" #include "alc/inprogext.h" #include "almalloc.h" diff --git a/al/source.h b/al/source.h index dd3f56a7..4d1f66dc 100644 --- a/al/source.h +++ b/al/source.h @@ -11,8 +11,8 @@ #include "AL/al.h" #include "AL/alc.h" -#include "alc/alcontext.h" #include "alc/alu.h" +#include "alc/context.h" #include "aldeque.h" #include "almalloc.h" #include "alnumeric.h" diff --git a/al/state.cpp b/al/state.cpp index 396c15c7..10c7bc74 100644 --- a/al/state.cpp +++ b/al/state.cpp @@ -32,8 +32,8 @@ #include "AL/alc.h" #include "AL/alext.h" -#include "alc/alcontext.h" #include "alc/alu.h" +#include "alc/context.h" #include "alc/inprogext.h" #include "alnumeric.h" #include "aloptional.h" diff --git a/alc/alc.cpp b/alc/alc.cpp index 856deb79..e564fcab 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -27,10 +27,10 @@ #include #endif -#include #include #include #include +#include #include #include #include @@ -48,9 +48,10 @@ #include #include #include -#include +#include +#include #include -#include +#include #include #include "AL/al.h" @@ -61,14 +62,14 @@ #include "al/auxeffectslot.h" #include "al/buffer.h" #include "al/effect.h" -#include "al/event.h" #include "al/filter.h" #include "al/listener.h" #include "al/source.h" #include "albit.h" #include "albyte.h" #include "alconfig.h" -#include "alcontext.h" +#include "alc/context.h" +#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "aloptional.h" @@ -77,32 +78,29 @@ #include "alu.h" #include "atomic.h" #include "core/ambidefs.h" -#include "core/async_event.h" #include "core/bformatdec.h" #include "core/bs2b.h" +#include "core/context.h" #include "core/cpu_caps.h" #include "core/devformat.h" +#include "core/device.h" #include "core/except.h" #include "core/helpers.h" #include "core/mastering.h" -#include "core/filters/nfc.h" -#include "core/filters/splitter.h" +#include "core/mixer/hrtfdefs.h" #include "core/fpu_ctrl.h" #include "core/front_stablizer.h" -#include "core/hrtf.h" #include "core/logging.h" #include "core/uhjfilter.h" +#include "core/voice.h" #include "core/voice_change.h" #include "device.h" #include "effects/base.h" #include "inprogext.h" #include "intrusive_ptr.h" #include "opthelpers.h" -#include "pragmadefs.h" -#include "ringbuffer.h" #include "strutils.h" #include "threads.h" -#include "vecmat.h" #include "vector.h" #include "backends/base.h" @@ -155,6 +153,31 @@ #endif +FILE *gLogFile{stderr}; +#ifdef _DEBUG +LogLevel gLogLevel{LogLevel::Warning}; +#else +LogLevel gLogLevel{LogLevel::Error}; +#endif + +/************************************************ + * Library initialization + ************************************************/ +#if defined(_WIN32) && !defined(AL_LIBTYPE_STATIC) +BOOL APIENTRY DllMain(HINSTANCE module, DWORD reason, LPVOID /*reserved*/) +{ + switch(reason) + { + case DLL_PROCESS_ATTACH: + /* Pin the DLL so we won't get unloaded until the process terminates */ + GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + reinterpret_cast(module), &module); + break; + } + return TRUE; +} +#endif + namespace { using namespace std::placeholders; @@ -162,6 +185,7 @@ using std::chrono::seconds; using std::chrono::nanoseconds; using voidp = void*; +using float2 = std::array; /************************************************ @@ -861,85 +885,14 @@ std::string alcCaptureDeviceList; std::string alcDefaultAllDevicesSpecifier; std::string alcCaptureDefaultDeviceSpecifier; -/* Default context extensions */ -constexpr ALchar alExtList[] = - "AL_EXT_ALAW " - "AL_EXT_BFORMAT " - "AL_EXT_DOUBLE " - "AL_EXT_EXPONENT_DISTANCE " - "AL_EXT_FLOAT32 " - "AL_EXT_IMA4 " - "AL_EXT_LINEAR_DISTANCE " - "AL_EXT_MCFORMATS " - "AL_EXT_MULAW " - "AL_EXT_MULAW_BFORMAT " - "AL_EXT_MULAW_MCFORMATS " - "AL_EXT_OFFSET " - "AL_EXT_source_distance_model " - "AL_EXT_SOURCE_RADIUS " - "AL_EXT_STEREO_ANGLES " - "AL_LOKI_quadriphonic " - "AL_SOFT_bformat_ex " - "AL_SOFTX_bformat_hoa " - "AL_SOFT_block_alignment " - "AL_SOFTX_callback_buffer " - "AL_SOFTX_convolution_reverb " - "AL_SOFT_deferred_updates " - "AL_SOFT_direct_channels " - "AL_SOFT_direct_channels_remix " - "AL_SOFT_effect_target " - "AL_SOFT_events " - "AL_SOFTX_filter_gain_ex " - "AL_SOFT_gain_clamp_ex " - "AL_SOFTX_hold_on_disconnect " - "AL_SOFT_loop_points " - "AL_SOFTX_map_buffer " - "AL_SOFT_MSADPCM " - "AL_SOFT_source_latency " - "AL_SOFT_source_length " - "AL_SOFT_source_resampler " - "AL_SOFT_source_spatialize " - "AL_SOFTX_UHJ"; - std::atomic LastNullDeviceError{ALC_NO_ERROR}; -/* Thread-local current context. The handling may look a little obtuse, but - * it's designed this way to avoid a bug with 32-bit GCC/MinGW, which causes - * thread-local object destructors to get a junk 'this' pointer. This method - * has the benefit of making LocalContext access more efficient since it's a - * a plain pointer, with the ThreadContext object used to check it at thread - * exit (and given no data fields, 'this' being junk is inconsequential since - * it's never accessed). - */ -thread_local ALCcontext *LocalContext{nullptr}; -class ThreadCtx { -public: - ~ThreadCtx() - { - if(ALCcontext *ctx{LocalContext}) - { - const bool result{ctx->releaseIfNoDelete()}; - ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx}, - result ? "" : ", leak detected"); - } - } - - void set(ALCcontext *ctx) const noexcept { LocalContext = ctx; } -}; -thread_local ThreadCtx ThreadContext; - -/* Process-wide current context */ -std::atomic GlobalContext{nullptr}; - /* Flag to trap ALC device errors */ bool TrapALCError{false}; /* One-time configuration init control */ std::once_flag alc_config_once{}; -/* Default effect that applies to sources that don't have an effect on send 0 */ -ALeffect DefaultEffect; - /* Flag to specify if alcSuspendContext/alcProcessContext should defer/process * updates. */ @@ -1271,10 +1224,10 @@ void alc_initconfig(void) } while(next++); } - InitEffect(&DefaultEffect); + InitEffect(&ALCcontext::sDefaultEffect); auto defrevopt = al::getenv("ALSOFT_DEFAULT_REVERB"); if(defrevopt || (defrevopt=ConfigValueStr(nullptr, nullptr, "default-reverb"))) - LoadReverbPreset(defrevopt->c_str(), &DefaultEffect); + LoadReverbPreset(defrevopt->c_str(), &ALCcontext::sDefaultEffect); } #define DO_INITCONFIG() std::call_once(alc_config_once, [](){alc_initconfig();}) @@ -1311,37 +1264,6 @@ void ProbeCaptureDeviceList() } } -} // namespace - -FILE *gLogFile{stderr}; -#ifdef _DEBUG -LogLevel gLogLevel{LogLevel::Warning}; -#else -LogLevel gLogLevel{LogLevel::Error}; -#endif - -/************************************************ - * Library initialization - ************************************************/ -#if defined(_WIN32) && !defined(AL_LIBTYPE_STATIC) -BOOL APIENTRY DllMain(HINSTANCE module, DWORD reason, LPVOID /*reserved*/) -{ - switch(reason) - { - case DLL_PROCESS_ATTACH: - /* Pin the DLL so we won't get unloaded until the process terminates */ - GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - reinterpret_cast(module), &module); - break; - } - return TRUE; -} -#endif - -/************************************************ - * Device format information - ************************************************/ -namespace { struct DevFmtPair { DevFmtChannels chans; DevFmtType type; }; al::optional DecomposeDevFormat(ALenum format) @@ -1534,92 +1456,9 @@ const std::array X71Downmix{{ { BackCenter, {{{BackLeft, 0.5f}, {BackRight, 0.5f}}} }, }}; -} // namespace - -/************************************************ - * Miscellaneous ALC helpers - ************************************************/ - -void ALCcontext::processUpdates() -{ - std::lock_guard _{mPropLock}; - if(mDeferUpdates.exchange(false, std::memory_order_acq_rel)) - { - /* Tell the mixer to stop applying updates, then wait for any active - * updating to finish, before providing updates. - */ - mHoldUpdates.store(true, std::memory_order_release); - while((mUpdateCount.load(std::memory_order_acquire)&1) != 0) { - /* busy-wait */ - } - - if(mPropsDirty.test_and_clear(std::memory_order_acq_rel)) - UpdateContextProps(this); - if(mListener.mPropsDirty.test_and_clear(std::memory_order_acq_rel)) - UpdateListenerProps(this); - UpdateAllEffectSlotProps(this); - UpdateAllSourceProps(this); - - /* Now with all updates declared, let the mixer continue applying them - * so they all happen at once. - */ - mHoldUpdates.store(false, std::memory_order_release); - } -} - - -void ContextBase::allocVoiceChanges(size_t addcount) -{ - constexpr size_t clustersize{128}; - /* Convert element count to cluster count. */ - addcount = (addcount+(clustersize-1)) / clustersize; - while(addcount) - { - VoiceChangeCluster cluster{std::make_unique(clustersize)}; - for(size_t i{1};i < clustersize;++i) - cluster[i-1].mNext.store(std::addressof(cluster[i]), std::memory_order_relaxed); - cluster[clustersize-1].mNext.store(mVoiceChangeTail, std::memory_order_relaxed); - mVoiceChangeClusters.emplace_back(std::move(cluster)); - mVoiceChangeTail = mVoiceChangeClusters.back().get(); - --addcount; - } -} - -void ContextBase::allocVoices(size_t addcount) -{ - constexpr size_t clustersize{32}; - /* Convert element count to cluster count. */ - addcount = (addcount+(clustersize-1)) / clustersize; - - if(addcount >= std::numeric_limits::max()/clustersize - mVoiceClusters.size()) - throw std::runtime_error{"Allocating too many voices"}; - const size_t totalcount{(mVoiceClusters.size()+addcount) * clustersize}; - TRACE("Increasing allocated voices to %zu\n", totalcount); - - auto newarray = VoiceArray::Create(totalcount); - while(addcount) - { - mVoiceClusters.emplace_back(std::make_unique(clustersize)); - --addcount; - } - - auto voice_iter = newarray->begin(); - for(VoiceCluster &cluster : mVoiceClusters) - { - for(size_t i{0};i < clustersize;++i) - *(voice_iter++) = &cluster[i]; - } - - if(auto *oldvoices = mVoices.exchange(newarray.release(), std::memory_order_acq_rel)) - { - mDevice->waitForMix(); - delete oldvoices; - } -} - /** Stores the latest ALC device error. */ -static void alcSetError(ALCdevice *device, ALCenum errorCode) +void alcSetError(ALCdevice *device, ALCenum errorCode) { WARN("Error generated on device %p, code 0x%04x\n", voidp{device}, errorCode); if(TrapALCError) @@ -1640,7 +1479,7 @@ static void alcSetError(ALCdevice *device, ALCenum errorCode) } -static std::unique_ptr CreateDeviceLimiter(const ALCdevice *device, const float threshold) +std::unique_ptr CreateDeviceLimiter(const ALCdevice *device, const float threshold) { constexpr bool AutoKnee{true}; constexpr bool AutoAttack{true}; @@ -1679,7 +1518,7 @@ static inline void UpdateClockBase(ALCdevice *device) * Updates device parameters according to the attribute list (caller is * responsible for holding the list lock). */ -static ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) +ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) { HrtfRequestMode hrtf_userreq{Hrtf_Default}; HrtfRequestMode hrtf_appreq{Hrtf_Default}; @@ -2261,7 +2100,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) * Updates device parameters as above, and also first clears the disconnected * status, if set. */ -static bool ResetDeviceParams(ALCdevice *device, const int *attrList) +bool ResetDeviceParams(ALCdevice *device, const int *attrList) { /* If the device was disconnected, reset it since we're opened anew. */ if UNLIKELY(!device->Connected.load(std::memory_order_relaxed)) @@ -2301,7 +2140,7 @@ static bool ResetDeviceParams(ALCdevice *device, const int *attrList) /** Checks if the device handle is valid, and returns a new reference if so. */ -static DeviceRef VerifyDevice(ALCdevice *device) +DeviceRef VerifyDevice(ALCdevice *device) { std::lock_guard _{ListLock}; auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device); @@ -2314,225 +2153,10 @@ static DeviceRef VerifyDevice(ALCdevice *device) } -ContextBase::ContextBase(DeviceBase *device) : mDevice{device} -{ } - -ContextBase::~ContextBase() -{ - size_t count{0}; - ContextProps *cprops{mParams.ContextUpdate.exchange(nullptr, std::memory_order_relaxed)}; - if(cprops) - { - ++count; - delete cprops; - } - cprops = mFreeContextProps.exchange(nullptr, std::memory_order_acquire); - while(cprops) - { - std::unique_ptr old{cprops}; - cprops = old->next.load(std::memory_order_relaxed); - ++count; - } - TRACE("Freed %zu context property object%s\n", count, (count==1)?"":"s"); - - count = 0; - EffectSlotProps *eprops{mFreeEffectslotProps.exchange(nullptr, std::memory_order_acquire)}; - while(eprops) - { - std::unique_ptr old{eprops}; - eprops = old->next.load(std::memory_order_relaxed); - ++count; - } - TRACE("Freed %zu AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s"); - - if(EffectSlotArray *curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)}) - { - al::destroy_n(curarray->end(), curarray->size()); - delete curarray; - } - - count = 0; - VoicePropsItem *vprops{mFreeVoiceProps.exchange(nullptr, std::memory_order_acquire)}; - while(vprops) - { - std::unique_ptr old{vprops}; - vprops = old->next.load(std::memory_order_relaxed); - ++count; - } - TRACE("Freed %zu voice property object%s\n", count, (count==1)?"":"s"); - - delete mVoices.exchange(nullptr, std::memory_order_relaxed); - - count = 0; - ListenerProps *lprops{mParams.ListenerUpdate.exchange(nullptr, std::memory_order_relaxed)}; - if(lprops) - { - ++count; - delete lprops; - } - lprops = mFreeListenerProps.exchange(nullptr, std::memory_order_acquire); - while(lprops) - { - std::unique_ptr old{lprops}; - lprops = old->next.load(std::memory_order_relaxed); - ++count; - } - TRACE("Freed %zu listener property object%s\n", count, (count==1)?"":"s"); - - if(mAsyncEvents) - { - count = 0; - auto evt_vec = mAsyncEvents->getReadVector(); - if(evt_vec.first.len > 0) - { - al::destroy_n(reinterpret_cast(evt_vec.first.buf), evt_vec.first.len); - count += evt_vec.first.len; - } - if(evt_vec.second.len > 0) - { - al::destroy_n(reinterpret_cast(evt_vec.second.buf), evt_vec.second.len); - count += evt_vec.second.len; - } - if(count > 0) - TRACE("Destructed %zu orphaned event%s\n", count, (count==1)?"":"s"); - mAsyncEvents->readAdvance(count); - } -} - - -ALCcontext::ALCcontext(al::intrusive_ptr device) - : ContextBase{device.get()}, mALDevice{std::move(device)} -{ - mPropsDirty.test_and_clear(std::memory_order_relaxed); -} - -ALCcontext::~ALCcontext() -{ - TRACE("Freeing context %p\n", voidp{this}); - - size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), size_t{0u}, - [](size_t cur, const SourceSubList &sublist) noexcept -> size_t - { return cur + static_cast(al::popcount(~sublist.FreeMask)); })}; - if(count > 0) - WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s"); - mSourceList.clear(); - mNumSources = 0; - - mDefaultSlot = nullptr; - count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), size_t{0u}, - [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t - { return cur + static_cast(al::popcount(~sublist.FreeMask)); }); - if(count > 0) - WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s"); - mEffectSlotList.clear(); - mNumEffectSlots = 0; -} - -void ALCcontext::init() -{ - if(DefaultEffect.type != AL_EFFECT_NULL && mDevice->Type == DeviceType::Playback) - { - mDefaultSlot = std::make_unique(); - aluInitEffectPanning(&mDefaultSlot->mSlot, this); - } - - EffectSlotArray *auxslots; - if(!mDefaultSlot) - auxslots = EffectSlot::CreatePtrArray(0); - else - { - auxslots = EffectSlot::CreatePtrArray(1); - (*auxslots)[0] = &mDefaultSlot->mSlot; - mDefaultSlot->mState = SlotState::Playing; - } - mActiveAuxSlots.store(auxslots, std::memory_order_relaxed); - - allocVoiceChanges(1); - { - VoiceChange *cur{mVoiceChangeTail}; - while(VoiceChange *next{cur->mNext.load(std::memory_order_relaxed)}) - cur = next; - mCurrentVoiceChange.store(cur, std::memory_order_relaxed); - } - - mExtensionList = alExtList; - - - mParams.Matrix = alu::Matrix::Identity(); - mParams.Velocity = alu::Vector{}; - mParams.Gain = mListener.Gain; - mParams.MetersPerUnit = mListener.mMetersPerUnit; - mParams.DopplerFactor = mDopplerFactor; - mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity; - mParams.SourceDistanceModel = mSourceDistanceModel; - mParams.mDistanceModel = mDistanceModel; - - - mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false); - StartEventThrd(this); - - - allocVoices(256); - mActiveVoiceCount.store(64, std::memory_order_relaxed); -} - -bool ALCcontext::deinit() -{ - if(LocalContext == this) - { - WARN("%p released while current on thread\n", voidp{this}); - ThreadContext.set(nullptr); - release(); - } - - ALCcontext *origctx{this}; - if(GlobalContext.compare_exchange_strong(origctx, nullptr)) - release(); - - bool ret{}; - /* First make sure this context exists in the device's list. */ - auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire); - if(auto toremove = static_cast(std::count(oldarray->begin(), oldarray->end(), this))) - { - using ContextArray = al::FlexArray; - auto alloc_ctx_array = [](const size_t count) -> ContextArray* - { - if(count == 0) return &DeviceBase::sEmptyContextArray; - return ContextArray::Create(count).release(); - }; - auto *newarray = alloc_ctx_array(oldarray->size() - toremove); - - /* Copy the current/old context handles to the new array, excluding the - * given context. - */ - std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(), - std::bind(std::not_equal_to<>{}, _1, this)); - - /* Store the new context array in the device. Wait for any current mix - * to finish before deleting the old array. - */ - mDevice->mContexts.store(newarray); - if(oldarray != &DeviceBase::sEmptyContextArray) - { - mDevice->waitForMix(); - delete oldarray; - } - - ret = !newarray->empty(); - } - else - ret = !oldarray->empty(); - - StopEventThrd(this); - - return ret; -} - - /** * Checks if the given context is valid, returning a new reference to it if so. */ -static ContextRef VerifyContext(ALCcontext *context) +ContextRef VerifyContext(ALCcontext *context) { std::lock_guard _{ListLock}; auto iter = std::lower_bound(ContextList.begin(), ContextList.end(), context); @@ -2544,16 +2168,18 @@ static ContextRef VerifyContext(ALCcontext *context) return nullptr; } +} // namespace + /** Returns a new reference to the currently active context for this thread. */ ContextRef GetContextRef(void) { - ALCcontext *context{LocalContext}; + ALCcontext *context{ALCcontext::sLocalContext}; if(context) context->add_ref(); else { std::lock_guard _{ListLock}; - context = GlobalContext.load(std::memory_order_acquire); + context = ALCcontext::sGlobalContext.load(std::memory_order_acquire); if(context) context->add_ref(); } return ContextRef{context}; @@ -3359,7 +2985,7 @@ START_API_FUNC if(ALeffectslot *slot{context->mDefaultSlot.get()}) { - if(slot->initEffect(&DefaultEffect, context.get()) == AL_NO_ERROR) + if(slot->initEffect(&ALCcontext::sDefaultEffect, context.get()) == AL_NO_ERROR) slot->updateProps(context.get()); else ERR("Failed to initialize the default effect\n"); @@ -3402,8 +3028,8 @@ END_API_FUNC ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) START_API_FUNC { - ALCcontext *Context{LocalContext}; - if(!Context) Context = GlobalContext.load(); + ALCcontext *Context{ALCcontext::sLocalContext}; + if(!Context) Context = ALCcontext::sGlobalContext.load(); return Context; } END_API_FUNC @@ -3411,7 +3037,7 @@ END_API_FUNC /** Returns the currently active thread-local context. */ ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) START_API_FUNC -{ return LocalContext; } +{ return ALCcontext::sLocalContext; } END_API_FUNC ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) @@ -3432,14 +3058,14 @@ START_API_FUNC * pointer. Take ownership of the reference (if any) that was previously * stored there. */ - ctx = ContextRef{GlobalContext.exchange(ctx.release())}; + ctx = ContextRef{ALCcontext::sGlobalContext.exchange(ctx.release())}; /* Reset (decrement) the previous global reference by replacing it with the * thread-local context. Take ownership of the thread-local context * reference (if any), clearing the storage to null. */ - ctx = ContextRef{LocalContext}; - if(ctx) ThreadContext.set(nullptr); + ctx = ContextRef{ALCcontext::sLocalContext}; + if(ctx) ALCcontext::sThreadContext.set(nullptr); /* Reset (decrement) the previous thread-local reference. */ return ALC_TRUE; @@ -3462,8 +3088,8 @@ START_API_FUNC } } /* context's reference count is already incremented */ - ContextRef old{LocalContext}; - ThreadContext.set(ctx.release()); + ContextRef old{ALCcontext::sLocalContext}; + ALCcontext::sThreadContext.set(ctx.release()); return ALC_TRUE; } diff --git a/alc/alcontext.h b/alc/alcontext.h deleted file mode 100644 index 2b38dd70..00000000 --- a/alc/alcontext.h +++ /dev/null @@ -1,162 +0,0 @@ -#ifndef ALCONTEXT_H -#define ALCONTEXT_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "AL/al.h" -#include "AL/alc.h" - -#include "al/listener.h" -#include "almalloc.h" -#include "alnumeric.h" -#include "alu.h" -#include "atomic.h" -#include "core/bufferline.h" -#include "core/context.h" -#include "inprogext.h" -#include "intrusive_ptr.h" -#include "threads.h" -#include "vecmat.h" -#include "vector.h" - -struct ALeffectslot; -struct ALsource; -struct DeviceBase; -struct EffectSlot; -struct EffectSlotProps; -struct RingBuffer; -struct Voice; -struct VoiceChange; -struct VoicePropsItem; - -using uint = unsigned int; - - -struct SourceSubList { - uint64_t FreeMask{~0_u64}; - ALsource *Sources{nullptr}; /* 64 */ - - SourceSubList() noexcept = default; - SourceSubList(const SourceSubList&) = delete; - SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources} - { rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; } - ~SourceSubList(); - - SourceSubList& operator=(const SourceSubList&) = delete; - SourceSubList& operator=(SourceSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; } -}; - -struct EffectSlotSubList { - uint64_t FreeMask{~0_u64}; - ALeffectslot *EffectSlots{nullptr}; /* 64 */ - - EffectSlotSubList() noexcept = default; - EffectSlotSubList(const EffectSlotSubList&) = delete; - EffectSlotSubList(EffectSlotSubList&& rhs) noexcept - : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots} - { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; } - ~EffectSlotSubList(); - - EffectSlotSubList& operator=(const EffectSlotSubList&) = delete; - EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; } -}; - -struct ALCcontext : public al::intrusive_ref, ContextBase { - const al::intrusive_ptr mALDevice; - - /* Wet buffers used by effect slots. */ - al::vector mWetBuffers; - - - al::atomic_invflag mPropsDirty; - std::atomic mDeferUpdates{false}; - - std::mutex mPropLock; - - std::atomic mLastError{AL_NO_ERROR}; - - DistanceModel mDistanceModel{DistanceModel::Default}; - bool mSourceDistanceModel{false}; - - float mDopplerFactor{1.0f}; - float mDopplerVelocity{1.0f}; - float mSpeedOfSound{SpeedOfSoundMetersPerSec}; - - std::mutex mEventCbLock; - ALEVENTPROCSOFT mEventCb{}; - void *mEventParam{nullptr}; - - ALlistener mListener{}; - - al::vector mSourceList; - ALuint mNumSources{0}; - std::mutex mSourceLock; - - al::vector mEffectSlotList; - ALuint mNumEffectSlots{0u}; - std::mutex mEffectSlotLock; - - /* Default effect slot */ - std::unique_ptr mDefaultSlot; - - const char *mExtensionList{nullptr}; - - - ALCcontext(al::intrusive_ptr device); - ALCcontext(const ALCcontext&) = delete; - ALCcontext& operator=(const ALCcontext&) = delete; - ~ALCcontext(); - - void init(); - /** - * Removes the context from its device and removes it from being current on - * the running thread or globally. Returns true if other contexts still - * exist on the device. - */ - bool deinit(); - - /** - * Defers/suspends updates for the given context's listener and sources. - * This does *NOT* stop mixing, but rather prevents certain property - * changes from taking effect. - */ - void deferUpdates() noexcept { mDeferUpdates.exchange(true, std::memory_order_acq_rel); } - - /** Resumes update processing after being deferred. */ - void processUpdates(); - -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf, 3, 4)]] -#else - [[gnu::format(printf, 3, 4)]] -#endif - void setError(ALenum errorCode, const char *msg, ...); - - DEF_NEWDEL(ALCcontext) -}; - -#define SETERR_RETURN(ctx, err, retval, ...) do { \ - (ctx)->setError((err), __VA_ARGS__); \ - return retval; \ -} while(0) - - -using ContextRef = al::intrusive_ptr; - -ContextRef GetContextRef(void); - -void UpdateContextProps(ALCcontext *context); - - -extern bool TrapALError; - -#endif /* ALCONTEXT_H */ diff --git a/alc/context.cpp b/alc/context.cpp new file mode 100644 index 00000000..9f0b6272 --- /dev/null +++ b/alc/context.cpp @@ -0,0 +1,387 @@ + +#include "config.h" + +#include "context.h" + +#include +#include +#include +#include +#include +#include + +#include "AL/efx.h" + +#include "al/auxeffectslot.h" +#include "al/source.h" +#include "al/effect.h" +#include "al/event.h" +#include "al/listener.h" +#include "albit.h" +#include "alc/alu.h" +#include "core/async_event.h" +#include "core/device.h" +#include "core/logging.h" +#include "core/voice.h" +#include "core/voice_change.h" +#include "device.h" +#include "effectslot.h" +#include "ringbuffer.h" +#include "vecmat.h" + + +namespace { + +using namespace std::placeholders; + +using voidp = void*; + +/* Default context extensions */ +constexpr ALchar alExtList[] = + "AL_EXT_ALAW " + "AL_EXT_BFORMAT " + "AL_EXT_DOUBLE " + "AL_EXT_EXPONENT_DISTANCE " + "AL_EXT_FLOAT32 " + "AL_EXT_IMA4 " + "AL_EXT_LINEAR_DISTANCE " + "AL_EXT_MCFORMATS " + "AL_EXT_MULAW " + "AL_EXT_MULAW_BFORMAT " + "AL_EXT_MULAW_MCFORMATS " + "AL_EXT_OFFSET " + "AL_EXT_source_distance_model " + "AL_EXT_SOURCE_RADIUS " + "AL_EXT_STEREO_ANGLES " + "AL_LOKI_quadriphonic " + "AL_SOFT_bformat_ex " + "AL_SOFTX_bformat_hoa " + "AL_SOFT_block_alignment " + "AL_SOFTX_callback_buffer " + "AL_SOFTX_convolution_reverb " + "AL_SOFT_deferred_updates " + "AL_SOFT_direct_channels " + "AL_SOFT_direct_channels_remix " + "AL_SOFT_effect_target " + "AL_SOFT_events " + "AL_SOFTX_filter_gain_ex " + "AL_SOFT_gain_clamp_ex " + "AL_SOFTX_hold_on_disconnect " + "AL_SOFT_loop_points " + "AL_SOFTX_map_buffer " + "AL_SOFT_MSADPCM " + "AL_SOFT_source_latency " + "AL_SOFT_source_length " + "AL_SOFT_source_resampler " + "AL_SOFT_source_spatialize " + "AL_SOFTX_UHJ"; + +} // namespace + + +std::atomic ALCcontext::sGlobalContext{nullptr}; + +thread_local ALCcontext *ALCcontext::sLocalContext{nullptr}; +ALCcontext::ThreadCtx::~ThreadCtx() +{ + if(ALCcontext *ctx{ALCcontext::sLocalContext}) + { + const bool result{ctx->releaseIfNoDelete()}; + ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx}, + result ? "" : ", leak detected"); + } +} +thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext; + +ALeffect ALCcontext::sDefaultEffect; + + +ContextBase::ContextBase(DeviceBase *device) : mDevice{device} +{ } + +ContextBase::~ContextBase() +{ + size_t count{0}; + ContextProps *cprops{mParams.ContextUpdate.exchange(nullptr, std::memory_order_relaxed)}; + if(cprops) + { + ++count; + delete cprops; + } + cprops = mFreeContextProps.exchange(nullptr, std::memory_order_acquire); + while(cprops) + { + std::unique_ptr old{cprops}; + cprops = old->next.load(std::memory_order_relaxed); + ++count; + } + TRACE("Freed %zu context property object%s\n", count, (count==1)?"":"s"); + + count = 0; + EffectSlotProps *eprops{mFreeEffectslotProps.exchange(nullptr, std::memory_order_acquire)}; + while(eprops) + { + std::unique_ptr old{eprops}; + eprops = old->next.load(std::memory_order_relaxed); + ++count; + } + TRACE("Freed %zu AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s"); + + if(EffectSlotArray *curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)}) + { + al::destroy_n(curarray->end(), curarray->size()); + delete curarray; + } + + count = 0; + VoicePropsItem *vprops{mFreeVoiceProps.exchange(nullptr, std::memory_order_acquire)}; + while(vprops) + { + std::unique_ptr old{vprops}; + vprops = old->next.load(std::memory_order_relaxed); + ++count; + } + TRACE("Freed %zu voice property object%s\n", count, (count==1)?"":"s"); + + delete mVoices.exchange(nullptr, std::memory_order_relaxed); + + count = 0; + ListenerProps *lprops{mParams.ListenerUpdate.exchange(nullptr, std::memory_order_relaxed)}; + if(lprops) + { + ++count; + delete lprops; + } + lprops = mFreeListenerProps.exchange(nullptr, std::memory_order_acquire); + while(lprops) + { + std::unique_ptr old{lprops}; + lprops = old->next.load(std::memory_order_relaxed); + ++count; + } + TRACE("Freed %zu listener property object%s\n", count, (count==1)?"":"s"); + + if(mAsyncEvents) + { + count = 0; + auto evt_vec = mAsyncEvents->getReadVector(); + if(evt_vec.first.len > 0) + { + al::destroy_n(reinterpret_cast(evt_vec.first.buf), evt_vec.first.len); + count += evt_vec.first.len; + } + if(evt_vec.second.len > 0) + { + al::destroy_n(reinterpret_cast(evt_vec.second.buf), evt_vec.second.len); + count += evt_vec.second.len; + } + if(count > 0) + TRACE("Destructed %zu orphaned event%s\n", count, (count==1)?"":"s"); + mAsyncEvents->readAdvance(count); + } +} + +void ContextBase::allocVoiceChanges(size_t addcount) +{ + constexpr size_t clustersize{128}; + /* Convert element count to cluster count. */ + addcount = (addcount+(clustersize-1)) / clustersize; + while(addcount) + { + VoiceChangeCluster cluster{std::make_unique(clustersize)}; + for(size_t i{1};i < clustersize;++i) + cluster[i-1].mNext.store(std::addressof(cluster[i]), std::memory_order_relaxed); + cluster[clustersize-1].mNext.store(mVoiceChangeTail, std::memory_order_relaxed); + mVoiceChangeClusters.emplace_back(std::move(cluster)); + mVoiceChangeTail = mVoiceChangeClusters.back().get(); + --addcount; + } +} + +void ContextBase::allocVoices(size_t addcount) +{ + constexpr size_t clustersize{32}; + /* Convert element count to cluster count. */ + addcount = (addcount+(clustersize-1)) / clustersize; + + if(addcount >= std::numeric_limits::max()/clustersize - mVoiceClusters.size()) + throw std::runtime_error{"Allocating too many voices"}; + const size_t totalcount{(mVoiceClusters.size()+addcount) * clustersize}; + TRACE("Increasing allocated voices to %zu\n", totalcount); + + auto newarray = VoiceArray::Create(totalcount); + while(addcount) + { + mVoiceClusters.emplace_back(std::make_unique(clustersize)); + --addcount; + } + + auto voice_iter = newarray->begin(); + for(VoiceCluster &cluster : mVoiceClusters) + { + for(size_t i{0};i < clustersize;++i) + *(voice_iter++) = &cluster[i]; + } + + if(auto *oldvoices = mVoices.exchange(newarray.release(), std::memory_order_acq_rel)) + { + mDevice->waitForMix(); + delete oldvoices; + } +} + + +ALCcontext::ALCcontext(al::intrusive_ptr device) + : ContextBase{device.get()}, mALDevice{std::move(device)} +{ + mPropsDirty.test_and_clear(std::memory_order_relaxed); +} + +ALCcontext::~ALCcontext() +{ + TRACE("Freeing context %p\n", voidp{this}); + + size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), size_t{0u}, + [](size_t cur, const SourceSubList &sublist) noexcept -> size_t + { return cur + static_cast(al::popcount(~sublist.FreeMask)); })}; + if(count > 0) + WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s"); + mSourceList.clear(); + mNumSources = 0; + + mDefaultSlot = nullptr; + count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), size_t{0u}, + [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t + { return cur + static_cast(al::popcount(~sublist.FreeMask)); }); + if(count > 0) + WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s"); + mEffectSlotList.clear(); + mNumEffectSlots = 0; +} + +void ALCcontext::init() +{ + if(sDefaultEffect.type != AL_EFFECT_NULL && mDevice->Type == DeviceType::Playback) + { + mDefaultSlot = std::make_unique(); + aluInitEffectPanning(&mDefaultSlot->mSlot, this); + } + + EffectSlotArray *auxslots; + if(!mDefaultSlot) + auxslots = EffectSlot::CreatePtrArray(0); + else + { + auxslots = EffectSlot::CreatePtrArray(1); + (*auxslots)[0] = &mDefaultSlot->mSlot; + mDefaultSlot->mState = SlotState::Playing; + } + mActiveAuxSlots.store(auxslots, std::memory_order_relaxed); + + allocVoiceChanges(1); + { + VoiceChange *cur{mVoiceChangeTail}; + while(VoiceChange *next{cur->mNext.load(std::memory_order_relaxed)}) + cur = next; + mCurrentVoiceChange.store(cur, std::memory_order_relaxed); + } + + mExtensionList = alExtList; + + + mParams.Matrix = alu::Matrix::Identity(); + mParams.Velocity = alu::Vector{}; + mParams.Gain = mListener.Gain; + mParams.MetersPerUnit = mListener.mMetersPerUnit; + mParams.DopplerFactor = mDopplerFactor; + mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity; + mParams.SourceDistanceModel = mSourceDistanceModel; + mParams.mDistanceModel = mDistanceModel; + + + mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false); + StartEventThrd(this); + + + allocVoices(256); + mActiveVoiceCount.store(64, std::memory_order_relaxed); +} + +bool ALCcontext::deinit() +{ + if(sLocalContext == this) + { + WARN("%p released while current on thread\n", voidp{this}); + sThreadContext.set(nullptr); + release(); + } + + ALCcontext *origctx{this}; + if(sGlobalContext.compare_exchange_strong(origctx, nullptr)) + release(); + + bool ret{}; + /* First make sure this context exists in the device's list. */ + auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire); + if(auto toremove = static_cast(std::count(oldarray->begin(), oldarray->end(), this))) + { + using ContextArray = al::FlexArray; + auto alloc_ctx_array = [](const size_t count) -> ContextArray* + { + if(count == 0) return &DeviceBase::sEmptyContextArray; + return ContextArray::Create(count).release(); + }; + auto *newarray = alloc_ctx_array(oldarray->size() - toremove); + + /* Copy the current/old context handles to the new array, excluding the + * given context. + */ + std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(), + std::bind(std::not_equal_to<>{}, _1, this)); + + /* Store the new context array in the device. Wait for any current mix + * to finish before deleting the old array. + */ + mDevice->mContexts.store(newarray); + if(oldarray != &DeviceBase::sEmptyContextArray) + { + mDevice->waitForMix(); + delete oldarray; + } + + ret = !newarray->empty(); + } + else + ret = !oldarray->empty(); + + StopEventThrd(this); + + return ret; +} + +void ALCcontext::processUpdates() +{ + std::lock_guard _{mPropLock}; + if(mDeferUpdates.exchange(false, std::memory_order_acq_rel)) + { + /* Tell the mixer to stop applying updates, then wait for any active + * updating to finish, before providing updates. + */ + mHoldUpdates.store(true, std::memory_order_release); + while((mUpdateCount.load(std::memory_order_acquire)&1) != 0) { + /* busy-wait */ + } + + if(mPropsDirty.test_and_clear(std::memory_order_acq_rel)) + UpdateContextProps(this); + if(mListener.mPropsDirty.test_and_clear(std::memory_order_acq_rel)) + UpdateListenerProps(this); + UpdateAllEffectSlotProps(this); + UpdateAllSourceProps(this); + + /* Now with all updates declared, let the mixer continue applying them + * so they all happen at once. + */ + mHoldUpdates.store(false, std::memory_order_release); + } +} diff --git a/alc/context.h b/alc/context.h new file mode 100644 index 00000000..d0afbdd9 --- /dev/null +++ b/alc/context.h @@ -0,0 +1,167 @@ +#ifndef ALC_CONTEXT_H +#define ALC_CONTEXT_H + +#include +#include +#include +#include +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + +#include "al/listener.h" +#include "almalloc.h" +#include "alnumeric.h" +#include "atomic.h" +#include "core/context.h" +#include "intrusive_ptr.h" +#include "vector.h" + +struct ALeffect; +struct ALeffectslot; +struct ALsource; + +using uint = unsigned int; + + +struct SourceSubList { + uint64_t FreeMask{~0_u64}; + ALsource *Sources{nullptr}; /* 64 */ + + SourceSubList() noexcept = default; + SourceSubList(const SourceSubList&) = delete; + SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources} + { rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; } + ~SourceSubList(); + + SourceSubList& operator=(const SourceSubList&) = delete; + SourceSubList& operator=(SourceSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; } +}; + +struct EffectSlotSubList { + uint64_t FreeMask{~0_u64}; + ALeffectslot *EffectSlots{nullptr}; /* 64 */ + + EffectSlotSubList() noexcept = default; + EffectSlotSubList(const EffectSlotSubList&) = delete; + EffectSlotSubList(EffectSlotSubList&& rhs) noexcept + : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots} + { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; } + ~EffectSlotSubList(); + + EffectSlotSubList& operator=(const EffectSlotSubList&) = delete; + EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; } +}; + +struct ALCcontext : public al::intrusive_ref, ContextBase { + const al::intrusive_ptr mALDevice; + + /* Wet buffers used by effect slots. */ + al::vector mWetBuffers; + + + al::atomic_invflag mPropsDirty; + std::atomic mDeferUpdates{false}; + + std::mutex mPropLock; + + std::atomic mLastError{AL_NO_ERROR}; + + DistanceModel mDistanceModel{DistanceModel::Default}; + bool mSourceDistanceModel{false}; + + float mDopplerFactor{1.0f}; + float mDopplerVelocity{1.0f}; + float mSpeedOfSound{SpeedOfSoundMetersPerSec}; + + std::mutex mEventCbLock; + ALEVENTPROCSOFT mEventCb{}; + void *mEventParam{nullptr}; + + ALlistener mListener{}; + + al::vector mSourceList; + ALuint mNumSources{0}; + std::mutex mSourceLock; + + al::vector mEffectSlotList; + ALuint mNumEffectSlots{0u}; + std::mutex mEffectSlotLock; + + /* Default effect slot */ + std::unique_ptr mDefaultSlot; + + const char *mExtensionList{nullptr}; + + + ALCcontext(al::intrusive_ptr device); + ALCcontext(const ALCcontext&) = delete; + ALCcontext& operator=(const ALCcontext&) = delete; + ~ALCcontext(); + + void init(); + /** + * Removes the context from its device and removes it from being current on + * the running thread or globally. Returns true if other contexts still + * exist on the device. + */ + bool deinit(); + + /** + * Defers/suspends updates for the given context's listener and sources. + * This does *NOT* stop mixing, but rather prevents certain property + * changes from taking effect. + */ + void deferUpdates() noexcept { mDeferUpdates.exchange(true, std::memory_order_acq_rel); } + + /** Resumes update processing after being deferred. */ + void processUpdates(); + +#ifdef __USE_MINGW_ANSI_STDIO + [[gnu::format(gnu_printf, 3, 4)]] +#else + [[gnu::format(printf, 3, 4)]] +#endif + void setError(ALenum errorCode, const char *msg, ...); + + /* Process-wide current context */ + static std::atomic sGlobalContext; + + /* Thread-local current context. */ + static thread_local ALCcontext *sLocalContext; + /* Thread-local context handling. This handles attempting to release the + * context which may have been left current when the thread is destroyed. + */ + class ThreadCtx { + public: + ~ThreadCtx(); + void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; } + }; + static thread_local ThreadCtx sThreadContext; + + /* Default effect that applies to sources that don't have an effect on send 0. */ + static ALeffect sDefaultEffect; + + DEF_NEWDEL(ALCcontext) +}; + +#define SETERR_RETURN(ctx, err, retval, ...) do { \ + (ctx)->setError((err), __VA_ARGS__); \ + return retval; \ +} while(0) + + +using ContextRef = al::intrusive_ptr; + +ContextRef GetContextRef(void); + +void UpdateContextProps(ALCcontext *context); + + +extern bool TrapALError; + +#endif /* ALC_CONTEXT_H */ diff --git a/alc/effectslot.cpp b/alc/effectslot.cpp index 8abac248..21084f39 100644 --- a/alc/effectslot.cpp +++ b/alc/effectslot.cpp @@ -5,8 +5,8 @@ #include -#include "alcontext.h" #include "almalloc.h" +#include "context.h" EffectSlotArray *EffectSlot::CreatePtrArray(size_t count) noexcept diff --git a/alc/panning.cpp b/alc/panning.cpp index 00fe9023..a057224f 100644 --- a/alc/panning.cpp +++ b/alc/panning.cpp @@ -39,7 +39,7 @@ #include "al/auxeffectslot.h" #include "alconfig.h" -#include "alcontext.h" +#include "alc/context.h" #include "almalloc.h" #include "alnumeric.h" #include "aloptional.h" -- cgit v1.2.3 From b40272d256442a02c1843a69114f470513062246 Mon Sep 17 00:00:00 2001 From: Tasos Sahanidis Date: Fri, 30 Apr 2021 12:04:32 +0300 Subject: Allow enabling SSE without SSE2 --- CMakeLists.txt | 25 +++++++++++++++++-------- core/mixer/mixer_sse.cpp | 5 ++--- 2 files changed, 19 insertions(+), 11 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cf0613d..53b325db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -369,20 +369,25 @@ set(HAVE_SSE3 0) set(HAVE_SSE4_1 0) set(HAVE_NEON 0) -# Check for SSE+SSE2 support +# Check for SSE support option(ALSOFT_REQUIRE_SSE "Require SSE support" OFF) -option(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF) -if(HAVE_XMMINTRIN_H AND HAVE_EMMINTRIN_H) +if(HAVE_XMMINTRIN_H) option(ALSOFT_CPUEXT_SSE "Enable SSE support" ON) - option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON) - if(ALSOFT_CPUEXT_SSE AND ALSOFT_CPUEXT_SSE2) + if(ALSOFT_CPUEXT_SSE) set(HAVE_SSE 1) - set(HAVE_SSE2 1) endif() endif() if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE) message(FATAL_ERROR "Failed to enabled required SSE CPU extensions") endif() + +option(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF) +if(HAVE_EMMINTRIN_H) + option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON) + if(HAVE_SSE AND ALSOFT_CPUEXT_SSE2) + set(HAVE_SSE2 1) + endif() +endif() if(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2) message(FATAL_ERROR "Failed to enable required SSE2 CPU extensions") endif() @@ -758,9 +763,13 @@ set(ALC_OBJS # Include SIMD mixers set(CPU_EXTS "Default") +if(HAVE_SSE) + set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse.cpp) + set(CPU_EXTS "${CPU_EXTS}, SSE") +endif() if(HAVE_SSE2) - set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse.cpp core/mixer/mixer_sse2.cpp) - set(CPU_EXTS "${CPU_EXTS}, SSE, SSE2") + set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse2.cpp) + set(CPU_EXTS "${CPU_EXTS}, SSE2") endif() if(HAVE_SSE3) set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse3.cpp) diff --git a/core/mixer/mixer_sse.cpp b/core/mixer/mixer_sse.cpp index 23caf797..c0fd8fa1 100644 --- a/core/mixer/mixer_sse.cpp +++ b/core/mixer/mixer_sse.cpp @@ -15,9 +15,8 @@ struct BSincTag; struct FastBSincTag; -/* SSE2 is required for any SSE support. */ -#if defined(__GNUC__) && !defined(__clang__) && !defined(__SSE2__) -#pragma GCC target("sse2") +#if defined(__GNUC__) && !defined(__clang__) && !defined(__SSE__) +#pragma GCC target("sse") #endif namespace { -- cgit v1.2.3 From 44d58bc69ca8ffc1238e050bd9bb9d0f041c66d3 Mon Sep 17 00:00:00 2001 From: HALX99 Date: Thu, 13 May 2021 16:43:39 +0800 Subject: Fix check_include_file return unexpected result (#563) Don't overwrite existing CMAKE_TRY_COMPILE_PLATFORM_VARIABLES values. --- CMakeLists.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 292f6a6a..aa452a5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,14 +2,6 @@ cmake_minimum_required(VERSION 3.0.2) -# The workaround for try_compile failing with code signing -# since cmake-3.18.2, not required -set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES - "CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED" - "CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED") -set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED NO) -set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO) - # Fix compile failure with armv7 deployment target >= 11.0, xcode clang will # report: # error: invalid iOS deployment version '--target=armv7-apple-ios13.6', @@ -17,6 +9,14 @@ set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO) # If CMAKE_OSX_DEPLOYMENT_TARGET is not defined, cmake will choose latest # deployment target if(CMAKE_SYSTEM_NAME STREQUAL "iOS") + # The workaround for try_compile failing with code signing + # since cmake-3.18.2, not required + set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES + ${CMAKE_TRY_COMPILE_PLATFORM_VARIABLES} + "CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED" + "CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED") + set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED NO) + set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO) if("${CMAKE_OSX_ARCHITECTURES}" MATCHES ".*armv7.*") if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET OR NOT CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "11.0") -- cgit v1.2.3 From 1d7ff54f7de005faded1cc0568f3538c65bb4227 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 May 2021 01:46:09 -0700 Subject: Update some comments in CMake --- CMakeLists.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index aa452a5b..007c2cdf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,12 +2,6 @@ cmake_minimum_required(VERSION 3.0.2) -# Fix compile failure with armv7 deployment target >= 11.0, xcode clang will -# report: -# error: invalid iOS deployment version '--target=armv7-apple-ios13.6', -# iOS 10 is the maximum deployment target for 32-bit targets -# If CMAKE_OSX_DEPLOYMENT_TARGET is not defined, cmake will choose latest -# deployment target if(CMAKE_SYSTEM_NAME STREQUAL "iOS") # The workaround for try_compile failing with code signing # since cmake-3.18.2, not required @@ -17,6 +11,13 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS") "CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED") set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED NO) set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO) + + # Fix compile failure with armv7 deployment target >= 11.0, xcode clang + # will report: + # error: invalid iOS deployment version '--target=armv7-apple-ios13.6', + # iOS 10 is the maximum deployment target for 32-bit targets + # If CMAKE_OSX_DEPLOYMENT_TARGET is not defined, cmake will choose latest + # deployment target if("${CMAKE_OSX_ARCHITECTURES}" MATCHES ".*armv7.*") if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET OR NOT CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "11.0") -- cgit v1.2.3 From 66ebc3dc4ba690c810e973555846eceb659d8d55 Mon Sep 17 00:00:00 2001 From: HALX99 Date: Thu, 13 May 2021 18:53:26 +0800 Subject: Update CMakeLists.txt --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 007c2cdf..8d6105e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0.2) -if(CMAKE_SYSTEM_NAME STREQUAL "iOS") +if(APPLE) # The workaround for try_compile failing with code signing # since cmake-3.18.2, not required set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES @@ -11,7 +11,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS") "CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED") set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED NO) set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO) +endif() +if(CMAKE_SYSTEM_NAME STREQUAL "iOS") # Fix compile failure with armv7 deployment target >= 11.0, xcode clang # will report: # error: invalid iOS deployment version '--target=armv7-apple-ios13.6', -- cgit v1.2.3 From 9a745292bd9263e0481dfb6c485e9fa961ca3cc4 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Tue, 6 Jul 2021 09:34:40 +0200 Subject: Make OpenALConfig.cmake compatible with CMake's FindOpenAL.cmake (#581) * Make OpenALConfig.cmake compatible with CMake's FindOpenAL.cmake * Create and install OpenALConfigVersion.cmake * cmake: drop creating of OpenALConfigVersion.cmake --- CMakeLists.txt | 16 ++++++++++++---- OpenALConfig.cmake.in | 9 +++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 OpenALConfig.cmake.in (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index f8a81422..e754bf31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,7 @@ include(CheckCXXCompilerFlag) include(CheckCSourceCompiles) include(CheckCXXSourceCompiles) include(CheckStructHasMember) +include(CMakePackageConfigHelpers) include(GNUInstallDirs) @@ -1289,7 +1290,7 @@ else() target_include_directories(OpenAL PUBLIC $ - $ + $ PRIVATE ${OpenAL_SOURCE_DIR}/common ${OpenAL_BINARY_DIR} @@ -1354,7 +1355,10 @@ endif() target_include_directories(${IMPL_TARGET} PUBLIC $ - $ + INTERFACE + $ + $ + $ PRIVATE ${INC_PATHS} ${OpenAL_BINARY_DIR} @@ -1421,6 +1425,8 @@ endif() # Install main library if(ALSOFT_INSTALL) + configure_package_config_file(OpenALConfig.cmake.in OpenALConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL) install(TARGETS OpenAL EXPORT OpenAL RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -1429,15 +1435,17 @@ if(ALSOFT_INSTALL) INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/AL) export(TARGETS OpenAL NAMESPACE OpenAL:: - FILE OpenALConfig.cmake) + FILE OpenALTargets.cmake) install(EXPORT OpenAL DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL NAMESPACE OpenAL:: - FILE OpenALConfig.cmake) + FILE OpenALTargets.cmake) install(DIRECTORY include/AL DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(FILES "${OpenAL_BINARY_DIR}/openal.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + install(FILES "${OpenAL_BINARY_DIR}/OpenALConfig.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL") if(TARGET soft_oal) install(TARGETS soft_oal RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/OpenALConfig.cmake.in b/OpenALConfig.cmake.in new file mode 100644 index 00000000..128c1a4e --- /dev/null +++ b/OpenALConfig.cmake.in @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.1) + +include("${CMAKE_CURRENT_LIST_DIR}/OpenALTargets.cmake") + +set(OPENAL_FOUND ON) +set(OPENAL_INCLUDE_DIR $) +set(OPENAL_LIBRARY $) +set(OPENAL_DEFINITIONS $) +set(OPENAL_VERSION_STRING @PACKAGE_VERSION@) -- cgit v1.2.3 From 955fdebcad228e471c2f6f6c4ff976ca1bf442a0 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 9 Jul 2021 08:57:33 -0700 Subject: Add a utility to encode audio files to UHJ --- CMakeLists.txt | 8 + utils/uhjencoder.cpp | 454 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 462 insertions(+) create mode 100644 utils/uhjencoder.cpp (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index e754bf31..4b793518 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1496,6 +1496,14 @@ if(ALSOFT_UTILS) target_compile_options(uhjdecoder PRIVATE ${C_FLAGS}) target_link_libraries(uhjdecoder PUBLIC common PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG}) + + add_executable(uhjencoder utils/uhjencoder.cpp) + target_compile_definitions(uhjencoder PRIVATE ${CPP_DEFS}) + target_include_directories(uhjencoder + PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common) + target_compile_options(uhjencoder PRIVATE ${C_FLAGS}) + target_link_libraries(uhjencoder PUBLIC common + PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG}) endif() if(MYSOFA_FOUND) diff --git a/utils/uhjencoder.cpp b/utils/uhjencoder.cpp new file mode 100644 index 00000000..70124f66 --- /dev/null +++ b/utils/uhjencoder.cpp @@ -0,0 +1,454 @@ +/* + * 2-channel UHJ Encoder + * + * Copyright (c) Chris Robinson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "almalloc.h" +#include "alspan.h" +#include "math_defs.h" +#include "opthelpers.h" +#include "phase_shifter.h" +#include "vector.h" + +#include "sndfile.h" + +#include "win_main_utf8.h" + + +namespace { + +struct SndFileDeleter { + void operator()(SNDFILE *sndfile) { sf_close(sndfile); } +}; +using SndFilePtr = std::unique_ptr; + + +using uint = unsigned int; + +constexpr uint BufferLineSize{1024}; + +using FloatBufferLine = std::array; +using FloatBufferSpan = al::span; + + +struct UhjEncoder { + constexpr static size_t sFilterDelay{256}; + + /* Delays and processing storage for the unfiltered signal. */ + alignas(16) std::array mS{}; + alignas(16) std::array mD{}; + + /* History for the FIR filter. */ + alignas(16) std::array mWXHistory{}; + + alignas(16) std::array mTemp{}; + + void encode(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, + const FloatBufferLine *InSamples, const size_t SamplesToDo); + + DEF_NEWDEL(UhjEncoder) +}; + +const PhaseShifterT PShift{}; + + +/* Encoding UHJ from B-Format is done as: + * + * S = 0.9396926*W + 0.1855740*X + * D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y + * + * Left = (S + D)/2.0 + * Right = (S - D)/2.0 + * T = j(-0.1432*W + 0.6511746*X) - 0.7071068*Y + * Q = 0.9772*Z + * + * where j is a wide-band +90 degree phase shift. T is excluded from 2-channel + * output, and Q is excluded from 2- and 3-channel output. + */ +void UhjEncoder::encode(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, + const FloatBufferLine *InSamples, const size_t SamplesToDo) +{ + float *RESTRICT left{al::assume_aligned<16>(LeftOut.data())}; + float *RESTRICT right{al::assume_aligned<16>(RightOut.data())}; + + const float *RESTRICT winput{al::assume_aligned<16>(InSamples[0].data())}; + const float *RESTRICT xinput{al::assume_aligned<16>(InSamples[1].data())}; + const float *RESTRICT yinput{al::assume_aligned<16>(InSamples[2].data())}; + + /* Combine the previously delayed S/D signal with the input. */ + + /* S = 0.9396926*W + 0.1855740*X */ + auto miditer = mS.begin() + sFilterDelay; + std::transform(winput, winput+SamplesToDo, xinput, miditer, + [](const float w, const float x) noexcept -> float + { return 0.9396926f*w + 0.1855740f*x; }); + + /* D = 0.6554516*Y */ + auto sideiter = mD.begin() + sFilterDelay; + std::transform(yinput, yinput+SamplesToDo, sideiter, + [](const float y) noexcept -> float { return 0.6554516f*y; }); + + /* D += j(-0.3420201*W + 0.5098604*X) */ + auto tmpiter = std::copy(mWXHistory.cbegin(), mWXHistory.cend(), mTemp.begin()); + std::transform(winput, winput+SamplesToDo, xinput, tmpiter, + [](const float w, const float x) noexcept -> float + { return -0.3420201f*w + 0.5098604f*x; }); + std::copy_n(mTemp.cbegin()+SamplesToDo, mWXHistory.size(), mWXHistory.begin()); + PShift.processAccum({mD.data(), SamplesToDo}, mTemp.data()); + + /* Left = (S + D)/2.0 */ + for(size_t i{0};i < SamplesToDo;i++) + left[i] = (mS[i] + mD[i]) * 0.5f; + /* Right = (S - D)/2.0 */ + for(size_t i{0};i < SamplesToDo;i++) + right[i] = (mS[i] - mD[i]) * 0.5f; + + /* Copy the future samples to the front for next time. */ + std::copy(mS.cbegin()+SamplesToDo, mS.cbegin()+SamplesToDo+sFilterDelay, mS.begin()); + std::copy(mD.cbegin()+SamplesToDo, mD.cbegin()+SamplesToDo+sFilterDelay, mD.begin()); +} + + +struct SpeakerPos { + int mChannelID; + float mAzimuth; + float mElevation; +}; + +/* Azimuth is counter-clockwise. */ +const SpeakerPos StereoMap[2]{ + { SF_CHANNEL_MAP_LEFT, Deg2Rad( 30.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_RIGHT, Deg2Rad(-30.0f), Deg2Rad(0.0f) }, +}, QuadMap[4]{ + { SF_CHANNEL_MAP_LEFT, Deg2Rad( 45.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_RIGHT, Deg2Rad( -45.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_REAR_LEFT, Deg2Rad( 135.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_REAR_RIGHT, Deg2Rad(-135.0f), Deg2Rad(0.0f) }, +}, X51Map[6]{ + { SF_CHANNEL_MAP_LEFT, Deg2Rad( 30.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_RIGHT, Deg2Rad( -30.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_CENTER, Deg2Rad( 0.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_LFE, 0.0f, 0.0f }, + { SF_CHANNEL_MAP_SIDE_LEFT, Deg2Rad( 110.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_SIDE_RIGHT, Deg2Rad(-110.0f), Deg2Rad(0.0f) }, +}, X51RearMap[6]{ + { SF_CHANNEL_MAP_LEFT, Deg2Rad( 30.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_RIGHT, Deg2Rad( -30.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_CENTER, Deg2Rad( 0.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_LFE, 0.0f, 0.0f }, + { SF_CHANNEL_MAP_REAR_LEFT, Deg2Rad( 110.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_REAR_RIGHT, Deg2Rad(-110.0f), Deg2Rad(0.0f) }, +}, X71Map[8]{ + { SF_CHANNEL_MAP_LEFT, Deg2Rad( 30.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_RIGHT, Deg2Rad( -30.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_CENTER, Deg2Rad( 0.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_LFE, 0.0f, 0.0f }, + { SF_CHANNEL_MAP_REAR_LEFT, Deg2Rad( 150.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_REAR_RIGHT, Deg2Rad(-150.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_SIDE_LEFT, Deg2Rad( 90.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_SIDE_RIGHT, Deg2Rad( -90.0f), Deg2Rad(0.0f) }, +}, X714Map[12]{ + { SF_CHANNEL_MAP_LEFT, Deg2Rad( 30.0f), Deg2Rad( 0.0f) }, + { SF_CHANNEL_MAP_RIGHT, Deg2Rad( -30.0f), Deg2Rad( 0.0f) }, + { SF_CHANNEL_MAP_CENTER, Deg2Rad( 0.0f), Deg2Rad( 0.0f) }, + { SF_CHANNEL_MAP_LFE, 0.0f, 0.0f }, + { SF_CHANNEL_MAP_REAR_LEFT, Deg2Rad( 150.0f), Deg2Rad( 0.0f) }, + { SF_CHANNEL_MAP_REAR_RIGHT, Deg2Rad(-150.0f), Deg2Rad( 0.0f) }, + { SF_CHANNEL_MAP_SIDE_LEFT, Deg2Rad( 90.0f), Deg2Rad( 0.0f) }, + { SF_CHANNEL_MAP_SIDE_RIGHT, Deg2Rad( -90.0f), Deg2Rad( 0.0f) }, + { SF_CHANNEL_MAP_TOP_FRONT_LEFT, Deg2Rad( 45.0f), Deg2Rad(35.0f) }, + { SF_CHANNEL_MAP_TOP_FRONT_RIGHT, Deg2Rad( -45.0f), Deg2Rad(35.0f) }, + { SF_CHANNEL_MAP_TOP_REAR_LEFT, Deg2Rad( 135.0f), Deg2Rad(35.0f) }, + { SF_CHANNEL_MAP_TOP_REAR_RIGHT, Deg2Rad(-135.0f), Deg2Rad(35.0f) }, +}; + +inline std::array GenCoeffs(float x /*+front*/, float y /*+left*/, float z /*+up*/) +{ + /* Coefficients are +3dB of FuMa. */ + std::array coeffs; + coeffs[0] = 1.0f; + coeffs[1] = 1.41421356237f * x; + coeffs[2] = 1.41421356237f * y; + coeffs[3] = 1.41421356237f * z; + return coeffs; +} + +} // namespace + + +int main(int argc, char **argv) +{ + if(argc < 2 || std::strcmp(argv[1], "-h") == 0 || std::strcmp(argv[1], "--help") == 0) + { + printf("Usage: %s \n\n", argv[0]); + return 1; + } + + size_t num_files{0}, num_encoded{0}; + for(int fidx{1};fidx < argc;++fidx) + { + ++num_files; + + std::string outname{argv[fidx]}; + size_t lastslash{outname.find_last_of('/')}; + if(lastslash != std::string::npos) + outname.erase(0, lastslash+1); + size_t extpos{outname.find_last_of('.')}; + if(extpos != std::string::npos) + outname.resize(extpos); + outname += ".uhj.flac"; + + SF_INFO ininfo{}; + SndFilePtr infile{sf_open(argv[fidx], SFM_READ, &ininfo)}; + if(!infile) + { + fprintf(stderr, "Failed to open %s\n", argv[fidx]); + continue; + } + printf("Converting %s to %s...\n", argv[fidx], outname.c_str()); + + /* Work out the channel map, preferably using the actual channel map + * from the file/format, but falling back to assuming WFX order. + * + * TODO: Map indices when the channel order differs from the virtual + * speaker position maps. + */ + al::span spkrs; + auto chanmap = std::vector(static_cast(ininfo.channels), SF_CHANNEL_MAP_INVALID); + if(sf_command(infile.get(), SFC_GET_CHANNEL_MAP_INFO, chanmap.data(), + ininfo.channels*int{sizeof(int)}) == SF_TRUE) + { + static const std::array stereomap{{SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT}}; + static const std::array quadmap{{SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, + SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT}}; + static const std::array x51map{{SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, + SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LFE, + SF_CHANNEL_MAP_SIDE_LEFT, SF_CHANNEL_MAP_SIDE_RIGHT}}; + static const std::array x51rearmap{{SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, + SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LFE, + SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT}}; + static const std::array x71map{{SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, + SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LFE, + SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT, + SF_CHANNEL_MAP_SIDE_LEFT, SF_CHANNEL_MAP_SIDE_RIGHT}}; + static const std::array x714map{{SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, + SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LFE, + SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT, + SF_CHANNEL_MAP_SIDE_LEFT, SF_CHANNEL_MAP_SIDE_RIGHT, + SF_CHANNEL_MAP_TOP_FRONT_LEFT, SF_CHANNEL_MAP_TOP_FRONT_RIGHT, + SF_CHANNEL_MAP_TOP_REAR_LEFT, SF_CHANNEL_MAP_TOP_REAR_RIGHT}}; + static const std::array ambi2dmap{{SF_CHANNEL_MAP_AMBISONIC_B_W, + SF_CHANNEL_MAP_AMBISONIC_B_X, SF_CHANNEL_MAP_AMBISONIC_B_Y}}; + static const std::array ambi3dmap{{SF_CHANNEL_MAP_AMBISONIC_B_W, + SF_CHANNEL_MAP_AMBISONIC_B_X, SF_CHANNEL_MAP_AMBISONIC_B_Y, + SF_CHANNEL_MAP_AMBISONIC_B_Z}}; + + auto match_chanmap = [](const al::span a, const al::span b) -> bool + { + return a.size() == b.size() + && std::mismatch(a.begin(), a.end(), b.begin(), b.end()).first == a.end(); + }; + if(match_chanmap(chanmap, stereomap)) + spkrs = StereoMap; + else if(match_chanmap(chanmap, quadmap)) + spkrs = QuadMap; + else if(match_chanmap(chanmap, x51map)) + spkrs = X51Map; + else if(match_chanmap(chanmap, x51rearmap)) + spkrs = X51RearMap; + else if(match_chanmap(chanmap, x71map)) + spkrs = X71Map; + else if(match_chanmap(chanmap, x714map)) + spkrs = X714Map; + else if(match_chanmap(chanmap, ambi2dmap) || match_chanmap(chanmap, ambi3dmap)) + { + /* Do nothing. */ + } + else + { + std::string mapstr; + if(chanmap.size() > 0) + { + mapstr = std::to_string(chanmap[0]); + for(int idx : al::span{chanmap}.subspan<1>()) + { + mapstr += ','; + mapstr += std::to_string(idx); + } + } + fprintf(stderr, " ... %zu channels not supported (map: %s)\n", chanmap.size(), + mapstr.c_str()); + continue; + } + } + else if(ininfo.channels == 2) + { + fprintf(stderr, " ... assuming WFX order stereo\n"); + spkrs = StereoMap; + } + else if(ininfo.channels == 6) + { + fprintf(stderr, " ... assuming WFX order 5.1\n"); + spkrs = X51Map; + } + else if(ininfo.channels == 8) + { + fprintf(stderr, " ... assuming WFX order 7.1\n"); + spkrs = X71Map; + } + else + { + fprintf(stderr, " ... unmapped %d-channel audio not supported\n", ininfo.channels); + continue; + } + + SF_INFO outinfo{}; + outinfo.frames = ininfo.frames; + outinfo.samplerate = ininfo.samplerate; + outinfo.channels = 2; + outinfo.format = SF_FORMAT_PCM_24 | SF_FORMAT_FLAC; + SndFilePtr outfile{sf_open(outname.c_str(), SFM_WRITE, &outinfo)}; + if(!outfile) + { + fprintf(stderr, " ... failed to create %s\n", outname.c_str()); + continue; + } + + auto encoder = std::make_unique(); + auto splbuf = al::vector(static_cast(ininfo.channels+9)); + auto ambmem = al::span{&splbuf[0], 4}; + auto encmem = al::span{&splbuf[4], 2}; + auto srcmem = al::span{splbuf[6].data(), BufferLineSize}; + auto outmem = al::span{splbuf[7].data(), BufferLineSize*2}; + + /* A number of initial samples need to be skipped to cut the lead-in + * from the all-pass filter delay. The same number of samples need to + * be fed through the encoder after reaching the end of the input file + * to ensure none of the original input is lost. + */ + size_t total_wrote{0}; + size_t LeadIn{UhjEncoder::sFilterDelay}; + sf_count_t LeadOut{UhjEncoder::sFilterDelay}; + while(LeadIn > 0 || LeadOut > 0) + { + auto inmem = splbuf[9].data(); + auto sgot = sf_readf_float(infile.get(), inmem, BufferLineSize); + + sgot = std::max(sgot, 0); + if(sgot < BufferLineSize) + { + const sf_count_t remaining{std::min(BufferLineSize - sgot, LeadOut)}; + std::fill_n(inmem + sgot*ininfo.channels, remaining*ininfo.channels, 0.0f); + sgot += remaining; + LeadOut -= remaining; + } + + for(auto&& buf : ambmem) + buf.fill(0.0f); + + auto got = static_cast(sgot); + if(spkrs.empty()) + { + /* B-Format is already in the correct order. It just needs a + * +3dB boost. + */ + constexpr float scale{1.41421356237f}; + const size_t chans{std::min(static_cast(ininfo.channels), 4u)}; + for(size_t c{0};c < chans;++c) + { + for(size_t i{0};i < got;++i) + ambmem[c][i] = inmem[i*static_cast(ininfo.channels)] * scale; + ++inmem; + } + } + else for(auto&& spkr : spkrs) + { + /* Skip LFE. Or mix directly into W? Or W+X? */ + if(spkr.mChannelID == SF_CHANNEL_MAP_LFE) + { + ++inmem; + continue; + } + + for(size_t i{0};i < got;++i) + srcmem[i] = inmem[i * static_cast(ininfo.channels)]; + ++inmem; + + const auto coeffs = GenCoeffs( + std::cos(spkr.mAzimuth) * std::cos(spkr.mElevation), + std::sin(spkr.mAzimuth) * std::cos(spkr.mElevation), + std::sin(spkr.mElevation)); + for(size_t c{0};c < 4;++c) + { + for(size_t i{0};i < got;++i) + ambmem[c][i] += srcmem[i] * coeffs[c]; + } + } + + encoder->encode(encmem[0], encmem[1], ambmem.data(), got); + if(LeadIn >= got) + { + LeadIn -= got; + continue; + } + + got -= LeadIn; + for(size_t c{0};c < 2;++c) + { + constexpr float max_val{8388607.0f / 8388608.0f}; + auto clamp = [](float v, float mn, float mx) noexcept + { return std::min(std::max(v, mn), mx); }; + for(size_t i{0};i < got;++i) + outmem[i*2 + c] = clamp(encmem[c][LeadIn+i], -1.0f, max_val); + } + LeadIn = 0; + + sf_count_t wrote{sf_writef_float(outfile.get(), outmem.data(), + static_cast(got))}; + if(wrote < 0) + fprintf(stderr, " ... failed to write samples: %d\n", sf_error(outfile.get())); + else + total_wrote += static_cast(wrote); + } + printf(" ... wrote %zu samples (%" PRId64 ").\n", total_wrote, int64_t{ininfo.frames}); + ++num_encoded; + } + if(num_encoded == 0) + fprintf(stderr, "Failed to encode any input files\n"); + else if(num_encoded < num_files) + fprintf(stderr, "Encoded %zu of %zu files\n", num_encoded, num_files); + else + printf("Encoded %s%zu file%s\n", (num_encoded > 1) ? "all " : "", num_encoded, + (num_encoded == 1) ? "" : "s"); + return 0; +} -- cgit v1.2.3 From dd659c981548b8fe5e8fc549312f5097ab299072 Mon Sep 17 00:00:00 2001 From: "Nick (nift4)" Date: Sat, 17 Jul 2021 11:28:37 +0200 Subject: CMakeLists: add QUIET to Dbus1 find_package() * Android doesn't have dbus, logspam is annoying --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b793518..8e1dbb5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -709,7 +709,7 @@ set(CORE_OBJS set(HAVE_RTKIT 0) option(ALSOFT_REQUIRE_RTKIT "Require RTKit/D-Bus support" FALSE) -find_package(DBus1) +find_package(DBus1 QUIET) if(DBus1_FOUND) option(ALSOFT_RTKIT "Enable RTKit support" ON) if(ALSOFT_RTKIT) -- cgit v1.2.3 From 87894057b240be825699dcdbfaf585862148edb3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 17 Jul 2021 11:11:26 -0700 Subject: Silence searching for Qt5 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e1dbb5e..13c62839 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1199,7 +1199,7 @@ endif() if(ALSOFT_UTILS) find_package(MySOFA) if(NOT ALSOFT_NO_CONFIG_UTIL) - find_package(Qt5Widgets) + find_package(Qt5Widgets QUIET) endif() endif() if(ALSOFT_UTILS OR ALSOFT_EXAMPLES) -- cgit v1.2.3 From 6406cc614130ba5f04555ba46e849c685ae6eae0 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 17 Jul 2021 11:47:05 -0700 Subject: Still print a warning when DBus or Qt5 aren't found This way merely avoids the several lines of spam when the config module isn't found, which there's otherwise no reasonable way to test for since they're provided by the package, which you need to use find_package to search for, which causes the spam. It's still useful to report the packages weren't found in case they were expected. --- CMakeLists.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 13c62839..446222ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -722,6 +722,16 @@ if(DBus1_FOUND) set(EXTRA_LIBS ${EXTRA_LIBS} ${DBus1_LIBRARIES}) endif() endif() +else() + set(MISSING_VARS "") + if(NOT DBus1_INCLUDE_DIRS) + set(MISSING_VARS "${MISSING_VARS} DBus1_INCLUDE_DIRS") + endif() + if(NOT DBus1_LIBRARIES) + set(MISSING_VARS "${MISSING_VARS} DBus1_LIBRARIES") + endif() + message(STATUS "Could NOT find DBus1 (missing:${MISSING_VARS})") + unset(MISSING_VARS) endif() if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT) message(FATAL_ERROR "Failed to enabled required RTKit support") @@ -1200,6 +1210,9 @@ if(ALSOFT_UTILS) find_package(MySOFA) if(NOT ALSOFT_NO_CONFIG_UTIL) find_package(Qt5Widgets QUIET) + if(NOT Qt5Widgets_FOUND) + message(STATUS "Could NOT find Qt5Widgets") + endif() endif() endif() if(ALSOFT_UTILS OR ALSOFT_EXAMPLES) -- cgit v1.2.3 From 4cc820bb5c4c3c93ec85fed5a5bf7978b6bd14b1 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 2 Aug 2021 21:03:18 -0700 Subject: Start a PipeWire backend It's just a copy of the Null backend to start with --- CMakeLists.txt | 21 ++++++ alc/alc.cpp | 6 ++ alc/backends/pipewire.cpp | 179 ++++++++++++++++++++++++++++++++++++++++++++++ alc/backends/pipewire.h | 19 +++++ config.h.in | 3 + 5 files changed, 228 insertions(+) create mode 100644 alc/backends/pipewire.cpp create mode 100644 alc/backends/pipewire.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 446222ff..5d56a729 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,8 @@ include(CheckStructHasMember) include(CMakePackageConfigHelpers) include(GNUInstallDirs) +find_package(PkgConfig) + option(ALSOFT_DLOPEN "Check for the dlopen API for loading optional libs" ON) @@ -836,6 +838,7 @@ endif() set(HAVE_ALSA 0) set(HAVE_OSS 0) +set(HAVE_PIPEWIRE 0) set(HAVE_SOLARIS 0) set(HAVE_SNDIO 0) set(HAVE_DSOUND 0) @@ -907,6 +910,24 @@ if(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS) message(FATAL_ERROR "Failed to enabled required OSS backend") endif() +# Check PipeWire backend +option(ALSOFT_REQUIRE_PIPEWIRE "Require PipeWire backend" OFF) +if(PkgConfig_FOUND) + pkg_check_modules(PIPEWIRE libpipewire-0.3) + if(PIPEWIRE_FOUND) + option(ALSOFT_BACKEND_PIPEWIRE "Enable PipeWire backend" ON) + if(ALSOFT_BACKEND_PIPEWIRE) + set(HAVE_PIPEWIRE 1) + set(BACKENDS "${BACKENDS} PipeWire,") + set(ALC_OBJS ${ALC_OBJS} alc/backends/pipewire.cpp alc/backends/pipewire.h) + set(INC_PATHS ${INC_PATHS} ${PIPEWIRE_INCLUDE_DIRS}) + endif() + endif() +endif() +if(ALSOFT_REQUIRE_PIPEWIRE AND NOT HAVE_PIPEWIRE) + message(FATAL_ERROR "Failed to enabled required PipeWire backend") +endif() + # Check Solaris backend option(ALSOFT_REQUIRE_SOLARIS "Require Solaris backend" OFF) find_package(AudioIO) diff --git a/alc/alc.cpp b/alc/alc.cpp index 8c09e474..c3bbdf29 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -106,6 +106,9 @@ #include "backends/base.h" #include "backends/null.h" #include "backends/loopback.h" +#ifdef HAVE_PIPEWIRE +#include "backends/pipewire.h" +#endif #ifdef HAVE_JACK #include "backends/jack.h" #endif @@ -240,6 +243,9 @@ BackendInfo BackendList[] = { { "sdl2", SDL2BackendFactory::getFactory }, #endif +#ifdef HAVE_PIPEWIRE + { "pipewire", PipeWireBackendFactory::getFactory }, +#endif { "null", NullBackendFactory::getFactory }, #ifdef HAVE_WAVE { "wave", WaveBackendFactory::getFactory }, diff --git a/alc/backends/pipewire.cpp b/alc/backends/pipewire.cpp new file mode 100644 index 00000000..00bf7cea --- /dev/null +++ b/alc/backends/pipewire.cpp @@ -0,0 +1,179 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2010 by Chris Robinson + * 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 "pipewire.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "core/device.h" +#include "almalloc.h" +#include "core/helpers.h" +#include "threads.h" + + +namespace { + +using std::chrono::seconds; +using std::chrono::milliseconds; +using std::chrono::nanoseconds; + +constexpr char pipeDevice[] = "No Output"; + + +struct PipeWireBackend final : public BackendBase { + PipeWireBackend(DeviceBase *device) noexcept : BackendBase{device} { } + + int mixerProc(); + + void open(const char *name) override; + bool reset() override; + void start() override; + void stop() override; + + std::atomic mKillNow{true}; + std::thread mThread; + + DEF_NEWDEL(PipeWireBackend) +}; + +int PipeWireBackend::mixerProc() +{ + const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2}; + + SetRTPriority(); + althrd_setname(MIXER_THREAD_NAME); + + int64_t done{0}; + auto start = std::chrono::steady_clock::now(); + while(!mKillNow.load(std::memory_order_acquire) + && mDevice->Connected.load(std::memory_order_acquire)) + { + auto now = std::chrono::steady_clock::now(); + + /* This converts from nanoseconds to nanosamples, then to samples. */ + int64_t avail{std::chrono::duration_cast((now-start) * mDevice->Frequency).count()}; + if(avail-done < mDevice->UpdateSize) + { + std::this_thread::sleep_for(restTime); + continue; + } + while(avail-done >= mDevice->UpdateSize) + { + mDevice->renderSamples(nullptr, mDevice->UpdateSize, 0u); + done += mDevice->UpdateSize; + } + + /* For every completed second, increment the start time and reduce the + * samples done. This prevents the difference between the start time + * and current time from growing too large, while maintaining the + * correct number of samples to render. + */ + if(done >= mDevice->Frequency) + { + seconds s{done/mDevice->Frequency}; + start += s; + done -= mDevice->Frequency*s.count(); + } + } + + return 0; +} + + +void PipeWireBackend::open(const char *name) +{ + if(!name) + name = pipeDevice; + else if(strcmp(name, pipeDevice) != 0) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", + name}; + + mDevice->DeviceName = name; +} + +bool PipeWireBackend::reset() +{ + setDefaultWFXChannelOrder(); + return true; +} + +void PipeWireBackend::start() +{ + try { + mKillNow.store(false, std::memory_order_release); + mThread = std::thread{std::mem_fn(&PipeWireBackend::mixerProc), this}; + } + catch(std::exception& e) { + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to start mixing thread: %s", e.what()}; + } +} + +void PipeWireBackend::stop() +{ + if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable()) + return; + mThread.join(); +} + +} // namespace + + +bool PipeWireBackendFactory::init() +{ return true; } + +bool PipeWireBackendFactory::querySupport(BackendType type) +{ return (type == BackendType::Playback); } + +std::string PipeWireBackendFactory::probe(BackendType type) +{ + std::string outnames; + switch(type) + { + case BackendType::Playback: + /* Includes null char. */ + outnames.append(pipeDevice, sizeof(pipeDevice)); + break; + case BackendType::Capture: + break; + } + return outnames; +} + +BackendPtr PipeWireBackendFactory::createBackend(DeviceBase *device, BackendType type) +{ + if(type == BackendType::Playback) + return BackendPtr{new PipeWireBackend{device}}; + return nullptr; +} + +BackendFactory &PipeWireBackendFactory::getFactory() +{ + static PipeWireBackendFactory factory{}; + return factory; +} diff --git a/alc/backends/pipewire.h b/alc/backends/pipewire.h new file mode 100644 index 00000000..f8d3d5c2 --- /dev/null +++ b/alc/backends/pipewire.h @@ -0,0 +1,19 @@ +#ifndef BACKENDS_PIPEWIRE_H +#define BACKENDS_PIPEWIRE_H + +#include "base.h" + +struct PipeWireBackendFactory final : public BackendFactory { +public: + bool init() override; + + bool querySupport(BackendType type) override; + + std::string probe(BackendType type) override; + + BackendPtr createBackend(DeviceBase *device, BackendType type) override; + + static BackendFactory &getFactory(); +}; + +#endif /* BACKENDS_PIPEWIRE_H */ diff --git a/config.h.in b/config.h.in index 5cc78ae5..b06314a8 100644 --- a/config.h.in +++ b/config.h.in @@ -31,6 +31,9 @@ /* Define if we have the OSS backend */ #cmakedefine HAVE_OSS +/* Define if we have the PipeWire backend */ +#cmakedefine HAVE_PIPEWIRE + /* Define if we have the Solaris backend */ #cmakedefine HAVE_SOLARIS -- cgit v1.2.3 From 602b33fede0ce7d158ca96cda6db45742b2b2fce Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 23 Aug 2021 11:08:58 -0700 Subject: Always define NOMINMAX on Windows Clang needs it too, not just MSVC, and it doesn't seem to hurt MinGW. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d56a729..9ed212db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,7 +121,7 @@ set(LINKER_FLAGS ) set(EXTRA_LIBS ) if(WIN32) - set(CPP_DEFS ${CPP_DEFS} _WIN32) + set(CPP_DEFS ${CPP_DEFS} _WIN32 NOMINMAX) if(MINGW) set(CPP_DEFS ${CPP_DEFS} __USE_MINGW_ANSI_STDIO) endif() @@ -210,7 +210,7 @@ if(HAVE_LIBLOG) endif() if(MSVC) - set(CPP_DEFS ${CPP_DEFS} _CRT_SECURE_NO_WARNINGS NOMINMAX) + set(CPP_DEFS ${CPP_DEFS} _CRT_SECURE_NO_WARNINGS) check_cxx_compiler_flag(/permissive- HAVE_PERMISSIVE_SWITCH) if(HAVE_PERMISSIVE_SWITCH) set(C_FLAGS ${C_FLAGS} $<$:/permissive->) -- cgit v1.2.3 From 35348869a9deb989328ed206c70c14ac3418c2ef Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 8 Nov 2021 21:16:33 -0800 Subject: Disable MSVC warning 4127 "conditional expression is constant", which C++14 can't do anything about since 'if constexpr' was added in C++17. The checks are necessary since it's dealing with a templatized type, or a compile-time non-macro constant for the target system's endian order. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ed212db..77b765c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -215,7 +215,7 @@ if(MSVC) if(HAVE_PERMISSIVE_SWITCH) set(C_FLAGS ${C_FLAGS} $<$:/permissive->) endif() - set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4268 /wd4324 /wd5030) + set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4127 /wd4268 /wd4324 /wd5030) # Remove /W3, which is added by default, since we set /W4. Some build # generators with MSVC complain about both /W3 and /W4 being specified. foreach(flag_var CMAKE_C_FLAGS CMAKE_CXX_FLAGS) -- cgit v1.2.3 From d16b61dffb24cbe90e03e921684cb538cb23e181 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 17 Dec 2021 17:13:59 -0800 Subject: Move the effects base and effectslot to core --- CMakeLists.txt | 5 +- al/auxeffectslot.h | 2 +- alc/alc.cpp | 4 +- alc/alu.cpp | 4 +- alc/context.cpp | 2 +- alc/effects/autowah.cpp | 2 +- alc/effects/base.h | 201 +------------------------------------------ alc/effects/chorus.cpp | 2 +- alc/effects/compressor.cpp | 2 +- alc/effects/convolution.cpp | 2 +- alc/effects/dedicated.cpp | 2 +- alc/effects/distortion.cpp | 2 +- alc/effects/echo.cpp | 2 +- alc/effects/equalizer.cpp | 2 +- alc/effects/fshifter.cpp | 2 +- alc/effects/modulator.cpp | 2 +- alc/effects/pshifter.cpp | 2 +- alc/effects/reverb.cpp | 2 +- alc/effects/vmorpher.cpp | 2 +- alc/effectslot.cpp | 25 ------ alc/effectslot.h | 88 ------------------- core/effects/base.h | 205 ++++++++++++++++++++++++++++++++++++++++++++ core/effectslot.cpp | 25 ++++++ core/effectslot.h | 88 +++++++++++++++++++ 24 files changed, 341 insertions(+), 334 deletions(-) delete mode 100644 alc/effectslot.cpp delete mode 100644 alc/effectslot.h create mode 100644 core/effects/base.h create mode 100644 core/effectslot.cpp create mode 100644 core/effectslot.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 77b765c3..0704df58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -678,6 +678,9 @@ set(CORE_OBJS core/devformat.h core/device.cpp core/device.h + core/effects/base.h + core/effectslot.cpp + core/effectslot.h core/except.cpp core/except.h core/filters/biquad.h @@ -792,8 +795,6 @@ set(ALC_OBJS alc/context.h alc/device.cpp alc/device.h - alc/effectslot.cpp - alc/effectslot.h alc/effects/base.h alc/effects/autowah.cpp alc/effects/chorus.cpp diff --git a/al/auxeffectslot.h b/al/auxeffectslot.h index 27423830..4de1df7a 100644 --- a/al/auxeffectslot.h +++ b/al/auxeffectslot.h @@ -9,10 +9,10 @@ #include "AL/efx.h" #include "alc/device.h" -#include "alc/effectslot.h" #include "alc/effects/base.h" #include "almalloc.h" #include "atomic.h" +#include "core/effectslot.h" #include "intrusive_ptr.h" #include "vector.h" diff --git a/alc/alc.cpp b/alc/alc.cpp index 89c836d0..c6ac6508 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -68,8 +68,6 @@ #include "albit.h" #include "albyte.h" #include "alconfig.h" -#include "alc/context.h" -#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "aloptional.h" @@ -77,6 +75,7 @@ #include "alstring.h" #include "alu.h" #include "atomic.h" +#include "context.h" #include "core/ambidefs.h" #include "core/bformatdec.h" #include "core/bs2b.h" @@ -84,6 +83,7 @@ #include "core/cpu_caps.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effectslot.h" #include "core/except.h" #include "core/helpers.h" #include "core/mastering.h" diff --git a/alc/alu.cpp b/alc/alu.cpp index 54bb7001..d11a6c18 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -56,6 +56,8 @@ #include "core/cpu_caps.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" +#include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/filters/nfc.h" #include "core/fpu_ctrl.h" @@ -68,8 +70,6 @@ #include "core/uhjfilter.h" #include "core/voice.h" #include "core/voice_change.h" -#include "effects/base.h" -#include "effectslot.h" #include "intrusive_ptr.h" #include "math_defs.h" #include "opthelpers.h" diff --git a/alc/context.cpp b/alc/context.cpp index af78603a..b870a2a3 100644 --- a/alc/context.cpp +++ b/alc/context.cpp @@ -21,11 +21,11 @@ #include "alc/alu.h" #include "core/async_event.h" #include "core/device.h" +#include "core/effectslot.h" #include "core/logging.h" #include "core/voice.h" #include "core/voice_change.h" #include "device.h" -#include "effectslot.h" #include "ringbuffer.h" #include "vecmat.h" diff --git a/alc/effects/autowah.cpp b/alc/effects/autowah.cpp index 9c2ec335..f496eb51 100644 --- a/alc/effects/autowah.cpp +++ b/alc/effects/autowah.cpp @@ -27,7 +27,6 @@ #include #include "alc/effects/base.h" -#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -36,6 +35,7 @@ #include "core/context.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effectslot.h" #include "core/mixer.h" #include "intrusive_ptr.h" #include "math_defs.h" diff --git a/alc/effects/base.h b/alc/effects/base.h index 1fb339aa..95695857 100644 --- a/alc/effects/base.h +++ b/alc/effects/base.h @@ -1,206 +1,7 @@ #ifndef EFFECTS_BASE_H #define EFFECTS_BASE_H -#include - -#include "albyte.h" -#include "almalloc.h" -#include "alspan.h" -#include "atomic.h" -#include "core/bufferline.h" -#include "intrusive_ptr.h" - -struct BufferStorage; -struct ContextBase; -struct DeviceBase; -struct EffectSlot; -struct MixParams; -struct RealMixParams; - - -/** Target gain for the reverb decay feedback reaching the decay time. */ -constexpr float ReverbDecayGain{0.001f}; /* -60 dB */ - -constexpr float ReverbMaxReflectionsDelay{0.3f}; -constexpr float ReverbMaxLateReverbDelay{0.1f}; - -enum class ChorusWaveform { - Sinusoid, - Triangle -}; - -constexpr float ChorusMaxDelay{0.016f}; -constexpr float FlangerMaxDelay{0.004f}; - -constexpr float EchoMaxDelay{0.207f}; -constexpr float EchoMaxLRDelay{0.404f}; - -enum class FShifterDirection { - Down, - Up, - Off -}; - -enum class ModulatorWaveform { - Sinusoid, - Sawtooth, - Square -}; - -enum class VMorpherPhenome { - A, E, I, O, U, - AA, AE, AH, AO, EH, ER, IH, IY, UH, UW, - B, D, F, G, J, K, L, M, N, P, R, S, T, V, Z -}; - -enum class VMorpherWaveform { - Sinusoid, - Triangle, - Sawtooth -}; - -union EffectProps { - struct { - // Shared Reverb Properties - float Density; - float Diffusion; - float Gain; - float GainHF; - float DecayTime; - float DecayHFRatio; - float ReflectionsGain; - float ReflectionsDelay; - float LateReverbGain; - float LateReverbDelay; - float AirAbsorptionGainHF; - float RoomRolloffFactor; - bool DecayHFLimit; - - // Additional EAX Reverb Properties - float GainLF; - float DecayLFRatio; - float ReflectionsPan[3]; - float LateReverbPan[3]; - float EchoTime; - float EchoDepth; - float ModulationTime; - float ModulationDepth; - float HFReference; - float LFReference; - } Reverb; - - struct { - float AttackTime; - float ReleaseTime; - float Resonance; - float PeakGain; - } Autowah; - - struct { - ChorusWaveform Waveform; - int Phase; - float Rate; - float Depth; - float Feedback; - float Delay; - } Chorus; /* Also Flanger */ - - struct { - bool OnOff; - } Compressor; - - struct { - float Edge; - float Gain; - float LowpassCutoff; - float EQCenter; - float EQBandwidth; - } Distortion; - - struct { - float Delay; - float LRDelay; - - float Damping; - float Feedback; - - float Spread; - } Echo; - - struct { - float LowCutoff; - float LowGain; - float Mid1Center; - float Mid1Gain; - float Mid1Width; - float Mid2Center; - float Mid2Gain; - float Mid2Width; - float HighCutoff; - float HighGain; - } Equalizer; - - struct { - float Frequency; - FShifterDirection LeftDirection; - FShifterDirection RightDirection; - } Fshifter; - - struct { - float Frequency; - float HighPassCutoff; - ModulatorWaveform Waveform; - } Modulator; - - struct { - int CoarseTune; - int FineTune; - } Pshifter; - - struct { - float Rate; - VMorpherPhenome PhonemeA; - VMorpherPhenome PhonemeB; - int PhonemeACoarseTuning; - int PhonemeBCoarseTuning; - VMorpherWaveform Waveform; - } Vmorpher; - - struct { - float Gain; - } Dedicated; -}; - - -struct EffectTarget { - MixParams *Main; - RealMixParams *RealOut; -}; - -struct EffectState : public al::intrusive_ref { - struct Buffer { - const BufferStorage *storage; - al::span samples; - }; - - al::span mOutTarget; - - - virtual ~EffectState() = default; - - virtual void deviceUpdate(const DeviceBase *device, const Buffer &buffer) = 0; - virtual void update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) = 0; - virtual void process(const size_t samplesToDo, const al::span samplesIn, - const al::span samplesOut) = 0; -}; - - -struct EffectStateFactory { - virtual ~EffectStateFactory() = default; - - virtual al::intrusive_ptr create() = 0; -}; +#include "core/effects/base.h" EffectStateFactory *NullStateFactory_getFactory(void); diff --git a/alc/effects/chorus.cpp b/alc/effects/chorus.cpp index 3a1b9ae4..7d32f8ff 100644 --- a/alc/effects/chorus.cpp +++ b/alc/effects/chorus.cpp @@ -27,7 +27,6 @@ #include #include "alc/effects/base.h" -#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -35,6 +34,7 @@ #include "core/context.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effectslot.h" #include "core/mixer.h" #include "core/mixer/defs.h" #include "core/resampler_limits.h" diff --git a/alc/effects/compressor.cpp b/alc/effects/compressor.cpp index 030bfe08..366f2275 100644 --- a/alc/effects/compressor.cpp +++ b/alc/effects/compressor.cpp @@ -38,7 +38,6 @@ #include #include "alc/effects/base.h" -#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -46,6 +45,7 @@ #include "core/bufferline.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effectslot.h" #include "core/mixer.h" #include "core/mixer/defs.h" #include "intrusive_ptr.h" diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index 5724badb..dbbca143 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -19,7 +19,6 @@ #include "albyte.h" #include "alcomplex.h" -#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -30,6 +29,7 @@ #include "core/context.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effectslot.h" #include "core/filters/splitter.h" #include "core/fmt_traits.h" #include "core/mixer.h" diff --git a/alc/effects/dedicated.cpp b/alc/effects/dedicated.cpp index e7ea89e0..671eb5ec 100644 --- a/alc/effects/dedicated.cpp +++ b/alc/effects/dedicated.cpp @@ -26,12 +26,12 @@ #include #include "alc/effects/base.h" -#include "alc/effectslot.h" #include "almalloc.h" #include "alspan.h" #include "core/bufferline.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effectslot.h" #include "core/mixer.h" #include "intrusive_ptr.h" diff --git a/alc/effects/distortion.cpp b/alc/effects/distortion.cpp index 26b4df8e..c9366791 100644 --- a/alc/effects/distortion.cpp +++ b/alc/effects/distortion.cpp @@ -26,7 +26,6 @@ #include #include "alc/effects/base.h" -#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -34,6 +33,7 @@ #include "core/context.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/mixer.h" #include "core/mixer/defs.h" diff --git a/alc/effects/echo.cpp b/alc/effects/echo.cpp index 4cdef37c..5d003718 100644 --- a/alc/effects/echo.cpp +++ b/alc/effects/echo.cpp @@ -27,7 +27,6 @@ #include #include "alc/effects/base.h" -#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -35,6 +34,7 @@ #include "core/context.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/mixer.h" #include "intrusive_ptr.h" diff --git a/alc/effects/equalizer.cpp b/alc/effects/equalizer.cpp index 800330a3..67ad67b0 100644 --- a/alc/effects/equalizer.cpp +++ b/alc/effects/equalizer.cpp @@ -28,7 +28,6 @@ #include #include "alc/effects/base.h" -#include "alc/effectslot.h" #include "almalloc.h" #include "alspan.h" #include "core/ambidefs.h" @@ -36,6 +35,7 @@ #include "core/context.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/mixer.h" #include "intrusive_ptr.h" diff --git a/alc/effects/fshifter.cpp b/alc/effects/fshifter.cpp index c25aab82..b143db0c 100644 --- a/alc/effects/fshifter.cpp +++ b/alc/effects/fshifter.cpp @@ -28,7 +28,6 @@ #include #include "alc/effects/base.h" -#include "alc/effectslot.h" #include "alcomplex.h" #include "almalloc.h" #include "alnumeric.h" @@ -37,6 +36,7 @@ #include "core/context.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effectslot.h" #include "core/mixer.h" #include "core/mixer/defs.h" #include "intrusive_ptr.h" diff --git a/alc/effects/modulator.cpp b/alc/effects/modulator.cpp index a518ff63..4a086b11 100644 --- a/alc/effects/modulator.cpp +++ b/alc/effects/modulator.cpp @@ -26,7 +26,6 @@ #include #include "alc/effects/base.h" -#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -35,6 +34,7 @@ #include "core/context.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/mixer.h" #include "intrusive_ptr.h" diff --git a/alc/effects/pshifter.cpp b/alc/effects/pshifter.cpp index 26115605..dae0a267 100644 --- a/alc/effects/pshifter.cpp +++ b/alc/effects/pshifter.cpp @@ -28,7 +28,6 @@ #include #include "alc/effects/base.h" -#include "alc/effectslot.h" #include "alcomplex.h" #include "almalloc.h" #include "alnumeric.h" @@ -36,6 +35,7 @@ #include "core/bufferline.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effectslot.h" #include "core/mixer.h" #include "core/mixer/defs.h" #include "intrusive_ptr.h" diff --git a/alc/effects/reverb.cpp b/alc/effects/reverb.cpp index d6f1dbbf..db0aeb62 100644 --- a/alc/effects/reverb.cpp +++ b/alc/effects/reverb.cpp @@ -29,7 +29,6 @@ #include #include "alc/effects/base.h" -#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -38,6 +37,7 @@ #include "core/context.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/filters/splitter.h" #include "core/mixer.h" diff --git a/alc/effects/vmorpher.cpp b/alc/effects/vmorpher.cpp index 6c419ba2..e3eed179 100644 --- a/alc/effects/vmorpher.cpp +++ b/alc/effects/vmorpher.cpp @@ -39,7 +39,6 @@ #include #include "alc/effects/base.h" -#include "alc/effectslot.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" @@ -48,6 +47,7 @@ #include "core/context.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effectslot.h" #include "core/mixer.h" #include "intrusive_ptr.h" #include "math_defs.h" diff --git a/alc/effectslot.cpp b/alc/effectslot.cpp deleted file mode 100644 index 51fb8d46..00000000 --- a/alc/effectslot.cpp +++ /dev/null @@ -1,25 +0,0 @@ - -#include "config.h" - -#include "effectslot.h" - -#include - -#include "almalloc.h" -#include "context.h" - - -EffectSlotArray *EffectSlot::CreatePtrArray(size_t count) noexcept -{ - /* Allocate space for twice as many pointers, so the mixer has scratch - * space to store a sorted list during mixing. - */ - void *ptr{al_calloc(alignof(EffectSlotArray), EffectSlotArray::Sizeof(count*2))}; - return al::construct_at(static_cast(ptr), count); -} - -EffectSlot::~EffectSlot() -{ - if(mWetBuffer) - mWetBuffer->mInUse = false; -} diff --git a/alc/effectslot.h b/alc/effectslot.h deleted file mode 100644 index cbb1a2f5..00000000 --- a/alc/effectslot.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef EFFECTSLOT_H -#define EFFECTSLOT_H - -#include - -#include "almalloc.h" -#include "core/device.h" -#include "effects/base.h" -#include "intrusive_ptr.h" - -struct EffectSlot; -struct WetBuffer; - -using EffectSlotArray = al::FlexArray; - - -enum class EffectSlotType : unsigned char { - None, - Reverb, - Chorus, - Distortion, - Echo, - Flanger, - FrequencyShifter, - VocalMorpher, - PitchShifter, - RingModulator, - Autowah, - Compressor, - Equalizer, - EAXReverb, - DedicatedLFE, - DedicatedDialog, - Convolution -}; - -struct EffectSlotProps { - float Gain; - bool AuxSendAuto; - EffectSlot *Target; - - EffectSlotType Type; - EffectProps Props; - - al::intrusive_ptr State; - - std::atomic next; - - DEF_NEWDEL(EffectSlotProps) -}; - - -struct EffectSlot { - std::atomic Update{nullptr}; - - /* Wet buffer configuration is ACN channel order with N3D scaling. - * Consequently, effects that only want to work with mono input can use - * channel 0 by itself. Effects that want multichannel can process the - * ambisonics signal and make a B-Format source pan. - */ - MixParams Wet; - - float Gain{1.0f}; - bool AuxSendAuto{true}; - EffectSlot *Target{nullptr}; - - EffectSlotType EffectType{EffectSlotType::None}; - EffectProps mEffectProps{}; - EffectState *mEffectState{nullptr}; - - float RoomRolloff{0.0f}; /* Added to the source's room rolloff, not multiplied. */ - float DecayTime{0.0f}; - float DecayLFRatio{0.0f}; - float DecayHFRatio{0.0f}; - bool DecayHFLimit{false}; - float AirAbsorptionGainHF{1.0f}; - - /* Mixing buffer used by the Wet mix. */ - WetBuffer *mWetBuffer{nullptr}; - - ~EffectSlot(); - - static EffectSlotArray *CreatePtrArray(size_t count) noexcept; - - DISABLE_ALLOC() -}; - -#endif /* EFFECTSLOT_H */ diff --git a/core/effects/base.h b/core/effects/base.h new file mode 100644 index 00000000..3094f627 --- /dev/null +++ b/core/effects/base.h @@ -0,0 +1,205 @@ +#ifndef CORE_EFFECTS_BASE_H +#define CORE_EFFECTS_BASE_H + +#include + +#include "albyte.h" +#include "almalloc.h" +#include "alspan.h" +#include "atomic.h" +#include "core/bufferline.h" +#include "intrusive_ptr.h" + +struct BufferStorage; +struct ContextBase; +struct DeviceBase; +struct EffectSlot; +struct MixParams; +struct RealMixParams; + + +/** Target gain for the reverb decay feedback reaching the decay time. */ +constexpr float ReverbDecayGain{0.001f}; /* -60 dB */ + +constexpr float ReverbMaxReflectionsDelay{0.3f}; +constexpr float ReverbMaxLateReverbDelay{0.1f}; + +enum class ChorusWaveform { + Sinusoid, + Triangle +}; + +constexpr float ChorusMaxDelay{0.016f}; +constexpr float FlangerMaxDelay{0.004f}; + +constexpr float EchoMaxDelay{0.207f}; +constexpr float EchoMaxLRDelay{0.404f}; + +enum class FShifterDirection { + Down, + Up, + Off +}; + +enum class ModulatorWaveform { + Sinusoid, + Sawtooth, + Square +}; + +enum class VMorpherPhenome { + A, E, I, O, U, + AA, AE, AH, AO, EH, ER, IH, IY, UH, UW, + B, D, F, G, J, K, L, M, N, P, R, S, T, V, Z +}; + +enum class VMorpherWaveform { + Sinusoid, + Triangle, + Sawtooth +}; + +union EffectProps { + struct { + // Shared Reverb Properties + float Density; + float Diffusion; + float Gain; + float GainHF; + float DecayTime; + float DecayHFRatio; + float ReflectionsGain; + float ReflectionsDelay; + float LateReverbGain; + float LateReverbDelay; + float AirAbsorptionGainHF; + float RoomRolloffFactor; + bool DecayHFLimit; + + // Additional EAX Reverb Properties + float GainLF; + float DecayLFRatio; + float ReflectionsPan[3]; + float LateReverbPan[3]; + float EchoTime; + float EchoDepth; + float ModulationTime; + float ModulationDepth; + float HFReference; + float LFReference; + } Reverb; + + struct { + float AttackTime; + float ReleaseTime; + float Resonance; + float PeakGain; + } Autowah; + + struct { + ChorusWaveform Waveform; + int Phase; + float Rate; + float Depth; + float Feedback; + float Delay; + } Chorus; /* Also Flanger */ + + struct { + bool OnOff; + } Compressor; + + struct { + float Edge; + float Gain; + float LowpassCutoff; + float EQCenter; + float EQBandwidth; + } Distortion; + + struct { + float Delay; + float LRDelay; + + float Damping; + float Feedback; + + float Spread; + } Echo; + + struct { + float LowCutoff; + float LowGain; + float Mid1Center; + float Mid1Gain; + float Mid1Width; + float Mid2Center; + float Mid2Gain; + float Mid2Width; + float HighCutoff; + float HighGain; + } Equalizer; + + struct { + float Frequency; + FShifterDirection LeftDirection; + FShifterDirection RightDirection; + } Fshifter; + + struct { + float Frequency; + float HighPassCutoff; + ModulatorWaveform Waveform; + } Modulator; + + struct { + int CoarseTune; + int FineTune; + } Pshifter; + + struct { + float Rate; + VMorpherPhenome PhonemeA; + VMorpherPhenome PhonemeB; + int PhonemeACoarseTuning; + int PhonemeBCoarseTuning; + VMorpherWaveform Waveform; + } Vmorpher; + + struct { + float Gain; + } Dedicated; +}; + + +struct EffectTarget { + MixParams *Main; + RealMixParams *RealOut; +}; + +struct EffectState : public al::intrusive_ref { + struct Buffer { + const BufferStorage *storage; + al::span samples; + }; + + al::span mOutTarget; + + + virtual ~EffectState() = default; + + virtual void deviceUpdate(const DeviceBase *device, const Buffer &buffer) = 0; + virtual void update(const ContextBase *context, const EffectSlot *slot, + const EffectProps *props, const EffectTarget target) = 0; + virtual void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) = 0; +}; + + +struct EffectStateFactory { + virtual ~EffectStateFactory() = default; + + virtual al::intrusive_ptr create() = 0; +}; + +#endif /* CORE_EFFECTS_BASE_H */ diff --git a/core/effectslot.cpp b/core/effectslot.cpp new file mode 100644 index 00000000..51fb8d46 --- /dev/null +++ b/core/effectslot.cpp @@ -0,0 +1,25 @@ + +#include "config.h" + +#include "effectslot.h" + +#include + +#include "almalloc.h" +#include "context.h" + + +EffectSlotArray *EffectSlot::CreatePtrArray(size_t count) noexcept +{ + /* Allocate space for twice as many pointers, so the mixer has scratch + * space to store a sorted list during mixing. + */ + void *ptr{al_calloc(alignof(EffectSlotArray), EffectSlotArray::Sizeof(count*2))}; + return al::construct_at(static_cast(ptr), count); +} + +EffectSlot::~EffectSlot() +{ + if(mWetBuffer) + mWetBuffer->mInUse = false; +} diff --git a/core/effectslot.h b/core/effectslot.h new file mode 100644 index 00000000..8b7b977c --- /dev/null +++ b/core/effectslot.h @@ -0,0 +1,88 @@ +#ifndef CORE_EFFECTSLOT_H +#define CORE_EFFECTSLOT_H + +#include + +#include "almalloc.h" +#include "device.h" +#include "effects/base.h" +#include "intrusive_ptr.h" + +struct EffectSlot; +struct WetBuffer; + +using EffectSlotArray = al::FlexArray; + + +enum class EffectSlotType : unsigned char { + None, + Reverb, + Chorus, + Distortion, + Echo, + Flanger, + FrequencyShifter, + VocalMorpher, + PitchShifter, + RingModulator, + Autowah, + Compressor, + Equalizer, + EAXReverb, + DedicatedLFE, + DedicatedDialog, + Convolution +}; + +struct EffectSlotProps { + float Gain; + bool AuxSendAuto; + EffectSlot *Target; + + EffectSlotType Type; + EffectProps Props; + + al::intrusive_ptr State; + + std::atomic next; + + DEF_NEWDEL(EffectSlotProps) +}; + + +struct EffectSlot { + std::atomic Update{nullptr}; + + /* Wet buffer configuration is ACN channel order with N3D scaling. + * Consequently, effects that only want to work with mono input can use + * channel 0 by itself. Effects that want multichannel can process the + * ambisonics signal and make a B-Format source pan. + */ + MixParams Wet; + + float Gain{1.0f}; + bool AuxSendAuto{true}; + EffectSlot *Target{nullptr}; + + EffectSlotType EffectType{EffectSlotType::None}; + EffectProps mEffectProps{}; + EffectState *mEffectState{nullptr}; + + float RoomRolloff{0.0f}; /* Added to the source's room rolloff, not multiplied. */ + float DecayTime{0.0f}; + float DecayLFRatio{0.0f}; + float DecayHFRatio{0.0f}; + bool DecayHFLimit{false}; + float AirAbsorptionGainHF{1.0f}; + + /* Mixing buffer used by the Wet mix. */ + WetBuffer *mWetBuffer{nullptr}; + + ~EffectSlot(); + + static EffectSlotArray *CreatePtrArray(size_t count) noexcept; + + DISABLE_ALLOC() +}; + +#endif /* CORE_EFFECTSLOT_H */ -- cgit v1.2.3 From 6bb9912513284ce5b6d6fbc7eb33b9bf38a647e8 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 25 Dec 2021 16:12:10 -0800 Subject: Fix non-runtime linking with pipewire --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 0704df58..ddc37e53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -919,8 +919,9 @@ if(PkgConfig_FOUND) option(ALSOFT_BACKEND_PIPEWIRE "Enable PipeWire backend" ON) if(ALSOFT_BACKEND_PIPEWIRE) set(HAVE_PIPEWIRE 1) - set(BACKENDS "${BACKENDS} PipeWire,") + set(BACKENDS "${BACKENDS} PipeWire${IS_LINKED},") set(ALC_OBJS ${ALC_OBJS} alc/backends/pipewire.cpp alc/backends/pipewire.h) + add_backend_libs(${PIPEWIRE_LIBRARIES}) set(INC_PATHS ${INC_PATHS} ${PIPEWIRE_INCLUDE_DIRS}) endif() endif() -- cgit v1.2.3 From 1bbea9cd3060ef65a2623f156b4f12ebf62c52fe Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 27 Jan 2022 01:38:39 -0800 Subject: Start and use a standard-like numbers header --- CMakeLists.txt | 1 + alc/effects/convolution.cpp | 6 ++++-- alc/effects/reverb.cpp | 17 ++++++++--------- common/alcomplex.cpp | 4 ++-- common/alnumbers.h | 32 ++++++++++++++++++++++++++++++++ core/mixer.cpp | 10 +++++----- 6 files changed, 52 insertions(+), 18 deletions(-) create mode 100644 common/alnumbers.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index ddc37e53..9e0662b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -626,6 +626,7 @@ set(COMMON_OBJS common/alfstream.h common/almalloc.cpp common/almalloc.h + common/alnumbers.h common/alnumeric.h common/aloptional.h common/alspan.h diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index dbbca143..10e3dd9d 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -20,6 +20,7 @@ #include "albyte.h" #include "alcomplex.h" #include "almalloc.h" +#include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" #include "base.h" @@ -464,9 +465,10 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot { auto ScaleAzimuthFront = [](float azimuth, float scale) -> float { + constexpr float half_pi{al::numbers::pi_v*0.5f}; const float abs_azi{std::fabs(azimuth)}; - if(!(abs_azi >= al::MathDefs::Pi()*0.5f)) - return std::copysign(minf(abs_azi*scale, al::MathDefs::Pi()*0.5f), azimuth); + if(!(abs_azi >= half_pi)) + return std::copysign(minf(abs_azi*scale, half_pi), azimuth); return azimuth; }; diff --git a/alc/effects/reverb.cpp b/alc/effects/reverb.cpp index 4fbfefd1..379cc1fb 100644 --- a/alc/effects/reverb.cpp +++ b/alc/effects/reverb.cpp @@ -30,6 +30,7 @@ #include "alc/effects/base.h" #include "almalloc.h" +#include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" #include "core/ambidefs.h" @@ -745,7 +746,7 @@ inline float CalcDensityGain(const float a) inline void CalcMatrixCoeffs(const float diffusion, float *x, float *y) { /* The matrix is of order 4, so n is sqrt(4 - 1). */ - constexpr float n{1.73205080756887719318f/*std::sqrt(3.0f)*/}; + constexpr float n{al::numbers::sqrt3_v}; const float t{diffusion * std::atan(n)}; /* Calculate the first mixing matrix coefficient. */ @@ -947,8 +948,6 @@ void ReverbState::updateDelayLine(const float earlyDelay, const float lateDelay, */ alu::Matrix GetTransformFromVector(const float *vec) { - constexpr float sqrt3{1.73205080756887719318f}; - /* Normalize the panning vector according to the N3D scale, which has an * extra sqrt(3) term on the directional components. Converting from OpenAL * to B-Format also requires negating X (ACN 1) and Z (ACN 3). Note however @@ -960,9 +959,9 @@ alu::Matrix GetTransformFromVector(const float *vec) float mag{std::sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2])}; if(mag > 1.0f) { - norm[0] = vec[0] / mag * -sqrt3; - norm[1] = vec[1] / mag * sqrt3; - norm[2] = vec[2] / mag * sqrt3; + norm[0] = vec[0] / mag * -al::numbers::sqrt3_v; + norm[1] = vec[1] / mag * al::numbers::sqrt3_v; + norm[2] = vec[2] / mag * al::numbers::sqrt3_v; mag = 1.0f; } else @@ -971,9 +970,9 @@ alu::Matrix GetTransformFromVector(const float *vec) * term. There's no need to renormalize the magnitude since it would * just be reapplied in the matrix. */ - norm[0] = vec[0] * -sqrt3; - norm[1] = vec[1] * sqrt3; - norm[2] = vec[2] * sqrt3; + norm[0] = vec[0] * -al::numbers::sqrt3_v; + norm[1] = vec[1] * al::numbers::sqrt3_v; + norm[2] = vec[2] * al::numbers::sqrt3_v; } return alu::Matrix{ diff --git a/common/alcomplex.cpp b/common/alcomplex.cpp index 5cb35f38..6d2b18c1 100644 --- a/common/alcomplex.cpp +++ b/common/alcomplex.cpp @@ -10,8 +10,8 @@ #include #include "albit.h" +#include "alnumbers.h" #include "alnumeric.h" -#include "math_defs.h" #include "opthelpers.h" @@ -123,7 +123,7 @@ void complex_fft(const al::span> buffer, const double sign) size_t step2{1u}; for(size_t i{0};i < log2_size;++i) { - const double arg{al::MathDefs::Pi() / static_cast(step2)}; + const double arg{al::numbers::pi / static_cast(step2)}; const std::complex w{std::cos(arg), std::sin(arg)*sign}; std::complex u{1.0, 0.0}; diff --git a/common/alnumbers.h b/common/alnumbers.h new file mode 100644 index 00000000..98994b44 --- /dev/null +++ b/common/alnumbers.h @@ -0,0 +1,32 @@ +#ifndef COMMON_ALNUMBERS_H +#define COMMON_ALNUMBERS_H + +#include + +namespace al { + +namespace numbers { + +namespace detail_ { + template + using as_fp = std::enable_if_t::value, T>; +} // detail_ + +template +static constexpr auto pi_v = detail_::as_fp(3.141592653589793238462643383279502884L); + +template +static constexpr auto inv_pi_v = detail_::as_fp(0.318309886183790671537767526745028724L); + +template +static constexpr auto sqrt3_v = detail_::as_fp(1.732050807568877293527446341505872367L); + +static constexpr auto pi = pi_v; +static constexpr auto inv_pi = inv_pi_v; +static constexpr auto sqrt3 = sqrt3_v; + +} // namespace numbers + +} // namespace al + +#endif /* COMMON_ALNUMBERS_H */ diff --git a/core/mixer.cpp b/core/mixer.cpp index 71e48fe3..4618406b 100644 --- a/core/mixer.cpp +++ b/core/mixer.cpp @@ -5,9 +5,9 @@ #include +#include "alnumbers.h" #include "devformat.h" #include "device.h" -#include "math_defs.h" #include "mixer/defs.h" struct CTag; @@ -24,9 +24,9 @@ std::array CalcAmbiCoeffs(const float y, const float z, c /* Zeroth-order */ coeffs[0] = 1.0f; /* ACN 0 = 1 */ /* First-order */ - coeffs[1] = 1.732050808f * y; /* ACN 1 = sqrt(3) * Y */ - coeffs[2] = 1.732050808f * z; /* ACN 2 = sqrt(3) * Z */ - coeffs[3] = 1.732050808f * x; /* ACN 3 = sqrt(3) * X */ + coeffs[1] = al::numbers::sqrt3_v * y; /* ACN 1 = sqrt(3) * Y */ + coeffs[2] = al::numbers::sqrt3_v * z; /* ACN 2 = sqrt(3) * Z */ + coeffs[3] = al::numbers::sqrt3_v * x; /* ACN 3 = sqrt(3) * X */ /* Second-order */ const float xx{x*x}, yy{y*y}, zz{z*z}, xy{x*y}, yz{y*z}, xz{x*z}; coeffs[4] = 3.872983346f * xy; /* ACN 4 = sqrt(15) * X * Y */ @@ -81,7 +81,7 @@ std::array CalcAmbiCoeffs(const float y, const float z, c */ const float ca{std::cos(spread * 0.5f)}; /* Increase the source volume by up to +3dB for a full spread. */ - const float scale{std::sqrt(1.0f + spread/al::MathDefs::Tau())}; + const float scale{std::sqrt(1.0f + al::numbers::inv_pi_v/2.0f*spread)}; const float ZH0_norm{scale}; const float ZH1_norm{scale * 0.5f * (ca+1.f)}; -- cgit v1.2.3 From 619249371a40f03cf988d1f5750d643df797c485 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 27 Jan 2022 04:04:41 -0800 Subject: Remove math_defs.h --- CMakeLists.txt | 1 - al/source.h | 3 +- alc/alu.cpp | 6 ++- alc/effects/convolution.cpp | 7 +++- common/math_defs.h | 7 ---- utils/uhjencoder.cpp | 95 +++++++++++++++++++++++---------------------- 6 files changed, 58 insertions(+), 61 deletions(-) delete mode 100644 common/math_defs.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e0662b1..93f2c7ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -637,7 +637,6 @@ set(COMMON_OBJS common/dynload.cpp common/dynload.h common/intrusive_ptr.h - common/math_defs.h common/opthelpers.h common/phase_shifter.h common/polyphase_resampler.cpp diff --git a/al/source.h b/al/source.h index 474d0a91..25ece822 100644 --- a/al/source.h +++ b/al/source.h @@ -19,7 +19,6 @@ #include "alnumeric.h" #include "atomic.h" #include "core/voice.h" -#include "math_defs.h" #include "vector.h" struct ALbuffer; @@ -79,7 +78,7 @@ struct ALsource { /* NOTE: Stereo pan angles are specified in radians, counter-clockwise * rather than clockwise. */ - std::array StereoPan{{Deg2Rad( 30.0f), Deg2Rad(-30.0f)}}; + std::array StereoPan{{al::numbers::pi_v/6.0f, -al::numbers::pi_v/6.0f}}; float Radius{0.0f}; float EnhWidth{0.593f}; diff --git a/alc/alu.cpp b/alc/alu.cpp index dbf4dedf..13e36d6a 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -72,7 +72,6 @@ #include "core/voice.h" #include "core/voice_change.h" #include "intrusive_ptr.h" -#include "math_defs.h" #include "opthelpers.h" #include "ringbuffer.h" #include "strutils.h" @@ -678,6 +677,9 @@ void AmbiRotator(std::array,MaxAmbiChannels> & /* End ambisonic rotation helpers. */ +constexpr float Deg2Rad(float x) noexcept +{ return static_cast(al::numbers::pi / 180.0 * x); } + struct GainTriplet { float Base, HF, LF; }; void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, const float zpos, @@ -685,7 +687,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con const al::span WetGain, EffectSlot *(&SendSlots)[MAX_SENDS], const VoiceProps *props, const ContextParams &Context, const DeviceBase *Device) { - static const ChanMap MonoMap[1]{ + static constexpr ChanMap MonoMap[1]{ { FrontCenter, 0.0f, 0.0f } }, RearMap[2]{ { BackLeft, Deg2Rad(-150.0f), Deg2Rad(0.0f) }, diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index 10e3dd9d..196238fc 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -35,7 +35,6 @@ #include "core/fmt_traits.h" #include "core/mixer.h" #include "intrusive_ptr.h" -#include "math_defs.h" #include "polyphase_resampler.h" #include "vector.h" @@ -121,6 +120,10 @@ struct ChanMap { float elevation; }; +constexpr float Deg2Rad(float x) noexcept +{ return static_cast(al::numbers::pi / 180.0 * x); } + + using complex_d = std::complex; constexpr size_t ConvolveUpdateSize{256}; @@ -346,7 +349,7 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot * to have its own output target since the main mixing buffer won't have an * LFE channel (due to being B-Format). */ - static const ChanMap MonoMap[1]{ + static constexpr ChanMap MonoMap[1]{ { FrontCenter, 0.0f, 0.0f } }, StereoMap[2]{ { FrontLeft, Deg2Rad(-45.0f), Deg2Rad(0.0f) }, diff --git a/common/math_defs.h b/common/math_defs.h deleted file mode 100644 index d66923ea..00000000 --- a/common/math_defs.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef AL_MATH_DEFS_H -#define AL_MATH_DEFS_H - -constexpr float Deg2Rad(float x) noexcept -{ return static_cast(x * 1.74532925199432955e-02/*pi/180*/); } - -#endif /* AL_MATH_DEFS_H */ diff --git a/utils/uhjencoder.cpp b/utils/uhjencoder.cpp index 700d1b17..2c99d2e1 100644 --- a/utils/uhjencoder.cpp +++ b/utils/uhjencoder.cpp @@ -34,8 +34,8 @@ #include #include "almalloc.h" +#include "alnumbers.h" #include "alspan.h" -#include "math_defs.h" #include "opthelpers.h" #include "phase_shifter.h" #include "vector.h" @@ -146,61 +146,61 @@ struct SpeakerPos { }; /* Azimuth is counter-clockwise. */ -const SpeakerPos StereoMap[2]{ - { SF_CHANNEL_MAP_LEFT, Deg2Rad( 30.0f), Deg2Rad(0.0f) }, - { SF_CHANNEL_MAP_RIGHT, Deg2Rad(-30.0f), Deg2Rad(0.0f) }, +constexpr SpeakerPos StereoMap[2]{ + { SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f }, + { SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f }, }, QuadMap[4]{ - { SF_CHANNEL_MAP_LEFT, Deg2Rad( 45.0f), Deg2Rad(0.0f) }, - { SF_CHANNEL_MAP_RIGHT, Deg2Rad( -45.0f), Deg2Rad(0.0f) }, - { SF_CHANNEL_MAP_REAR_LEFT, Deg2Rad( 135.0f), Deg2Rad(0.0f) }, - { SF_CHANNEL_MAP_REAR_RIGHT, Deg2Rad(-135.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_LEFT, 45.0f, 0.0f }, + { SF_CHANNEL_MAP_RIGHT, -45.0f, 0.0f }, + { SF_CHANNEL_MAP_REAR_LEFT, 135.0f, 0.0f }, + { SF_CHANNEL_MAP_REAR_RIGHT, -135.0f, 0.0f }, }, X51Map[6]{ - { SF_CHANNEL_MAP_LEFT, Deg2Rad( 30.0f), Deg2Rad(0.0f) }, - { SF_CHANNEL_MAP_RIGHT, Deg2Rad( -30.0f), Deg2Rad(0.0f) }, - { SF_CHANNEL_MAP_CENTER, Deg2Rad( 0.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f }, + { SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f }, + { SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f }, { SF_CHANNEL_MAP_LFE, 0.0f, 0.0f }, - { SF_CHANNEL_MAP_SIDE_LEFT, Deg2Rad( 110.0f), Deg2Rad(0.0f) }, - { SF_CHANNEL_MAP_SIDE_RIGHT, Deg2Rad(-110.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_SIDE_LEFT, 110.0f, 0.0f }, + { SF_CHANNEL_MAP_SIDE_RIGHT, -110.0f, 0.0f }, }, X51RearMap[6]{ - { SF_CHANNEL_MAP_LEFT, Deg2Rad( 30.0f), Deg2Rad(0.0f) }, - { SF_CHANNEL_MAP_RIGHT, Deg2Rad( -30.0f), Deg2Rad(0.0f) }, - { SF_CHANNEL_MAP_CENTER, Deg2Rad( 0.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f }, + { SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f }, + { SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f }, { SF_CHANNEL_MAP_LFE, 0.0f, 0.0f }, - { SF_CHANNEL_MAP_REAR_LEFT, Deg2Rad( 110.0f), Deg2Rad(0.0f) }, - { SF_CHANNEL_MAP_REAR_RIGHT, Deg2Rad(-110.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_REAR_LEFT, 110.0f, 0.0f }, + { SF_CHANNEL_MAP_REAR_RIGHT, -110.0f, 0.0f }, }, X71Map[8]{ - { SF_CHANNEL_MAP_LEFT, Deg2Rad( 30.0f), Deg2Rad(0.0f) }, - { SF_CHANNEL_MAP_RIGHT, Deg2Rad( -30.0f), Deg2Rad(0.0f) }, - { SF_CHANNEL_MAP_CENTER, Deg2Rad( 0.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f }, + { SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f }, + { SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f }, { SF_CHANNEL_MAP_LFE, 0.0f, 0.0f }, - { SF_CHANNEL_MAP_REAR_LEFT, Deg2Rad( 150.0f), Deg2Rad(0.0f) }, - { SF_CHANNEL_MAP_REAR_RIGHT, Deg2Rad(-150.0f), Deg2Rad(0.0f) }, - { SF_CHANNEL_MAP_SIDE_LEFT, Deg2Rad( 90.0f), Deg2Rad(0.0f) }, - { SF_CHANNEL_MAP_SIDE_RIGHT, Deg2Rad( -90.0f), Deg2Rad(0.0f) }, + { SF_CHANNEL_MAP_REAR_LEFT, 150.0f, 0.0f }, + { SF_CHANNEL_MAP_REAR_RIGHT, -150.0f, 0.0f }, + { SF_CHANNEL_MAP_SIDE_LEFT, 90.0f, 0.0f }, + { SF_CHANNEL_MAP_SIDE_RIGHT, -90.0f, 0.0f }, }, X714Map[12]{ - { SF_CHANNEL_MAP_LEFT, Deg2Rad( 30.0f), Deg2Rad( 0.0f) }, - { SF_CHANNEL_MAP_RIGHT, Deg2Rad( -30.0f), Deg2Rad( 0.0f) }, - { SF_CHANNEL_MAP_CENTER, Deg2Rad( 0.0f), Deg2Rad( 0.0f) }, + { SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f }, + { SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f }, + { SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f }, { SF_CHANNEL_MAP_LFE, 0.0f, 0.0f }, - { SF_CHANNEL_MAP_REAR_LEFT, Deg2Rad( 150.0f), Deg2Rad( 0.0f) }, - { SF_CHANNEL_MAP_REAR_RIGHT, Deg2Rad(-150.0f), Deg2Rad( 0.0f) }, - { SF_CHANNEL_MAP_SIDE_LEFT, Deg2Rad( 90.0f), Deg2Rad( 0.0f) }, - { SF_CHANNEL_MAP_SIDE_RIGHT, Deg2Rad( -90.0f), Deg2Rad( 0.0f) }, - { SF_CHANNEL_MAP_TOP_FRONT_LEFT, Deg2Rad( 45.0f), Deg2Rad(35.0f) }, - { SF_CHANNEL_MAP_TOP_FRONT_RIGHT, Deg2Rad( -45.0f), Deg2Rad(35.0f) }, - { SF_CHANNEL_MAP_TOP_REAR_LEFT, Deg2Rad( 135.0f), Deg2Rad(35.0f) }, - { SF_CHANNEL_MAP_TOP_REAR_RIGHT, Deg2Rad(-135.0f), Deg2Rad(35.0f) }, + { SF_CHANNEL_MAP_REAR_LEFT, 150.0f, 0.0f }, + { SF_CHANNEL_MAP_REAR_RIGHT, -150.0f, 0.0f }, + { SF_CHANNEL_MAP_SIDE_LEFT, 90.0f, 0.0f }, + { SF_CHANNEL_MAP_SIDE_RIGHT, -90.0f, 0.0f }, + { SF_CHANNEL_MAP_TOP_FRONT_LEFT, 45.0f, 35.0f }, + { SF_CHANNEL_MAP_TOP_FRONT_RIGHT, -45.0f, 35.0f }, + { SF_CHANNEL_MAP_TOP_REAR_LEFT, 135.0f, 35.0f }, + { SF_CHANNEL_MAP_TOP_REAR_RIGHT, -135.0f, 35.0f }, }; -inline std::array GenCoeffs(float x /*+front*/, float y /*+left*/, float z /*+up*/) +constexpr auto GenCoeffs(double x /*+front*/, double y /*+left*/, double z /*+up*/) noexcept { /* Coefficients are +3dB of FuMa. */ - std::array coeffs; - coeffs[0] = 1.0f; - coeffs[1] = 1.41421356237f * x; - coeffs[2] = 1.41421356237f * y; - coeffs[3] = 1.41421356237f * z; - return coeffs; + return std::array{{ + 1.0f, + static_cast(al::numbers::sqrt2 * x), + static_cast(al::numbers::sqrt2 * y), + static_cast(al::numbers::sqrt2 * z) + }}; } } // namespace @@ -382,7 +382,7 @@ int main(int argc, char **argv) /* B-Format is already in the correct order. It just needs a * +3dB boost. */ - constexpr float scale{1.41421356237f}; + constexpr float scale{al::numbers::sqrt2_v}; const size_t chans{std::min(static_cast(ininfo.channels), 4u)}; for(size_t c{0};c < chans;++c) { @@ -404,10 +404,11 @@ int main(int argc, char **argv) srcmem[i] = inmem[i * static_cast(ininfo.channels)]; ++inmem; + constexpr auto Deg2Rad = al::numbers::pi / 180.0; const auto coeffs = GenCoeffs( - std::cos(spkr.mAzimuth) * std::cos(spkr.mElevation), - std::sin(spkr.mAzimuth) * std::cos(spkr.mElevation), - std::sin(spkr.mElevation)); + std::cos(spkr.mAzimuth*Deg2Rad) * std::cos(spkr.mElevation*Deg2Rad), + std::sin(spkr.mAzimuth*Deg2Rad) * std::cos(spkr.mElevation*Deg2Rad), + std::sin(spkr.mElevation*Deg2Rad)); for(size_t c{0};c < 4;++c) { for(size_t i{0};i < got;++i) -- cgit v1.2.3 From 19ed994dc30ed84ea7cbbb5152577669fc25caf6 Mon Sep 17 00:00:00 2001 From: "Boris I. Bendovsky" Date: Sun, 30 Jan 2022 14:47:32 +0200 Subject: Add EAX extensions (EAX 2.0-5.0, X-RAM) (#632) * Add EAX extensions (EAX 2.0-5.0, X-RAM) * Comment out C++17 leftovers * Remove everything related to patching * Update alsoftrc.sample * Rewrite integration * Fix GCC compilation under Linux * Always reset EAX effect properties when loading it into FX slot --- CMakeLists.txt | 30 + al/auxeffectslot.cpp | 834 +++++++++++++++ al/auxeffectslot.h | 221 ++++ al/buffer.cpp | 292 ++++++ al/buffer.h | 9 + al/eax_api.cpp | 1151 +++++++++++++++++++++ al/eax_api.h | 1542 ++++++++++++++++++++++++++++ al/eax_eax_call.cpp | 370 +++++++ al/eax_eax_call.h | 128 +++ al/eax_effect.cpp | 1 + al/eax_effect.h | 28 + al/eax_exception.cpp | 61 ++ al/eax_exception.h | 25 + al/eax_fx_slot_index.cpp | 164 +++ al/eax_fx_slot_index.h | 69 ++ al/eax_fx_slots.cpp | 81 ++ al/eax_fx_slots.h | 46 + al/eax_globals.cpp | 18 + al/eax_globals.h | 22 + al/eax_utils.cpp | 35 + al/eax_utils.h | 164 +++ al/eax_x_ram.cpp | 1 + al/eax_x_ram.h | 38 + al/effect.cpp | 143 +++ al/effect.h | 53 + al/effects/autowah.cpp | 471 +++++++++ al/effects/chorus.cpp | 1240 ++++++++++++++++++++++ al/effects/compressor.cpp | 272 +++++ al/effects/distortion.cpp | 536 ++++++++++ al/effects/echo.cpp | 534 ++++++++++ al/effects/effects.cpp | 106 ++ al/effects/effects.h | 11 + al/effects/equalizer.cpp | 866 ++++++++++++++++ al/effects/fshifter.cpp | 414 ++++++++ al/effects/modulator.cpp | 411 ++++++++ al/effects/null.cpp | 57 ++ al/effects/pshifter.cpp | 338 ++++++ al/effects/reverb.cpp | 1922 ++++++++++++++++++++++++++++++++++ al/effects/vmorpher.cpp | 624 ++++++++++++ al/extension.cpp | 117 +++ al/filter.cpp | 81 ++ al/filter.h | 40 + al/listener.cpp | 22 + al/listener.h | 7 + al/source.cpp | 2488 +++++++++++++++++++++++++++++++++++++++++++++ al/source.h | 689 +++++++++++++ al/state.cpp | 42 + alc/alc.cpp | 44 + alc/context.cpp | 1228 ++++++++++++++++++++++ alc/context.h | 386 +++++++ alc/device.h | 8 + alsoftrc.sample | 8 + common/alnumeric.h | 45 + 53 files changed, 18533 insertions(+) create mode 100644 al/eax_api.cpp create mode 100644 al/eax_api.h create mode 100644 al/eax_eax_call.cpp create mode 100644 al/eax_eax_call.h create mode 100644 al/eax_effect.cpp create mode 100644 al/eax_effect.h create mode 100644 al/eax_exception.cpp create mode 100644 al/eax_exception.h create mode 100644 al/eax_fx_slot_index.cpp create mode 100644 al/eax_fx_slot_index.h create mode 100644 al/eax_fx_slots.cpp create mode 100644 al/eax_fx_slots.h create mode 100644 al/eax_globals.cpp create mode 100644 al/eax_globals.h create mode 100644 al/eax_utils.cpp create mode 100644 al/eax_utils.h create mode 100644 al/eax_x_ram.cpp create mode 100644 al/eax_x_ram.h create mode 100644 al/effects/effects.cpp (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 93f2c7ec..9480910b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,8 @@ option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...) option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" ON) option(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON) +option(ALSOFT_EAX "Enable EAX extensions." ON) + if(DEFINED SHARE_INSTALL_DIR) message(WARNING "SHARE_INSTALL_DIR is deprecated. Use the variables provided by the GNUInstallDirs module instead") set(CMAKE_INSTALL_DATADIR "${SHARE_INSTALL_DIR}") @@ -764,6 +766,7 @@ set(OPENAL_OBJS al/effects/dedicated.cpp al/effects/distortion.cpp al/effects/echo.cpp + al/effects/effects.cpp al/effects/effects.h al/effects/equalizer.cpp al/effects/fshifter.cpp @@ -813,6 +816,30 @@ set(ALC_OBJS alc/inprogext.h alc/panning.cpp) +if (ALSOFT_EAX) + set(OPENAL_OBJS + ${OPENAL_OBJS} + al/eax_api.cpp + al/eax_api.h + al/eax_eax_call.cpp + al/eax_eax_call.h + al/eax_effect.cpp + al/eax_effect.h + al/eax_exception.cpp + al/eax_exception.h + al/eax_fx_slot_index.cpp + al/eax_fx_slot_index.h + al/eax_fx_slots.cpp + al/eax_fx_slots.h + al/eax_globals.cpp + al/eax_globals.h + al/eax_utils.cpp + al/eax_utils.h + al/eax_x_ram.cpp + al/eax_x_ram.h +) +endif () + # Include SIMD mixers set(CPU_EXTS "Default") if(HAVE_SSE) @@ -1289,6 +1316,7 @@ target_include_directories(common PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_D target_compile_definitions(common PRIVATE ${CPP_DEFS}) target_compile_options(common PRIVATE ${C_FLAGS}) set_target_properties(common PROPERTIES POSITION_INDEPENDENT_CODE TRUE) +target_compile_definitions(common PRIVATE "ALSOFT_EAX=$") unset(HAS_ROUTER) @@ -1299,6 +1327,7 @@ if(LIBTYPE STREQUAL "STATIC") add_library(${IMPL_TARGET} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS} ${CORE_OBJS}) target_compile_definitions(${IMPL_TARGET} PUBLIC AL_LIBTYPE_STATIC) target_link_libraries(${IMPL_TARGET} PRIVATE ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) + if(WIN32) # This option is for static linking OpenAL Soft into another project # that already defines the IDs. It is up to that project to ensure all @@ -1409,6 +1438,7 @@ set_target_properties(${IMPL_TARGET} PROPERTIES OUTPUT_NAME ${LIBNAME} target_compile_definitions(${IMPL_TARGET} PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}" ${CPP_DEFS}) +target_compile_definitions(${IMPL_TARGET} PRIVATE "ALSOFT_EAX=$") target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS}) if(TARGET build_version) diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp index 16e4cbea..40949aa3 100644 --- a/al/auxeffectslot.cpp +++ b/al/auxeffectslot.cpp @@ -50,6 +50,10 @@ #include "effect.h" #include "opthelpers.h" +#if ALSOFT_EAX +#include "eax_exception.h" +#include "eax_utils.h" +#endif // ALSOFT_EAX namespace { @@ -1042,3 +1046,833 @@ EffectSlotSubList::~EffectSlotSubList() al_free(EffectSlots); EffectSlots = nullptr; } + +#if ALSOFT_EAX +namespace +{ + + +class EaxFxSlotException : + public EaxException +{ +public: + explicit EaxFxSlotException( + const char* message) + : + EaxException{"EAX_FX_SLOT", message} + { + } +}; // EaxFxSlotException + + +} // namespace + + +void ALeffectslot::eax_initialize( + ALCcontext& al_context, + EaxFxSlotIndexValue index) +{ + eax_al_context_ = &al_context; + + if (index >= EAX_MAX_FXSLOTS) + { + eax_fail("Index out of range."); + } + + eax_fx_slot_index_ = index; + + eax_initialize_eax(); + eax_initialize_effects(); + eax_set_default_slots_defaults(); +} + +void ALeffectslot::eax_uninitialize() noexcept +{ + eax_al_effect_ = nullptr; +} + +const EAX50FXSLOTPROPERTIES& ALeffectslot::eax_get_eax_fx_slot() const noexcept +{ + return eax_eax_fx_slot_; +} + +void ALeffectslot::eax_validate_fx_slot_effect( + const GUID& eax_effect_id) +{ + if (eax_effect_id != EAX_NULL_GUID && + eax_effect_id != EAX_REVERB_EFFECT) + { + eax_fail("Unsupported EAX effect GUID."); + } +} + +void ALeffectslot::eax_validate_fx_slot_volume( + long eax_volume) +{ + eax_validate_range( + "Volume", + eax_volume, + EAXFXSLOT_MINVOLUME, + EAXFXSLOT_MAXVOLUME); +} + +void ALeffectslot::eax_validate_fx_slot_lock( + long eax_lock) +{ + eax_validate_range( + "Lock", + eax_lock, + EAXFXSLOT_MINLOCK, + EAXFXSLOT_MAXLOCK); +} + +void ALeffectslot::eax_validate_fx_slot_lock_state( + long eax_lock, + const GUID& eax_effect_id) +{ + if (eax_lock == EAXFXSLOT_LOCKED && eax_effect_id != eax_eax_fx_slot_.guidLoadEffect) + { + eax_fail("Loading an effect in a locked slot."); + } +} + +void ALeffectslot::eax_validate_fx_slot_flags( + unsigned long eax_flags, + int eax_version) +{ + eax_validate_range( + "Flags", + eax_flags, + 0UL, + ~(eax_version == 4 ? EAX40FXSLOTFLAGS_RESERVED : EAX50FXSLOTFLAGS_RESERVED)); +} + +void ALeffectslot::eax_validate_fx_slot_occlusion( + long eax_occlusion) +{ + eax_validate_range( + "Occlusion", + eax_occlusion, + EAXFXSLOT_MINOCCLUSION, + EAXFXSLOT_MAXOCCLUSION); +} + +void ALeffectslot::eax_validate_fx_slot_occlusion_lf_ratio( + float eax_occlusion_lf_ratio) +{ + eax_validate_range( + "Occlusion LF Ratio", + eax_occlusion_lf_ratio, + EAXFXSLOT_MINOCCLUSIONLFRATIO, + EAXFXSLOT_MAXOCCLUSIONLFRATIO); +} + +void ALeffectslot::eax_validate_fx_slot_all( + const EAX40FXSLOTPROPERTIES& fx_slot, + int eax_version) +{ + eax_validate_fx_slot_effect(fx_slot.guidLoadEffect); + eax_validate_fx_slot_volume(fx_slot.lVolume); + eax_validate_fx_slot_lock(fx_slot.lLock); + eax_validate_fx_slot_flags(fx_slot.ulFlags, eax_version); +} + +void ALeffectslot::eax_validate_fx_slot_all( + const EAX50FXSLOTPROPERTIES& fx_slot, + int eax_version) +{ + eax_validate_fx_slot_all(static_cast(fx_slot), eax_version); + + eax_validate_fx_slot_occlusion(fx_slot.lOcclusion); + eax_validate_fx_slot_occlusion_lf_ratio(fx_slot.flOcclusionLFRatio); +} + +void ALeffectslot::eax_set_fx_slot_effect( + const GUID& eax_effect_id) +{ + if (eax_eax_fx_slot_.guidLoadEffect == eax_effect_id) + { + return; + } + + eax_eax_fx_slot_.guidLoadEffect = eax_effect_id; + + eax_set_fx_slot_effect(); +} + +void ALeffectslot::eax_set_fx_slot_volume( + long eax_volume) +{ + if (eax_eax_fx_slot_.lVolume == eax_volume) + { + return; + } + + eax_eax_fx_slot_.lVolume = eax_volume; + + eax_set_fx_slot_volume(); +} + +void ALeffectslot::eax_set_fx_slot_lock( + long eax_lock) +{ + if (eax_eax_fx_slot_.lLock == eax_lock) + { + return; + } + + eax_eax_fx_slot_.lLock = eax_lock; +} + +void ALeffectslot::eax_set_fx_slot_flags( + unsigned long eax_flags) +{ + if (eax_eax_fx_slot_.ulFlags == eax_flags) + { + return; + } + + eax_eax_fx_slot_.ulFlags = eax_flags; + + eax_set_fx_slot_flags(); +} + +// [[nodiscard]] +bool ALeffectslot::eax_set_fx_slot_occlusion( + long eax_occlusion) +{ + if (eax_eax_fx_slot_.lOcclusion == eax_occlusion) + { + return false; + } + + eax_eax_fx_slot_.lOcclusion = eax_occlusion; + + return true; +} + +// [[nodiscard]] +bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio( + float eax_occlusion_lf_ratio) +{ + if (eax_eax_fx_slot_.flOcclusionLFRatio == eax_occlusion_lf_ratio) + { + return false; + } + + eax_eax_fx_slot_.flOcclusionLFRatio = eax_occlusion_lf_ratio; + + return true; +} + +void ALeffectslot::eax_set_fx_slot_all( + const EAX40FXSLOTPROPERTIES& eax_fx_slot) +{ + eax_set_fx_slot_effect(eax_fx_slot.guidLoadEffect); + eax_set_fx_slot_volume(eax_fx_slot.lVolume); + eax_set_fx_slot_lock(eax_fx_slot.lLock); + eax_set_fx_slot_flags(eax_fx_slot.ulFlags); +} + +// [[nodiscard]] +bool ALeffectslot::eax_set_fx_slot_all( + const EAX50FXSLOTPROPERTIES& eax_fx_slot) +{ + eax_set_fx_slot_all(static_cast(eax_fx_slot)); + + const auto is_occlusion_modified = eax_set_fx_slot_occlusion(eax_fx_slot.lOcclusion); + const auto is_occlusion_lf_ratio_modified = eax_set_fx_slot_occlusion_lf_ratio(eax_fx_slot.flOcclusionLFRatio); + + return is_occlusion_modified || is_occlusion_lf_ratio_modified; +} + +// [[nodiscard]] +bool ALeffectslot::eax_dispatch( + const EaxEaxCall& eax_call) +{ + return eax_call.is_get() ? eax_get(eax_call) : eax_set(eax_call); +} + +[[noreturn]] +void ALeffectslot::eax_fail( + const char* message) +{ + throw EaxFxSlotException{message}; +} + +void ALeffectslot::eax_set_eax_fx_slot_defaults() +{ + eax_eax_fx_slot_.guidLoadEffect = EAX_NULL_GUID; + eax_eax_fx_slot_.lVolume = EAXFXSLOT_DEFAULTVOLUME; + eax_eax_fx_slot_.lLock = EAXFXSLOT_UNLOCKED; + eax_eax_fx_slot_.ulFlags = EAX50FXSLOT_DEFAULTFLAGS; + eax_eax_fx_slot_.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION; + eax_eax_fx_slot_.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO; +} + +void ALeffectslot::eax_initialize_eax() +{ + eax_set_eax_fx_slot_defaults(); +} + +void ALeffectslot::eax_initialize_effects() +{ + eax_set_fx_slot_effect(); +} + +void ALeffectslot::eax_set_default_slot_0_defaults() +{ + eax_set_fx_slot_effect(EAX_REVERB_EFFECT); +} + +void ALeffectslot::eax_set_default_slot_1_defaults() +{ + eax_set_fx_slot_effect(EAX_CHORUS_EFFECT); +} + +void ALeffectslot::eax_set_default_slots_defaults() +{ + switch (eax_fx_slot_index_) + { + case 0: + eax_set_default_slot_0_defaults(); + break; + + case 1: + eax_set_default_slot_1_defaults(); + break; + + case 2: + case 3: + break; + + default: + eax_fail("FX slot index out of range."); + } +} + +void ALeffectslot::eax_get_fx_slot_all( + const EaxEaxCall& eax_call) const +{ + switch (eax_call.get_version()) + { + case 4: + eax_call.set_value(eax_eax_fx_slot_); + break; + + case 5: + eax_call.set_value(eax_eax_fx_slot_); + break; + + default: + eax_fail("Unsupported EAX version."); + } +} + +void ALeffectslot::eax_get_fx_slot( + const EaxEaxCall& eax_call) const +{ + switch (eax_call.get_property_id()) + { + case EAXFXSLOT_ALLPARAMETERS: + eax_get_fx_slot_all(eax_call); + break; + + case EAXFXSLOT_LOADEFFECT: + eax_call.set_value(eax_eax_fx_slot_.guidLoadEffect); + break; + + case EAXFXSLOT_VOLUME: + eax_call.set_value(eax_eax_fx_slot_.lVolume); + break; + + case EAXFXSLOT_LOCK: + eax_call.set_value(eax_eax_fx_slot_.lLock); + break; + + case EAXFXSLOT_FLAGS: + eax_call.set_value(eax_eax_fx_slot_.ulFlags); + break; + + case EAXFXSLOT_OCCLUSION: + eax_call.set_value(eax_eax_fx_slot_.lOcclusion); + break; + + case EAXFXSLOT_OCCLUSIONLFRATIO: + eax_call.set_value(eax_eax_fx_slot_.flOcclusionLFRatio); + break; + + default: + eax_fail("Unsupported FX slot property id."); + } +} + +// [[nodiscard]] +bool ALeffectslot::eax_get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_set_id()) + { + case EaxEaxCallPropertySetId::fx_slot: + eax_get_fx_slot(eax_call); + break; + + case EaxEaxCallPropertySetId::fx_slot_effect: + eax_dispatch_effect(eax_call); + break; + + default: + eax_fail("Unsupported property id."); + } + + return false; +} + +void ALeffectslot::eax_set_fx_slot_effect( + ALenum al_effect_type) +{ + if (!eax_al_effect_) + { + eax_al_effect_ = eax_create_al_effect(*eax_al_context_, al_effect_type); + } + else + { + auto& device = eax_al_context_->mALDevice; + std::lock_guard effect_lock{device->EffectLock}; + + eax_al_effect_->eax_al_set_effect(al_effect_type); + } + + eax_al_effect_->eax_initialize(); + + eax_set_effect_slot_effect(*eax_al_effect_); +} + +void ALeffectslot::eax_set_fx_slot_effect() +{ + auto al_effect_type = ALenum{}; + + if (false) + { + } + else if (eax_eax_fx_slot_.guidLoadEffect == EAX_NULL_GUID) + { + al_effect_type = AL_EFFECT_NULL; + } + else if (eax_eax_fx_slot_.guidLoadEffect == EAX_AUTOWAH_EFFECT) + { + al_effect_type = AL_EFFECT_AUTOWAH; + } + else if (eax_eax_fx_slot_.guidLoadEffect == EAX_CHORUS_EFFECT) + { + al_effect_type = AL_EFFECT_CHORUS; + } + else if (eax_eax_fx_slot_.guidLoadEffect == EAX_AGCCOMPRESSOR_EFFECT) + { + al_effect_type = AL_EFFECT_COMPRESSOR; + } + else if (eax_eax_fx_slot_.guidLoadEffect == EAX_DISTORTION_EFFECT) + { + al_effect_type = AL_EFFECT_DISTORTION; + } + else if (eax_eax_fx_slot_.guidLoadEffect == EAX_REVERB_EFFECT) + { + al_effect_type = AL_EFFECT_EAXREVERB; + } + else if (eax_eax_fx_slot_.guidLoadEffect == EAX_ECHO_EFFECT) + { + al_effect_type = AL_EFFECT_ECHO; + } + else if (eax_eax_fx_slot_.guidLoadEffect == EAX_EQUALIZER_EFFECT) + { + al_effect_type = AL_EFFECT_EQUALIZER; + } + else if (eax_eax_fx_slot_.guidLoadEffect == EAX_FLANGER_EFFECT) + { + al_effect_type = AL_EFFECT_FLANGER; + } + else if (eax_eax_fx_slot_.guidLoadEffect == EAX_FREQUENCYSHIFTER_EFFECT) + { + al_effect_type = AL_EFFECT_FREQUENCY_SHIFTER; + } + else if (eax_eax_fx_slot_.guidLoadEffect == EAX_PITCHSHIFTER_EFFECT) + { + al_effect_type = AL_EFFECT_PITCH_SHIFTER; + } + else if (eax_eax_fx_slot_.guidLoadEffect == EAX_RINGMODULATOR_EFFECT) + { + al_effect_type = AL_EFFECT_RING_MODULATOR; + } + else if (eax_eax_fx_slot_.guidLoadEffect == EAX_VOCALMORPHER_EFFECT) + { + al_effect_type = AL_EFFECT_VOCAL_MORPHER; + } + else + { + eax_fail("Unsupported effect."); + } + + eax_set_fx_slot_effect(al_effect_type); +} + +void ALeffectslot::eax_set_efx_effect_slot_gain() +{ + const auto gain = level_mb_to_gain( + static_cast(clamp( + eax_eax_fx_slot_.lVolume, + EAXFXSLOT_MINVOLUME, + EAXFXSLOT_MAXVOLUME))); + + eax_set_effect_slot_gain(gain); +} + +void ALeffectslot::eax_set_fx_slot_volume() +{ + eax_set_efx_effect_slot_gain(); +} + +void ALeffectslot::eax_set_effect_slot_send_auto() +{ + eax_set_effect_slot_send_auto((eax_eax_fx_slot_.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0); +} + +void ALeffectslot::eax_set_fx_slot_flags() +{ + eax_set_effect_slot_send_auto(); +} + +void ALeffectslot::eax_set_fx_slot_effect( + const EaxEaxCall& eax_call) +{ + const auto& eax_effect_id = + eax_call.get_value(); + + eax_validate_fx_slot_effect(eax_effect_id); + eax_validate_fx_slot_lock_state(eax_eax_fx_slot_.lLock, eax_effect_id); + + eax_set_fx_slot_effect(eax_effect_id); +} + +void ALeffectslot::eax_set_fx_slot_volume( + const EaxEaxCall& eax_call) +{ + const auto& eax_volume = + eax_call.get_value(); + + eax_validate_fx_slot_volume(eax_volume); + eax_set_fx_slot_volume(eax_volume); +} + +void ALeffectslot::eax_set_fx_slot_lock( + const EaxEaxCall& eax_call) +{ + const auto& eax_lock = + eax_call.get_value(); + + eax_validate_fx_slot_lock(eax_lock); + eax_set_fx_slot_lock(eax_lock); +} + +void ALeffectslot::eax_set_fx_slot_flags( + const EaxEaxCall& eax_call) +{ + const auto& eax_flags = + eax_call.get_value(); + + eax_validate_fx_slot_flags(eax_flags, eax_call.get_version()); + eax_set_fx_slot_flags(eax_flags); +} + +// [[nodiscard]] +bool ALeffectslot::eax_set_fx_slot_occlusion( + const EaxEaxCall& eax_call) +{ + const auto& eax_occlusion = + eax_call.get_value(); + + eax_validate_fx_slot_occlusion(eax_occlusion); + + return eax_set_fx_slot_occlusion(eax_occlusion); +} + +// [[nodiscard]] +bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio( + const EaxEaxCall& eax_call) +{ + const auto& eax_occlusion_lf_ratio = + eax_call.get_value(); + + eax_validate_fx_slot_occlusion_lf_ratio(eax_occlusion_lf_ratio); + + return eax_set_fx_slot_occlusion_lf_ratio(eax_occlusion_lf_ratio); +} + +// [[nodiscard]] +bool ALeffectslot::eax_set_fx_slot_all( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_version()) + { + case 4: + { + const auto& eax_all = + eax_call.get_value(); + + eax_validate_fx_slot_all(eax_all, eax_call.get_version()); + eax_set_fx_slot_all(eax_all); + + return false; + } + + case 5: + { + const auto& eax_all = + eax_call.get_value(); + + eax_validate_fx_slot_all(eax_all, eax_call.get_version()); + return eax_set_fx_slot_all(eax_all); + } + + default: + eax_fail("Unsupported EAX version."); + } +} + +bool ALeffectslot::eax_set_fx_slot( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXFXSLOT_NONE: + return false; + + case EAXFXSLOT_ALLPARAMETERS: + return eax_set_fx_slot_all(eax_call); + + case EAXFXSLOT_LOADEFFECT: + eax_set_fx_slot_effect(eax_call); + return false; + + case EAXFXSLOT_VOLUME: + eax_set_fx_slot_volume(eax_call); + return false; + + case EAXFXSLOT_LOCK: + eax_set_fx_slot_lock(eax_call); + return false; + + case EAXFXSLOT_FLAGS: + eax_set_fx_slot_flags(eax_call); + return false; + + case EAXFXSLOT_OCCLUSION: + return eax_set_fx_slot_occlusion(eax_call); + + case EAXFXSLOT_OCCLUSIONLFRATIO: + return eax_set_fx_slot_occlusion_lf_ratio(eax_call); + + + default: + eax_fail("Unsupported FX slot property id."); + } +} + +// [[nodiscard]] +bool ALeffectslot::eax_set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_set_id()) + { + case EaxEaxCallPropertySetId::fx_slot: + return eax_set_fx_slot(eax_call); + + case EaxEaxCallPropertySetId::fx_slot_effect: + eax_dispatch_effect(eax_call); + return false; + + default: + eax_fail("Unsupported property id."); + } +} + +void ALeffectslot::eax_dispatch_effect( + const EaxEaxCall& eax_call) +{ + auto is_changed = false; + + { + std::lock_guard effect_lock{eax_al_context_->mALDevice->EffectLock}; + + if (!eax_al_effect_->eax_effect) + { + return; + } + + is_changed = eax_al_effect_->eax_effect->dispatch(eax_call); + } + + if (is_changed) + { + eax_set_effect_slot_effect(*eax_al_effect_); + } +} + +void ALeffectslot::eax_set_effect_slot_effect( + ALeffect& effect) +{ +#define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] " + + auto& device = *eax_al_context_->mALDevice; + + std::lock_guard effect_slot_lock{eax_al_context_->mEffectSlotLock}; + std::lock_guard effect_lock{device.EffectLock}; + + const auto error = initEffect(&effect, eax_al_context_); + + if (error != AL_NO_ERROR) + { + ERR(EAX_PREFIX "%s\n", "Failed to initialize an effect."); + return; + } + + if (mState == SlotState::Initial) + { + mPropsDirty.test_and_clear(std::memory_order_acq_rel); + updateProps(eax_al_context_); + + auto effect_slot_ptr = this; + + AddActiveEffectSlots({&effect_slot_ptr, 1}, eax_al_context_); + mState = SlotState::Playing; + + return; + } + + { + auto context = EaxAlContextWrapper{*eax_al_context_}; + auto slot = this; + + DO_UPDATEPROPS(); + } + +#undef EAX_PREFIX +} + +void ALeffectslot::eax_set_effect_slot_send_auto( + bool is_send_auto) +{ + std::lock_guard effect_slot_lock{eax_al_context_->mEffectSlotLock}; + + const auto is_changed = (AuxSendAuto != is_send_auto); + + AuxSendAuto = is_send_auto; + + if (is_changed) + { + auto context = EaxAlContextWrapper{*eax_al_context_}; + auto slot = this; + + DO_UPDATEPROPS(); + } +} + +void ALeffectslot::eax_set_effect_slot_gain( + ALfloat gain) +{ +#define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_GAIN] " + + if (gain < 0.0F || gain > 1.0F) + { + ERR(EAX_PREFIX "%s\n", "Gain out of range."); + return; + } + + std::lock_guard effect_slot_lock{eax_al_context_->mEffectSlotLock}; + + const auto is_changed = (Gain != gain); + + Gain = gain; + + if (is_changed) + { + auto context = EaxAlContextWrapper{*eax_al_context_}; + auto slot = this; + + DO_UPDATEPROPS(); + } + +#undef EAX_PREFIX +} + + +EaxAlEffectSlotDeleter::EaxAlEffectSlotDeleter( + ALCcontext& context) noexcept + : + context_{&context} +{ +} + +void EaxAlEffectSlotDeleter::operator()( + ALeffectslot* effect_slot) +{ + assert(effect_slot); + eax_delete_al_effect_slot(*context_, *effect_slot); +} + + +EaxAlEffectSlotUPtr eax_create_al_effect_slot( + ALCcontext& context) +{ +#define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] " + + std::unique_lock effect_slot_lock{context.mEffectSlotLock}; + + auto& device = *context.mALDevice; + + if (context.mNumEffectSlots == device.AuxiliaryEffectSlotMax) + { + ERR(EAX_PREFIX "%s\n", "Out of memory."); + return nullptr; + } + + if (!EnsureEffectSlots(&context, 1)) + { + ERR(EAX_PREFIX "%s\n", "Failed to ensure."); + return nullptr; + } + + auto effect_slot = EaxAlEffectSlotUPtr{AllocEffectSlot(&context), EaxAlEffectSlotDeleter{context}}; + + if (!effect_slot) + { + ERR(EAX_PREFIX "%s\n", "Failed to allocate."); + return nullptr; + } + + return effect_slot; + +#undef EAX_PREFIX +} + +void eax_delete_al_effect_slot( + ALCcontext& context, + ALeffectslot& effect_slot) +{ +#define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] " + + std::lock_guard effect_slot_lock{context.mEffectSlotLock}; + + if (ReadRef(effect_slot.ref) != 0) + { + ERR(EAX_PREFIX "Deleting in-use effect slot %u.\n", effect_slot.id); + return; + } + + auto effect_slot_ptr = &effect_slot; + + RemoveActiveEffectSlots({&effect_slot_ptr, 1}, &context); + FreeEffectSlot(&context, &effect_slot); + +#undef EAX_PREFIX +} +#endif // ALSOFT_EAX diff --git a/al/auxeffectslot.h b/al/auxeffectslot.h index 4de1df7a..54f1987d 100644 --- a/al/auxeffectslot.h +++ b/al/auxeffectslot.h @@ -16,6 +16,15 @@ #include "intrusive_ptr.h" #include "vector.h" +#if ALSOFT_EAX +#include + +#include "al/effect.h" + +#include "eax_eax_call.h" +#include "eax_fx_slot_index.h" +#endif // ALSOFT_EAX + struct ALbuffer; struct ALeffect; struct WetBuffer; @@ -61,8 +70,220 @@ struct ALeffectslot { /* This can be new'd for the context's default effect slot. */ DEF_NEWDEL(ALeffectslot) + + +#if ALSOFT_EAX +public: + void eax_initialize( + ALCcontext& al_context, + EaxFxSlotIndexValue index); + + void eax_uninitialize() noexcept; + + + const EAX50FXSLOTPROPERTIES& eax_get_eax_fx_slot() const noexcept; + + + // [[nodiscard]] + bool eax_dispatch( + const EaxEaxCall& eax_call); + + +private: + ALCcontext* eax_al_context_{}; + + EaxFxSlotIndexValue eax_fx_slot_index_{}; + + EAX50FXSLOTPROPERTIES eax_eax_fx_slot_{}; + + EaxAlEffectUPtr eax_al_effect_{}; + + + [[noreturn]] + static void eax_fail( + const char* message); + + + void eax_set_eax_fx_slot_defaults(); + + void eax_initialize_eax(); + + + void eax_initialize_effects(); + + + void eax_set_default_slot_0_defaults(); + + void eax_set_default_slot_1_defaults(); + + void eax_set_default_slots_defaults(); + + + void eax_get_fx_slot_all( + const EaxEaxCall& eax_call) const; + + void eax_get_fx_slot( + const EaxEaxCall& eax_call) const; + + // [[nodiscard]] + bool eax_get( + const EaxEaxCall& eax_call); + + + void eax_set_fx_slot_effect( + ALenum effect_type); + + void eax_set_fx_slot_effect(); + + + void eax_set_efx_effect_slot_gain(); + + void eax_set_fx_slot_volume(); + + + void eax_set_effect_slot_send_auto(); + + void eax_set_fx_slot_flags(); + + + void eax_validate_fx_slot_effect( + const GUID& eax_effect_id); + + void eax_validate_fx_slot_volume( + long eax_volume); + + void eax_validate_fx_slot_lock( + long eax_lock); + + void eax_validate_fx_slot_lock_state( + long eax_lock, + const GUID& eax_effect_id); + + void eax_validate_fx_slot_flags( + unsigned long eax_flags, + int eax_version); + + void eax_validate_fx_slot_occlusion( + long eax_occlusion); + + void eax_validate_fx_slot_occlusion_lf_ratio( + float eax_occlusion_lf_ratio); + + void eax_validate_fx_slot_all( + const EAX40FXSLOTPROPERTIES& fx_slot, + int eax_version); + + void eax_validate_fx_slot_all( + const EAX50FXSLOTPROPERTIES& fx_slot, + int eax_version); + + + void eax_set_fx_slot_effect( + const GUID& eax_effect_id); + + void eax_set_fx_slot_volume( + long eax_volume); + + void eax_set_fx_slot_lock( + long eax_lock); + + void eax_set_fx_slot_flags( + unsigned long eax_flags); + + // [[nodiscard]] + bool eax_set_fx_slot_occlusion( + long eax_occlusion); + + // [[nodiscard]] + bool eax_set_fx_slot_occlusion_lf_ratio( + float eax_occlusion_lf_ratio); + + void eax_set_fx_slot_all( + const EAX40FXSLOTPROPERTIES& eax_fx_slot); + + // [[nodiscard]] + bool eax_set_fx_slot_all( + const EAX50FXSLOTPROPERTIES& eax_fx_slot); + + + void eax_set_fx_slot_effect( + const EaxEaxCall& eax_call); + + void eax_set_fx_slot_volume( + const EaxEaxCall& eax_call); + + void eax_set_fx_slot_lock( + const EaxEaxCall& eax_call); + + void eax_set_fx_slot_flags( + const EaxEaxCall& eax_call); + + // [[nodiscard]] + bool eax_set_fx_slot_occlusion( + const EaxEaxCall& eax_call); + + // [[nodiscard]] + bool eax_set_fx_slot_occlusion_lf_ratio( + const EaxEaxCall& eax_call); + + // [[nodiscard]] + bool eax_set_fx_slot_all( + const EaxEaxCall& eax_call); + + bool eax_set_fx_slot( + const EaxEaxCall& eax_call); + + // [[nodiscard]] + bool eax_set( + const EaxEaxCall& eax_call); + + + void eax_dispatch_effect( + const EaxEaxCall& eax_call); + + + // `alAuxiliaryEffectSloti(effect_slot, AL_EFFECTSLOT_EFFECT, effect)` + void eax_set_effect_slot_effect( + ALeffect& effect); + + // `alAuxiliaryEffectSloti(effect_slot, AL_EFFECTSLOT_AUXILIARY_SEND_AUTO, value)` + void eax_set_effect_slot_send_auto( + bool is_send_auto); + + // `alAuxiliaryEffectSlotf(effect_slot, AL_EFFECTSLOT_GAIN, gain)` + void eax_set_effect_slot_gain( + ALfloat gain); +#endif // ALSOFT_EAX }; void UpdateAllEffectSlotProps(ALCcontext *context); +#if ALSOFT_EAX +class EaxAlEffectSlotDeleter +{ +public: + EaxAlEffectSlotDeleter() noexcept = default; + + EaxAlEffectSlotDeleter( + ALCcontext& context) noexcept; + + void operator()( + ALeffectslot* effect_slot); + + +private: + ALCcontext* context_{}; +}; // EaxAlEffectSlotDeleter + +using EaxAlEffectSlotUPtr = std::unique_ptr; + + +EaxAlEffectSlotUPtr eax_create_al_effect_slot( + ALCcontext& context); + +void eax_delete_al_effect_slot( + ALCcontext& context, + ALeffectslot& effect_slot); +#endif // ALSOFT_EAX + #endif diff --git a/al/buffer.cpp b/al/buffer.cpp index a473c3f9..a4967223 100644 --- a/al/buffer.cpp +++ b/al/buffer.cpp @@ -56,6 +56,11 @@ #include "core/voice.h" #include "opthelpers.h" +#if ALSOFT_EAX +#include "eax_globals.h" +#include "eax_x_ram.h" +#endif // ALSOFT_EAX + namespace { @@ -412,6 +417,15 @@ ALbuffer *AllocBuffer(ALCdevice *device) void FreeBuffer(ALCdevice *device, ALbuffer *buffer) { +#if ALSOFT_EAX + if (buffer->eax_x_ram_is_hardware) + { + const auto buffer_size = static_cast(buffer->OriginalSize); + assert((device->eax_x_ram_free_size + buffer_size) <= eax_x_ram_max_size); + device->eax_x_ram_free_size += buffer_size; + } +#endif // ALSOFT_EAX + const ALuint id{buffer->id - 1}; const size_t lidx{id >> 6}; const ALuint slidx{id & 0x3f}; @@ -485,6 +499,102 @@ const ALchar *NameFromUserFmtType(UserFmtType type) return ""; } +#if ALSOFT_EAX +bool eax_x_ram_validate_buffer( + ALCdevice& al_device, + ALbuffer& al_buffer) +{ + switch (al_buffer.eax_x_ram_mode) + { + case AL_STORAGE_HARDWARE: + if (al_buffer.OriginalSize > static_cast(al_device.eax_x_ram_free_size)) + { + return false; + } + + break; + + case AL_STORAGE_AUTOMATIC: + case AL_STORAGE_ACCESSIBLE: + break; + + default: + assert(false && "Unsupported X-RAM mode."); + return false; + } + + return true; +} + +void eax_x_ram_update_buffer( + ALCdevice& al_device, + ALbuffer& al_buffer) +{ + const auto buffer_size = static_cast(al_buffer.OriginalSize); + + auto is_hardware = al_buffer.eax_x_ram_is_hardware; + auto size_delta = ALsizei{}; + + switch (al_buffer.eax_x_ram_mode) + { + case AL_STORAGE_AUTOMATIC: + if (!al_buffer.eax_x_ram_is_dirty) + { + // First usage. + + if (buffer_size <= al_device.eax_x_ram_free_size) + { + // Have enough X-RAM memory. + + is_hardware = true; + size_delta = -buffer_size; + } + } + else + { + // Used at least once. + // From now on, use only system memory. + + is_hardware = false; + + if (al_buffer.eax_x_ram_is_hardware) + { + // First allocation was in X-RAM. + // Free that block. + + size_delta = buffer_size; + } + } + + break; + + case AL_STORAGE_HARDWARE: + is_hardware = true; + size_delta = buffer_size; + + break; + + case AL_STORAGE_ACCESSIBLE: + // Always use system memory. + is_hardware = false; + break; + + default: + break; + } + + al_buffer.eax_x_ram_is_hardware = is_hardware; + al_buffer.eax_x_ram_is_dirty = true; + + assert( + (al_device.eax_x_ram_free_size + size_delta) >= eax_x_ram_min_size && + (al_device.eax_x_ram_free_size + size_delta) <= eax_x_ram_max_size + ); + + al_device.eax_x_ram_free_size += size_delta; +} +#endif // ALSOFT_EAX + /** Loads the specified data into the buffer, using the specified format. */ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, UserFmtChannels SrcChannels, UserFmtType SrcType, const al::byte *SrcData, @@ -620,6 +730,13 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, ALBuf->mSampleLen = frames; ALBuf->mLoopStart = 0; ALBuf->mLoopEnd = ALBuf->mSampleLen; + +#if ALSOFT_EAX + if (eax_g_is_enabled) + { + eax_x_ram_update_buffer(*context->mALDevice, *ALBuf); + } +#endif // ALSOFT_EAX } /** Prepares the buffer to use the specified callback, using the specified format. */ @@ -889,8 +1006,24 @@ START_API_FUNC if UNLIKELY(!usrfmt) context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format); else +#if ALSOFT_EAX + { + if (eax_g_is_enabled) + { + const auto is_buffer_valid = eax_x_ram_validate_buffer(*device, *albuf); + + if (!is_buffer_valid) + { + context->setError(AL_OUT_OF_MEMORY, "Out of X-RAM memory."); + return; + } + } +#endif // ALSOFT_EAX LoadData(context.get(), albuf, freq, static_cast(size), usrfmt->channels, usrfmt->type, static_cast(data), flags); +#if ALSOFT_EAX + } +#endif // ALSOFT_EAX } } END_API_FUNC @@ -1642,3 +1775,162 @@ BufferSubList::~BufferSubList() al_free(Buffers); Buffers = nullptr; } + + +#if ALSOFT_EAX +ALboolean AL_APIENTRY EAXSetBufferMode( + ALsizei n, + const ALuint* buffers, + ALint value) +START_API_FUNC +{ +#define EAX_PREFIX "[EAXSetBufferMode] " + + if (n == 0) + { + return ALC_TRUE; + } + + if (n < 0) + { + ERR(EAX_PREFIX "Buffer count %d out of range.\n", n); + return ALC_FALSE; + } + + if (!buffers) + { + ERR(EAX_PREFIX "%s\n", "Null AL buffers."); + return ALC_FALSE; + } + + switch (value) + { + case AL_STORAGE_AUTOMATIC: + case AL_STORAGE_HARDWARE: + case AL_STORAGE_ACCESSIBLE: + break; + + default: + ERR(EAX_PREFIX "Unsupported X-RAM mode %d.\n", value); + return ALC_FALSE; + } + + const auto context = ContextRef{GetContextRef()}; + + if (!context) + { + ERR(EAX_PREFIX "%s\n", "No current context."); + return ALC_FALSE; + } + + if (!eax_g_is_enabled) + { + ERR(EAX_PREFIX "%s\n", "EAX not enabled."); + return ALC_FALSE; + } + + auto device = context->mALDevice.get(); + std::lock_guard device_lock{device->BufferLock}; + + // Validate the buffers. + // + for (auto i = 0; i < n; ++i) + { + const auto buffer = buffers[i]; + + if (buffer == AL_NONE) + { + continue; + } + + const auto al_buffer = LookupBuffer(device, buffer); + + if (!al_buffer) + { + ERR(EAX_PREFIX "Invalid buffer ID %u.\n", buffer); + return ALC_FALSE; + } + + if (al_buffer->eax_x_ram_is_dirty) + { + ERR(EAX_PREFIX "Buffer %u has audio data.\n", buffer); + return ALC_FALSE; + } + } + + // Update the mode. + // + for (auto i = 0; i < n; ++i) + { + const auto buffer = buffers[i]; + + if (buffer == AL_NONE) + { + continue; + } + + const auto al_buffer = LookupBuffer(device, buffer); + assert(al_buffer); + assert(!al_buffer->eax_x_ram_is_dirty); + + al_buffer->eax_x_ram_mode = value; + } + + return AL_TRUE; + +#undef EAX_PREFIX +} +END_API_FUNC + +ALenum AL_APIENTRY EAXGetBufferMode( + ALuint buffer, + ALint* pReserved) +START_API_FUNC +{ +#define EAX_PREFIX "[EAXGetBufferMode] " + + if (buffer == AL_NONE) + { + ERR(EAX_PREFIX "%s\n", "Null AL buffer."); + return AL_NONE; + } + + if (pReserved) + { + ERR(EAX_PREFIX "%s\n", "Non-null reserved parameter."); + return AL_NONE; + } + + const auto context = ContextRef{GetContextRef()}; + + if (!context) + { + ERR(EAX_PREFIX "%s\n", "No current context."); + return AL_NONE; + } + + if (!eax_g_is_enabled) + { + ERR(EAX_PREFIX "%s\n", "EAX not enabled."); + return AL_NONE; + } + + auto device = context->mALDevice.get(); + std::lock_guard device_lock{device->BufferLock}; + + const auto al_buffer = LookupBuffer(device, buffer); + + if (!al_buffer) + { + ERR(EAX_PREFIX "Invalid buffer ID %u.\n", buffer); + return AL_NONE; + } + + return al_buffer->eax_x_ram_mode; + +#undef EAX_PREFIX +} +END_API_FUNC + + +#endif // ALSOFT_EAX diff --git a/al/buffer.h b/al/buffer.h index a78c65c6..0514d984 100644 --- a/al/buffer.h +++ b/al/buffer.h @@ -12,6 +12,9 @@ #include "core/buffer_storage.h" #include "vector.h" +#if ALSOFT_EAX +#include "eax_x_ram.h" +#endif // ALSOFT_EAX /* User formats */ enum UserFmtType : unsigned char { @@ -68,6 +71,12 @@ struct ALbuffer : public BufferStorage { ALuint id{0}; DISABLE_ALLOC() + +#if ALSOFT_EAX + ALenum eax_x_ram_mode{AL_STORAGE_AUTOMATIC}; + bool eax_x_ram_is_hardware{}; + bool eax_x_ram_is_dirty{}; +#endif // ALSOFT_EAX }; #endif diff --git a/al/eax_api.cpp b/al/eax_api.cpp new file mode 100644 index 00000000..9907dd4d --- /dev/null +++ b/al/eax_api.cpp @@ -0,0 +1,1151 @@ +// +// EAX API. +// +// Based on headers `eax[2-5].h` included in Doom 3 source code: +// https://github.com/id-Software/DOOM-3/tree/master/neo/openal/include +// + + +#include + +#include "al/eax_api.h" + + +const GUID DSPROPSETID_EAX20_ListenerProperties = +{ + 0x306A6A8, + 0xB224, + 0x11D2, + {0x99, 0xE5, 0x00, 0x00, 0xE8, 0xD8, 0xC7, 0x22} +}; + +const GUID DSPROPSETID_EAX20_BufferProperties = +{ + 0x306A6A7, + 0xB224, + 0x11D2, + {0x99, 0xE5, 0x00, 0x00, 0xE8, 0xD8, 0xC7, 0x22} +}; + +const GUID DSPROPSETID_EAX30_ListenerProperties = +{ + 0xA8FA6882, + 0xB476, + 0x11D3, + {0xBD, 0xB9, 0x00, 0xC0, 0xF0, 0x2D, 0xDF, 0x87} +}; + +const GUID DSPROPSETID_EAX30_BufferProperties = +{ + 0xA8FA6881, + 0xB476, + 0x11D3, + {0xBD, 0xB9, 0x00, 0xC0, 0xF0, 0x2D, 0xDF, 0x87} +}; + +const GUID EAX_NULL_GUID = +{ + 0x00000000, + 0x0000, + 0x0000, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +const GUID EAX_PrimaryFXSlotID = +{ + 0xF317866D, + 0x924C, + 0x450C, + {0x86, 0x1B, 0xE6, 0xDA, 0xA2, 0x5E, 0x7C, 0x20} +}; + +const GUID EAXPROPERTYID_EAX40_Context = +{ + 0x1D4870AD, + 0xDEF, + 0x43C0, + {0xA4, 0xC, 0x52, 0x36, 0x32, 0x29, 0x63, 0x42} +}; + +const GUID EAXPROPERTYID_EAX50_Context = +{ + 0x57E13437, + 0xB932, + 0x4AB2, + {0xB8, 0xBD, 0x52, 0x66, 0xC1, 0xA8, 0x87, 0xEE} +}; + +const GUID EAXPROPERTYID_EAX40_FXSlot0 = +{ + 0xC4D79F1E, + 0xF1AC, + 0x436B, + {0xA8, 0x1D, 0xA7, 0x38, 0xE7, 0x04, 0x54, 0x69} +}; + +const GUID EAXPROPERTYID_EAX50_FXSlot0 = +{ + 0x91F9590F, + 0xC388, + 0x407A, + {0x84, 0xB0, 0x1B, 0xAE, 0xE, 0xF7, 0x1A, 0xBC} +}; + +const GUID EAXPROPERTYID_EAX40_FXSlot1 = +{ + 0x8C00E96, + 0x74BE, + 0x4491, + {0x93, 0xAA, 0xE8, 0xAD, 0x35, 0xA4, 0x91, 0x17} +}; + +const GUID EAXPROPERTYID_EAX50_FXSlot1 = +{ + 0x8F5F7ACA, + 0x9608, + 0x4965, + {0x81, 0x37, 0x82, 0x13, 0xC7, 0xB9, 0xD9, 0xDE} +}; + +const GUID EAXPROPERTYID_EAX40_FXSlot2 = +{ + 0x1D433B88, + 0xF0F6, + 0x4637, + {0x91, 0x9F, 0x60, 0xE7, 0xE0, 0x6B, 0x5E, 0xDD} +}; + +const GUID EAXPROPERTYID_EAX50_FXSlot2 = +{ + 0x3C0F5252, + 0x9834, + 0x46F0, + {0xA1, 0xD8, 0x5B, 0x95, 0xC4, 0xA0, 0xA, 0x30} +}; + +const GUID EAXPROPERTYID_EAX40_FXSlot3 = +{ + 0xEFFF08EA, + 0xC7D8, + 0x44AB, + {0x93, 0xAD, 0x6D, 0xBD, 0x5F, 0x91, 0x00, 0x64} +}; + +const GUID EAXPROPERTYID_EAX50_FXSlot3 = +{ + 0xE2EB0EAA, + 0xE806, + 0x45E7, + {0x9F, 0x86, 0x06, 0xC1, 0x57, 0x1A, 0x6F, 0xA3} +}; + +const GUID EAXPROPERTYID_EAX40_Source = +{ + 0x1B86B823, + 0x22DF, + 0x4EAE, + {0x8B, 0x3C, 0x12, 0x78, 0xCE, 0x54, 0x42, 0x27} +}; + +const GUID EAXPROPERTYID_EAX50_Source = +{ + 0x5EDF82F0, + 0x24A7, + 0x4F38, + {0x8E, 0x64, 0x2F, 0x09, 0xCA, 0x05, 0xDE, 0xE1} +}; + +const GUID EAX_REVERB_EFFECT = +{ + 0xCF95C8F, + 0xA3CC, + 0x4849, + {0xB0, 0xB6, 0x83, 0x2E, 0xCC, 0x18, 0x22, 0xDF} +}; + +const GUID EAX_AGCCOMPRESSOR_EFFECT = +{ + 0xBFB7A01E, + 0x7825, + 0x4039, + {0x92, 0x7F, 0x03, 0xAA, 0xBD, 0xA0, 0xC5, 0x60} +}; + +const GUID EAX_AUTOWAH_EFFECT = +{ + 0xEC3130C0, + 0xAC7A, + 0x11D2, + {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + +const GUID EAX_CHORUS_EFFECT = +{ + 0xDE6D6FE0, + 0xAC79, + 0x11D2, + {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + +const GUID EAX_DISTORTION_EFFECT = +{ + 0x975A4CE0, + 0xAC7E, + 0x11D2, + {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + +const GUID EAX_ECHO_EFFECT = +{ + 0xE9F1BC0, + 0xAC82, + 0x11D2, + {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + +const GUID EAX_EQUALIZER_EFFECT = +{ + 0x65F94CE0, + 0x9793, + 0x11D3, + {0x93, 0x9D, 0x00, 0xC0, 0xF0, 0x2D, 0xD6, 0xF0} +}; + +const GUID EAX_FLANGER_EFFECT = +{ + 0xA70007C0, + 0x7D2, + 0x11D3, + {0x9B, 0x1E, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + +const GUID EAX_FREQUENCYSHIFTER_EFFECT = +{ + 0xDC3E1880, + 0x9212, + 0x11D3, + {0x93, 0x9D, 0x00, 0xC0, 0xF0, 0x2D, 0xD6, 0xF0} +}; + +const GUID EAX_VOCALMORPHER_EFFECT = +{ + 0xE41CF10C, + 0x3383, + 0x11D2, + {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + +const GUID EAX_PITCHSHIFTER_EFFECT = +{ + 0xE7905100, + 0xAFB2, + 0x11D2, + {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + +const GUID EAX_RINGMODULATOR_EFFECT = +{ + 0xB89FE60, + 0xAFB5, + 0x11D2, + {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + + +bool operator==( + const EAXVECTOR& lhs, + const EAXVECTOR& rhs) noexcept +{ + return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z; +} + +bool operator!=( + const EAXVECTOR& lhs, + const EAXVECTOR& rhs) noexcept +{ + return !(lhs == rhs); +} + + +bool operator==( + const EAX40CONTEXTPROPERTIES& lhs, + const EAX40CONTEXTPROPERTIES& rhs) noexcept +{ + return + lhs.guidPrimaryFXSlotID == rhs.guidPrimaryFXSlotID && + lhs.flDistanceFactor == rhs.flDistanceFactor && + lhs.flAirAbsorptionHF == rhs.flAirAbsorptionHF && + lhs.flHFReference == rhs.flHFReference; +} + +bool operator==( + const EAX50CONTEXTPROPERTIES& lhs, + const EAX50CONTEXTPROPERTIES& rhs) noexcept +{ + return + static_cast(lhs) == static_cast(rhs) && + lhs.flMacroFXFactor == rhs.flMacroFXFactor; +} + + +const GUID EAXCONTEXT_DEFAULTPRIMARYFXSLOTID = EAXPROPERTYID_EAX40_FXSlot0; + +bool operator==( + const EAX40FXSLOTPROPERTIES& lhs, + const EAX40FXSLOTPROPERTIES& rhs) noexcept +{ + return + lhs.guidLoadEffect == rhs.guidLoadEffect && + lhs.lVolume == rhs.lVolume && + lhs.lLock == rhs.lLock && + lhs.ulFlags == rhs.ulFlags; +} + +bool operator==( + const EAX50FXSLOTPROPERTIES& lhs, + const EAX50FXSLOTPROPERTIES& rhs) noexcept +{ + return + static_cast(lhs) == static_cast(rhs) && + lhs.lOcclusion == rhs.lOcclusion && + lhs.flOcclusionLFRatio == rhs.flOcclusionLFRatio; +} + +const EAX50ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID = EAX50ACTIVEFXSLOTS +{{ + EAX_NULL_GUID, + EAXPROPERTYID_EAX40_FXSlot0, +}}; + +bool operator==( + const EAX50ACTIVEFXSLOTS& lhs, + const EAX50ACTIVEFXSLOTS& rhs) noexcept +{ + return std::equal( + std::cbegin(lhs.guidActiveFXSlots), + std::cend(lhs.guidActiveFXSlots), + std::begin(rhs.guidActiveFXSlots)); +} + +bool operator!=( + const EAX50ACTIVEFXSLOTS& lhs, + const EAX50ACTIVEFXSLOTS& rhs) noexcept +{ + return !(lhs == rhs); +} + + +const EAX50ACTIVEFXSLOTS EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID = EAX50ACTIVEFXSLOTS +{{ + EAX_NULL_GUID, + EAX_PrimaryFXSlotID, + EAX_NULL_GUID, + EAX_NULL_GUID, +}}; + + +const EAX50ACTIVEFXSLOTS EAX50SOURCE_2DDEFAULTACTIVEFXSLOTID = EAX50ACTIVEFXSLOTS +{{ + EAX_NULL_GUID, + EAX_NULL_GUID, + EAX_NULL_GUID, + EAX_NULL_GUID, +}}; + +bool operator==( + const EAXREVERBPROPERTIES& lhs, + const EAXREVERBPROPERTIES& rhs) noexcept +{ + return + lhs.ulEnvironment == rhs.ulEnvironment && + lhs.flEnvironmentSize == rhs.flEnvironmentSize && + lhs.flEnvironmentDiffusion == rhs.flEnvironmentDiffusion && + lhs.lRoom == rhs.lRoom && + lhs.lRoomHF == rhs.lRoomHF && + lhs.lRoomLF == rhs.lRoomLF && + lhs.flDecayTime == rhs.flDecayTime && + lhs.flDecayHFRatio == rhs.flDecayHFRatio && + lhs.flDecayLFRatio == rhs.flDecayLFRatio && + lhs.lReflections == rhs.lReflections && + lhs.flReflectionsDelay == rhs.flReflectionsDelay && + lhs.vReflectionsPan == rhs.vReflectionsPan && + lhs.lReverb == rhs.lReverb && + lhs.flReverbDelay == rhs.flReverbDelay && + lhs.vReverbPan == rhs.vReverbPan && + lhs.flEchoTime == rhs.flEchoTime && + lhs.flEchoDepth == rhs.flEchoDepth && + lhs.flModulationTime == rhs.flModulationTime && + lhs.flModulationDepth == rhs.flModulationDepth && + lhs.flAirAbsorptionHF == rhs.flAirAbsorptionHF && + lhs.flHFReference == rhs.flHFReference && + lhs.flLFReference == rhs.flLFReference && + lhs.flRoomRolloffFactor == rhs.flRoomRolloffFactor && + lhs.ulFlags == rhs.ulFlags; +} + +bool operator!=( + const EAXREVERBPROPERTIES& lhs, + const EAXREVERBPROPERTIES& rhs) noexcept +{ + return !(lhs == rhs); +} + + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_GENERIC = +{ + EAXREVERB_DEFAULTENVIRONMENT, + EAXREVERB_DEFAULTENVIRONMENTSIZE, + EAXREVERB_DEFAULTENVIRONMENTDIFFUSION, + EAXREVERB_DEFAULTROOM, + EAXREVERB_DEFAULTROOMHF, + EAXREVERB_DEFAULTROOMLF, + EAXREVERB_DEFAULTDECAYTIME, + EAXREVERB_DEFAULTDECAYHFRATIO, + EAXREVERB_DEFAULTDECAYLFRATIO, + EAXREVERB_DEFAULTREFLECTIONS, + EAXREVERB_DEFAULTREFLECTIONSDELAY, + EAXREVERB_DEFAULTREFLECTIONSPAN, + EAXREVERB_DEFAULTREVERB, + EAXREVERB_DEFAULTREVERBDELAY, + EAXREVERB_DEFAULTREVERBPAN, + EAXREVERB_DEFAULTECHOTIME, + EAXREVERB_DEFAULTECHODEPTH, + EAXREVERB_DEFAULTMODULATIONTIME, + EAXREVERB_DEFAULTMODULATIONDEPTH, + EAXREVERB_DEFAULTAIRABSORPTIONHF, + EAXREVERB_DEFAULTHFREFERENCE, + EAXREVERB_DEFAULTLFREFERENCE, + EAXREVERB_DEFAULTROOMROLLOFFFACTOR, + EAXREVERB_DEFAULTFLAGS, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_PADDEDCELL = +{ + EAX_ENVIRONMENT_PADDEDCELL, + 1.4F, + 1.0F, + -1'000L, + -6'000L, + 0L, + 0.17F, + 0.10F, + 1.0F, + -1'204L, + 0.001F, + EAXVECTOR{}, + 207L, + 0.002F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_ROOM = +{ + EAX_ENVIRONMENT_ROOM, + 1.9F, + 1.0F, + -1'000L, + -454L, + 0L, + 0.40F, + 0.83F, + 1.0F, + -1'646L, + 0.002F, + EAXVECTOR{}, + 53L, + 0.003F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_BATHROOM = +{ + EAX_ENVIRONMENT_BATHROOM, + 1.4F, + 1.0F, + -1'000L, + -1'200L, + 0L, + 1.49F, + 0.54F, + 1.0F, + -370L, + 0.007F, + EAXVECTOR{}, + 1'030L, + 0.011F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_LIVINGROOM = +{ + EAX_ENVIRONMENT_LIVINGROOM, + 2.5F, + 1.0F, + -1'000L, + -6'000L, + 0L, + 0.50F, + 0.10F, + 1.0F, + -1'376, + 0.003F, + EAXVECTOR{}, + -1'104L, + 0.004F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_STONEROOM = +{ + EAX_ENVIRONMENT_STONEROOM, + 11.6F, + 1.0F, + -1'000L, + -300L, + 0L, + 2.31F, + 0.64F, + 1.0F, + -711L, + 0.012F, + EAXVECTOR{}, + 83L, + 0.017F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_AUDITORIUM = +{ + EAX_ENVIRONMENT_AUDITORIUM, + 21.6F, + 1.0F, + -1'000L, + -476L, + 0L, + 4.32F, + 0.59F, + 1.0F, + -789L, + 0.020F, + EAXVECTOR{}, + -289L, + 0.030F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_CONCERTHALL = +{ + EAX_ENVIRONMENT_CONCERTHALL, + 19.6F, + 1.0F, + -1'000L, + -500L, + 0L, + 3.92F, + 0.70F, + 1.0F, + -1'230L, + 0.020F, + EAXVECTOR{}, + -2L, + 0.029F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_CAVE = +{ + EAX_ENVIRONMENT_CAVE, + 14.6F, + 1.0F, + -1'000L, + 0L, + 0L, + 2.91F, + 1.30F, + 1.0F, + -602L, + 0.015F, + EAXVECTOR{}, + -302L, + 0.022F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x1FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_ARENA = +{ + EAX_ENVIRONMENT_ARENA, + 36.2F, + 1.0F, + -1'000L, + -698L, + 0L, + 7.24F, + 0.33F, + 1.0F, + -1'166L, + 0.020F, + EAXVECTOR{}, + 16L, + 0.030F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_HANGAR = +{ + EAX_ENVIRONMENT_HANGAR, + 50.3F, + 1.0F, + -1'000L, + -1'000L, + 0L, + 10.05F, + 0.23F, + 1.0F, + -602L, + 0.020F, + EAXVECTOR{}, + 198L, + 0.030F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_CARPETTEDHALLWAY = +{ + EAX_ENVIRONMENT_CARPETEDHALLWAY, + 1.9F, + 1.0F, + -1'000L, + -4'000L, + 0L, + 0.30F, + 0.10F, + 1.0F, + -1'831L, + 0.002F, + EAXVECTOR{}, + -1'630L, + 0.030F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_HALLWAY = +{ + EAX_ENVIRONMENT_HALLWAY, + 1.8F, + 1.0F, + -1'000L, + -300L, + 0L, + 1.49F, + 0.59F, + 1.0F, + -1'219L, + 0.007F, + EAXVECTOR{}, + 441L, + 0.011F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_STONECORRIDOR = +{ + EAX_ENVIRONMENT_STONECORRIDOR, + 13.5F, + 1.0F, + -1'000L, + -237L, + 0L, + 2.70F, + 0.79F, + 1.0F, + -1'214L, + 0.013F, + EAXVECTOR{}, + 395L, + 0.020F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_ALLEY = +{ + EAX_ENVIRONMENT_ALLEY, + 7.5F, + 0.300F, + -1'000L, + -270L, + 0L, + 1.49F, + 0.86F, + 1.0F, + -1'204L, + 0.007F, + EAXVECTOR{}, + -4L, + 0.011F, + EAXVECTOR{}, + 0.125F, + 0.950F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_FOREST = +{ + EAX_ENVIRONMENT_FOREST, + 38.0F, + 0.300F, + -1'000L, + -3'300L, + 0L, + 1.49F, + 0.54F, + 1.0F, + -2'560L, + 0.162F, + EAXVECTOR{}, + -229L, + 0.088F, + EAXVECTOR{}, + 0.125F, + 1.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_CITY = +{ + EAX_ENVIRONMENT_CITY, + 7.5F, + 0.500F, + -1'000L, + -800L, + 0L, + 1.49F, + 0.67F, + 1.0F, + -2'273L, + 0.007F, + EAXVECTOR{}, + -1'691L, + 0.011F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_MOUNTAINS = +{ + EAX_ENVIRONMENT_MOUNTAINS, + 100.0F, + 0.270F, + -1'000L, + -2'500L, + 0L, + 1.49F, + 0.21F, + 1.0F, + -2'780L, + 0.300F, + EAXVECTOR{}, + -1'434L, + 0.100F, + EAXVECTOR{}, + 0.250F, + 1.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x1FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_QUARRY = +{ + EAX_ENVIRONMENT_QUARRY, + 17.5F, + 1.0F, + -1'000L, + -1'000L, + 0L, + 1.49F, + 0.83F, + 1.0F, + -10'000L, + 0.061F, + EAXVECTOR{}, + 500L, + 0.025F, + EAXVECTOR{}, + 0.125F, + 0.700F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_PLAIN = +{ + EAX_ENVIRONMENT_PLAIN, + 42.5F, + 0.210F, + -1'000L, + -2'000L, + 0L, + 1.49F, + 0.50F, + 1.0F, + -2'466L, + 0.179F, + EAXVECTOR{}, + -1'926L, + 0.100F, + EAXVECTOR{}, + 0.250F, + 1.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_PARKINGLOT = +{ + EAX_ENVIRONMENT_PARKINGLOT, + 8.3F, + 1.0F, + -1'000L, + 0L, + 0L, + 1.65F, + 1.50F, + 1.0F, + -1'363L, + 0.008F, + EAXVECTOR{}, + -1'153L, + 0.012F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x1FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_SEWERPIPE = +{ + EAX_ENVIRONMENT_SEWERPIPE, + 1.7F, + 0.800F, + -1'000L, + -1'000L, + 0L, + 2.81F, + 0.14F, + 1.0F, + 429L, + 0.014F, + EAXVECTOR{}, + 1'023L, + 0.021F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_UNDERWATER = +{ + EAX_ENVIRONMENT_UNDERWATER, + 1.8F, + 1.0F, + -1'000L, + -4'000L, + 0L, + 1.49F, + 0.10F, + 1.0F, + -449L, + 0.007F, + EAXVECTOR{}, + 1'700L, + 0.011F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 1.180F, + 0.348F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_DRUGGED = +{ + EAX_ENVIRONMENT_DRUGGED, + 1.9F, + 0.500F, + -1'000L, + 0L, + 0L, + 8.39F, + 1.39F, + 1.0F, + -115L, + 0.002F, + EAXVECTOR{}, + 985L, + 0.030F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 1.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x1FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_DIZZY = +{ + EAX_ENVIRONMENT_DIZZY, + 1.8F, + 0.600F, + -1'000L, + -400L, + 0L, + 17.23F, + 0.56F, + 1.0F, + -1'713L, + 0.020F, + EAXVECTOR{}, + -613L, + 0.030F, + EAXVECTOR{}, + 0.250F, + 1.0F, + 0.810F, + 0.310F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x1FUL, +}; + +const EAXREVERBPROPERTIES EAXREVERB_PRESET_PSYCHOTIC = +{ + EAX_ENVIRONMENT_PSYCHOTIC, + 1.0F, + 0.500F, + -1'000L, + -151L, + 0L, + 7.56F, + 0.91F, + 1.0F, + -626L, + 0.020F, + EAXVECTOR{}, + 774L, + 0.030F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 4.0F, + 1.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x1FUL, +}; + + +const EaxReverbPresets EAXREVERB_PRESETS = +{ + EAXREVERB_PRESET_GENERIC, + EAXREVERB_PRESET_PADDEDCELL, + EAXREVERB_PRESET_ROOM, + EAXREVERB_PRESET_BATHROOM, + EAXREVERB_PRESET_LIVINGROOM, + EAXREVERB_PRESET_STONEROOM, + EAXREVERB_PRESET_AUDITORIUM, + EAXREVERB_PRESET_CONCERTHALL, + EAXREVERB_PRESET_CAVE, + EAXREVERB_PRESET_ARENA, + EAXREVERB_PRESET_HANGAR, + EAXREVERB_PRESET_CARPETTEDHALLWAY, + EAXREVERB_PRESET_HALLWAY, + EAXREVERB_PRESET_STONECORRIDOR, + EAXREVERB_PRESET_ALLEY, + EAXREVERB_PRESET_FOREST, + EAXREVERB_PRESET_CITY, + EAXREVERB_PRESET_MOUNTAINS, + EAXREVERB_PRESET_QUARRY, + EAXREVERB_PRESET_PLAIN, + EAXREVERB_PRESET_PARKINGLOT, + EAXREVERB_PRESET_SEWERPIPE, + EAXREVERB_PRESET_UNDERWATER, + EAXREVERB_PRESET_DRUGGED, + EAXREVERB_PRESET_DIZZY, + EAXREVERB_PRESET_PSYCHOTIC, +}; diff --git a/al/eax_api.h b/al/eax_api.h new file mode 100644 index 00000000..98eb1fc5 --- /dev/null +++ b/al/eax_api.h @@ -0,0 +1,1542 @@ +#ifndef EAX_API_INCLUDED +#define EAX_API_INCLUDED + + +// +// EAX API. +// +// Based on headers `eax[2-5].h` included in Doom 3 source code: +// https://github.com/id-Software/DOOM-3/tree/master/neo/openal/include +// + + +#include +#include + +#include + +#include "AL/al.h" + + +#ifndef GUID_DEFINED +#define GUID_DEFINED +typedef struct _GUID +{ + std::uint32_t Data1; + std::uint16_t Data2; + std::uint16_t Data3; + std::uint8_t Data4[8]; +} GUID; + +inline constexpr bool operator==( + const GUID& lhs, + const GUID& rhs) noexcept +{ + return + lhs.Data1 == rhs.Data1 && + lhs.Data2 == rhs.Data2 && + lhs.Data3 == rhs.Data3 && + lhs.Data4[0] == rhs.Data4[0] && + lhs.Data4[1] == rhs.Data4[1] && + lhs.Data4[2] == rhs.Data4[2] && + lhs.Data4[3] == rhs.Data4[3] && + lhs.Data4[4] == rhs.Data4[4] && + lhs.Data4[5] == rhs.Data4[5] && + lhs.Data4[6] == rhs.Data4[6] && + lhs.Data4[7] == rhs.Data4[7]; +} + +inline constexpr bool operator!=( + const GUID& lhs, + const GUID& rhs) noexcept +{ + return !(lhs == rhs); +} +#endif // GUID_DEFINED + + +extern "C" const GUID DSPROPSETID_EAX20_ListenerProperties; + +enum DSPROPERTY_EAX20_LISTENERPROPERTY : + unsigned int +{ + DSPROPERTY_EAX20LISTENER_NONE, + DSPROPERTY_EAX20LISTENER_ALLPARAMETERS, + DSPROPERTY_EAX20LISTENER_ROOM, + DSPROPERTY_EAX20LISTENER_ROOMHF, + DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAX20LISTENER_DECAYTIME, + DSPROPERTY_EAX20LISTENER_DECAYHFRATIO, + DSPROPERTY_EAX20LISTENER_REFLECTIONS, + DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY, + DSPROPERTY_EAX20LISTENER_REVERB, + DSPROPERTY_EAX20LISTENER_REVERBDELAY, + DSPROPERTY_EAX20LISTENER_ENVIRONMENT, + DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE, + DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION, + DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF, + DSPROPERTY_EAX20LISTENER_FLAGS +}; // DSPROPERTY_EAX20_LISTENERPROPERTY + +struct EAX20LISTENERPROPERTIES +{ + long lRoom; // room effect level at low frequencies + long lRoomHF; // room effect high-frequency level re. low frequency level + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flDecayTime; // reverberation decay time at low frequencies + float flDecayHFRatio; // high-frequency to low-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + unsigned long dwEnvironment; // sets all listener properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + float flAirAbsorptionHF; // change in level per meter at 5 kHz + unsigned long dwFlags; // modifies the behavior of properties +}; // EAX20LISTENERPROPERTIES + + +extern "C" const GUID DSPROPSETID_EAX20_BufferProperties; + + +enum DSPROPERTY_EAX20_BUFFERPROPERTY : + unsigned int +{ + DSPROPERTY_EAX20BUFFER_NONE, + DSPROPERTY_EAX20BUFFER_ALLPARAMETERS, + DSPROPERTY_EAX20BUFFER_DIRECT, + DSPROPERTY_EAX20BUFFER_DIRECTHF, + DSPROPERTY_EAX20BUFFER_ROOM, + DSPROPERTY_EAX20BUFFER_ROOMHF, + DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAX20BUFFER_OBSTRUCTION, + DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO, + DSPROPERTY_EAX20BUFFER_OCCLUSION, + DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO, + DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO, + DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF, + DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR, + DSPROPERTY_EAX20BUFFER_FLAGS +}; // DSPROPERTY_EAX20_BUFFERPROPERTY + + +struct EAX20BUFFERPROPERTIES +{ + long lDirect; // direct path level + long lDirectHF; // direct path level at high frequencies + long lRoom; // room effect level + long lRoomHF; // room effect level at high frequencies + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // occlusion room effect level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF + unsigned long dwFlags; // modifies the behavior of properties +}; // EAX20BUFFERPROPERTIES + + +extern "C" const GUID DSPROPSETID_EAX30_ListenerProperties; + +extern "C" const GUID DSPROPSETID_EAX30_BufferProperties; + + +constexpr auto EAX_MAX_FXSLOTS = 4; + +constexpr auto EAX40_MAX_ACTIVE_FXSLOTS = 2; +constexpr auto EAX50_MAX_ACTIVE_FXSLOTS = 4; + + +constexpr auto EAX_OK = 0L; +constexpr auto EAXERR_INVALID_OPERATION = -1L; +constexpr auto EAXERR_INVALID_VALUE = -2L; +constexpr auto EAXERR_NO_EFFECT_LOADED = -3L; +constexpr auto EAXERR_UNKNOWN_EFFECT = -4L; +constexpr auto EAXERR_INCOMPATIBLE_SOURCE_TYPE = -5L; +constexpr auto EAXERR_INCOMPATIBLE_EAX_VERSION = -6L; + + +extern "C" const GUID EAX_NULL_GUID; + +extern "C" const GUID EAX_PrimaryFXSlotID; + + +struct EAXVECTOR +{ + float x; + float y; + float z; +}; // EAXVECTOR + +bool operator==( + const EAXVECTOR& lhs, + const EAXVECTOR& rhs) noexcept; + +bool operator!=( + const EAXVECTOR& lhs, + const EAXVECTOR& rhs) noexcept; + + +extern "C" const GUID EAXPROPERTYID_EAX40_Context; + +extern "C" const GUID EAXPROPERTYID_EAX50_Context; + +// EAX50 +enum : + unsigned long +{ + HEADPHONES = 0, + SPEAKERS_2, + SPEAKERS_4, + SPEAKERS_5, // 5.1 speakers + SPEAKERS_6, // 6.1 speakers + SPEAKERS_7, // 7.1 speakers +}; + +// EAX50 +enum : + unsigned long +{ + EAX_40 = 5, // EAX 4.0 + EAX_50 = 6, // EAX 5.0 +}; + +constexpr auto EAXCONTEXT_MINEAXSESSION = EAX_40; +constexpr auto EAXCONTEXT_MAXEAXSESSION = EAX_50; +constexpr auto EAXCONTEXT_DEFAULTEAXSESSION = EAX_40; + +constexpr auto EAXCONTEXT_MINMAXACTIVESENDS = 2UL; +constexpr auto EAXCONTEXT_MAXMAXACTIVESENDS = 4UL; +constexpr auto EAXCONTEXT_DEFAULTMAXACTIVESENDS = 2UL; + +// EAX50 +struct EAXSESSIONPROPERTIES +{ + unsigned long ulEAXVersion; + unsigned long ulMaxActiveSends; +}; // EAXSESSIONPROPERTIES + +enum EAXCONTEXT_PROPERTY : + unsigned int +{ + EAXCONTEXT_NONE = 0, + EAXCONTEXT_ALLPARAMETERS, + EAXCONTEXT_PRIMARYFXSLOTID, + EAXCONTEXT_DISTANCEFACTOR, + EAXCONTEXT_AIRABSORPTIONHF, + EAXCONTEXT_HFREFERENCE, + EAXCONTEXT_LASTERROR, + + // EAX50 + EAXCONTEXT_SPEAKERCONFIG, + EAXCONTEXT_EAXSESSION, + EAXCONTEXT_MACROFXFACTOR, +}; // EAXCONTEXT_PROPERTY + +struct EAX40CONTEXTPROPERTIES +{ + GUID guidPrimaryFXSlotID; + float flDistanceFactor; + float flAirAbsorptionHF; + float flHFReference; +}; // EAX40CONTEXTPROPERTIES + +struct EAX50CONTEXTPROPERTIES : + public EAX40CONTEXTPROPERTIES +{ + float flMacroFXFactor; +}; // EAX40CONTEXTPROPERTIES + + +bool operator==( + const EAX40CONTEXTPROPERTIES& lhs, + const EAX40CONTEXTPROPERTIES& rhs) noexcept; + +bool operator==( + const EAX50CONTEXTPROPERTIES& lhs, + const EAX50CONTEXTPROPERTIES& rhs) noexcept; + + +constexpr auto EAXCONTEXT_MINDISTANCEFACTOR = FLT_MIN; +constexpr auto EAXCONTEXT_MAXDISTANCEFACTOR = FLT_MAX; +constexpr auto EAXCONTEXT_DEFAULTDISTANCEFACTOR = 1.0F; + +constexpr auto EAXCONTEXT_MINAIRABSORPTIONHF = -100.0F; +constexpr auto EAXCONTEXT_MAXAIRABSORPTIONHF = 0.0F; +constexpr auto EAXCONTEXT_DEFAULTAIRABSORPTIONHF = -5.0F; + +constexpr auto EAXCONTEXT_MINHFREFERENCE = 1000.0F; +constexpr auto EAXCONTEXT_MAXHFREFERENCE = 20000.0F; +constexpr auto EAXCONTEXT_DEFAULTHFREFERENCE = 5000.0F; + +constexpr auto EAXCONTEXT_MINMACROFXFACTOR = 0.0F; +constexpr auto EAXCONTEXT_MAXMACROFXFACTOR = 1.0F; +constexpr auto EAXCONTEXT_DEFAULTMACROFXFACTOR = 0.0F; + + +extern "C" const GUID EAXPROPERTYID_EAX40_FXSlot0; + +extern "C" const GUID EAXPROPERTYID_EAX50_FXSlot0; + +extern "C" const GUID EAXPROPERTYID_EAX40_FXSlot1; + +extern "C" const GUID EAXPROPERTYID_EAX50_FXSlot1; + +extern "C" const GUID EAXPROPERTYID_EAX40_FXSlot2; + +extern "C" const GUID EAXPROPERTYID_EAX50_FXSlot2; + +extern "C" const GUID EAXPROPERTYID_EAX40_FXSlot3; + +extern "C" const GUID EAXPROPERTYID_EAX50_FXSlot3; + +extern "C" const GUID EAXCONTEXT_DEFAULTPRIMARYFXSLOTID; + +enum EAXFXSLOT_PROPERTY : + unsigned int +{ + EAXFXSLOT_PARAMETER = 0, + + EAXFXSLOT_NONE = 0x10000, + EAXFXSLOT_ALLPARAMETERS, + EAXFXSLOT_LOADEFFECT, + EAXFXSLOT_VOLUME, + EAXFXSLOT_LOCK, + EAXFXSLOT_FLAGS, + + // EAX50 + EAXFXSLOT_OCCLUSION, + EAXFXSLOT_OCCLUSIONLFRATIO, +}; // EAXFXSLOT_PROPERTY + +constexpr auto EAXFXSLOTFLAGS_ENVIRONMENT = 0x00000001UL; +// EAX50 +constexpr auto EAXFXSLOTFLAGS_UPMIX = 0x00000002UL; + +constexpr auto EAX40FXSLOTFLAGS_RESERVED = 0xFFFFFFFEUL; // reserved future use +constexpr auto EAX50FXSLOTFLAGS_RESERVED = 0xFFFFFFFCUL; // reserved future use + + +constexpr auto EAXFXSLOT_MINVOLUME = -10'000L; +constexpr auto EAXFXSLOT_MAXVOLUME = 0L; +constexpr auto EAXFXSLOT_DEFAULTVOLUME = 0L; + +constexpr auto EAXFXSLOT_MINLOCK = 0L; +constexpr auto EAXFXSLOT_MAXLOCK = 1L; + +enum : + long +{ + EAXFXSLOT_UNLOCKED = 0, + EAXFXSLOT_LOCKED = 1 +}; + +constexpr auto EAXFXSLOT_MINOCCLUSION = -10'000L; +constexpr auto EAXFXSLOT_MAXOCCLUSION = 0L; +constexpr auto EAXFXSLOT_DEFAULTOCCLUSION = 0L; + +constexpr auto EAXFXSLOT_MINOCCLUSIONLFRATIO = 0.0F; +constexpr auto EAXFXSLOT_MAXOCCLUSIONLFRATIO = 1.0F; +constexpr auto EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO = 0.25F; + +constexpr auto EAX40FXSLOT_DEFAULTFLAGS = EAXFXSLOTFLAGS_ENVIRONMENT; + +constexpr auto EAX50FXSLOT_DEFAULTFLAGS = + EAXFXSLOTFLAGS_ENVIRONMENT | + EAXFXSLOTFLAGS_UPMIX; // ignored for reverb; + +struct EAX40FXSLOTPROPERTIES +{ + GUID guidLoadEffect; + long lVolume; + long lLock; + unsigned long ulFlags; +}; // EAX40FXSLOTPROPERTIES + +struct EAX50FXSLOTPROPERTIES : + public EAX40FXSLOTPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; +}; // EAX50FXSLOTPROPERTIES + +bool operator==( + const EAX40FXSLOTPROPERTIES& lhs, + const EAX40FXSLOTPROPERTIES& rhs) noexcept; + +bool operator==( + const EAX50FXSLOTPROPERTIES& lhs, + const EAX50FXSLOTPROPERTIES& rhs) noexcept; + +extern "C" const GUID EAXPROPERTYID_EAX40_Source; + +extern "C" const GUID EAXPROPERTYID_EAX50_Source; + +// Source object properties +enum EAXSOURCE_PROPERTY : + unsigned int +{ + // EAX30 + + EAXSOURCE_NONE, + EAXSOURCE_ALLPARAMETERS, + EAXSOURCE_OBSTRUCTIONPARAMETERS, + EAXSOURCE_OCCLUSIONPARAMETERS, + EAXSOURCE_EXCLUSIONPARAMETERS, + EAXSOURCE_DIRECT, + EAXSOURCE_DIRECTHF, + EAXSOURCE_ROOM, + EAXSOURCE_ROOMHF, + EAXSOURCE_OBSTRUCTION, + EAXSOURCE_OBSTRUCTIONLFRATIO, + EAXSOURCE_OCCLUSION, + EAXSOURCE_OCCLUSIONLFRATIO, + EAXSOURCE_OCCLUSIONROOMRATIO, + EAXSOURCE_OCCLUSIONDIRECTRATIO, + EAXSOURCE_EXCLUSION, + EAXSOURCE_EXCLUSIONLFRATIO, + EAXSOURCE_OUTSIDEVOLUMEHF, + EAXSOURCE_DOPPLERFACTOR, + EAXSOURCE_ROLLOFFFACTOR, + EAXSOURCE_ROOMROLLOFFFACTOR, + EAXSOURCE_AIRABSORPTIONFACTOR, + EAXSOURCE_FLAGS, + + // EAX40 + + EAXSOURCE_SENDPARAMETERS, + EAXSOURCE_ALLSENDPARAMETERS, + EAXSOURCE_OCCLUSIONSENDPARAMETERS, + EAXSOURCE_EXCLUSIONSENDPARAMETERS, + EAXSOURCE_ACTIVEFXSLOTID, + + // EAX50 + + EAXSOURCE_MACROFXFACTOR, + EAXSOURCE_SPEAKERLEVELS, + EAXSOURCE_ALL2DPARAMETERS, +}; // EAXSOURCE_PROPERTY + + +constexpr auto EAXSOURCEFLAGS_DIRECTHFAUTO = 0x00000001UL; // relates to EAXSOURCE_DIRECTHF +constexpr auto EAXSOURCEFLAGS_ROOMAUTO = 0x00000002UL; // relates to EAXSOURCE_ROOM +constexpr auto EAXSOURCEFLAGS_ROOMHFAUTO = 0x00000004UL; // relates to EAXSOURCE_ROOMHF +// EAX50 +constexpr auto EAXSOURCEFLAGS_3DELEVATIONFILTER = 0x00000008UL; +// EAX50 +constexpr auto EAXSOURCEFLAGS_UPMIX = 0x00000010UL; +// EAX50 +constexpr auto EAXSOURCEFLAGS_APPLYSPEAKERLEVELS = 0x00000020UL; + +constexpr auto EAX20SOURCEFLAGS_RESERVED = 0xFFFFFFF8UL; // reserved future use +constexpr auto EAX50SOURCEFLAGS_RESERVED = 0xFFFFFFC0UL; // reserved future use + + +constexpr auto EAXSOURCE_MINSEND = -10'000L; +constexpr auto EAXSOURCE_MAXSEND = 0L; +constexpr auto EAXSOURCE_DEFAULTSEND = 0L; + +constexpr auto EAXSOURCE_MINSENDHF = -10'000L; +constexpr auto EAXSOURCE_MAXSENDHF = 0L; +constexpr auto EAXSOURCE_DEFAULTSENDHF = 0L; + +constexpr auto EAXSOURCE_MINDIRECT = -10'000L; +constexpr auto EAXSOURCE_MAXDIRECT = 1'000L; +constexpr auto EAXSOURCE_DEFAULTDIRECT = 0L; + +constexpr auto EAXSOURCE_MINDIRECTHF = -10'000L; +constexpr auto EAXSOURCE_MAXDIRECTHF = 0L; +constexpr auto EAXSOURCE_DEFAULTDIRECTHF = 0L; + +constexpr auto EAXSOURCE_MINROOM = -10'000L; +constexpr auto EAXSOURCE_MAXROOM = 1'000L; +constexpr auto EAXSOURCE_DEFAULTROOM = 0L; + +constexpr auto EAXSOURCE_MINROOMHF = -10'000L; +constexpr auto EAXSOURCE_MAXROOMHF = 0L; +constexpr auto EAXSOURCE_DEFAULTROOMHF = 0L; + +constexpr auto EAXSOURCE_MINOBSTRUCTION = -10'000L; +constexpr auto EAXSOURCE_MAXOBSTRUCTION = 0L; +constexpr auto EAXSOURCE_DEFAULTOBSTRUCTION = 0L; + +constexpr auto EAXSOURCE_MINOBSTRUCTIONLFRATIO = 0.0F; +constexpr auto EAXSOURCE_MAXOBSTRUCTIONLFRATIO = 1.0F; +constexpr auto EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO = 0.0F; + +constexpr auto EAXSOURCE_MINOCCLUSION = -10'000L; +constexpr auto EAXSOURCE_MAXOCCLUSION = 0L; +constexpr auto EAXSOURCE_DEFAULTOCCLUSION = 0L; + +constexpr auto EAXSOURCE_MINOCCLUSIONLFRATIO = 0.0F; +constexpr auto EAXSOURCE_MAXOCCLUSIONLFRATIO = 1.0F; +constexpr auto EAXSOURCE_DEFAULTOCCLUSIONLFRATIO = 0.25F; + +constexpr auto EAXSOURCE_MINOCCLUSIONROOMRATIO = 0.0F; +constexpr auto EAXSOURCE_MAXOCCLUSIONROOMRATIO = 10.0F; +constexpr auto EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO = 1.5F; + +constexpr auto EAXSOURCE_MINOCCLUSIONDIRECTRATIO = 0.0F; +constexpr auto EAXSOURCE_MAXOCCLUSIONDIRECTRATIO = 10.0F; +constexpr auto EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO = 1.0F; + +constexpr auto EAXSOURCE_MINEXCLUSION = -10'000L; +constexpr auto EAXSOURCE_MAXEXCLUSION = 0L; +constexpr auto EAXSOURCE_DEFAULTEXCLUSION = 0L; + +constexpr auto EAXSOURCE_MINEXCLUSIONLFRATIO = 0.0F; +constexpr auto EAXSOURCE_MAXEXCLUSIONLFRATIO = 1.0F; +constexpr auto EAXSOURCE_DEFAULTEXCLUSIONLFRATIO = 1.0F; + +constexpr auto EAXSOURCE_MINOUTSIDEVOLUMEHF = -10'000L; +constexpr auto EAXSOURCE_MAXOUTSIDEVOLUMEHF = 0L; +constexpr auto EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF = 0L; + +constexpr auto EAXSOURCE_MINDOPPLERFACTOR = 0.0F; +constexpr auto EAXSOURCE_MAXDOPPLERFACTOR = 10.0F; +constexpr auto EAXSOURCE_DEFAULTDOPPLERFACTOR = 1.0F; + +constexpr auto EAXSOURCE_MINROLLOFFFACTOR = 0.0F; +constexpr auto EAXSOURCE_MAXROLLOFFFACTOR = 10.0F; +constexpr auto EAXSOURCE_DEFAULTROLLOFFFACTOR = 0.0F; + +constexpr auto EAXSOURCE_MINROOMROLLOFFFACTOR = 0.0F; +constexpr auto EAXSOURCE_MAXROOMROLLOFFFACTOR = 10.0F; +constexpr auto EAXSOURCE_DEFAULTROOMROLLOFFFACTOR = 0.0F; + +constexpr auto EAXSOURCE_MINAIRABSORPTIONFACTOR = 0.0F; +constexpr auto EAXSOURCE_MAXAIRABSORPTIONFACTOR = 10.0F; +constexpr auto EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR = 0.0F; + +// EAX50 + +constexpr auto EAXSOURCE_MINMACROFXFACTOR = 0.0F; +constexpr auto EAXSOURCE_MAXMACROFXFACTOR = 1.0F; +constexpr auto EAXSOURCE_DEFAULTMACROFXFACTOR = 1.0F; + +// EAX50 + +constexpr auto EAXSOURCE_MINSPEAKERLEVEL = -10'000L; +constexpr auto EAXSOURCE_MAXSPEAKERLEVEL = 0L; +constexpr auto EAXSOURCE_DEFAULTSPEAKERLEVEL = -10'000L; + +constexpr auto EAXSOURCE_DEFAULTFLAGS = + EAXSOURCEFLAGS_DIRECTHFAUTO | + EAXSOURCEFLAGS_ROOMAUTO | + EAXSOURCEFLAGS_ROOMHFAUTO; + +enum : + long +{ + EAXSPEAKER_FRONT_LEFT = 1, + EAXSPEAKER_FRONT_CENTER = 2, + EAXSPEAKER_FRONT_RIGHT = 3, + EAXSPEAKER_SIDE_RIGHT = 4, + EAXSPEAKER_REAR_RIGHT = 5, + EAXSPEAKER_REAR_CENTER = 6, + EAXSPEAKER_REAR_LEFT = 7, + EAXSPEAKER_SIDE_LEFT = 8, + EAXSPEAKER_LOW_FREQUENCY = 9 +}; + +// EAXSOURCEFLAGS_DIRECTHFAUTO, EAXSOURCEFLAGS_ROOMAUTO and EAXSOURCEFLAGS_ROOMHFAUTO are ignored for 2D sources +// EAXSOURCEFLAGS_UPMIX is ignored for 3D sources +constexpr auto EAX50SOURCE_DEFAULTFLAGS = + EAXSOURCEFLAGS_DIRECTHFAUTO | + EAXSOURCEFLAGS_ROOMAUTO | + EAXSOURCEFLAGS_ROOMHFAUTO | + EAXSOURCEFLAGS_UPMIX; + +struct EAX30SOURCEPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // relative occlusion control for room effect + float flOcclusionDirectRatio; // relative occlusion control for direct path + long lExclusion; // main exlusion control (attenuation at high frequencies) + float flExclusionLFRatio; // exclusion low-frequency level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flDopplerFactor; // like DS3D flDopplerFactor but per source + float flRolloffFactor; // like DS3D flRolloffFactor but per source + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flAirAbsorptionFactor; // multiplies EAXREVERB_AIRABSORPTIONHF + unsigned long ulFlags; // modifies the behavior of properties +}; // EAX30SOURCEPROPERTIES + +struct EAX50SOURCEPROPERTIES : + public EAX30SOURCEPROPERTIES +{ + float flMacroFXFactor; +}; // EAX50SOURCEPROPERTIES + +struct EAXSOURCEALLSENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lSend; // send level (at low and mid frequencies) + long lSendHF; // relative send level at high frequencies + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; + long lExclusion; + float flExclusionLFRatio; +}; // EAXSOURCEALLSENDPROPERTIES + +struct EAXSOURCE2DPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + unsigned long ulFlags; // modifies the behavior of properties +}; // EAXSOURCE2DPROPERTIES + +struct EAXSPEAKERLEVELPROPERTIES +{ + long lSpeakerID; + long lLevel; +}; // EAXSPEAKERLEVELPROPERTIES + +struct EAX40ACTIVEFXSLOTS +{ + GUID guidActiveFXSlots[EAX40_MAX_ACTIVE_FXSLOTS]; +}; // EAX40ACTIVEFXSLOTS + +struct EAX50ACTIVEFXSLOTS +{ + GUID guidActiveFXSlots[EAX50_MAX_ACTIVE_FXSLOTS]; +}; // EAX50ACTIVEFXSLOTS + +bool operator==( + const EAX50ACTIVEFXSLOTS& lhs, + const EAX50ACTIVEFXSLOTS& rhs) noexcept; + +bool operator!=( + const EAX50ACTIVEFXSLOTS& lhs, + const EAX50ACTIVEFXSLOTS& rhs) noexcept; + +// Use this structure for EAXSOURCE_OBSTRUCTIONPARAMETERS property. +struct EAXOBSTRUCTIONPROPERTIES +{ + long lObstruction; + float flObstructionLFRatio; +}; // EAXOBSTRUCTIONPROPERTIES + +// Use this structure for EAXSOURCE_OCCLUSIONPARAMETERS property. +struct EAXOCCLUSIONPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +}; // EAXOCCLUSIONPROPERTIES + +// Use this structure for EAXSOURCE_EXCLUSIONPARAMETERS property. +struct EAXEXCLUSIONPROPERTIES +{ + long lExclusion; + float flExclusionLFRatio; +}; // EAXEXCLUSIONPROPERTIES + +// Use this structure for EAXSOURCE_SENDPARAMETERS properties. +struct EAXSOURCESENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lSend; + long lSendHF; +}; // EAXSOURCESENDPROPERTIES + +// Use this structure for EAXSOURCE_OCCLUSIONSENDPARAMETERS +struct EAXSOURCEOCCLUSIONSENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +}; // EAXSOURCEOCCLUSIONSENDPROPERTIES + +// Use this structure for EAXSOURCE_EXCLUSIONSENDPARAMETERS +struct EAXSOURCEEXCLUSIONSENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lExclusion; + float flExclusionLFRatio; +}; // EAXSOURCEEXCLUSIONSENDPROPERTIES + +extern const EAX50ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID; + +extern const EAX50ACTIVEFXSLOTS EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID; + +extern const EAX50ACTIVEFXSLOTS EAX50SOURCE_2DDEFAULTACTIVEFXSLOTID; + + +// EAX Reverb Effect + +extern "C" const GUID EAX_REVERB_EFFECT; + +// Reverb effect properties +enum EAXREVERB_PROPERTY : + unsigned int +{ + EAXREVERB_NONE, + EAXREVERB_ALLPARAMETERS, + EAXREVERB_ENVIRONMENT, + EAXREVERB_ENVIRONMENTSIZE, + EAXREVERB_ENVIRONMENTDIFFUSION, + EAXREVERB_ROOM, + EAXREVERB_ROOMHF, + EAXREVERB_ROOMLF, + EAXREVERB_DECAYTIME, + EAXREVERB_DECAYHFRATIO, + EAXREVERB_DECAYLFRATIO, + EAXREVERB_REFLECTIONS, + EAXREVERB_REFLECTIONSDELAY, + EAXREVERB_REFLECTIONSPAN, + EAXREVERB_REVERB, + EAXREVERB_REVERBDELAY, + EAXREVERB_REVERBPAN, + EAXREVERB_ECHOTIME, + EAXREVERB_ECHODEPTH, + EAXREVERB_MODULATIONTIME, + EAXREVERB_MODULATIONDEPTH, + EAXREVERB_AIRABSORPTIONHF, + EAXREVERB_HFREFERENCE, + EAXREVERB_LFREFERENCE, + EAXREVERB_ROOMROLLOFFFACTOR, + EAXREVERB_FLAGS, +}; // EAXREVERB_PROPERTY + +// used by EAXREVERB_ENVIRONMENT +enum : + unsigned long +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + // EAX30 + EAX_ENVIRONMENT_UNDEFINED, + + EAX_ENVIRONMENT_COUNT, +}; + + +// reverberation decay time +constexpr auto EAXREVERBFLAGS_DECAYTIMESCALE = 0x00000001UL; + +// reflection level +constexpr auto EAXREVERBFLAGS_REFLECTIONSSCALE = 0x00000002UL; + +// initial reflection delay time +constexpr auto EAXREVERBFLAGS_REFLECTIONSDELAYSCALE = 0x00000004UL; + +// reflections level +constexpr auto EAXREVERBFLAGS_REVERBSCALE = 0x00000008UL; + +// late reverberation delay time +constexpr auto EAXREVERBFLAGS_REVERBDELAYSCALE = 0x00000010UL; + +// echo time +// EAX30+ +constexpr auto EAXREVERBFLAGS_ECHOTIMESCALE = 0x00000040UL; + +// modulation time +// EAX30+ +constexpr auto EAXREVERBFLAGS_MODULATIONTIMESCALE = 0x00000080UL; + +// This flag limits high-frequency decay time according to air absorption. +constexpr auto EAXREVERBFLAGS_DECAYHFLIMIT = 0x00000020UL; + +constexpr auto EAXREVERBFLAGS_RESERVED = 0xFFFFFF00UL; // reserved future use + + +struct EAXREVERBPROPERTIES +{ + unsigned long ulEnvironment; // sets all reverb properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + long lRoom; // room effect level (at mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lRoomLF; // relative room effect level at low frequencies + float flDecayTime; // reverberation decay time at mid frequencies + float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio + float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + EAXVECTOR vReflectionsPan; // early reflections panning vector + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + EAXVECTOR vReverbPan; // late reverberation panning vector + float flEchoTime; // echo time + float flEchoDepth; // echo depth + float flModulationTime; // modulation time + float flModulationDepth; // modulation depth + float flAirAbsorptionHF; // change in level per meter at high frequencies + float flHFReference; // reference high frequency + float flLFReference; // reference low frequency + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + unsigned long ulFlags; // modifies the behavior of properties +}; // EAXREVERBPROPERTIES + +bool operator==( + const EAXREVERBPROPERTIES& lhs, + const EAXREVERBPROPERTIES& rhs) noexcept; + +bool operator!=( + const EAXREVERBPROPERTIES& lhs, + const EAXREVERBPROPERTIES& rhs) noexcept; + + +constexpr auto EAXREVERB_MINENVIRONMENT = 0UL; +constexpr auto EAX20REVERB_MAXENVIRONMENT = EAX_ENVIRONMENT_COUNT - 2UL; +constexpr auto EAX30REVERB_MAXENVIRONMENT = EAX_ENVIRONMENT_COUNT - 1UL; +constexpr auto EAXREVERB_DEFAULTENVIRONMENT = EAX_ENVIRONMENT_GENERIC; + +constexpr auto EAXREVERB_MINENVIRONMENTSIZE = 1.0F; +constexpr auto EAXREVERB_MAXENVIRONMENTSIZE = 100.0F; +constexpr auto EAXREVERB_DEFAULTENVIRONMENTSIZE = 7.5F; + +constexpr auto EAXREVERB_MINENVIRONMENTDIFFUSION = 0.0F; +constexpr auto EAXREVERB_MAXENVIRONMENTDIFFUSION = 1.0F; +constexpr auto EAXREVERB_DEFAULTENVIRONMENTDIFFUSION = 1.0F; + +constexpr auto EAXREVERB_MINROOM = -10'000L; +constexpr auto EAXREVERB_MAXROOM = 0L; +constexpr auto EAXREVERB_DEFAULTROOM = -1'000L; + +constexpr auto EAXREVERB_MINROOMHF = -10'000L; +constexpr auto EAXREVERB_MAXROOMHF = 0L; +constexpr auto EAXREVERB_DEFAULTROOMHF = -100L; + +constexpr auto EAXREVERB_MINROOMLF = -10'000L; +constexpr auto EAXREVERB_MAXROOMLF = 0L; +constexpr auto EAXREVERB_DEFAULTROOMLF = 0L; + +constexpr auto EAXREVERB_MINDECAYTIME = 0.1F; +constexpr auto EAXREVERB_MAXDECAYTIME = 20.0F; +constexpr auto EAXREVERB_DEFAULTDECAYTIME = 1.49F; + +constexpr auto EAXREVERB_MINDECAYHFRATIO = 0.1F; +constexpr auto EAXREVERB_MAXDECAYHFRATIO = 2.0F; +constexpr auto EAXREVERB_DEFAULTDECAYHFRATIO = 0.83F; + +constexpr auto EAXREVERB_MINDECAYLFRATIO = 0.1F; +constexpr auto EAXREVERB_MAXDECAYLFRATIO = 2.0F; +constexpr auto EAXREVERB_DEFAULTDECAYLFRATIO = 1.0F; + +constexpr auto EAXREVERB_MINREFLECTIONS = -10'000L; +constexpr auto EAXREVERB_MAXREFLECTIONS = 1'000L; +constexpr auto EAXREVERB_DEFAULTREFLECTIONS = -2'602L; + +constexpr auto EAXREVERB_MINREFLECTIONSDELAY = 0.0F; +constexpr auto EAXREVERB_MAXREFLECTIONSDELAY = 0.3F; +constexpr auto EAXREVERB_DEFAULTREFLECTIONSDELAY = 0.007F; + +constexpr auto EAXREVERB_DEFAULTREFLECTIONSPAN = EAXVECTOR{0.0F, 0.0F, 0.0F}; + +constexpr auto EAXREVERB_MINREVERB = -10'000L; +constexpr auto EAXREVERB_MAXREVERB = 2'000L; +constexpr auto EAXREVERB_DEFAULTREVERB = 200L; + +constexpr auto EAXREVERB_MINREVERBDELAY = 0.0F; +constexpr auto EAXREVERB_MAXREVERBDELAY = 0.1F; +constexpr auto EAXREVERB_DEFAULTREVERBDELAY = 0.011F; + +constexpr auto EAXREVERB_DEFAULTREVERBPAN = EAXVECTOR{0.0F, 0.0F, 0.0F}; + +constexpr auto EAXREVERB_MINECHOTIME = 0.075F; +constexpr auto EAXREVERB_MAXECHOTIME = 0.25F; +constexpr auto EAXREVERB_DEFAULTECHOTIME = 0.25F; + +constexpr auto EAXREVERB_MINECHODEPTH = 0.0F; +constexpr auto EAXREVERB_MAXECHODEPTH = 1.0F; +constexpr auto EAXREVERB_DEFAULTECHODEPTH = 0.0F; + +constexpr auto EAXREVERB_MINMODULATIONTIME = 0.04F; +constexpr auto EAXREVERB_MAXMODULATIONTIME = 4.0F; +constexpr auto EAXREVERB_DEFAULTMODULATIONTIME = 0.25F; + +constexpr auto EAXREVERB_MINMODULATIONDEPTH = 0.0F; +constexpr auto EAXREVERB_MAXMODULATIONDEPTH = 1.0F; +constexpr auto EAXREVERB_DEFAULTMODULATIONDEPTH = 0.0F; + +constexpr auto EAXREVERB_MINAIRABSORPTIONHF = -100.0F; +constexpr auto EAXREVERB_MAXAIRABSORPTIONHF = 0.0F; +constexpr auto EAXREVERB_DEFAULTAIRABSORPTIONHF = -5.0F; + +constexpr auto EAXREVERB_MINHFREFERENCE = 1'000.0F; +constexpr auto EAXREVERB_MAXHFREFERENCE = 20'000.0F; +constexpr auto EAXREVERB_DEFAULTHFREFERENCE = 5'000.0F; + +constexpr auto EAXREVERB_MINLFREFERENCE = 20.0F; +constexpr auto EAXREVERB_MAXLFREFERENCE = 1'000.0F; +constexpr auto EAXREVERB_DEFAULTLFREFERENCE = 250.0F; + +constexpr auto EAXREVERB_MINROOMROLLOFFFACTOR = 0.0F; +constexpr auto EAXREVERB_MAXROOMROLLOFFFACTOR = 10.0F; +constexpr auto EAXREVERB_DEFAULTROOMROLLOFFFACTOR = 0.0F; + +constexpr auto EAXREVERB_DEFAULTFLAGS = + EAXREVERBFLAGS_DECAYTIMESCALE | + EAXREVERBFLAGS_REFLECTIONSSCALE | + EAXREVERBFLAGS_REFLECTIONSDELAYSCALE | + EAXREVERBFLAGS_REVERBSCALE | + EAXREVERBFLAGS_REVERBDELAYSCALE | + EAXREVERBFLAGS_DECAYHFLIMIT; + + +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_GENERIC; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_PADDEDCEL; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_ROOM; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_BATHROOM; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_LIVINGROOM; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_STONEROOM; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_AUDITORIUM; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_CONCERTHAL; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_CAVE; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_ARENA; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_HANGAR; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_CARPETTEDHALLWAY; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_HALLWAY; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_STONECORRIDOR; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_ALLEY; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_FOREST; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_CITY; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_MOUNTAINS; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_QUARRY; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_PLAIN; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_PARKINGLOT; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_SEWERPIPE; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_UNDERWATER; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_DRUGGED; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_DIZZY; +extern const EAXREVERBPROPERTIES EAXREVERB_PRESET_PSYCHOTIC; + + +using EaxReverbPresets = std::array; +extern const EaxReverbPresets EAXREVERB_PRESETS; + + +// AGC Compressor Effect + +extern "C" const GUID EAX_AGCCOMPRESSOR_EFFECT; + +enum EAXAGCCOMPRESSOR_PROPERTY : + unsigned int +{ + EAXAGCCOMPRESSOR_NONE, + EAXAGCCOMPRESSOR_ALLPARAMETERS, + EAXAGCCOMPRESSOR_ONOFF, +}; // EAXAGCCOMPRESSOR_PROPERTY + +struct EAXAGCCOMPRESSORPROPERTIES +{ + unsigned long ulOnOff; // Switch Compressor on or off +}; // EAXAGCCOMPRESSORPROPERTIES + + +constexpr auto EAXAGCCOMPRESSOR_MINONOFF = 0UL; +constexpr auto EAXAGCCOMPRESSOR_MAXONOFF = 1UL; +constexpr auto EAXAGCCOMPRESSOR_DEFAULTONOFF = EAXAGCCOMPRESSOR_MAXONOFF; + + +// Autowah Effect + +extern "C" const GUID EAX_AUTOWAH_EFFECT; + +enum EAXAUTOWAH_PROPERTY : + unsigned int +{ + EAXAUTOWAH_NONE, + EAXAUTOWAH_ALLPARAMETERS, + EAXAUTOWAH_ATTACKTIME, + EAXAUTOWAH_RELEASETIME, + EAXAUTOWAH_RESONANCE, + EAXAUTOWAH_PEAKLEVEL, +}; // EAXAUTOWAH_PROPERTY + +struct EAXAUTOWAHPROPERTIES +{ + float flAttackTime; // Attack time (seconds) + float flReleaseTime; // Release time (seconds) + long lResonance; // Resonance (mB) + long lPeakLevel; // Peak level (mB) +}; // EAXAUTOWAHPROPERTIES + + +constexpr auto EAXAUTOWAH_MINATTACKTIME = 0.0001F; +constexpr auto EAXAUTOWAH_MAXATTACKTIME = 1.0F; +constexpr auto EAXAUTOWAH_DEFAULTATTACKTIME = 0.06F; + +constexpr auto EAXAUTOWAH_MINRELEASETIME = 0.0001F; +constexpr auto EAXAUTOWAH_MAXRELEASETIME = 1.0F; +constexpr auto EAXAUTOWAH_DEFAULTRELEASETIME = 0.06F; + +constexpr auto EAXAUTOWAH_MINRESONANCE = 600L; +constexpr auto EAXAUTOWAH_MAXRESONANCE = 6000L; +constexpr auto EAXAUTOWAH_DEFAULTRESONANCE = 6000L; + +constexpr auto EAXAUTOWAH_MINPEAKLEVEL = -9000L; +constexpr auto EAXAUTOWAH_MAXPEAKLEVEL = 9000L; +constexpr auto EAXAUTOWAH_DEFAULTPEAKLEVEL = 2100L; + + +// Chorus Effect + +extern "C" const GUID EAX_CHORUS_EFFECT; + + +enum EAXCHORUS_PROPERTY : + unsigned int +{ + EAXCHORUS_NONE, + EAXCHORUS_ALLPARAMETERS, + EAXCHORUS_WAVEFORM, + EAXCHORUS_PHASE, + EAXCHORUS_RATE, + EAXCHORUS_DEPTH, + EAXCHORUS_FEEDBACK, + EAXCHORUS_DELAY, +}; // EAXCHORUS_PROPERTY + +enum : + unsigned long +{ + EAX_CHORUS_SINUSOID, + EAX_CHORUS_TRIANGLE, +}; + +struct EAXCHORUSPROPERTIES +{ + unsigned long ulWaveform; // Waveform selector - see enum above + long lPhase; // Phase (Degrees) + float flRate; // Rate (Hz) + float flDepth; // Depth (0 to 1) + float flFeedback; // Feedback (-1 to 1) + float flDelay; // Delay (seconds) +}; // EAXCHORUSPROPERTIES + + +constexpr auto EAXCHORUS_MINWAVEFORM = 0UL; +constexpr auto EAXCHORUS_MAXWAVEFORM = 1UL; +constexpr auto EAXCHORUS_DEFAULTWAVEFORM = 1UL; + +constexpr auto EAXCHORUS_MINPHASE = -180L; +constexpr auto EAXCHORUS_MAXPHASE = 180L; +constexpr auto EAXCHORUS_DEFAULTPHASE = 90L; + +constexpr auto EAXCHORUS_MINRATE = 0.0F; +constexpr auto EAXCHORUS_MAXRATE = 10.0F; +constexpr auto EAXCHORUS_DEFAULTRATE = 1.1F; + +constexpr auto EAXCHORUS_MINDEPTH = 0.0F; +constexpr auto EAXCHORUS_MAXDEPTH = 1.0F; +constexpr auto EAXCHORUS_DEFAULTDEPTH = 0.1F; + +constexpr auto EAXCHORUS_MINFEEDBACK = -1.0F; +constexpr auto EAXCHORUS_MAXFEEDBACK = 1.0F; +constexpr auto EAXCHORUS_DEFAULTFEEDBACK = 0.25F; + +constexpr auto EAXCHORUS_MINDELAY = 0.0002F; +constexpr auto EAXCHORUS_MAXDELAY = 0.016F; +constexpr auto EAXCHORUS_DEFAULTDELAY = 0.016F; + + +// Distortion Effect + +extern "C" const GUID EAX_DISTORTION_EFFECT; + +enum EAXDISTORTION_PROPERTY : + unsigned int +{ + EAXDISTORTION_NONE, + EAXDISTORTION_ALLPARAMETERS, + EAXDISTORTION_EDGE, + EAXDISTORTION_GAIN, + EAXDISTORTION_LOWPASSCUTOFF, + EAXDISTORTION_EQCENTER, + EAXDISTORTION_EQBANDWIDTH, +}; // EAXDISTORTION_PROPERTY + + +struct EAXDISTORTIONPROPERTIES +{ + float flEdge; // Controls the shape of the distortion (0 to 1) + long lGain; // Controls the post distortion gain (mB) + float flLowPassCutOff; // Controls the cut-off of the filter pre-distortion (Hz) + float flEQCenter; // Controls the center frequency of the EQ post-distortion (Hz) + float flEQBandwidth; // Controls the bandwidth of the EQ post-distortion (Hz) +}; // EAXDISTORTIONPROPERTIES + + +constexpr auto EAXDISTORTION_MINEDGE = 0.0F; +constexpr auto EAXDISTORTION_MAXEDGE = 1.0F; +constexpr auto EAXDISTORTION_DEFAULTEDGE = 0.2F; + +constexpr auto EAXDISTORTION_MINGAIN = -6000L; +constexpr auto EAXDISTORTION_MAXGAIN = 0L; +constexpr auto EAXDISTORTION_DEFAULTGAIN = -2600L; + +constexpr auto EAXDISTORTION_MINLOWPASSCUTOFF = 80.0F; +constexpr auto EAXDISTORTION_MAXLOWPASSCUTOFF = 24000.0F; +constexpr auto EAXDISTORTION_DEFAULTLOWPASSCUTOFF = 8000.0F; + +constexpr auto EAXDISTORTION_MINEQCENTER = 80.0F; +constexpr auto EAXDISTORTION_MAXEQCENTER = 24000.0F; +constexpr auto EAXDISTORTION_DEFAULTEQCENTER = 3600.0F; + +constexpr auto EAXDISTORTION_MINEQBANDWIDTH = 80.0F; +constexpr auto EAXDISTORTION_MAXEQBANDWIDTH = 24000.0F; +constexpr auto EAXDISTORTION_DEFAULTEQBANDWIDTH = 3600.0F; + + +// Echo Effect + +extern "C" const GUID EAX_ECHO_EFFECT; + + +enum EAXECHO_PROPERTY : + unsigned int +{ + EAXECHO_NONE, + EAXECHO_ALLPARAMETERS, + EAXECHO_DELAY, + EAXECHO_LRDELAY, + EAXECHO_DAMPING, + EAXECHO_FEEDBACK, + EAXECHO_SPREAD, +}; // EAXECHO_PROPERTY + + +struct EAXECHOPROPERTIES +{ + float flDelay; // Controls the initial delay time (seconds) + float flLRDelay; // Controls the delay time between the first and second taps (seconds) + float flDamping; // Controls a low-pass filter that dampens the echoes (0 to 1) + float flFeedback; // Controls the duration of echo repetition (0 to 1) + float flSpread; // Controls the left-right spread of the echoes +}; // EAXECHOPROPERTIES + + +constexpr auto EAXECHO_MINDAMPING = 0.0F; +constexpr auto EAXECHO_MAXDAMPING = 0.99F; +constexpr auto EAXECHO_DEFAULTDAMPING = 0.5F; + +constexpr auto EAXECHO_MINDELAY = 0.002F; +constexpr auto EAXECHO_MAXDELAY = 0.207F; +constexpr auto EAXECHO_DEFAULTDELAY = 0.1F; + +constexpr auto EAXECHO_MINLRDELAY = 0.0F; +constexpr auto EAXECHO_MAXLRDELAY = 0.404F; +constexpr auto EAXECHO_DEFAULTLRDELAY = 0.1F; + +constexpr auto EAXECHO_MINFEEDBACK = 0.0F; +constexpr auto EAXECHO_MAXFEEDBACK = 1.0F; +constexpr auto EAXECHO_DEFAULTFEEDBACK = 0.5F; + +constexpr auto EAXECHO_MINSPREAD = -1.0F; +constexpr auto EAXECHO_MAXSPREAD = 1.0F; +constexpr auto EAXECHO_DEFAULTSPREAD = -1.0F; + + +// Equalizer Effect + +extern "C" const GUID EAX_EQUALIZER_EFFECT; + + +enum EAXEQUALIZER_PROPERTY : + unsigned int +{ + EAXEQUALIZER_NONE, + EAXEQUALIZER_ALLPARAMETERS, + EAXEQUALIZER_LOWGAIN, + EAXEQUALIZER_LOWCUTOFF, + EAXEQUALIZER_MID1GAIN, + EAXEQUALIZER_MID1CENTER, + EAXEQUALIZER_MID1WIDTH, + EAXEQUALIZER_MID2GAIN, + EAXEQUALIZER_MID2CENTER, + EAXEQUALIZER_MID2WIDTH, + EAXEQUALIZER_HIGHGAIN, + EAXEQUALIZER_HIGHCUTOFF, +}; // EAXEQUALIZER_PROPERTY + + +struct EAXEQUALIZERPROPERTIES +{ + long lLowGain; // (mB) + float flLowCutOff; // (Hz) + long lMid1Gain; // (mB) + float flMid1Center; // (Hz) + float flMid1Width; // (octaves) + long lMid2Gain; // (mB) + float flMid2Center; // (Hz) + float flMid2Width; // (octaves) + long lHighGain; // (mB) + float flHighCutOff; // (Hz) +}; // EAXEQUALIZERPROPERTIES + + +constexpr auto EAXEQUALIZER_MINLOWGAIN = -1800L; +constexpr auto EAXEQUALIZER_MAXLOWGAIN = 1800L; +constexpr auto EAXEQUALIZER_DEFAULTLOWGAIN = 0L; + +constexpr auto EAXEQUALIZER_MINLOWCUTOFF = 50.0F; +constexpr auto EAXEQUALIZER_MAXLOWCUTOFF = 800.0F; +constexpr auto EAXEQUALIZER_DEFAULTLOWCUTOFF = 200.0F; + +constexpr auto EAXEQUALIZER_MINMID1GAIN = -1800L; +constexpr auto EAXEQUALIZER_MAXMID1GAIN = 1800L; +constexpr auto EAXEQUALIZER_DEFAULTMID1GAIN = 0L; + +constexpr auto EAXEQUALIZER_MINMID1CENTER = 200.0F; +constexpr auto EAXEQUALIZER_MAXMID1CENTER = 3000.0F; +constexpr auto EAXEQUALIZER_DEFAULTMID1CENTER = 500.0F; + +constexpr auto EAXEQUALIZER_MINMID1WIDTH = 0.01F; +constexpr auto EAXEQUALIZER_MAXMID1WIDTH = 1.0F; +constexpr auto EAXEQUALIZER_DEFAULTMID1WIDTH = 1.0F; + +constexpr auto EAXEQUALIZER_MINMID2GAIN = -1800L; +constexpr auto EAXEQUALIZER_MAXMID2GAIN = 1800L; +constexpr auto EAXEQUALIZER_DEFAULTMID2GAIN = 0L; + +constexpr auto EAXEQUALIZER_MINMID2CENTER = 1000.0F; +constexpr auto EAXEQUALIZER_MAXMID2CENTER = 8000.0F; +constexpr auto EAXEQUALIZER_DEFAULTMID2CENTER = 3000.0F; + +constexpr auto EAXEQUALIZER_MINMID2WIDTH = 0.01F; +constexpr auto EAXEQUALIZER_MAXMID2WIDTH = 1.0F; +constexpr auto EAXEQUALIZER_DEFAULTMID2WIDTH = 1.0F; + +constexpr auto EAXEQUALIZER_MINHIGHGAIN = -1800L; +constexpr auto EAXEQUALIZER_MAXHIGHGAIN = 1800L; +constexpr auto EAXEQUALIZER_DEFAULTHIGHGAIN = 0L; + +constexpr auto EAXEQUALIZER_MINHIGHCUTOFF = 4000.0F; +constexpr auto EAXEQUALIZER_MAXHIGHCUTOFF = 16000.0F; +constexpr auto EAXEQUALIZER_DEFAULTHIGHCUTOFF = 6000.0F; + + +// Flanger Effect + +extern "C" const GUID EAX_FLANGER_EFFECT; + +enum EAXFLANGER_PROPERTY : + unsigned int +{ + EAXFLANGER_NONE, + EAXFLANGER_ALLPARAMETERS, + EAXFLANGER_WAVEFORM, + EAXFLANGER_PHASE, + EAXFLANGER_RATE, + EAXFLANGER_DEPTH, + EAXFLANGER_FEEDBACK, + EAXFLANGER_DELAY, +}; // EAXFLANGER_PROPERTY + +enum : + unsigned long +{ + EAX_FLANGER_SINUSOID, + EAX_FLANGER_TRIANGLE, +}; + +struct EAXFLANGERPROPERTIES +{ + unsigned long ulWaveform; // Waveform selector - see enum above + long lPhase; // Phase (Degrees) + float flRate; // Rate (Hz) + float flDepth; // Depth (0 to 1) + float flFeedback; // Feedback (0 to 1) + float flDelay; // Delay (seconds) +}; // EAXFLANGERPROPERTIES + + +constexpr auto EAXFLANGER_MINWAVEFORM = 0UL; +constexpr auto EAXFLANGER_MAXWAVEFORM = 1UL; +constexpr auto EAXFLANGER_DEFAULTWAVEFORM = 1UL; + +constexpr auto EAXFLANGER_MINPHASE = -180L; +constexpr auto EAXFLANGER_MAXPHASE = 180L; +constexpr auto EAXFLANGER_DEFAULTPHASE = 0L; + +constexpr auto EAXFLANGER_MINRATE = 0.0F; +constexpr auto EAXFLANGER_MAXRATE = 10.0F; +constexpr auto EAXFLANGER_DEFAULTRATE = 0.27F; + +constexpr auto EAXFLANGER_MINDEPTH = 0.0F; +constexpr auto EAXFLANGER_MAXDEPTH = 1.0F; +constexpr auto EAXFLANGER_DEFAULTDEPTH = 1.0F; + +constexpr auto EAXFLANGER_MINFEEDBACK = -1.0F; +constexpr auto EAXFLANGER_MAXFEEDBACK = 1.0F; +constexpr auto EAXFLANGER_DEFAULTFEEDBACK = -0.5F; + +constexpr auto EAXFLANGER_MINDELAY = 0.0002F; +constexpr auto EAXFLANGER_MAXDELAY = 0.004F; +constexpr auto EAXFLANGER_DEFAULTDELAY = 0.002F; + + +// Frequency Shifter Effect + +extern "C" const GUID EAX_FREQUENCYSHIFTER_EFFECT; + +enum EAXFREQUENCYSHIFTER_PROPERTY : + unsigned int +{ + EAXFREQUENCYSHIFTER_NONE, + EAXFREQUENCYSHIFTER_ALLPARAMETERS, + EAXFREQUENCYSHIFTER_FREQUENCY, + EAXFREQUENCYSHIFTER_LEFTDIRECTION, + EAXFREQUENCYSHIFTER_RIGHTDIRECTION, +}; // EAXFREQUENCYSHIFTER_PROPERTY + +enum : + unsigned long +{ + EAX_FREQUENCYSHIFTER_DOWN, + EAX_FREQUENCYSHIFTER_UP, + EAX_FREQUENCYSHIFTER_OFF +}; + +struct EAXFREQUENCYSHIFTERPROPERTIES +{ + float flFrequency; // (Hz) + unsigned long ulLeftDirection; // see enum above + unsigned long ulRightDirection; // see enum above +}; // EAXFREQUENCYSHIFTERPROPERTIES + + +constexpr auto EAXFREQUENCYSHIFTER_MINFREQUENCY = 0.0F; +constexpr auto EAXFREQUENCYSHIFTER_MAXFREQUENCY = 24000.0F; +constexpr auto EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY = EAXFREQUENCYSHIFTER_MINFREQUENCY; + +constexpr auto EAXFREQUENCYSHIFTER_MINLEFTDIRECTION = 0UL; +constexpr auto EAXFREQUENCYSHIFTER_MAXLEFTDIRECTION = 2UL; +constexpr auto EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION = EAXFREQUENCYSHIFTER_MINLEFTDIRECTION; + +constexpr auto EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION = 0UL; +constexpr auto EAXFREQUENCYSHIFTER_MAXRIGHTDIRECTION = 2UL; +constexpr auto EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION = EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION; + + +// Vocal Morpher Effect + +extern "C" const GUID EAX_VOCALMORPHER_EFFECT; + +enum EAXVOCALMORPHER_PROPERTY : + unsigned int +{ + EAXVOCALMORPHER_NONE, + EAXVOCALMORPHER_ALLPARAMETERS, + EAXVOCALMORPHER_PHONEMEA, + EAXVOCALMORPHER_PHONEMEACOARSETUNING, + EAXVOCALMORPHER_PHONEMEB, + EAXVOCALMORPHER_PHONEMEBCOARSETUNING, + EAXVOCALMORPHER_WAVEFORM, + EAXVOCALMORPHER_RATE, +}; // EAXVOCALMORPHER_PROPERTY + +enum : + unsigned long +{ + A, + E, + I, + O, + U, + AA, + AE, + AH, + AO, + EH, + ER, + IH, + IY, + UH, + UW, + B, + D, + F, + G, + J, + K, + L, + M, + N, + P, + R, + S, + T, + V, + Z, +}; + +enum : + unsigned long +{ + EAX_VOCALMORPHER_SINUSOID, + EAX_VOCALMORPHER_TRIANGLE, + EAX_VOCALMORPHER_SAWTOOTH +}; + +// Use this structure for EAXVOCALMORPHER_ALLPARAMETERS +struct EAXVOCALMORPHERPROPERTIES +{ + unsigned long ulPhonemeA; // see enum above + long lPhonemeACoarseTuning; // (semitones) + unsigned long ulPhonemeB; // see enum above + long lPhonemeBCoarseTuning; // (semitones) + unsigned long ulWaveform; // Waveform selector - see enum above + float flRate; // (Hz) +}; // EAXVOCALMORPHERPROPERTIES + + +constexpr auto EAXVOCALMORPHER_MINPHONEMEA = 0UL; +constexpr auto EAXVOCALMORPHER_MAXPHONEMEA = 29UL; +constexpr auto EAXVOCALMORPHER_DEFAULTPHONEMEA = EAXVOCALMORPHER_MINPHONEMEA; + +constexpr auto EAXVOCALMORPHER_MINPHONEMEACOARSETUNING = -24L; +constexpr auto EAXVOCALMORPHER_MAXPHONEMEACOARSETUNING = 24L; +constexpr auto EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING = 0L; + +constexpr auto EAXVOCALMORPHER_MINPHONEMEB = 0UL; +constexpr auto EAXVOCALMORPHER_MAXPHONEMEB = 29UL; +constexpr auto EAXVOCALMORPHER_DEFAULTPHONEMEB = 10UL; + +constexpr auto EAXVOCALMORPHER_MINPHONEMEBCOARSETUNING = -24L; +constexpr auto EAXVOCALMORPHER_MAXPHONEMEBCOARSETUNING = 24L; +constexpr auto EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING = 0L; + +constexpr auto EAXVOCALMORPHER_MINWAVEFORM = 0UL; +constexpr auto EAXVOCALMORPHER_MAXWAVEFORM = 2UL; +constexpr auto EAXVOCALMORPHER_DEFAULTWAVEFORM = EAXVOCALMORPHER_MINWAVEFORM; + +constexpr auto EAXVOCALMORPHER_MINRATE = 0.0F; +constexpr auto EAXVOCALMORPHER_MAXRATE = 10.0F; +constexpr auto EAXVOCALMORPHER_DEFAULTRATE = 1.41F; + + +// Pitch Shifter Effect + +extern "C" const GUID EAX_PITCHSHIFTER_EFFECT; + +enum EAXPITCHSHIFTER_PROPERTY : + unsigned int +{ + EAXPITCHSHIFTER_NONE, + EAXPITCHSHIFTER_ALLPARAMETERS, + EAXPITCHSHIFTER_COARSETUNE, + EAXPITCHSHIFTER_FINETUNE, +}; // EAXPITCHSHIFTER_PROPERTY + +struct EAXPITCHSHIFTERPROPERTIES +{ + long lCoarseTune; // Amount of pitch shift (semitones) + long lFineTune; // Amount of pitch shift (cents) +}; // EAXPITCHSHIFTERPROPERTIES + + +constexpr auto EAXPITCHSHIFTER_MINCOARSETUNE = -12L; +constexpr auto EAXPITCHSHIFTER_MAXCOARSETUNE = 12L; +constexpr auto EAXPITCHSHIFTER_DEFAULTCOARSETUNE = 12L; + +constexpr auto EAXPITCHSHIFTER_MINFINETUNE = -50L; +constexpr auto EAXPITCHSHIFTER_MAXFINETUNE = 50L; +constexpr auto EAXPITCHSHIFTER_DEFAULTFINETUNE = 0L; + + +// Ring Modulator Effect + +extern "C" const GUID EAX_RINGMODULATOR_EFFECT; + +enum EAXRINGMODULATOR_PROPERTY : + unsigned int +{ + EAXRINGMODULATOR_NONE, + EAXRINGMODULATOR_ALLPARAMETERS, + EAXRINGMODULATOR_FREQUENCY, + EAXRINGMODULATOR_HIGHPASSCUTOFF, + EAXRINGMODULATOR_WAVEFORM, +}; // EAXRINGMODULATOR_PROPERTY + +enum : + unsigned long +{ + EAX_RINGMODULATOR_SINUSOID, + EAX_RINGMODULATOR_SAWTOOTH, + EAX_RINGMODULATOR_SQUARE, +}; + +// Use this structure for EAXRINGMODULATOR_ALLPARAMETERS +struct EAXRINGMODULATORPROPERTIES +{ + float flFrequency; // Frequency of modulation (Hz) + float flHighPassCutOff; // Cut-off frequency of high-pass filter (Hz) + unsigned long ulWaveform; // Waveform selector - see enum above +}; // EAXRINGMODULATORPROPERTIES + + +constexpr auto EAXRINGMODULATOR_MINFREQUENCY = 0.0F; +constexpr auto EAXRINGMODULATOR_MAXFREQUENCY = 8000.0F; +constexpr auto EAXRINGMODULATOR_DEFAULTFREQUENCY = 440.0F; + +constexpr auto EAXRINGMODULATOR_MINHIGHPASSCUTOFF = 0.0F; +constexpr auto EAXRINGMODULATOR_MAXHIGHPASSCUTOFF = 24000.0F; +constexpr auto EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF = 800.0F; + +constexpr auto EAXRINGMODULATOR_MINWAVEFORM = 0UL; +constexpr auto EAXRINGMODULATOR_MAXWAVEFORM = 2UL; +constexpr auto EAXRINGMODULATOR_DEFAULTWAVEFORM = EAXRINGMODULATOR_MINWAVEFORM; + + +using LPEAXSET = ALenum(AL_APIENTRY*)( + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size); + +using LPEAXGET = ALenum(AL_APIENTRY*)( + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size); + + +#endif // !EAX_API_INCLUDED diff --git a/al/eax_eax_call.cpp b/al/eax_eax_call.cpp new file mode 100644 index 00000000..e6967ded --- /dev/null +++ b/al/eax_eax_call.cpp @@ -0,0 +1,370 @@ +#include "al/eax_eax_call.h" + +#include "al/eax_exception.h" + + +namespace +{ + + +class EaxEaxCallException : + public EaxException +{ +public: + explicit EaxEaxCallException( + const char* message) + : + EaxException{"EAX_EAX_CALL", message} + { + } +}; // EaxEaxCallException + + +} // namespace + + +EaxEaxCall::EaxEaxCall( + bool is_get, + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size) +{ + if (!property_set_id) + { + fail("Null property set ID."); + } + + is_get_ = is_get; + + constexpr auto deferred_flag = 0x80000000U; + is_deferred_ = ((property_id & 0x80000000U) != 0); + + version_ = 0; + fx_slot_index_.reset(); + property_set_id_ = EaxEaxCallPropertySetId::none; + + property_set_guid_ = *property_set_id; + property_id_ = property_id & (~deferred_flag); + property_source_id_ = property_source_id; + property_buffer_ = property_buffer; + property_size_ = property_size; + + if (false) + { + } + else if (property_set_guid_ == EAXPROPERTYID_EAX40_Context) + { + version_ = 4; + property_set_id_ = EaxEaxCallPropertySetId::context; + } + else if (property_set_guid_ == EAXPROPERTYID_EAX50_Context) + { + version_ = 5; + property_set_id_ = EaxEaxCallPropertySetId::context; + } + else if (property_set_guid_ == DSPROPSETID_EAX20_ListenerProperties) + { + version_ = 2; + fx_slot_index_ = 0; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect; + property_id_ = convert_eax_v2_0_listener_property_id(property_id_); + } + else if (property_set_guid_ == DSPROPSETID_EAX30_ListenerProperties) + { + version_ = 3; + fx_slot_index_ = 0; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect; + } + else if (property_set_guid_ == EAXPROPERTYID_EAX40_FXSlot0) + { + version_ = 4; + fx_slot_index_ = 0; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid_ == EAXPROPERTYID_EAX50_FXSlot0) + { + version_ = 5; + fx_slot_index_ = 0; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid_ == EAXPROPERTYID_EAX40_FXSlot1) + { + version_ = 4; + fx_slot_index_ = 1; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid_ == EAXPROPERTYID_EAX50_FXSlot1) + { + version_ = 5; + fx_slot_index_ = 1; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid_ == EAXPROPERTYID_EAX40_FXSlot2) + { + version_ = 4; + fx_slot_index_ = 2; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid_ == EAXPROPERTYID_EAX50_FXSlot2) + { + version_ = 5; + fx_slot_index_ = 2; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid_ == EAXPROPERTYID_EAX40_FXSlot3) + { + version_ = 4; + fx_slot_index_ = 3; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid_ == EAXPROPERTYID_EAX50_FXSlot3) + { + version_ = 5; + fx_slot_index_ = 3; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid_ == DSPROPSETID_EAX20_BufferProperties) + { + version_ = 2; + property_set_id_ = EaxEaxCallPropertySetId::source; + property_id_ = convert_eax_v2_0_buffer_property_id(property_id_); + } + else if (property_set_guid_ == DSPROPSETID_EAX30_BufferProperties) + { + version_ = 3; + property_set_id_ = EaxEaxCallPropertySetId::source; + } + else if (property_set_guid_ == EAXPROPERTYID_EAX40_Source) + { + version_ = 4; + property_set_id_ = EaxEaxCallPropertySetId::source; + } + else if (property_set_guid_ == EAXPROPERTYID_EAX50_Source) + { + version_ = 5; + property_set_id_ = EaxEaxCallPropertySetId::source; + } + else + { + fail("Unsupported property set id."); + } + + if (version_ < 2 || version_ > 5) + { + fail("EAX version out of range."); + } + + if (is_deferred_) + { + if (is_get_) + { + fail("Getting deferred properties not supported."); + } + } + else + { + if (property_set_id_ != EaxEaxCallPropertySetId::fx_slot && + property_id_ != 0) + { + if (!property_buffer) + { + fail("Null property buffer."); + } + + if (property_size == 0) + { + fail("Empty property."); + } + } + } + + if (property_set_id_ == EaxEaxCallPropertySetId::source && + property_source_id_ == 0) + { + fail("Null AL source id."); + } + + if (property_set_id_ == EaxEaxCallPropertySetId::fx_slot) + { + if (property_id_ < EAXFXSLOT_NONE) + { + property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect; + } + } +} + +bool EaxEaxCall::is_get() const noexcept +{ + return is_get_; +} + +bool EaxEaxCall::is_deferred() const noexcept +{ + return is_deferred_; +} + +int EaxEaxCall::get_version() const noexcept +{ + return version_; +} + +EaxEaxCallPropertySetId EaxEaxCall::get_property_set_id() const noexcept +{ + return property_set_id_; +} + +ALuint EaxEaxCall::get_property_id() const noexcept +{ + return property_id_; +} + +ALuint EaxEaxCall::get_property_al_name() const noexcept +{ + return property_source_id_; +} + +EaxFxSlotIndex EaxEaxCall::get_fx_slot_index() const noexcept +{ + return fx_slot_index_; +} + +[[noreturn]] +void EaxEaxCall::fail( + const char* message) +{ + throw EaxEaxCallException{message}; +} + +ALuint EaxEaxCall::convert_eax_v2_0_listener_property_id( + ALuint property_id) +{ + switch (property_id) + { + case DSPROPERTY_EAX20LISTENER_NONE: + return EAXREVERB_NONE; + + case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS: + return EAXREVERB_ALLPARAMETERS; + + case DSPROPERTY_EAX20LISTENER_ROOM: + return EAXREVERB_ROOM; + + case DSPROPERTY_EAX20LISTENER_ROOMHF: + return EAXREVERB_ROOMHF; + + case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR: + return EAXREVERB_ROOMROLLOFFFACTOR; + + case DSPROPERTY_EAX20LISTENER_DECAYTIME: + return EAXREVERB_DECAYTIME; + + case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO: + return EAXREVERB_DECAYHFRATIO; + + case DSPROPERTY_EAX20LISTENER_REFLECTIONS: + return EAXREVERB_REFLECTIONS; + + case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY: + return EAXREVERB_REFLECTIONSDELAY; + + case DSPROPERTY_EAX20LISTENER_REVERB: + return EAXREVERB_REVERB; + + case DSPROPERTY_EAX20LISTENER_REVERBDELAY: + return EAXREVERB_REVERBDELAY; + + case DSPROPERTY_EAX20LISTENER_ENVIRONMENT: + return EAXREVERB_ENVIRONMENT; + + case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE: + return EAXREVERB_ENVIRONMENTSIZE; + + case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION: + return EAXREVERB_ENVIRONMENTDIFFUSION; + + case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF: + return EAXREVERB_AIRABSORPTIONHF; + + case DSPROPERTY_EAX20LISTENER_FLAGS: + return EAXREVERB_FLAGS; + + default: + fail("Unsupported EAX 2.0 listener property id."); + } +} + +ALuint EaxEaxCall::convert_eax_v2_0_buffer_property_id( + ALuint property_id) +{ + switch (property_id) + { + case DSPROPERTY_EAX20BUFFER_NONE: + return EAXSOURCE_NONE; + + case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS: + return EAXSOURCE_ALLPARAMETERS; + + case DSPROPERTY_EAX20BUFFER_DIRECT: + return EAXSOURCE_DIRECT; + + case DSPROPERTY_EAX20BUFFER_DIRECTHF: + return EAXSOURCE_DIRECTHF; + + case DSPROPERTY_EAX20BUFFER_ROOM: + return EAXSOURCE_ROOM; + + case DSPROPERTY_EAX20BUFFER_ROOMHF: + return EAXSOURCE_ROOMHF; + + case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR: + return EAXSOURCE_ROOMROLLOFFFACTOR; + + case DSPROPERTY_EAX20BUFFER_OBSTRUCTION: + return EAXSOURCE_OBSTRUCTION; + + case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO: + return EAXSOURCE_OBSTRUCTIONLFRATIO; + + case DSPROPERTY_EAX20BUFFER_OCCLUSION: + return EAXSOURCE_OCCLUSION; + + case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO: + return EAXSOURCE_OCCLUSIONLFRATIO; + + case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO: + return EAXSOURCE_OCCLUSIONROOMRATIO; + + case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF: + return EAXSOURCE_OUTSIDEVOLUMEHF; + + case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR: + return EAXSOURCE_AIRABSORPTIONFACTOR; + + case DSPROPERTY_EAX20BUFFER_FLAGS: + return EAXSOURCE_FLAGS; + + default: + fail("Unsupported EAX 2.0 buffer property id."); + } +} + + +EaxEaxCall create_eax_call( + bool is_get, + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size) +{ + return EaxEaxCall{ + is_get, + property_set_id, + property_id, + property_source_id, + property_buffer, + property_size + }; +} diff --git a/al/eax_eax_call.h b/al/eax_eax_call.h new file mode 100644 index 00000000..ec632b7d --- /dev/null +++ b/al/eax_eax_call.h @@ -0,0 +1,128 @@ +#ifndef EAX_EAX_CALL_INCLUDED +#define EAX_EAX_CALL_INCLUDED + + +#include "AL/al.h" + +#include "alspan.h" + +#include "eax_api.h" +#include "eax_fx_slot_index.h" + + +enum class EaxEaxCallPropertySetId +{ + none, + + context, + fx_slot, + source, + fx_slot_effect, +}; // EaxEaxCallPropertySetId + + +class EaxEaxCall +{ +public: + EaxEaxCall( + bool is_get, + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size); + + + bool is_get() const noexcept; + + bool is_deferred() const noexcept; + + int get_version() const noexcept; + + EaxEaxCallPropertySetId get_property_set_id() const noexcept; + + ALuint get_property_id() const noexcept; + + ALuint get_property_al_name() const noexcept; + + EaxFxSlotIndex get_fx_slot_index() const noexcept; + + + template< + typename TException, + typename TValue + > + TValue& get_value() const + { + if (property_size_ < static_cast(sizeof(TValue))) + { + throw TException{"Property buffer too small."}; + } + + return *static_cast(property_buffer_); + } + + template< + typename TException, + typename TValue + > + al::span get_values() const + { + if (property_size_ < static_cast(sizeof(TValue))) + { + throw TException{"Property buffer too small."}; + } + + const auto count = property_size_ / sizeof(TValue); + + return al::span{static_cast(property_buffer_), count}; + } + + template< + typename TException, + typename TValue + > + void set_value( + const TValue& value) const + { + get_value() = value; + } + + +private: + bool is_get_; + bool is_deferred_; + int version_; + EaxFxSlotIndex fx_slot_index_; + EaxEaxCallPropertySetId property_set_id_; + + GUID property_set_guid_; + ALuint property_id_; + ALuint property_source_id_; + ALvoid* property_buffer_; + ALuint property_size_; + + + [[noreturn]] + static void fail( + const char* message); + + + static ALuint convert_eax_v2_0_listener_property_id( + ALuint property_id); + + static ALuint convert_eax_v2_0_buffer_property_id( + ALuint property_id); +}; // EaxEaxCall + + +EaxEaxCall create_eax_call( + bool is_get, + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size); + + +#endif // !EAX_EAX_CALL_INCLUDED diff --git a/al/eax_effect.cpp b/al/eax_effect.cpp new file mode 100644 index 00000000..72af94fc --- /dev/null +++ b/al/eax_effect.cpp @@ -0,0 +1 @@ +#include "eax_effect.h" diff --git a/al/eax_effect.h b/al/eax_effect.h new file mode 100644 index 00000000..23dbb73e --- /dev/null +++ b/al/eax_effect.h @@ -0,0 +1,28 @@ +#ifndef EAX_EFFECT_INCLUDED +#define EAX_EFFECT_INCLUDED + + +#include + +#include "eax_eax_call.h" + + +class EaxEffect +{ +public: + EaxEffect() = default; + + virtual ~EaxEffect() = default; + + + // Returns "true" if any immediated property was changed. + // [[nodiscard]] + virtual bool dispatch( + const EaxEaxCall& eax_call) = 0; +}; // EaxEffect + + +using EaxEffectUPtr = std::unique_ptr; + + +#endif // !EAX_EFFECT_INCLUDED diff --git a/al/eax_exception.cpp b/al/eax_exception.cpp new file mode 100644 index 00000000..e3635793 --- /dev/null +++ b/al/eax_exception.cpp @@ -0,0 +1,61 @@ +#include "eax_exception.h" + +#include + +#include + + +EaxException::EaxException( + const char* context, + const char* message) + : + std::runtime_error{make_message(context, message)} +{ +} + +std::string EaxException::make_message( + const char* context, + const char* message) +{ + const auto context_size = (context ? std::string::traits_type::length(context) : 0); + const auto has_contex = (context_size > 0); + + const auto message_size = (message ? std::string::traits_type::length(message) : 0); + const auto has_message = (message_size > 0); + + if (!has_contex && !has_message) + { + return std::string{}; + } + + constexpr auto left_prefix = "["; + const auto left_prefix_size = std::string::traits_type::length(left_prefix); + + constexpr auto right_prefix = "] "; + const auto right_prefix_size = std::string::traits_type::length(right_prefix); + + const auto what_size = + ( + has_contex ? + left_prefix_size + context_size + right_prefix_size : + 0) + + message_size + + 1; + + auto what = std::string{}; + what.reserve(what_size); + + if (has_contex) + { + what.append(left_prefix, left_prefix_size); + what.append(context, context_size); + what.append(right_prefix, right_prefix_size); + } + + if (has_message) + { + what.append(message, message_size); + } + + return what; +} diff --git a/al/eax_exception.h b/al/eax_exception.h new file mode 100644 index 00000000..9a7acf71 --- /dev/null +++ b/al/eax_exception.h @@ -0,0 +1,25 @@ +#ifndef EAX_EXCEPTION_INCLUDED +#define EAX_EXCEPTION_INCLUDED + + +#include +#include + + +class EaxException : + public std::runtime_error +{ +public: + EaxException( + const char* context, + const char* message); + + +private: + static std::string make_message( + const char* context, + const char* message); +}; // EaxException + + +#endif // !EAX_EXCEPTION_INCLUDED diff --git a/al/eax_fx_slot_index.cpp b/al/eax_fx_slot_index.cpp new file mode 100644 index 00000000..dffaef47 --- /dev/null +++ b/al/eax_fx_slot_index.cpp @@ -0,0 +1,164 @@ +#include "eax_fx_slot_index.h" + +#include "eax_exception.h" + + +namespace +{ + + +class EaxFxSlotIndexException : + public EaxException +{ +public: + explicit EaxFxSlotIndexException( + const char* message) + : + EaxException{"EAX_FX_SLOT_INDEX", message} + { + } +}; // EaxFxSlotIndexException + + +} // namespace + + +EaxFxSlotIndex::EaxFxSlotIndex( + EaxFxSlotIndexValue index) +{ + set(index); +} + +EaxFxSlotIndex::EaxFxSlotIndex( + const EaxFxSlotIndex& rhs) noexcept + : + has_value_{rhs.has_value_}, + value_{rhs.value_} +{ +} + +void EaxFxSlotIndex::operator=( + EaxFxSlotIndexValue index) +{ + set(index); +} + +void EaxFxSlotIndex::operator=( + const GUID& guid) +{ + set(guid); +} + +void EaxFxSlotIndex::operator=( + const EaxFxSlotIndex& rhs) noexcept +{ + has_value_ = rhs.has_value_; + value_ = rhs.value_; +} + +bool EaxFxSlotIndex::has_value() const noexcept +{ + return has_value_; +} + +EaxFxSlotIndexValue EaxFxSlotIndex::get() const +{ + if (!has_value_) + { + throw EaxFxSlotIndexException{"No value."}; + } + + return value_; +} + +void EaxFxSlotIndex::reset() noexcept +{ + has_value_ = false; +} + +void EaxFxSlotIndex::set( + EaxFxSlotIndexValue index) +{ + if (index >= static_cast(EAX_MAX_FXSLOTS)) + { + fail("Index out of range."); + } + + has_value_ = true; + value_ = index; +} + +void EaxFxSlotIndex::set( + const GUID& guid) +{ + if (false) + { + } + else if (guid == EAX_NULL_GUID) + { + has_value_ = false; + } + else if (guid == EAXPROPERTYID_EAX40_FXSlot0 || guid == EAXPROPERTYID_EAX50_FXSlot0) + { + has_value_ = true; + value_ = 0; + } + else if (guid == EAXPROPERTYID_EAX40_FXSlot1 || guid == EAXPROPERTYID_EAX50_FXSlot1) + { + has_value_ = true; + value_ = 1; + } + else if (guid == EAXPROPERTYID_EAX40_FXSlot2 || guid == EAXPROPERTYID_EAX50_FXSlot2) + { + has_value_ = true; + value_ = 2; + } + else if (guid == EAXPROPERTYID_EAX40_FXSlot3 || guid == EAXPROPERTYID_EAX50_FXSlot3) + { + has_value_ = true; + value_ = 3; + } + else + { + fail("Unsupported GUID."); + } +} + +EaxFxSlotIndex::operator EaxFxSlotIndexValue() const +{ + return get(); +} + +[[noreturn]] +void EaxFxSlotIndex::fail( + const char* message) +{ + throw EaxFxSlotIndexException{message}; +} + + +bool operator==( + const EaxFxSlotIndex& lhs, + const EaxFxSlotIndex& rhs) noexcept +{ + if (lhs.has_value() != rhs.has_value()) + { + return false; + } + + if (lhs.has_value()) + { + return lhs.get() == rhs.get(); + } + else + { + return true; + } +} + +bool operator!=( + const EaxFxSlotIndex& lhs, + const EaxFxSlotIndex& rhs) noexcept +{ + return !(lhs == rhs); +} diff --git a/al/eax_fx_slot_index.h b/al/eax_fx_slot_index.h new file mode 100644 index 00000000..95ff9313 --- /dev/null +++ b/al/eax_fx_slot_index.h @@ -0,0 +1,69 @@ +#ifndef EAX_FX_SLOT_INDEX_INCLUDED +#define EAX_FX_SLOT_INDEX_INCLUDED + + +#include + +#include "eax_api.h" + + +using EaxFxSlotIndexValue = std::size_t; + + +class EaxFxSlotIndex +{ +public: + EaxFxSlotIndex() noexcept = default; + + EaxFxSlotIndex( + EaxFxSlotIndexValue index); + + EaxFxSlotIndex( + const EaxFxSlotIndex& rhs) noexcept; + + void operator=( + EaxFxSlotIndexValue index); + + void operator=( + const GUID& guid); + + void operator=( + const EaxFxSlotIndex& rhs) noexcept; + + + bool has_value() const noexcept; + + EaxFxSlotIndexValue get() const; + + void reset() noexcept; + + void set( + EaxFxSlotIndexValue index); + + void set( + const GUID& guid); + + operator EaxFxSlotIndexValue() const; + + +private: + [[noreturn]] + static void fail( + const char* message); + + + bool has_value_{}; + EaxFxSlotIndexValue value_{}; +}; // EaxFxSlotIndex + + +bool operator==( + const EaxFxSlotIndex& lhs, + const EaxFxSlotIndex& rhs) noexcept; + +bool operator!=( + const EaxFxSlotIndex& lhs, + const EaxFxSlotIndex& rhs) noexcept; + + +#endif // !EAX_FX_SLOT_INDEX_INCLUDED diff --git a/al/eax_fx_slots.cpp b/al/eax_fx_slots.cpp new file mode 100644 index 00000000..41b18f77 --- /dev/null +++ b/al/eax_fx_slots.cpp @@ -0,0 +1,81 @@ +#include "eax_fx_slots.h" + +#include + +#include "eax_exception.h" + +#include "eax_api.h" + + +namespace +{ + + +class EaxFxSlotsException : + public EaxException +{ +public: + explicit EaxFxSlotsException( + const char* message) + : + EaxException{"EAX_FX_SLOTS", message} + { + } +}; // EaxFxSlotsException + + +} // namespace + + +void EaxFxSlots::initialize( + ALCcontext& al_context) +{ + initialize_fx_slots(al_context); +} + +void EaxFxSlots::uninitialize() noexcept +{ + for (auto& fx_slot : fx_slots_) + { + fx_slot->eax_uninitialize(); + fx_slot = nullptr; + } +} + +const ALeffectslot& EaxFxSlots::get( + EaxFxSlotIndex index) const +{ + if (!index.has_value()) + { + fail("Empty index."); + } + + return *fx_slots_[index.get()]; +} + +ALeffectslot& EaxFxSlots::get( + EaxFxSlotIndex index) +{ + return const_cast(const_cast(this)->get(index)); +} + + +[[noreturn]] +void EaxFxSlots::fail( + const char* message) +{ + throw EaxFxSlotsException{message}; +} + +void EaxFxSlots::initialize_fx_slots( + ALCcontext& al_context) +{ + auto fx_slot_index = EaxFxSlotIndexValue{}; + + for (auto& fx_slot : fx_slots_) + { + fx_slot = eax_create_al_effect_slot(al_context); + fx_slot->eax_initialize(al_context, fx_slot_index); + fx_slot_index += 1; + } +} diff --git a/al/eax_fx_slots.h b/al/eax_fx_slots.h new file mode 100644 index 00000000..6abe61b6 --- /dev/null +++ b/al/eax_fx_slots.h @@ -0,0 +1,46 @@ +#ifndef EAX_FX_SLOTS_INCLUDED +#define EAX_FX_SLOTS_INCLUDED + + +#include + +#include "al/auxeffectslot.h" + +#include "eax_api.h" + +#include "eax_fx_slot_index.h" + + +class EaxFxSlots +{ +public: + void initialize( + ALCcontext& al_context); + + void uninitialize() noexcept; + + + const ALeffectslot& get( + EaxFxSlotIndex index) const; + + ALeffectslot& get( + EaxFxSlotIndex index); + + +private: + using Items = std::array; + + + Items fx_slots_{}; + + + [[noreturn]] + static void fail( + const char* message); + + void initialize_fx_slots( + ALCcontext& al_context); +}; // EaxFxSlots + + +#endif // !EAX_FX_SLOTS_INCLUDED diff --git a/al/eax_globals.cpp b/al/eax_globals.cpp new file mode 100644 index 00000000..e2f4681e --- /dev/null +++ b/al/eax_globals.cpp @@ -0,0 +1,18 @@ +#include "eax_globals.h" + + +bool eax_g_is_enabled = true; + + +const char* eax_v2_0_ext_name = "EAX2.0"; +const char* eax_v3_0_ext_name = "EAX3.0"; +const char* eax_v4_0_ext_name = "EAX4.0"; +const char* eax_v5_0_ext_name = "EAX5.0"; + +const char* eax_x_ram_ext_name = "EAX-RAM"; + +const char* eax_eax_set_func_name = "EAXSet"; +const char* eax_eax_get_func_name = "EAXGet"; + +const char* eax_eax_set_buffer_mode_func_name = "EAXSetBufferMode"; +const char* eax_eax_get_buffer_mode_func_name = "EAXGetBufferMode"; diff --git a/al/eax_globals.h b/al/eax_globals.h new file mode 100644 index 00000000..dd2434ed --- /dev/null +++ b/al/eax_globals.h @@ -0,0 +1,22 @@ +#ifndef EAX_GLOBALS_INCLUDED +#define EAX_GLOBALS_INCLUDED + + +extern bool eax_g_is_enabled; + + +extern const char* eax_v2_0_ext_name; +extern const char* eax_v3_0_ext_name; +extern const char* eax_v4_0_ext_name; +extern const char* eax_v5_0_ext_name; + +extern const char* eax_x_ram_ext_name; + +extern const char* eax_eax_set_func_name; +extern const char* eax_eax_get_func_name; + +extern const char* eax_eax_set_buffer_mode_func_name; +extern const char* eax_eax_get_buffer_mode_func_name; + + +#endif // !EAX_GLOBALS_INCLUDED diff --git a/al/eax_utils.cpp b/al/eax_utils.cpp new file mode 100644 index 00000000..9a8f04f1 --- /dev/null +++ b/al/eax_utils.cpp @@ -0,0 +1,35 @@ +#include "eax_utils.h" + +#include + +#include + +#include "core/logging.h" + + +void eax_log_exception( + const char* message) noexcept +{ + const auto exception_ptr = std::current_exception(); + + assert(exception_ptr); + + if (message) + { + ERR("%s\n", message); + } + + try + { + std::rethrow_exception(exception_ptr); + } + catch (const std::exception& ex) + { + const auto ex_message = ex.what(); + ERR("%s\n", ex_message); + } + catch (...) + { + ERR("%s\n", "Generic exception."); + } +} diff --git a/al/eax_utils.h b/al/eax_utils.h new file mode 100644 index 00000000..68e35fcd --- /dev/null +++ b/al/eax_utils.h @@ -0,0 +1,164 @@ +#ifndef EAX_UTILS_INCLUDED +#define EAX_UTILS_INCLUDED + + +#include + +#include +#include +#include + + +struct ALCcontext; + + +struct EaxAlLowPassParam +{ + float gain; + float gain_hf; +}; // EaxAlLowPassParam + + +// Required to call macro `DO_UPDATEPROPS`. +class EaxAlContextWrapper +{ +public: + explicit EaxAlContextWrapper( + ALCcontext& al_context) noexcept + : + al_context_{&al_context} + { + } + + constexpr ALCcontext* get() noexcept + { + return al_context_; + } + + constexpr ALCcontext* operator->() noexcept + { + return al_context_; + } + + +private: + ALCcontext* al_context_; +}; // EaxAlContextWrapper + + +void eax_log_exception( + const char* message = nullptr) noexcept; + + +template< + typename TException, + typename TValue +> +void eax_validate_range( + const char* value_name, + const TValue& value, + const TValue& min_value, + const TValue& max_value) +{ + if (value >= min_value && value <= max_value) + { + return; + } + + const auto message = + std::string{value_name} + + " out of range (value: " + + std::to_string(value) + "; min: " + + std::to_string(min_value) + "; max: " + + std::to_string(max_value) + ")."; + + throw TException{message.c_str()}; +} + + +namespace detail +{ + + +template< + typename T +> +struct EaxIsBitFieldStruct +{ +private: + using yes = std::true_type; + using no = std::false_type; + + template< + typename U + > + static auto test(int) -> decltype(std::declval(), yes{}); + + template< + typename + > + static no test(...); + + +public: + static constexpr auto value = std::is_same(0)), yes>::value; +}; // EaxIsBitFieldStruct + + +template< + typename T, + typename TValue +> +inline bool eax_bit_fields_are_equal( + const T& lhs, + const T& rhs) noexcept +{ + static_assert(sizeof(T) == sizeof(TValue), "Invalid type size."); + + return reinterpret_cast(lhs) == reinterpret_cast(rhs); +} + + +} // namespace detail + + +template< + typename T, + std::enable_if_t::value, int> = 0 +> +inline bool operator==( + const T& lhs, + const T& rhs) noexcept +{ + using Value = std::conditional_t< + sizeof(T) == 1, + std::uint8_t, + std::conditional_t< + sizeof(T) == 2, + std::uint16_t, + std::conditional_t< + sizeof(T) == 4, + std::uint32_t, + void + > + > + >; + + static_assert(!std::is_same::value, "Unsupported type."); + + return detail::eax_bit_fields_are_equal(lhs, rhs); +} + +template< + typename T, + std::enable_if_t::value, int> = 0 +> +inline bool operator!=( + const T& lhs, + const T& rhs) noexcept +{ + return !(lhs == rhs); +} + + +#endif // !EAX_UTILS_INCLUDED diff --git a/al/eax_x_ram.cpp b/al/eax_x_ram.cpp new file mode 100644 index 00000000..d11a03ab --- /dev/null +++ b/al/eax_x_ram.cpp @@ -0,0 +1 @@ +#include "eax_x_ram.h" diff --git a/al/eax_x_ram.h b/al/eax_x_ram.h new file mode 100644 index 00000000..438b9916 --- /dev/null +++ b/al/eax_x_ram.h @@ -0,0 +1,38 @@ +#ifndef EAX_X_RAM_INCLUDED +#define EAX_X_RAM_INCLUDED + + +#include "AL/al.h" + + +constexpr auto eax_x_ram_min_size = ALsizei{}; +constexpr auto eax_x_ram_max_size = ALsizei{64 * 1'024 * 1'024}; + + +constexpr auto AL_EAX_RAM_SIZE = ALenum{0x202201}; +constexpr auto AL_EAX_RAM_FREE = ALenum{0x202202}; + +constexpr auto AL_STORAGE_AUTOMATIC = ALenum{0x202203}; +constexpr auto AL_STORAGE_HARDWARE = ALenum{0x202204}; +constexpr auto AL_STORAGE_ACCESSIBLE = ALenum{0x202205}; + + +constexpr auto AL_EAX_RAM_SIZE_NAME = "AL_EAX_RAM_SIZE"; +constexpr auto AL_EAX_RAM_FREE_NAME = "AL_EAX_RAM_FREE"; + +constexpr auto AL_STORAGE_AUTOMATIC_NAME = "AL_STORAGE_AUTOMATIC"; +constexpr auto AL_STORAGE_HARDWARE_NAME = "AL_STORAGE_HARDWARE"; +constexpr auto AL_STORAGE_ACCESSIBLE_NAME = "AL_STORAGE_ACCESSIBLE"; + + +ALboolean AL_APIENTRY EAXSetBufferMode( + ALsizei n, + const ALuint* buffers, + ALint value); + +ALenum AL_APIENTRY EAXGetBufferMode( + ALuint buffer, + ALint* pReserved); + + +#endif // !EAX_X_RAM_INCLUDED diff --git a/al/effect.cpp b/al/effect.cpp index 217cc1c2..79cd7fab 100644 --- a/al/effect.cpp +++ b/al/effect.cpp @@ -51,6 +51,11 @@ #include "opthelpers.h" #include "vector.h" +#if ALSOFT_EAX +#include + +#include "eax_exception.h" +#endif // ALSOFT_EAX const EffectList gEffectList[16]{ { "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB }, @@ -745,3 +750,141 @@ void LoadReverbPreset(const char *name, ALeffect *effect) WARN("Reverb preset '%s' not found\n", name); } + +#if ALSOFT_EAX +namespace +{ + +class EaxAlEffectException : + public EaxException +{ +public: + explicit EaxAlEffectException( + const char* message) + : + EaxException{"[EAX_AL_EFFECT]", message} + { + } +}; // EaxAlEffectException + + +} // namespace + + +void ALeffect::eax_initialize() +{ + eax_effect = nullptr; + eax_effect = eax_create_eax_effect(type, Props); +} + +void ALeffect::eax_al_set_effect( + ALenum al_effect_type) +{ + if (al_effect_type != AL_EFFECT_NULL) + { + auto has_effect = false; + + for (const auto &effect_item : gEffectList) + { + if (al_effect_type == effect_item.val && !DisabledEffects[effect_item.type]) + { + has_effect = true; + break; + } + } + + if (!has_effect) + { + eax_fail("Effect not available."); + } + } + + InitEffectParams(this, al_effect_type); +} + +[[noreturn]] +void ALeffect::eax_fail( + const char* message) +{ + throw EaxAlEffectException{message}; +} + +EaxAlEffectDeleter::EaxAlEffectDeleter( + ALCcontext& context) noexcept + : + context_{&context} +{ +} + +void EaxAlEffectDeleter::operator()( + ALeffect* effect) const +{ + assert(effect); + + eax_al_delete_effect(*context_, *effect); +} + +EaxAlEffectUPtr eax_create_al_effect( + ALCcontext& context, + ALenum effect_type) +{ +#define EAX_PREFIX "[EAX_MAKE_EFFECT] " + + auto& device = *context.mALDevice; + std::lock_guard effect_lock{device.EffectLock}; + + // Allocate. + // + if (!EnsureEffects(&device, 1)) + { + ERR(EAX_PREFIX "%s\n", "Failed to ensure."); + return nullptr; + } + + auto effect = EaxAlEffectUPtr{AllocEffect(&device), EaxAlEffectDeleter{context}}; + + if (!effect) + { + ERR(EAX_PREFIX "%s\n", "Failed to allocate."); + return nullptr; + } + + // Set the type. + // + auto is_supported = (effect_type == AL_EFFECT_NULL); + + if (!is_supported) + { + for (const auto& effect_item : gEffectList) + { + if(effect_type == effect_item.val && !DisabledEffects[effect_item.type]) + { + is_supported = true; + break; + } + } + } + + if (!is_supported) + { + ERR(EAX_PREFIX "Effect type 0x%04x not supported.\n", effect_type); + return nullptr; + } + + InitEffectParams(effect.get(), effect_type); + + return effect; + +#undef EAX_PREFIX +} + +void eax_al_delete_effect( + ALCcontext& context, + ALeffect& effect) +{ + auto& device = *context.mALDevice; + std::lock_guard effect_lock{device.EffectLock}; + + FreeEffect(&device, &effect); +} +#endif // ALSOFT_EAX diff --git a/al/effect.h b/al/effect.h index 10a692c9..5b5e4b03 100644 --- a/al/effect.h +++ b/al/effect.h @@ -7,6 +7,12 @@ #include "al/effects/effects.h" #include "alc/effects/base.h" +#if ALSOFT_EAX +#include + +#include "eax_effect.h" +#endif // ALSOFT_EAX + enum { EAXREVERB_EFFECT = 0, @@ -51,10 +57,57 @@ struct ALeffect { ALuint id{0u}; DISABLE_ALLOC() + + +#if ALSOFT_EAX +public: + EaxEffectUPtr eax_effect{}; + + + void eax_initialize(); + + void eax_al_set_effect( + ALenum al_effect_type); + + +private: + [[noreturn]] + static void eax_fail( + const char* message); +#endif // ALSOFT_EAX }; void InitEffect(ALeffect *effect); void LoadReverbPreset(const char *name, ALeffect *effect); +#if ALSOFT_EAX +class EaxAlEffectDeleter +{ +public: + EaxAlEffectDeleter() noexcept = default; + + EaxAlEffectDeleter( + ALCcontext& context) noexcept; + + void operator()( + ALeffect* effect) const; + + +private: + ALCcontext* context_{}; +}; // EaxAlEffectDeleter + +using EaxAlEffectUPtr = std::unique_ptr; + + +EaxAlEffectUPtr eax_create_al_effect( + ALCcontext& context, + ALenum effect_type); + +void eax_al_delete_effect( + ALCcontext& context, + ALeffect& effect); +#endif // ALSOFT_EAX + #endif diff --git a/al/effects/autowah.cpp b/al/effects/autowah.cpp index 9abef1f7..bdd1bc09 100644 --- a/al/effects/autowah.cpp +++ b/al/effects/autowah.cpp @@ -11,6 +11,14 @@ #include "alc/effects/base.h" #include "effects.h" +#if ALSOFT_EAX +#include "alnumeric.h" + +#include "al/eax_exception.h" +#include "al/eax_utils.h" +#endif // ALSOFT_EAX + + namespace { void Autowah_setParamf(EffectProps *props, ALenum param, float val) @@ -107,3 +115,466 @@ EffectProps genDefaultProps() noexcept DEFINE_ALEFFECT_VTABLE(Autowah); const EffectProps AutowahEffectProps{genDefaultProps()}; + +#if ALSOFT_EAX +namespace +{ + + +using EaxAutoWahEffectDirtyFlagsValue = std::uint_least8_t; + +struct EaxAutoWahEffectDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxAutoWahEffectDirtyFlagsValue flAttackTime : 1; + EaxAutoWahEffectDirtyFlagsValue flReleaseTime : 1; + EaxAutoWahEffectDirtyFlagsValue lResonance : 1; + EaxAutoWahEffectDirtyFlagsValue lPeakLevel : 1; +}; // EaxAutoWahEffectDirtyFlags + + +class EaxAutoWahEffect final : + public EaxEffect +{ +public: + EaxAutoWahEffect( + EffectProps& al_effect_props); + + + // [[nodiscard]] + bool dispatch( + const EaxEaxCall& eax_call) override; + + +private: + EffectProps& al_effect_props_; + + EAXAUTOWAHPROPERTIES eax_{}; + EAXAUTOWAHPROPERTIES eax_d_{}; + EaxAutoWahEffectDirtyFlags eax_dirty_flags_{}; + + + void set_eax_defaults(); + + + void set_efx_attack_time(); + + void set_efx_release_time(); + + void set_efx_resonance(); + + void set_efx_peak_gain(); + + void set_efx_defaults(); + + + // [[nodiscard]] + bool get( + const EaxEaxCall& eax_call); + + + void validate_attack_time( + float flAttackTime); + + void validate_release_time( + float flReleaseTime); + + void validate_resonance( + long lResonance); + + void validate_peak_level( + long lPeakLevel); + + void validate_all( + const EAXAUTOWAHPROPERTIES& eax_all); + + + void defer_attack_time( + float flAttackTime); + + void defer_release_time( + float flReleaseTime); + + void defer_resonance( + long lResonance); + + void defer_peak_level( + long lPeakLevel); + + void defer_all( + const EAXAUTOWAHPROPERTIES& eax_all); + + + void defer_attack_time( + const EaxEaxCall& eax_call); + + void defer_release_time( + const EaxEaxCall& eax_call); + + void defer_resonance( + const EaxEaxCall& eax_call); + + void defer_peak_level( + const EaxEaxCall& eax_call); + + void defer_all( + const EaxEaxCall& eax_call); + + // [[nodiscard]] + bool apply_deferred(); + + // [[nodiscard]] + bool set( + const EaxEaxCall& eax_call); +}; // EaxAutoWahEffect + + +class EaxAutoWahEffectException : + public EaxException +{ +public: + explicit EaxAutoWahEffectException( + const char* message) + : + EaxException{"EAX_AUTO_WAH_EFFECT", message} + { + } +}; // EaxAutoWahEffectException + + +EaxAutoWahEffect::EaxAutoWahEffect( + EffectProps& al_effect_props) + : + al_effect_props_{al_effect_props} +{ + set_eax_defaults(); + set_efx_defaults(); +} + +// [[nodiscard]] +bool EaxAutoWahEffect::dispatch( + const EaxEaxCall& eax_call) +{ + return eax_call.is_get() ? get(eax_call) : set(eax_call); +} + +void EaxAutoWahEffect::set_eax_defaults() +{ + eax_.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME; + eax_.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME; + eax_.lResonance = EAXAUTOWAH_DEFAULTRESONANCE; + eax_.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL; + + eax_d_ = eax_; +} + +void EaxAutoWahEffect::set_efx_attack_time() +{ + const auto attack_time = clamp( + eax_.flAttackTime, + AL_AUTOWAH_MIN_ATTACK_TIME, + AL_AUTOWAH_MAX_ATTACK_TIME); + + al_effect_props_.Autowah.AttackTime = attack_time; +} + +void EaxAutoWahEffect::set_efx_release_time() +{ + const auto release_time = clamp( + eax_.flReleaseTime, + AL_AUTOWAH_MIN_RELEASE_TIME, + AL_AUTOWAH_MAX_RELEASE_TIME); + + al_effect_props_.Autowah.ReleaseTime = release_time; +} + +void EaxAutoWahEffect::set_efx_resonance() +{ + const auto resonance = clamp( + level_mb_to_gain(static_cast(eax_.lResonance)), + AL_AUTOWAH_MIN_RESONANCE, + AL_AUTOWAH_MAX_RESONANCE); + + al_effect_props_.Autowah.Resonance = resonance; +} + +void EaxAutoWahEffect::set_efx_peak_gain() +{ + const auto peak_gain = clamp( + level_mb_to_gain(static_cast(eax_.lPeakLevel)), + AL_AUTOWAH_MIN_PEAK_GAIN, + AL_AUTOWAH_MAX_PEAK_GAIN); + + al_effect_props_.Autowah.PeakGain = peak_gain; +} + +void EaxAutoWahEffect::set_efx_defaults() +{ + set_efx_attack_time(); + set_efx_release_time(); + set_efx_resonance(); + set_efx_peak_gain(); +} + +bool EaxAutoWahEffect::get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXAUTOWAH_NONE: + break; + + case EAXAUTOWAH_ALLPARAMETERS: + eax_call.set_value(eax_); + break; + + case EAXAUTOWAH_ATTACKTIME: + eax_call.set_value(eax_.flAttackTime); + break; + + case EAXAUTOWAH_RELEASETIME: + eax_call.set_value(eax_.flReleaseTime); + break; + + case EAXAUTOWAH_RESONANCE: + eax_call.set_value(eax_.lResonance); + break; + + case EAXAUTOWAH_PEAKLEVEL: + eax_call.set_value(eax_.lPeakLevel); + break; + + default: + throw EaxAutoWahEffectException{"Unsupported property id."}; + } + + return false; +} + +void EaxAutoWahEffect::validate_attack_time( + float flAttackTime) +{ + eax_validate_range( + "Attack Time", + flAttackTime, + EAXAUTOWAH_MINATTACKTIME, + EAXAUTOWAH_MAXATTACKTIME); +} + +void EaxAutoWahEffect::validate_release_time( + float flReleaseTime) +{ + eax_validate_range( + "Release Time", + flReleaseTime, + EAXAUTOWAH_MINRELEASETIME, + EAXAUTOWAH_MAXRELEASETIME); +} + +void EaxAutoWahEffect::validate_resonance( + long lResonance) +{ + eax_validate_range( + "Resonance", + lResonance, + EAXAUTOWAH_MINRESONANCE, + EAXAUTOWAH_MAXRESONANCE); +} + +void EaxAutoWahEffect::validate_peak_level( + long lPeakLevel) +{ + eax_validate_range( + "Peak Level", + lPeakLevel, + EAXAUTOWAH_MINPEAKLEVEL, + EAXAUTOWAH_MAXPEAKLEVEL); +} + +void EaxAutoWahEffect::validate_all( + const EAXAUTOWAHPROPERTIES& eax_all) +{ + validate_attack_time(eax_all.flAttackTime); + validate_release_time(eax_all.flReleaseTime); + validate_resonance(eax_all.lResonance); + validate_peak_level(eax_all.lPeakLevel); +} + +void EaxAutoWahEffect::defer_attack_time( + float flAttackTime) +{ + eax_d_.flAttackTime = flAttackTime; + eax_dirty_flags_.flAttackTime = (eax_.flAttackTime != eax_d_.flAttackTime); +} + +void EaxAutoWahEffect::defer_release_time( + float flReleaseTime) +{ + eax_d_.flReleaseTime = flReleaseTime; + eax_dirty_flags_.flReleaseTime = (eax_.flReleaseTime != eax_d_.flReleaseTime); +} + +void EaxAutoWahEffect::defer_resonance( + long lResonance) +{ + eax_d_.lResonance = lResonance; + eax_dirty_flags_.lResonance = (eax_.lResonance != eax_d_.lResonance); +} + +void EaxAutoWahEffect::defer_peak_level( + long lPeakLevel) +{ + eax_d_.lPeakLevel = lPeakLevel; + eax_dirty_flags_.lPeakLevel = (eax_.lPeakLevel != eax_d_.lPeakLevel); +} + +void EaxAutoWahEffect::defer_all( + const EAXAUTOWAHPROPERTIES& eax_all) +{ + validate_all(eax_all); + + defer_attack_time(eax_all.flAttackTime); + defer_release_time(eax_all.flReleaseTime); + defer_resonance(eax_all.lResonance); + defer_peak_level(eax_all.lPeakLevel); +} + +void EaxAutoWahEffect::defer_attack_time( + const EaxEaxCall& eax_call) +{ + const auto& attack_time = + eax_call.get_value(); + + validate_attack_time(attack_time); + defer_attack_time(attack_time); +} + +void EaxAutoWahEffect::defer_release_time( + const EaxEaxCall& eax_call) +{ + const auto& release_time = + eax_call.get_value(); + + validate_release_time(release_time); + defer_release_time(release_time); +} + +void EaxAutoWahEffect::defer_resonance( + const EaxEaxCall& eax_call) +{ + const auto& resonance = + eax_call.get_value(); + + validate_resonance(resonance); + defer_resonance(resonance); +} + +void EaxAutoWahEffect::defer_peak_level( + const EaxEaxCall& eax_call) +{ + const auto& peak_level = + eax_call.get_value(); + + validate_peak_level(peak_level); + defer_peak_level(peak_level); +} + +void EaxAutoWahEffect::defer_all( + const EaxEaxCall& eax_call) +{ + const auto& all = + eax_call.get_value(); + + validate_all(all); + defer_all(all); +} + +// [[nodiscard]] +bool EaxAutoWahEffect::apply_deferred() +{ + if (eax_dirty_flags_ == EaxAutoWahEffectDirtyFlags{}) + { + return false; + } + + eax_ = eax_d_; + + if (eax_dirty_flags_.flAttackTime) + { + set_efx_attack_time(); + } + + if (eax_dirty_flags_.flReleaseTime) + { + set_efx_release_time(); + } + + if (eax_dirty_flags_.lResonance) + { + set_efx_resonance(); + } + + if (eax_dirty_flags_.lPeakLevel) + { + set_efx_peak_gain(); + } + + eax_dirty_flags_ = EaxAutoWahEffectDirtyFlags{}; + + return true; +} + +// [[nodiscard]] +bool EaxAutoWahEffect::set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXAUTOWAH_NONE: + break; + + case EAXAUTOWAH_ALLPARAMETERS: + defer_all(eax_call); + break; + + case EAXAUTOWAH_ATTACKTIME: + defer_attack_time(eax_call); + break; + + case EAXAUTOWAH_RELEASETIME: + defer_release_time(eax_call); + break; + + case EAXAUTOWAH_RESONANCE: + defer_resonance(eax_call); + break; + + case EAXAUTOWAH_PEAKLEVEL: + defer_peak_level(eax_call); + break; + + default: + throw EaxAutoWahEffectException{"Unsupported property id."}; + } + + if (!eax_call.is_deferred()) + { + return apply_deferred(); + } + + return false; +} + + +} // namespace + + +EaxEffectUPtr eax_create_eax_auto_wah_effect( + EffectProps& al_effect_props) +{ + return std::make_unique<::EaxAutoWahEffect>(al_effect_props); +} + + +#endif // ALSOFT_EAX diff --git a/al/effects/chorus.cpp b/al/effects/chorus.cpp index 2466d97e..ed994fbb 100644 --- a/al/effects/chorus.cpp +++ b/al/effects/chorus.cpp @@ -11,6 +11,15 @@ #include "core/logging.h" #include "effects.h" +#if ALSOFT_EAX +#include + +#include "alnumeric.h" + +#include "al/eax_exception.h" +#include "al/eax_utils.h" +#endif // ALSOFT_EAX + namespace { @@ -279,3 +288,1234 @@ const EffectProps ChorusEffectProps{genDefaultChorusProps()}; DEFINE_ALEFFECT_VTABLE(Flanger); const EffectProps FlangerEffectProps{genDefaultFlangerProps()}; + + +#if ALSOFT_EAX +namespace +{ + + +void eax_set_efx_waveform( + ALenum waveform, + EffectProps& al_effect_props) +{ + const auto efx_waveform = WaveformFromEnum(waveform); + assert(efx_waveform.has_value()); + al_effect_props.Chorus.Waveform = *efx_waveform; +} + +void eax_set_efx_phase( + ALint phase, + EffectProps& al_effect_props) +{ + al_effect_props.Chorus.Phase = phase; +} + +void eax_set_efx_rate( + ALfloat rate, + EffectProps& al_effect_props) +{ + al_effect_props.Chorus.Rate = rate; +} + +void eax_set_efx_depth( + ALfloat depth, + EffectProps& al_effect_props) +{ + al_effect_props.Chorus.Depth = depth; +} + +void eax_set_efx_feedback( + ALfloat feedback, + EffectProps& al_effect_props) +{ + al_effect_props.Chorus.Feedback = feedback; +} + +void eax_set_efx_delay( + ALfloat delay, + EffectProps& al_effect_props) +{ + al_effect_props.Chorus.Delay = delay; +} + + +using EaxChorusEffectDirtyFlagsValue = std::uint_least8_t; + +struct EaxChorusEffectDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxChorusEffectDirtyFlagsValue ulWaveform : 1; + EaxChorusEffectDirtyFlagsValue lPhase : 1; + EaxChorusEffectDirtyFlagsValue flRate : 1; + EaxChorusEffectDirtyFlagsValue flDepth : 1; + EaxChorusEffectDirtyFlagsValue flFeedback : 1; + EaxChorusEffectDirtyFlagsValue flDelay : 1; +}; // EaxChorusEffectDirtyFlags + + +class EaxChorusEffect final : + public EaxEffect +{ +public: + EaxChorusEffect( + EffectProps& al_effect_props); + + + // [[nodiscard]] + bool dispatch( + const EaxEaxCall& eax_call) override; + + +private: + EffectProps& al_effect_props_; + EAXCHORUSPROPERTIES eax_{}; + EAXCHORUSPROPERTIES eax_d_{}; + EaxChorusEffectDirtyFlags eax_dirty_flags_{}; + + + void set_eax_defaults() noexcept; + + + void set_efx_waveform(); + + void set_efx_phase(); + + void set_efx_rate(); + + void set_efx_depth(); + + void set_efx_feedback(); + + void set_efx_delay(); + + void set_efx_defaults(); + + + // [[nodiscard]] + bool get( + const EaxEaxCall& eax_call); + + + void validate_waveform( + unsigned long ulWaveform); + + void validate_phase( + long lPhase); + + void validate_rate( + float flRate); + + void validate_depth( + float flDepth); + + void validate_feedback( + float flFeedback); + + void validate_delay( + float flDelay); + + void validate_all( + const EAXCHORUSPROPERTIES& eax_all); + + + void defer_waveform( + unsigned long ulWaveform); + + void defer_phase( + long lPhase); + + void defer_rate( + float flRate); + + void defer_depth( + float flDepth); + + void defer_feedback( + float flFeedback); + + void defer_delay( + float flDelay); + + void defer_all( + const EAXCHORUSPROPERTIES& eax_all); + + + void defer_waveform( + const EaxEaxCall& eax_call); + + void defer_phase( + const EaxEaxCall& eax_call); + + void defer_rate( + const EaxEaxCall& eax_call); + + void defer_depth( + const EaxEaxCall& eax_call); + + void defer_feedback( + const EaxEaxCall& eax_call); + + void defer_delay( + const EaxEaxCall& eax_call); + + void defer_all( + const EaxEaxCall& eax_call); + + + // [[nodiscard]] + bool apply_deferred(); + + // [[nodiscard]] + bool set( + const EaxEaxCall& eax_call); +}; // EaxChorusEffect + + +class EaxChorusEffectException : + public EaxException +{ +public: + explicit EaxChorusEffectException( + const char* message) + : + EaxException{"EAX_CHORUS_EFFECT", message} + { + } +}; // EaxChorusEffectException + + +EaxChorusEffect::EaxChorusEffect( + EffectProps& al_effect_props) + : + al_effect_props_{al_effect_props} +{ + set_eax_defaults(); + set_efx_defaults(); +} + +// [[nodiscard]] +bool EaxChorusEffect::dispatch( + const EaxEaxCall& eax_call) +{ + return eax_call.is_get() ? get(eax_call) : set(eax_call); +} + +void EaxChorusEffect::set_eax_defaults() noexcept +{ + eax_.ulWaveform = EAXCHORUS_DEFAULTWAVEFORM; + eax_.lPhase = EAXCHORUS_DEFAULTPHASE; + eax_.flRate = EAXCHORUS_DEFAULTRATE; + eax_.flDepth = EAXCHORUS_DEFAULTDEPTH; + eax_.flFeedback = EAXCHORUS_DEFAULTFEEDBACK; + eax_.flDelay = EAXCHORUS_DEFAULTDELAY; + + eax_d_ = eax_; +} + +void EaxChorusEffect::set_efx_waveform() +{ + const auto waveform = clamp( + static_cast(eax_.ulWaveform), + AL_CHORUS_MIN_WAVEFORM, + AL_CHORUS_MAX_WAVEFORM); + + eax_set_efx_waveform(waveform, al_effect_props_); +} + +void EaxChorusEffect::set_efx_phase() +{ + const auto phase = clamp( + static_cast(eax_.lPhase), + AL_CHORUS_MIN_PHASE, + AL_CHORUS_MAX_PHASE); + + eax_set_efx_phase(phase, al_effect_props_); +} + +void EaxChorusEffect::set_efx_rate() +{ + const auto rate = clamp( + eax_.flRate, + AL_CHORUS_MIN_RATE, + AL_CHORUS_MAX_RATE); + + eax_set_efx_rate(rate, al_effect_props_); +} + +void EaxChorusEffect::set_efx_depth() +{ + const auto depth = clamp( + eax_.flDepth, + AL_CHORUS_MIN_DEPTH, + AL_CHORUS_MAX_DEPTH); + + eax_set_efx_depth(depth, al_effect_props_); +} + +void EaxChorusEffect::set_efx_feedback() +{ + const auto feedback = clamp( + eax_.flFeedback, + AL_CHORUS_MIN_FEEDBACK, + AL_CHORUS_MAX_FEEDBACK); + + eax_set_efx_feedback(feedback, al_effect_props_); +} + +void EaxChorusEffect::set_efx_delay() +{ + const auto delay = clamp( + eax_.flDelay, + AL_CHORUS_MIN_DELAY, + AL_CHORUS_MAX_DELAY); + + eax_set_efx_delay(delay, al_effect_props_); +} + +void EaxChorusEffect::set_efx_defaults() +{ + set_efx_waveform(); + set_efx_phase(); + set_efx_rate(); + set_efx_depth(); + set_efx_feedback(); + set_efx_delay(); +} + +bool EaxChorusEffect::get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXCHORUS_NONE: + break; + + case EAXCHORUS_ALLPARAMETERS: + eax_call.set_value(eax_); + break; + + case EAXCHORUS_WAVEFORM: + eax_call.set_value(eax_.ulWaveform); + break; + + case EAXCHORUS_PHASE: + eax_call.set_value(eax_.lPhase); + break; + + case EAXCHORUS_RATE: + eax_call.set_value(eax_.flRate); + break; + + case EAXCHORUS_DEPTH: + eax_call.set_value(eax_.flDepth); + break; + + case EAXCHORUS_FEEDBACK: + eax_call.set_value(eax_.flFeedback); + break; + + case EAXCHORUS_DELAY: + eax_call.set_value(eax_.flDelay); + break; + + default: + throw EaxChorusEffectException{"Unsupported property id."}; + } + + return false; +} + +void EaxChorusEffect::validate_waveform( + unsigned long ulWaveform) +{ + eax_validate_range( + "Waveform", + ulWaveform, + EAXCHORUS_MINWAVEFORM, + EAXCHORUS_MAXWAVEFORM); +} + +void EaxChorusEffect::validate_phase( + long lPhase) +{ + eax_validate_range( + "Phase", + lPhase, + EAXCHORUS_MINPHASE, + EAXCHORUS_MAXPHASE); +} + +void EaxChorusEffect::validate_rate( + float flRate) +{ + eax_validate_range( + "Rate", + flRate, + EAXCHORUS_MINRATE, + EAXCHORUS_MAXRATE); +} + +void EaxChorusEffect::validate_depth( + float flDepth) +{ + eax_validate_range( + "Depth", + flDepth, + EAXCHORUS_MINDEPTH, + EAXCHORUS_MAXDEPTH); +} + +void EaxChorusEffect::validate_feedback( + float flFeedback) +{ + eax_validate_range( + "Feedback", + flFeedback, + EAXCHORUS_MINFEEDBACK, + EAXCHORUS_MAXFEEDBACK); +} + +void EaxChorusEffect::validate_delay( + float flDelay) +{ + eax_validate_range( + "Delay", + flDelay, + EAXCHORUS_MINDELAY, + EAXCHORUS_MAXDELAY); +} + +void EaxChorusEffect::validate_all( + const EAXCHORUSPROPERTIES& eax_all) +{ + validate_waveform(eax_all.ulWaveform); + validate_phase(eax_all.lPhase); + validate_rate(eax_all.flRate); + validate_depth(eax_all.flDepth); + validate_feedback(eax_all.flFeedback); + validate_delay(eax_all.flDelay); +} + +void EaxChorusEffect::defer_waveform( + unsigned long ulWaveform) +{ + eax_d_.ulWaveform = ulWaveform; + eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform); +} + +void EaxChorusEffect::defer_phase( + long lPhase) +{ + eax_d_.lPhase = lPhase; + eax_dirty_flags_.lPhase = (eax_.lPhase != eax_d_.lPhase); +} + +void EaxChorusEffect::defer_rate( + float flRate) +{ + eax_d_.flRate = flRate; + eax_dirty_flags_.flRate = (eax_.flRate != eax_d_.flRate); +} + +void EaxChorusEffect::defer_depth( + float flDepth) +{ + eax_d_.flDepth = flDepth; + eax_dirty_flags_.flDepth = (eax_.flDepth != eax_d_.flDepth); +} + +void EaxChorusEffect::defer_feedback( + float flFeedback) +{ + eax_d_.flFeedback = flFeedback; + eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback); +} + +void EaxChorusEffect::defer_delay( + float flDelay) +{ + eax_d_.flDelay = flDelay; + eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay); +} + +void EaxChorusEffect::defer_all( + const EAXCHORUSPROPERTIES& eax_all) +{ + defer_waveform(eax_all.ulWaveform); + defer_phase(eax_all.lPhase); + defer_rate(eax_all.flRate); + defer_depth(eax_all.flDepth); + defer_feedback(eax_all.flFeedback); + defer_delay(eax_all.flDelay); +} + +void EaxChorusEffect::defer_waveform( + const EaxEaxCall& eax_call) +{ + const auto& waveform = + eax_call.get_value(); + + validate_waveform(waveform); + defer_waveform(waveform); +} + +void EaxChorusEffect::defer_phase( + const EaxEaxCall& eax_call) +{ + const auto& phase = + eax_call.get_value(); + + validate_phase(phase); + defer_phase(phase); +} + +void EaxChorusEffect::defer_rate( + const EaxEaxCall& eax_call) +{ + const auto& rate = + eax_call.get_value(); + + validate_rate(rate); + defer_rate(rate); +} + +void EaxChorusEffect::defer_depth( + const EaxEaxCall& eax_call) +{ + const auto& depth = + eax_call.get_value(); + + validate_depth(depth); + defer_depth(depth); +} + +void EaxChorusEffect::defer_feedback( + const EaxEaxCall& eax_call) +{ + const auto& feedback = + eax_call.get_value(); + + validate_feedback(feedback); + defer_feedback(feedback); +} + +void EaxChorusEffect::defer_delay( + const EaxEaxCall& eax_call) +{ + const auto& delay = + eax_call.get_value(); + + validate_delay(delay); + defer_delay(delay); +} + +void EaxChorusEffect::defer_all( + const EaxEaxCall& eax_call) +{ + const auto& all = + eax_call.get_value(); + + validate_all(all); + defer_all(all); +} + +// [[nodiscard]] +bool EaxChorusEffect::apply_deferred() +{ + if (eax_dirty_flags_ == EaxChorusEffectDirtyFlags{}) + { + return false; + } + + eax_ = eax_d_; + + if (eax_dirty_flags_.ulWaveform) + { + set_efx_waveform(); + } + + if (eax_dirty_flags_.lPhase) + { + set_efx_phase(); + } + + if (eax_dirty_flags_.flRate) + { + set_efx_rate(); + } + + if (eax_dirty_flags_.flDepth) + { + set_efx_depth(); + } + + if (eax_dirty_flags_.flFeedback) + { + set_efx_feedback(); + } + + if (eax_dirty_flags_.flDelay) + { + set_efx_delay(); + } + + eax_dirty_flags_ = EaxChorusEffectDirtyFlags{}; + + return true; +} + +// [[nodiscard]] +bool EaxChorusEffect::set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXCHORUS_NONE: + break; + + case EAXCHORUS_ALLPARAMETERS: + defer_all(eax_call); + break; + + case EAXCHORUS_WAVEFORM: + defer_waveform(eax_call); + break; + + case EAXCHORUS_PHASE: + defer_phase(eax_call); + break; + + case EAXCHORUS_RATE: + defer_rate(eax_call); + break; + + case EAXCHORUS_DEPTH: + defer_depth(eax_call); + break; + + case EAXCHORUS_FEEDBACK: + defer_feedback(eax_call); + break; + + case EAXCHORUS_DELAY: + defer_delay(eax_call); + break; + + default: + throw EaxChorusEffectException{"Unsupported property id."}; + } + + if (!eax_call.is_deferred()) + { + return apply_deferred(); + } + + return false; +} + + +} // namespace + + +EaxEffectUPtr eax_create_eax_chorus_effect( + EffectProps& al_effect_props) +{ + return std::make_unique<::EaxChorusEffect>(al_effect_props); +} + + +namespace +{ + + +using EaxFlangerEffectDirtyFlagsValue = std::uint_least8_t; + +struct EaxFlangerEffectDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxFlangerEffectDirtyFlagsValue ulWaveform : 1; + EaxFlangerEffectDirtyFlagsValue lPhase : 1; + EaxFlangerEffectDirtyFlagsValue flRate : 1; + EaxFlangerEffectDirtyFlagsValue flDepth : 1; + EaxFlangerEffectDirtyFlagsValue flFeedback : 1; + EaxFlangerEffectDirtyFlagsValue flDelay : 1; +}; // EaxFlangerEffectDirtyFlags + + +class EaxFlangerEffect final : + public EaxEffect +{ +public: + EaxFlangerEffect( + EffectProps& al_effect_props); + + + // [[nodiscard]] + bool dispatch( + const EaxEaxCall& eax_call) override; + + +private: + EffectProps& al_effect_props_; + + EAXFLANGERPROPERTIES eax_{}; + EAXFLANGERPROPERTIES eax_d_{}; + EaxFlangerEffectDirtyFlags eax_dirty_flags_{}; + + + void set_eax_defaults(); + + + void set_efx_waveform(); + + void set_efx_phase(); + + void set_efx_rate(); + + void set_efx_depth(); + + void set_efx_feedback(); + + void set_efx_delay(); + + void set_efx_defaults(); + + + // [[nodiscard]] + bool get( + const EaxEaxCall& eax_call); + + + void validate_waveform( + unsigned long ulWaveform); + + void validate_phase( + long lPhase); + + void validate_rate( + float flRate); + + void validate_depth( + float flDepth); + + void validate_feedback( + float flFeedback); + + void validate_delay( + float flDelay); + + void validate_all( + const EAXFLANGERPROPERTIES& all); + + + void defer_waveform( + unsigned long ulWaveform); + + void defer_phase( + long lPhase); + + void defer_rate( + float flRate); + + void defer_depth( + float flDepth); + + void defer_feedback( + float flFeedback); + + void defer_delay( + float flDelay); + + void defer_all( + const EAXFLANGERPROPERTIES& all); + + + void defer_waveform( + const EaxEaxCall& eax_call); + + void defer_phase( + const EaxEaxCall& eax_call); + + void defer_rate( + const EaxEaxCall& eax_call); + + void defer_depth( + const EaxEaxCall& eax_call); + + void defer_feedback( + const EaxEaxCall& eax_call); + + void defer_delay( + const EaxEaxCall& eax_call); + + void defer_all( + const EaxEaxCall& eax_call); + + + // [[nodiscard]] + bool apply_deferred(); + + // [[nodiscard]] + bool set( + const EaxEaxCall& eax_call); +}; // EaxFlangerEffect + + +class EaxFlangerEffectException : + public EaxException +{ +public: + explicit EaxFlangerEffectException( + const char* message) + : + EaxException{"EAX_FLANGER_EFFECT", message} + { + } +}; // EaxFlangerEffectException + + +EaxFlangerEffect::EaxFlangerEffect( + EffectProps& al_effect_props) + : + al_effect_props_{al_effect_props} +{ + set_eax_defaults(); + set_efx_defaults(); +} + +// [[nodiscard]] +bool EaxFlangerEffect::dispatch( + const EaxEaxCall& eax_call) +{ + return eax_call.is_get() ? get(eax_call) : set(eax_call); +} + +void EaxFlangerEffect::set_eax_defaults() +{ + eax_.ulWaveform = EAXFLANGER_DEFAULTWAVEFORM; + eax_.lPhase = EAXFLANGER_DEFAULTPHASE; + eax_.flRate = EAXFLANGER_DEFAULTRATE; + eax_.flDepth = EAXFLANGER_DEFAULTDEPTH; + eax_.flFeedback = EAXFLANGER_DEFAULTFEEDBACK; + eax_.flDelay = EAXFLANGER_DEFAULTDELAY; + + eax_d_ = eax_; +} + +void EaxFlangerEffect::set_efx_waveform() +{ + const auto waveform = clamp( + static_cast(eax_.ulWaveform), + AL_FLANGER_MIN_WAVEFORM, + AL_FLANGER_MAX_WAVEFORM); + + eax_set_efx_waveform(waveform, al_effect_props_); +} + +void EaxFlangerEffect::set_efx_phase() +{ + const auto phase = clamp( + static_cast(eax_.lPhase), + AL_FLANGER_MIN_PHASE, + AL_FLANGER_MAX_PHASE); + + eax_set_efx_phase(phase, al_effect_props_); +} + +void EaxFlangerEffect::set_efx_rate() +{ + const auto rate = clamp( + eax_.flRate, + AL_FLANGER_MIN_RATE, + AL_FLANGER_MAX_RATE); + + eax_set_efx_rate(rate, al_effect_props_); +} + +void EaxFlangerEffect::set_efx_depth() +{ + const auto depth = clamp( + eax_.flDepth, + AL_FLANGER_MIN_DEPTH, + AL_FLANGER_MAX_DEPTH); + + eax_set_efx_depth(depth, al_effect_props_); +} + +void EaxFlangerEffect::set_efx_feedback() +{ + const auto feedback = clamp( + eax_.flFeedback, + AL_FLANGER_MIN_FEEDBACK, + AL_FLANGER_MAX_FEEDBACK); + + eax_set_efx_feedback(feedback, al_effect_props_); +} + +void EaxFlangerEffect::set_efx_delay() +{ + const auto delay = clamp( + eax_.flDelay, + AL_FLANGER_MIN_DELAY, + AL_FLANGER_MAX_DELAY); + + eax_set_efx_delay(delay, al_effect_props_); +} + +void EaxFlangerEffect::set_efx_defaults() +{ + set_efx_waveform(); + set_efx_phase(); + set_efx_rate(); + set_efx_depth(); + set_efx_feedback(); + set_efx_delay(); +} + +// [[nodiscard]] +bool EaxFlangerEffect::get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXFLANGER_NONE: + break; + + case EAXFLANGER_ALLPARAMETERS: + eax_call.set_value(eax_); + break; + + case EAXFLANGER_WAVEFORM: + eax_call.set_value(eax_.ulWaveform); + break; + + case EAXFLANGER_PHASE: + eax_call.set_value(eax_.lPhase); + break; + + case EAXFLANGER_RATE: + eax_call.set_value(eax_.flRate); + break; + + case EAXFLANGER_DEPTH: + eax_call.set_value(eax_.flDepth); + break; + + case EAXFLANGER_FEEDBACK: + eax_call.set_value(eax_.flFeedback); + break; + + case EAXFLANGER_DELAY: + eax_call.set_value(eax_.flDelay); + break; + + default: + throw EaxFlangerEffectException{"Unsupported property id."}; + } + + return false; +} + +void EaxFlangerEffect::validate_waveform( + unsigned long ulWaveform) +{ + eax_validate_range( + "Waveform", + ulWaveform, + EAXFLANGER_MINWAVEFORM, + EAXFLANGER_MAXWAVEFORM); +} + +void EaxFlangerEffect::validate_phase( + long lPhase) +{ + eax_validate_range( + "Phase", + lPhase, + EAXFLANGER_MINPHASE, + EAXFLANGER_MAXPHASE); +} + +void EaxFlangerEffect::validate_rate( + float flRate) +{ + eax_validate_range( + "Rate", + flRate, + EAXFLANGER_MINRATE, + EAXFLANGER_MAXRATE); +} + +void EaxFlangerEffect::validate_depth( + float flDepth) +{ + eax_validate_range( + "Depth", + flDepth, + EAXFLANGER_MINDEPTH, + EAXFLANGER_MAXDEPTH); +} + +void EaxFlangerEffect::validate_feedback( + float flFeedback) +{ + eax_validate_range( + "Feedback", + flFeedback, + EAXFLANGER_MINFEEDBACK, + EAXFLANGER_MAXFEEDBACK); +} + +void EaxFlangerEffect::validate_delay( + float flDelay) +{ + eax_validate_range( + "Delay", + flDelay, + EAXFLANGER_MINDELAY, + EAXFLANGER_MAXDELAY); +} + +void EaxFlangerEffect::validate_all( + const EAXFLANGERPROPERTIES& all) +{ + validate_waveform(all.ulWaveform); + validate_phase(all.lPhase); + validate_rate(all.flRate); + validate_depth(all.flDepth); + validate_feedback(all.flDelay); + validate_delay(all.flDelay); +} + +void EaxFlangerEffect::defer_waveform( + unsigned long ulWaveform) +{ + eax_d_.ulWaveform = ulWaveform; + eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform); +} + +void EaxFlangerEffect::defer_phase( + long lPhase) +{ + eax_d_.lPhase = lPhase; + eax_dirty_flags_.lPhase = (eax_.lPhase != eax_d_.lPhase); +} + +void EaxFlangerEffect::defer_rate( + float flRate) +{ + eax_d_.flRate = flRate; + eax_dirty_flags_.flRate = (eax_.flRate != eax_d_.flRate); +} + +void EaxFlangerEffect::defer_depth( + float flDepth) +{ + eax_d_.flDepth = flDepth; + eax_dirty_flags_.flDepth = (eax_.flDepth != eax_d_.flDepth); +} + +void EaxFlangerEffect::defer_feedback( + float flFeedback) +{ + eax_d_.flFeedback = flFeedback; + eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback); +} + +void EaxFlangerEffect::defer_delay( + float flDelay) +{ + eax_d_.flDelay = flDelay; + eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay); +} + +void EaxFlangerEffect::defer_all( + const EAXFLANGERPROPERTIES& all) +{ + defer_waveform(all.ulWaveform); + defer_phase(all.lPhase); + defer_rate(all.flRate); + defer_depth(all.flDepth); + defer_feedback(all.flDelay); + defer_delay(all.flDelay); +} + +void EaxFlangerEffect::defer_waveform( + const EaxEaxCall& eax_call) +{ + const auto& waveform = + eax_call.get_value(); + + validate_waveform(waveform); + defer_waveform(waveform); +} + +void EaxFlangerEffect::defer_phase( + const EaxEaxCall& eax_call) +{ + const auto& phase = + eax_call.get_value(); + + validate_phase(phase); + defer_phase(phase); +} + +void EaxFlangerEffect::defer_rate( + const EaxEaxCall& eax_call) +{ + const auto& rate = + eax_call.get_value(); + + validate_rate(rate); + defer_rate(rate); +} + +void EaxFlangerEffect::defer_depth( + const EaxEaxCall& eax_call) +{ + const auto& depth = + eax_call.get_value(); + + validate_depth(depth); + defer_depth(depth); +} + +void EaxFlangerEffect::defer_feedback( + const EaxEaxCall& eax_call) +{ + const auto& feedback = + eax_call.get_value(); + + validate_feedback(feedback); + defer_feedback(feedback); +} + +void EaxFlangerEffect::defer_delay( + const EaxEaxCall& eax_call) +{ + const auto& delay = + eax_call.get_value(); + + validate_delay(delay); + defer_delay(delay); +} + +void EaxFlangerEffect::defer_all( + const EaxEaxCall& eax_call) +{ + const auto& all = + eax_call.get_value(); + + validate_all(all); + defer_all(all); +} + +// [[nodiscard]] +bool EaxFlangerEffect::apply_deferred() +{ + if (eax_dirty_flags_ == EaxFlangerEffectDirtyFlags{}) + { + return false; + } + + eax_ = eax_d_; + + if (eax_dirty_flags_.ulWaveform) + { + set_efx_waveform(); + } + + if (eax_dirty_flags_.lPhase) + { + set_efx_phase(); + } + + if (eax_dirty_flags_.flRate) + { + set_efx_rate(); + } + + if (eax_dirty_flags_.flDepth) + { + set_efx_depth(); + } + + if (eax_dirty_flags_.flFeedback) + { + set_efx_feedback(); + } + + if (eax_dirty_flags_.flDelay) + { + set_efx_delay(); + } + + eax_dirty_flags_ = EaxFlangerEffectDirtyFlags{}; + + return true; +} + +// [[nodiscard]] +bool EaxFlangerEffect::set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXFLANGER_NONE: + break; + + case EAXFLANGER_ALLPARAMETERS: + defer_all(eax_call); + break; + + case EAXFLANGER_WAVEFORM: + defer_waveform(eax_call); + break; + + case EAXFLANGER_PHASE: + defer_phase(eax_call); + break; + + case EAXFLANGER_RATE: + defer_rate(eax_call); + break; + + case EAXFLANGER_DEPTH: + defer_depth(eax_call); + break; + + case EAXFLANGER_FEEDBACK: + defer_feedback(eax_call); + break; + + case EAXFLANGER_DELAY: + defer_delay(eax_call); + break; + + default: + throw EaxFlangerEffectException{"Unsupported property id."}; + } + + if (!eax_call.is_deferred()) + { + return apply_deferred(); + } + + return false; +} + + +} // namespace + + +EaxEffectUPtr eax_create_eax_flanger_effect( + EffectProps& al_effect_props) +{ + return std::make_unique(al_effect_props); +} + + +#endif // ALSOFT_EAX diff --git a/al/effects/compressor.cpp b/al/effects/compressor.cpp index f5db2a6f..868c5c1b 100644 --- a/al/effects/compressor.cpp +++ b/al/effects/compressor.cpp @@ -7,6 +7,13 @@ #include "alc/effects/base.h" #include "effects.h" +#if ALSOFT_EAX +#include "alnumeric.h" + +#include "al/eax_exception.h" +#include "al/eax_utils.h" +#endif // ALSOFT_EAX + namespace { @@ -70,3 +77,268 @@ EffectProps genDefaultProps() noexcept DEFINE_ALEFFECT_VTABLE(Compressor); const EffectProps CompressorEffectProps{genDefaultProps()}; + +#if ALSOFT_EAX +namespace +{ + + +using EaxCompressorEffectDirtyFlagsValue = std::uint_least8_t; + +struct EaxCompressorEffectDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxCompressorEffectDirtyFlagsValue ulOnOff : 1; +}; // EaxCompressorEffectDirtyFlags + + +class EaxCompressorEffect final : + public EaxEffect +{ +public: + EaxCompressorEffect( + EffectProps& al_effect_props); + + + // [[nodiscard]] + bool dispatch( + const EaxEaxCall& eax_call) override; + + +private: + EffectProps& al_effect_props_; + + EAXAGCCOMPRESSORPROPERTIES eax_{}; + EAXAGCCOMPRESSORPROPERTIES eax_d_{}; + EaxCompressorEffectDirtyFlags eax_dirty_flags_{}; + + + void set_eax_defaults(); + + + void set_efx_on_off(); + + void set_efx_defaults(); + + + // [[nodiscard]] + bool get( + const EaxEaxCall& eax_call); + + + void validate_on_off( + unsigned long ulOnOff); + + void validate_all( + const EAXAGCCOMPRESSORPROPERTIES& eax_all); + + + void defer_on_off( + unsigned long ulOnOff); + + void defer_all( + const EAXAGCCOMPRESSORPROPERTIES& eax_all); + + + void defer_on_off( + const EaxEaxCall& eax_call); + + void defer_all( + const EaxEaxCall& eax_call); + + + // [[nodiscard]] + bool apply_deferred(); + + // [[nodiscard]] + bool set( + const EaxEaxCall& eax_call); +}; // EaxCompressorEffect + + +class EaxCompressorEffectException : + public EaxException +{ +public: + explicit EaxCompressorEffectException( + const char* message) + : + EaxException{"EAX_COMPRESSOR_EFFECT", message} + { + } +}; // EaxCompressorEffectException + + +EaxCompressorEffect::EaxCompressorEffect( + EffectProps& al_effect_props) + : + al_effect_props_{al_effect_props} +{ + set_eax_defaults(); + set_efx_defaults(); +} + +// [[nodiscard]] +bool EaxCompressorEffect::dispatch( + const EaxEaxCall& eax_call) +{ + return eax_call.is_get() ? get(eax_call) : set(eax_call); +} + +void EaxCompressorEffect::set_eax_defaults() +{ + eax_.ulOnOff = EAXAGCCOMPRESSOR_DEFAULTONOFF; + + eax_d_ = eax_; +} + +void EaxCompressorEffect::set_efx_on_off() +{ + const auto on_off = clamp( + static_cast(eax_.ulOnOff), + AL_COMPRESSOR_MIN_ONOFF, + AL_COMPRESSOR_MAX_ONOFF); + + al_effect_props_.Compressor.OnOff = (on_off != AL_FALSE); +} + +void EaxCompressorEffect::set_efx_defaults() +{ + set_efx_on_off(); +} + +// [[nodiscard]] +bool EaxCompressorEffect::get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXAGCCOMPRESSOR_NONE: + break; + + case EAXAGCCOMPRESSOR_ALLPARAMETERS: + eax_call.set_value(eax_); + break; + + case EAXAGCCOMPRESSOR_ONOFF: + eax_call.set_value(eax_.ulOnOff); + break; + + default: + throw EaxCompressorEffectException{"Unsupported property id."}; + } + + return false; +} + +void EaxCompressorEffect::validate_on_off( + unsigned long ulOnOff) +{ + eax_validate_range( + "On-Off", + ulOnOff, + EAXAGCCOMPRESSOR_MINONOFF, + EAXAGCCOMPRESSOR_MAXONOFF); +} + +void EaxCompressorEffect::validate_all( + const EAXAGCCOMPRESSORPROPERTIES& eax_all) +{ + validate_on_off(eax_all.ulOnOff); +} + +void EaxCompressorEffect::defer_on_off( + unsigned long ulOnOff) +{ + eax_d_.ulOnOff = ulOnOff; + eax_dirty_flags_.ulOnOff = (eax_.ulOnOff != eax_d_.ulOnOff); +} + +void EaxCompressorEffect::defer_all( + const EAXAGCCOMPRESSORPROPERTIES& eax_all) +{ + defer_on_off(eax_all.ulOnOff); +} + +void EaxCompressorEffect::defer_on_off( + const EaxEaxCall& eax_call) +{ + const auto& on_off = + eax_call.get_value(); + + validate_on_off(on_off); + defer_on_off(on_off); +} + +void EaxCompressorEffect::defer_all( + const EaxEaxCall& eax_call) +{ + const auto& all = + eax_call.get_value(); + + validate_all(all); + defer_all(all); +} + +// [[nodiscard]] +bool EaxCompressorEffect::apply_deferred() +{ + if (eax_dirty_flags_ == EaxCompressorEffectDirtyFlags{}) + { + return false; + } + + eax_ = eax_d_; + + if (eax_dirty_flags_.ulOnOff) + { + set_efx_on_off(); + } + + eax_dirty_flags_ = EaxCompressorEffectDirtyFlags{}; + + return true; +} + +// [[nodiscard]] +bool EaxCompressorEffect::set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXAGCCOMPRESSOR_NONE: + break; + + case EAXAGCCOMPRESSOR_ALLPARAMETERS: + defer_all(eax_call); + break; + + case EAXAGCCOMPRESSOR_ONOFF: + defer_on_off(eax_call); + break; + + default: + throw EaxCompressorEffectException{"Unsupported property id."}; + } + + if (!eax_call.is_deferred()) + { + return apply_deferred(); + } + + return false; +} + + +} // namespace + + +EaxEffectUPtr eax_create_eax_compressor_effect( + EffectProps& al_effect_props) +{ + return std::make_unique(al_effect_props); +} + + +#endif // ALSOFT_EAX diff --git a/al/effects/distortion.cpp b/al/effects/distortion.cpp index f5d64a9a..062cdc54 100644 --- a/al/effects/distortion.cpp +++ b/al/effects/distortion.cpp @@ -7,6 +7,13 @@ #include "alc/effects/base.h" #include "effects.h" +#if ALSOFT_EAX +#include "alnumeric.h" + +#include "al/eax_exception.h" +#include "al/eax_utils.h" +#endif // ALSOFT_EAX + namespace { @@ -112,3 +119,532 @@ EffectProps genDefaultProps() noexcept DEFINE_ALEFFECT_VTABLE(Distortion); const EffectProps DistortionEffectProps{genDefaultProps()}; + +#if ALSOFT_EAX +namespace +{ + + +using EaxDistortionEffectDirtyFlagsValue = std::uint_least8_t; + +struct EaxDistortionEffectDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxDistortionEffectDirtyFlagsValue flEdge : 1; + EaxDistortionEffectDirtyFlagsValue lGain : 1; + EaxDistortionEffectDirtyFlagsValue flLowPassCutOff : 1; + EaxDistortionEffectDirtyFlagsValue flEQCenter : 1; + EaxDistortionEffectDirtyFlagsValue flEQBandwidth : 1; +}; // EaxDistortionEffectDirtyFlags + + +class EaxDistortionEffect final : + public EaxEffect +{ +public: + EaxDistortionEffect( + EffectProps& al_effect_props); + + + // [[nodiscard]] + bool dispatch( + const EaxEaxCall& eax_call) override; + + +private: + EffectProps& al_effect_props_; + + EAXDISTORTIONPROPERTIES eax_{}; + EAXDISTORTIONPROPERTIES eax_d_{}; + EaxDistortionEffectDirtyFlags eax_dirty_flags_{}; + + + void set_eax_defaults(); + + + void set_efx_edge(); + + void set_efx_gain(); + + void set_efx_lowpass_cutoff(); + + void set_efx_eq_center(); + + void set_efx_eq_bandwidth(); + + void set_efx_defaults(); + + + // [[nodiscard]] + bool get( + const EaxEaxCall& eax_call); + + + void validate_edge( + float flEdge); + + void validate_gain( + long lGain); + + void validate_lowpass_cutoff( + float flLowPassCutOff); + + void validate_eq_center( + float flEQCenter); + + void validate_eq_bandwidth( + float flEQBandwidth); + + void validate_all( + const EAXDISTORTIONPROPERTIES& eax_all); + + + void defer_edge( + float flEdge); + + void defer_gain( + long lGain); + + void defer_low_pass_cutoff( + float flLowPassCutOff); + + void defer_eq_center( + float flEQCenter); + + void defer_eq_bandwidth( + float flEQBandwidth); + + void defer_all( + const EAXDISTORTIONPROPERTIES& eax_all); + + + void defer_edge( + const EaxEaxCall& eax_call); + + void defer_gain( + const EaxEaxCall& eax_call); + + void defer_low_pass_cutoff( + const EaxEaxCall& eax_call); + + void defer_eq_center( + const EaxEaxCall& eax_call); + + void defer_eq_bandwidth( + const EaxEaxCall& eax_call); + + void defer_all( + const EaxEaxCall& eax_call); + + + // [[nodiscard]] + bool apply_deferred(); + + // [[nodiscard]] + bool set( + const EaxEaxCall& eax_call); +}; // EaxDistortionEffect + + +class EaxDistortionEffectException : + public EaxException +{ +public: + explicit EaxDistortionEffectException( + const char* message) + : + EaxException{"EAX_DISTORTION_EFFECT", message} + { + } +}; // EaxDistortionEffectException + + +EaxDistortionEffect::EaxDistortionEffect( + EffectProps& al_effect_props) + : + al_effect_props_{al_effect_props} +{ + set_eax_defaults(); + set_efx_defaults(); +} + +// [[nodiscard]] +bool EaxDistortionEffect::dispatch( + const EaxEaxCall& eax_call) +{ + return eax_call.is_get() ? get(eax_call) : set(eax_call); +} + +void EaxDistortionEffect::set_eax_defaults() +{ + eax_.flEdge = EAXDISTORTION_DEFAULTEDGE; + eax_.lGain = EAXDISTORTION_DEFAULTGAIN; + eax_.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF; + eax_.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER; + eax_.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH; + + eax_d_ = eax_; +} + +void EaxDistortionEffect::set_efx_edge() +{ + const auto edge = clamp( + eax_.flEdge, + AL_DISTORTION_MIN_EDGE, + AL_DISTORTION_MAX_EDGE); + + al_effect_props_.Distortion.Edge = edge; +} + +void EaxDistortionEffect::set_efx_gain() +{ + const auto gain = clamp( + level_mb_to_gain(static_cast(eax_.lGain)), + AL_DISTORTION_MIN_GAIN, + AL_DISTORTION_MAX_GAIN); + + al_effect_props_.Distortion.Gain = gain; +} + +void EaxDistortionEffect::set_efx_lowpass_cutoff() +{ + const auto lowpass_cutoff = clamp( + eax_.flLowPassCutOff, + AL_DISTORTION_MIN_LOWPASS_CUTOFF, + AL_DISTORTION_MAX_LOWPASS_CUTOFF); + + al_effect_props_.Distortion.LowpassCutoff = lowpass_cutoff; +} + +void EaxDistortionEffect::set_efx_eq_center() +{ + const auto eq_center = clamp( + eax_.flEQCenter, + AL_DISTORTION_MIN_EQCENTER, + AL_DISTORTION_MAX_EQCENTER); + + al_effect_props_.Distortion.EQCenter = eq_center; +} + +void EaxDistortionEffect::set_efx_eq_bandwidth() +{ + const auto eq_bandwidth = clamp( + eax_.flEdge, + AL_DISTORTION_MIN_EQBANDWIDTH, + AL_DISTORTION_MAX_EQBANDWIDTH); + + al_effect_props_.Distortion.EQBandwidth = eq_bandwidth; +} + +void EaxDistortionEffect::set_efx_defaults() +{ + set_efx_edge(); + set_efx_gain(); + set_efx_lowpass_cutoff(); + set_efx_eq_center(); + set_efx_eq_bandwidth(); +} + +// [[nodiscard]] +bool EaxDistortionEffect::get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXDISTORTION_NONE: + break; + + case EAXDISTORTION_ALLPARAMETERS: + eax_call.set_value(eax_); + break; + + case EAXDISTORTION_EDGE: + eax_call.set_value(eax_.flEdge); + break; + + case EAXDISTORTION_GAIN: + eax_call.set_value(eax_.lGain); + break; + + case EAXDISTORTION_LOWPASSCUTOFF: + eax_call.set_value(eax_.flLowPassCutOff); + break; + + case EAXDISTORTION_EQCENTER: + eax_call.set_value(eax_.flEQCenter); + break; + + case EAXDISTORTION_EQBANDWIDTH: + eax_call.set_value(eax_.flEQBandwidth); + break; + + default: + throw EaxDistortionEffectException{"Unsupported property id."}; + } + + return false; +} + +void EaxDistortionEffect::validate_edge( + float flEdge) +{ + eax_validate_range( + "Edge", + flEdge, + EAXDISTORTION_MINEDGE, + EAXDISTORTION_MAXEDGE); +} + +void EaxDistortionEffect::validate_gain( + long lGain) +{ + eax_validate_range( + "Gain", + lGain, + EAXDISTORTION_MINGAIN, + EAXDISTORTION_MAXGAIN); +} + +void EaxDistortionEffect::validate_lowpass_cutoff( + float flLowPassCutOff) +{ + eax_validate_range( + "Low-pass Cut-off", + flLowPassCutOff, + EAXDISTORTION_MINLOWPASSCUTOFF, + EAXDISTORTION_MAXLOWPASSCUTOFF); +} + +void EaxDistortionEffect::validate_eq_center( + float flEQCenter) +{ + eax_validate_range( + "EQ Center", + flEQCenter, + EAXDISTORTION_MINEQCENTER, + EAXDISTORTION_MAXEQCENTER); +} + +void EaxDistortionEffect::validate_eq_bandwidth( + float flEQBandwidth) +{ + eax_validate_range( + "EQ Bandwidth", + flEQBandwidth, + EAXDISTORTION_MINEQBANDWIDTH, + EAXDISTORTION_MAXEQBANDWIDTH); +} + +void EaxDistortionEffect::validate_all( + const EAXDISTORTIONPROPERTIES& eax_all) +{ + validate_edge(eax_all.flEdge); + validate_gain(eax_all.lGain); + validate_lowpass_cutoff(eax_all.flLowPassCutOff); + validate_eq_center(eax_all.flEQCenter); + validate_eq_bandwidth(eax_all.flEQBandwidth); +} + +void EaxDistortionEffect::defer_edge( + float flEdge) +{ + eax_d_.flEdge = flEdge; + eax_dirty_flags_.flEdge = (eax_.flEdge != eax_d_.flEdge); +} + +void EaxDistortionEffect::defer_gain( + long lGain) +{ + eax_d_.lGain = lGain; + eax_dirty_flags_.lGain = (eax_.lGain != eax_d_.lGain); +} + +void EaxDistortionEffect::defer_low_pass_cutoff( + float flLowPassCutOff) +{ + eax_d_.flLowPassCutOff = flLowPassCutOff; + eax_dirty_flags_.flLowPassCutOff = (eax_.flLowPassCutOff != eax_d_.flLowPassCutOff); +} + +void EaxDistortionEffect::defer_eq_center( + float flEQCenter) +{ + eax_d_.flEQCenter = flEQCenter; + eax_dirty_flags_.flEQCenter = (eax_.flEQCenter != eax_d_.flEQCenter); +} + +void EaxDistortionEffect::defer_eq_bandwidth( + float flEQBandwidth) +{ + eax_d_.flEQBandwidth = flEQBandwidth; + eax_dirty_flags_.flEQBandwidth = (eax_.flEQBandwidth != eax_d_.flEQBandwidth); +} + +void EaxDistortionEffect::defer_all( + const EAXDISTORTIONPROPERTIES& eax_all) +{ + defer_edge(eax_all.flEdge); + defer_gain(eax_all.lGain); + defer_low_pass_cutoff(eax_all.flLowPassCutOff); + defer_eq_center(eax_all.flEQCenter); + defer_eq_bandwidth(eax_all.flEQBandwidth); +} + +void EaxDistortionEffect::defer_edge( + const EaxEaxCall& eax_call) +{ + const auto& edge = + eax_call.get_value(); + + validate_edge(edge); + defer_edge(edge); +} + +void EaxDistortionEffect::defer_gain( + const EaxEaxCall& eax_call) +{ + const auto& gain = + eax_call.get_value(); + + validate_gain(gain); + defer_gain(gain); +} + +void EaxDistortionEffect::defer_low_pass_cutoff( + const EaxEaxCall& eax_call) +{ + const auto& lowpass_cutoff = + eax_call.get_value(); + + validate_lowpass_cutoff(lowpass_cutoff); + defer_low_pass_cutoff(lowpass_cutoff); +} + +void EaxDistortionEffect::defer_eq_center( + const EaxEaxCall& eax_call) +{ + const auto& eq_center = + eax_call.get_value(); + + validate_eq_center(eq_center); + defer_eq_center(eq_center); +} + +void EaxDistortionEffect::defer_eq_bandwidth( + const EaxEaxCall& eax_call) +{ + const auto& eq_bandwidth = + eax_call.get_value(); + + validate_eq_bandwidth(eq_bandwidth); + defer_eq_bandwidth(eq_bandwidth); +} + +void EaxDistortionEffect::defer_all( + const EaxEaxCall& eax_call) +{ + const auto& all = + eax_call.get_value(); + + validate_all(all); + defer_all(all); +} + +// [[nodiscard]] +bool EaxDistortionEffect::apply_deferred() +{ + if (eax_dirty_flags_ == EaxDistortionEffectDirtyFlags{}) + { + return false; + } + + eax_ = eax_d_; + + if (eax_dirty_flags_.flEdge) + { + set_efx_edge(); + } + + if (eax_dirty_flags_.lGain) + { + set_efx_gain(); + } + + if (eax_dirty_flags_.flLowPassCutOff) + { + set_efx_lowpass_cutoff(); + } + + if (eax_dirty_flags_.flEQCenter) + { + set_efx_eq_center(); + } + + if (eax_dirty_flags_.flEQBandwidth) + { + set_efx_eq_bandwidth(); + } + + eax_dirty_flags_ = EaxDistortionEffectDirtyFlags{}; + + return true; +} + +// [[nodiscard]] +bool EaxDistortionEffect::set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXDISTORTION_NONE: + break; + + case EAXDISTORTION_ALLPARAMETERS: + defer_all(eax_call); + break; + + case EAXDISTORTION_EDGE: + defer_edge(eax_call); + break; + + case EAXDISTORTION_GAIN: + defer_gain(eax_call); + break; + + case EAXDISTORTION_LOWPASSCUTOFF: + defer_low_pass_cutoff(eax_call); + break; + + case EAXDISTORTION_EQCENTER: + defer_eq_center(eax_call); + break; + + case EAXDISTORTION_EQBANDWIDTH: + defer_eq_bandwidth(eax_call); + break; + + default: + throw EaxDistortionEffectException{"Unsupported property id."}; + } + + if (!eax_call.is_deferred()) + { + return apply_deferred(); + } + + return false; +} + + +} // namespace + + +EaxEffectUPtr eax_create_eax_distortion_effect( + EffectProps& al_effect_props) +{ + return std::make_unique(al_effect_props); +} + + +#endif // ALSOFT_EAX diff --git a/al/effects/echo.cpp b/al/effects/echo.cpp index 65f691c6..5ceb161d 100644 --- a/al/effects/echo.cpp +++ b/al/effects/echo.cpp @@ -7,6 +7,13 @@ #include "alc/effects/base.h" #include "effects.h" +#if ALSOFT_EAX +#include "alnumeric.h" + +#include "al/eax_exception.h" +#include "al/eax_utils.h" +#endif // ALSOFT_EAX + namespace { @@ -109,3 +116,530 @@ EffectProps genDefaultProps() noexcept DEFINE_ALEFFECT_VTABLE(Echo); const EffectProps EchoEffectProps{genDefaultProps()}; + +#if ALSOFT_EAX +namespace +{ + + +using EaxEchoEffectDirtyFlagsValue = std::uint_least8_t; + +struct EaxEchoEffectDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxEchoEffectDirtyFlagsValue flDelay : 1; + EaxEchoEffectDirtyFlagsValue flLRDelay : 1; + EaxEchoEffectDirtyFlagsValue flDamping : 1; + EaxEchoEffectDirtyFlagsValue flFeedback : 1; + EaxEchoEffectDirtyFlagsValue flSpread : 1; +}; // EaxEchoEffectDirtyFlags + + +class EaxEchoEffect final : + public EaxEffect +{ +public: + EaxEchoEffect( + EffectProps& al_effect_props); + + + // [[nodiscard]] + bool dispatch( + const EaxEaxCall& eax_call) override; + + +private: + EffectProps& al_effect_props_; + + EAXECHOPROPERTIES eax_{}; + EAXECHOPROPERTIES eax_d_{}; + EaxEchoEffectDirtyFlags eax_dirty_flags_{}; + + + void set_eax_defaults(); + + + void set_efx_delay(); + + void set_efx_lr_delay(); + + void set_efx_damping(); + + void set_efx_feedback(); + + void set_efx_spread(); + + void set_efx_defaults(); + + + // [[nodiscard]] + bool get( + const EaxEaxCall& eax_call); + + + void validate_delay( + float flDelay); + + void validate_lr_delay( + float flLRDelay); + + void validate_damping( + float flDamping); + + void validate_feedback( + float flFeedback); + + void validate_spread( + float flSpread); + + void validate_all( + const EAXECHOPROPERTIES& all); + + + void defer_delay( + float flDelay); + + void defer_lr_delay( + float flLRDelay); + + void defer_damping( + float flDamping); + + void defer_feedback( + float flFeedback); + + void defer_spread( + float flSpread); + + void defer_all( + const EAXECHOPROPERTIES& all); + + + void defer_delay( + const EaxEaxCall& eax_call); + + void defer_lr_delay( + const EaxEaxCall& eax_call); + + void defer_damping( + const EaxEaxCall& eax_call); + + void defer_feedback( + const EaxEaxCall& eax_call); + + void defer_spread( + const EaxEaxCall& eax_call); + + void defer_all( + const EaxEaxCall& eax_call); + + + bool apply_deferred(); + + bool set( + const EaxEaxCall& eax_call); +}; // EaxEchoEffect + + +class EaxEchoEffectException : + public EaxException +{ +public: + explicit EaxEchoEffectException( + const char* message) + : + EaxException{"EAX_ECHO_EFFECT", message} + { + } +}; // EaxEchoEffectException + + +EaxEchoEffect::EaxEchoEffect( + EffectProps& al_effect_props) + : + al_effect_props_{al_effect_props} +{ + set_eax_defaults(); + set_efx_defaults(); +} + +// [[nodiscard]] +bool EaxEchoEffect::dispatch( + const EaxEaxCall& eax_call) +{ + return eax_call.is_get() ? get(eax_call) : set(eax_call); +} + +void EaxEchoEffect::set_eax_defaults() +{ + eax_.flDelay = EAXECHO_DEFAULTDELAY; + eax_.flLRDelay = EAXECHO_DEFAULTLRDELAY; + eax_.flDamping = EAXECHO_DEFAULTDAMPING; + eax_.flFeedback = EAXECHO_DEFAULTFEEDBACK; + eax_.flSpread = EAXECHO_DEFAULTSPREAD; + + eax_d_ = eax_; +} + +void EaxEchoEffect::set_efx_delay() +{ + const auto delay = clamp( + eax_.flDelay, + AL_ECHO_MIN_DELAY, + AL_ECHO_MAX_DELAY); + + al_effect_props_.Echo.Delay = delay; +} + +void EaxEchoEffect::set_efx_lr_delay() +{ + const auto lr_delay = clamp( + eax_.flLRDelay, + AL_ECHO_MIN_LRDELAY, + AL_ECHO_MAX_LRDELAY); + + al_effect_props_.Echo.LRDelay = lr_delay; +} + +void EaxEchoEffect::set_efx_damping() +{ + const auto damping = clamp( + eax_.flDamping, + AL_ECHO_MIN_DAMPING, + AL_ECHO_MAX_DAMPING); + + al_effect_props_.Echo.Damping = damping; +} + +void EaxEchoEffect::set_efx_feedback() +{ + const auto feedback = clamp( + eax_.flFeedback, + AL_ECHO_MIN_FEEDBACK, + AL_ECHO_MAX_FEEDBACK); + + al_effect_props_.Echo.Feedback = feedback; +} + +void EaxEchoEffect::set_efx_spread() +{ + const auto spread = clamp( + eax_.flSpread, + AL_ECHO_MIN_SPREAD, + AL_ECHO_MAX_SPREAD); + + al_effect_props_.Echo.Spread = spread; +} + +void EaxEchoEffect::set_efx_defaults() +{ + set_efx_delay(); + set_efx_lr_delay(); + set_efx_damping(); + set_efx_feedback(); + set_efx_spread(); +} + +// [[nodiscard]] +bool EaxEchoEffect::get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXECHO_NONE: + break; + + case EAXECHO_ALLPARAMETERS: + eax_call.set_value(eax_); + break; + + case EAXECHO_DELAY: + eax_call.set_value(eax_.flDelay); + break; + + case EAXECHO_LRDELAY: + eax_call.set_value(eax_.flLRDelay); + break; + + case EAXECHO_DAMPING: + eax_call.set_value(eax_.flDamping); + break; + + case EAXECHO_FEEDBACK: + eax_call.set_value(eax_.flFeedback); + break; + + case EAXECHO_SPREAD: + eax_call.set_value(eax_.flSpread); + break; + + default: + throw EaxEchoEffectException{"Unsupported property id."}; + } + + return false; +} + +void EaxEchoEffect::validate_delay( + float flDelay) +{ + eax_validate_range( + "Delay", + flDelay, + EAXECHO_MINDELAY, + EAXECHO_MAXDELAY); +} + +void EaxEchoEffect::validate_lr_delay( + float flLRDelay) +{ + eax_validate_range( + "LR Delay", + flLRDelay, + EAXECHO_MINLRDELAY, + EAXECHO_MAXLRDELAY); +} + +void EaxEchoEffect::validate_damping( + float flDamping) +{ + eax_validate_range( + "Damping", + flDamping, + EAXECHO_MINDAMPING, + EAXECHO_MAXDAMPING); +} + +void EaxEchoEffect::validate_feedback( + float flFeedback) +{ + eax_validate_range( + "Feedback", + flFeedback, + EAXECHO_MINFEEDBACK, + EAXECHO_MAXFEEDBACK); +} + +void EaxEchoEffect::validate_spread( + float flSpread) +{ + eax_validate_range( + "Spread", + flSpread, + EAXECHO_MINSPREAD, + EAXECHO_MAXSPREAD); +} + +void EaxEchoEffect::validate_all( + const EAXECHOPROPERTIES& all) +{ + validate_delay(all.flDelay); + validate_lr_delay(all.flLRDelay); + validate_damping(all.flDamping); + validate_feedback(all.flFeedback); + validate_spread(all.flSpread); +} + +void EaxEchoEffect::defer_delay( + float flDelay) +{ + eax_d_.flDelay = flDelay; + eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay); +} + +void EaxEchoEffect::defer_lr_delay( + float flLRDelay) +{ + eax_d_.flLRDelay = flLRDelay; + eax_dirty_flags_.flLRDelay = (eax_.flLRDelay != eax_d_.flLRDelay); +} + +void EaxEchoEffect::defer_damping( + float flDamping) +{ + eax_d_.flDamping = flDamping; + eax_dirty_flags_.flDamping = (eax_.flDamping != eax_d_.flDamping); +} + +void EaxEchoEffect::defer_feedback( + float flFeedback) +{ + eax_d_.flFeedback = flFeedback; + eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback); +} + +void EaxEchoEffect::defer_spread( + float flSpread) +{ + eax_d_.flSpread = flSpread; + eax_dirty_flags_.flSpread = (eax_.flSpread != eax_d_.flSpread); +} + +void EaxEchoEffect::defer_all( + const EAXECHOPROPERTIES& all) +{ + defer_delay(all.flDelay); + defer_lr_delay(all.flLRDelay); + defer_damping(all.flDamping); + defer_feedback(all.flFeedback); + defer_spread(all.flSpread); +} + +void EaxEchoEffect::defer_delay( + const EaxEaxCall& eax_call) +{ + const auto& delay = + eax_call.get_value(); + + validate_delay(delay); + defer_delay(delay); +} + +void EaxEchoEffect::defer_lr_delay( + const EaxEaxCall& eax_call) +{ + const auto& lr_delay = + eax_call.get_value(); + + validate_lr_delay(lr_delay); + defer_lr_delay(lr_delay); +} + +void EaxEchoEffect::defer_damping( + const EaxEaxCall& eax_call) +{ + const auto& damping = + eax_call.get_value(); + + validate_damping(damping); + defer_damping(damping); +} + +void EaxEchoEffect::defer_feedback( + const EaxEaxCall& eax_call) +{ + const auto& feedback = + eax_call.get_value(); + + validate_feedback(feedback); + defer_feedback(feedback); +} + +void EaxEchoEffect::defer_spread( + const EaxEaxCall& eax_call) +{ + const auto& spread = + eax_call.get_value(); + + validate_spread(spread); + defer_spread(spread); +} + +void EaxEchoEffect::defer_all( + const EaxEaxCall& eax_call) +{ + const auto& all = + eax_call.get_value(); + + validate_all(all); + defer_all(all); +} + +// [[nodiscard]] +bool EaxEchoEffect::apply_deferred() +{ + if (eax_dirty_flags_ == EaxEchoEffectDirtyFlags{}) + { + return false; + } + + eax_ = eax_d_; + + if (eax_dirty_flags_.flDelay) + { + set_efx_delay(); + } + + if (eax_dirty_flags_.flLRDelay) + { + set_efx_lr_delay(); + } + + if (eax_dirty_flags_.flDamping) + { + set_efx_damping(); + } + + if (eax_dirty_flags_.flFeedback) + { + set_efx_feedback(); + } + + if (eax_dirty_flags_.flSpread) + { + set_efx_spread(); + } + + eax_dirty_flags_ = EaxEchoEffectDirtyFlags{}; + + return true; +} + +// [[nodiscard]] +bool EaxEchoEffect::set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXECHO_NONE: + break; + + case EAXECHO_ALLPARAMETERS: + defer_all(eax_call); + break; + + case EAXECHO_DELAY: + defer_delay(eax_call); + break; + + case EAXECHO_LRDELAY: + defer_lr_delay(eax_call); + break; + + case EAXECHO_DAMPING: + defer_damping(eax_call); + break; + + case EAXECHO_FEEDBACK: + defer_feedback(eax_call); + break; + + case EAXECHO_SPREAD: + defer_spread(eax_call); + break; + + default: + throw EaxEchoEffectException{"Unsupported property id."}; + } + + if (!eax_call.is_deferred()) + { + return apply_deferred(); + } + + return false; +} + + +} // namespace + + +EaxEffectUPtr eax_create_eax_echo_effect( + EffectProps& al_effect_props) +{ + return std::make_unique(al_effect_props); +} + + +#endif // ALSOFT_EAX diff --git a/al/effects/effects.cpp b/al/effects/effects.cpp new file mode 100644 index 00000000..55abfdc5 --- /dev/null +++ b/al/effects/effects.cpp @@ -0,0 +1,106 @@ +#if ALSOFT_EAX + + +#include + +#include "AL/efx.h" + +#include "effects.h" + + +EaxEffectUPtr eax_create_eax_null_effect(); + +EaxEffectUPtr eax_create_eax_chorus_effect( + EffectProps& al_effect_props); + +EaxEffectUPtr eax_create_eax_distortion_effect( + EffectProps& al_effect_props); + +EaxEffectUPtr eax_create_eax_echo_effect( + EffectProps& al_effect_props); + +EaxEffectUPtr eax_create_eax_flanger_effect( + EffectProps& al_effect_props); + +EaxEffectUPtr eax_create_eax_frequency_shifter_effect( + EffectProps& al_effect_props); + +EaxEffectUPtr eax_create_eax_vocal_morpher_effect( + EffectProps& al_effect_props); + +EaxEffectUPtr eax_create_eax_pitch_shifter_effect( + EffectProps& al_effect_props); + +EaxEffectUPtr eax_create_eax_ring_modulator_effect( + EffectProps& al_effect_props); + +EaxEffectUPtr eax_create_eax_auto_wah_effect( + EffectProps& al_effect_props); + +EaxEffectUPtr eax_create_eax_compressor_effect( + EffectProps& al_effect_props); + +EaxEffectUPtr eax_create_eax_equalizer_effect( + EffectProps& al_effect_props); + +EaxEffectUPtr eax_create_eax_reverb_effect( + EffectProps& al_effect_props); + + +EaxEffectUPtr eax_create_eax_effect( + ALenum al_effect_type, + EffectProps& al_effect_props) +{ +#define EAX_PREFIX "[EAX_MAKE_EAX_EFFECT] " + + switch (al_effect_type) + { + case AL_EFFECT_NULL: + return eax_create_eax_null_effect(); + + case AL_EFFECT_CHORUS: + return eax_create_eax_chorus_effect(al_effect_props); + + case AL_EFFECT_DISTORTION: + return eax_create_eax_distortion_effect(al_effect_props); + + case AL_EFFECT_ECHO: + return eax_create_eax_echo_effect(al_effect_props); + + case AL_EFFECT_FLANGER: + return eax_create_eax_flanger_effect(al_effect_props); + + case AL_EFFECT_FREQUENCY_SHIFTER: + return eax_create_eax_frequency_shifter_effect(al_effect_props); + + case AL_EFFECT_VOCAL_MORPHER: + return eax_create_eax_vocal_morpher_effect(al_effect_props); + + case AL_EFFECT_PITCH_SHIFTER: + return eax_create_eax_pitch_shifter_effect(al_effect_props); + + case AL_EFFECT_RING_MODULATOR: + return eax_create_eax_ring_modulator_effect(al_effect_props); + + case AL_EFFECT_AUTOWAH: + return eax_create_eax_auto_wah_effect(al_effect_props); + + case AL_EFFECT_COMPRESSOR: + return eax_create_eax_compressor_effect(al_effect_props); + + case AL_EFFECT_EQUALIZER: + return eax_create_eax_equalizer_effect(al_effect_props); + + case AL_EFFECT_EAXREVERB: + return eax_create_eax_reverb_effect(al_effect_props); + + default: + assert(false && "Unsupported AL effect type."); + return nullptr; + } + +#undef EAX_PREFIX +} + + +#endif // ALSOFT_EAX diff --git a/al/effects/effects.h b/al/effects/effects.h index 30b4bd75..6813beaa 100644 --- a/al/effects/effects.h +++ b/al/effects/effects.h @@ -5,6 +5,10 @@ #include "core/except.h" +#if ALSOFT_EAX +#include "al/eax_effect.h" +#endif // ALSOFT_EAX + union EffectProps; @@ -80,4 +84,11 @@ extern const EffectVtable VmorpherEffectVtable; extern const EffectVtable DedicatedEffectVtable; extern const EffectVtable ConvolutionEffectVtable; + +#if ALSOFT_EAX +EaxEffectUPtr eax_create_eax_effect( + ALenum al_effect_type, + EffectProps& al_effect_props); +#endif // ALSOFT_EAX + #endif /* AL_EFFECTS_EFFECTS_H */ diff --git a/al/effects/equalizer.cpp b/al/effects/equalizer.cpp index 3c039688..c052db3e 100644 --- a/al/effects/equalizer.cpp +++ b/al/effects/equalizer.cpp @@ -7,6 +7,13 @@ #include "alc/effects/base.h" #include "effects.h" +#if ALSOFT_EAX +#include "alnumeric.h" + +#include "al/eax_exception.h" +#include "al/eax_utils.h" +#endif // ALSOFT_EAX + namespace { @@ -167,3 +174,862 @@ EffectProps genDefaultProps() noexcept DEFINE_ALEFFECT_VTABLE(Equalizer); const EffectProps EqualizerEffectProps{genDefaultProps()}; + +#if ALSOFT_EAX +namespace +{ + + +using EaxEqualizerEffectDirtyFlagsValue = std::uint_least16_t; + +struct EaxEqualizerEffectDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxEqualizerEffectDirtyFlagsValue lLowGain : 1; + EaxEqualizerEffectDirtyFlagsValue flLowCutOff : 1; + EaxEqualizerEffectDirtyFlagsValue lMid1Gain : 1; + EaxEqualizerEffectDirtyFlagsValue flMid1Center : 1; + EaxEqualizerEffectDirtyFlagsValue flMid1Width : 1; + EaxEqualizerEffectDirtyFlagsValue lMid2Gain : 1; + EaxEqualizerEffectDirtyFlagsValue flMid2Center : 1; + EaxEqualizerEffectDirtyFlagsValue flMid2Width : 1; + EaxEqualizerEffectDirtyFlagsValue lHighGain : 1; + EaxEqualizerEffectDirtyFlagsValue flHighCutOff : 1; +}; // EaxEqualizerEffectDirtyFlags + + +class EaxEqualizerEffect final : + public EaxEffect +{ +public: + EaxEqualizerEffect( + EffectProps& al_effect_props); + + + // [[nodiscard]] + bool dispatch( + const EaxEaxCall& eax_call) override; + + +private: + EffectProps& al_effect_props_; + + EAXEQUALIZERPROPERTIES eax_{}; + EAXEQUALIZERPROPERTIES eax_d_{}; + EaxEqualizerEffectDirtyFlags eax_dirty_flags_{}; + + + void set_eax_defaults(); + + + void set_efx_low_gain(); + + void set_efx_low_cutoff(); + + void set_efx_mid1_gain(); + + void set_efx_mid1_center(); + + void set_efx_mid1_width(); + + void set_efx_mid2_gain(); + + void set_efx_mid2_center(); + + void set_efx_mid2_width(); + + void set_efx_high_gain(); + + void set_efx_high_cutoff(); + + void set_efx_defaults(); + + + // [[nodiscard]] + bool get( + const EaxEaxCall& eax_call); + + + void validate_low_gain( + long lLowGain); + + void validate_low_cutoff( + float flLowCutOff); + + void validate_mid1_gain( + long lMid1Gain); + + void validate_mid1_center( + float flMid1Center); + + void validate_mid1_width( + float flMid1Width); + + void validate_mid2_gain( + long lMid2Gain); + + void validate_mid2_center( + float flMid2Center); + + void validate_mid2_width( + float flMid2Width); + + void validate_high_gain( + long lHighGain); + + void validate_high_cutoff( + float flHighCutOff); + + void validate_all( + const EAXEQUALIZERPROPERTIES& all); + + + void defer_low_gain( + long lLowGain); + + void defer_low_cutoff( + float flLowCutOff); + + void defer_mid1_gain( + long lMid1Gain); + + void defer_mid1_center( + float flMid1Center); + + void defer_mid1_width( + float flMid1Width); + + void defer_mid2_gain( + long lMid2Gain); + + void defer_mid2_center( + float flMid2Center); + + void defer_mid2_width( + float flMid2Width); + + void defer_high_gain( + long lHighGain); + + void defer_high_cutoff( + float flHighCutOff); + + void defer_all( + const EAXEQUALIZERPROPERTIES& all); + + + void defer_low_gain( + const EaxEaxCall& eax_call); + + void defer_low_cutoff( + const EaxEaxCall& eax_call); + + void defer_mid1_gain( + const EaxEaxCall& eax_call); + + void defer_mid1_center( + const EaxEaxCall& eax_call); + + void defer_mid1_width( + const EaxEaxCall& eax_call); + + void defer_mid2_gain( + const EaxEaxCall& eax_call); + + void defer_mid2_center( + const EaxEaxCall& eax_call); + + void defer_mid2_width( + const EaxEaxCall& eax_call); + + void defer_high_gain( + const EaxEaxCall& eax_call); + + void defer_high_cutoff( + const EaxEaxCall& eax_call); + + void defer_all( + const EaxEaxCall& eax_call); + + + // [[nodiscard]] + bool apply_deferred(); + + // [[nodiscard]] + bool set( + const EaxEaxCall& eax_call); +}; // EaxEqualizerEffect + + +class EaxEqualizerEffectException : + public EaxException +{ +public: + explicit EaxEqualizerEffectException( + const char* message) + : + EaxException{"EAX_EQUALIZER_EFFECT", message} + { + } +}; // EaxEqualizerEffectException + + +EaxEqualizerEffect::EaxEqualizerEffect( + EffectProps& al_effect_props) + : + al_effect_props_{al_effect_props} +{ + set_eax_defaults(); + set_efx_defaults(); +} + +// [[nodiscard]] +bool EaxEqualizerEffect::dispatch( + const EaxEaxCall& eax_call) +{ + return eax_call.is_get() ? get(eax_call) : set(eax_call); +} + +void EaxEqualizerEffect::set_eax_defaults() +{ + eax_.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN; + eax_.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF; + eax_.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN; + eax_.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER; + eax_.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH; + eax_.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN; + eax_.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER; + eax_.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH; + eax_.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN; + eax_.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF; + + eax_d_ = eax_; +} + +void EaxEqualizerEffect::set_efx_low_gain() +{ + const auto low_gain = clamp( + level_mb_to_gain(static_cast(eax_.lLowGain)), + AL_EQUALIZER_MIN_LOW_GAIN, + AL_EQUALIZER_MAX_LOW_GAIN); + + al_effect_props_.Equalizer.LowGain = low_gain; +} + +void EaxEqualizerEffect::set_efx_low_cutoff() +{ + const auto low_cutoff = clamp( + eax_.flLowCutOff, + AL_EQUALIZER_MIN_LOW_CUTOFF, + AL_EQUALIZER_MAX_LOW_CUTOFF); + + al_effect_props_.Equalizer.LowCutoff = low_cutoff; +} + +void EaxEqualizerEffect::set_efx_mid1_gain() +{ + const auto mid1_gain = clamp( + level_mb_to_gain(static_cast(eax_.lMid1Gain)), + AL_EQUALIZER_MIN_MID1_GAIN, + AL_EQUALIZER_MAX_MID1_GAIN); + + al_effect_props_.Equalizer.Mid1Gain = mid1_gain; +} + +void EaxEqualizerEffect::set_efx_mid1_center() +{ + const auto mid1_center = clamp( + eax_.flMid1Center, + AL_EQUALIZER_MIN_MID1_CENTER, + AL_EQUALIZER_MAX_MID1_CENTER); + + al_effect_props_.Equalizer.Mid1Center = mid1_center; +} + +void EaxEqualizerEffect::set_efx_mid1_width() +{ + const auto mid1_width = clamp( + eax_.flMid1Width, + AL_EQUALIZER_MIN_MID1_WIDTH, + AL_EQUALIZER_MAX_MID1_WIDTH); + + al_effect_props_.Equalizer.Mid1Width = mid1_width; +} + +void EaxEqualizerEffect::set_efx_mid2_gain() +{ + const auto mid2_gain = clamp( + level_mb_to_gain(static_cast(eax_.lMid2Gain)), + AL_EQUALIZER_MIN_MID2_GAIN, + AL_EQUALIZER_MAX_MID2_GAIN); + + al_effect_props_.Equalizer.Mid2Gain = mid2_gain; +} + +void EaxEqualizerEffect::set_efx_mid2_center() +{ + const auto mid2_center = clamp( + eax_.flMid2Center, + AL_EQUALIZER_MIN_MID2_CENTER, + AL_EQUALIZER_MAX_MID2_CENTER); + + al_effect_props_.Equalizer.Mid2Center = mid2_center; +} + +void EaxEqualizerEffect::set_efx_mid2_width() +{ + const auto mid2_width = clamp( + eax_.flMid2Width, + AL_EQUALIZER_MIN_MID2_WIDTH, + AL_EQUALIZER_MAX_MID2_WIDTH); + + al_effect_props_.Equalizer.Mid2Width = mid2_width; +} + +void EaxEqualizerEffect::set_efx_high_gain() +{ + const auto high_gain = clamp( + level_mb_to_gain(static_cast(eax_.lHighGain)), + AL_EQUALIZER_MIN_HIGH_GAIN, + AL_EQUALIZER_MAX_HIGH_GAIN); + + al_effect_props_.Equalizer.HighGain = high_gain; +} + +void EaxEqualizerEffect::set_efx_high_cutoff() +{ + const auto high_cutoff = clamp( + eax_.flHighCutOff, + AL_EQUALIZER_MIN_HIGH_CUTOFF, + AL_EQUALIZER_MAX_HIGH_CUTOFF); + + al_effect_props_.Equalizer.HighCutoff = high_cutoff; +} + +void EaxEqualizerEffect::set_efx_defaults() +{ + set_efx_low_gain(); + set_efx_low_cutoff(); + set_efx_mid1_gain(); + set_efx_mid1_center(); + set_efx_mid1_width(); + set_efx_mid2_gain(); + set_efx_mid2_center(); + set_efx_mid2_width(); + set_efx_high_gain(); + set_efx_high_cutoff(); +} + +// [[nodiscard]] +bool EaxEqualizerEffect::get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXEQUALIZER_NONE: + break; + + case EAXEQUALIZER_ALLPARAMETERS: + eax_call.set_value(eax_); + break; + + case EAXEQUALIZER_LOWGAIN: + eax_call.set_value(eax_.lLowGain); + break; + + case EAXEQUALIZER_LOWCUTOFF: + eax_call.set_value(eax_.flLowCutOff); + break; + + case EAXEQUALIZER_MID1GAIN: + eax_call.set_value(eax_.lMid1Gain); + break; + + case EAXEQUALIZER_MID1CENTER: + eax_call.set_value(eax_.flMid1Center); + break; + + case EAXEQUALIZER_MID1WIDTH: + eax_call.set_value(eax_.flMid1Width); + break; + + case EAXEQUALIZER_MID2GAIN: + eax_call.set_value(eax_.lMid2Gain); + break; + + case EAXEQUALIZER_MID2CENTER: + eax_call.set_value(eax_.flMid2Center); + break; + + case EAXEQUALIZER_MID2WIDTH: + eax_call.set_value(eax_.flMid2Width); + break; + + case EAXEQUALIZER_HIGHGAIN: + eax_call.set_value(eax_.lHighGain); + break; + + case EAXEQUALIZER_HIGHCUTOFF: + eax_call.set_value(eax_.flHighCutOff); + break; + + default: + throw EaxEqualizerEffectException{"Unsupported property id."}; + } + + return false; +} + +void EaxEqualizerEffect::validate_low_gain( + long lLowGain) +{ + eax_validate_range( + "Low Gain", + lLowGain, + EAXEQUALIZER_MINLOWGAIN, + EAXEQUALIZER_MAXLOWGAIN); +} + +void EaxEqualizerEffect::validate_low_cutoff( + float flLowCutOff) +{ + eax_validate_range( + "Low Cutoff", + flLowCutOff, + EAXEQUALIZER_MINLOWCUTOFF, + EAXEQUALIZER_MAXLOWCUTOFF); +} + +void EaxEqualizerEffect::validate_mid1_gain( + long lMid1Gain) +{ + eax_validate_range( + "Mid1 Gain", + lMid1Gain, + EAXEQUALIZER_MINMID1GAIN, + EAXEQUALIZER_MAXMID1GAIN); +} + +void EaxEqualizerEffect::validate_mid1_center( + float flMid1Center) +{ + eax_validate_range( + "Mid1 Center", + flMid1Center, + EAXEQUALIZER_MINMID1CENTER, + EAXEQUALIZER_MAXMID1CENTER); +} + +void EaxEqualizerEffect::validate_mid1_width( + float flMid1Width) +{ + eax_validate_range( + "Mid1 Width", + flMid1Width, + EAXEQUALIZER_MINMID1WIDTH, + EAXEQUALIZER_MAXMID1WIDTH); +} + +void EaxEqualizerEffect::validate_mid2_gain( + long lMid2Gain) +{ + eax_validate_range( + "Mid2 Gain", + lMid2Gain, + EAXEQUALIZER_MINMID2GAIN, + EAXEQUALIZER_MAXMID2GAIN); +} + +void EaxEqualizerEffect::validate_mid2_center( + float flMid2Center) +{ + eax_validate_range( + "Mid2 Center", + flMid2Center, + EAXEQUALIZER_MINMID2CENTER, + EAXEQUALIZER_MAXMID2CENTER); +} + +void EaxEqualizerEffect::validate_mid2_width( + float flMid2Width) +{ + eax_validate_range( + "Mid2 Width", + flMid2Width, + EAXEQUALIZER_MINMID2WIDTH, + EAXEQUALIZER_MAXMID2WIDTH); +} + +void EaxEqualizerEffect::validate_high_gain( + long lHighGain) +{ + eax_validate_range( + "High Gain", + lHighGain, + EAXEQUALIZER_MINHIGHGAIN, + EAXEQUALIZER_MAXHIGHGAIN); +} + +void EaxEqualizerEffect::validate_high_cutoff( + float flHighCutOff) +{ + eax_validate_range( + "High Cutoff", + flHighCutOff, + EAXEQUALIZER_MINHIGHCUTOFF, + EAXEQUALIZER_MAXHIGHCUTOFF); +} + +void EaxEqualizerEffect::validate_all( + const EAXEQUALIZERPROPERTIES& all) +{ + validate_low_gain(all.lLowGain); + validate_low_cutoff(all.flLowCutOff); + validate_mid1_gain(all.lMid1Gain); + validate_mid1_center(all.flMid1Center); + validate_mid1_width(all.flMid1Width); + validate_mid2_gain(all.lMid2Gain); + validate_mid2_center(all.flMid2Center); + validate_mid2_width(all.flMid2Width); + validate_high_gain(all.lHighGain); + validate_high_cutoff(all.flHighCutOff); +} + +void EaxEqualizerEffect::defer_low_gain( + long lLowGain) +{ + eax_d_.lLowGain = lLowGain; + eax_dirty_flags_.lLowGain = (eax_.lLowGain != eax_d_.lLowGain); +} + +void EaxEqualizerEffect::defer_low_cutoff( + float flLowCutOff) +{ + eax_d_.flLowCutOff = flLowCutOff; + eax_dirty_flags_.flLowCutOff = (eax_.flLowCutOff != eax_d_.flLowCutOff); +} + +void EaxEqualizerEffect::defer_mid1_gain( + long lMid1Gain) +{ + eax_d_.lMid1Gain = lMid1Gain; + eax_dirty_flags_.lMid1Gain = (eax_.lMid1Gain != eax_d_.lMid1Gain); +} + +void EaxEqualizerEffect::defer_mid1_center( + float flMid1Center) +{ + eax_d_.flMid1Center = flMid1Center; + eax_dirty_flags_.flMid1Center = (eax_.flMid1Center != eax_d_.flMid1Center); +} + +void EaxEqualizerEffect::defer_mid1_width( + float flMid1Width) +{ + eax_d_.flMid1Width = flMid1Width; + eax_dirty_flags_.flMid1Width = (eax_.flMid1Width != eax_d_.flMid1Width); +} + +void EaxEqualizerEffect::defer_mid2_gain( + long lMid2Gain) +{ + eax_d_.lMid2Gain = lMid2Gain; + eax_dirty_flags_.lMid2Gain = (eax_.lMid2Gain != eax_d_.lMid2Gain); +} + +void EaxEqualizerEffect::defer_mid2_center( + float flMid2Center) +{ + eax_d_.flMid2Center = flMid2Center; + eax_dirty_flags_.flMid2Center = (eax_.flMid2Center != eax_d_.flMid2Center); +} + +void EaxEqualizerEffect::defer_mid2_width( + float flMid2Width) +{ + eax_d_.flMid2Width = flMid2Width; + eax_dirty_flags_.flMid2Width = (eax_.flMid2Width != eax_d_.flMid2Width); +} + +void EaxEqualizerEffect::defer_high_gain( + long lHighGain) +{ + eax_d_.lHighGain = lHighGain; + eax_dirty_flags_.lHighGain = (eax_.lHighGain != eax_d_.lHighGain); +} + +void EaxEqualizerEffect::defer_high_cutoff( + float flHighCutOff) +{ + eax_d_.flHighCutOff = flHighCutOff; + eax_dirty_flags_.flHighCutOff = (eax_.flHighCutOff != eax_d_.flHighCutOff); +} + +void EaxEqualizerEffect::defer_all( + const EAXEQUALIZERPROPERTIES& all) +{ + defer_low_gain(all.lLowGain); + defer_low_cutoff(all.flLowCutOff); + defer_mid1_gain(all.lMid1Gain); + defer_mid1_center(all.flMid1Center); + defer_mid1_width(all.flMid1Width); + defer_mid2_gain(all.lMid2Gain); + defer_mid2_center(all.flMid2Center); + defer_mid2_width(all.flMid2Width); + defer_high_gain(all.lHighGain); + defer_high_cutoff(all.flHighCutOff); +} + +void EaxEqualizerEffect::defer_low_gain( + const EaxEaxCall& eax_call) +{ + const auto& low_gain = + eax_call.get_value(); + + validate_low_gain(low_gain); + defer_low_gain(low_gain); +} + +void EaxEqualizerEffect::defer_low_cutoff( + const EaxEaxCall& eax_call) +{ + const auto& low_cutoff = + eax_call.get_value(); + + validate_low_cutoff(low_cutoff); + defer_low_cutoff(low_cutoff); +} + +void EaxEqualizerEffect::defer_mid1_gain( + const EaxEaxCall& eax_call) +{ + const auto& mid1_gain = + eax_call.get_value(); + + validate_mid1_gain(mid1_gain); + defer_mid1_gain(mid1_gain); +} + +void EaxEqualizerEffect::defer_mid1_center( + const EaxEaxCall& eax_call) +{ + const auto& mid1_center = + eax_call.get_value(); + + validate_mid1_center(mid1_center); + defer_mid1_center(mid1_center); +} + +void EaxEqualizerEffect::defer_mid1_width( + const EaxEaxCall& eax_call) +{ + const auto& mid1_width = + eax_call.get_value(); + + validate_mid1_width(mid1_width); + defer_mid1_width(mid1_width); +} + +void EaxEqualizerEffect::defer_mid2_gain( + const EaxEaxCall& eax_call) +{ + const auto& mid2_gain = + eax_call.get_value(); + + validate_mid2_gain(mid2_gain); + defer_mid2_gain(mid2_gain); +} + +void EaxEqualizerEffect::defer_mid2_center( + const EaxEaxCall& eax_call) +{ + const auto& mid2_center = + eax_call.get_value(); + + validate_mid2_center(mid2_center); + defer_mid2_center(mid2_center); +} + +void EaxEqualizerEffect::defer_mid2_width( + const EaxEaxCall& eax_call) +{ + const auto& mid2_width = + eax_call.get_value(); + + validate_mid2_width(mid2_width); + defer_mid2_width(mid2_width); +} + +void EaxEqualizerEffect::defer_high_gain( + const EaxEaxCall& eax_call) +{ + const auto& high_gain = + eax_call.get_value(); + + validate_high_gain(high_gain); + defer_high_gain(high_gain); +} + +void EaxEqualizerEffect::defer_high_cutoff( + const EaxEaxCall& eax_call) +{ + const auto& high_cutoff = + eax_call.get_value(); + + validate_high_cutoff(high_cutoff); + defer_high_cutoff(high_cutoff); +} + +void EaxEqualizerEffect::defer_all( + const EaxEaxCall& eax_call) +{ + const auto& all = + eax_call.get_value(); + + validate_all(all); + defer_all(all); +} + +// [[nodiscard]] +bool EaxEqualizerEffect::apply_deferred() +{ + if (eax_dirty_flags_ == EaxEqualizerEffectDirtyFlags{}) + { + return false; + } + + eax_ = eax_d_; + + if (eax_dirty_flags_.lLowGain) + { + set_efx_low_gain(); + } + + if (eax_dirty_flags_.flLowCutOff) + { + set_efx_low_cutoff(); + } + + if (eax_dirty_flags_.lMid1Gain) + { + set_efx_mid1_gain(); + } + + if (eax_dirty_flags_.flMid1Center) + { + set_efx_mid1_center(); + } + + if (eax_dirty_flags_.flMid1Width) + { + set_efx_mid1_width(); + } + + if (eax_dirty_flags_.lMid2Gain) + { + set_efx_mid2_gain(); + } + + if (eax_dirty_flags_.flMid2Center) + { + set_efx_mid2_center(); + } + + if (eax_dirty_flags_.flMid2Width) + { + set_efx_mid2_width(); + } + + if (eax_dirty_flags_.lHighGain) + { + set_efx_high_gain(); + } + + if (eax_dirty_flags_.flHighCutOff) + { + set_efx_high_cutoff(); + } + + eax_dirty_flags_ = EaxEqualizerEffectDirtyFlags{}; + + return true; +} + +// [[nodiscard]] +bool EaxEqualizerEffect::set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXEQUALIZER_NONE: + break; + + case EAXEQUALIZER_ALLPARAMETERS: + defer_all(eax_call); + break; + + case EAXEQUALIZER_LOWGAIN: + defer_low_gain(eax_call); + break; + + case EAXEQUALIZER_LOWCUTOFF: + defer_low_cutoff(eax_call); + break; + + case EAXEQUALIZER_MID1GAIN: + defer_mid1_gain(eax_call); + break; + + case EAXEQUALIZER_MID1CENTER: + defer_mid1_center(eax_call); + break; + + case EAXEQUALIZER_MID1WIDTH: + defer_mid1_width(eax_call); + break; + + case EAXEQUALIZER_MID2GAIN: + defer_mid2_gain(eax_call); + break; + + case EAXEQUALIZER_MID2CENTER: + defer_mid2_center(eax_call); + break; + + case EAXEQUALIZER_MID2WIDTH: + defer_mid2_width(eax_call); + break; + + case EAXEQUALIZER_HIGHGAIN: + defer_high_gain(eax_call); + break; + + case EAXEQUALIZER_HIGHCUTOFF: + defer_high_cutoff(eax_call); + break; + + default: + throw EaxEqualizerEffectException{"Unsupported property id."}; + } + + if (!eax_call.is_deferred()) + { + return apply_deferred(); + } + + return false; +} + + +} // namespace + + +EaxEffectUPtr eax_create_eax_equalizer_effect( + EffectProps& al_effect_props) +{ + return std::make_unique(al_effect_props); +} + + +#endif // ALSOFT_EAX diff --git a/al/effects/fshifter.cpp b/al/effects/fshifter.cpp index 4aa860a2..aa4ddadb 100644 --- a/al/effects/fshifter.cpp +++ b/al/effects/fshifter.cpp @@ -10,6 +10,15 @@ #include "aloptional.h" #include "effects.h" +#if ALSOFT_EAX +#include + +#include "alnumeric.h" + +#include "al/eax_exception.h" +#include "al/eax_utils.h" +#endif // ALSOFT_EAX + namespace { @@ -128,3 +137,408 @@ EffectProps genDefaultProps() noexcept DEFINE_ALEFFECT_VTABLE(Fshifter); const EffectProps FshifterEffectProps{genDefaultProps()}; + +#if ALSOFT_EAX +namespace +{ + + +using EaxFrequencyShifterEffectDirtyFlagsValue = std::uint_least8_t; + +struct EaxFrequencyShifterEffectDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxFrequencyShifterEffectDirtyFlagsValue flFrequency : 1; + EaxFrequencyShifterEffectDirtyFlagsValue ulLeftDirection : 1; + EaxFrequencyShifterEffectDirtyFlagsValue ulRightDirection : 1; +}; // EaxFrequencyShifterEffectDirtyFlags + + +class EaxFrequencyShifterEffect final : + public EaxEffect +{ +public: + EaxFrequencyShifterEffect( + EffectProps& al_effect_props); + + + // [[nodiscard]] + bool dispatch( + const EaxEaxCall& eax_call) override; + + +private: + EffectProps& al_effect_props_; + + EAXFREQUENCYSHIFTERPROPERTIES eax_{}; + EAXFREQUENCYSHIFTERPROPERTIES eax_d_{}; + EaxFrequencyShifterEffectDirtyFlags eax_dirty_flags_{}; + + + void set_eax_defaults(); + + + void set_efx_frequency(); + + void set_efx_left_direction(); + + void set_efx_right_direction(); + + void set_efx_defaults(); + + + // [[nodiscard]] + bool get( + const EaxEaxCall& eax_call); + + + void validate_frequency( + float flFrequency); + + void validate_left_direction( + unsigned long ulLeftDirection); + + void validate_right_direction( + unsigned long ulRightDirection); + + void validate_all( + const EAXFREQUENCYSHIFTERPROPERTIES& all); + + + void defer_frequency( + float flFrequency); + + void defer_left_direction( + unsigned long ulLeftDirection); + + void defer_right_direction( + unsigned long ulRightDirection); + + void defer_all( + const EAXFREQUENCYSHIFTERPROPERTIES& all); + + + void defer_frequency( + const EaxEaxCall& eax_call); + + void defer_left_direction( + const EaxEaxCall& eax_call); + + void defer_right_direction( + const EaxEaxCall& eax_call); + + void defer_all( + const EaxEaxCall& eax_call); + + + // [[nodiscard]] + bool apply_deferred(); + + // [[nodiscard]] + bool set( + const EaxEaxCall& eax_call); +}; // EaxFrequencyShifterEffect + + +class EaxFrequencyShifterEffectException : + public EaxException +{ +public: + explicit EaxFrequencyShifterEffectException( + const char* message) + : + EaxException{"EAX_FREQUENCY_SHIFTER_EFFECT", message} + { + } +}; // EaxFrequencyShifterEffectException + + +EaxFrequencyShifterEffect::EaxFrequencyShifterEffect( + EffectProps& al_effect_props) + : + al_effect_props_{al_effect_props} +{ + set_eax_defaults(); + set_efx_defaults(); +} + +// [[nodiscard]] +bool EaxFrequencyShifterEffect::dispatch( + const EaxEaxCall& eax_call) +{ + return eax_call.is_get() ? get(eax_call) : set(eax_call); +} + +void EaxFrequencyShifterEffect::set_eax_defaults() +{ + eax_.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY; + eax_.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION; + eax_.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION; + + eax_d_ = eax_; +} + +void EaxFrequencyShifterEffect::set_efx_frequency() +{ + const auto frequency = clamp( + eax_.flFrequency, + AL_FREQUENCY_SHIFTER_MIN_FREQUENCY, + AL_FREQUENCY_SHIFTER_MAX_FREQUENCY); + + al_effect_props_.Fshifter.Frequency = frequency; +} + +void EaxFrequencyShifterEffect::set_efx_left_direction() +{ + const auto left_direction = clamp( + static_cast(eax_.ulLeftDirection), + AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION, + AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION); + + const auto efx_left_direction = DirectionFromEmum(left_direction); + assert(efx_left_direction.has_value()); + al_effect_props_.Fshifter.LeftDirection = *efx_left_direction; +} + +void EaxFrequencyShifterEffect::set_efx_right_direction() +{ + const auto right_direction = clamp( + static_cast(eax_.ulRightDirection), + AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION, + AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION); + + const auto efx_right_direction = DirectionFromEmum(right_direction); + assert(efx_right_direction.has_value()); + al_effect_props_.Fshifter.RightDirection = *efx_right_direction; +} + +void EaxFrequencyShifterEffect::set_efx_defaults() +{ + set_efx_frequency(); + set_efx_left_direction(); + set_efx_right_direction(); +} + +// [[nodiscard]] +bool EaxFrequencyShifterEffect::get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXFREQUENCYSHIFTER_NONE: + break; + + case EAXFREQUENCYSHIFTER_ALLPARAMETERS: + eax_call.set_value(eax_); + break; + + case EAXFREQUENCYSHIFTER_FREQUENCY: + eax_call.set_value(eax_.flFrequency); + break; + + case EAXFREQUENCYSHIFTER_LEFTDIRECTION: + eax_call.set_value(eax_.ulLeftDirection); + break; + + case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: + eax_call.set_value(eax_.ulRightDirection); + break; + + default: + throw EaxFrequencyShifterEffectException{"Unsupported property id."}; + } + + return false; +} + +void EaxFrequencyShifterEffect::validate_frequency( + float flFrequency) +{ + eax_validate_range( + "Frequency", + flFrequency, + EAXFREQUENCYSHIFTER_MINFREQUENCY, + EAXFREQUENCYSHIFTER_MAXFREQUENCY); +} + +void EaxFrequencyShifterEffect::validate_left_direction( + unsigned long ulLeftDirection) +{ + eax_validate_range( + "Left Direction", + ulLeftDirection, + EAXFREQUENCYSHIFTER_MINLEFTDIRECTION, + EAXFREQUENCYSHIFTER_MAXLEFTDIRECTION); +} + +void EaxFrequencyShifterEffect::validate_right_direction( + unsigned long ulRightDirection) +{ + eax_validate_range( + "Right Direction", + ulRightDirection, + EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION, + EAXFREQUENCYSHIFTER_MAXRIGHTDIRECTION); +} + +void EaxFrequencyShifterEffect::validate_all( + const EAXFREQUENCYSHIFTERPROPERTIES& all) +{ + validate_frequency(all.flFrequency); + validate_left_direction(all.ulLeftDirection); + validate_right_direction(all.ulRightDirection); +} + +void EaxFrequencyShifterEffect::defer_frequency( + float flFrequency) +{ + eax_d_.flFrequency = flFrequency; + eax_dirty_flags_.flFrequency = (eax_.flFrequency != eax_d_.flFrequency); +} + +void EaxFrequencyShifterEffect::defer_left_direction( + unsigned long ulLeftDirection) +{ + eax_d_.ulLeftDirection = ulLeftDirection; + eax_dirty_flags_.ulLeftDirection = (eax_.ulLeftDirection != eax_d_.ulLeftDirection); +} + +void EaxFrequencyShifterEffect::defer_right_direction( + unsigned long ulRightDirection) +{ + eax_d_.ulRightDirection = ulRightDirection; + eax_dirty_flags_.ulRightDirection = (eax_.ulRightDirection != eax_d_.ulRightDirection); +} + +void EaxFrequencyShifterEffect::defer_all( + const EAXFREQUENCYSHIFTERPROPERTIES& all) +{ + defer_frequency(all.flFrequency); + defer_left_direction(all.ulLeftDirection); + defer_right_direction(all.ulRightDirection); +} + +void EaxFrequencyShifterEffect::defer_frequency( + const EaxEaxCall& eax_call) +{ + const auto& frequency = + eax_call.get_value< + EaxFrequencyShifterEffectException, const decltype(EAXFREQUENCYSHIFTERPROPERTIES::flFrequency)>(); + + validate_frequency(frequency); + defer_frequency(frequency); +} + +void EaxFrequencyShifterEffect::defer_left_direction( + const EaxEaxCall& eax_call) +{ + const auto& left_direction = + eax_call.get_value< + EaxFrequencyShifterEffectException, const decltype(EAXFREQUENCYSHIFTERPROPERTIES::ulLeftDirection)>(); + + validate_left_direction(left_direction); + defer_left_direction(left_direction); +} + +void EaxFrequencyShifterEffect::defer_right_direction( + const EaxEaxCall& eax_call) +{ + const auto& right_direction = + eax_call.get_value< + EaxFrequencyShifterEffectException, const decltype(EAXFREQUENCYSHIFTERPROPERTIES::ulRightDirection)>(); + + validate_right_direction(right_direction); + defer_right_direction(right_direction); +} + +void EaxFrequencyShifterEffect::defer_all( + const EaxEaxCall& eax_call) +{ + const auto& all = + eax_call.get_value< + EaxFrequencyShifterEffectException, const EAXFREQUENCYSHIFTERPROPERTIES>(); + + validate_all(all); + defer_all(all); +} + +// [[nodiscard]] +bool EaxFrequencyShifterEffect::apply_deferred() +{ + if (eax_dirty_flags_ == EaxFrequencyShifterEffectDirtyFlags{}) + { + return false; + } + + eax_ = eax_d_; + + if (eax_dirty_flags_.flFrequency) + { + set_efx_frequency(); + } + + if (eax_dirty_flags_.ulLeftDirection) + { + set_efx_left_direction(); + } + + if (eax_dirty_flags_.ulRightDirection) + { + set_efx_right_direction(); + } + + eax_dirty_flags_ = EaxFrequencyShifterEffectDirtyFlags{}; + + return true; +} + +// [[nodiscard]] +bool EaxFrequencyShifterEffect::set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXFREQUENCYSHIFTER_NONE: + break; + + case EAXFREQUENCYSHIFTER_ALLPARAMETERS: + defer_all(eax_call); + break; + + case EAXFREQUENCYSHIFTER_FREQUENCY: + defer_frequency(eax_call); + break; + + case EAXFREQUENCYSHIFTER_LEFTDIRECTION: + defer_left_direction(eax_call); + break; + + case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: + defer_right_direction(eax_call); + break; + + default: + throw EaxFrequencyShifterEffectException{"Unsupported property id."}; + } + + if (!eax_call.is_deferred()) + { + return apply_deferred(); + } + + return false; +} + + +} // namespace + + +EaxEffectUPtr eax_create_eax_frequency_shifter_effect( + EffectProps& al_effect_props) +{ + return std::make_unique(al_effect_props); +} + + +#endif // ALSOFT_EAX diff --git a/al/effects/modulator.cpp b/al/effects/modulator.cpp index d8a1bed6..6a30dc09 100644 --- a/al/effects/modulator.cpp +++ b/al/effects/modulator.cpp @@ -10,6 +10,15 @@ #include "aloptional.h" #include "effects.h" +#if ALSOFT_EAX +#include + +#include "alnumeric.h" + +#include "al/eax_exception.h" +#include "al/eax_utils.h" +#endif // ALSOFT_EAX + namespace { @@ -134,3 +143,405 @@ EffectProps genDefaultProps() noexcept DEFINE_ALEFFECT_VTABLE(Modulator); const EffectProps ModulatorEffectProps{genDefaultProps()}; + +#if ALSOFT_EAX +namespace +{ + + +using EaxRingModulatorEffectDirtyFlagsValue = std::uint_least8_t; + +struct EaxRingModulatorEffectDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxRingModulatorEffectDirtyFlagsValue flFrequency : 1; + EaxRingModulatorEffectDirtyFlagsValue flHighPassCutOff : 1; + EaxRingModulatorEffectDirtyFlagsValue ulWaveform : 1; +}; // EaxPitchShifterEffectDirtyFlags + + +class EaxRingModulatorEffect final : + public EaxEffect +{ +public: + EaxRingModulatorEffect( + EffectProps& al_effect_props); + + + // [[nodiscard]] + bool dispatch( + const EaxEaxCall& eax_call) override; + + +private: + EffectProps& al_effect_props_; + + EAXRINGMODULATORPROPERTIES eax_{}; + EAXRINGMODULATORPROPERTIES eax_d_{}; + EaxRingModulatorEffectDirtyFlags eax_dirty_flags_{}; + + + void set_eax_defaults(); + + + void set_efx_frequency(); + + void set_efx_high_pass_cutoff(); + + void set_efx_waveform(); + + void set_efx_defaults(); + + + // [[nodiscard]] + bool get( + const EaxEaxCall& eax_call); + + + void validate_frequency( + float flFrequency); + + void validate_high_pass_cutoff( + float flHighPassCutOff); + + void validate_waveform( + unsigned long ulWaveform); + + void validate_all( + const EAXRINGMODULATORPROPERTIES& all); + + + void defer_frequency( + float flFrequency); + + void defer_high_pass_cutoff( + float flHighPassCutOff); + + void defer_waveform( + unsigned long ulWaveform); + + void defer_all( + const EAXRINGMODULATORPROPERTIES& all); + + + void defer_frequency( + const EaxEaxCall& eax_call); + + void defer_high_pass_cutoff( + const EaxEaxCall& eax_call); + + void defer_waveform( + const EaxEaxCall& eax_call); + + void defer_all( + const EaxEaxCall& eax_call); + + + // [[nodiscard]] + bool apply_deferred(); + + // [[nodiscard]] + bool set( + const EaxEaxCall& eax_call); +}; // EaxRingModulatorEffect + + +class EaxRingModulatorEffectException : + public EaxException +{ +public: + explicit EaxRingModulatorEffectException( + const char* message) + : + EaxException{"EAX_RING_MODULATOR_EFFECT", message} + { + } +}; // EaxRingModulatorEffectException + + +EaxRingModulatorEffect::EaxRingModulatorEffect( + EffectProps& al_effect_props) + : + al_effect_props_{al_effect_props} +{ + set_eax_defaults(); + set_efx_defaults(); +} + +// [[nodiscard]] +bool EaxRingModulatorEffect::dispatch( + const EaxEaxCall& eax_call) +{ + return eax_call.is_get() ? get(eax_call) : set(eax_call); +} + +void EaxRingModulatorEffect::set_eax_defaults() +{ + eax_.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY; + eax_.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF; + eax_.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM; + + eax_d_ = eax_; +} + +void EaxRingModulatorEffect::set_efx_frequency() +{ + const auto frequency = clamp( + eax_.flFrequency, + AL_RING_MODULATOR_MIN_FREQUENCY, + AL_RING_MODULATOR_MAX_FREQUENCY); + + al_effect_props_.Modulator.Frequency = frequency; +} + +void EaxRingModulatorEffect::set_efx_high_pass_cutoff() +{ + const auto high_pass_cutoff = clamp( + eax_.flHighPassCutOff, + AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF, + AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF); + + al_effect_props_.Modulator.HighPassCutoff = high_pass_cutoff; +} + +void EaxRingModulatorEffect::set_efx_waveform() +{ + const auto waveform = clamp( + static_cast(eax_.ulWaveform), + AL_RING_MODULATOR_MIN_WAVEFORM, + AL_RING_MODULATOR_MAX_WAVEFORM); + + const auto efx_waveform = WaveformFromEmum(waveform); + assert(efx_waveform.has_value()); + al_effect_props_.Modulator.Waveform = *efx_waveform; +} + +void EaxRingModulatorEffect::set_efx_defaults() +{ + set_efx_frequency(); + set_efx_high_pass_cutoff(); + set_efx_waveform(); +} + +// [[nodiscard]] +bool EaxRingModulatorEffect::get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXRINGMODULATOR_NONE: + break; + + case EAXRINGMODULATOR_ALLPARAMETERS: + eax_call.set_value(eax_); + break; + + case EAXRINGMODULATOR_FREQUENCY: + eax_call.set_value(eax_.flFrequency); + break; + + case EAXRINGMODULATOR_HIGHPASSCUTOFF: + eax_call.set_value(eax_.flHighPassCutOff); + break; + + case EAXRINGMODULATOR_WAVEFORM: + eax_call.set_value(eax_.ulWaveform); + break; + + default: + throw EaxRingModulatorEffectException{"Unsupported property id."}; + } + + return false; +} + +void EaxRingModulatorEffect::validate_frequency( + float flFrequency) +{ + eax_validate_range( + "Frequency", + flFrequency, + EAXRINGMODULATOR_MINFREQUENCY, + EAXRINGMODULATOR_MAXFREQUENCY); +} + +void EaxRingModulatorEffect::validate_high_pass_cutoff( + float flHighPassCutOff) +{ + eax_validate_range( + "High-Pass Cutoff", + flHighPassCutOff, + EAXRINGMODULATOR_MINHIGHPASSCUTOFF, + EAXRINGMODULATOR_MAXHIGHPASSCUTOFF); +} + +void EaxRingModulatorEffect::validate_waveform( + unsigned long ulWaveform) +{ + eax_validate_range( + "Waveform", + ulWaveform, + EAXRINGMODULATOR_MINWAVEFORM, + EAXRINGMODULATOR_MAXWAVEFORM); +} + +void EaxRingModulatorEffect::validate_all( + const EAXRINGMODULATORPROPERTIES& all) +{ + validate_frequency(all.flFrequency); + validate_high_pass_cutoff(all.flHighPassCutOff); + validate_waveform(all.ulWaveform); +} + +void EaxRingModulatorEffect::defer_frequency( + float flFrequency) +{ + eax_d_.flFrequency = flFrequency; + eax_dirty_flags_.flFrequency = (eax_.flFrequency != eax_d_.flFrequency); +} + +void EaxRingModulatorEffect::defer_high_pass_cutoff( + float flHighPassCutOff) +{ + eax_d_.flHighPassCutOff = flHighPassCutOff; + eax_dirty_flags_.flHighPassCutOff = (eax_.flHighPassCutOff != eax_d_.flHighPassCutOff); +} + +void EaxRingModulatorEffect::defer_waveform( + unsigned long ulWaveform) +{ + eax_d_.ulWaveform = ulWaveform; + eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform); +} + +void EaxRingModulatorEffect::defer_all( + const EAXRINGMODULATORPROPERTIES& all) +{ + defer_frequency(all.flFrequency); + defer_high_pass_cutoff(all.flHighPassCutOff); + defer_waveform(all.ulWaveform); +} + +void EaxRingModulatorEffect::defer_frequency( + const EaxEaxCall& eax_call) +{ + const auto& frequency = + eax_call.get_value< + EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::flFrequency)>(); + + validate_frequency(frequency); + defer_frequency(frequency); +} + +void EaxRingModulatorEffect::defer_high_pass_cutoff( + const EaxEaxCall& eax_call) +{ + const auto& high_pass_cutoff = + eax_call.get_value< + EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::flHighPassCutOff)>(); + + validate_high_pass_cutoff(high_pass_cutoff); + defer_high_pass_cutoff(high_pass_cutoff); +} + +void EaxRingModulatorEffect::defer_waveform( + const EaxEaxCall& eax_call) +{ + const auto& waveform = + eax_call.get_value< + EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::ulWaveform)>(); + + validate_waveform(waveform); + defer_waveform(waveform); +} + +void EaxRingModulatorEffect::defer_all( + const EaxEaxCall& eax_call) +{ + const auto& all = + eax_call.get_value(); + + validate_all(all); + defer_all(all); +} + +// [[nodiscard]] +bool EaxRingModulatorEffect::apply_deferred() +{ + if (eax_dirty_flags_ == EaxRingModulatorEffectDirtyFlags{}) + { + return false; + } + + eax_ = eax_d_; + + if (eax_dirty_flags_.flFrequency) + { + set_efx_frequency(); + } + + if (eax_dirty_flags_.flHighPassCutOff) + { + set_efx_high_pass_cutoff(); + } + + if (eax_dirty_flags_.ulWaveform) + { + set_efx_waveform(); + } + + eax_dirty_flags_ = EaxRingModulatorEffectDirtyFlags{}; + + return true; +} + +// [[nodiscard]] +bool EaxRingModulatorEffect::set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXRINGMODULATOR_NONE: + break; + + case EAXRINGMODULATOR_ALLPARAMETERS: + defer_all(eax_call); + break; + + case EAXRINGMODULATOR_FREQUENCY: + defer_frequency(eax_call); + break; + + case EAXRINGMODULATOR_HIGHPASSCUTOFF: + defer_high_pass_cutoff(eax_call); + break; + + case EAXRINGMODULATOR_WAVEFORM: + defer_waveform(eax_call); + break; + + default: + throw EaxRingModulatorEffectException{"Unsupported property id."}; + } + + if (!eax_call.is_deferred()) + { + return apply_deferred(); + } + + return false; +} + + +} // namespace + + +EaxEffectUPtr eax_create_eax_ring_modulator_effect( + EffectProps& al_effect_props) +{ + return std::make_unique(al_effect_props); +} + + +#endif // ALSOFT_EAX diff --git a/al/effects/null.cpp b/al/effects/null.cpp index 516446db..44595208 100644 --- a/al/effects/null.cpp +++ b/al/effects/null.cpp @@ -7,6 +7,10 @@ #include "alc/effects/base.h" #include "effects.h" +#if ALSOFT_EAX +#include "al/eax_exception.h" +#endif // ALSOFT_EAX + namespace { @@ -91,3 +95,56 @@ EffectProps genDefaultProps() noexcept DEFINE_ALEFFECT_VTABLE(Null); const EffectProps NullEffectProps{genDefaultProps()}; + + +#if ALSOFT_EAX +namespace +{ + + +class EaxNullEffect final : + public EaxEffect +{ +public: + // [[nodiscard]] + bool dispatch( + const EaxEaxCall& eax_call) override; +}; // EaxNullEffect + + +class EaxNullEffectException : + public EaxException +{ +public: + explicit EaxNullEffectException( + const char* message) + : + EaxException{"EAX_NULL_EFFECT", message} + { + } +}; // EaxNullEffectException + + +// [[nodiscard]] +bool EaxNullEffect::dispatch( + const EaxEaxCall& eax_call) +{ + if (eax_call.get_property_id() != 0) + { + throw EaxNullEffectException{"Unsupported property id."}; + } + + return false; +} + + +} // namespace + + +EaxEffectUPtr eax_create_eax_null_effect() +{ + return std::make_unique(); +} + + +#endif // ALSOFT_EAX diff --git a/al/effects/pshifter.cpp b/al/effects/pshifter.cpp index 56059a3c..03f9a139 100644 --- a/al/effects/pshifter.cpp +++ b/al/effects/pshifter.cpp @@ -7,6 +7,13 @@ #include "alc/effects/base.h" #include "effects.h" +#if ALSOFT_EAX +#include "alnumeric.h" + +#include "al/eax_exception.h" +#include "al/eax_utils.h" +#endif // ALSOFT_EAX + namespace { @@ -82,3 +89,334 @@ EffectProps genDefaultProps() noexcept DEFINE_ALEFFECT_VTABLE(Pshifter); const EffectProps PshifterEffectProps{genDefaultProps()}; + +#if ALSOFT_EAX +namespace +{ + + +using EaxPitchShifterEffectDirtyFlagsValue = std::uint_least8_t; + +struct EaxPitchShifterEffectDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxPitchShifterEffectDirtyFlagsValue lCoarseTune : 1; + EaxPitchShifterEffectDirtyFlagsValue lFineTune : 1; +}; // EaxPitchShifterEffectDirtyFlags + + +class EaxPitchShifterEffect final : + public EaxEffect +{ +public: + EaxPitchShifterEffect( + EffectProps& al_effect_props); + + + // [[nodiscard]] + bool dispatch( + const EaxEaxCall& eax_call) override; + + +private: + EffectProps& al_effect_props_; + + EAXPITCHSHIFTERPROPERTIES eax_{}; + EAXPITCHSHIFTERPROPERTIES eax_d_{}; + EaxPitchShifterEffectDirtyFlags eax_dirty_flags_{}; + + + void set_eax_defaults(); + + + void set_efx_coarse_tune(); + + void set_efx_fine_tune(); + + void set_efx_defaults(); + + + // [[nodiscard]] + bool get( + const EaxEaxCall& eax_call); + + + void validate_coarse_tune( + long lCoarseTune); + + void validate_fine_tune( + long lFineTune); + + void validate_all( + const EAXPITCHSHIFTERPROPERTIES& all); + + + void defer_coarse_tune( + long lCoarseTune); + + void defer_fine_tune( + long lFineTune); + + void defer_all( + const EAXPITCHSHIFTERPROPERTIES& all); + + + void defer_coarse_tune( + const EaxEaxCall& eax_call); + + void defer_fine_tune( + const EaxEaxCall& eax_call); + + void defer_all( + const EaxEaxCall& eax_call); + + + // [[nodiscard]] + bool apply_deferred(); + + // [[nodiscard]] + bool set( + const EaxEaxCall& eax_call); +}; // EaxPitchShifterEffect + + +class EaxPitchShifterEffectException : + public EaxException +{ +public: + explicit EaxPitchShifterEffectException( + const char* message) + : + EaxException{"EAX_PITCH_SHIFTER_EFFECT", message} + { + } +}; // EaxPitchShifterEffectException + + +EaxPitchShifterEffect::EaxPitchShifterEffect( + EffectProps& al_effect_props) + : + al_effect_props_{al_effect_props} +{ + set_eax_defaults(); + set_efx_defaults(); +} + +// [[nodiscard]] +bool EaxPitchShifterEffect::dispatch( + const EaxEaxCall& eax_call) +{ + return eax_call.is_get() ? get(eax_call) : set(eax_call); +} + +void EaxPitchShifterEffect::set_eax_defaults() +{ + eax_.lCoarseTune = EAXPITCHSHIFTER_DEFAULTCOARSETUNE; + eax_.lFineTune = EAXPITCHSHIFTER_DEFAULTFINETUNE; + + eax_d_ = eax_; +} + +void EaxPitchShifterEffect::set_efx_coarse_tune() +{ + const auto coarse_tune = clamp( + static_cast(eax_.lCoarseTune), + AL_PITCH_SHIFTER_MIN_COARSE_TUNE, + AL_PITCH_SHIFTER_MAX_COARSE_TUNE); + + al_effect_props_.Pshifter.CoarseTune = coarse_tune; +} + +void EaxPitchShifterEffect::set_efx_fine_tune() +{ + const auto fine_tune = clamp( + static_cast(eax_.lFineTune), + AL_PITCH_SHIFTER_MIN_FINE_TUNE, + AL_PITCH_SHIFTER_MAX_FINE_TUNE); + + al_effect_props_.Pshifter.FineTune = fine_tune; +} + +void EaxPitchShifterEffect::set_efx_defaults() +{ + set_efx_coarse_tune(); + set_efx_fine_tune(); +} + +// [[nodiscard]] +bool EaxPitchShifterEffect::get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXPITCHSHIFTER_NONE: + break; + + case EAXPITCHSHIFTER_ALLPARAMETERS: + eax_call.set_value(eax_); + break; + + case EAXPITCHSHIFTER_COARSETUNE: + eax_call.set_value(eax_.lCoarseTune); + break; + + case EAXPITCHSHIFTER_FINETUNE: + eax_call.set_value(eax_.lFineTune); + break; + + default: + throw EaxPitchShifterEffectException{"Unsupported property id."}; + } + + return false; +} + +void EaxPitchShifterEffect::validate_coarse_tune( + long lCoarseTune) +{ + eax_validate_range( + "Coarse Tune", + lCoarseTune, + EAXPITCHSHIFTER_MINCOARSETUNE, + EAXPITCHSHIFTER_MAXCOARSETUNE); +} + +void EaxPitchShifterEffect::validate_fine_tune( + long lFineTune) +{ + eax_validate_range( + "Fine Tune", + lFineTune, + EAXPITCHSHIFTER_MINFINETUNE, + EAXPITCHSHIFTER_MAXFINETUNE); +} + +void EaxPitchShifterEffect::validate_all( + const EAXPITCHSHIFTERPROPERTIES& all) +{ + validate_coarse_tune(all.lCoarseTune); + validate_fine_tune(all.lFineTune); +} + +void EaxPitchShifterEffect::defer_coarse_tune( + long lCoarseTune) +{ + eax_d_.lCoarseTune = lCoarseTune; + eax_dirty_flags_.lCoarseTune = (eax_.lCoarseTune != eax_d_.lCoarseTune); +} + +void EaxPitchShifterEffect::defer_fine_tune( + long lFineTune) +{ + eax_d_.lFineTune = lFineTune; + eax_dirty_flags_.lFineTune = (eax_.lFineTune != eax_d_.lFineTune); +} + +void EaxPitchShifterEffect::defer_all( + const EAXPITCHSHIFTERPROPERTIES& all) +{ + defer_coarse_tune(all.lCoarseTune); + defer_fine_tune(all.lFineTune); +} + +void EaxPitchShifterEffect::defer_coarse_tune( + const EaxEaxCall& eax_call) +{ + const auto& coarse_tune = + eax_call.get_value(); + + validate_coarse_tune(coarse_tune); + defer_coarse_tune(coarse_tune); +} + +void EaxPitchShifterEffect::defer_fine_tune( + const EaxEaxCall& eax_call) +{ + const auto& fine_tune = + eax_call.get_value(); + + validate_fine_tune(fine_tune); + defer_fine_tune(fine_tune); +} + +void EaxPitchShifterEffect::defer_all( + const EaxEaxCall& eax_call) +{ + const auto& all = + eax_call.get_value(); + + validate_all(all); + defer_all(all); +} + +// [[nodiscard]] +bool EaxPitchShifterEffect::apply_deferred() +{ + if (eax_dirty_flags_ == EaxPitchShifterEffectDirtyFlags{}) + { + return false; + } + + eax_ = eax_d_; + + if (eax_dirty_flags_.lCoarseTune) + { + set_efx_coarse_tune(); + } + + if (eax_dirty_flags_.lFineTune) + { + set_efx_fine_tune(); + } + + eax_dirty_flags_ = EaxPitchShifterEffectDirtyFlags{}; + + return true; +} + +// [[nodiscard]] +bool EaxPitchShifterEffect::set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXPITCHSHIFTER_NONE: + break; + + case EAXPITCHSHIFTER_ALLPARAMETERS: + defer_all(eax_call); + break; + + case EAXPITCHSHIFTER_COARSETUNE: + defer_coarse_tune(eax_call); + break; + + case EAXPITCHSHIFTER_FINETUNE: + defer_fine_tune(eax_call); + break; + + default: + throw EaxPitchShifterEffectException{"Unsupported property id."}; + } + + if (!eax_call.is_deferred()) + { + return apply_deferred(); + } + + return false; +} + + +} // namespace + + +EaxEffectUPtr eax_create_eax_pitch_shifter_effect( + EffectProps& al_effect_props) +{ + return std::make_unique(al_effect_props); +} + + +#endif // ALSOFT_EAX diff --git a/al/effects/reverb.cpp b/al/effects/reverb.cpp index 3f234b93..8012450d 100644 --- a/al/effects/reverb.cpp +++ b/al/effects/reverb.cpp @@ -9,6 +9,15 @@ #include "alc/effects/base.h" #include "effects.h" +#if ALSOFT_EAX +#include + +#include "alnumeric.h" + +#include "al/eax_exception.h" +#include "al/eax_utils.h" +#endif // ALSOFT_EAX + namespace { @@ -554,3 +563,1916 @@ const EffectProps ReverbEffectProps{genDefaultProps()}; DEFINE_ALEFFECT_VTABLE(StdReverb); const EffectProps StdReverbEffectProps{genDefaultStdProps()}; + +#if ALSOFT_EAX +namespace +{ + + +using EaxReverbEffectDirtyFlagsValue = std::uint_least32_t; + +struct EaxReverbEffectDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxReverbEffectDirtyFlagsValue ulEnvironment : 1; + EaxReverbEffectDirtyFlagsValue flEnvironmentSize : 1; + EaxReverbEffectDirtyFlagsValue flEnvironmentDiffusion : 1; + EaxReverbEffectDirtyFlagsValue lRoom : 1; + EaxReverbEffectDirtyFlagsValue lRoomHF : 1; + EaxReverbEffectDirtyFlagsValue lRoomLF : 1; + EaxReverbEffectDirtyFlagsValue flDecayTime : 1; + EaxReverbEffectDirtyFlagsValue flDecayHFRatio : 1; + EaxReverbEffectDirtyFlagsValue flDecayLFRatio : 1; + EaxReverbEffectDirtyFlagsValue lReflections : 1; + EaxReverbEffectDirtyFlagsValue flReflectionsDelay : 1; + EaxReverbEffectDirtyFlagsValue vReflectionsPan : 1; + EaxReverbEffectDirtyFlagsValue lReverb : 1; + EaxReverbEffectDirtyFlagsValue flReverbDelay : 1; + EaxReverbEffectDirtyFlagsValue vReverbPan : 1; + EaxReverbEffectDirtyFlagsValue flEchoTime : 1; + EaxReverbEffectDirtyFlagsValue flEchoDepth : 1; + EaxReverbEffectDirtyFlagsValue flModulationTime : 1; + EaxReverbEffectDirtyFlagsValue flModulationDepth : 1; + EaxReverbEffectDirtyFlagsValue flAirAbsorptionHF : 1; + EaxReverbEffectDirtyFlagsValue flHFReference : 1; + EaxReverbEffectDirtyFlagsValue flLFReference : 1; + EaxReverbEffectDirtyFlagsValue flRoomRolloffFactor : 1; + EaxReverbEffectDirtyFlagsValue ulFlags : 1; +}; // EaxReverbEffectDirtyFlags + + +class EaxReverbEffect final : + public EaxEffect +{ +public: + EaxReverbEffect( + EffectProps& al_effect_props); + + + // [[nodiscard]] + bool dispatch( + const EaxEaxCall& eax_call) override; + + +private: + EffectProps& al_effect_props_; + + EAXREVERBPROPERTIES eax_{}; + EAXREVERBPROPERTIES eax_d_{}; + EaxReverbEffectDirtyFlags eax_dirty_flags_{}; + + + void set_eax_defaults(); + + + void set_efx_density(); + + void set_efx_diffusion(); + + void set_efx_gain(); + + void set_efx_gain_hf(); + + void set_efx_gain_lf(); + + void set_efx_decay_time(); + + void set_efx_decay_hf_ratio(); + + void set_efx_decay_lf_ratio(); + + void set_efx_reflections_gain(); + + void set_efx_reflections_delay(); + + void set_efx_reflections_pan(); + + void set_efx_late_reverb_gain(); + + void set_efx_late_reverb_delay(); + + void set_efx_late_reverb_pan(); + + void set_efx_echo_time(); + + void set_efx_echo_depth(); + + void set_efx_modulation_time(); + + void set_efx_modulation_depth(); + + void set_efx_air_absorption_gain_hf(); + + void set_efx_hf_reference(); + + void set_efx_lf_reference(); + + void set_efx_room_rolloff_factor(); + + void set_efx_flags(); + + void set_efx_defaults(); + + + void get_all( + const EaxEaxCall& eax_call) const; + + // [[nodiscard]] + bool get( + const EaxEaxCall& eax_call) const; + + + void validate_environment( + unsigned long ulEnvironment, + int version, + bool is_standalone); + + void validate_environment_size( + float flEnvironmentSize); + + void validate_environment_diffusion( + float flEnvironmentDiffusion); + + void validate_room( + long lRoom); + + void validate_room_hf( + long lRoomHF); + + void validate_room_lf( + long lRoomLF); + + void validate_decay_time( + float flDecayTime); + + void validate_decay_hf_ratio( + float flDecayHFRatio); + + void validate_decay_lf_ratio( + float flDecayLFRatio); + + void validate_reflections( + long lReflections); + + void validate_reflections_delay( + float flReflectionsDelay); + + void validate_reflections_pan( + const EAXVECTOR& vReflectionsPan); + + void validate_reverb( + long lReverb); + + void validate_reverb_delay( + float flReverbDelay); + + void validate_reverb_pan( + const EAXVECTOR& vReverbPan); + + void validate_echo_time( + float flEchoTime); + + void validate_echo_depth( + float flEchoDepth); + + void validate_modulation_time( + float flModulationTime); + + void validate_modulation_depth( + float flModulationDepth); + + void validate_air_absorbtion_hf( + float air_absorbtion_hf); + + void validate_hf_reference( + float flHFReference); + + void validate_lf_reference( + float flLFReference); + + void validate_room_rolloff_factor( + float flRoomRolloffFactor); + + void validate_flags( + unsigned long ulFlags); + + void validate_all( + const EAX20LISTENERPROPERTIES& all, + int version); + + void validate_all( + const EAXREVERBPROPERTIES& all, + int version); + + + void defer_environment( + unsigned long ulEnvironment); + + void defer_environment_size( + float flEnvironmentSize); + + void defer_environment_diffusion( + float flEnvironmentDiffusion); + + void defer_room( + long lRoom); + + void defer_room_hf( + long lRoomHF); + + void defer_room_lf( + long lRoomLF); + + void defer_decay_time( + float flDecayTime); + + void defer_decay_hf_ratio( + float flDecayHFRatio); + + void defer_decay_lf_ratio( + float flDecayLFRatio); + + void defer_reflections( + long lReflections); + + void defer_reflections_delay( + float flReflectionsDelay); + + void defer_reflections_pan( + const EAXVECTOR& vReflectionsPan); + + void defer_reverb( + long lReverb); + + void defer_reverb_delay( + float flReverbDelay); + + void defer_reverb_pan( + const EAXVECTOR& vReverbPan); + + void defer_echo_time( + float flEchoTime); + + void defer_echo_depth( + float flEchoDepth); + + void defer_modulation_time( + float flModulationTime); + + void defer_modulation_depth( + float flModulationDepth); + + void defer_air_absorbtion_hf( + float flAirAbsorptionHF); + + void defer_hf_reference( + float flHFReference); + + void defer_lf_reference( + float flLFReference); + + void defer_room_rolloff_factor( + float flRoomRolloffFactor); + + void defer_flags( + unsigned long ulFlags); + + void defer_all( + const EAX20LISTENERPROPERTIES& all); + + void defer_all( + const EAXREVERBPROPERTIES& all); + + + void defer_environment( + const EaxEaxCall& eax_call); + + void defer_environment_size( + const EaxEaxCall& eax_call); + + void defer_environment_diffusion( + const EaxEaxCall& eax_call); + + void defer_room( + const EaxEaxCall& eax_call); + + void defer_room_hf( + const EaxEaxCall& eax_call); + + void defer_room_lf( + const EaxEaxCall& eax_call); + + void defer_decay_time( + const EaxEaxCall& eax_call); + + void defer_decay_hf_ratio( + const EaxEaxCall& eax_call); + + void defer_decay_lf_ratio( + const EaxEaxCall& eax_call); + + void defer_reflections( + const EaxEaxCall& eax_call); + + void defer_reflections_delay( + const EaxEaxCall& eax_call); + + void defer_reflections_pan( + const EaxEaxCall& eax_call); + + void defer_reverb( + const EaxEaxCall& eax_call); + + void defer_reverb_delay( + const EaxEaxCall& eax_call); + + void defer_reverb_pan( + const EaxEaxCall& eax_call); + + void defer_echo_time( + const EaxEaxCall& eax_call); + + void defer_echo_depth( + const EaxEaxCall& eax_call); + + void defer_modulation_time( + const EaxEaxCall& eax_call); + + void defer_modulation_depth( + const EaxEaxCall& eax_call); + + void defer_air_absorbtion_hf( + const EaxEaxCall& eax_call); + + void defer_hf_reference( + const EaxEaxCall& eax_call); + + void defer_lf_reference( + const EaxEaxCall& eax_call); + + void defer_room_rolloff_factor( + const EaxEaxCall& eax_call); + + void defer_flags( + const EaxEaxCall& eax_call); + + void defer_all( + const EaxEaxCall& eax_call); + + + // [[nodiscard]] + bool apply_deferred(); + + // [[nodiscard]] + bool set( + const EaxEaxCall& eax_call); +}; // EaxReverbEffect + + +class EaxReverbEffectException : + public EaxException +{ +public: + explicit EaxReverbEffectException( + const char* message) + : + EaxException{"EAX_REVERB_EFFECT", message} + { + } +}; // EaxReverbEffectException + + +EaxReverbEffect::EaxReverbEffect( + EffectProps& al_effect_props) + : + al_effect_props_{al_effect_props} +{ + set_eax_defaults(); + set_efx_defaults(); +} + +// [[nodiscard]] +bool EaxReverbEffect::dispatch( + const EaxEaxCall& eax_call) +{ + return eax_call.is_get() ? get(eax_call) : set(eax_call); +} + +void EaxReverbEffect::set_eax_defaults() +{ + eax_ = EAXREVERB_PRESETS[EAX_ENVIRONMENT_GENERIC]; + + eax_d_ = eax_; +} + +void EaxReverbEffect::set_efx_density() +{ + const auto eax_environment_size = eax_.flEnvironmentSize; + + const auto efx_density = clamp( + (eax_environment_size * eax_environment_size * eax_environment_size) / 16.0F, + AL_EAXREVERB_MIN_DENSITY, + AL_EAXREVERB_MAX_DENSITY); + + al_effect_props_.Reverb.Density = efx_density; +} + +void EaxReverbEffect::set_efx_diffusion() +{ + const auto efx_diffusion = clamp( + eax_.flEnvironmentDiffusion, + AL_EAXREVERB_MIN_DIFFUSION, + AL_EAXREVERB_MAX_DIFFUSION); + + al_effect_props_.Reverb.Diffusion = efx_diffusion; +} + +void EaxReverbEffect::set_efx_gain() +{ + const auto efx_gain = clamp( + level_mb_to_gain(static_cast(eax_.lRoom)), + AL_EAXREVERB_MIN_GAIN, + AL_EAXREVERB_MAX_GAIN); + + al_effect_props_.Reverb.Gain = efx_gain; +} + +void EaxReverbEffect::set_efx_gain_hf() +{ + const auto efx_gain_hf = clamp( + level_mb_to_gain(static_cast(eax_.lRoomHF)), + AL_EAXREVERB_MIN_GAINHF, + AL_EAXREVERB_MAX_GAINHF); + + al_effect_props_.Reverb.GainHF = efx_gain_hf; +} + +void EaxReverbEffect::set_efx_gain_lf() +{ + const auto efx_gain_lf = clamp( + level_mb_to_gain(static_cast(eax_.lRoomLF)), + AL_EAXREVERB_MIN_GAINLF, + AL_EAXREVERB_MAX_GAINLF); + + al_effect_props_.Reverb.GainLF = efx_gain_lf; +} + +void EaxReverbEffect::set_efx_decay_time() +{ + const auto efx_decay_time = clamp( + eax_.flDecayTime, + AL_EAXREVERB_MIN_DECAY_TIME, + AL_EAXREVERB_MAX_DECAY_TIME); + + al_effect_props_.Reverb.DecayTime = efx_decay_time; +} + +void EaxReverbEffect::set_efx_decay_hf_ratio() +{ + const auto efx_decay_hf_ratio = clamp( + eax_.flDecayHFRatio, + AL_EAXREVERB_MIN_DECAY_HFRATIO, + AL_EAXREVERB_MAX_DECAY_HFRATIO); + + al_effect_props_.Reverb.DecayHFRatio = efx_decay_hf_ratio; +} + +void EaxReverbEffect::set_efx_decay_lf_ratio() +{ + const auto efx_decay_lf_ratio = clamp( + eax_.flDecayLFRatio, + AL_EAXREVERB_MIN_DECAY_LFRATIO, + AL_EAXREVERB_MAX_DECAY_LFRATIO); + + al_effect_props_.Reverb.DecayLFRatio = efx_decay_lf_ratio; +} + +void EaxReverbEffect::set_efx_reflections_gain() +{ + const auto efx_reflections_gain = clamp( + level_mb_to_gain(static_cast(eax_.lReflections)), + AL_EAXREVERB_MIN_REFLECTIONS_GAIN, + AL_EAXREVERB_MAX_REFLECTIONS_GAIN); + + al_effect_props_.Reverb.ReflectionsGain = efx_reflections_gain; +} + +void EaxReverbEffect::set_efx_reflections_delay() +{ + const auto efx_reflections_delay = clamp( + eax_.flReflectionsDelay, + AL_EAXREVERB_MIN_REFLECTIONS_DELAY, + AL_EAXREVERB_MAX_REFLECTIONS_DELAY); + + al_effect_props_.Reverb.ReflectionsDelay = efx_reflections_delay; +} + +void EaxReverbEffect::set_efx_reflections_pan() +{ + al_effect_props_.Reverb.ReflectionsPan[0] = eax_.vReflectionsPan.x; + al_effect_props_.Reverb.ReflectionsPan[1] = eax_.vReflectionsPan.y; + al_effect_props_.Reverb.ReflectionsPan[2] = eax_.vReflectionsPan.z; +} + +void EaxReverbEffect::set_efx_late_reverb_gain() +{ + const auto efx_late_reverb_gain = clamp( + level_mb_to_gain(static_cast(eax_.lReverb)), + AL_EAXREVERB_MIN_LATE_REVERB_GAIN, + AL_EAXREVERB_MAX_LATE_REVERB_GAIN); + + al_effect_props_.Reverb.LateReverbGain = efx_late_reverb_gain; +} + +void EaxReverbEffect::set_efx_late_reverb_delay() +{ + const auto efx_late_reverb_delay = clamp( + eax_.flReverbDelay, + AL_EAXREVERB_MIN_LATE_REVERB_DELAY, + AL_EAXREVERB_MAX_LATE_REVERB_DELAY); + + al_effect_props_.Reverb.LateReverbDelay = efx_late_reverb_delay; +} + +void EaxReverbEffect::set_efx_late_reverb_pan() +{ + al_effect_props_.Reverb.LateReverbPan[0] = eax_.vReverbPan.x; + al_effect_props_.Reverb.LateReverbPan[1] = eax_.vReverbPan.y; + al_effect_props_.Reverb.LateReverbPan[2] = eax_.vReverbPan.z; +} + +void EaxReverbEffect::set_efx_echo_time() +{ + const auto efx_echo_time = clamp( + eax_.flEchoTime, + AL_EAXREVERB_MIN_ECHO_TIME, + AL_EAXREVERB_MAX_ECHO_TIME); + + al_effect_props_.Reverb.EchoTime = efx_echo_time; +} + +void EaxReverbEffect::set_efx_echo_depth() +{ + const auto efx_echo_depth = clamp( + eax_.flEchoDepth, + AL_EAXREVERB_MIN_ECHO_DEPTH, + AL_EAXREVERB_MAX_ECHO_DEPTH); + + al_effect_props_.Reverb.EchoDepth = efx_echo_depth; +} + +void EaxReverbEffect::set_efx_modulation_time() +{ + const auto efx_modulation_time = clamp( + eax_.flModulationTime, + AL_EAXREVERB_MIN_MODULATION_TIME, + AL_EAXREVERB_MAX_MODULATION_TIME); + + al_effect_props_.Reverb.ModulationTime = efx_modulation_time; +} + +void EaxReverbEffect::set_efx_modulation_depth() +{ + const auto efx_modulation_depth = clamp( + eax_.flModulationDepth, + AL_EAXREVERB_MIN_MODULATION_DEPTH, + AL_EAXREVERB_MAX_MODULATION_DEPTH); + + al_effect_props_.Reverb.ModulationDepth = efx_modulation_depth; +} + +void EaxReverbEffect::set_efx_air_absorption_gain_hf() +{ + const auto efx_air_absorption_hf = clamp( + level_mb_to_gain(eax_.flAirAbsorptionHF), + AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF, + AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF); + + al_effect_props_.Reverb.AirAbsorptionGainHF = efx_air_absorption_hf; +} + +void EaxReverbEffect::set_efx_hf_reference() +{ + const auto efx_hf_reference = clamp( + eax_.flHFReference, + AL_EAXREVERB_MIN_HFREFERENCE, + AL_EAXREVERB_MAX_HFREFERENCE); + + al_effect_props_.Reverb.HFReference = efx_hf_reference; +} + +void EaxReverbEffect::set_efx_lf_reference() +{ + const auto efx_lf_reference = clamp( + eax_.flLFReference, + AL_EAXREVERB_MIN_LFREFERENCE, + AL_EAXREVERB_MAX_LFREFERENCE); + + al_effect_props_.Reverb.LFReference = efx_lf_reference; +} + +void EaxReverbEffect::set_efx_room_rolloff_factor() +{ + const auto efx_room_rolloff_factor = clamp( + eax_.flRoomRolloffFactor, + AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR, + AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR); + + al_effect_props_.Reverb.RoomRolloffFactor = efx_room_rolloff_factor; +} + +void EaxReverbEffect::set_efx_flags() +{ + al_effect_props_.Reverb.DecayHFLimit = ((eax_.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0); +} + +void EaxReverbEffect::set_efx_defaults() +{ + set_efx_density(); + set_efx_diffusion(); + set_efx_gain(); + set_efx_gain_hf(); + set_efx_gain_lf(); + set_efx_decay_time(); + set_efx_decay_hf_ratio(); + set_efx_decay_lf_ratio(); + set_efx_reflections_gain(); + set_efx_reflections_delay(); + set_efx_reflections_pan(); + set_efx_late_reverb_gain(); + set_efx_late_reverb_delay(); + set_efx_late_reverb_pan(); + set_efx_echo_time(); + set_efx_echo_depth(); + set_efx_modulation_time(); + set_efx_modulation_depth(); + set_efx_air_absorption_gain_hf(); + set_efx_hf_reference(); + set_efx_lf_reference(); + set_efx_room_rolloff_factor(); + set_efx_flags(); +} + +void EaxReverbEffect::get_all( + const EaxEaxCall& eax_call) const +{ + if (eax_call.get_version() == 2) + { + auto& eax_reverb = eax_call.get_value(); + eax_reverb.lRoom = eax_.lRoom; + eax_reverb.lRoomHF = eax_.lRoomHF; + eax_reverb.flRoomRolloffFactor = eax_.flRoomRolloffFactor; + eax_reverb.flDecayTime = eax_.flDecayTime; + eax_reverb.flDecayHFRatio = eax_.flDecayHFRatio; + eax_reverb.lReflections = eax_.lReflections; + eax_reverb.flReflectionsDelay = eax_.flReflectionsDelay; + eax_reverb.lReverb = eax_.lReverb; + eax_reverb.flReverbDelay = eax_.flReverbDelay; + eax_reverb.dwEnvironment = eax_.ulEnvironment; + eax_reverb.flEnvironmentSize = eax_.flEnvironmentSize; + eax_reverb.flEnvironmentDiffusion = eax_.flEnvironmentDiffusion; + eax_reverb.flAirAbsorptionHF = eax_.flAirAbsorptionHF; + eax_reverb.dwFlags = eax_.ulFlags; + } + else + { + eax_call.set_value(eax_); + } +} + +// [[nodiscard]] +bool EaxReverbEffect::get( + const EaxEaxCall& eax_call) const +{ + switch (eax_call.get_property_id()) + { + case EAXREVERB_NONE: + break; + + case EAXREVERB_ALLPARAMETERS: + get_all(eax_call); + break; + + case EAXREVERB_ENVIRONMENT: + eax_call.set_value(eax_.ulEnvironment); + break; + + case EAXREVERB_ENVIRONMENTSIZE: + eax_call.set_value(eax_.flEnvironmentSize); + break; + + case EAXREVERB_ENVIRONMENTDIFFUSION: + eax_call.set_value(eax_.flEnvironmentDiffusion); + break; + + case EAXREVERB_ROOM: + eax_call.set_value(eax_.lRoom); + break; + + case EAXREVERB_ROOMHF: + eax_call.set_value(eax_.lRoomHF); + break; + + case EAXREVERB_ROOMLF: + eax_call.set_value(eax_.lRoomLF); + break; + + case EAXREVERB_DECAYTIME: + eax_call.set_value(eax_.flDecayTime); + break; + + case EAXREVERB_DECAYHFRATIO: + eax_call.set_value(eax_.flDecayHFRatio); + break; + + case EAXREVERB_DECAYLFRATIO: + eax_call.set_value(eax_.flDecayLFRatio); + break; + + case EAXREVERB_REFLECTIONS: + eax_call.set_value(eax_.lReflections); + break; + + case EAXREVERB_REFLECTIONSDELAY: + eax_call.set_value(eax_.flReflectionsDelay); + break; + + case EAXREVERB_REFLECTIONSPAN: + eax_call.set_value(eax_.vReflectionsPan); + break; + + case EAXREVERB_REVERB: + eax_call.set_value(eax_.lReverb); + break; + + case EAXREVERB_REVERBDELAY: + eax_call.set_value(eax_.flReverbDelay); + break; + + case EAXREVERB_REVERBPAN: + eax_call.set_value(eax_.vReverbPan); + break; + + case EAXREVERB_ECHOTIME: + eax_call.set_value(eax_.flEchoTime); + break; + + case EAXREVERB_ECHODEPTH: + eax_call.set_value(eax_.flEchoDepth); + break; + + case EAXREVERB_MODULATIONTIME: + eax_call.set_value(eax_.flModulationTime); + break; + + case EAXREVERB_MODULATIONDEPTH: + eax_call.set_value(eax_.flModulationDepth); + break; + + case EAXREVERB_AIRABSORPTIONHF: + eax_call.set_value(eax_.flAirAbsorptionHF); + break; + + case EAXREVERB_HFREFERENCE: + eax_call.set_value(eax_.flHFReference); + break; + + case EAXREVERB_LFREFERENCE: + eax_call.set_value(eax_.flLFReference); + break; + + case EAXREVERB_ROOMROLLOFFFACTOR: + eax_call.set_value(eax_.flRoomRolloffFactor); + break; + + case EAXREVERB_FLAGS: + eax_call.set_value(eax_.ulFlags); + break; + + default: + throw EaxReverbEffectException{"Unsupported property id."}; + } + + return false; +} + +void EaxReverbEffect::validate_environment( + unsigned long ulEnvironment, + int version, + bool is_standalone) +{ + eax_validate_range( + "Environment", + ulEnvironment, + EAXREVERB_MINENVIRONMENT, + (version == 2 || is_standalone) ? EAX20REVERB_MAXENVIRONMENT : EAX30REVERB_MAXENVIRONMENT); +} + +void EaxReverbEffect::validate_environment_size( + float flEnvironmentSize) +{ + eax_validate_range( + "Environment Size", + flEnvironmentSize, + EAXREVERB_MINENVIRONMENTSIZE, + EAXREVERB_MAXENVIRONMENTSIZE); +} + +void EaxReverbEffect::validate_environment_diffusion( + float flEnvironmentDiffusion) +{ + eax_validate_range( + "Environment Diffusion", + flEnvironmentDiffusion, + EAXREVERB_MINENVIRONMENTDIFFUSION, + EAXREVERB_MAXENVIRONMENTDIFFUSION); +} + +void EaxReverbEffect::validate_room( + long lRoom) +{ + eax_validate_range( + "Room", + lRoom, + EAXREVERB_MINROOM, + EAXREVERB_MAXROOM); +} + +void EaxReverbEffect::validate_room_hf( + long lRoomHF) +{ + eax_validate_range( + "Room HF", + lRoomHF, + EAXREVERB_MINROOMHF, + EAXREVERB_MAXROOMHF); +} + +void EaxReverbEffect::validate_room_lf( + long lRoomLF) +{ + eax_validate_range( + "Room LF", + lRoomLF, + EAXREVERB_MINROOMLF, + EAXREVERB_MAXROOMLF); +} + +void EaxReverbEffect::validate_decay_time( + float flDecayTime) +{ + eax_validate_range( + "Decay Time", + flDecayTime, + EAXREVERB_MINDECAYTIME, + EAXREVERB_MAXDECAYTIME); +} + +void EaxReverbEffect::validate_decay_hf_ratio( + float flDecayHFRatio) +{ + eax_validate_range( + "Decay HF Ratio", + flDecayHFRatio, + EAXREVERB_MINDECAYHFRATIO, + EAXREVERB_MAXDECAYHFRATIO); +} + +void EaxReverbEffect::validate_decay_lf_ratio( + float flDecayLFRatio) +{ + eax_validate_range( + "Decay LF Ratio", + flDecayLFRatio, + EAXREVERB_MINDECAYLFRATIO, + EAXREVERB_MAXDECAYLFRATIO); +} + +void EaxReverbEffect::validate_reflections( + long lReflections) +{ + eax_validate_range( + "Reflections", + lReflections, + EAXREVERB_MINREFLECTIONS, + EAXREVERB_MAXREFLECTIONS); +} + +void EaxReverbEffect::validate_reflections_delay( + float flReflectionsDelay) +{ + eax_validate_range( + "Reflections Delay", + flReflectionsDelay, + EAXREVERB_MINREFLECTIONSDELAY, + EAXREVERB_MAXREFLECTIONSDELAY); +} + +void EaxReverbEffect::validate_reflections_pan( + const EAXVECTOR& vReflectionsPan) +{ + std::ignore = vReflectionsPan; +} + +void EaxReverbEffect::validate_reverb( + long lReverb) +{ + eax_validate_range( + "Reverb", + lReverb, + EAXREVERB_MINREVERB, + EAXREVERB_MAXREVERB); +} + +void EaxReverbEffect::validate_reverb_delay( + float flReverbDelay) +{ + eax_validate_range( + "Reverb Delay", + flReverbDelay, + EAXREVERB_MINREVERBDELAY, + EAXREVERB_MAXREVERBDELAY); +} + +void EaxReverbEffect::validate_reverb_pan( + const EAXVECTOR& vReverbPan) +{ + std::ignore = vReverbPan; +} + +void EaxReverbEffect::validate_echo_time( + float flEchoTime) +{ + eax_validate_range( + "Echo Time", + flEchoTime, + EAXREVERB_MINECHOTIME, + EAXREVERB_MAXECHOTIME); +} + +void EaxReverbEffect::validate_echo_depth( + float flEchoDepth) +{ + eax_validate_range( + "Echo Depth", + flEchoDepth, + EAXREVERB_MINECHODEPTH, + EAXREVERB_MAXECHODEPTH); +} + +void EaxReverbEffect::validate_modulation_time( + float flModulationTime) +{ + eax_validate_range( + "Modulation Time", + flModulationTime, + EAXREVERB_MINMODULATIONTIME, + EAXREVERB_MAXMODULATIONTIME); +} + +void EaxReverbEffect::validate_modulation_depth( + float flModulationDepth) +{ + eax_validate_range( + "Modulation Depth", + flModulationDepth, + EAXREVERB_MINMODULATIONDEPTH, + EAXREVERB_MAXMODULATIONDEPTH); +} + +void EaxReverbEffect::validate_air_absorbtion_hf( + float air_absorbtion_hf) +{ + eax_validate_range( + "Air Absorbtion HF", + air_absorbtion_hf, + EAXREVERB_MINAIRABSORPTIONHF, + EAXREVERB_MAXAIRABSORPTIONHF); +} + +void EaxReverbEffect::validate_hf_reference( + float flHFReference) +{ + eax_validate_range( + "HF Reference", + flHFReference, + EAXREVERB_MINHFREFERENCE, + EAXREVERB_MAXHFREFERENCE); +} + +void EaxReverbEffect::validate_lf_reference( + float flLFReference) +{ + eax_validate_range( + "LF Reference", + flLFReference, + EAXREVERB_MINLFREFERENCE, + EAXREVERB_MAXLFREFERENCE); +} + +void EaxReverbEffect::validate_room_rolloff_factor( + float flRoomRolloffFactor) +{ + eax_validate_range( + "Room Rolloff Factor", + flRoomRolloffFactor, + EAXREVERB_MINROOMROLLOFFFACTOR, + EAXREVERB_MAXROOMROLLOFFFACTOR); +} + +void EaxReverbEffect::validate_flags( + unsigned long ulFlags) +{ + eax_validate_range( + "Flags", + ulFlags, + 0UL, + ~EAXREVERBFLAGS_RESERVED); +} + +void EaxReverbEffect::validate_all( + const EAX20LISTENERPROPERTIES& listener, + int version) +{ + validate_room(listener.lRoom); + validate_room_hf(listener.lRoomHF); + validate_room_rolloff_factor(listener.flRoomRolloffFactor); + validate_decay_time(listener.flDecayTime); + validate_decay_hf_ratio(listener.flDecayHFRatio); + validate_reflections(listener.lReflections); + validate_reflections_delay(listener.flReflectionsDelay); + validate_reverb(listener.lReverb); + validate_reverb_delay(listener.flReverbDelay); + validate_environment(listener.dwEnvironment, version, false); + validate_environment_size(listener.flEnvironmentSize); + validate_environment_diffusion(listener.flEnvironmentDiffusion); + validate_air_absorbtion_hf(listener.flAirAbsorptionHF); + validate_flags(listener.dwFlags); +} + +void EaxReverbEffect::validate_all( + const EAXREVERBPROPERTIES& lReverb, + int version) +{ + validate_environment(lReverb.ulEnvironment, version, false); + validate_environment_size(lReverb.flEnvironmentSize); + validate_environment_diffusion(lReverb.flEnvironmentDiffusion); + validate_room(lReverb.lRoom); + validate_room_hf(lReverb.lRoomHF); + validate_room_lf(lReverb.lRoomLF); + validate_decay_time(lReverb.flDecayTime); + validate_decay_hf_ratio(lReverb.flDecayHFRatio); + validate_decay_lf_ratio(lReverb.flDecayLFRatio); + validate_reflections(lReverb.lReflections); + validate_reflections_delay(lReverb.flReflectionsDelay); + validate_reverb(lReverb.lReverb); + validate_reverb_delay(lReverb.flReverbDelay); + validate_echo_time(lReverb.flEchoTime); + validate_echo_depth(lReverb.flEchoDepth); + validate_modulation_time(lReverb.flModulationTime); + validate_modulation_depth(lReverb.flModulationDepth); + validate_air_absorbtion_hf(lReverb.flAirAbsorptionHF); + validate_hf_reference(lReverb.flHFReference); + validate_lf_reference(lReverb.flLFReference); + validate_room_rolloff_factor(lReverb.flRoomRolloffFactor); + validate_flags(lReverb.ulFlags); +} + +void EaxReverbEffect::defer_environment( + unsigned long ulEnvironment) +{ + eax_d_.ulEnvironment = ulEnvironment; + eax_dirty_flags_.ulEnvironment = (eax_.ulEnvironment != eax_d_.ulEnvironment); +} + +void EaxReverbEffect::defer_environment_size( + float flEnvironmentSize) +{ + eax_d_.flEnvironmentSize = flEnvironmentSize; + eax_dirty_flags_.flEnvironmentSize = (eax_.flEnvironmentSize != eax_d_.flEnvironmentSize); +} + +void EaxReverbEffect::defer_environment_diffusion( + float flEnvironmentDiffusion) +{ + eax_d_.flEnvironmentDiffusion = flEnvironmentDiffusion; + eax_dirty_flags_.flEnvironmentDiffusion = (eax_.flEnvironmentDiffusion != eax_d_.flEnvironmentDiffusion); +} + +void EaxReverbEffect::defer_room( + long lRoom) +{ + eax_d_.lRoom = lRoom; + eax_dirty_flags_.lRoom = (eax_.lRoom != eax_d_.lRoom); +} + +void EaxReverbEffect::defer_room_hf( + long lRoomHF) +{ + eax_d_.lRoomHF = lRoomHF; + eax_dirty_flags_.lRoomHF = (eax_.lRoomHF != eax_d_.lRoomHF); +} + +void EaxReverbEffect::defer_room_lf( + long lRoomLF) +{ + eax_d_.lRoomLF = lRoomLF; + eax_dirty_flags_.lRoomLF = (eax_.lRoomLF != eax_d_.lRoomLF); +} + +void EaxReverbEffect::defer_decay_time( + float flDecayTime) +{ + eax_d_.flDecayTime = flDecayTime; + eax_dirty_flags_.flDecayTime = (eax_.flDecayTime != eax_d_.flDecayTime); +} + +void EaxReverbEffect::defer_decay_hf_ratio( + float flDecayHFRatio) +{ + eax_d_.flDecayHFRatio = flDecayHFRatio; + eax_dirty_flags_.flDecayHFRatio = (eax_.flDecayHFRatio != eax_d_.flDecayHFRatio); +} + +void EaxReverbEffect::defer_decay_lf_ratio( + float flDecayLFRatio) +{ + eax_d_.flDecayLFRatio = flDecayLFRatio; + eax_dirty_flags_.flDecayLFRatio = (eax_.flDecayLFRatio != eax_d_.flDecayLFRatio); +} + +void EaxReverbEffect::defer_reflections( + long lReflections) +{ + eax_d_.lReflections = lReflections; + eax_dirty_flags_.lReflections = (eax_.lReflections != eax_d_.lReflections); +} + +void EaxReverbEffect::defer_reflections_delay( + float flReflectionsDelay) +{ + eax_d_.flReflectionsDelay = flReflectionsDelay; + eax_dirty_flags_.flReflectionsDelay = (eax_.flReflectionsDelay != eax_d_.flReflectionsDelay); +} + +void EaxReverbEffect::defer_reflections_pan( + const EAXVECTOR& vReflectionsPan) +{ + eax_d_.vReflectionsPan = vReflectionsPan; + eax_dirty_flags_.vReflectionsPan = (eax_.vReflectionsPan != eax_d_.vReflectionsPan); +} + +void EaxReverbEffect::defer_reverb( + long lReverb) +{ + eax_d_.lReverb = lReverb; + eax_dirty_flags_.lReverb = (eax_.lReverb != eax_d_.lReverb); +} + +void EaxReverbEffect::defer_reverb_delay( + float flReverbDelay) +{ + eax_d_.flReverbDelay = flReverbDelay; + eax_dirty_flags_.flReverbDelay = (eax_.flReverbDelay != eax_d_.flReverbDelay); +} + +void EaxReverbEffect::defer_reverb_pan( + const EAXVECTOR& vReverbPan) +{ + eax_d_.vReverbPan = vReverbPan; + eax_dirty_flags_.vReverbPan = (eax_.vReverbPan != eax_d_.vReverbPan); +} + +void EaxReverbEffect::defer_echo_time( + float flEchoTime) +{ + eax_d_.flEchoTime = flEchoTime; + eax_dirty_flags_.flEchoTime = (eax_.flEchoTime != eax_d_.flEchoTime); +} + +void EaxReverbEffect::defer_echo_depth( + float flEchoDepth) +{ + eax_d_.flEchoDepth = flEchoDepth; + eax_dirty_flags_.flEchoDepth = (eax_.flEchoDepth != eax_d_.flEchoDepth); +} + +void EaxReverbEffect::defer_modulation_time( + float flModulationTime) +{ + eax_d_.flModulationTime = flModulationTime; + eax_dirty_flags_.flModulationTime = (eax_.flModulationTime != eax_d_.flModulationTime); +} + +void EaxReverbEffect::defer_modulation_depth( + float flModulationDepth) +{ + eax_d_.flModulationDepth = flModulationDepth; + eax_dirty_flags_.flModulationDepth = (eax_.flModulationDepth != eax_d_.flModulationDepth); +} + +void EaxReverbEffect::defer_air_absorbtion_hf( + float flAirAbsorptionHF) +{ + eax_d_.flAirAbsorptionHF = flAirAbsorptionHF; + eax_dirty_flags_.flAirAbsorptionHF = (eax_.flAirAbsorptionHF != eax_d_.flAirAbsorptionHF); +} + +void EaxReverbEffect::defer_hf_reference( + float flHFReference) +{ + eax_d_.flHFReference = flHFReference; + eax_dirty_flags_.flHFReference = (eax_.flHFReference != eax_d_.flHFReference); +} + +void EaxReverbEffect::defer_lf_reference( + float flLFReference) +{ + eax_d_.flLFReference = flLFReference; + eax_dirty_flags_.flLFReference = (eax_.flLFReference != eax_d_.flLFReference); +} + +void EaxReverbEffect::defer_room_rolloff_factor( + float flRoomRolloffFactor) +{ + eax_d_.flRoomRolloffFactor = flRoomRolloffFactor; + eax_dirty_flags_.flRoomRolloffFactor = (eax_.flRoomRolloffFactor != eax_d_.flRoomRolloffFactor); +} + +void EaxReverbEffect::defer_flags( + unsigned long ulFlags) +{ + eax_d_.ulFlags = ulFlags; + eax_dirty_flags_.ulFlags = (eax_.ulFlags != eax_d_.ulFlags); +} + +void EaxReverbEffect::defer_all( + const EAX20LISTENERPROPERTIES& listener) +{ + defer_room(listener.lRoom); + defer_room_hf(listener.lRoomHF); + defer_room_rolloff_factor(listener.flRoomRolloffFactor); + defer_decay_time(listener.flDecayTime); + defer_decay_hf_ratio(listener.flDecayHFRatio); + defer_reflections(listener.lReflections); + defer_reflections_delay(listener.flReflectionsDelay); + defer_reverb(listener.lReverb); + defer_reverb_delay(listener.flReverbDelay); + defer_environment(listener.dwEnvironment); + defer_environment_size(listener.flEnvironmentSize); + defer_environment_diffusion(listener.flEnvironmentDiffusion); + defer_air_absorbtion_hf(listener.flAirAbsorptionHF); + defer_flags(listener.dwFlags); +} + +void EaxReverbEffect::defer_all( + const EAXREVERBPROPERTIES& lReverb) +{ + defer_environment(lReverb.ulEnvironment); + defer_environment_size(lReverb.flEnvironmentSize); + defer_environment_diffusion(lReverb.flEnvironmentDiffusion); + defer_room(lReverb.lRoom); + defer_room_hf(lReverb.lRoomHF); + defer_room_lf(lReverb.lRoomLF); + defer_decay_time(lReverb.flDecayTime); + defer_decay_hf_ratio(lReverb.flDecayHFRatio); + defer_decay_lf_ratio(lReverb.flDecayLFRatio); + defer_reflections(lReverb.lReflections); + defer_reflections_delay(lReverb.flReflectionsDelay); + defer_reflections_pan(lReverb.vReflectionsPan); + defer_reverb(lReverb.lReverb); + defer_reverb_delay(lReverb.flReverbDelay); + defer_reverb_pan(lReverb.vReverbPan); + defer_echo_time(lReverb.flEchoTime); + defer_echo_depth(lReverb.flEchoDepth); + defer_modulation_time(lReverb.flModulationTime); + defer_modulation_depth(lReverb.flModulationDepth); + defer_air_absorbtion_hf(lReverb.flAirAbsorptionHF); + defer_hf_reference(lReverb.flHFReference); + defer_lf_reference(lReverb.flLFReference); + defer_room_rolloff_factor(lReverb.flRoomRolloffFactor); + defer_flags(lReverb.ulFlags); +} + +void EaxReverbEffect::defer_environment( + const EaxEaxCall& eax_call) +{ + const auto& ulEnvironment = + eax_call.get_value(); + + validate_environment(ulEnvironment, eax_call.get_version(), true); + + if (eax_d_.ulEnvironment == ulEnvironment) + { + return; + } + + const auto& reverb_preset = EAXREVERB_PRESETS[ulEnvironment]; + + defer_all(reverb_preset); +} + +void EaxReverbEffect::defer_environment_size( + const EaxEaxCall& eax_call) +{ + const auto& flEnvironmentSize = + eax_call.get_value(); + + validate_environment_size(flEnvironmentSize); + + if (eax_d_.flEnvironmentSize == flEnvironmentSize) + { + return; + } + + const auto scale = flEnvironmentSize / eax_d_.flEnvironmentSize; + + defer_environment_size(flEnvironmentSize); + + if ((eax_d_.ulFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0) + { + const auto flDecayTime = clamp( + scale * eax_d_.flDecayTime, + EAXREVERB_MINDECAYTIME, + EAXREVERB_MAXDECAYTIME); + + defer_decay_time(flDecayTime); + } + + if ((eax_d_.ulFlags & EAXREVERBFLAGS_REFLECTIONSSCALE) != 0) + { + if ((eax_d_.ulFlags & EAXREVERBFLAGS_REFLECTIONSDELAYSCALE) != 0) + { + const auto lReflections = clamp( + eax_d_.lReflections - static_cast(gain_to_level_mb(scale)), + EAXREVERB_MINREFLECTIONS, + EAXREVERB_MAXREFLECTIONS); + + defer_reflections(lReflections); + } + } + + if ((eax_d_.ulFlags & EAXREVERBFLAGS_REFLECTIONSDELAYSCALE) != 0) + { + const auto flReflectionsDelay = clamp( + eax_d_.flReflectionsDelay * scale, + EAXREVERB_MINREFLECTIONSDELAY, + EAXREVERB_MAXREFLECTIONSDELAY); + + defer_reflections_delay(flReflectionsDelay); + } + + if ((eax_d_.ulFlags & EAXREVERBFLAGS_REVERBSCALE) != 0) + { + const auto log_scalar = ((eax_d_.ulFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0) ? 2'000.0F : 3'000.0F; + + const auto lReverb = clamp( + eax_d_.lReverb - static_cast(std::log10(scale) * log_scalar), + EAXREVERB_MINREVERB, + EAXREVERB_MAXREVERB); + + defer_reverb(lReverb); + } + + if ((eax_d_.ulFlags & EAXREVERBFLAGS_REVERBDELAYSCALE) != 0) + { + const auto flReverbDelay = clamp( + scale * eax_d_.flReverbDelay, + EAXREVERB_MINREVERBDELAY, + EAXREVERB_MAXREVERBDELAY); + + defer_reverb_delay(flReverbDelay); + } + + if ((eax_d_.ulFlags & EAXREVERBFLAGS_ECHOTIMESCALE) != 0) + { + const auto flEchoTime = clamp( + eax_d_.flEchoTime * scale, + EAXREVERB_MINECHOTIME, + EAXREVERB_MAXECHOTIME); + + defer_echo_time(flEchoTime); + } + + if ((eax_d_.ulFlags & EAXREVERBFLAGS_MODULATIONTIMESCALE) != 0) + { + const auto flModulationTime = clamp( + scale * eax_d_.flModulationTime, + EAXREVERB_MINMODULATIONTIME, + EAXREVERB_MAXMODULATIONTIME); + + defer_modulation_time(flModulationTime); + } +} + +void EaxReverbEffect::defer_environment_diffusion( + const EaxEaxCall& eax_call) +{ + const auto& flEnvironmentDiffusion = + eax_call.get_value(); + + validate_environment_diffusion(flEnvironmentDiffusion); + defer_environment_diffusion(flEnvironmentDiffusion); +} + +void EaxReverbEffect::defer_room( + const EaxEaxCall& eax_call) +{ + const auto& lRoom = + eax_call.get_value(); + + validate_room(lRoom); + defer_room(lRoom); +} + +void EaxReverbEffect::defer_room_hf( + const EaxEaxCall& eax_call) +{ + const auto& lRoomHF = + eax_call.get_value(); + + validate_room_hf(lRoomHF); + defer_room_hf(lRoomHF); +} + +void EaxReverbEffect::defer_room_lf( + const EaxEaxCall& eax_call) +{ + const auto& lRoomLF = + eax_call.get_value(); + + validate_room_lf(lRoomLF); + defer_room_lf(lRoomLF); +} + +void EaxReverbEffect::defer_decay_time( + const EaxEaxCall& eax_call) +{ + const auto& flDecayTime = + eax_call.get_value(); + + validate_decay_time(flDecayTime); + defer_decay_time(flDecayTime); +} + +void EaxReverbEffect::defer_decay_hf_ratio( + const EaxEaxCall& eax_call) +{ + const auto& flDecayHFRatio = + eax_call.get_value(); + + validate_decay_hf_ratio(flDecayHFRatio); + defer_decay_hf_ratio(flDecayHFRatio); +} + +void EaxReverbEffect::defer_decay_lf_ratio( + const EaxEaxCall& eax_call) +{ + const auto& flDecayLFRatio = + eax_call.get_value(); + + validate_decay_lf_ratio(flDecayLFRatio); + defer_decay_lf_ratio(flDecayLFRatio); +} + +void EaxReverbEffect::defer_reflections( + const EaxEaxCall& eax_call) +{ + const auto& lReflections = + eax_call.get_value(); + + validate_reflections(lReflections); + defer_reflections(lReflections); +} + +void EaxReverbEffect::defer_reflections_delay( + const EaxEaxCall& eax_call) +{ + const auto& flReflectionsDelay = + eax_call.get_value(); + + validate_reflections_delay(flReflectionsDelay); + defer_reflections_delay(flReflectionsDelay); +} + +void EaxReverbEffect::defer_reflections_pan( + const EaxEaxCall& eax_call) +{ + const auto& vReflectionsPan = + eax_call.get_value(); + + validate_reflections_pan(vReflectionsPan); + defer_reflections_pan(vReflectionsPan); +} + +void EaxReverbEffect::defer_reverb( + const EaxEaxCall& eax_call) +{ + const auto& lReverb = + eax_call.get_value(); + + validate_reverb(lReverb); + defer_reverb(lReverb); +} + +void EaxReverbEffect::defer_reverb_delay( + const EaxEaxCall& eax_call) +{ + const auto& flReverbDelay = + eax_call.get_value(); + + validate_reverb_delay(flReverbDelay); + defer_reverb_delay(flReverbDelay); +} + +void EaxReverbEffect::defer_reverb_pan( + const EaxEaxCall& eax_call) +{ + const auto& vReverbPan = + eax_call.get_value(); + + validate_reverb_pan(vReverbPan); + defer_reverb_pan(vReverbPan); +} + +void EaxReverbEffect::defer_echo_time( + const EaxEaxCall& eax_call) +{ + const auto& flEchoTime = + eax_call.get_value(); + + validate_echo_time(flEchoTime); + defer_echo_time(flEchoTime); +} + +void EaxReverbEffect::defer_echo_depth( + const EaxEaxCall& eax_call) +{ + const auto& flEchoDepth = + eax_call.get_value(); + + validate_echo_depth(flEchoDepth); + defer_echo_depth(flEchoDepth); +} + +void EaxReverbEffect::defer_modulation_time( + const EaxEaxCall& eax_call) +{ + const auto& flModulationTime = + eax_call.get_value(); + + validate_modulation_time(flModulationTime); + defer_modulation_time(flModulationTime); +} + +void EaxReverbEffect::defer_modulation_depth( + const EaxEaxCall& eax_call) +{ + const auto& flModulationDepth = + eax_call.get_value(); + + validate_modulation_depth(flModulationDepth); + defer_modulation_depth(flModulationDepth); +} + +void EaxReverbEffect::defer_air_absorbtion_hf( + const EaxEaxCall& eax_call) +{ + const auto& air_absorbtion_hf = + eax_call.get_value(); + + validate_air_absorbtion_hf(air_absorbtion_hf); + defer_air_absorbtion_hf(air_absorbtion_hf); +} + +void EaxReverbEffect::defer_hf_reference( + const EaxEaxCall& eax_call) +{ + const auto& flHFReference = + eax_call.get_value(); + + validate_hf_reference(flHFReference); + defer_hf_reference(flHFReference); +} + +void EaxReverbEffect::defer_lf_reference( + const EaxEaxCall& eax_call) +{ + const auto& flLFReference = + eax_call.get_value(); + + validate_lf_reference(flLFReference); + defer_lf_reference(flLFReference); +} + +void EaxReverbEffect::defer_room_rolloff_factor( + const EaxEaxCall& eax_call) +{ + const auto& flRoomRolloffFactor = + eax_call.get_value(); + + validate_room_rolloff_factor(flRoomRolloffFactor); + defer_room_rolloff_factor(flRoomRolloffFactor); +} + +void EaxReverbEffect::defer_flags( + const EaxEaxCall& eax_call) +{ + const auto& ulFlags = + eax_call.get_value(); + + validate_flags(ulFlags); + defer_flags(ulFlags); +} + +void EaxReverbEffect::defer_all( + const EaxEaxCall& eax_call) +{ + const auto eax_version = eax_call.get_version(); + + if (eax_version == 2) + { + const auto& listener = + eax_call.get_value(); + + validate_all(listener, eax_version); + defer_all(listener); + } + else + { + const auto& reverb_all = + eax_call.get_value(); + + validate_all(reverb_all, eax_version); + defer_all(reverb_all); + } +} + +// [[nodiscard]] +bool EaxReverbEffect::apply_deferred() +{ + if (eax_dirty_flags_ == EaxReverbEffectDirtyFlags{}) + { + return false; + } + + eax_ = eax_d_; + + if (eax_dirty_flags_.ulEnvironment) + { + } + + if (eax_dirty_flags_.flEnvironmentSize) + { + set_efx_density(); + } + + if (eax_dirty_flags_.flEnvironmentDiffusion) + { + set_efx_diffusion(); + } + + if (eax_dirty_flags_.lRoom) + { + set_efx_gain(); + } + + if (eax_dirty_flags_.lRoomHF) + { + set_efx_gain_hf(); + } + + if (eax_dirty_flags_.lRoomLF) + { + set_efx_gain_lf(); + } + + if (eax_dirty_flags_.flDecayTime) + { + set_efx_decay_time(); + } + + if (eax_dirty_flags_.flDecayHFRatio) + { + set_efx_decay_hf_ratio(); + } + + if (eax_dirty_flags_.flDecayLFRatio) + { + set_efx_decay_lf_ratio(); + } + + if (eax_dirty_flags_.lReflections) + { + set_efx_reflections_gain(); + } + + if (eax_dirty_flags_.flReflectionsDelay) + { + set_efx_reflections_delay(); + } + + if (eax_dirty_flags_.vReflectionsPan) + { + set_efx_reflections_pan(); + } + + if (eax_dirty_flags_.lReverb) + { + set_efx_late_reverb_gain(); + } + + if (eax_dirty_flags_.flReverbDelay) + { + set_efx_late_reverb_delay(); + } + + if (eax_dirty_flags_.vReverbPan) + { + set_efx_late_reverb_pan(); + } + + if (eax_dirty_flags_.flEchoTime) + { + set_efx_echo_time(); + } + + if (eax_dirty_flags_.flEchoDepth) + { + set_efx_echo_depth(); + } + + if (eax_dirty_flags_.flModulationTime) + { + set_efx_modulation_time(); + } + + if (eax_dirty_flags_.flModulationDepth) + { + set_efx_modulation_depth(); + } + + if (eax_dirty_flags_.flAirAbsorptionHF) + { + set_efx_air_absorption_gain_hf(); + } + + if (eax_dirty_flags_.flHFReference) + { + set_efx_hf_reference(); + } + + if (eax_dirty_flags_.flLFReference) + { + set_efx_lf_reference(); + } + + if (eax_dirty_flags_.flRoomRolloffFactor) + { + set_efx_room_rolloff_factor(); + } + + if (eax_dirty_flags_.ulFlags) + { + set_efx_flags(); + } + + eax_dirty_flags_ = EaxReverbEffectDirtyFlags{}; + + return true; +} + +// [[nodiscard]] +bool EaxReverbEffect::set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXREVERB_NONE: + break; + + case EAXREVERB_ALLPARAMETERS: + defer_all(eax_call); + break; + + case EAXREVERB_ENVIRONMENT: + defer_environment(eax_call); + break; + + case EAXREVERB_ENVIRONMENTSIZE: + defer_environment_size(eax_call); + break; + + case EAXREVERB_ENVIRONMENTDIFFUSION: + defer_environment_diffusion(eax_call); + break; + + case EAXREVERB_ROOM: + defer_room(eax_call); + break; + + case EAXREVERB_ROOMHF: + defer_room_hf(eax_call); + break; + + case EAXREVERB_ROOMLF: + defer_room_lf(eax_call); + break; + + case EAXREVERB_DECAYTIME: + defer_decay_time(eax_call); + break; + + case EAXREVERB_DECAYHFRATIO: + defer_decay_hf_ratio(eax_call); + break; + + case EAXREVERB_DECAYLFRATIO: + defer_decay_lf_ratio(eax_call); + break; + + case EAXREVERB_REFLECTIONS: + defer_reflections(eax_call); + break; + + case EAXREVERB_REFLECTIONSDELAY: + defer_reflections_delay(eax_call); + break; + + case EAXREVERB_REFLECTIONSPAN: + defer_reflections_pan(eax_call); + break; + + case EAXREVERB_REVERB: + defer_reverb(eax_call); + break; + + case EAXREVERB_REVERBDELAY: + defer_reverb_delay(eax_call); + break; + + case EAXREVERB_REVERBPAN: + defer_reverb_pan(eax_call); + break; + + case EAXREVERB_ECHOTIME: + defer_echo_time(eax_call); + break; + + case EAXREVERB_ECHODEPTH: + defer_echo_depth(eax_call); + break; + + case EAXREVERB_MODULATIONTIME: + defer_modulation_time(eax_call); + break; + + case EAXREVERB_MODULATIONDEPTH: + defer_modulation_depth(eax_call); + break; + + case EAXREVERB_AIRABSORPTIONHF: + defer_air_absorbtion_hf(eax_call); + break; + + case EAXREVERB_HFREFERENCE: + defer_hf_reference(eax_call); + break; + + case EAXREVERB_LFREFERENCE: + defer_lf_reference(eax_call); + break; + + case EAXREVERB_ROOMROLLOFFFACTOR: + defer_room_rolloff_factor(eax_call); + break; + + case EAXREVERB_FLAGS: + defer_flags(eax_call); + break; + + default: + throw EaxReverbEffectException{"Unsupported property id."}; + } + + if (!eax_call.is_deferred()) + { + return apply_deferred(); + } + + return false; +} + + +} // namespace + + +EaxEffectUPtr eax_create_eax_reverb_effect( + EffectProps& al_effect_props) +{ + return std::make_unique(al_effect_props); +} + + +#endif // ALSOFT_EAX diff --git a/al/effects/vmorpher.cpp b/al/effects/vmorpher.cpp index 1b4710ff..2a9e0702 100644 --- a/al/effects/vmorpher.cpp +++ b/al/effects/vmorpher.cpp @@ -10,6 +10,15 @@ #include "aloptional.h" #include "effects.h" +#if ALSOFT_EAX +#include + +#include "alnumeric.h" + +#include "al/eax_exception.h" +#include "al/eax_utils.h" +#endif // ALSOFT_EAX + namespace { @@ -247,3 +256,618 @@ EffectProps genDefaultProps() noexcept DEFINE_ALEFFECT_VTABLE(Vmorpher); const EffectProps VmorpherEffectProps{genDefaultProps()}; + +#if ALSOFT_EAX +namespace +{ + + +using EaxVocalMorpherEffectDirtyFlagsValue = std::uint_least8_t; + +struct EaxVocalMorpherEffectDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxVocalMorpherEffectDirtyFlagsValue ulPhonemeA : 1; + EaxVocalMorpherEffectDirtyFlagsValue lPhonemeACoarseTuning : 1; + EaxVocalMorpherEffectDirtyFlagsValue ulPhonemeB : 1; + EaxVocalMorpherEffectDirtyFlagsValue lPhonemeBCoarseTuning : 1; + EaxVocalMorpherEffectDirtyFlagsValue ulWaveform : 1; + EaxVocalMorpherEffectDirtyFlagsValue flRate : 1; +}; // EaxPitchShifterEffectDirtyFlags + + +class EaxVocalMorpherEffect final : + public EaxEffect +{ +public: + EaxVocalMorpherEffect( + EffectProps& al_effect_props); + + + // [[nodiscard]] + bool dispatch( + const EaxEaxCall& eax_call) override; + + +private: + EffectProps& al_effect_props_; + + EAXVOCALMORPHERPROPERTIES eax_{}; + EAXVOCALMORPHERPROPERTIES eax_d_{}; + EaxVocalMorpherEffectDirtyFlags eax_dirty_flags_{}; + + + void set_eax_defaults(); + + + void set_efx_phoneme_a(); + + void set_efx_phoneme_a_coarse_tuning(); + + void set_efx_phoneme_b(); + + void set_efx_phoneme_b_coarse_tuning(); + + void set_efx_waveform(); + + void set_efx_rate(); + + void set_efx_defaults(); + + + // [[nodiscard]] + bool get( + const EaxEaxCall& eax_call); + + + void validate_phoneme_a( + unsigned long ulPhonemeA); + + void validate_phoneme_a_coarse_tuning( + long lPhonemeACoarseTuning); + + void validate_phoneme_b( + unsigned long ulPhonemeB); + + void validate_phoneme_b_coarse_tuning( + long lPhonemeBCoarseTuning); + + void validate_waveform( + unsigned long ulWaveform); + + void validate_rate( + float flRate); + + void validate_all( + const EAXVOCALMORPHERPROPERTIES& all); + + + void defer_phoneme_a( + unsigned long ulPhonemeA); + + void defer_phoneme_a_coarse_tuning( + long lPhonemeACoarseTuning); + + void defer_phoneme_b( + unsigned long ulPhonemeB); + + void defer_phoneme_b_coarse_tuning( + long lPhonemeBCoarseTuning); + + void defer_waveform( + unsigned long ulWaveform); + + void defer_rate( + float flRate); + + void defer_all( + const EAXVOCALMORPHERPROPERTIES& all); + + + void defer_phoneme_a( + const EaxEaxCall& eax_call); + + void defer_phoneme_a_coarse_tuning( + const EaxEaxCall& eax_call); + + void defer_phoneme_b( + const EaxEaxCall& eax_call); + + void defer_phoneme_b_coarse_tuning( + const EaxEaxCall& eax_call); + + void defer_waveform( + const EaxEaxCall& eax_call); + + void defer_rate( + const EaxEaxCall& eax_call); + + void defer_all( + const EaxEaxCall& eax_call); + + + // [[nodiscard]] + bool apply_deferred(); + + // [[nodiscard]] + bool set( + const EaxEaxCall& eax_call); +}; // EaxVocalMorpherEffect + + +class EaxVocalMorpherEffectException : + public EaxException +{ +public: + explicit EaxVocalMorpherEffectException( + const char* message) + : + EaxException{"EAX_VOCAL_MORPHER_EFFECT", message} + { + } +}; // EaxVocalMorpherEffectException + + +EaxVocalMorpherEffect::EaxVocalMorpherEffect( + EffectProps& al_effect_props) + : + al_effect_props_{al_effect_props} +{ + set_eax_defaults(); + set_efx_defaults(); +} + +// [[nodiscard]] +bool EaxVocalMorpherEffect::dispatch( + const EaxEaxCall& eax_call) +{ + return eax_call.is_get() ? get(eax_call) : set(eax_call); +} + +void EaxVocalMorpherEffect::set_eax_defaults() +{ + eax_.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA; + eax_.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING; + eax_.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB; + eax_.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING; + eax_.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM; + eax_.flRate = EAXVOCALMORPHER_DEFAULTRATE; + + eax_d_ = eax_; +} + +void EaxVocalMorpherEffect::set_efx_phoneme_a() +{ + const auto phoneme_a = clamp( + static_cast(eax_.ulPhonemeA), + AL_VOCAL_MORPHER_MIN_PHONEMEA, + AL_VOCAL_MORPHER_MAX_PHONEMEA); + + const auto efx_phoneme_a = PhenomeFromEnum(phoneme_a); + assert(efx_phoneme_a.has_value()); + al_effect_props_.Vmorpher.PhonemeA = *efx_phoneme_a; +} + +void EaxVocalMorpherEffect::set_efx_phoneme_a_coarse_tuning() +{ + const auto phoneme_a_coarse_tuning = clamp( + static_cast(eax_.lPhonemeACoarseTuning), + AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING, + AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING); + + al_effect_props_.Vmorpher.PhonemeACoarseTuning = phoneme_a_coarse_tuning; +} + +void EaxVocalMorpherEffect::set_efx_phoneme_b() +{ + const auto phoneme_b = clamp( + static_cast(eax_.ulPhonemeB), + AL_VOCAL_MORPHER_MIN_PHONEMEB, + AL_VOCAL_MORPHER_MAX_PHONEMEB); + + const auto efx_phoneme_b = PhenomeFromEnum(phoneme_b); + assert(efx_phoneme_b.has_value()); + al_effect_props_.Vmorpher.PhonemeB = *efx_phoneme_b; +} + +void EaxVocalMorpherEffect::set_efx_phoneme_b_coarse_tuning() +{ + const auto phoneme_b_coarse_tuning = clamp( + static_cast(eax_.lPhonemeBCoarseTuning), + AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING, + AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING); + + al_effect_props_.Vmorpher.PhonemeBCoarseTuning = phoneme_b_coarse_tuning; +} + +void EaxVocalMorpherEffect::set_efx_waveform() +{ + const auto waveform = clamp( + static_cast(eax_.ulWaveform), + AL_VOCAL_MORPHER_MIN_WAVEFORM, + AL_VOCAL_MORPHER_MAX_WAVEFORM); + + const auto wfx_waveform = WaveformFromEmum(waveform); + assert(wfx_waveform.has_value()); + al_effect_props_.Vmorpher.Waveform = *wfx_waveform; +} + +void EaxVocalMorpherEffect::set_efx_rate() +{ + const auto rate = clamp( + eax_.flRate, + AL_VOCAL_MORPHER_MIN_RATE, + AL_VOCAL_MORPHER_MAX_RATE); + + al_effect_props_.Vmorpher.Rate = rate; +} + +void EaxVocalMorpherEffect::set_efx_defaults() +{ + set_efx_phoneme_a(); + set_efx_phoneme_a_coarse_tuning(); + set_efx_phoneme_b(); + set_efx_phoneme_b_coarse_tuning(); + set_efx_waveform(); + set_efx_rate(); +} + +// [[nodiscard]] +bool EaxVocalMorpherEffect::get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXVOCALMORPHER_NONE: + break; + + case EAXVOCALMORPHER_ALLPARAMETERS: + eax_call.set_value(eax_); + break; + + case EAXVOCALMORPHER_PHONEMEA: + eax_call.set_value(eax_.ulPhonemeA); + break; + + case EAXVOCALMORPHER_PHONEMEACOARSETUNING: + eax_call.set_value(eax_.lPhonemeACoarseTuning); + break; + + case EAXVOCALMORPHER_PHONEMEB: + eax_call.set_value(eax_.ulPhonemeB); + break; + + case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: + eax_call.set_value(eax_.lPhonemeBCoarseTuning); + break; + + case EAXVOCALMORPHER_WAVEFORM: + eax_call.set_value(eax_.ulWaveform); + break; + + case EAXVOCALMORPHER_RATE: + eax_call.set_value(eax_.flRate); + break; + + default: + throw EaxVocalMorpherEffectException{"Unsupported property id."}; + } + + return false; +} + +void EaxVocalMorpherEffect::validate_phoneme_a( + unsigned long ulPhonemeA) +{ + eax_validate_range( + "Phoneme A", + ulPhonemeA, + EAXVOCALMORPHER_MINPHONEMEA, + EAXVOCALMORPHER_MAXPHONEMEA); +} + +void EaxVocalMorpherEffect::validate_phoneme_a_coarse_tuning( + long lPhonemeACoarseTuning) +{ + eax_validate_range( + "Phoneme A Coarse Tuning", + lPhonemeACoarseTuning, + EAXVOCALMORPHER_MINPHONEMEACOARSETUNING, + EAXVOCALMORPHER_MAXPHONEMEACOARSETUNING); +} + +void EaxVocalMorpherEffect::validate_phoneme_b( + unsigned long ulPhonemeB) +{ + eax_validate_range( + "Phoneme B", + ulPhonemeB, + EAXVOCALMORPHER_MINPHONEMEB, + EAXVOCALMORPHER_MAXPHONEMEB); +} + +void EaxVocalMorpherEffect::validate_phoneme_b_coarse_tuning( + long lPhonemeBCoarseTuning) +{ + eax_validate_range( + "Phoneme B Coarse Tuning", + lPhonemeBCoarseTuning, + EAXVOCALMORPHER_MINPHONEMEBCOARSETUNING, + EAXVOCALMORPHER_MAXPHONEMEBCOARSETUNING); +} + +void EaxVocalMorpherEffect::validate_waveform( + unsigned long ulWaveform) +{ + eax_validate_range( + "Waveform", + ulWaveform, + EAXVOCALMORPHER_MINWAVEFORM, + EAXVOCALMORPHER_MAXWAVEFORM); +} + +void EaxVocalMorpherEffect::validate_rate( + float flRate) +{ + eax_validate_range( + "Rate", + flRate, + EAXVOCALMORPHER_MINRATE, + EAXVOCALMORPHER_MAXRATE); +} + +void EaxVocalMorpherEffect::validate_all( + const EAXVOCALMORPHERPROPERTIES& all) +{ + validate_phoneme_a(all.ulPhonemeA); + validate_phoneme_a_coarse_tuning(all.lPhonemeACoarseTuning); + validate_phoneme_b(all.ulPhonemeB); + validate_phoneme_b_coarse_tuning(all.lPhonemeBCoarseTuning); + validate_waveform(all.ulWaveform); + validate_rate(all.flRate); +} + +void EaxVocalMorpherEffect::defer_phoneme_a( + unsigned long ulPhonemeA) +{ + eax_d_.ulPhonemeA = ulPhonemeA; + eax_dirty_flags_.ulPhonemeA = (eax_.ulPhonemeA != eax_d_.ulPhonemeA); +} + +void EaxVocalMorpherEffect::defer_phoneme_a_coarse_tuning( + long lPhonemeACoarseTuning) +{ + eax_d_.lPhonemeACoarseTuning = lPhonemeACoarseTuning; + eax_dirty_flags_.lPhonemeACoarseTuning = (eax_.lPhonemeACoarseTuning != eax_d_.lPhonemeACoarseTuning); +} + +void EaxVocalMorpherEffect::defer_phoneme_b( + unsigned long ulPhonemeB) +{ + eax_d_.ulPhonemeB = ulPhonemeB; + eax_dirty_flags_.ulPhonemeB = (eax_.ulPhonemeB != eax_d_.ulPhonemeB); +} + +void EaxVocalMorpherEffect::defer_phoneme_b_coarse_tuning( + long lPhonemeBCoarseTuning) +{ + eax_d_.lPhonemeBCoarseTuning = lPhonemeBCoarseTuning; + eax_dirty_flags_.lPhonemeBCoarseTuning = (eax_.lPhonemeBCoarseTuning != eax_d_.lPhonemeBCoarseTuning); +} + +void EaxVocalMorpherEffect::defer_waveform( + unsigned long ulWaveform) +{ + eax_d_.ulWaveform = ulWaveform; + eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform); +} + +void EaxVocalMorpherEffect::defer_rate( + float flRate) +{ + eax_d_.flRate = flRate; + eax_dirty_flags_.flRate = (eax_.flRate != eax_d_.flRate); +} + +void EaxVocalMorpherEffect::defer_all( + const EAXVOCALMORPHERPROPERTIES& all) +{ + defer_phoneme_a(all.ulPhonemeA); + defer_phoneme_a_coarse_tuning(all.lPhonemeACoarseTuning); + defer_phoneme_b(all.ulPhonemeB); + defer_phoneme_b_coarse_tuning(all.lPhonemeBCoarseTuning); + defer_waveform(all.ulWaveform); + defer_rate(all.flRate); +} + +void EaxVocalMorpherEffect::defer_phoneme_a( + const EaxEaxCall& eax_call) +{ + const auto& phoneme_a = eax_call.get_value< + EaxVocalMorpherEffectException, + const decltype(EAXVOCALMORPHERPROPERTIES::ulPhonemeA) + >(); + + validate_phoneme_a(phoneme_a); + defer_phoneme_a(phoneme_a); +} + +void EaxVocalMorpherEffect::defer_phoneme_a_coarse_tuning( + const EaxEaxCall& eax_call) +{ + const auto& phoneme_a_coarse_tuning = eax_call.get_value< + EaxVocalMorpherEffectException, + const decltype(EAXVOCALMORPHERPROPERTIES::lPhonemeACoarseTuning) + >(); + + validate_phoneme_a_coarse_tuning(phoneme_a_coarse_tuning); + defer_phoneme_a_coarse_tuning(phoneme_a_coarse_tuning); +} + +void EaxVocalMorpherEffect::defer_phoneme_b( + const EaxEaxCall& eax_call) +{ + const auto& phoneme_b = eax_call.get_value< + EaxVocalMorpherEffectException, + const decltype(EAXVOCALMORPHERPROPERTIES::ulPhonemeB) + >(); + + validate_phoneme_b(phoneme_b); + defer_phoneme_b(phoneme_b); +} + +void EaxVocalMorpherEffect::defer_phoneme_b_coarse_tuning( + const EaxEaxCall& eax_call) +{ + const auto& phoneme_b_coarse_tuning = eax_call.get_value< + EaxVocalMorpherEffectException, + const decltype(EAXVOCALMORPHERPROPERTIES::lPhonemeBCoarseTuning) + >(); + + validate_phoneme_b_coarse_tuning(phoneme_b_coarse_tuning); + defer_phoneme_b_coarse_tuning(phoneme_b_coarse_tuning); +} + +void EaxVocalMorpherEffect::defer_waveform( + const EaxEaxCall& eax_call) +{ + const auto& waveform = eax_call.get_value< + EaxVocalMorpherEffectException, + const decltype(EAXVOCALMORPHERPROPERTIES::ulWaveform) + >(); + + validate_waveform(waveform); + defer_waveform(waveform); +} + +void EaxVocalMorpherEffect::defer_rate( + const EaxEaxCall& eax_call) +{ + const auto& rate = eax_call.get_value< + EaxVocalMorpherEffectException, + const decltype(EAXVOCALMORPHERPROPERTIES::flRate) + >(); + + validate_rate(rate); + defer_rate(rate); +} + +void EaxVocalMorpherEffect::defer_all( + const EaxEaxCall& eax_call) +{ + const auto& all = eax_call.get_value< + EaxVocalMorpherEffectException, + const EAXVOCALMORPHERPROPERTIES + >(); + + validate_all(all); + defer_all(all); +} + +// [[nodiscard]] +bool EaxVocalMorpherEffect::apply_deferred() +{ + if (eax_dirty_flags_ == EaxVocalMorpherEffectDirtyFlags{}) + { + return false; + } + + eax_ = eax_d_; + + if (eax_dirty_flags_.ulPhonemeA) + { + set_efx_phoneme_a(); + } + + if (eax_dirty_flags_.lPhonemeACoarseTuning) + { + set_efx_phoneme_a_coarse_tuning(); + } + + if (eax_dirty_flags_.ulPhonemeB) + { + set_efx_phoneme_b(); + } + + if (eax_dirty_flags_.lPhonemeBCoarseTuning) + { + set_efx_phoneme_b_coarse_tuning(); + } + + if (eax_dirty_flags_.ulWaveform) + { + set_efx_waveform(); + } + + if (eax_dirty_flags_.flRate) + { + set_efx_rate(); + } + + eax_dirty_flags_ = EaxVocalMorpherEffectDirtyFlags{}; + + return true; +} + +// [[nodiscard]] +bool EaxVocalMorpherEffect::set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXVOCALMORPHER_NONE: + break; + + case EAXVOCALMORPHER_ALLPARAMETERS: + defer_all(eax_call); + break; + + case EAXVOCALMORPHER_PHONEMEA: + defer_phoneme_a(eax_call); + break; + + case EAXVOCALMORPHER_PHONEMEACOARSETUNING: + defer_phoneme_a_coarse_tuning(eax_call); + break; + + case EAXVOCALMORPHER_PHONEMEB: + defer_phoneme_b(eax_call); + break; + + case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: + defer_phoneme_b_coarse_tuning(eax_call); + break; + + case EAXVOCALMORPHER_WAVEFORM: + defer_waveform(eax_call); + break; + + case EAXVOCALMORPHER_RATE: + defer_rate(eax_call); + break; + + default: + throw EaxVocalMorpherEffectException{"Unsupported property id."}; + } + + if (!eax_call.is_deferred()) + { + return apply_deferred(); + } + + return false; +} + + +} // namespace + + +EaxEffectUPtr eax_create_eax_vocal_morpher_effect( + EffectProps& al_effect_props) +{ + return std::make_unique(al_effect_props); +} + + +#endif // ALSOFT_EAX diff --git a/al/extension.cpp b/al/extension.cpp index 5dda2a86..373f87a9 100644 --- a/al/extension.cpp +++ b/al/extension.cpp @@ -32,6 +32,10 @@ #include "core/except.h" #include "opthelpers.h" +#if ALSOFT_EAX +#include "eax_globals.h" +#include "eax_x_ram.h" +#endif // ALSOFT_EAX AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName) START_API_FUNC @@ -43,6 +47,22 @@ START_API_FUNC SETERR_RETURN(context, AL_INVALID_VALUE, AL_FALSE, "NULL pointer"); size_t len{strlen(extName)}; +#if ALSOFT_EAX + if (al::strncasecmp(eax_v2_0_ext_name, extName, len) == 0 || + al::strncasecmp(eax_v3_0_ext_name, extName, len) == 0 || + al::strncasecmp(eax_v4_0_ext_name, extName, len) == 0 || + al::strncasecmp(eax_v5_0_ext_name, extName, len) == 0) + { + const auto is_present = eax_g_is_enabled && context->eax_is_capable(); + return is_present ? AL_TRUE : AL_FALSE; + } + + if (al::strncasecmp(eax_x_ram_ext_name, extName, len) == 0) + { + const auto is_present = eax_g_is_enabled; + return is_present ? AL_TRUE : AL_FALSE; + } +#endif // ALSOFT_EAX const char *ptr{context->mExtensionList}; while(ptr && *ptr) { @@ -66,6 +86,75 @@ AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName) START_API_FUNC { if(!funcName) return nullptr; +#if ALSOFT_EAX + if (al::strcasecmp(funcName, eax_eax_set_func_name) == 0) + { + if (!eax_g_is_enabled) + { + return nullptr; + } + + ContextRef context{GetContextRef()}; + + if (!context || !context->eax_is_capable()) + { + return nullptr; + } + + return reinterpret_cast(EAXSet); + } + + if (al::strcasecmp(funcName, eax_eax_get_func_name) == 0) + { + if (!eax_g_is_enabled) + { + return nullptr; + } + + ContextRef context{GetContextRef()}; + + if (!context || !context->eax_is_capable()) + { + return nullptr; + } + + return reinterpret_cast(EAXGet); + } + + if (al::strcasecmp(funcName, eax_eax_set_buffer_mode_func_name) == 0) + { + if (!eax_g_is_enabled) + { + return nullptr; + } + + ContextRef context{GetContextRef()}; + + if (!context) + { + return nullptr; + } + + return reinterpret_cast(EAXSetBufferMode); + } + + if (al::strcasecmp(funcName, eax_eax_get_buffer_mode_func_name) == 0) + { + if (!eax_g_is_enabled) + { + return nullptr; + } + + ContextRef context{GetContextRef()}; + + if (!context) + { + return nullptr; + } + + return reinterpret_cast(EAXGetBufferMode); + } +#endif // ALSOFT_EAX return alcGetProcAddress(nullptr, funcName); } END_API_FUNC @@ -74,6 +163,34 @@ AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName) START_API_FUNC { if(!enumName) return static_cast(0); +#if ALSOFT_EAX + if (eax_g_is_enabled) + { + struct Descriptor + { + const char* name; + ALenum value; + }; // Descriptor + + constexpr Descriptor descriptors[] = + { + Descriptor{AL_EAX_RAM_SIZE_NAME, AL_EAX_RAM_SIZE}, + Descriptor{AL_EAX_RAM_FREE_NAME, AL_EAX_RAM_FREE}, + + Descriptor{AL_STORAGE_AUTOMATIC_NAME, AL_STORAGE_AUTOMATIC}, + Descriptor{AL_STORAGE_HARDWARE_NAME, AL_STORAGE_HARDWARE}, + Descriptor{AL_STORAGE_ACCESSIBLE_NAME, AL_STORAGE_ACCESSIBLE}, + }; // descriptors + + for (const auto& descriptor : descriptors) + { + if (strcmp(descriptor.name, enumName) == 0) + { + return descriptor.value; + } + } + } +#endif // ALSOFT_EAX return alcGetEnumValue(nullptr, enumName); } END_API_FUNC diff --git a/al/filter.cpp b/al/filter.cpp index 73067fd9..563fb3ef 100644 --- a/al/filter.cpp +++ b/al/filter.cpp @@ -45,6 +45,10 @@ #include "opthelpers.h" #include "vector.h" +#if ALSOFT_EAX +#include "core/logging.h" +#endif // ALSOFT_EAX + namespace { @@ -717,3 +721,80 @@ FilterSubList::~FilterSubList() al_free(Filters); Filters = nullptr; } + + +#if ALSOFT_EAX +EaxAlFilterDeleter::EaxAlFilterDeleter( + ALCcontext& context) + : + context_{&context} +{ +} + +void EaxAlFilterDeleter::operator()( + ALfilter* filter) +{ + eax_delete_low_pass_filter(*context_, *filter); +} + +EaxAlFilterUPtr eax_create_al_low_pass_filter( + ALCcontext& context) +{ +#define EAX_PREFIX "[EAX_MAKE_LOW_PASS_FILTER] " + + auto& device = *context.mALDevice; + std::lock_guard filter_lock{device.FilterLock}; + + if (!EnsureFilters(&device, 1)) + { + ERR(EAX_PREFIX "%s\n", "Failed to ensure."); + return nullptr; + } + + auto filter = EaxAlFilterUPtr{AllocFilter(&device), EaxAlFilterDeleter{context}}; + + if (!filter) + { + ERR(EAX_PREFIX "%s\n", "Failed to allocate."); + return nullptr; + } + + InitFilterParams(filter.get(), AL_FILTER_LOWPASS); + + return filter; + +#undef EAX_PREFIX +} + +void eax_delete_low_pass_filter( + ALCcontext& context, + ALfilter& filter) +{ + auto& device = *context.mALDevice; + std::lock_guard filter_lock{device.FilterLock}; + + FreeFilter(&device, &filter); +} + +void ALfilter::eax_set_low_pass_params( + ALCcontext& context, + const EaxAlLowPassParam& param) +{ +#define EAX_PREFIX "[EAX_SET_LOW_PASS_FILTER_PARAMS] " + + auto& device = *context.mALDevice; + std::lock_guard filter_lock{device.FilterLock}; + + try + { + setParamf(AL_LOWPASS_GAIN, param.gain); + setParamf(AL_LOWPASS_GAINHF, param.gain_hf); + } + catch (const filter_exception &e) + { + ERR(EAX_PREFIX "%s\n", e.what()); + } + +#undef EAX_PREFIX +} +#endif // ALSOFT_EAX diff --git a/al/filter.h b/al/filter.h index 123e64b0..222a6917 100644 --- a/al/filter.h +++ b/al/filter.h @@ -1,12 +1,18 @@ #ifndef AL_FILTER_H #define AL_FILTER_H + #include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" #include "almalloc.h" +#if ALSOFT_EAX +#include + +#include "eax_utils.h" +#endif // ALSOFT_EAX #define LOWPASSFREQREF 5000.0f #define HIGHPASSFREQREF 250.0f @@ -47,6 +53,40 @@ struct ALfilter { void getParamfv(ALenum param, float *values) const { vtab->getParamfv(this, param, values); } DISABLE_ALLOC() + +#if ALSOFT_EAX +public: + void eax_set_low_pass_params( + ALCcontext& context, + const EaxAlLowPassParam& param); +#endif // ALSOFT_EAX }; +#if ALSOFT_EAX +class EaxAlFilterDeleter +{ +public: + EaxAlFilterDeleter() noexcept = default; + + EaxAlFilterDeleter( + ALCcontext& context); + + + void operator()( + ALfilter* filter); + +private: + ALCcontext* context_{}; +}; // EaxAlFilterDeleter + +using EaxAlFilterUPtr = std::unique_ptr; + +EaxAlFilterUPtr eax_create_al_low_pass_filter( + ALCcontext& context); + +void eax_delete_low_pass_filter( + ALCcontext& context, + ALfilter& filter); +#endif // ALSOFT_EAX + #endif diff --git a/al/listener.cpp b/al/listener.cpp index 0997ff95..1909c644 100644 --- a/al/listener.cpp +++ b/al/listener.cpp @@ -450,3 +450,25 @@ void UpdateListenerProps(ALCcontext *context) AtomicReplaceHead(context->mFreeListenerProps, props); } } + +#if ALSOFT_EAX +// `alListenerf(AL_METERS_PER_UNIT, value)` +void eax_set_al_listener_meters_per_unit( + ALCcontext& al_context, + ALfloat meters_per_unit) +{ + auto context = EaxAlContextWrapper{al_context}; + + auto& listener = context->mListener; + + if (meters_per_unit < AL_MIN_METERS_PER_UNIT || + meters_per_unit > AL_MAX_METERS_PER_UNIT) + { + SETERR_RETURN(context, AL_INVALID_VALUE,, "Listener meters per unit out of range"); + } + + listener.mMetersPerUnit = meters_per_unit; + + DO_UPDATEPROPS(); +} +#endif // ALSOFT_EAX diff --git a/al/listener.h b/al/listener.h index f3332763..05cd3abf 100644 --- a/al/listener.h +++ b/al/listener.h @@ -29,4 +29,11 @@ struct ALlistener { void UpdateListenerProps(ALCcontext *context); +#if ALSOFT_EAX +// `alListenerf(AL_METERS_PER_UNIT, value)` +void eax_set_al_listener_meters_per_unit( + ALCcontext& al_context, + ALfloat meters_per_unit); +#endif // ALSOFT_EAX + #endif diff --git a/al/source.cpp b/al/source.cpp index f7b07338..3f7fa540 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -72,6 +72,10 @@ #include "ringbuffer.h" #include "threads.h" +#if ALSOFT_EAX +#include "eax_exception.h" +#include "eax_globals.h" +#endif // ALSOFT_EAX namespace { @@ -2414,16 +2418,52 @@ START_API_FUNC { ALsource *source{AllocSource(context.get())}; sources[0] = source->id; + +#if ALSOFT_EAX + if (context->has_eax()) + { + std::unique_lock prop_lock{context->mPropLock}; + context->eax_initialize_source(*source); + } +#endif // ALSOFT_EAX } else { +#if ALSOFT_EAX + auto eax_sources = al::vector{}; + + if (context->has_eax()) + { + eax_sources.reserve(static_cast(n)); + } +#endif // ALSOFT_EAX + al::vector ids; ids.reserve(static_cast(n)); do { ALsource *source{AllocSource(context.get())}; ids.emplace_back(source->id); + +#if ALSOFT_EAX + if (context->has_eax()) + { + eax_sources.emplace_back(source); + } +#endif // ALSOFT_EAX } while(--n); std::copy(ids.cbegin(), ids.cend(), sources); + +#if ALSOFT_EAX + if (context->has_eax()) + { + std::unique_lock prop_lock{context->mPropLock}; + + for (auto& eax_source : eax_sources) + { + context->eax_initialize_source(*eax_source); + } + } +#endif // ALSOFT_EAX } } END_API_FUNC @@ -3555,6 +3595,10 @@ ALsource::ALsource() ALsource::~ALsource() { +#if ALSOFT_EAX + eax_uninitialize(); +#endif // ALSOFT_EAX + for(auto &item : mQueue) { if(ALbuffer *buffer{item.mBuffer}) @@ -3597,3 +3641,2447 @@ SourceSubList::~SourceSubList() al_free(Sources); Sources = nullptr; } + + +#if ALSOFT_EAX +class EaxSourceException : + public EaxException +{ +public: + explicit EaxSourceException( + const char* message) + : + EaxException{"EAX_SOURCE", message} + { + } +}; // EaxSourceException + + +class EaxSourceActiveFxSlotsException : + public EaxException +{ +public: + explicit EaxSourceActiveFxSlotsException( + const char* message) + : + EaxException{"EAX_SOURCE_ACTIVE_FX_SLOTS", message} + { + } +}; // EaxSourceActiveFxSlotsException + + +class EaxSourceSendException : + public EaxException +{ +public: + explicit EaxSourceSendException( + const char* message) + : + EaxException{"EAX_SOURCE_SEND", message} + { + } +}; // EaxSourceSendException + + +void ALsource::eax_initialize( + const EaxSourceInitParam& param) noexcept +{ + eax_validate_init_param(param); + eax_copy_init_param(param); + eax_set_defaults(); + eax_initialize_fx_slots(); + + eax_d_ = eax_; +} + +void ALsource::eax_uninitialize() +{ + if (!eax_al_context_) + { + return; + } + + if (eax_al_context_->has_eax()) + { + for (auto i = 0; i < EAX_MAX_FXSLOTS; ++i) + { + eax_al_source_3i( + AL_AUXILIARY_SEND_FILTER, + AL_EFFECTSLOT_NULL, + static_cast(i), + AL_FILTER_NULL + ); + } + } + + eax_al_context_ = nullptr; +} + +void ALsource::eax_dispatch( + const EaxEaxCall& eax_call) +{ + if (eax_call.is_get()) + { + eax_get(eax_call); + } + else + { + eax_set(eax_call); + } +} + +void ALsource::eax_update_filters() +{ + eax_update_filters_internal(); +} + +void ALsource::eax_update( + EaxContextSharedDirtyFlags dirty_flags) +{ + if (dirty_flags.primary_fx_slot_id) + { + if (eax_uses_primary_id_) + { + eax_update_primary_fx_slot_id(); + } + } + + if (dirty_flags.air_absorption_hf) + { + eax_set_air_absorption_factor(); + } +} + +ALsource* ALsource::eax_lookup_source( + ALCcontext& al_context, + ALuint source_id) noexcept +{ + return LookupSource(&al_context, source_id); +} + +[[noreturn]] +void ALsource::eax_fail( + const char* message) +{ + throw EaxSourceException{message}; +} + +void ALsource::eax_validate_init_param( + const EaxSourceInitParam& param) +{ + if (!param.al_context) + { + eax_fail("Null context."); + } + + if (!param.al_filter) + { + eax_fail("Null filter."); + } +} + +void ALsource::eax_copy_init_param( + const EaxSourceInitParam& param) +{ + eax_al_context_ = param.al_context; + eax_al_filter_ = param.al_filter; +} + +void ALsource::eax_set_source_defaults() +{ + eax_.source.lDirect = EAXSOURCE_DEFAULTDIRECT; + eax_.source.lDirectHF = EAXSOURCE_DEFAULTDIRECTHF; + eax_.source.lRoom = EAXSOURCE_DEFAULTROOM; + eax_.source.lRoomHF = EAXSOURCE_DEFAULTROOMHF; + eax_.source.lObstruction = EAXSOURCE_DEFAULTOBSTRUCTION; + eax_.source.flObstructionLFRatio = EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO; + eax_.source.lOcclusion = EAXSOURCE_DEFAULTOCCLUSION; + eax_.source.flOcclusionLFRatio = EAXSOURCE_DEFAULTOCCLUSIONLFRATIO; + eax_.source.flOcclusionRoomRatio = EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO; + eax_.source.flOcclusionDirectRatio = EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO; + eax_.source.lExclusion = EAXSOURCE_DEFAULTEXCLUSION; + eax_.source.flExclusionLFRatio = EAXSOURCE_DEFAULTEXCLUSIONLFRATIO; + eax_.source.lOutsideVolumeHF = EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF; + eax_.source.flDopplerFactor = EAXSOURCE_DEFAULTDOPPLERFACTOR; + eax_.source.flRolloffFactor = EAXSOURCE_DEFAULTROLLOFFFACTOR; + eax_.source.flRoomRolloffFactor = EAXSOURCE_DEFAULTROOMROLLOFFFACTOR; + eax_.source.flAirAbsorptionFactor = EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR; + eax_.source.ulFlags = EAXSOURCE_DEFAULTFLAGS; +} + +void ALsource::eax_set_active_fx_slots_defaults() +{ + eax_.active_fx_slots = EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID; +} + +void ALsource::eax_set_send_defaults( + EAXSOURCEALLSENDPROPERTIES& eax_send) +{ + eax_send.guidReceivingFXSlotID = EAX_NULL_GUID; + eax_send.lSend = EAXSOURCE_DEFAULTSEND; + eax_send.lSendHF = EAXSOURCE_DEFAULTSENDHF; + eax_send.lOcclusion = EAXSOURCE_DEFAULTOCCLUSION; + eax_send.flOcclusionLFRatio = EAXSOURCE_DEFAULTOCCLUSIONLFRATIO; + eax_send.flOcclusionRoomRatio = EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO; + eax_send.flOcclusionDirectRatio = EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO; + eax_send.lExclusion = EAXSOURCE_DEFAULTEXCLUSION; + eax_send.flExclusionLFRatio = EAXSOURCE_DEFAULTEXCLUSIONLFRATIO; +} + +void ALsource::eax_set_sends_defaults() +{ + for (auto& eax_send : eax_.sends) + { + eax_set_send_defaults(eax_send); + } +} + +void ALsource::eax_set_speaker_levels_defaults() +{ + std::fill(eax_.speaker_levels.begin(), eax_.speaker_levels.end(), EAXSOURCE_DEFAULTSPEAKERLEVEL); +} + +void ALsource::eax_set_defaults() +{ + eax_set_source_defaults(); + eax_set_active_fx_slots_defaults(); + eax_set_sends_defaults(); + eax_set_speaker_levels_defaults(); +} + +float ALsource::eax_calculate_dst_occlusion_mb( + long src_occlusion_mb, + float path_ratio, + float lf_ratio) noexcept +{ + const auto ratio_1 = path_ratio + lf_ratio - 1.0F; + const auto ratio_2 = path_ratio * lf_ratio; + const auto ratio = (ratio_2 > ratio_1) ? ratio_2 : ratio_1; + const auto dst_occlustion_mb = static_cast(src_occlusion_mb) * ratio; + + return dst_occlustion_mb; +} + +EaxAlLowPassParam ALsource::eax_create_direct_filter_param() const noexcept +{ + auto gain_mb = + static_cast(eax_.source.lDirect) + + + (static_cast(eax_.source.lObstruction) * eax_.source.flObstructionLFRatio) + + + eax_calculate_dst_occlusion_mb( + eax_.source.lOcclusion, + eax_.source.flOcclusionDirectRatio, + eax_.source.flOcclusionLFRatio); + + auto gain_hf_mb = + static_cast(eax_.source.lDirectHF) + + + static_cast(eax_.source.lObstruction) + + + (static_cast(eax_.source.lOcclusion) * eax_.source.flOcclusionDirectRatio); + + for (auto i = std::size_t{}; i < EAX_MAX_FXSLOTS; ++i) + { + if (eax_active_fx_slots_[i]) + { + const auto& send = eax_.sends[i]; + + gain_mb += eax_calculate_dst_occlusion_mb( + send.lOcclusion, + send.flOcclusionDirectRatio, + send.flOcclusionLFRatio); + + gain_hf_mb += static_cast(send.lOcclusion) * send.flOcclusionDirectRatio; + } + } + + const auto max_gain = eax_al_context_->eax_get_max_filter_gain(); + + const auto al_low_pass_param = EaxAlLowPassParam + { + clamp(level_mb_to_gain(gain_mb), 0.0F, max_gain), + clamp(level_mb_to_gain(gain_hf_mb), 0.0F, max_gain) + }; + + return al_low_pass_param; +} + +EaxAlLowPassParam ALsource::eax_create_room_filter_param( + const ALeffectslot& fx_slot, + const EAXSOURCEALLSENDPROPERTIES& send) const noexcept +{ + const auto& fx_slot_eax = fx_slot.eax_get_eax_fx_slot(); + + const auto gain_mb = + static_cast( + eax_.source.lRoom + + send.lSend) + + + eax_calculate_dst_occlusion_mb( + eax_.source.lOcclusion, + eax_.source.flOcclusionRoomRatio, + eax_.source.flOcclusionLFRatio + ) + + + eax_calculate_dst_occlusion_mb( + send.lOcclusion, + send.flOcclusionRoomRatio, + send.flOcclusionLFRatio + ) + + + (static_cast(eax_.source.lExclusion) * eax_.source.flExclusionLFRatio) + + (static_cast(send.lExclusion) * send.flExclusionLFRatio) + + + 0.0F; + + const auto gain_hf_mb = + static_cast( + eax_.source.lRoomHF + + send.lSendHF) + + + (static_cast(fx_slot_eax.lOcclusion + eax_.source.lOcclusion) * eax_.source.flOcclusionRoomRatio) + + (static_cast(send.lOcclusion) * send.flOcclusionRoomRatio) + + + static_cast( + eax_.source.lExclusion + + send.lExclusion) + + + 0.0F; + + const auto max_gain = eax_al_context_->eax_get_max_filter_gain(); + + const auto al_low_pass_param = EaxAlLowPassParam + { + clamp(level_mb_to_gain(gain_mb), 0.0F, max_gain), + clamp(level_mb_to_gain(gain_hf_mb), 0.0F, max_gain) + }; + + return al_low_pass_param; +} + +void ALsource::eax_set_al_filter_parameters( + const EaxAlLowPassParam& al_low_pass_param) const noexcept +{ + eax_al_filter_->eax_set_low_pass_params(*eax_al_context_, al_low_pass_param); +} + +void ALsource::eax_set_fx_slots() +{ + eax_uses_primary_id_ = false; + eax_has_active_fx_slots_ = false; + + for (auto i = 0; i < EAX_MAX_FXSLOTS; ++i) + { + const auto& eax_active_fx_slot_id = eax_.active_fx_slots.guidActiveFXSlots[i]; + + auto fx_slot_index = EaxFxSlotIndex{}; + + if (eax_active_fx_slot_id == EAX_PrimaryFXSlotID) + { + eax_uses_primary_id_ = true; + fx_slot_index = eax_al_context_->eax_get_primary_fx_slot_index(); + } + else + { + fx_slot_index = eax_active_fx_slot_id; + } + + if (fx_slot_index.has_value()) + { + eax_has_active_fx_slots_ = true; + eax_active_fx_slots_[fx_slot_index] = true; + } + } + + for (auto i = 0; i < EAX_MAX_FXSLOTS; ++i) + { + if (!eax_active_fx_slots_[static_cast(i)]) + { + eax_al_source_3i( + AL_AUXILIARY_SEND_FILTER, + AL_EFFECTSLOT_NULL, + i, + AL_FILTER_NULL); + } + } +} + +void ALsource::eax_initialize_fx_slots() +{ + eax_set_fx_slots(); + eax_update_filters_internal(); +} + +void ALsource::eax_update_direct_filter_internal() +{ + const auto& direct_param = eax_create_direct_filter_param(); + eax_set_al_filter_parameters(direct_param); + + eax_al_source_i( + AL_DIRECT_FILTER, + static_cast(eax_al_filter_->id)); +} + +void ALsource::eax_update_room_filters_internal() +{ + if (!eax_has_active_fx_slots_) + { + return; + } + + for (auto i = 0; i < EAX_MAX_FXSLOTS; ++i) + { + if (eax_active_fx_slots_[static_cast(i)]) + { + const auto& fx_slot = eax_al_context_->eax_get_fx_slot(static_cast(i)); + const auto& send = eax_.sends[static_cast(i)]; + const auto& room_param = eax_create_room_filter_param(fx_slot, send); + + eax_set_al_filter_parameters(room_param); + + eax_al_source_3i( + AL_AUXILIARY_SEND_FILTER, + static_cast(fx_slot.id), + i, + static_cast(eax_al_filter_->id) + ); + } + } +} + +void ALsource::eax_update_filters_internal() +{ + eax_update_direct_filter_internal(); + eax_update_room_filters_internal(); +} + +void ALsource::eax_update_primary_fx_slot_id() +{ + const auto& previous_primary_fx_slot_index = eax_al_context_->eax_get_previous_primary_fx_slot_index(); + const auto& primary_fx_slot_index = eax_al_context_->eax_get_primary_fx_slot_index(); + + if (previous_primary_fx_slot_index == primary_fx_slot_index) + { + return; + } + + if (previous_primary_fx_slot_index.has_value()) + { + const auto fx_slot_index = previous_primary_fx_slot_index.get(); + eax_active_fx_slots_[fx_slot_index] = false; + + eax_al_source_3i( + AL_AUXILIARY_SEND_FILTER, + AL_EFFECTSLOT_NULL, + static_cast(fx_slot_index), + static_cast(AL_FILTER_NULL)); + } + + if (primary_fx_slot_index.has_value()) + { + const auto fx_slot_index = primary_fx_slot_index.get(); + eax_active_fx_slots_[fx_slot_index] = true; + + const auto& fx_slot = eax_al_context_->eax_get_fx_slot(fx_slot_index); + const auto& send = eax_.sends[fx_slot_index]; + const auto& room_param = eax_create_room_filter_param(fx_slot, send); + + eax_set_al_filter_parameters(room_param); + + eax_al_source_3i( + AL_AUXILIARY_SEND_FILTER, + static_cast(fx_slot.id), + static_cast(fx_slot_index), + static_cast(eax_al_filter_->id)); + } + + eax_has_active_fx_slots_ = std::any_of( + eax_active_fx_slots_.cbegin(), + eax_active_fx_slots_.cend(), + [](const auto& item) + { + return item; + } + ); +} + +void ALsource::eax_defer_active_fx_slots( + const EaxEaxCall& eax_call) +{ + const auto active_fx_slots_span = + eax_call.get_values(); + + const auto fx_slot_count = active_fx_slots_span.size(); + + if (fx_slot_count <= 0 || fx_slot_count > EAX_MAX_FXSLOTS) + { + throw EaxSourceActiveFxSlotsException{"Count out of range."}; + } + + for (auto i = std::size_t{}; i < fx_slot_count; ++i) + { + const auto& fx_slot_guid = active_fx_slots_span[i]; + + if (fx_slot_guid != EAX_NULL_GUID && + fx_slot_guid != EAX_PrimaryFXSlotID && + fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot0 && + fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot0 && + fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot1 && + fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot1 && + fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot2 && + fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot2 && + fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot3 && + fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot3) + { + throw EaxSourceActiveFxSlotsException{"Unsupported GUID."}; + } + } + + for (auto i = std::size_t{}; i < fx_slot_count; ++i) + { + eax_d_.active_fx_slots.guidActiveFXSlots[i] = active_fx_slots_span[i]; + } + + for (auto i = fx_slot_count; i < EAX_MAX_FXSLOTS; ++i) + { + eax_d_.active_fx_slots.guidActiveFXSlots[i] = EAX_NULL_GUID; + } + + eax_are_active_fx_slots_dirty_ = (eax_d_.active_fx_slots != eax_.active_fx_slots); +} + + +const char* ALsource::eax_get_exclusion_name() noexcept +{ + return "Exclusion"; +} + +const char* ALsource::eax_get_exclusion_lf_ratio_name() noexcept +{ + return "Exclusion LF Ratio"; +} + +const char* ALsource::eax_get_occlusion_name() noexcept +{ + return "Occlusion"; +} + +const char* ALsource::eax_get_occlusion_lf_ratio_name() noexcept +{ + return "Occlusion LF Ratio"; +} + +const char* ALsource::eax_get_occlusion_direct_ratio_name() noexcept +{ + return "Occlusion Direct Ratio"; +} + +const char* ALsource::eax_get_occlusion_room_ratio_name() noexcept +{ + return "Occlusion Room Ratio"; +} + + +void ALsource::eax_validate_send_receiving_fx_slot_guid( + const GUID& guidReceivingFXSlotID) +{ + if (guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot0 && + guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot0 && + guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot1 && + guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot1 && + guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot2 && + guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot2 && + guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot3 && + guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot3) + { + throw EaxSourceSendException{"Unsupported receiving FX slot GUID."}; + } +} + +void ALsource::eax_validate_send_send( + long lSend) +{ + eax_validate_range( + "Send", + lSend, + EAXSOURCE_MINSEND, + EAXSOURCE_MAXSEND); +} + +void ALsource::eax_validate_send_send_hf( + long lSendHF) +{ + eax_validate_range( + "Send HF", + lSendHF, + EAXSOURCE_MINSENDHF, + EAXSOURCE_MAXSENDHF); +} + +void ALsource::eax_validate_send_occlusion( + long lOcclusion) +{ + eax_validate_range( + eax_get_occlusion_name(), + lOcclusion, + EAXSOURCE_MINOCCLUSION, + EAXSOURCE_MAXOCCLUSION); +} + +void ALsource::eax_validate_send_occlusion_lf_ratio( + float flOcclusionLFRatio) +{ + eax_validate_range( + eax_get_occlusion_lf_ratio_name(), + flOcclusionLFRatio, + EAXSOURCE_MINOCCLUSIONLFRATIO, + EAXSOURCE_MAXOCCLUSIONLFRATIO); +} + +void ALsource::eax_validate_send_occlusion_room_ratio( + float flOcclusionRoomRatio) +{ + eax_validate_range( + eax_get_occlusion_room_ratio_name(), + flOcclusionRoomRatio, + EAXSOURCE_MINOCCLUSIONROOMRATIO, + EAXSOURCE_MAXOCCLUSIONROOMRATIO); +} + +void ALsource::eax_validate_send_occlusion_direct_ratio( + float flOcclusionDirectRatio) +{ + eax_validate_range( + eax_get_occlusion_direct_ratio_name(), + flOcclusionDirectRatio, + EAXSOURCE_MINOCCLUSIONDIRECTRATIO, + EAXSOURCE_MAXOCCLUSIONDIRECTRATIO); +} + +void ALsource::eax_validate_send_exclusion( + long lExclusion) +{ + eax_validate_range( + eax_get_exclusion_name(), + lExclusion, + EAXSOURCE_MINEXCLUSION, + EAXSOURCE_MAXEXCLUSION); +} + +void ALsource::eax_validate_send_exclusion_lf_ratio( + float flExclusionLFRatio) +{ + eax_validate_range( + eax_get_exclusion_lf_ratio_name(), + flExclusionLFRatio, + EAXSOURCE_MINEXCLUSIONLFRATIO, + EAXSOURCE_MAXEXCLUSIONLFRATIO); +} + +void ALsource::eax_validate_send( + const EAXSOURCESENDPROPERTIES& all) +{ + eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID); + eax_validate_send_send(all.lSend); + eax_validate_send_send_hf(all.lSendHF); +} + +void ALsource::eax_validate_send_exclusion_all( + const EAXSOURCEEXCLUSIONSENDPROPERTIES& all) +{ + eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID); + eax_validate_send_exclusion(all.lExclusion); + eax_validate_send_exclusion_lf_ratio(all.flExclusionLFRatio); +} + +void ALsource::eax_validate_send_occlusion_all( + const EAXSOURCEOCCLUSIONSENDPROPERTIES& all) +{ + eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID); + eax_validate_send_occlusion(all.lOcclusion); + eax_validate_send_occlusion_lf_ratio(all.flOcclusionLFRatio); + eax_validate_send_occlusion_room_ratio(all.flOcclusionRoomRatio); + eax_validate_send_occlusion_direct_ratio(all.flOcclusionDirectRatio); +} + +void ALsource::eax_validate_send_all( + const EAXSOURCEALLSENDPROPERTIES& all) +{ + eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID); + eax_validate_send_send(all.lSend); + eax_validate_send_send_hf(all.lSendHF); + eax_validate_send_occlusion(all.lOcclusion); + eax_validate_send_occlusion_lf_ratio(all.flOcclusionLFRatio); + eax_validate_send_occlusion_room_ratio(all.flOcclusionRoomRatio); + eax_validate_send_occlusion_direct_ratio(all.flOcclusionDirectRatio); + eax_validate_send_exclusion(all.lExclusion); + eax_validate_send_exclusion_lf_ratio(all.flExclusionLFRatio); +} + +EaxFxSlotIndexValue ALsource::eax_get_send_index( + const GUID& send_guid) +{ + if (false) + { + } + else if (send_guid == EAXPROPERTYID_EAX40_FXSlot0 || send_guid == EAXPROPERTYID_EAX50_FXSlot0) + { + return 0; + } + else if (send_guid == EAXPROPERTYID_EAX40_FXSlot1 || send_guid == EAXPROPERTYID_EAX50_FXSlot1) + { + return 1; + } + else if (send_guid == EAXPROPERTYID_EAX40_FXSlot2 || send_guid == EAXPROPERTYID_EAX50_FXSlot2) + { + return 2; + } + else if (send_guid == EAXPROPERTYID_EAX40_FXSlot3 || send_guid == EAXPROPERTYID_EAX50_FXSlot3) + { + return 3; + } + else + { + throw EaxSourceSendException{"Unsupported receiving FX slot GUID."}; + } +} + +void ALsource::eax_defer_send_send( + long lSend, + EaxFxSlotIndexValue index) +{ + eax_d_.sends[index].lSend = lSend; + + eax_sends_dirty_flags_.sends[index].lSend = + (eax_.sends[index].lSend != eax_d_.sends[index].lSend); +} + +void ALsource::eax_defer_send_send_hf( + long lSendHF, + EaxFxSlotIndexValue index) +{ + eax_d_.sends[index].lSendHF = lSendHF; + + eax_sends_dirty_flags_.sends[index].lSendHF = + (eax_.sends[index].lSendHF != eax_d_.sends[index].lSendHF); +} + +void ALsource::eax_defer_send_occlusion( + long lOcclusion, + EaxFxSlotIndexValue index) +{ + eax_d_.sends[index].lOcclusion = lOcclusion; + + eax_sends_dirty_flags_.sends[index].lOcclusion = + (eax_.sends[index].lOcclusion != eax_d_.sends[index].lOcclusion); +} + +void ALsource::eax_defer_send_occlusion_lf_ratio( + float flOcclusionLFRatio, + EaxFxSlotIndexValue index) +{ + eax_d_.sends[index].flOcclusionLFRatio = flOcclusionLFRatio; + + eax_sends_dirty_flags_.sends[index].flOcclusionLFRatio = + (eax_.sends[index].flOcclusionLFRatio != eax_d_.sends[index].flOcclusionLFRatio); +} + +void ALsource::eax_defer_send_occlusion_room_ratio( + float flOcclusionRoomRatio, + EaxFxSlotIndexValue index) +{ + eax_d_.sends[index].flOcclusionRoomRatio = flOcclusionRoomRatio; + + eax_sends_dirty_flags_.sends[index].flOcclusionRoomRatio = + (eax_.sends[index].flOcclusionRoomRatio != eax_d_.sends[index].flOcclusionRoomRatio); +} + +void ALsource::eax_defer_send_occlusion_direct_ratio( + float flOcclusionDirectRatio, + EaxFxSlotIndexValue index) +{ + eax_d_.sends[index].flOcclusionDirectRatio = flOcclusionDirectRatio; + + eax_sends_dirty_flags_.sends[index].flOcclusionDirectRatio = + (eax_.sends[index].flOcclusionDirectRatio != eax_d_.sends[index].flOcclusionDirectRatio); +} + +void ALsource::eax_defer_send_exclusion( + long lExclusion, + EaxFxSlotIndexValue index) +{ + eax_d_.sends[index].lExclusion = lExclusion; + + eax_sends_dirty_flags_.sends[index].lExclusion = + (eax_.sends[index].lExclusion != eax_d_.sends[index].lExclusion); +} + +void ALsource::eax_defer_send_exclusion_lf_ratio( + float flExclusionLFRatio, + EaxFxSlotIndexValue index) +{ + eax_d_.sends[index].flExclusionLFRatio = flExclusionLFRatio; + + eax_sends_dirty_flags_.sends[index].flExclusionLFRatio = + (eax_.sends[index].flExclusionLFRatio != eax_d_.sends[index].flExclusionLFRatio); +} + +void ALsource::eax_defer_send( + const EAXSOURCESENDPROPERTIES& all, + EaxFxSlotIndexValue index) +{ + eax_defer_send_send(all.lSend, index); + eax_defer_send_send_hf(all.lSendHF, index); +} + +void ALsource::eax_defer_send_exclusion_all( + const EAXSOURCEEXCLUSIONSENDPROPERTIES& all, + EaxFxSlotIndexValue index) +{ + eax_defer_send_exclusion(all.lExclusion, index); + eax_defer_send_exclusion_lf_ratio(all.flExclusionLFRatio, index); +} + +void ALsource::eax_defer_send_occlusion_all( + const EAXSOURCEOCCLUSIONSENDPROPERTIES& all, + EaxFxSlotIndexValue index) +{ + eax_defer_send_occlusion(all.lOcclusion, index); + eax_defer_send_occlusion_lf_ratio(all.flOcclusionLFRatio, index); + eax_defer_send_occlusion_room_ratio(all.flOcclusionRoomRatio, index); + eax_defer_send_occlusion_direct_ratio(all.flOcclusionDirectRatio, index); +} + +void ALsource::eax_defer_send_all( + const EAXSOURCEALLSENDPROPERTIES& all, + EaxFxSlotIndexValue index) +{ + eax_defer_send_send(all.lSend, index); + eax_defer_send_send_hf(all.lSendHF, index); + eax_defer_send_occlusion(all.lOcclusion, index); + eax_defer_send_occlusion_lf_ratio(all.flOcclusionLFRatio, index); + eax_defer_send_occlusion_room_ratio(all.flOcclusionRoomRatio, index); + eax_defer_send_occlusion_direct_ratio(all.flOcclusionDirectRatio, index); + eax_defer_send_exclusion(all.lExclusion, index); + eax_defer_send_exclusion_lf_ratio(all.flExclusionLFRatio, index); +} + +void ALsource::eax_defer_send( + const EaxEaxCall& eax_call) +{ + const auto eax_all_span = + eax_call.get_values(); + + const auto count = eax_all_span.size(); + + if (count <= 0 || count > EAX_MAX_FXSLOTS) + { + throw EaxSourceSendException{"Send count out of range."}; + } + + for (auto i = std::size_t{}; i < count; ++i) + { + const auto& all = eax_all_span[i]; + eax_validate_send(all); + } + + for (auto i = std::size_t{}; i < count; ++i) + { + const auto& all = eax_all_span[i]; + const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID); + eax_defer_send(all, send_index); + } +} + +void ALsource::eax_defer_send_exclusion_all( + const EaxEaxCall& eax_call) +{ + const auto eax_all_span = + eax_call.get_values(); + + const auto count = eax_all_span.size(); + + if (count <= 0 || count > EAX_MAX_FXSLOTS) + { + throw EaxSourceSendException{"Send exclusion all count out of range."}; + } + + for (auto i = std::size_t{}; i < count; ++i) + { + const auto& all = eax_all_span[i]; + eax_validate_send_exclusion_all(all); + } + + for (auto i = std::size_t{}; i < count; ++i) + { + const auto& all = eax_all_span[i]; + const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID); + eax_defer_send_exclusion_all(all, send_index); + } +} + +void ALsource::eax_defer_send_occlusion_all( + const EaxEaxCall& eax_call) +{ + const auto eax_all_span = + eax_call.get_values(); + + const auto count = eax_all_span.size(); + + if (count <= 0 || count > EAX_MAX_FXSLOTS) + { + throw EaxSourceSendException{"Send occlusion all count out of range."}; + } + + for (auto i = std::size_t{}; i < count; ++i) + { + const auto& all = eax_all_span[i]; + eax_validate_send_occlusion_all(all); + } + + for (auto i = std::size_t{}; i < count; ++i) + { + const auto& all = eax_all_span[i]; + const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID); + eax_defer_send_occlusion_all(all, send_index); + } +} + +void ALsource::eax_defer_send_all( + const EaxEaxCall& eax_call) +{ + const auto eax_all_span = + eax_call.get_values(); + + const auto count = eax_all_span.size(); + + if (count <= 0 || count > EAX_MAX_FXSLOTS) + { + throw EaxSourceSendException{"Send all count out of range."}; + } + + for (auto i = std::size_t{}; i < count; ++i) + { + const auto& all = eax_all_span[i]; + eax_validate_send_all(all); + } + + for (auto i = std::size_t{}; i < count; ++i) + { + const auto& all = eax_all_span[i]; + const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID); + eax_defer_send_all(all, send_index); + } +} + + +void ALsource::eax_validate_source_direct( + long direct) +{ + eax_validate_range( + "Direct", + direct, + EAXSOURCE_MINDIRECT, + EAXSOURCE_MAXDIRECT); +} + +void ALsource::eax_validate_source_direct_hf( + long direct_hf) +{ + eax_validate_range( + "Direct HF", + direct_hf, + EAXSOURCE_MINDIRECTHF, + EAXSOURCE_MAXDIRECTHF); +} + +void ALsource::eax_validate_source_room( + long room) +{ + eax_validate_range( + "Room", + room, + EAXSOURCE_MINROOM, + EAXSOURCE_MAXROOM); +} + +void ALsource::eax_validate_source_room_hf( + long room_hf) +{ + eax_validate_range( + "Room HF", + room_hf, + EAXSOURCE_MINROOMHF, + EAXSOURCE_MAXROOMHF); +} + +void ALsource::eax_validate_source_obstruction( + long obstruction) +{ + eax_validate_range( + "Obstruction", + obstruction, + EAXSOURCE_MINOBSTRUCTION, + EAXSOURCE_MAXOBSTRUCTION); +} + +void ALsource::eax_validate_source_obstruction_lf_ratio( + float obstruction_lf_ratio) +{ + eax_validate_range( + "Obstruction LF Ratio", + obstruction_lf_ratio, + EAXSOURCE_MINOBSTRUCTIONLFRATIO, + EAXSOURCE_MAXOBSTRUCTIONLFRATIO); +} + +void ALsource::eax_validate_source_occlusion( + long occlusion) +{ + eax_validate_range( + eax_get_occlusion_name(), + occlusion, + EAXSOURCE_MINOCCLUSION, + EAXSOURCE_MAXOCCLUSION); +} + +void ALsource::eax_validate_source_occlusion_lf_ratio( + float occlusion_lf_ratio) +{ + eax_validate_range( + eax_get_occlusion_lf_ratio_name(), + occlusion_lf_ratio, + EAXSOURCE_MINOCCLUSIONLFRATIO, + EAXSOURCE_MAXOCCLUSIONLFRATIO); +} + +void ALsource::eax_validate_source_occlusion_room_ratio( + float occlusion_room_ratio) +{ + eax_validate_range( + eax_get_occlusion_room_ratio_name(), + occlusion_room_ratio, + EAXSOURCE_MINOCCLUSIONROOMRATIO, + EAXSOURCE_MAXOCCLUSIONROOMRATIO); +} + +void ALsource::eax_validate_source_occlusion_direct_ratio( + float occlusion_direct_ratio) +{ + eax_validate_range( + eax_get_occlusion_direct_ratio_name(), + occlusion_direct_ratio, + EAXSOURCE_MINOCCLUSIONDIRECTRATIO, + EAXSOURCE_MAXOCCLUSIONDIRECTRATIO); +} + +void ALsource::eax_validate_source_exclusion( + long exclusion) +{ + eax_validate_range( + eax_get_exclusion_name(), + exclusion, + EAXSOURCE_MINEXCLUSION, + EAXSOURCE_MAXEXCLUSION); +} + +void ALsource::eax_validate_source_exclusion_lf_ratio( + float exclusion_lf_ratio) +{ + eax_validate_range( + eax_get_exclusion_lf_ratio_name(), + exclusion_lf_ratio, + EAXSOURCE_MINEXCLUSIONLFRATIO, + EAXSOURCE_MAXEXCLUSIONLFRATIO); +} + +void ALsource::eax_validate_source_outside_volume_hf( + long outside_volume_hf) +{ + eax_validate_range( + "Outside Volume HF", + outside_volume_hf, + EAXSOURCE_MINOUTSIDEVOLUMEHF, + EAXSOURCE_MAXOUTSIDEVOLUMEHF); +} + +void ALsource::eax_validate_source_doppler_factor( + float doppler_factor) +{ + eax_validate_range( + "Doppler Factor", + doppler_factor, + EAXSOURCE_MINDOPPLERFACTOR, + EAXSOURCE_MAXDOPPLERFACTOR); +} + +void ALsource::eax_validate_source_rolloff_factor( + float rolloff_factor) +{ + eax_validate_range( + "Rolloff Factor", + rolloff_factor, + EAXSOURCE_MINROLLOFFFACTOR, + EAXSOURCE_MAXROLLOFFFACTOR); +} + +void ALsource::eax_validate_source_room_rolloff_factor( + float room_rolloff_factor) +{ + eax_validate_range( + "Room Rolloff Factor", + room_rolloff_factor, + EAXSOURCE_MINROOMROLLOFFFACTOR, + EAXSOURCE_MAXROOMROLLOFFFACTOR); +} + +void ALsource::eax_validate_source_air_absorption_factor( + float air_absorption_factor) +{ + eax_validate_range( + "Air Absorption Factor", + air_absorption_factor, + EAXSOURCE_MINAIRABSORPTIONFACTOR, + EAXSOURCE_MAXAIRABSORPTIONFACTOR); +} + +void ALsource::eax_validate_source_flags( + unsigned long flags, + int eax_version) +{ + eax_validate_range( + "Flags", + flags, + 0UL, + ~((eax_version == 5) ? EAX50SOURCEFLAGS_RESERVED : EAX20SOURCEFLAGS_RESERVED)); +} + +void ALsource::eax_validate_source_macro_fx_factor( + float macro_fx_factor) +{ + eax_validate_range( + "Macro FX Factor", + macro_fx_factor, + EAXSOURCE_MINMACROFXFACTOR, + EAXSOURCE_MAXMACROFXFACTOR); +} + +void ALsource::eax_validate_source_2d_all( + const EAXSOURCE2DPROPERTIES& all, + int eax_version) +{ + eax_validate_source_direct(all.lDirect); + eax_validate_source_direct_hf(all.lDirectHF); + eax_validate_source_room(all.lRoom); + eax_validate_source_room_hf(all.lRoomHF); + eax_validate_source_flags(all.ulFlags, eax_version); +} + +void ALsource::eax_validate_source_obstruction_all( + const EAXOBSTRUCTIONPROPERTIES& all) +{ + eax_validate_source_obstruction(all.lObstruction); + eax_validate_source_obstruction_lf_ratio(all.flObstructionLFRatio); +} + +void ALsource::eax_validate_source_exclusion_all( + const EAXEXCLUSIONPROPERTIES& all) +{ + eax_validate_source_exclusion(all.lExclusion); + eax_validate_source_exclusion_lf_ratio(all.flExclusionLFRatio); +} + +void ALsource::eax_validate_source_occlusion_all( + const EAXOCCLUSIONPROPERTIES& all) +{ + eax_validate_source_occlusion(all.lOcclusion); + eax_validate_source_occlusion_lf_ratio(all.flOcclusionLFRatio); + eax_validate_source_occlusion_room_ratio(all.flOcclusionRoomRatio); + eax_validate_source_occlusion_direct_ratio(all.flOcclusionDirectRatio); +} + +void ALsource::eax_validate_source_all( + const EAX20BUFFERPROPERTIES& all, + int eax_version) +{ + eax_validate_source_direct(all.lDirect); + eax_validate_source_direct_hf(all.lDirectHF); + eax_validate_source_room(all.lRoom); + eax_validate_source_room_hf(all.lRoomHF); + eax_validate_source_obstruction(all.lObstruction); + eax_validate_source_obstruction_lf_ratio(all.flObstructionLFRatio); + eax_validate_source_occlusion(all.lOcclusion); + eax_validate_source_occlusion_lf_ratio(all.flOcclusionLFRatio); + eax_validate_source_occlusion_room_ratio(all.flOcclusionRoomRatio); + eax_validate_source_outside_volume_hf(all.lOutsideVolumeHF); + eax_validate_source_room_rolloff_factor(all.flRoomRolloffFactor); + eax_validate_source_air_absorption_factor(all.flAirAbsorptionFactor); + eax_validate_source_flags(all.dwFlags, eax_version); +} + +void ALsource::eax_validate_source_all( + const EAX30SOURCEPROPERTIES& all, + int eax_version) +{ + eax_validate_source_direct(all.lDirect); + eax_validate_source_direct_hf(all.lDirectHF); + eax_validate_source_room(all.lRoom); + eax_validate_source_room_hf(all.lRoomHF); + eax_validate_source_obstruction(all.lObstruction); + eax_validate_source_obstruction_lf_ratio(all.flObstructionLFRatio); + eax_validate_source_occlusion(all.lOcclusion); + eax_validate_source_occlusion_lf_ratio(all.flOcclusionLFRatio); + eax_validate_source_occlusion_room_ratio(all.flOcclusionRoomRatio); + eax_validate_source_occlusion_direct_ratio(all.flOcclusionDirectRatio); + eax_validate_source_exclusion(all.lExclusion); + eax_validate_source_exclusion_lf_ratio(all.flExclusionLFRatio); + eax_validate_source_outside_volume_hf(all.lOutsideVolumeHF); + eax_validate_source_doppler_factor(all.flDopplerFactor); + eax_validate_source_rolloff_factor(all.flRolloffFactor); + eax_validate_source_room_rolloff_factor(all.flRoomRolloffFactor); + eax_validate_source_air_absorption_factor(all.flAirAbsorptionFactor); + eax_validate_source_flags(all.ulFlags, eax_version); +} + +void ALsource::eax_validate_source_all( + const EAX50SOURCEPROPERTIES& all, + int eax_version) +{ + eax_validate_source_all(static_cast(all), eax_version); + eax_validate_source_macro_fx_factor(all.flMacroFXFactor); +} + +void ALsource::eax_validate_source_speaker_id( + long speaker_id) +{ + eax_validate_range( + "Speaker Id", + speaker_id, + static_cast(EAXSPEAKER_FRONT_LEFT), + static_cast(EAXSPEAKER_LOW_FREQUENCY)); +} + +void ALsource::eax_validate_source_speaker_level( + long speaker_level) +{ + eax_validate_range( + "Speaker Level", + speaker_level, + EAXSOURCE_MINSPEAKERLEVEL, + EAXSOURCE_MAXSPEAKERLEVEL); +} + +void ALsource::eax_validate_source_speaker_level_all( + const EAXSPEAKERLEVELPROPERTIES& all) +{ + eax_validate_source_speaker_id(all.lSpeakerID); + eax_validate_source_speaker_level(all.lLevel); +} + +void ALsource::eax_defer_source_direct( + long lDirect) +{ + eax_d_.source.lDirect = lDirect; + eax_source_dirty_filter_flags_.lDirect = (eax_.source.lDirect != eax_d_.source.lDirect); +} + +void ALsource::eax_defer_source_direct_hf( + long lDirectHF) +{ + eax_d_.source.lDirectHF = lDirectHF; + eax_source_dirty_filter_flags_.lDirectHF = (eax_.source.lDirectHF != eax_d_.source.lDirectHF); +} + +void ALsource::eax_defer_source_room( + long lRoom) +{ + eax_d_.source.lRoom = lRoom; + eax_source_dirty_filter_flags_.lRoom = (eax_.source.lRoom != eax_d_.source.lRoom); +} + +void ALsource::eax_defer_source_room_hf( + long lRoomHF) +{ + eax_d_.source.lRoomHF = lRoomHF; + eax_source_dirty_filter_flags_.lRoomHF = (eax_.source.lRoomHF != eax_d_.source.lRoomHF); +} + +void ALsource::eax_defer_source_obstruction( + long lObstruction) +{ + eax_d_.source.lObstruction = lObstruction; + eax_source_dirty_filter_flags_.lObstruction = (eax_.source.lObstruction != eax_d_.source.lObstruction); +} + +void ALsource::eax_defer_source_obstruction_lf_ratio( + float flObstructionLFRatio) +{ + eax_d_.source.flObstructionLFRatio = flObstructionLFRatio; + eax_source_dirty_filter_flags_.flObstructionLFRatio = (eax_.source.flObstructionLFRatio != eax_d_.source.flObstructionLFRatio); +} + +void ALsource::eax_defer_source_occlusion( + long lOcclusion) +{ + eax_d_.source.lOcclusion = lOcclusion; + eax_source_dirty_filter_flags_.lOcclusion = (eax_.source.lOcclusion != eax_d_.source.lOcclusion); +} + +void ALsource::eax_defer_source_occlusion_lf_ratio( + float flOcclusionLFRatio) +{ + eax_d_.source.flOcclusionLFRatio = flOcclusionLFRatio; + eax_source_dirty_filter_flags_.flOcclusionLFRatio = (eax_.source.flOcclusionLFRatio != eax_d_.source.flOcclusionLFRatio); +} + +void ALsource::eax_defer_source_occlusion_room_ratio( + float flOcclusionRoomRatio) +{ + eax_d_.source.flOcclusionRoomRatio = flOcclusionRoomRatio; + eax_source_dirty_filter_flags_.flOcclusionRoomRatio = (eax_.source.flOcclusionRoomRatio != eax_d_.source.flOcclusionRoomRatio); +} + +void ALsource::eax_defer_source_occlusion_direct_ratio( + float flOcclusionDirectRatio) +{ + eax_d_.source.flOcclusionDirectRatio = flOcclusionDirectRatio; + eax_source_dirty_filter_flags_.flOcclusionDirectRatio = (eax_.source.flOcclusionDirectRatio != eax_d_.source.flOcclusionDirectRatio); +} + +void ALsource::eax_defer_source_exclusion( + long lExclusion) +{ + eax_d_.source.lExclusion = lExclusion; + eax_source_dirty_filter_flags_.lExclusion = (eax_.source.lExclusion != eax_d_.source.lExclusion); +} + +void ALsource::eax_defer_source_exclusion_lf_ratio( + float flExclusionLFRatio) +{ + eax_d_.source.flExclusionLFRatio = flExclusionLFRatio; + eax_source_dirty_filter_flags_.flExclusionLFRatio = (eax_.source.flExclusionLFRatio != eax_d_.source.flExclusionLFRatio); +} + +void ALsource::eax_defer_source_outside_volume_hf( + long lOutsideVolumeHF) +{ + eax_d_.source.lOutsideVolumeHF = lOutsideVolumeHF; + eax_source_dirty_misc_flags_.lOutsideVolumeHF = (eax_.source.lOutsideVolumeHF != eax_d_.source.lOutsideVolumeHF); +} + +void ALsource::eax_defer_source_doppler_factor( + float flDopplerFactor) +{ + eax_d_.source.flDopplerFactor = flDopplerFactor; + eax_source_dirty_misc_flags_.flDopplerFactor = (eax_.source.flDopplerFactor != eax_d_.source.flDopplerFactor); +} + +void ALsource::eax_defer_source_rolloff_factor( + float flRolloffFactor) +{ + eax_d_.source.flRolloffFactor = flRolloffFactor; + eax_source_dirty_misc_flags_.flRolloffFactor = (eax_.source.flRolloffFactor != eax_d_.source.flRolloffFactor); +} + +void ALsource::eax_defer_source_room_rolloff_factor( + float flRoomRolloffFactor) +{ + eax_d_.source.flRoomRolloffFactor = flRoomRolloffFactor; + eax_source_dirty_misc_flags_.flRoomRolloffFactor = (eax_.source.flRoomRolloffFactor != eax_d_.source.flRoomRolloffFactor); +} + +void ALsource::eax_defer_source_air_absorption_factor( + float flAirAbsorptionFactor) +{ + eax_d_.source.flAirAbsorptionFactor = flAirAbsorptionFactor; + eax_source_dirty_misc_flags_.flAirAbsorptionFactor = (eax_.source.flAirAbsorptionFactor != eax_d_.source.flAirAbsorptionFactor); +} + +void ALsource::eax_defer_source_flags( + unsigned long ulFlags) +{ + eax_d_.source.ulFlags = ulFlags; + eax_source_dirty_misc_flags_.ulFlags = (eax_.source.ulFlags != eax_d_.source.ulFlags); +} + +void ALsource::eax_defer_source_macro_fx_factor( + float flMacroFXFactor) +{ + eax_d_.source.flMacroFXFactor = flMacroFXFactor; + eax_source_dirty_misc_flags_.flMacroFXFactor = (eax_.source.flMacroFXFactor != eax_d_.source.flMacroFXFactor); +} + +void ALsource::eax_defer_source_2d_all( + const EAXSOURCE2DPROPERTIES& all) +{ + eax_defer_source_direct(all.lDirect); + eax_defer_source_direct_hf(all.lDirectHF); + eax_defer_source_room(all.lRoom); + eax_defer_source_room_hf(all.lRoomHF); + eax_defer_source_flags(all.ulFlags); +} + +void ALsource::eax_defer_source_obstruction_all( + const EAXOBSTRUCTIONPROPERTIES& all) +{ + eax_defer_source_obstruction(all.lObstruction); + eax_defer_source_obstruction_lf_ratio(all.flObstructionLFRatio); +} + +void ALsource::eax_defer_source_exclusion_all( + const EAXEXCLUSIONPROPERTIES& all) +{ + eax_defer_source_exclusion(all.lExclusion); + eax_defer_source_exclusion_lf_ratio(all.flExclusionLFRatio); +} + +void ALsource::eax_defer_source_occlusion_all( + const EAXOCCLUSIONPROPERTIES& all) +{ + eax_defer_source_occlusion(all.lOcclusion); + eax_defer_source_occlusion_lf_ratio(all.flOcclusionLFRatio); + eax_defer_source_occlusion_room_ratio(all.flOcclusionRoomRatio); + eax_defer_source_occlusion_direct_ratio(all.flOcclusionDirectRatio); +} + +void ALsource::eax_defer_source_all( + const EAX20BUFFERPROPERTIES& all) +{ + eax_defer_source_direct(all.lDirect); + eax_defer_source_direct_hf(all.lDirectHF); + eax_defer_source_room(all.lRoom); + eax_defer_source_room_hf(all.lRoomHF); + eax_defer_source_obstruction(all.lObstruction); + eax_defer_source_obstruction_lf_ratio(all.flObstructionLFRatio); + eax_defer_source_occlusion(all.lOcclusion); + eax_defer_source_occlusion_lf_ratio(all.flOcclusionLFRatio); + eax_defer_source_occlusion_room_ratio(all.flOcclusionRoomRatio); + eax_defer_source_outside_volume_hf(all.lOutsideVolumeHF); + eax_defer_source_room_rolloff_factor(all.flRoomRolloffFactor); + eax_defer_source_air_absorption_factor(all.flAirAbsorptionFactor); + eax_defer_source_flags(all.dwFlags); +} + +void ALsource::eax_defer_source_all( + const EAX30SOURCEPROPERTIES& all) +{ + eax_defer_source_direct(all.lDirect); + eax_defer_source_direct_hf(all.lDirectHF); + eax_defer_source_room(all.lRoom); + eax_defer_source_room_hf(all.lRoomHF); + eax_defer_source_obstruction(all.lObstruction); + eax_defer_source_obstruction_lf_ratio(all.flObstructionLFRatio); + eax_defer_source_occlusion(all.lOcclusion); + eax_defer_source_occlusion_lf_ratio(all.flOcclusionLFRatio); + eax_defer_source_occlusion_room_ratio(all.flOcclusionRoomRatio); + eax_defer_source_occlusion_direct_ratio(all.flOcclusionDirectRatio); + eax_defer_source_exclusion(all.lExclusion); + eax_defer_source_exclusion_lf_ratio(all.flExclusionLFRatio); + eax_defer_source_outside_volume_hf(all.lOutsideVolumeHF); + eax_defer_source_doppler_factor(all.flDopplerFactor); + eax_defer_source_rolloff_factor(all.flRolloffFactor); + eax_defer_source_room_rolloff_factor(all.flRoomRolloffFactor); + eax_defer_source_air_absorption_factor(all.flAirAbsorptionFactor); + eax_defer_source_flags(all.ulFlags); +} + +void ALsource::eax_defer_source_all( + const EAX50SOURCEPROPERTIES& all) +{ + eax_defer_source_all(static_cast(all)); + eax_defer_source_macro_fx_factor(all.flMacroFXFactor); +} + +void ALsource::eax_defer_source_speaker_level_all( + const EAXSPEAKERLEVELPROPERTIES& all) +{ + const auto speaker_index = static_cast(all.lSpeakerID - 1); + auto& speaker_level_d = eax_d_.speaker_levels[speaker_index]; + const auto& speaker_level = eax_.speaker_levels[speaker_index]; + + if (speaker_level != speaker_level_d) + { + eax_source_dirty_misc_flags_.speaker_levels = true; + } +} + +void ALsource::eax_defer_source_direct( + const EaxEaxCall& eax_call) +{ + const auto direct = + eax_call.get_value(); + + eax_validate_source_direct(direct); + eax_defer_source_direct(direct); +} + +void ALsource::eax_defer_source_direct_hf( + const EaxEaxCall& eax_call) +{ + const auto direct_hf = + eax_call.get_value(); + + eax_validate_source_direct_hf(direct_hf); + eax_defer_source_direct_hf(direct_hf); +} + +void ALsource::eax_defer_source_room( + const EaxEaxCall& eax_call) +{ + const auto room = + eax_call.get_value(); + + eax_validate_source_room(room); + eax_defer_source_room(room); +} + +void ALsource::eax_defer_source_room_hf( + const EaxEaxCall& eax_call) +{ + const auto room_hf = + eax_call.get_value(); + + eax_validate_source_room_hf(room_hf); + eax_defer_source_room_hf(room_hf); +} + +void ALsource::eax_defer_source_obstruction( + const EaxEaxCall& eax_call) +{ + const auto obstruction = + eax_call.get_value(); + + eax_validate_source_obstruction(obstruction); + eax_defer_source_obstruction(obstruction); +} + +void ALsource::eax_defer_source_obstruction_lf_ratio( + const EaxEaxCall& eax_call) +{ + const auto obstruction_lf_ratio = + eax_call.get_value(); + + eax_validate_source_obstruction_lf_ratio(obstruction_lf_ratio); + eax_defer_source_obstruction_lf_ratio(obstruction_lf_ratio); +} + +void ALsource::eax_defer_source_occlusion( + const EaxEaxCall& eax_call) +{ + const auto occlusion = + eax_call.get_value(); + + eax_validate_source_occlusion(occlusion); + eax_defer_source_occlusion(occlusion); +} + +void ALsource::eax_defer_source_occlusion_lf_ratio( + const EaxEaxCall& eax_call) +{ + const auto occlusion_lf_ratio = + eax_call.get_value(); + + eax_validate_source_occlusion_lf_ratio(occlusion_lf_ratio); + eax_defer_source_occlusion_lf_ratio(occlusion_lf_ratio); +} + +void ALsource::eax_defer_source_occlusion_room_ratio( + const EaxEaxCall& eax_call) +{ + const auto occlusion_room_ratio = + eax_call.get_value(); + + eax_validate_source_occlusion_room_ratio(occlusion_room_ratio); + eax_defer_source_occlusion_room_ratio(occlusion_room_ratio); +} + +void ALsource::eax_defer_source_occlusion_direct_ratio( + const EaxEaxCall& eax_call) +{ + const auto occlusion_direct_ratio = + eax_call.get_value(); + + eax_validate_source_occlusion_direct_ratio(occlusion_direct_ratio); + eax_defer_source_occlusion_direct_ratio(occlusion_direct_ratio); +} + +void ALsource::eax_defer_source_exclusion( + const EaxEaxCall& eax_call) +{ + const auto exclusion = + eax_call.get_value(); + + eax_validate_source_exclusion(exclusion); + eax_defer_source_exclusion(exclusion); +} + +void ALsource::eax_defer_source_exclusion_lf_ratio( + const EaxEaxCall& eax_call) +{ + const auto exclusion_lf_ratio = + eax_call.get_value(); + + eax_validate_source_exclusion_lf_ratio(exclusion_lf_ratio); + eax_defer_source_exclusion_lf_ratio(exclusion_lf_ratio); +} + +void ALsource::eax_defer_source_outside_volume_hf( + const EaxEaxCall& eax_call) +{ + const auto outside_volume_hf = + eax_call.get_value(); + + eax_validate_source_outside_volume_hf(outside_volume_hf); + eax_defer_source_outside_volume_hf(outside_volume_hf); +} + +void ALsource::eax_defer_source_doppler_factor( + const EaxEaxCall& eax_call) +{ + const auto doppler_factor = + eax_call.get_value(); + + eax_validate_source_doppler_factor(doppler_factor); + eax_defer_source_doppler_factor(doppler_factor); +} + +void ALsource::eax_defer_source_rolloff_factor( + const EaxEaxCall& eax_call) +{ + const auto rolloff_factor = + eax_call.get_value(); + + eax_validate_source_rolloff_factor(rolloff_factor); + eax_defer_source_rolloff_factor(rolloff_factor); +} + +void ALsource::eax_defer_source_room_rolloff_factor( + const EaxEaxCall& eax_call) +{ + const auto room_rolloff_factor = + eax_call.get_value(); + + eax_validate_source_room_rolloff_factor(room_rolloff_factor); + eax_defer_source_room_rolloff_factor(room_rolloff_factor); +} + +void ALsource::eax_defer_source_air_absorption_factor( + const EaxEaxCall& eax_call) +{ + const auto air_absorption_factor = + eax_call.get_value(); + + eax_validate_source_air_absorption_factor(air_absorption_factor); + eax_defer_source_air_absorption_factor(air_absorption_factor); +} + +void ALsource::eax_defer_source_flags( + const EaxEaxCall& eax_call) +{ + const auto flags = + eax_call.get_value(); + + eax_validate_source_flags(flags, eax_call.get_version()); + eax_defer_source_flags(flags); +} + +void ALsource::eax_defer_source_macro_fx_factor( + const EaxEaxCall& eax_call) +{ + const auto macro_fx_factor = + eax_call.get_value(); + + eax_validate_source_macro_fx_factor(macro_fx_factor); + eax_defer_source_macro_fx_factor(macro_fx_factor); +} + +void ALsource::eax_defer_source_2d_all( + const EaxEaxCall& eax_call) +{ + const auto all = eax_call.get_value(); + + eax_validate_source_2d_all(all, eax_call.get_version()); + eax_defer_source_2d_all(all); +} + +void ALsource::eax_defer_source_obstruction_all( + const EaxEaxCall& eax_call) +{ + const auto all = eax_call.get_value(); + + eax_validate_source_obstruction_all(all); + eax_defer_source_obstruction_all(all); +} + +void ALsource::eax_defer_source_exclusion_all( + const EaxEaxCall& eax_call) +{ + const auto all = eax_call.get_value(); + + eax_validate_source_exclusion_all(all); + eax_defer_source_exclusion_all(all); +} + +void ALsource::eax_defer_source_occlusion_all( + const EaxEaxCall& eax_call) +{ + const auto all = eax_call.get_value(); + + eax_validate_source_occlusion_all(all); + eax_defer_source_occlusion_all(all); +} + +void ALsource::eax_defer_source_all( + const EaxEaxCall& eax_call) +{ + const auto eax_version = eax_call.get_version(); + + if (eax_version == 2) + { + const auto all = eax_call.get_value(); + + eax_validate_source_all(all, eax_version); + eax_defer_source_all(all); + } + else if (eax_version < 5) + { + const auto all = eax_call.get_value(); + + eax_validate_source_all(all, eax_version); + eax_defer_source_all(all); + } + else + { + const auto all = eax_call.get_value(); + + eax_validate_source_all(all, eax_version); + eax_defer_source_all(all); + } +} + +void ALsource::eax_defer_source_speaker_level_all( + const EaxEaxCall& eax_call) +{ + const auto speaker_level_properties = eax_call.get_value(); + + eax_validate_source_speaker_level_all(speaker_level_properties); + eax_defer_source_speaker_level_all(speaker_level_properties); +} + +void ALsource::eax_set_outside_volume_hf() +{ + const auto efx_gain_hf = clamp( + level_mb_to_gain(static_cast(eax_.source.lOutsideVolumeHF)), + AL_MIN_CONE_OUTER_GAINHF, + AL_MAX_CONE_OUTER_GAINHF + ); + + eax_al_source_f( + AL_CONE_OUTER_GAINHF, + efx_gain_hf + ); +} + +void ALsource::eax_set_doppler_factor() +{ + eax_al_source_f( + AL_DOPPLER_FACTOR, + eax_.source.flDopplerFactor + ); +} + +void ALsource::eax_set_rolloff_factor() +{ + eax_al_source_f( + AL_ROLLOFF_FACTOR, + eax_.source.flRolloffFactor + ); +} + +void ALsource::eax_set_room_rolloff_factor() +{ + eax_al_source_f( + AL_ROOM_ROLLOFF_FACTOR, + eax_.source.flRoomRolloffFactor + ); +} + +void ALsource::eax_set_air_absorption_factor() +{ + const auto air_absorption_factor = + eax_al_context_->eax_get_air_absorption_factor() * eax_.source.flAirAbsorptionFactor; + + eax_al_source_f( + AL_AIR_ABSORPTION_FACTOR, + air_absorption_factor + ); +} + +void ALsource::eax_set_direct_hf_auto_flag() +{ + const auto is_enable = (eax_.source.ulFlags & EAXSOURCEFLAGS_DIRECTHFAUTO) != 0; + const auto al_value = static_cast(is_enable ? AL_TRUE : AL_FALSE); + + eax_al_source_i( + AL_DIRECT_FILTER_GAINHF_AUTO, + al_value + ); +} + +void ALsource::eax_set_room_auto_flag() +{ + const auto is_enable = (eax_.source.ulFlags & EAXSOURCEFLAGS_ROOMAUTO) != 0; + const auto al_value = static_cast(is_enable ? AL_TRUE : AL_FALSE); + + eax_al_source_i( + AL_AUXILIARY_SEND_FILTER_GAIN_AUTO, + al_value + ); +} + +void ALsource::eax_set_room_hf_auto_flag() +{ + const auto is_enable = (eax_.source.ulFlags & EAXSOURCEFLAGS_ROOMHFAUTO) != 0; + const auto al_value = static_cast(is_enable ? AL_TRUE : AL_FALSE); + + eax_al_source_i( + AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO, + al_value + ); +} + +void ALsource::eax_set_flags() +{ + eax_set_direct_hf_auto_flag(); + eax_set_room_auto_flag(); + eax_set_room_hf_auto_flag(); + eax_set_speaker_levels(); +} + +void ALsource::eax_set_macro_fx_factor() +{ + // TODO +} + +void ALsource::eax_set_speaker_levels() +{ + // TODO +} + +void ALsource::eax_apply_deferred() +{ + if (!eax_are_active_fx_slots_dirty_ && + eax_sends_dirty_flags_ == EaxSourceSendsDirtyFlags{} && + eax_source_dirty_filter_flags_ == EaxSourceSourceFilterDirtyFlags{} && + eax_source_dirty_misc_flags_ == EaxSourceSourceMiscDirtyFlags{}) + { + return; + } + + eax_ = eax_d_; + + if (eax_are_active_fx_slots_dirty_) + { + eax_are_active_fx_slots_dirty_ = false; + eax_set_fx_slots(); + eax_update_filters_internal(); + } + else if (eax_has_active_fx_slots_) + { + if (eax_source_dirty_filter_flags_ != EaxSourceSourceFilterDirtyFlags{}) + { + eax_update_filters_internal(); + } + else if (eax_sends_dirty_flags_ != EaxSourceSendsDirtyFlags{}) + { + for (auto i = std::size_t{}; i < EAX_MAX_FXSLOTS; ++i) + { + if (eax_active_fx_slots_[i]) + { + if (eax_sends_dirty_flags_.sends[i] != EaxSourceSendDirtyFlags{}) + { + eax_update_filters_internal(); + break; + } + } + } + } + } + + if (eax_source_dirty_misc_flags_ != EaxSourceSourceMiscDirtyFlags{}) + { + if (eax_source_dirty_misc_flags_.lOutsideVolumeHF) + { + eax_set_outside_volume_hf(); + } + + if (eax_source_dirty_misc_flags_.flDopplerFactor) + { + eax_set_doppler_factor(); + } + + if (eax_source_dirty_misc_flags_.flRolloffFactor) + { + eax_set_rolloff_factor(); + } + + if (eax_source_dirty_misc_flags_.flRoomRolloffFactor) + { + eax_set_room_rolloff_factor(); + } + + if (eax_source_dirty_misc_flags_.flAirAbsorptionFactor) + { + eax_set_air_absorption_factor(); + } + + if (eax_source_dirty_misc_flags_.ulFlags) + { + eax_set_flags(); + } + + if (eax_source_dirty_misc_flags_.flMacroFXFactor) + { + eax_set_macro_fx_factor(); + } + + eax_source_dirty_misc_flags_ = EaxSourceSourceMiscDirtyFlags{}; + } + + eax_sends_dirty_flags_ = EaxSourceSendsDirtyFlags{}; + eax_source_dirty_filter_flags_ = EaxSourceSourceFilterDirtyFlags{}; +} + +void ALsource::eax_set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXSOURCE_NONE: + break; + + case EAXSOURCE_ALLPARAMETERS: + eax_defer_source_all(eax_call); + break; + + case EAXSOURCE_OBSTRUCTIONPARAMETERS: + eax_defer_source_obstruction_all(eax_call); + break; + + case EAXSOURCE_OCCLUSIONPARAMETERS: + eax_defer_source_occlusion_all(eax_call); + break; + + case EAXSOURCE_EXCLUSIONPARAMETERS: + eax_defer_source_exclusion_all(eax_call); + break; + + case EAXSOURCE_DIRECT: + eax_defer_source_direct(eax_call); + break; + + case EAXSOURCE_DIRECTHF: + eax_defer_source_direct_hf(eax_call); + break; + + case EAXSOURCE_ROOM: + eax_defer_source_room(eax_call); + break; + + case EAXSOURCE_ROOMHF: + eax_defer_source_room_hf(eax_call); + break; + + case EAXSOURCE_OBSTRUCTION: + eax_defer_source_obstruction(eax_call); + break; + + case EAXSOURCE_OBSTRUCTIONLFRATIO: + eax_defer_source_obstruction_lf_ratio(eax_call); + break; + + case EAXSOURCE_OCCLUSION: + eax_defer_source_occlusion(eax_call); + break; + + case EAXSOURCE_OCCLUSIONLFRATIO: + eax_defer_source_occlusion_lf_ratio(eax_call); + break; + + case EAXSOURCE_OCCLUSIONROOMRATIO: + eax_defer_source_occlusion_room_ratio(eax_call); + break; + + case EAXSOURCE_OCCLUSIONDIRECTRATIO: + eax_defer_source_occlusion_direct_ratio(eax_call); + break; + + case EAXSOURCE_EXCLUSION: + eax_defer_source_exclusion(eax_call); + break; + + case EAXSOURCE_EXCLUSIONLFRATIO: + eax_defer_source_exclusion_lf_ratio(eax_call); + break; + + case EAXSOURCE_OUTSIDEVOLUMEHF: + eax_defer_source_outside_volume_hf(eax_call); + break; + + case EAXSOURCE_DOPPLERFACTOR: + eax_defer_source_doppler_factor(eax_call); + break; + + case EAXSOURCE_ROLLOFFFACTOR: + eax_defer_source_rolloff_factor(eax_call); + break; + + case EAXSOURCE_ROOMROLLOFFFACTOR: + eax_defer_source_room_rolloff_factor(eax_call); + break; + + case EAXSOURCE_AIRABSORPTIONFACTOR: + eax_defer_source_air_absorption_factor(eax_call); + break; + + case EAXSOURCE_FLAGS: + eax_defer_source_flags(eax_call); + break; + + case EAXSOURCE_SENDPARAMETERS: + eax_defer_send(eax_call); + break; + + case EAXSOURCE_ALLSENDPARAMETERS: + eax_defer_send_all(eax_call); + break; + + case EAXSOURCE_OCCLUSIONSENDPARAMETERS: + eax_defer_send_occlusion_all(eax_call); + break; + + case EAXSOURCE_EXCLUSIONSENDPARAMETERS: + eax_defer_send_exclusion_all(eax_call); + break; + + case EAXSOURCE_ACTIVEFXSLOTID: + eax_defer_active_fx_slots(eax_call); + break; + + case EAXSOURCE_MACROFXFACTOR: + eax_defer_source_macro_fx_factor(eax_call); + break; + + case EAXSOURCE_SPEAKERLEVELS: + eax_defer_source_speaker_level_all(eax_call); + break; + + case EAXSOURCE_ALL2DPARAMETERS: + eax_defer_source_2d_all(eax_call); + break; + + default: + eax_fail("Unsupported property id."); + } + + if (!eax_call.is_deferred()) + { + eax_apply_deferred(); + } +} + +const GUID& ALsource::eax_get_send_fx_slot_guid( + int eax_version, + EaxFxSlotIndexValue fx_slot_index) +{ + switch (eax_version) + { + case 4: + switch (fx_slot_index) + { + case 0: + return EAXPROPERTYID_EAX40_FXSlot0; + + case 1: + return EAXPROPERTYID_EAX40_FXSlot1; + + case 2: + return EAXPROPERTYID_EAX40_FXSlot2; + + case 3: + return EAXPROPERTYID_EAX40_FXSlot3; + + default: + eax_fail("FX slot index out of range."); + } + + case 5: + switch (fx_slot_index) + { + case 0: + return EAXPROPERTYID_EAX50_FXSlot0; + + case 1: + return EAXPROPERTYID_EAX50_FXSlot1; + + case 2: + return EAXPROPERTYID_EAX50_FXSlot2; + + case 3: + return EAXPROPERTYID_EAX50_FXSlot3; + + default: + eax_fail("FX slot index out of range."); + } + + default: + eax_fail("Unsupported EAX version."); + } +} + +void ALsource::eax_copy_send( + const EAXSOURCEALLSENDPROPERTIES& src_send, + EAXSOURCESENDPROPERTIES& dst_send) +{ + dst_send.lSend = src_send.lSend; + dst_send.lSendHF = src_send.lSendHF; +} + +void ALsource::eax_copy_send( + const EAXSOURCEALLSENDPROPERTIES& src_send, + EAXSOURCEALLSENDPROPERTIES& dst_send) +{ + dst_send = src_send; +} + +void ALsource::eax_copy_send( + const EAXSOURCEALLSENDPROPERTIES& src_send, + EAXSOURCEOCCLUSIONSENDPROPERTIES& dst_send) +{ + dst_send.lOcclusion = src_send.lOcclusion; + dst_send.flOcclusionLFRatio = src_send.flOcclusionLFRatio; + dst_send.flOcclusionRoomRatio = src_send.flOcclusionRoomRatio; + dst_send.flOcclusionDirectRatio = src_send.flOcclusionDirectRatio; +} + +void ALsource::eax_copy_send( + const EAXSOURCEALLSENDPROPERTIES& src_send, + EAXSOURCEEXCLUSIONSENDPROPERTIES& dst_send) +{ + dst_send.lExclusion = src_send.lExclusion; + dst_send.flExclusionLFRatio = src_send.flExclusionLFRatio; +} + +void ALsource::eax_api_get_source_all_v2( + const EaxEaxCall& eax_call) +{ + auto eax_2_all = EAX20BUFFERPROPERTIES{}; + eax_2_all.lDirect = eax_.source.lDirect; + eax_2_all.lDirectHF = eax_.source.lDirectHF; + eax_2_all.lRoom = eax_.source.lRoom; + eax_2_all.lRoomHF = eax_.source.lRoomHF; + eax_2_all.flRoomRolloffFactor = eax_.source.flRoomRolloffFactor; + eax_2_all.lObstruction = eax_.source.lObstruction; + eax_2_all.flObstructionLFRatio = eax_.source.flObstructionLFRatio; + eax_2_all.lOcclusion = eax_.source.lOcclusion; + eax_2_all.flOcclusionLFRatio = eax_.source.flOcclusionLFRatio; + eax_2_all.flOcclusionRoomRatio = eax_.source.flOcclusionRoomRatio; + eax_2_all.lOutsideVolumeHF = eax_.source.lOutsideVolumeHF; + eax_2_all.flAirAbsorptionFactor = eax_.source.flAirAbsorptionFactor; + eax_2_all.dwFlags = eax_.source.ulFlags; + + eax_call.set_value(eax_2_all); +} + +void ALsource::eax_api_get_source_all_v3( + const EaxEaxCall& eax_call) +{ + eax_call.set_value(static_cast(eax_.source)); +} + +void ALsource::eax_api_get_source_all_v5( + const EaxEaxCall& eax_call) +{ + eax_call.set_value(eax_.source); +} + +void ALsource::eax_api_get_source_all( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_version()) + { + case 2: + eax_api_get_source_all_v2(eax_call); + break; + + case 3: + case 4: + eax_api_get_source_all_v3(eax_call); + break; + + case 5: + eax_api_get_source_all_v5(eax_call); + break; + + default: + eax_fail("Unsupported EAX version."); + } +} + +void ALsource::eax_api_get_source_all_obstruction( + const EaxEaxCall& eax_call) +{ + static_assert( + offsetof(EAXOBSTRUCTIONPROPERTIES, flObstructionLFRatio) - offsetof(EAXOBSTRUCTIONPROPERTIES, lObstruction) == + offsetof(EAX30SOURCEPROPERTIES, flObstructionLFRatio) - offsetof(EAX30SOURCEPROPERTIES, lObstruction), + "Type size." + ); + + const auto eax_obstruction_all = *reinterpret_cast(&eax_.source.lObstruction); + + eax_call.set_value(eax_obstruction_all); +} + +void ALsource::eax_api_get_source_all_occlusion( + const EaxEaxCall& eax_call) +{ + static_assert( + offsetof(EAXOCCLUSIONPROPERTIES, flOcclusionLFRatio) - offsetof(EAXOCCLUSIONPROPERTIES, lOcclusion) == + offsetof(EAX30SOURCEPROPERTIES, flOcclusionLFRatio) - offsetof(EAX30SOURCEPROPERTIES, lOcclusion) && + + offsetof(EAXOCCLUSIONPROPERTIES, flOcclusionRoomRatio) - offsetof(EAXOCCLUSIONPROPERTIES, lOcclusion) == + offsetof(EAX30SOURCEPROPERTIES, flOcclusionRoomRatio) - offsetof(EAX30SOURCEPROPERTIES, lOcclusion) && + + offsetof(EAXOCCLUSIONPROPERTIES, flOcclusionDirectRatio) - offsetof(EAXOCCLUSIONPROPERTIES, lOcclusion) == + offsetof(EAX30SOURCEPROPERTIES, flOcclusionDirectRatio) - offsetof(EAX30SOURCEPROPERTIES, lOcclusion), + + "Type size." + ); + + const auto eax_occlusion_all = *reinterpret_cast(&eax_.source.lOcclusion); + + eax_call.set_value(eax_occlusion_all); +} + +void ALsource::eax_api_get_source_all_exclusion( + const EaxEaxCall& eax_call) +{ + static_assert( + offsetof(EAXEXCLUSIONPROPERTIES, flExclusionLFRatio) - offsetof(EAXEXCLUSIONPROPERTIES, lExclusion) == + offsetof(EAX30SOURCEPROPERTIES, flExclusionLFRatio) - offsetof(EAX30SOURCEPROPERTIES, lExclusion), + + "Type size." + ); + + const auto eax_exclusion_all = *reinterpret_cast(&eax_.source.lExclusion); + + eax_call.set_value(eax_exclusion_all); +} + +void ALsource::eax_api_get_source_active_fx_slot_id( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_version()) + { + case 4: + { + const auto& active_fx_slots = reinterpret_cast(eax_.active_fx_slots); + eax_call.set_value(active_fx_slots); + } + break; + + case 5: + { + const auto& active_fx_slots = reinterpret_cast(eax_.active_fx_slots); + eax_call.set_value(active_fx_slots); + } + break; + + default: + eax_fail("Unsupported EAX version."); + } +} + +void ALsource::eax_api_get_source_all_2d( + const EaxEaxCall& eax_call) +{ + auto eax_2d_all = EAXSOURCE2DPROPERTIES{}; + eax_2d_all.lDirect = eax_.source.lDirect; + eax_2d_all.lDirectHF = eax_.source.lDirectHF; + eax_2d_all.lRoom = eax_.source.lRoom; + eax_2d_all.lRoomHF = eax_.source.lRoomHF; + eax_2d_all.ulFlags = eax_.source.ulFlags; + + eax_call.set_value(eax_2d_all); +} + +void ALsource::eax_api_get_source_speaker_level_all( + const EaxEaxCall& eax_call) +{ + auto& all = eax_call.get_value(); + + eax_validate_source_speaker_id(all.lSpeakerID); + const auto speaker_index = static_cast(all.lSpeakerID - 1); + all.lLevel = eax_.speaker_levels[speaker_index]; +} + +void ALsource::eax_get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXSOURCE_NONE: + break; + + case EAXSOURCE_ALLPARAMETERS: + eax_api_get_source_all(eax_call); + break; + + case EAXSOURCE_OBSTRUCTIONPARAMETERS: + eax_api_get_source_all_obstruction(eax_call); + break; + + case EAXSOURCE_OCCLUSIONPARAMETERS: + eax_api_get_source_all_occlusion(eax_call); + break; + + case EAXSOURCE_EXCLUSIONPARAMETERS: + eax_api_get_source_all_exclusion(eax_call); + break; + + case EAXSOURCE_DIRECT: + eax_call.set_value(eax_.source.lDirect); + break; + + case EAXSOURCE_DIRECTHF: + eax_call.set_value(eax_.source.lDirectHF); + break; + + case EAXSOURCE_ROOM: + eax_call.set_value(eax_.source.lRoom); + break; + + case EAXSOURCE_ROOMHF: + eax_call.set_value(eax_.source.lRoomHF); + break; + + case EAXSOURCE_OBSTRUCTION: + eax_call.set_value(eax_.source.lObstruction); + break; + + case EAXSOURCE_OBSTRUCTIONLFRATIO: + eax_call.set_value(eax_.source.flObstructionLFRatio); + break; + + case EAXSOURCE_OCCLUSION: + eax_call.set_value(eax_.source.lOcclusion); + break; + + case EAXSOURCE_OCCLUSIONLFRATIO: + eax_call.set_value(eax_.source.flOcclusionLFRatio); + break; + + case EAXSOURCE_OCCLUSIONROOMRATIO: + eax_call.set_value(eax_.source.flOcclusionRoomRatio); + break; + + case EAXSOURCE_OCCLUSIONDIRECTRATIO: + eax_call.set_value(eax_.source.flOcclusionDirectRatio); + break; + + case EAXSOURCE_EXCLUSION: + eax_call.set_value(eax_.source.lExclusion); + break; + + case EAXSOURCE_EXCLUSIONLFRATIO: + eax_call.set_value(eax_.source.flExclusionLFRatio); + break; + + case EAXSOURCE_OUTSIDEVOLUMEHF: + eax_call.set_value(eax_.source.lOutsideVolumeHF); + break; + + case EAXSOURCE_DOPPLERFACTOR: + eax_call.set_value(eax_.source.flDopplerFactor); + break; + + case EAXSOURCE_ROLLOFFFACTOR: + eax_call.set_value(eax_.source.flRolloffFactor); + break; + + case EAXSOURCE_ROOMROLLOFFFACTOR: + eax_call.set_value(eax_.source.flRoomRolloffFactor); + break; + + case EAXSOURCE_AIRABSORPTIONFACTOR: + eax_call.set_value(eax_.source.flAirAbsorptionFactor); + break; + + case EAXSOURCE_FLAGS: + eax_call.set_value(eax_.source.ulFlags); + break; + + case EAXSOURCE_SENDPARAMETERS: + eax_api_get_send_properties(eax_call); + break; + + case EAXSOURCE_ALLSENDPARAMETERS: + eax_api_get_send_properties(eax_call); + break; + + case EAXSOURCE_OCCLUSIONSENDPARAMETERS: + eax_api_get_send_properties(eax_call); + break; + + case EAXSOURCE_EXCLUSIONSENDPARAMETERS: + eax_api_get_send_properties(eax_call); + break; + + case EAXSOURCE_ACTIVEFXSLOTID: + eax_api_get_source_active_fx_slot_id(eax_call); + break; + + case EAXSOURCE_MACROFXFACTOR: + eax_call.set_value(eax_.source.flMacroFXFactor); + break; + + case EAXSOURCE_SPEAKERLEVELS: + eax_api_get_source_speaker_level_all(eax_call); + break; + + case EAXSOURCE_ALL2DPARAMETERS: + eax_api_get_source_all_2d(eax_call); + break; + + default: + eax_fail("Unsupported property id."); + } +} + +void ALsource::eax_al_source_i( + ALenum param, + ALint value) +{ + SetSourceiv(this, eax_al_context_, static_cast(param), {&value, 1}); +} + +void ALsource::eax_al_source_f( + ALenum param, + ALfloat value) +{ + SetSourcefv(this, eax_al_context_, static_cast(param), {&value, 1}); +} + +void ALsource::eax_al_source_3i( + ALenum param, + ALint value1, + ALint value2, + ALint value3) +{ + const ALint values[3] = {value1, value2, value3}; + SetSourceiv(this, eax_al_context_, static_cast(param), values); +} + + +#endif // ALSOFT_EAX diff --git a/al/source.h b/al/source.h index 25ece822..41cd6187 100644 --- a/al/source.h +++ b/al/source.h @@ -21,6 +21,12 @@ #include "core/voice.h" #include "vector.h" +#if ALSOFT_EAX +#include "eax_eax_call.h" +#include "eax_fx_slot_index.h" +#include "eax_utils.h" +#endif // ALSOFT_EAX + struct ALbuffer; struct ALeffectslot; @@ -41,6 +47,77 @@ struct ALbufferQueueItem : public VoiceBufferItem { }; +#if ALSOFT_EAX +struct EaxSourceInitParam +{ + ALCcontext* al_context{}; + ALfilter* al_filter{}; +}; // EaxSourceInitParam + + +using EaxSourceSourceFilterDirtyFlagsValue = std::uint_least16_t; + +struct EaxSourceSourceFilterDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxSourceSourceFilterDirtyFlagsValue lDirect : 1; + EaxSourceSourceFilterDirtyFlagsValue lDirectHF : 1; + EaxSourceSourceFilterDirtyFlagsValue lRoom : 1; + EaxSourceSourceFilterDirtyFlagsValue lRoomHF : 1; + EaxSourceSourceFilterDirtyFlagsValue lObstruction : 1; + EaxSourceSourceFilterDirtyFlagsValue flObstructionLFRatio : 1; + EaxSourceSourceFilterDirtyFlagsValue lOcclusion : 1; + EaxSourceSourceFilterDirtyFlagsValue flOcclusionLFRatio : 1; + EaxSourceSourceFilterDirtyFlagsValue flOcclusionRoomRatio : 1; + EaxSourceSourceFilterDirtyFlagsValue flOcclusionDirectRatio : 1; + EaxSourceSourceFilterDirtyFlagsValue lExclusion : 1; + EaxSourceSourceFilterDirtyFlagsValue flExclusionLFRatio : 1; +}; // EaxSourceSourceFilterDirtyFlags + + +using EaxSourceSourceMiscDirtyFlagsValue = std::uint_least8_t; + +struct EaxSourceSourceMiscDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxSourceSourceMiscDirtyFlagsValue lOutsideVolumeHF : 1; + EaxSourceSourceMiscDirtyFlagsValue flDopplerFactor : 1; + EaxSourceSourceMiscDirtyFlagsValue flRolloffFactor : 1; + EaxSourceSourceMiscDirtyFlagsValue flRoomRolloffFactor : 1; + EaxSourceSourceMiscDirtyFlagsValue flAirAbsorptionFactor : 1; + EaxSourceSourceMiscDirtyFlagsValue ulFlags : 1; + EaxSourceSourceMiscDirtyFlagsValue flMacroFXFactor : 1; + EaxSourceSourceMiscDirtyFlagsValue speaker_levels : 1; +}; // EaxSourceSourceMiscDirtyFlags + + +using EaxSourceSendDirtyFlagsValue = std::uint_least8_t; + +struct EaxSourceSendDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxSourceSendDirtyFlagsValue lSend : 1; + EaxSourceSendDirtyFlagsValue lSendHF : 1; + EaxSourceSendDirtyFlagsValue lOcclusion : 1; + EaxSourceSendDirtyFlagsValue flOcclusionLFRatio : 1; + EaxSourceSendDirtyFlagsValue flOcclusionRoomRatio : 1; + EaxSourceSendDirtyFlagsValue flOcclusionDirectRatio : 1; + EaxSourceSendDirtyFlagsValue lExclusion : 1; + EaxSourceSendDirtyFlagsValue flExclusionLFRatio : 1; +}; // EaxSourceSendDirtyFlags + + +struct EaxSourceSendsDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxSourceSendDirtyFlags sends[EAX_MAX_FXSLOTS]; +}; // EaxSourceSendsDirtyFlags +#endif // ALSOFT_EAX + struct ALsource { /** Source properties. */ float Pitch{1.0f}; @@ -135,6 +212,618 @@ struct ALsource { ALsource& operator=(const ALsource&) = delete; DISABLE_ALLOC() + +#if ALSOFT_EAX +public: + void eax_initialize( + const EaxSourceInitParam& param) noexcept; + + void eax_uninitialize(); + + + void eax_dispatch( + const EaxEaxCall& eax_call); + + + void eax_update_filters(); + + void eax_update( + EaxContextSharedDirtyFlags dirty_flags); + + + static ALsource* eax_lookup_source( + ALCcontext& al_context, + ALuint source_id) noexcept; + + +private: + static constexpr auto eax_max_speakers = 9; + + + using EaxActiveFxSlots = std::array; + using EaxSpeakerLevels = std::array; + + + struct Eax + { + using Sends = std::array; + + EAX50ACTIVEFXSLOTS active_fx_slots{}; + EAX50SOURCEPROPERTIES source{}; + Sends sends{}; + EaxSpeakerLevels speaker_levels{}; + }; // Eax + + + bool eax_uses_primary_id_{}; + bool eax_has_active_fx_slots_{}; + bool eax_are_active_fx_slots_dirty_{}; + + ALCcontext* eax_al_context_{}; + ALfilter* eax_al_filter_{}; + + Eax eax_{}; + Eax eax_d_{}; + EaxActiveFxSlots eax_active_fx_slots_{}; + + EaxSourceSendsDirtyFlags eax_sends_dirty_flags_{}; + EaxSourceSourceFilterDirtyFlags eax_source_dirty_filter_flags_{}; + EaxSourceSourceMiscDirtyFlags eax_source_dirty_misc_flags_{}; + + + [[noreturn]] + static void eax_fail( + const char* message); + + + static void eax_validate_init_param( + const EaxSourceInitParam& param); + + void eax_copy_init_param( + const EaxSourceInitParam& param); + + + void eax_set_source_defaults(); + + void eax_set_active_fx_slots_defaults(); + + void eax_set_send_defaults( + EAXSOURCEALLSENDPROPERTIES& eax_send); + + void eax_set_sends_defaults(); + + void eax_set_speaker_levels_defaults(); + + void eax_set_defaults(); + + + static float eax_calculate_dst_occlusion_mb( + long src_occlusion_mb, + float path_ratio, + float lf_ratio) noexcept; + + EaxAlLowPassParam eax_create_direct_filter_param() const noexcept; + + EaxAlLowPassParam eax_create_room_filter_param( + const ALeffectslot& fx_slot, + const EAXSOURCEALLSENDPROPERTIES& send) const noexcept; + + void eax_set_al_filter_parameters( + const EaxAlLowPassParam& al_low_pass_param) const noexcept; + + void eax_set_fx_slots(); + + void eax_initialize_fx_slots(); + + void eax_update_direct_filter_internal(); + + void eax_update_room_filters_internal(); + + void eax_update_filters_internal(); + + void eax_update_primary_fx_slot_id(); + + + void eax_defer_active_fx_slots( + const EaxEaxCall& eax_call); + + + static const char* eax_get_exclusion_name() noexcept; + + static const char* eax_get_exclusion_lf_ratio_name() noexcept; + + + static const char* eax_get_occlusion_name() noexcept; + + static const char* eax_get_occlusion_lf_ratio_name() noexcept; + + static const char* eax_get_occlusion_direct_ratio_name() noexcept; + + static const char* eax_get_occlusion_room_ratio_name() noexcept; + + + static void eax_validate_send_receiving_fx_slot_guid( + const GUID& guidReceivingFXSlotID); + + static void eax_validate_send_send( + long lSend); + + static void eax_validate_send_send_hf( + long lSendHF); + + static void eax_validate_send_occlusion( + long lOcclusion); + + static void eax_validate_send_occlusion_lf_ratio( + float flOcclusionLFRatio); + + static void eax_validate_send_occlusion_room_ratio( + float flOcclusionRoomRatio); + + static void eax_validate_send_occlusion_direct_ratio( + float flOcclusionDirectRatio); + + static void eax_validate_send_exclusion( + long lExclusion); + + static void eax_validate_send_exclusion_lf_ratio( + float flExclusionLFRatio); + + static void eax_validate_send( + const EAXSOURCESENDPROPERTIES& all); + + static void eax_validate_send_exclusion_all( + const EAXSOURCEEXCLUSIONSENDPROPERTIES& all); + + static void eax_validate_send_occlusion_all( + const EAXSOURCEOCCLUSIONSENDPROPERTIES& all); + + static void eax_validate_send_all( + const EAXSOURCEALLSENDPROPERTIES& all); + + + static EaxFxSlotIndexValue eax_get_send_index( + const GUID& send_guid); + + + void eax_defer_send_send( + long lSend, + EaxFxSlotIndexValue index); + + void eax_defer_send_send_hf( + long lSendHF, + EaxFxSlotIndexValue index); + + void eax_defer_send_occlusion( + long lOcclusion, + EaxFxSlotIndexValue index); + + void eax_defer_send_occlusion_lf_ratio( + float flOcclusionLFRatio, + EaxFxSlotIndexValue index); + + void eax_defer_send_occlusion_room_ratio( + float flOcclusionRoomRatio, + EaxFxSlotIndexValue index); + + void eax_defer_send_occlusion_direct_ratio( + float flOcclusionDirectRatio, + EaxFxSlotIndexValue index); + + void eax_defer_send_exclusion( + long lExclusion, + EaxFxSlotIndexValue index); + + void eax_defer_send_exclusion_lf_ratio( + float flExclusionLFRatio, + EaxFxSlotIndexValue index); + + void eax_defer_send( + const EAXSOURCESENDPROPERTIES& all, + EaxFxSlotIndexValue index); + + void eax_defer_send_exclusion_all( + const EAXSOURCEEXCLUSIONSENDPROPERTIES& all, + EaxFxSlotIndexValue index); + + void eax_defer_send_occlusion_all( + const EAXSOURCEOCCLUSIONSENDPROPERTIES& all, + EaxFxSlotIndexValue index); + + void eax_defer_send_all( + const EAXSOURCEALLSENDPROPERTIES& all, + EaxFxSlotIndexValue index); + + + void eax_defer_send( + const EaxEaxCall& eax_call); + + void eax_defer_send_exclusion_all( + const EaxEaxCall& eax_call); + + void eax_defer_send_occlusion_all( + const EaxEaxCall& eax_call); + + void eax_defer_send_all( + const EaxEaxCall& eax_call); + + + static void eax_validate_source_direct( + long direct); + + static void eax_validate_source_direct_hf( + long direct_hf); + + static void eax_validate_source_room( + long room); + + static void eax_validate_source_room_hf( + long room_hf); + + static void eax_validate_source_obstruction( + long obstruction); + + static void eax_validate_source_obstruction_lf_ratio( + float obstruction_lf_ratio); + + static void eax_validate_source_occlusion( + long occlusion); + + static void eax_validate_source_occlusion_lf_ratio( + float occlusion_lf_ratio); + + static void eax_validate_source_occlusion_room_ratio( + float occlusion_room_ratio); + + static void eax_validate_source_occlusion_direct_ratio( + float occlusion_direct_ratio); + + static void eax_validate_source_exclusion( + long exclusion); + + static void eax_validate_source_exclusion_lf_ratio( + float exclusion_lf_ratio); + + static void eax_validate_source_outside_volume_hf( + long outside_volume_hf); + + static void eax_validate_source_doppler_factor( + float doppler_factor); + + static void eax_validate_source_rolloff_factor( + float rolloff_factor); + + static void eax_validate_source_room_rolloff_factor( + float room_rolloff_factor); + + static void eax_validate_source_air_absorption_factor( + float air_absorption_factor); + + static void eax_validate_source_flags( + unsigned long flags, + int eax_version); + + static void eax_validate_source_macro_fx_factor( + float macro_fx_factor); + + static void eax_validate_source_2d_all( + const EAXSOURCE2DPROPERTIES& all, + int eax_version); + + static void eax_validate_source_obstruction_all( + const EAXOBSTRUCTIONPROPERTIES& all); + + static void eax_validate_source_exclusion_all( + const EAXEXCLUSIONPROPERTIES& all); + + static void eax_validate_source_occlusion_all( + const EAXOCCLUSIONPROPERTIES& all); + + static void eax_validate_source_all( + const EAX20BUFFERPROPERTIES& all, + int eax_version); + + static void eax_validate_source_all( + const EAX30SOURCEPROPERTIES& all, + int eax_version); + + static void eax_validate_source_all( + const EAX50SOURCEPROPERTIES& all, + int eax_version); + + static void eax_validate_source_speaker_id( + long speaker_id); + + static void eax_validate_source_speaker_level( + long speaker_level); + + static void eax_validate_source_speaker_level_all( + const EAXSPEAKERLEVELPROPERTIES& all); + + + void eax_defer_source_direct( + long lDirect); + + void eax_defer_source_direct_hf( + long lDirectHF); + + void eax_defer_source_room( + long lRoom); + + void eax_defer_source_room_hf( + long lRoomHF); + + void eax_defer_source_obstruction( + long lObstruction); + + void eax_defer_source_obstruction_lf_ratio( + float flObstructionLFRatio); + + void eax_defer_source_occlusion( + long lOcclusion); + + void eax_defer_source_occlusion_lf_ratio( + float flOcclusionLFRatio); + + void eax_defer_source_occlusion_room_ratio( + float flOcclusionRoomRatio); + + void eax_defer_source_occlusion_direct_ratio( + float flOcclusionDirectRatio); + + void eax_defer_source_exclusion( + long lExclusion); + + void eax_defer_source_exclusion_lf_ratio( + float flExclusionLFRatio); + + void eax_defer_source_outside_volume_hf( + long lOutsideVolumeHF); + + void eax_defer_source_doppler_factor( + float flDopplerFactor); + + void eax_defer_source_rolloff_factor( + float flRolloffFactor); + + void eax_defer_source_room_rolloff_factor( + float flRoomRolloffFactor); + + void eax_defer_source_air_absorption_factor( + float flAirAbsorptionFactor); + + void eax_defer_source_flags( + unsigned long ulFlags); + + void eax_defer_source_macro_fx_factor( + float flMacroFXFactor); + + void eax_defer_source_2d_all( + const EAXSOURCE2DPROPERTIES& all); + + void eax_defer_source_obstruction_all( + const EAXOBSTRUCTIONPROPERTIES& all); + + void eax_defer_source_exclusion_all( + const EAXEXCLUSIONPROPERTIES& all); + + void eax_defer_source_occlusion_all( + const EAXOCCLUSIONPROPERTIES& all); + + void eax_defer_source_all( + const EAX20BUFFERPROPERTIES& all); + + void eax_defer_source_all( + const EAX30SOURCEPROPERTIES& all); + + void eax_defer_source_all( + const EAX50SOURCEPROPERTIES& all); + + void eax_defer_source_speaker_level_all( + const EAXSPEAKERLEVELPROPERTIES& all); + + + void eax_defer_source_direct( + const EaxEaxCall& eax_call); + + void eax_defer_source_direct_hf( + const EaxEaxCall& eax_call); + + void eax_defer_source_room( + const EaxEaxCall& eax_call); + + void eax_defer_source_room_hf( + const EaxEaxCall& eax_call); + + void eax_defer_source_obstruction( + const EaxEaxCall& eax_call); + + void eax_defer_source_obstruction_lf_ratio( + const EaxEaxCall& eax_call); + + void eax_defer_source_occlusion( + const EaxEaxCall& eax_call); + + void eax_defer_source_occlusion_lf_ratio( + const EaxEaxCall& eax_call); + + void eax_defer_source_occlusion_room_ratio( + const EaxEaxCall& eax_call); + + void eax_defer_source_occlusion_direct_ratio( + const EaxEaxCall& eax_call); + + void eax_defer_source_exclusion( + const EaxEaxCall& eax_call); + + void eax_defer_source_exclusion_lf_ratio( + const EaxEaxCall& eax_call); + + void eax_defer_source_outside_volume_hf( + const EaxEaxCall& eax_call); + + void eax_defer_source_doppler_factor( + const EaxEaxCall& eax_call); + + void eax_defer_source_rolloff_factor( + const EaxEaxCall& eax_call); + + void eax_defer_source_room_rolloff_factor( + const EaxEaxCall& eax_call); + + void eax_defer_source_air_absorption_factor( + const EaxEaxCall& eax_call); + + void eax_defer_source_flags( + const EaxEaxCall& eax_call); + + void eax_defer_source_macro_fx_factor( + const EaxEaxCall& eax_call); + + void eax_defer_source_2d_all( + const EaxEaxCall& eax_call); + + void eax_defer_source_obstruction_all( + const EaxEaxCall& eax_call); + + void eax_defer_source_exclusion_all( + const EaxEaxCall& eax_call); + + void eax_defer_source_occlusion_all( + const EaxEaxCall& eax_call); + + void eax_defer_source_all( + const EaxEaxCall& eax_call); + + void eax_defer_source_speaker_level_all( + const EaxEaxCall& eax_call); + + + void eax_set_outside_volume_hf(); + + void eax_set_doppler_factor(); + + void eax_set_rolloff_factor(); + + void eax_set_room_rolloff_factor(); + + void eax_set_air_absorption_factor(); + + + void eax_set_direct_hf_auto_flag(); + + void eax_set_room_auto_flag(); + + void eax_set_room_hf_auto_flag(); + + void eax_set_flags(); + + + void eax_set_macro_fx_factor(); + + void eax_set_speaker_levels(); + + + void eax_apply_deferred(); + + void eax_set( + const EaxEaxCall& eax_call); + + + static const GUID& eax_get_send_fx_slot_guid( + int eax_version, + EaxFxSlotIndexValue fx_slot_index); + + static void eax_copy_send( + const EAXSOURCEALLSENDPROPERTIES& src_send, + EAXSOURCESENDPROPERTIES& dst_send); + + static void eax_copy_send( + const EAXSOURCEALLSENDPROPERTIES& src_send, + EAXSOURCEALLSENDPROPERTIES& dst_send); + + static void eax_copy_send( + const EAXSOURCEALLSENDPROPERTIES& src_send, + EAXSOURCEOCCLUSIONSENDPROPERTIES& dst_send); + + static void eax_copy_send( + const EAXSOURCEALLSENDPROPERTIES& src_send, + EAXSOURCEEXCLUSIONSENDPROPERTIES& dst_send); + + template< + typename TException, + typename TSrcSend + > + void eax_api_get_send_properties( + const EaxEaxCall& eax_call) const + { + const auto eax_version = eax_call.get_version(); + const auto dst_sends = eax_call.get_values(); + const auto send_count = dst_sends.size(); + + for (auto fx_slot_index = EaxFxSlotIndexValue{}; fx_slot_index < send_count; ++fx_slot_index) + { + auto& dst_send = dst_sends[fx_slot_index]; + const auto& src_send = eax_.sends[fx_slot_index]; + + eax_copy_send(src_send, dst_send); + + dst_send.guidReceivingFXSlotID = eax_get_send_fx_slot_guid(eax_version, fx_slot_index); + } + } + + + void eax_api_get_source_all_v2( + const EaxEaxCall& eax_call); + + void eax_api_get_source_all_v3( + const EaxEaxCall& eax_call); + + void eax_api_get_source_all_v5( + const EaxEaxCall& eax_call); + + void eax_api_get_source_all( + const EaxEaxCall& eax_call); + + void eax_api_get_source_all_obstruction( + const EaxEaxCall& eax_call); + + void eax_api_get_source_all_occlusion( + const EaxEaxCall& eax_call); + + void eax_api_get_source_all_exclusion( + const EaxEaxCall& eax_call); + + void eax_api_get_source_active_fx_slot_id( + const EaxEaxCall& eax_call); + + void eax_api_get_source_all_2d( + const EaxEaxCall& eax_call); + + void eax_api_get_source_speaker_level_all( + const EaxEaxCall& eax_call); + + void eax_get( + const EaxEaxCall& eax_call); + + + // `alSourcei` + void eax_al_source_i( + ALenum param, + ALint value); + + // `alSourcef` + void eax_al_source_f( + ALenum param, + ALfloat value); + + // `alSource3i` + void eax_al_source_3i( + ALenum param, + ALint value1, + ALint value2, + ALint value3); +#endif // ALSOFT_EAX }; void UpdateAllSourceProps(ALCcontext *context); diff --git a/al/state.cpp b/al/state.cpp index 10c7bc74..0ec0e280 100644 --- a/al/state.cpp +++ b/al/state.cpp @@ -46,6 +46,13 @@ #include "opthelpers.h" #include "strutils.h" +#if ALSOFT_EAX +#include "alc/device.h" + +#include "eax_globals.h" +#include "eax_x_ram.h" +#endif // ALSOFT_EAX + namespace { @@ -427,6 +434,41 @@ START_API_FUNC value = static_cast(ResamplerDefault); break; +#if ALSOFT_EAX + +#define EAX_ERROR "[alGetInteger] EAX not enabled." + + case AL_EAX_RAM_SIZE: + if (eax_g_is_enabled) + { + value = eax_x_ram_max_size; + } + else + { + context->setError(AL_INVALID_VALUE, EAX_ERROR); + } + + break; + + case AL_EAX_RAM_FREE: + if (eax_g_is_enabled) + { + auto device = context->mALDevice.get(); + std::lock_guard device_lock{device->BufferLock}; + + value = device->eax_x_ram_free_size; + } + else + { + context->setError(AL_INVALID_VALUE, EAX_ERROR); + } + + break; + +#undef EAX_ERROR + +#endif // ALSOFT_EAX + default: context->setError(AL_INVALID_VALUE, "Invalid integer property 0x%04x", pname); } diff --git a/alc/alc.cpp b/alc/alc.cpp index 04dbcab3..b9f322a3 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -155,6 +155,11 @@ #include "backends/wave.h" #endif +#if ALSOFT_EAX +#include "al/eax_globals.h" +#include "al/eax_x_ram.h" +#endif // ALSOFT_EAX + FILE *gLogFile{stderr}; #ifdef _DEBUG @@ -875,6 +880,14 @@ constexpr struct { DECL(AL_SUPER_STEREO_WIDTH_SOFT), DECL(AL_STOP_SOURCES_ON_DISCONNECT_SOFT), + +#if ALSOFT_EAX + DECL(AL_EAX_RAM_SIZE), + DECL(AL_EAX_RAM_FREE), + DECL(AL_STORAGE_AUTOMATIC), + DECL(AL_STORAGE_HARDWARE), + DECL(AL_STORAGE_ACCESSIBLE), +#endif // ALSOFT_EAX }; #undef DECL @@ -1244,6 +1257,28 @@ void alc_initconfig(void) auto defrevopt = al::getenv("ALSOFT_DEFAULT_REVERB"); if(defrevopt || (defrevopt=ConfigValueStr(nullptr, nullptr, "default-reverb"))) LoadReverbPreset(defrevopt->c_str(), &ALCcontext::sDefaultEffect); + +#if ALSOFT_EAX + { + constexpr auto eax_block_name = "eax"; + + const auto eax_enable_opt = ConfigValueBool(nullptr, eax_block_name, "enable"); + + if (eax_enable_opt) + { + eax_g_is_enabled = *eax_enable_opt; + + if (!eax_g_is_enabled) + { + TRACE("%s\n", "EAX disabled by a configuration."); + } + } + else + { + eax_g_is_enabled = true; + } + } +#endif // ALSOFT_EAX } #define DO_INITCONFIG() std::call_once(alc_config_once, [](){alc_initconfig();}) @@ -2929,6 +2964,7 @@ START_API_FUNC return enm.value; } } + return 0; } END_API_FUNC @@ -3038,6 +3074,7 @@ START_API_FUNC alcSetError(nullptr, ALC_INVALID_CONTEXT); return; } + /* Hold a reference to this context so it remains valid until the ListLock * is released. */ @@ -3186,6 +3223,13 @@ START_API_FUNC device->AuxiliaryEffectSlotMax = 64; device->NumAuxSends = DEFAULT_SENDS; +#if ALSOFT_EAX + if (eax_g_is_enabled) + { + device->NumAuxSends = EAX_MAX_FXSLOTS; + } +#endif // ALSOFT_EAX + try { auto backend = PlaybackFactory->createBackend(device.get(), BackendType::Playback); std::lock_guard _{ListLock}; diff --git a/alc/context.cpp b/alc/context.cpp index c2d3e351..d5fd94b2 100644 --- a/alc/context.cpp +++ b/alc/context.cpp @@ -29,6 +29,14 @@ #include "ringbuffer.h" #include "vecmat.h" +#if ALSOFT_EAX +#include +#include + +#include "alstring.h" +#include "al/eax_exception.h" +#include "al/eax_globals.h" +#endif // ALSOFT_EAX namespace { @@ -121,6 +129,10 @@ ALCcontext::~ALCcontext() mSourceList.clear(); mNumSources = 0; +#if ALSOFT_EAX + eax_uninitialize(); +#endif // ALSOFT_EAX + mDefaultSlot = nullptr; count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), size_t{0u}, [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t @@ -160,6 +172,9 @@ void ALCcontext::init() mExtensionList = alExtList; +#if ALSOFT_EAX + eax_initialize_extensions(); +#endif // ALSOFT_EAX mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f}; mParams.Matrix = alu::Matrix::Identity(); @@ -258,3 +273,1216 @@ void ALCcontext::processUpdates() mHoldUpdates.store(false, std::memory_order_release); } } + +#if ALSOFT_EAX +namespace +{ + + +class ContextException : + public EaxException +{ +public: + explicit ContextException( + const char* message) + : + EaxException{"EAX_CONTEXT", message} + { + } +}; // ContextException + + +} // namespace + + +ALCcontext::SourceListIterator::SourceListIterator( + SourceList& sources, + SourceListIteratorBeginTag) noexcept + : + sub_list_iterator_{sources.begin()}, + sub_list_end_iterator_{sources.end()}, + sub_list_item_index_{} +{ + // Search for first non-free item. + // + while (true) + { + if (sub_list_iterator_ == sub_list_end_iterator_) + { + // End of list. + + sub_list_item_index_ = 0; + return; + } + + if ((~sub_list_iterator_->FreeMask) == 0_u64) + { + // All sub-list's items are free. + + ++sub_list_iterator_; + sub_list_item_index_ = 0; + continue; + } + + if (sub_list_item_index_ >= 64_u64) + { + // Sub-list item's index beyond the last one. + + ++sub_list_iterator_; + sub_list_item_index_ = 0; + continue; + } + + if ((sub_list_iterator_->FreeMask & (1_u64 << sub_list_item_index_)) == 0_u64) + { + // Found non-free item. + + break; + } + + sub_list_item_index_ += 1; + } +} + +ALCcontext::SourceListIterator::SourceListIterator( + SourceList& sources, + SourceListIteratorEndTag) noexcept + : + sub_list_iterator_{sources.end()}, + sub_list_end_iterator_{sources.end()}, + sub_list_item_index_{} +{ +} + +ALCcontext::SourceListIterator::SourceListIterator( + const SourceListIterator& rhs) + : + sub_list_iterator_{rhs.sub_list_iterator_}, + sub_list_end_iterator_{rhs.sub_list_end_iterator_}, + sub_list_item_index_{rhs.sub_list_item_index_} +{ +} + +ALCcontext::SourceListIterator& ALCcontext::SourceListIterator::operator++() +{ + while (true) + { + if (sub_list_iterator_ == sub_list_end_iterator_) + { + // End of list. + + sub_list_item_index_ = 0; + break; + } + + if ((~sub_list_iterator_->FreeMask) == 0_u64) + { + // All sub-list's items are free. + + ++sub_list_iterator_; + sub_list_item_index_ = 0; + continue; + } + + sub_list_item_index_ += 1; + + if (sub_list_item_index_ >= 64_u64) + { + // Sub-list item's index beyond the last one. + + ++sub_list_iterator_; + sub_list_item_index_ = 0; + continue; + } + + if ((sub_list_iterator_->FreeMask & (1_u64 << sub_list_item_index_)) == 0_u64) + { + // Found non-free item. + + break; + } + } + + return *this; +} + +ALsource& ALCcontext::SourceListIterator::operator*() noexcept +{ + assert(sub_list_iterator_ != sub_list_end_iterator_); + return (*sub_list_iterator_).Sources[sub_list_item_index_]; +} + +bool ALCcontext::SourceListIterator::operator==( + const SourceListIterator& rhs) const noexcept +{ + return + sub_list_iterator_ == rhs.sub_list_iterator_ && + sub_list_end_iterator_ == rhs.sub_list_end_iterator_ && + sub_list_item_index_ == rhs.sub_list_item_index_; +} + +bool ALCcontext::SourceListIterator::operator!=( + const SourceListIterator& rhs) const noexcept +{ + return !((*this) == rhs); +} + + +ALCcontext::SourceListEnumerator::SourceListEnumerator( + ALCcontext::SourceList& sources) noexcept + : + sources_{sources} +{ +} + +ALCcontext::SourceListIterator ALCcontext::SourceListEnumerator::begin() noexcept +{ + return SourceListIterator{sources_, SourceListIteratorBeginTag{}}; +} + +ALCcontext::SourceListIterator ALCcontext::SourceListEnumerator::end() noexcept +{ + return SourceListIterator{sources_, SourceListIteratorEndTag{}}; +} + + +bool ALCcontext::has_eax() const noexcept +{ + return eax_is_initialized_; +} + +bool ALCcontext::eax_is_capable() const noexcept +{ + return + eax_has_enough_aux_sends() && + eax_has_eax_reverb_effect(); +} + +void ALCcontext::eax_uninitialize() noexcept +{ + if (!eax_is_initialized_) + { + return; + } + + eax_is_initialized_ = true; + eax_is_tried_ = false; + + eax_fx_slots_.uninitialize(); + eax_al_filter_ = nullptr; +} + +void ALCcontext::eax_initialize_source( + ALsource& al_source) noexcept +try +{ + auto param = EaxSourceInitParam{}; + param.al_context = this; + param.al_filter = eax_al_filter_.get(); + + al_source.eax_initialize(param); +} +catch (...) +{ + eax_log_exception("Failed to initialize a source."); +} + +ALenum ALCcontext::eax_eax_set( + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_value, + ALuint property_value_size) +{ + eax_initialize(); + + const auto eax_call = create_eax_call( + false, + property_set_id, + property_id, + property_source_id, + property_value, + property_value_size + ); + + switch (eax_call.get_property_set_id()) + { + case EaxEaxCallPropertySetId::context: + eax_set(eax_call); + break; + + case EaxEaxCallPropertySetId::fx_slot: + case EaxEaxCallPropertySetId::fx_slot_effect: + eax_dispatch_fx_slot(eax_call); + break; + + case EaxEaxCallPropertySetId::source: + eax_dispatch_source(eax_call); + break; + + default: + eax_fail("Unsupported property set id."); + } + + return AL_NO_ERROR; +} + +ALenum ALCcontext::eax_eax_get( + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_value, + ALuint property_value_size) +{ + eax_initialize(); + + const auto eax_call = create_eax_call( + true, + property_set_id, + property_id, + property_source_id, + property_value, + property_value_size + ); + + switch (eax_call.get_property_set_id()) + { + case EaxEaxCallPropertySetId::context: + eax_get(eax_call); + break; + + case EaxEaxCallPropertySetId::fx_slot: + case EaxEaxCallPropertySetId::fx_slot_effect: + eax_dispatch_fx_slot(eax_call); + break; + + case EaxEaxCallPropertySetId::source: + eax_dispatch_source(eax_call); + break; + + default: + eax_fail("Unsupported property set id."); + } + + return AL_NO_ERROR; +} + +void ALCcontext::eax_update_filters() +{ + for (auto& source : SourceListEnumerator{mSourceList}) + { + source.eax_update_filters(); + } +} + +void ALCcontext::eax_set_last_error() noexcept +{ + eax_last_error_ = EAXERR_INVALID_OPERATION; +} + +float ALCcontext::eax_get_max_filter_gain() const noexcept +{ + return eax_max_filter_gain_; +} + +float ALCcontext::eax_get_air_absorption_factor() const noexcept +{ + return eax_air_absorption_factor_; +} + +EaxFxSlotIndex ALCcontext::eax_get_previous_primary_fx_slot_index() const noexcept +{ + return eax_previous_primary_fx_slot_index_; +} + +EaxFxSlotIndex ALCcontext::eax_get_primary_fx_slot_index() const noexcept +{ + return eax_primary_fx_slot_index_; +} + +const ALeffectslot& ALCcontext::eax_get_fx_slot( + EaxFxSlotIndexValue fx_slot_index) const +{ + return eax_fx_slots_.get(fx_slot_index); +} + +ALeffectslot& ALCcontext::eax_get_fx_slot( + EaxFxSlotIndexValue fx_slot_index) +{ + return eax_fx_slots_.get(fx_slot_index); +} + +[[noreturn]] +void ALCcontext::eax_fail( + const char* message) +{ + throw ContextException{message}; +} + +void ALCcontext::eax_initialize_extensions() +{ + if (!eax_g_is_enabled) + { + return; + } + + const auto string_max_capacity = + std::strlen(mExtensionList) + 1 + + std::strlen(eax_v2_0_ext_name) + 1 + + std::strlen(eax_v3_0_ext_name) + 1 + + std::strlen(eax_v4_0_ext_name) + 1 + + std::strlen(eax_v5_0_ext_name) + 1 + + std::strlen(eax_x_ram_ext_name) + 1 + + 0; + + eax_extension_list_.reserve(string_max_capacity); + + if (eax_is_capable()) + { + eax_extension_list_ += eax_v2_0_ext_name; + eax_extension_list_ += ' '; + + eax_extension_list_ += eax_v3_0_ext_name; + eax_extension_list_ += ' '; + + eax_extension_list_ += eax_v4_0_ext_name; + eax_extension_list_ += ' '; + + eax_extension_list_ += eax_v5_0_ext_name; + eax_extension_list_ += ' '; + } + + eax_extension_list_ += eax_x_ram_ext_name; + eax_extension_list_ += ' '; + + eax_extension_list_ += mExtensionList; + mExtensionList = eax_extension_list_.c_str(); +} + +void ALCcontext::eax_initialize() +{ + if (eax_is_initialized_) + { + return; + } + + if (eax_is_tried_) + { + eax_fail("No EAX."); + } + + eax_is_tried_ = true; + + if (!eax_g_is_enabled) + { + eax_fail("EAX disabled by a configuration."); + } + + eax_ensure_compatibility(); + eax_initialize_filter_gain(); + eax_initialize_filter(); + eax_set_defaults(); + eax_set_air_absorbtion_hf(); + eax_initialize_fx_slots(); + eax_initialize_sources(); + + eax_is_initialized_ = true; +} + +bool ALCcontext::eax_has_no_default_effect_slot() const noexcept +{ + return mDefaultSlot == nullptr; +} + +void ALCcontext::eax_ensure_no_default_effect_slot() const +{ + if (!eax_has_no_default_effect_slot()) + { + eax_fail("There is a default effect slot in the context."); + } +} + +bool ALCcontext::eax_has_enough_aux_sends() const noexcept +{ + return mALDevice->NumAuxSends >= EAX_MAX_FXSLOTS; +} + +void ALCcontext::eax_ensure_enough_aux_sends() const +{ + if (!eax_has_enough_aux_sends()) + { + eax_fail("Not enough aux sends."); + } +} + +bool ALCcontext::eax_has_eax_reverb_effect() const noexcept +{ + return !DisabledEffects[EAXREVERB_EFFECT]; +} + +void ALCcontext::eax_ensure_eax_reverb_effect() const +{ + if (!eax_has_eax_reverb_effect()) + { + eax_fail("Disabled EAX Reverb Effect."); + } +} + +void ALCcontext::eax_ensure_compatibility() +{ + eax_ensure_enough_aux_sends(); + eax_ensure_eax_reverb_effect(); +} + +void ALCcontext::eax_initialize_filter_gain() +{ + eax_max_filter_gain_ = level_mb_to_gain(GainMixMax / mGainBoost); +} + +void ALCcontext::eax_set_last_error_defaults() noexcept +{ + eax_last_error_ = EAX_OK; +} + +void ALCcontext::eax_set_speaker_config_defaults() noexcept +{ + eax_speaker_config_ = HEADPHONES; +} + +void ALCcontext::eax_set_session_defaults() noexcept +{ + eax_session_.ulEAXVersion = EAXCONTEXT_MINEAXSESSION; + eax_session_.ulMaxActiveSends = EAXCONTEXT_DEFAULTMAXACTIVESENDS; +} + +void ALCcontext::eax_set_context_defaults() noexcept +{ + eax_.context.guidPrimaryFXSlotID = EAXCONTEXT_DEFAULTPRIMARYFXSLOTID; + eax_.context.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR; + eax_.context.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF; + eax_.context.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE; +} + +void ALCcontext::eax_set_defaults() noexcept +{ + eax_set_last_error_defaults(); + eax_set_speaker_config_defaults(); + eax_set_session_defaults(); + eax_set_context_defaults(); + + eax_d_ = eax_; +} + +void ALCcontext::eax_initialize_filter() +{ + eax_al_filter_ = eax_create_al_low_pass_filter(*this); + + if (!eax_al_filter_) + { + eax_fail("Failed to make a low-pass filter."); + } +} + +void ALCcontext::eax_dispatch_fx_slot( + const EaxEaxCall& eax_call) +{ + auto& fx_slot = eax_get_fx_slot(eax_call.get_fx_slot_index()); + + if (fx_slot.eax_dispatch(eax_call)) + { + std::lock_guard source_lock{mSourceLock}; + + eax_update_filters(); + } +} + +void ALCcontext::eax_dispatch_source( + const EaxEaxCall& eax_call) +{ + const auto source_id = eax_call.get_property_al_name(); + + std::lock_guard source_lock{mSourceLock}; + + const auto source = ALsource::eax_lookup_source(*this, source_id); + + if (!source) + { + eax_fail("Source not found."); + } + + source->eax_dispatch(eax_call); +} + +void ALCcontext::eax_get_primary_fx_slot_id( + const EaxEaxCall& eax_call) +{ + eax_call.set_value(eax_.context.guidPrimaryFXSlotID); +} + +void ALCcontext::eax_get_distance_factor( + const EaxEaxCall& eax_call) +{ + eax_call.set_value(eax_.context.flDistanceFactor); +} + +void ALCcontext::eax_get_air_absorption_hf( + const EaxEaxCall& eax_call) +{ + eax_call.set_value(eax_.context.flAirAbsorptionHF); +} + +void ALCcontext::eax_get_hf_reference( + const EaxEaxCall& eax_call) +{ + eax_call.set_value(eax_.context.flHFReference); +} + +void ALCcontext::eax_get_last_error( + const EaxEaxCall& eax_call) +{ + const auto eax_last_error = eax_last_error_; + eax_last_error_ = EAX_OK; + eax_call.set_value(eax_last_error); +} + +void ALCcontext::eax_get_speaker_config( + const EaxEaxCall& eax_call) +{ + eax_call.set_value(eax_speaker_config_); +} + +void ALCcontext::eax_get_session( + const EaxEaxCall& eax_call) +{ + eax_call.set_value(eax_session_); +} + +void ALCcontext::eax_get_macro_fx_factor( + const EaxEaxCall& eax_call) +{ + eax_call.set_value(eax_.context.flMacroFXFactor); +} + +void ALCcontext::eax_get_context_all( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_version()) + { + case 4: + eax_call.set_value(static_cast(eax_.context)); + break; + + case 5: + eax_call.set_value(static_cast(eax_.context)); + break; + + default: + eax_fail("Unsupported EAX version."); + } +} + +void ALCcontext::eax_get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXCONTEXT_NONE: + break; + + case EAXCONTEXT_ALLPARAMETERS: + eax_get_context_all(eax_call); + break; + + case EAXCONTEXT_PRIMARYFXSLOTID: + eax_get_primary_fx_slot_id(eax_call); + break; + + case EAXCONTEXT_DISTANCEFACTOR: + eax_get_distance_factor(eax_call); + break; + + case EAXCONTEXT_AIRABSORPTIONHF: + eax_get_air_absorption_hf(eax_call); + break; + + case EAXCONTEXT_HFREFERENCE: + eax_get_hf_reference(eax_call); + break; + + case EAXCONTEXT_LASTERROR: + eax_get_last_error(eax_call); + break; + + case EAXCONTEXT_SPEAKERCONFIG: + eax_get_speaker_config(eax_call); + break; + + case EAXCONTEXT_EAXSESSION: + eax_get_session(eax_call); + break; + + case EAXCONTEXT_MACROFXFACTOR: + eax_get_macro_fx_factor(eax_call); + break; + + default: + eax_fail("Unsupported property id."); + } +} + +void ALCcontext::eax_set_primary_fx_slot_id() +{ + eax_previous_primary_fx_slot_index_ = eax_primary_fx_slot_index_; + eax_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID; +} + +void ALCcontext::eax_set_distance_factor() +{ + eax_set_al_listener_meters_per_unit(*this, eax_.context.flDistanceFactor); +} + +void ALCcontext::eax_set_air_absorbtion_hf() +{ + eax_air_absorption_factor_ = eax_.context.flAirAbsorptionHF / EAXCONTEXT_DEFAULTAIRABSORPTIONHF; +} + +void ALCcontext::eax_set_hf_reference() +{ + // TODO +} + +void ALCcontext::eax_set_macro_fx_factor() +{ + // TODO +} + +void ALCcontext::eax_set_context() +{ + eax_set_primary_fx_slot_id(); + eax_set_distance_factor(); + eax_set_air_absorbtion_hf(); + eax_set_hf_reference(); +} + +void ALCcontext::eax_initialize_fx_slots() +{ + eax_fx_slots_.initialize(*this); + eax_previous_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID; + eax_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID; +} + +void ALCcontext::eax_initialize_sources() +{ + std::unique_lock source_lock{mSourceLock}; + + for (auto& source : SourceListEnumerator{mSourceList}) + { + eax_initialize_source(source); + } +} + +void ALCcontext::eax_update_sources() +{ + std::unique_lock source_lock{mSourceLock}; + + for (auto& source : SourceListEnumerator{mSourceList}) + { + source.eax_update(eax_context_shared_dirty_flags_); + } +} + +void ALCcontext::eax_validate_primary_fx_slot_id( + const GUID& primary_fx_slot_id) +{ + if (primary_fx_slot_id != EAX_NULL_GUID && + primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot0 && + primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot0 && + primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot1 && + primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot1 && + primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot2 && + primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot2 && + primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot3 && + primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot3) + { + eax_fail("Unsupported primary FX slot id."); + } +} + +void ALCcontext::eax_validate_distance_factor( + float distance_factor) +{ + eax_validate_range( + "Distance Factor", + distance_factor, + EAXCONTEXT_MINDISTANCEFACTOR, + EAXCONTEXT_MAXDISTANCEFACTOR); +} + +void ALCcontext::eax_validate_air_absorption_hf( + float air_absorption_hf) +{ + eax_validate_range( + "Air Absorption HF", + air_absorption_hf, + EAXCONTEXT_MINAIRABSORPTIONHF, + EAXCONTEXT_MAXAIRABSORPTIONHF); +} + +void ALCcontext::eax_validate_hf_reference( + float hf_reference) +{ + eax_validate_range( + "HF Reference", + hf_reference, + EAXCONTEXT_MINHFREFERENCE, + EAXCONTEXT_MAXHFREFERENCE); +} + +void ALCcontext::eax_validate_speaker_config( + unsigned long speaker_config) +{ + switch (speaker_config) + { + case HEADPHONES: + case SPEAKERS_2: + case SPEAKERS_4: + case SPEAKERS_5: + case SPEAKERS_6: + case SPEAKERS_7: + break; + + default: + eax_fail("Unsupported speaker configuration."); + } +} + +void ALCcontext::eax_validate_session_eax_version( + unsigned long eax_version) +{ + switch (eax_version) + { + case EAX_40: + case EAX_50: + break; + + default: + eax_fail("Unsupported session EAX version."); + } +} + +void ALCcontext::eax_validate_session_max_active_sends( + unsigned long max_active_sends) +{ + eax_validate_range( + "Max Active Sends", + max_active_sends, + EAXCONTEXT_MINMAXACTIVESENDS, + EAXCONTEXT_MAXMAXACTIVESENDS); +} + +void ALCcontext::eax_validate_session( + const EAXSESSIONPROPERTIES& eax_session) +{ + eax_validate_session_eax_version(eax_session.ulEAXVersion); + eax_validate_session_max_active_sends(eax_session.ulMaxActiveSends); +} + +void ALCcontext::eax_validate_macro_fx_factor( + float macro_fx_factor) +{ + eax_validate_range( + "Macro FX Factor", + macro_fx_factor, + EAXCONTEXT_MINMACROFXFACTOR, + EAXCONTEXT_MAXMACROFXFACTOR); +} + +void ALCcontext::eax_validate_context_all( + const EAX40CONTEXTPROPERTIES& context_all) +{ + eax_validate_primary_fx_slot_id(context_all.guidPrimaryFXSlotID); + eax_validate_distance_factor(context_all.flDistanceFactor); + eax_validate_air_absorption_hf(context_all.flAirAbsorptionHF); + eax_validate_hf_reference(context_all.flHFReference); +} + +void ALCcontext::eax_validate_context_all( + const EAX50CONTEXTPROPERTIES& context_all) +{ + eax_validate_context_all(static_cast(context_all)); + eax_validate_macro_fx_factor(context_all.flMacroFXFactor); +} + +void ALCcontext::eax_defer_primary_fx_slot_id( + const GUID& primary_fx_slot_id) +{ + eax_d_.context.guidPrimaryFXSlotID = primary_fx_slot_id; + + eax_context_dirty_flags_.guidPrimaryFXSlotID = + (eax_.context.guidPrimaryFXSlotID != eax_d_.context.guidPrimaryFXSlotID); +} + +void ALCcontext::eax_defer_distance_factor( + float distance_factor) +{ + eax_d_.context.flDistanceFactor = distance_factor; + + eax_context_dirty_flags_.flDistanceFactor = + (eax_.context.flDistanceFactor != eax_d_.context.flDistanceFactor); +} + +void ALCcontext::eax_defer_air_absorption_hf( + float air_absorption_hf) +{ + eax_d_.context.flAirAbsorptionHF = air_absorption_hf; + + eax_context_dirty_flags_.flAirAbsorptionHF = + (eax_.context.flAirAbsorptionHF != eax_d_.context.flAirAbsorptionHF); +} + +void ALCcontext::eax_defer_hf_reference( + float hf_reference) +{ + eax_d_.context.flHFReference = hf_reference; + + eax_context_dirty_flags_.flHFReference = + (eax_.context.flHFReference != eax_d_.context.flHFReference); +} + +void ALCcontext::eax_defer_macro_fx_factor( + float macro_fx_factor) +{ + eax_d_.context.flMacroFXFactor = macro_fx_factor; + + eax_context_dirty_flags_.flMacroFXFactor = + (eax_.context.flMacroFXFactor != eax_d_.context.flMacroFXFactor); +} + +void ALCcontext::eax_defer_context_all( + const EAX40CONTEXTPROPERTIES& context_all) +{ + eax_defer_primary_fx_slot_id(context_all.guidPrimaryFXSlotID); + eax_defer_distance_factor(context_all.flDistanceFactor); + eax_defer_air_absorption_hf(context_all.flAirAbsorptionHF); + eax_defer_hf_reference(context_all.flHFReference); +} + +void ALCcontext::eax_defer_context_all( + const EAX50CONTEXTPROPERTIES& context_all) +{ + eax_defer_context_all(static_cast(context_all)); + eax_defer_macro_fx_factor(context_all.flMacroFXFactor); +} + +void ALCcontext::eax_defer_context_all( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_version()) + { + case 4: + { + const auto& context_all = + eax_call.get_value(); + + eax_validate_context_all(context_all); + } + + break; + + case 5: + { + const auto& context_all = + eax_call.get_value(); + + eax_validate_context_all(context_all); + } + + break; + + default: + eax_fail("Unsupported EAX version."); + } +} + +void ALCcontext::eax_defer_primary_fx_slot_id( + const EaxEaxCall& eax_call) +{ + const auto& primary_fx_slot_id = + eax_call.get_value(); + + eax_validate_primary_fx_slot_id(primary_fx_slot_id); + eax_defer_primary_fx_slot_id(primary_fx_slot_id); +} + +void ALCcontext::eax_defer_distance_factor( + const EaxEaxCall& eax_call) +{ + const auto& distance_factor = + eax_call.get_value(); + + eax_validate_distance_factor(distance_factor); + eax_defer_distance_factor(distance_factor); +} + +void ALCcontext::eax_defer_air_absorption_hf( + const EaxEaxCall& eax_call) +{ + const auto& air_absorption_hf = + eax_call.get_value(); + + eax_validate_air_absorption_hf(air_absorption_hf); + eax_defer_air_absorption_hf(air_absorption_hf); +} + +void ALCcontext::eax_defer_hf_reference( + const EaxEaxCall& eax_call) +{ + const auto& hf_reference = + eax_call.get_value(); + + eax_validate_hf_reference(hf_reference); + eax_defer_hf_reference(hf_reference); +} + +void ALCcontext::eax_set_speaker_config( + const EaxEaxCall& eax_call) +{ + const auto speaker_config = + eax_call.get_value(); + + eax_validate_speaker_config(speaker_config); + + eax_speaker_config_ = speaker_config; +} + +void ALCcontext::eax_set_session( + const EaxEaxCall& eax_call) +{ + const auto& eax_session = + eax_call.get_value(); + + eax_validate_session(eax_session); + + eax_session_ = eax_session; +} + +void ALCcontext::eax_defer_macro_fx_factor( + const EaxEaxCall& eax_call) +{ + const auto& macro_fx_factor = + eax_call.get_value(); + + eax_validate_macro_fx_factor(macro_fx_factor); + eax_defer_macro_fx_factor(macro_fx_factor); +} + +void ALCcontext::eax_set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXCONTEXT_NONE: + break; + + case EAXCONTEXT_ALLPARAMETERS: + eax_defer_context_all(eax_call); + break; + + case EAXCONTEXT_PRIMARYFXSLOTID: + eax_defer_primary_fx_slot_id(eax_call); + break; + + case EAXCONTEXT_DISTANCEFACTOR: + eax_defer_distance_factor(eax_call); + break; + + case EAXCONTEXT_AIRABSORPTIONHF: + eax_defer_air_absorption_hf(eax_call); + break; + + case EAXCONTEXT_HFREFERENCE: + eax_defer_hf_reference(eax_call); + break; + + case EAXCONTEXT_LASTERROR: + eax_fail("Setting last error not supported."); + + case EAXCONTEXT_SPEAKERCONFIG: + eax_set_speaker_config(eax_call); + break; + + case EAXCONTEXT_EAXSESSION: + eax_set_session(eax_call); + break; + + case EAXCONTEXT_MACROFXFACTOR: + eax_defer_macro_fx_factor(eax_call); + break; + + default: + eax_fail("Unsupported property id."); + } + + if (!eax_call.is_deferred()) + { + eax_apply_deferred(); + } +} + +void ALCcontext::eax_apply_deferred() +{ + if (eax_context_dirty_flags_ == ContextDirtyFlags{}) + { + return; + } + + eax_ = eax_d_; + + if (eax_context_dirty_flags_.guidPrimaryFXSlotID) + { + eax_context_shared_dirty_flags_.primary_fx_slot_id = true; + eax_set_primary_fx_slot_id(); + } + + if (eax_context_dirty_flags_.flDistanceFactor) + { + eax_set_distance_factor(); + } + + if (eax_context_dirty_flags_.flAirAbsorptionHF) + { + eax_context_shared_dirty_flags_.air_absorption_hf = true; + eax_set_air_absorbtion_hf(); + } + + if (eax_context_dirty_flags_.flHFReference) + { + eax_set_hf_reference(); + } + + if (eax_context_dirty_flags_.flMacroFXFactor) + { + eax_set_macro_fx_factor(); + } + + if (eax_context_shared_dirty_flags_ != EaxContextSharedDirtyFlags{}) + { + eax_update_sources(); + } + + eax_context_shared_dirty_flags_ = EaxContextSharedDirtyFlags{}; + eax_context_dirty_flags_ = ContextDirtyFlags{}; +} + + +namespace +{ + + +class EaxSetException : + public EaxException +{ +public: + explicit EaxSetException( + const char* message) + : + EaxException{"EAX_SET", message} + { + } +}; // EaxSetException + + +[[noreturn]] +void eax_fail_set( + const char* message) +{ + throw EaxSetException{message}; +} + + +class EaxGetException : + public EaxException +{ +public: + explicit EaxGetException( + const char* message) + : + EaxException{"EAX_GET", message} + { + } +}; // EaxGetException + + +[[noreturn]] +void eax_fail_get( + const char* message) +{ + throw EaxGetException{message}; +} + + +} // namespace + + +ALenum AL_APIENTRY EAXSet( + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_value, + ALuint property_value_size) noexcept +try +{ + auto context = GetContextRef(); + + if (!context) + { + eax_fail_set("No current context."); + } + + std::lock_guard prop_lock{context->mPropLock}; + + return context->eax_eax_set( + property_set_id, + property_id, + property_source_id, + property_value, + property_value_size + ); +} +catch (...) +{ + eax_log_exception(__func__); + return AL_INVALID_OPERATION; +} + +ALenum AL_APIENTRY EAXGet( + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_value, + ALuint property_value_size) noexcept +try +{ + auto context = GetContextRef(); + + if (!context) + { + eax_fail_get("No current context."); + } + + std::lock_guard prop_lock{context->mPropLock}; + + return context->eax_eax_get( + property_set_id, + property_id, + property_source_id, + property_value, + property_value_size + ); +} +catch (...) +{ + eax_log_exception(__func__); + return AL_INVALID_OPERATION; +} +#endif // ALSOFT_EAX diff --git a/alc/context.h b/alc/context.h index 87754235..b34807b8 100644 --- a/alc/context.h +++ b/alc/context.h @@ -19,6 +19,49 @@ #include "intrusive_ptr.h" #include "vector.h" +#if ALSOFT_EAX +#include "al/filter.h" + +#include "al/eax_eax_call.h" +#include "al/eax_fx_slot_index.h" +#include "al/eax_fx_slots.h" +#include "al/eax_utils.h" +#endif // ALSOFT_EAX + + +#if ALSOFT_EAX +using EaxContextSharedDirtyFlagsValue = std::uint_least8_t; + +struct EaxContextSharedDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxContextSharedDirtyFlagsValue primary_fx_slot_id : 1; + EaxContextSharedDirtyFlagsValue air_absorption_hf : 1; +}; // EaxContextSharedDirtyFlags + + +using ContextDirtyFlagsValue = std::uint_least8_t; + +struct ContextDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + ContextDirtyFlagsValue guidPrimaryFXSlotID : 1; + ContextDirtyFlagsValue flDistanceFactor : 1; + ContextDirtyFlagsValue flAirAbsorptionHF : 1; + ContextDirtyFlagsValue flHFReference : 1; + ContextDirtyFlagsValue flMacroFXFactor : 1; +}; // ContextDirtyFlags + + +struct EaxAlIsExtensionPresentResult +{ + ALboolean is_present; + bool is_return; +}; // EaxAlIsExtensionPresentResult +#endif // ALSOFT_EAX + struct ALeffect; struct ALeffectslot; struct ALsource; @@ -162,6 +205,332 @@ public: static ALeffect sDefaultEffect; DEF_NEWDEL(ALCcontext) + +#if ALSOFT_EAX +public: + bool has_eax() const noexcept; + + bool eax_is_capable() const noexcept; + + + void eax_uninitialize() noexcept; + + void eax_initialize_source( + ALsource& al_source) noexcept; + + + ALenum eax_eax_set( + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_value, + ALuint property_value_size); + + ALenum eax_eax_get( + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_value, + ALuint property_value_size); + + + void eax_update_filters(); + + + void eax_set_last_error() noexcept; + + + float eax_get_max_filter_gain() const noexcept; + + float eax_get_air_absorption_factor() const noexcept; + + EaxFxSlotIndex eax_get_previous_primary_fx_slot_index() const noexcept; + + EaxFxSlotIndex eax_get_primary_fx_slot_index() const noexcept; + + const ALeffectslot& eax_get_fx_slot( + EaxFxSlotIndexValue fx_slot_index) const; + + ALeffectslot& eax_get_fx_slot( + EaxFxSlotIndexValue fx_slot_index); + + +private: + using SourceList = al::vector; + + + struct SourceListIteratorBeginTag{}; + struct SourceListIteratorEndTag{}; + + class SourceListIterator + { + public: + SourceListIterator( + SourceList& sources, + SourceListIteratorBeginTag) noexcept; + + SourceListIterator( + SourceList& sources, + SourceListIteratorEndTag) noexcept; + + SourceListIterator( + const SourceListIterator& rhs); + + SourceListIterator& operator=( + const SourceListIterator& rhs) = delete; + + SourceListIterator& operator++(); + + ALsource& operator*() noexcept; + + bool operator==( + const SourceListIterator& rhs) const noexcept; + + bool operator!=( + const SourceListIterator& rhs) const noexcept; + + + private: + SourceList::iterator sub_list_iterator_; + SourceList::iterator sub_list_end_iterator_; + std::uint64_t sub_list_item_index_; + }; // SourceListIterator + + class SourceListEnumerator + { + public: + explicit SourceListEnumerator( + SourceList& sources) noexcept; + + SourceListEnumerator( + const SourceListEnumerator& rhs) = delete; + + SourceListEnumerator& operator=( + const SourceListEnumerator& rhs) = delete; + + SourceListIterator begin() noexcept; + + SourceListIterator end() noexcept; + + + private: + SourceList& sources_; + }; // SourceListEnumerator + + + struct Eax + { + EAX50CONTEXTPROPERTIES context{}; + }; // Eax + + + bool eax_is_initialized_{}; + bool eax_is_tried_{}; + + long eax_last_error_{}; + unsigned long eax_speaker_config_{}; + + float eax_max_filter_gain_{}; + float eax_air_absorption_factor_{}; + EaxFxSlotIndex eax_previous_primary_fx_slot_index_{}; + EaxFxSlotIndex eax_primary_fx_slot_index_{}; + EaxFxSlots eax_fx_slots_{}; + + EaxContextSharedDirtyFlags eax_context_shared_dirty_flags_{}; + + EaxAlFilterUPtr eax_al_filter_{}; + Eax eax_{}; + Eax eax_d_{}; + EAXSESSIONPROPERTIES eax_session_{}; + + ContextDirtyFlags eax_context_dirty_flags_{}; + + std::string eax_extension_list_{}; + + + [[noreturn]] + static void eax_fail( + const char* message); + + + void eax_initialize_extensions(); + + void eax_initialize(); + + + bool eax_has_no_default_effect_slot() const noexcept; + + void eax_ensure_no_default_effect_slot() const; + + bool eax_has_enough_aux_sends() const noexcept; + + void eax_ensure_enough_aux_sends() const; + + bool eax_has_eax_reverb_effect() const noexcept; + + void eax_ensure_eax_reverb_effect() const; + + void eax_ensure_compatibility(); + + + void eax_initialize_filter_gain(); + + void eax_set_last_error_defaults() noexcept; + + void eax_set_speaker_config_defaults() noexcept; + + void eax_set_session_defaults() noexcept; + + void eax_set_context_defaults() noexcept; + + void eax_set_defaults() noexcept; + + void eax_initialize_filter(); + + void eax_initialize_sources(); + + + void eax_dispatch_fx_slot( + const EaxEaxCall& eax_call); + + void eax_dispatch_source( + const EaxEaxCall& eax_call); + + + void eax_get_primary_fx_slot_id( + const EaxEaxCall& eax_call); + + void eax_get_distance_factor( + const EaxEaxCall& eax_call); + + void eax_get_air_absorption_hf( + const EaxEaxCall& eax_call); + + void eax_get_hf_reference( + const EaxEaxCall& eax_call); + + void eax_get_last_error( + const EaxEaxCall& eax_call); + + void eax_get_speaker_config( + const EaxEaxCall& eax_call); + + void eax_get_session( + const EaxEaxCall& eax_call); + + void eax_get_macro_fx_factor( + const EaxEaxCall& eax_call); + + void eax_get_context_all( + const EaxEaxCall& eax_call); + + void eax_get( + const EaxEaxCall& eax_call); + + + void eax_set_primary_fx_slot_id(); + + void eax_set_distance_factor(); + + void eax_set_air_absorbtion_hf(); + + void eax_set_hf_reference(); + + void eax_set_macro_fx_factor(); + + void eax_set_context(); + + void eax_initialize_fx_slots(); + + + void eax_update_sources(); + + + void eax_validate_primary_fx_slot_id( + const GUID& primary_fx_slot_id); + + void eax_validate_distance_factor( + float distance_factor); + + void eax_validate_air_absorption_hf( + float air_absorption_hf); + + void eax_validate_hf_reference( + float hf_reference); + + void eax_validate_speaker_config( + unsigned long speaker_config); + + void eax_validate_session_eax_version( + unsigned long eax_version); + + void eax_validate_session_max_active_sends( + unsigned long max_active_sends); + + void eax_validate_session( + const EAXSESSIONPROPERTIES& eax_session); + + void eax_validate_macro_fx_factor( + float macro_fx_factor); + + void eax_validate_context_all( + const EAX40CONTEXTPROPERTIES& context_all); + + void eax_validate_context_all( + const EAX50CONTEXTPROPERTIES& context_all); + + + void eax_defer_primary_fx_slot_id( + const GUID& primary_fx_slot_id); + + void eax_defer_distance_factor( + float distance_factor); + + void eax_defer_air_absorption_hf( + float air_absorption_hf); + + void eax_defer_hf_reference( + float hf_reference); + + void eax_defer_macro_fx_factor( + float macro_fx_factor); + + void eax_defer_context_all( + const EAX40CONTEXTPROPERTIES& context_all); + + void eax_defer_context_all( + const EAX50CONTEXTPROPERTIES& context_all); + + + void eax_defer_context_all( + const EaxEaxCall& eax_call); + + void eax_defer_primary_fx_slot_id( + const EaxEaxCall& eax_call); + + void eax_defer_distance_factor( + const EaxEaxCall& eax_call); + + void eax_defer_air_absorption_hf( + const EaxEaxCall& eax_call); + + void eax_defer_hf_reference( + const EaxEaxCall& eax_call); + + void eax_set_speaker_config( + const EaxEaxCall& eax_call); + + void eax_set_session( + const EaxEaxCall& eax_call); + + void eax_defer_macro_fx_factor( + const EaxEaxCall& eax_call); + + void eax_set( + const EaxEaxCall& eax_call); + + void eax_apply_deferred(); +#endif // ALSOFT_EAX }; #define SETERR_RETURN(ctx, err, retval, ...) do { \ @@ -179,4 +548,21 @@ void UpdateContextProps(ALCcontext *context); extern bool TrapALError; + +#if ALSOFT_EAX +ALenum AL_APIENTRY EAXSet( + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_value, + ALuint property_value_size) noexcept; + +ALenum AL_APIENTRY EAXGet( + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_value, + ALuint property_value_size) noexcept; +#endif // ALSOFT_EAX + #endif /* ALC_CONTEXT_H */ diff --git a/alc/device.h b/alc/device.h index 4798d422..e4eb76ea 100644 --- a/alc/device.h +++ b/alc/device.h @@ -18,6 +18,10 @@ #include "intrusive_ptr.h" #include "vector.h" +#if ALSOFT_EAX +#include "al/eax_x_ram.h" +#endif // ALSOFT_EAX + struct ALbuffer; struct ALeffect; struct ALfilter; @@ -106,6 +110,10 @@ struct ALCdevice : public al::intrusive_ref, DeviceBase { std::mutex FilterLock; al::vector FilterList; +#if ALSOFT_EAX + ALsizei eax_x_ram_free_size{eax_x_ram_max_size}; +#endif // ALSOFT_EAX + ALCdevice(DeviceType type); ~ALCdevice(); diff --git a/alsoftrc.sample b/alsoftrc.sample index e2a45579..ab0345d9 100644 --- a/alsoftrc.sample +++ b/alsoftrc.sample @@ -578,3 +578,11 @@ # Creates AMB format files using first-order ambisonics instead of a standard # single- or multi-channel .wav file. #bformat = false + +## +## EAX extensions stuff +## +[eax] +##enable: +# Sets whether to enable EAX extensions or not. +#enable = true diff --git a/common/alnumeric.h b/common/alnumeric.h index c16f3e62..d72ba1e3 100644 --- a/common/alnumeric.h +++ b/common/alnumeric.h @@ -1,6 +1,10 @@ #ifndef AL_NUMERIC_H #define AL_NUMERIC_H +#if ALSOFT_EAX +#include +#endif // ALSOFT_EAX + #include #include #ifdef HAVE_INTRIN_H @@ -271,4 +275,45 @@ inline float fast_roundf(float f) noexcept #endif } +#if ALSOFT_EAX +template< + typename T +> +inline constexpr const T& clamp( + const T& value, + const T& min_value, + const T& max_value) noexcept +{ + return value < min_value ? min_value : (value > max_value ? max_value : value); +} + +// Converts level (mB) to gain. +inline float level_mb_to_gain( + float x) +{ + if (x <= -10'000.0F) + { + return 0.0F; + } + else + { + return std::pow(10.0F, x / 2'000.0F); + } +} + +// Converts gain to level (mB). +inline float gain_to_level_mb( + float x) +{ + if (x <= 0.0F) + { + return -10'000.0F; + } + else + { + return std::log10(x * 2'000.0F); + } +} +#endif // ALSOFT_EAX + #endif /* AL_NUMERIC_H */ -- cgit v1.2.3 From 816bd8ab309dc0fe9afefcc5e3f2c294d3dc60a5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 30 Jan 2022 05:42:44 -0800 Subject: Move ALSOFT_EAX definition to config.h And disable it by default for non-Windows targets --- CMakeLists.txt | 4 +--- al/auxeffectslot.cpp | 8 +++----- al/auxeffectslot.h | 6 +++--- al/buffer.cpp | 15 ++++++--------- al/buffer.h | 4 ++-- al/eax_api.cpp | 1 + al/eax_eax_call.cpp | 2 ++ al/eax_effect.cpp | 2 ++ al/eax_exception.cpp | 2 ++ al/eax_fx_slot_index.cpp | 2 ++ al/eax_fx_slots.cpp | 2 ++ al/eax_globals.cpp | 2 ++ al/eax_utils.cpp | 3 ++- al/eax_x_ram.cpp | 2 ++ al/effect.cpp | 7 +++---- al/effect.h | 9 ++++----- al/effects/autowah.cpp | 11 +++-------- al/effects/chorus.cpp | 11 +++-------- al/effects/compressor.cpp | 11 +++-------- al/effects/distortion.cpp | 11 +++-------- al/effects/echo.cpp | 11 +++-------- al/effects/effects.cpp | 9 +++++---- al/effects/effects.h | 4 ++-- al/effects/equalizer.cpp | 11 +++-------- al/effects/fshifter.cpp | 11 +++-------- al/effects/modulator.cpp | 11 +++-------- al/effects/null.cpp | 11 +++-------- al/effects/pshifter.cpp | 11 +++-------- al/effects/reverb.cpp | 11 +++-------- al/effects/vmorpher.cpp | 10 +++------- al/extension.cpp | 8 ++++---- al/filter.cpp | 4 ++-- al/filter.h | 9 ++++----- al/listener.cpp | 2 +- al/listener.h | 2 +- al/source.cpp | 15 +++++++-------- al/source.h | 13 ++++++------- al/state.cpp | 4 ++-- alc/alc.cpp | 8 ++++---- alc/context.cpp | 12 +++++------- alc/context.h | 28 +++++++++++++--------------- alc/device.h | 4 ++-- common/alnumeric.h | 46 +++++++++++++--------------------------------- config.h.in | 3 +++ 44 files changed, 149 insertions(+), 224 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 9480910b..148090c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,7 +95,7 @@ option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...) option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" ON) option(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON) -option(ALSOFT_EAX "Enable EAX extensions." ON) +option(ALSOFT_EAX "Enable EAX extensions." $) if(DEFINED SHARE_INSTALL_DIR) message(WARNING "SHARE_INSTALL_DIR is deprecated. Use the variables provided by the GNUInstallDirs module instead") @@ -1316,7 +1316,6 @@ target_include_directories(common PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_D target_compile_definitions(common PRIVATE ${CPP_DEFS}) target_compile_options(common PRIVATE ${C_FLAGS}) set_target_properties(common PROPERTIES POSITION_INDEPENDENT_CODE TRUE) -target_compile_definitions(common PRIVATE "ALSOFT_EAX=$") unset(HAS_ROUTER) @@ -1438,7 +1437,6 @@ set_target_properties(${IMPL_TARGET} PROPERTIES OUTPUT_NAME ${LIBNAME} target_compile_definitions(${IMPL_TARGET} PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}" ${CPP_DEFS}) -target_compile_definitions(${IMPL_TARGET} PRIVATE "ALSOFT_EAX=$") target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS}) if(TARGET build_version) diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp index 40949aa3..4382553e 100644 --- a/al/auxeffectslot.cpp +++ b/al/auxeffectslot.cpp @@ -50,7 +50,7 @@ #include "effect.h" #include "opthelpers.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "eax_exception.h" #include "eax_utils.h" #endif // ALSOFT_EAX @@ -1047,10 +1047,8 @@ EffectSlotSubList::~EffectSlotSubList() EffectSlots = nullptr; } -#if ALSOFT_EAX -namespace -{ - +#ifdef ALSOFT_EAX +namespace { class EaxFxSlotException : public EaxException diff --git a/al/auxeffectslot.h b/al/auxeffectslot.h index 54f1987d..12213df6 100644 --- a/al/auxeffectslot.h +++ b/al/auxeffectslot.h @@ -16,7 +16,7 @@ #include "intrusive_ptr.h" #include "vector.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include #include "al/effect.h" @@ -72,7 +72,7 @@ struct ALeffectslot { DEF_NEWDEL(ALeffectslot) -#if ALSOFT_EAX +#ifdef ALSOFT_EAX public: void eax_initialize( ALCcontext& al_context, @@ -258,7 +258,7 @@ private: void UpdateAllEffectSlotProps(ALCcontext *context); -#if ALSOFT_EAX +#ifdef ALSOFT_EAX class EaxAlEffectSlotDeleter { public: diff --git a/al/buffer.cpp b/al/buffer.cpp index a4967223..8a5bee25 100644 --- a/al/buffer.cpp +++ b/al/buffer.cpp @@ -56,7 +56,7 @@ #include "core/voice.h" #include "opthelpers.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "eax_globals.h" #include "eax_x_ram.h" #endif // ALSOFT_EAX @@ -417,7 +417,7 @@ ALbuffer *AllocBuffer(ALCdevice *device) void FreeBuffer(ALCdevice *device, ALbuffer *buffer) { -#if ALSOFT_EAX +#ifdef ALSOFT_EAX if (buffer->eax_x_ram_is_hardware) { const auto buffer_size = static_cast(buffer->OriginalSize); @@ -499,7 +499,7 @@ const ALchar *NameFromUserFmtType(UserFmtType type) return ""; } -#if ALSOFT_EAX +#ifdef ALSOFT_EAX bool eax_x_ram_validate_buffer( ALCdevice& al_device, ALbuffer& al_buffer) @@ -731,7 +731,7 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, ALBuf->mLoopStart = 0; ALBuf->mLoopEnd = ALBuf->mSampleLen; -#if ALSOFT_EAX +#ifdef ALSOFT_EAX if (eax_g_is_enabled) { eax_x_ram_update_buffer(*context->mALDevice, *ALBuf); @@ -1006,8 +1006,8 @@ START_API_FUNC if UNLIKELY(!usrfmt) context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format); else -#if ALSOFT_EAX { +#ifdef ALSOFT_EAX if (eax_g_is_enabled) { const auto is_buffer_valid = eax_x_ram_validate_buffer(*device, *albuf); @@ -1021,9 +1021,7 @@ START_API_FUNC #endif // ALSOFT_EAX LoadData(context.get(), albuf, freq, static_cast(size), usrfmt->channels, usrfmt->type, static_cast(data), flags); -#if ALSOFT_EAX } -#endif // ALSOFT_EAX } } END_API_FUNC @@ -1777,7 +1775,7 @@ BufferSubList::~BufferSubList() } -#if ALSOFT_EAX +#ifdef ALSOFT_EAX ALboolean AL_APIENTRY EAXSetBufferMode( ALsizei n, const ALuint* buffers, @@ -1932,5 +1930,4 @@ START_API_FUNC } END_API_FUNC - #endif // ALSOFT_EAX diff --git a/al/buffer.h b/al/buffer.h index 0514d984..8c323bea 100644 --- a/al/buffer.h +++ b/al/buffer.h @@ -12,7 +12,7 @@ #include "core/buffer_storage.h" #include "vector.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "eax_x_ram.h" #endif // ALSOFT_EAX @@ -72,7 +72,7 @@ struct ALbuffer : public BufferStorage { DISABLE_ALLOC() -#if ALSOFT_EAX +#ifdef ALSOFT_EAX ALenum eax_x_ram_mode{AL_STORAGE_AUTOMATIC}; bool eax_x_ram_is_hardware{}; bool eax_x_ram_is_dirty{}; diff --git a/al/eax_api.cpp b/al/eax_api.cpp index 9907dd4d..391363ec 100644 --- a/al/eax_api.cpp +++ b/al/eax_api.cpp @@ -5,6 +5,7 @@ // https://github.com/id-Software/DOOM-3/tree/master/neo/openal/include // +#include "config.h" #include diff --git a/al/eax_eax_call.cpp b/al/eax_eax_call.cpp index e6967ded..e9cb2746 100644 --- a/al/eax_eax_call.cpp +++ b/al/eax_eax_call.cpp @@ -1,3 +1,5 @@ +#include "config.h" + #include "al/eax_eax_call.h" #include "al/eax_exception.h" diff --git a/al/eax_effect.cpp b/al/eax_effect.cpp index 72af94fc..9cbf4c13 100644 --- a/al/eax_effect.cpp +++ b/al/eax_effect.cpp @@ -1 +1,3 @@ +#include "config.h" + #include "eax_effect.h" diff --git a/al/eax_exception.cpp b/al/eax_exception.cpp index e3635793..c8ecf79d 100644 --- a/al/eax_exception.cpp +++ b/al/eax_exception.cpp @@ -1,3 +1,5 @@ +#include "config.h" + #include "eax_exception.h" #include diff --git a/al/eax_fx_slot_index.cpp b/al/eax_fx_slot_index.cpp index dffaef47..484c3499 100644 --- a/al/eax_fx_slot_index.cpp +++ b/al/eax_fx_slot_index.cpp @@ -1,3 +1,5 @@ +#include "config.h" + #include "eax_fx_slot_index.h" #include "eax_exception.h" diff --git a/al/eax_fx_slots.cpp b/al/eax_fx_slots.cpp index 41b18f77..63e867ec 100644 --- a/al/eax_fx_slots.cpp +++ b/al/eax_fx_slots.cpp @@ -1,3 +1,5 @@ +#include "config.h" + #include "eax_fx_slots.h" #include diff --git a/al/eax_globals.cpp b/al/eax_globals.cpp index e2f4681e..454144a9 100644 --- a/al/eax_globals.cpp +++ b/al/eax_globals.cpp @@ -1,3 +1,5 @@ +#include "config.h" + #include "eax_globals.h" diff --git a/al/eax_utils.cpp b/al/eax_utils.cpp index 9a8f04f1..67389de4 100644 --- a/al/eax_utils.cpp +++ b/al/eax_utils.cpp @@ -1,7 +1,8 @@ +#include "config.h" + #include "eax_utils.h" #include - #include #include "core/logging.h" diff --git a/al/eax_x_ram.cpp b/al/eax_x_ram.cpp index d11a03ab..ac3e7ebb 100644 --- a/al/eax_x_ram.cpp +++ b/al/eax_x_ram.cpp @@ -1 +1,3 @@ +#include "config.h" + #include "eax_x_ram.h" diff --git a/al/effect.cpp b/al/effect.cpp index 79cd7fab..e4fe95b3 100644 --- a/al/effect.cpp +++ b/al/effect.cpp @@ -51,7 +51,7 @@ #include "opthelpers.h" #include "vector.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include #include "eax_exception.h" @@ -751,9 +751,8 @@ void LoadReverbPreset(const char *name, ALeffect *effect) WARN("Reverb preset '%s' not found\n", name); } -#if ALSOFT_EAX -namespace -{ +#ifdef ALSOFT_EAX +namespace { class EaxAlEffectException : public EaxException diff --git a/al/effect.h b/al/effect.h index 5b5e4b03..0e4948f7 100644 --- a/al/effect.h +++ b/al/effect.h @@ -7,7 +7,7 @@ #include "al/effects/effects.h" #include "alc/effects/base.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include #include "eax_effect.h" @@ -59,7 +59,7 @@ struct ALeffect { DISABLE_ALLOC() -#if ALSOFT_EAX +#ifdef ALSOFT_EAX public: EaxEffectUPtr eax_effect{}; @@ -81,9 +81,8 @@ void InitEffect(ALeffect *effect); void LoadReverbPreset(const char *name, ALeffect *effect); -#if ALSOFT_EAX -class EaxAlEffectDeleter -{ +#ifdef ALSOFT_EAX +class EaxAlEffectDeleter { public: EaxAlEffectDeleter() noexcept = default; diff --git a/al/effects/autowah.cpp b/al/effects/autowah.cpp index bdd1bc09..6dbafca3 100644 --- a/al/effects/autowah.cpp +++ b/al/effects/autowah.cpp @@ -11,7 +11,7 @@ #include "alc/effects/base.h" #include "effects.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "alnumeric.h" #include "al/eax_exception.h" @@ -116,10 +116,8 @@ DEFINE_ALEFFECT_VTABLE(Autowah); const EffectProps AutowahEffectProps{genDefaultProps()}; -#if ALSOFT_EAX -namespace -{ - +#ifdef ALSOFT_EAX +namespace { using EaxAutoWahEffectDirtyFlagsValue = std::uint_least8_t; @@ -566,15 +564,12 @@ bool EaxAutoWahEffect::set( return false; } - } // namespace - EaxEffectUPtr eax_create_eax_auto_wah_effect( EffectProps& al_effect_props) { return std::make_unique<::EaxAutoWahEffect>(al_effect_props); } - #endif // ALSOFT_EAX diff --git a/al/effects/chorus.cpp b/al/effects/chorus.cpp index ed994fbb..37651406 100644 --- a/al/effects/chorus.cpp +++ b/al/effects/chorus.cpp @@ -11,7 +11,7 @@ #include "core/logging.h" #include "effects.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include #include "alnumeric.h" @@ -290,10 +290,8 @@ DEFINE_ALEFFECT_VTABLE(Flanger); const EffectProps FlangerEffectProps{genDefaultFlangerProps()}; -#if ALSOFT_EAX -namespace -{ - +#ifdef ALSOFT_EAX +namespace { void eax_set_efx_waveform( ALenum waveform, @@ -1507,15 +1505,12 @@ bool EaxFlangerEffect::set( return false; } - } // namespace - EaxEffectUPtr eax_create_eax_flanger_effect( EffectProps& al_effect_props) { return std::make_unique(al_effect_props); } - #endif // ALSOFT_EAX diff --git a/al/effects/compressor.cpp b/al/effects/compressor.cpp index 868c5c1b..a4835178 100644 --- a/al/effects/compressor.cpp +++ b/al/effects/compressor.cpp @@ -7,7 +7,7 @@ #include "alc/effects/base.h" #include "effects.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "alnumeric.h" #include "al/eax_exception.h" @@ -78,10 +78,8 @@ DEFINE_ALEFFECT_VTABLE(Compressor); const EffectProps CompressorEffectProps{genDefaultProps()}; -#if ALSOFT_EAX -namespace -{ - +#ifdef ALSOFT_EAX +namespace { using EaxCompressorEffectDirtyFlagsValue = std::uint_least8_t; @@ -330,15 +328,12 @@ bool EaxCompressorEffect::set( return false; } - } // namespace - EaxEffectUPtr eax_create_eax_compressor_effect( EffectProps& al_effect_props) { return std::make_unique(al_effect_props); } - #endif // ALSOFT_EAX diff --git a/al/effects/distortion.cpp b/al/effects/distortion.cpp index 062cdc54..d0c9a3c2 100644 --- a/al/effects/distortion.cpp +++ b/al/effects/distortion.cpp @@ -7,7 +7,7 @@ #include "alc/effects/base.h" #include "effects.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "alnumeric.h" #include "al/eax_exception.h" @@ -120,10 +120,8 @@ DEFINE_ALEFFECT_VTABLE(Distortion); const EffectProps DistortionEffectProps{genDefaultProps()}; -#if ALSOFT_EAX -namespace -{ - +#ifdef ALSOFT_EAX +namespace { using EaxDistortionEffectDirtyFlagsValue = std::uint_least8_t; @@ -636,15 +634,12 @@ bool EaxDistortionEffect::set( return false; } - } // namespace - EaxEffectUPtr eax_create_eax_distortion_effect( EffectProps& al_effect_props) { return std::make_unique(al_effect_props); } - #endif // ALSOFT_EAX diff --git a/al/effects/echo.cpp b/al/effects/echo.cpp index 5ceb161d..7e7a38bc 100644 --- a/al/effects/echo.cpp +++ b/al/effects/echo.cpp @@ -7,7 +7,7 @@ #include "alc/effects/base.h" #include "effects.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "alnumeric.h" #include "al/eax_exception.h" @@ -117,10 +117,8 @@ DEFINE_ALEFFECT_VTABLE(Echo); const EffectProps EchoEffectProps{genDefaultProps()}; -#if ALSOFT_EAX -namespace -{ - +#ifdef ALSOFT_EAX +namespace { using EaxEchoEffectDirtyFlagsValue = std::uint_least8_t; @@ -631,15 +629,12 @@ bool EaxEchoEffect::set( return false; } - } // namespace - EaxEffectUPtr eax_create_eax_echo_effect( EffectProps& al_effect_props) { return std::make_unique(al_effect_props); } - #endif // ALSOFT_EAX diff --git a/al/effects/effects.cpp b/al/effects/effects.cpp index 55abfdc5..ede88f91 100644 --- a/al/effects/effects.cpp +++ b/al/effects/effects.cpp @@ -1,12 +1,14 @@ -#if ALSOFT_EAX +#include "config.h" + +#ifdef ALSOFT_EAX + +#include "effects.h" #include #include "AL/efx.h" -#include "effects.h" - EaxEffectUPtr eax_create_eax_null_effect(); @@ -102,5 +104,4 @@ EaxEffectUPtr eax_create_eax_effect( #undef EAX_PREFIX } - #endif // ALSOFT_EAX diff --git a/al/effects/effects.h b/al/effects/effects.h index 6813beaa..a2ab8485 100644 --- a/al/effects/effects.h +++ b/al/effects/effects.h @@ -5,7 +5,7 @@ #include "core/except.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "al/eax_effect.h" #endif // ALSOFT_EAX @@ -85,7 +85,7 @@ extern const EffectVtable DedicatedEffectVtable; extern const EffectVtable ConvolutionEffectVtable; -#if ALSOFT_EAX +#ifdef ALSOFT_EAX EaxEffectUPtr eax_create_eax_effect( ALenum al_effect_type, EffectProps& al_effect_props); diff --git a/al/effects/equalizer.cpp b/al/effects/equalizer.cpp index c052db3e..4ff26178 100644 --- a/al/effects/equalizer.cpp +++ b/al/effects/equalizer.cpp @@ -7,7 +7,7 @@ #include "alc/effects/base.h" #include "effects.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "alnumeric.h" #include "al/eax_exception.h" @@ -175,10 +175,8 @@ DEFINE_ALEFFECT_VTABLE(Equalizer); const EffectProps EqualizerEffectProps{genDefaultProps()}; -#if ALSOFT_EAX -namespace -{ - +#ifdef ALSOFT_EAX +namespace { using EaxEqualizerEffectDirtyFlagsValue = std::uint_least16_t; @@ -1021,15 +1019,12 @@ bool EaxEqualizerEffect::set( return false; } - } // namespace - EaxEffectUPtr eax_create_eax_equalizer_effect( EffectProps& al_effect_props) { return std::make_unique(al_effect_props); } - #endif // ALSOFT_EAX diff --git a/al/effects/fshifter.cpp b/al/effects/fshifter.cpp index aa4ddadb..74ef52d9 100644 --- a/al/effects/fshifter.cpp +++ b/al/effects/fshifter.cpp @@ -10,7 +10,7 @@ #include "aloptional.h" #include "effects.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include #include "alnumeric.h" @@ -138,10 +138,8 @@ DEFINE_ALEFFECT_VTABLE(Fshifter); const EffectProps FshifterEffectProps{genDefaultProps()}; -#if ALSOFT_EAX -namespace -{ - +#ifdef ALSOFT_EAX +namespace { using EaxFrequencyShifterEffectDirtyFlagsValue = std::uint_least8_t; @@ -530,15 +528,12 @@ bool EaxFrequencyShifterEffect::set( return false; } - } // namespace - EaxEffectUPtr eax_create_eax_frequency_shifter_effect( EffectProps& al_effect_props) { return std::make_unique(al_effect_props); } - #endif // ALSOFT_EAX diff --git a/al/effects/modulator.cpp b/al/effects/modulator.cpp index 6a30dc09..c33db8c3 100644 --- a/al/effects/modulator.cpp +++ b/al/effects/modulator.cpp @@ -10,7 +10,7 @@ #include "aloptional.h" #include "effects.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include #include "alnumeric.h" @@ -144,10 +144,8 @@ DEFINE_ALEFFECT_VTABLE(Modulator); const EffectProps ModulatorEffectProps{genDefaultProps()}; -#if ALSOFT_EAX -namespace -{ - +#ifdef ALSOFT_EAX +namespace { using EaxRingModulatorEffectDirtyFlagsValue = std::uint_least8_t; @@ -533,15 +531,12 @@ bool EaxRingModulatorEffect::set( return false; } - } // namespace - EaxEffectUPtr eax_create_eax_ring_modulator_effect( EffectProps& al_effect_props) { return std::make_unique(al_effect_props); } - #endif // ALSOFT_EAX diff --git a/al/effects/null.cpp b/al/effects/null.cpp index 44595208..8b68eec0 100644 --- a/al/effects/null.cpp +++ b/al/effects/null.cpp @@ -7,7 +7,7 @@ #include "alc/effects/base.h" #include "effects.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "al/eax_exception.h" #endif // ALSOFT_EAX @@ -97,10 +97,8 @@ DEFINE_ALEFFECT_VTABLE(Null); const EffectProps NullEffectProps{genDefaultProps()}; -#if ALSOFT_EAX -namespace -{ - +#ifdef ALSOFT_EAX +namespace { class EaxNullEffect final : public EaxEffect @@ -137,14 +135,11 @@ bool EaxNullEffect::dispatch( return false; } - } // namespace - EaxEffectUPtr eax_create_eax_null_effect() { return std::make_unique(); } - #endif // ALSOFT_EAX diff --git a/al/effects/pshifter.cpp b/al/effects/pshifter.cpp index 03f9a139..7c355be1 100644 --- a/al/effects/pshifter.cpp +++ b/al/effects/pshifter.cpp @@ -7,7 +7,7 @@ #include "alc/effects/base.h" #include "effects.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "alnumeric.h" #include "al/eax_exception.h" @@ -90,10 +90,8 @@ DEFINE_ALEFFECT_VTABLE(Pshifter); const EffectProps PshifterEffectProps{genDefaultProps()}; -#if ALSOFT_EAX -namespace -{ - +#ifdef ALSOFT_EAX +namespace { using EaxPitchShifterEffectDirtyFlagsValue = std::uint_least8_t; @@ -408,15 +406,12 @@ bool EaxPitchShifterEffect::set( return false; } - } // namespace - EaxEffectUPtr eax_create_eax_pitch_shifter_effect( EffectProps& al_effect_props) { return std::make_unique(al_effect_props); } - #endif // ALSOFT_EAX diff --git a/al/effects/reverb.cpp b/al/effects/reverb.cpp index 8012450d..a8404f8b 100644 --- a/al/effects/reverb.cpp +++ b/al/effects/reverb.cpp @@ -9,7 +9,7 @@ #include "alc/effects/base.h" #include "effects.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include #include "alnumeric.h" @@ -564,10 +564,8 @@ DEFINE_ALEFFECT_VTABLE(StdReverb); const EffectProps StdReverbEffectProps{genDefaultStdProps()}; -#if ALSOFT_EAX -namespace -{ - +#ifdef ALSOFT_EAX +namespace { using EaxReverbEffectDirtyFlagsValue = std::uint_least32_t; @@ -2464,15 +2462,12 @@ bool EaxReverbEffect::set( return false; } - } // namespace - EaxEffectUPtr eax_create_eax_reverb_effect( EffectProps& al_effect_props) { return std::make_unique(al_effect_props); } - #endif // ALSOFT_EAX diff --git a/al/effects/vmorpher.cpp b/al/effects/vmorpher.cpp index 2a9e0702..2ea2594e 100644 --- a/al/effects/vmorpher.cpp +++ b/al/effects/vmorpher.cpp @@ -10,7 +10,7 @@ #include "aloptional.h" #include "effects.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include #include "alnumeric.h" @@ -257,10 +257,8 @@ DEFINE_ALEFFECT_VTABLE(Vmorpher); const EffectProps VmorpherEffectProps{genDefaultProps()}; -#if ALSOFT_EAX -namespace -{ - +#ifdef ALSOFT_EAX +namespace { using EaxVocalMorpherEffectDirtyFlagsValue = std::uint_least8_t; @@ -859,7 +857,6 @@ bool EaxVocalMorpherEffect::set( return false; } - } // namespace @@ -869,5 +866,4 @@ EaxEffectUPtr eax_create_eax_vocal_morpher_effect( return std::make_unique(al_effect_props); } - #endif // ALSOFT_EAX diff --git a/al/extension.cpp b/al/extension.cpp index 373f87a9..6d9d181a 100644 --- a/al/extension.cpp +++ b/al/extension.cpp @@ -32,7 +32,7 @@ #include "core/except.h" #include "opthelpers.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "eax_globals.h" #include "eax_x_ram.h" #endif // ALSOFT_EAX @@ -47,7 +47,7 @@ START_API_FUNC SETERR_RETURN(context, AL_INVALID_VALUE, AL_FALSE, "NULL pointer"); size_t len{strlen(extName)}; -#if ALSOFT_EAX +#ifdef ALSOFT_EAX if (al::strncasecmp(eax_v2_0_ext_name, extName, len) == 0 || al::strncasecmp(eax_v3_0_ext_name, extName, len) == 0 || al::strncasecmp(eax_v4_0_ext_name, extName, len) == 0 || @@ -86,7 +86,7 @@ AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName) START_API_FUNC { if(!funcName) return nullptr; -#if ALSOFT_EAX +#ifdef ALSOFT_EAX if (al::strcasecmp(funcName, eax_eax_set_func_name) == 0) { if (!eax_g_is_enabled) @@ -163,7 +163,7 @@ AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName) START_API_FUNC { if(!enumName) return static_cast(0); -#if ALSOFT_EAX +#ifdef ALSOFT_EAX if (eax_g_is_enabled) { struct Descriptor diff --git a/al/filter.cpp b/al/filter.cpp index 563fb3ef..9989063b 100644 --- a/al/filter.cpp +++ b/al/filter.cpp @@ -45,7 +45,7 @@ #include "opthelpers.h" #include "vector.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "core/logging.h" #endif // ALSOFT_EAX @@ -723,7 +723,7 @@ FilterSubList::~FilterSubList() } -#if ALSOFT_EAX +#ifdef ALSOFT_EAX EaxAlFilterDeleter::EaxAlFilterDeleter( ALCcontext& context) : diff --git a/al/filter.h b/al/filter.h index 222a6917..98c32325 100644 --- a/al/filter.h +++ b/al/filter.h @@ -8,7 +8,7 @@ #include "almalloc.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include #include "eax_utils.h" @@ -54,7 +54,7 @@ struct ALfilter { DISABLE_ALLOC() -#if ALSOFT_EAX +#ifdef ALSOFT_EAX public: void eax_set_low_pass_params( ALCcontext& context, @@ -62,9 +62,8 @@ public: #endif // ALSOFT_EAX }; -#if ALSOFT_EAX -class EaxAlFilterDeleter -{ +#ifdef ALSOFT_EAX +class EaxAlFilterDeleter { public: EaxAlFilterDeleter() noexcept = default; diff --git a/al/listener.cpp b/al/listener.cpp index 1909c644..16c3f6a5 100644 --- a/al/listener.cpp +++ b/al/listener.cpp @@ -451,7 +451,7 @@ void UpdateListenerProps(ALCcontext *context) } } -#if ALSOFT_EAX +#ifdef ALSOFT_EAX // `alListenerf(AL_METERS_PER_UNIT, value)` void eax_set_al_listener_meters_per_unit( ALCcontext& al_context, diff --git a/al/listener.h b/al/listener.h index 05cd3abf..df20ee69 100644 --- a/al/listener.h +++ b/al/listener.h @@ -29,7 +29,7 @@ struct ALlistener { void UpdateListenerProps(ALCcontext *context); -#if ALSOFT_EAX +#ifdef ALSOFT_EAX // `alListenerf(AL_METERS_PER_UNIT, value)` void eax_set_al_listener_meters_per_unit( ALCcontext& al_context, diff --git a/al/source.cpp b/al/source.cpp index 3f7fa540..a306404c 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -72,7 +72,7 @@ #include "ringbuffer.h" #include "threads.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "eax_exception.h" #include "eax_globals.h" #endif // ALSOFT_EAX @@ -2419,7 +2419,7 @@ START_API_FUNC ALsource *source{AllocSource(context.get())}; sources[0] = source->id; -#if ALSOFT_EAX +#ifdef ALSOFT_EAX if (context->has_eax()) { std::unique_lock prop_lock{context->mPropLock}; @@ -2429,7 +2429,7 @@ START_API_FUNC } else { -#if ALSOFT_EAX +#ifdef ALSOFT_EAX auto eax_sources = al::vector{}; if (context->has_eax()) @@ -2444,7 +2444,7 @@ START_API_FUNC ALsource *source{AllocSource(context.get())}; ids.emplace_back(source->id); -#if ALSOFT_EAX +#ifdef ALSOFT_EAX if (context->has_eax()) { eax_sources.emplace_back(source); @@ -2453,7 +2453,7 @@ START_API_FUNC } while(--n); std::copy(ids.cbegin(), ids.cend(), sources); -#if ALSOFT_EAX +#ifdef ALSOFT_EAX if (context->has_eax()) { std::unique_lock prop_lock{context->mPropLock}; @@ -3595,7 +3595,7 @@ ALsource::ALsource() ALsource::~ALsource() { -#if ALSOFT_EAX +#ifdef ALSOFT_EAX eax_uninitialize(); #endif // ALSOFT_EAX @@ -3643,7 +3643,7 @@ SourceSubList::~SourceSubList() } -#if ALSOFT_EAX +#ifdef ALSOFT_EAX class EaxSourceException : public EaxException { @@ -6083,5 +6083,4 @@ void ALsource::eax_al_source_3i( SetSourceiv(this, eax_al_context_, static_cast(param), values); } - #endif // ALSOFT_EAX diff --git a/al/source.h b/al/source.h index 41cd6187..2fbb7b22 100644 --- a/al/source.h +++ b/al/source.h @@ -21,7 +21,7 @@ #include "core/voice.h" #include "vector.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "eax_eax_call.h" #include "eax_fx_slot_index.h" #include "eax_utils.h" @@ -47,11 +47,10 @@ struct ALbufferQueueItem : public VoiceBufferItem { }; -#if ALSOFT_EAX -struct EaxSourceInitParam -{ - ALCcontext* al_context{}; - ALfilter* al_filter{}; +#ifdef ALSOFT_EAX +struct EaxSourceInitParam { + ALCcontext* al_context{}; + ALfilter* al_filter{}; }; // EaxSourceInitParam @@ -213,7 +212,7 @@ struct ALsource { DISABLE_ALLOC() -#if ALSOFT_EAX +#ifdef ALSOFT_EAX public: void eax_initialize( const EaxSourceInitParam& param) noexcept; diff --git a/al/state.cpp b/al/state.cpp index 0ec0e280..bd62c4e3 100644 --- a/al/state.cpp +++ b/al/state.cpp @@ -46,7 +46,7 @@ #include "opthelpers.h" #include "strutils.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "alc/device.h" #include "eax_globals.h" @@ -434,7 +434,7 @@ START_API_FUNC value = static_cast(ResamplerDefault); break; -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #define EAX_ERROR "[alGetInteger] EAX not enabled." diff --git a/alc/alc.cpp b/alc/alc.cpp index b9f322a3..4effcc67 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -155,7 +155,7 @@ #include "backends/wave.h" #endif -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "al/eax_globals.h" #include "al/eax_x_ram.h" #endif // ALSOFT_EAX @@ -881,7 +881,7 @@ constexpr struct { DECL(AL_STOP_SOURCES_ON_DISCONNECT_SOFT), -#if ALSOFT_EAX +#ifdef ALSOFT_EAX DECL(AL_EAX_RAM_SIZE), DECL(AL_EAX_RAM_FREE), DECL(AL_STORAGE_AUTOMATIC), @@ -1258,7 +1258,7 @@ void alc_initconfig(void) if(defrevopt || (defrevopt=ConfigValueStr(nullptr, nullptr, "default-reverb"))) LoadReverbPreset(defrevopt->c_str(), &ALCcontext::sDefaultEffect); -#if ALSOFT_EAX +#ifdef ALSOFT_EAX { constexpr auto eax_block_name = "eax"; @@ -3223,7 +3223,7 @@ START_API_FUNC device->AuxiliaryEffectSlotMax = 64; device->NumAuxSends = DEFAULT_SENDS; -#if ALSOFT_EAX +#ifdef ALSOFT_EAX if (eax_g_is_enabled) { device->NumAuxSends = EAX_MAX_FXSLOTS; diff --git a/alc/context.cpp b/alc/context.cpp index d5fd94b2..33e9e7e0 100644 --- a/alc/context.cpp +++ b/alc/context.cpp @@ -29,7 +29,7 @@ #include "ringbuffer.h" #include "vecmat.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include #include @@ -129,7 +129,7 @@ ALCcontext::~ALCcontext() mSourceList.clear(); mNumSources = 0; -#if ALSOFT_EAX +#ifdef ALSOFT_EAX eax_uninitialize(); #endif // ALSOFT_EAX @@ -172,7 +172,7 @@ void ALCcontext::init() mExtensionList = alExtList; -#if ALSOFT_EAX +#ifdef ALSOFT_EAX eax_initialize_extensions(); #endif // ALSOFT_EAX @@ -274,10 +274,8 @@ void ALCcontext::processUpdates() } } -#if ALSOFT_EAX -namespace -{ - +#ifdef ALSOFT_EAX +namespace { class ContextException : public EaxException diff --git a/alc/context.h b/alc/context.h index b34807b8..b964d813 100644 --- a/alc/context.h +++ b/alc/context.h @@ -19,17 +19,15 @@ #include "intrusive_ptr.h" #include "vector.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "al/filter.h" #include "al/eax_eax_call.h" #include "al/eax_fx_slot_index.h" #include "al/eax_fx_slots.h" #include "al/eax_utils.h" -#endif // ALSOFT_EAX -#if ALSOFT_EAX using EaxContextSharedDirtyFlagsValue = std::uint_least8_t; struct EaxContextSharedDirtyFlags @@ -206,7 +204,7 @@ public: DEF_NEWDEL(ALCcontext) -#if ALSOFT_EAX +#ifdef ALSOFT_EAX public: bool has_eax() const noexcept; @@ -549,20 +547,20 @@ void UpdateContextProps(ALCcontext *context); extern bool TrapALError; -#if ALSOFT_EAX +#ifdef ALSOFT_EAX ALenum AL_APIENTRY EAXSet( - const GUID* property_set_id, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_value, - ALuint property_value_size) noexcept; + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_value, + ALuint property_value_size) noexcept; ALenum AL_APIENTRY EAXGet( - const GUID* property_set_id, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_value, - ALuint property_value_size) noexcept; + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_value, + ALuint property_value_size) noexcept; #endif // ALSOFT_EAX #endif /* ALC_CONTEXT_H */ diff --git a/alc/device.h b/alc/device.h index e4eb76ea..daade87a 100644 --- a/alc/device.h +++ b/alc/device.h @@ -18,7 +18,7 @@ #include "intrusive_ptr.h" #include "vector.h" -#if ALSOFT_EAX +#ifdef ALSOFT_EAX #include "al/eax_x_ram.h" #endif // ALSOFT_EAX @@ -110,7 +110,7 @@ struct ALCdevice : public al::intrusive_ref, DeviceBase { std::mutex FilterLock; al::vector FilterList; -#if ALSOFT_EAX +#ifdef ALSOFT_EAX ALsizei eax_x_ram_free_size{eax_x_ram_max_size}; #endif // ALSOFT_EAX diff --git a/common/alnumeric.h b/common/alnumeric.h index d72ba1e3..18df2689 100644 --- a/common/alnumeric.h +++ b/common/alnumeric.h @@ -1,10 +1,8 @@ #ifndef AL_NUMERIC_H #define AL_NUMERIC_H -#if ALSOFT_EAX +#include #include -#endif // ALSOFT_EAX - #include #include #ifdef HAVE_INTRIN_H @@ -275,45 +273,27 @@ inline float fast_roundf(float f) noexcept #endif } -#if ALSOFT_EAX -template< - typename T -> -inline constexpr const T& clamp( - const T& value, - const T& min_value, - const T& max_value) noexcept + +template +constexpr const T& clamp(const T& value, const T& min_value, const T& max_value) noexcept { - return value < min_value ? min_value : (value > max_value ? max_value : value); + return std::min(std::max(value, min_value), max_value); } // Converts level (mB) to gain. -inline float level_mb_to_gain( - float x) +inline float level_mb_to_gain(float x) { - if (x <= -10'000.0F) - { - return 0.0F; - } - else - { - return std::pow(10.0F, x / 2'000.0F); - } + if(x <= -10'000.0f) + return 0.0f; + return std::pow(10.0f, x / 2'000.0f); } // Converts gain to level (mB). -inline float gain_to_level_mb( - float x) +inline float gain_to_level_mb(float x) { - if (x <= 0.0F) - { - return -10'000.0F; - } - else - { - return std::log10(x * 2'000.0F); - } + if (x <= 0.0f) + return -10'000.0f; + return std::log10(x * 2'000.0f); } -#endif // ALSOFT_EAX #endif /* AL_NUMERIC_H */ diff --git a/config.h.in b/config.h.in index b06314a8..588d45a5 100644 --- a/config.h.in +++ b/config.h.in @@ -1,3 +1,6 @@ +/* Define if deprecated EAX extensions are enabled */ +#cmakedefine ALSOFT_EAX + /* Define if HRTF data is embedded in the library */ #cmakedefine ALSOFT_EMBED_HRTF_DATA -- cgit v1.2.3 From 96366c1fc142659a7a6da40d78403c30b3af4698 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 30 Jan 2022 05:46:29 -0800 Subject: Output a message when EAX is enabled in CMake --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 148090c3..a6df47d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1487,6 +1487,11 @@ if(ALSOFT_EMBED_HRTF_DATA) ") endif() +if(ALSOFT_EAX) + message(STATUS "Enabled deprecated EAX extensions +") +endif() + # Install main library if(ALSOFT_INSTALL) configure_package_config_file(OpenALConfig.cmake.in OpenALConfig.cmake -- cgit v1.2.3 From 071c9a6758b9c6810b81e60a3524b21684762f15 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 30 Jan 2022 05:46:29 -0800 Subject: Output a message when EAX is enabled in CMake --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index a6df47d3..d396e547 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1482,6 +1482,11 @@ if(FPMATH_SET) ") endif() +if(ALSOFT_EAX) + message(STATUS "Building with deprecated EAX extensions +") +endif() + if(ALSOFT_EMBED_HRTF_DATA) message(STATUS "Embedding HRTF datasets ") -- cgit v1.2.3 From 97dbd9a19149b4d71bf375d3354be6eccaa92f7b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 3 Feb 2022 00:27:48 -0800 Subject: Don't search for __android_log_print on non-Android targets For some reason this check is passing on iOS, causing the build to later fail because it can't find -llog. Need to investigate why it finds something that doesn't exist, but this should fix the build error in the mean time. --- CMakeLists.txt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index d396e547..3a8ae9b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -204,11 +204,13 @@ else() endif() unset(OLD_REQUIRED_LIBRARIES) -# Include liblog for Android logging -check_library_exists(log __android_log_print "" HAVE_LIBLOG) -if(HAVE_LIBLOG) - set(EXTRA_LIBS log ${EXTRA_LIBS}) - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} log) +if(ANDROID) + # Include liblog for Android logging + check_library_exists(log __android_log_print "" HAVE_LIBLOG) + if(HAVE_LIBLOG) + set(EXTRA_LIBS log ${EXTRA_LIBS}) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} log) + endif() endif() if(MSVC) -- cgit v1.2.3 From 2212b8d8ade3816d954d920a3e117d36931bcf3a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 14 Feb 2022 05:05:05 -0800 Subject: Don't use a generator expression in a CMake option --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a8ae9b5..1e086039 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,7 +95,7 @@ option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...) option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" ON) option(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON) -option(ALSOFT_EAX "Enable EAX extensions." $) +option(ALSOFT_EAX "Enable EAX extensions." ${WIN32}) if(DEFINED SHARE_INSTALL_DIR) message(WARNING "SHARE_INSTALL_DIR is deprecated. Use the variables provided by the GNUInstallDirs module instead") -- cgit v1.2.3 From 246412bfa6c7aa7329a3115afd26aff7709142b8 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 14 Feb 2022 14:22:45 -0800 Subject: Rename deprecated -> legacy --- CMakeLists.txt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e086039..ac50c0ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,7 +95,7 @@ option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...) option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" ON) option(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON) -option(ALSOFT_EAX "Enable EAX extensions." ${WIN32}) +option(ALSOFT_EAX "Enable legacy EAX extensions" ${WIN32}) if(DEFINED SHARE_INSTALL_DIR) message(WARNING "SHARE_INSTALL_DIR is deprecated. Use the variables provided by the GNUInstallDirs module instead") @@ -1485,7 +1485,7 @@ if(FPMATH_SET) endif() if(ALSOFT_EAX) - message(STATUS "Building with deprecated EAX extensions + message(STATUS "Building with legacy EAX extensions ") endif() @@ -1494,11 +1494,6 @@ if(ALSOFT_EMBED_HRTF_DATA) ") endif() -if(ALSOFT_EAX) - message(STATUS "Enabled deprecated EAX extensions -") -endif() - # Install main library if(ALSOFT_INSTALL) configure_package_config_file(OpenALConfig.cmake.in OpenALConfig.cmake -- cgit v1.2.3 From d3d9f8ca3df46aa2c74cb3eb4f598564e107a0d7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 22 Feb 2022 08:06:39 -0800 Subject: Clean up some cmake output formatting --- CMakeLists.txt | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index ac50c0ec..e926c1e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1472,26 +1472,26 @@ if(HAS_ROUTER) message(STATUS "Building DLL router") endif() -message(STATUS " -Building OpenAL with support for the following backends: - ${BACKENDS} - -Building with support for CPU extensions: - ${CPU_EXTS} -") +message(STATUS "") +message(STATUS "Building OpenAL with support for the following backends:") +message(STATUS " ${BACKENDS}") +message(STATUS "") +message(STATUS "Building with support for CPU extensions:") +message(STATUS " ${CPU_EXTS}") +message(STATUS "") if(FPMATH_SET) - message(STATUS "Building with SSE${FPMATH_SET} codegen -") + message(STATUS "Building with SSE${FPMATH_SET} codegen") + message(STATUS "") endif() if(ALSOFT_EAX) - message(STATUS "Building with legacy EAX extensions -") + message(STATUS "Building with legacy EAX extension support") + message(STATUS "") endif() if(ALSOFT_EMBED_HRTF_DATA) - message(STATUS "Embedding HRTF datasets -") + message(STATUS "Embedding HRTF datasets") + message(STATUS "") endif() # Install main library -- cgit v1.2.3 From d22699d9bd098a2800b8434a22d9e59209065de2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 22 Feb 2022 08:25:40 -0800 Subject: Add a OpenAL::OpenAL cmake target alias For when built as a sub-project, to clarify it's a target instead of a library. --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index e926c1e7..c9681de4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1494,6 +1494,9 @@ if(ALSOFT_EMBED_HRTF_DATA) message(STATUS "") endif() +# An alias for sub-project builds +add_library(OpenAL::OpenAL ALIAS OpenAL) + # Install main library if(ALSOFT_INSTALL) configure_package_config_file(OpenALConfig.cmake.in OpenALConfig.cmake -- cgit v1.2.3 From 625b0d380ad552692a79f3f69d0f286b90bc0e98 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 23 Feb 2022 05:56:29 -0800 Subject: Use function overloading to handle pthread_setname_np differences --- CMakeLists.txt | 40 ---------------------------------------- common/threads.cpp | 39 ++++++++++++++++++++++++++------------- config.h.in | 6 ------ 3 files changed, 26 insertions(+), 59 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index c9681de4..ad956b10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -566,51 +566,11 @@ if(NOT WIN32) check_symbol_exists(pthread_setname_np "pthread.h;pthread_np.h" HAVE_PTHREAD_SETNAME_NP) if(NOT HAVE_PTHREAD_SETNAME_NP) check_symbol_exists(pthread_set_name_np "pthread.h;pthread_np.h" HAVE_PTHREAD_SET_NAME_NP) - else() - check_c_source_compiles(" -#include -#include -int main() -{ - pthread_setname_np(\"testname\"); - return 0; -}" - PTHREAD_SETNAME_NP_ONE_PARAM - ) - check_c_source_compiles(" -#include -#include -int main() -{ - pthread_setname_np(pthread_self(), \"%s\", \"testname\"); - return 0; -}" - PTHREAD_SETNAME_NP_THREE_PARAMS - ) endif() else() check_symbol_exists(pthread_setname_np pthread.h HAVE_PTHREAD_SETNAME_NP) if(NOT HAVE_PTHREAD_SETNAME_NP) check_symbol_exists(pthread_set_name_np pthread.h HAVE_PTHREAD_SET_NAME_NP) - else() - check_c_source_compiles(" -#include -int main() -{ - pthread_setname_np(\"testname\"); - return 0; -}" - PTHREAD_SETNAME_NP_ONE_PARAM - ) - check_c_source_compiles(" -#include -int main() -{ - pthread_setname_np(pthread_self(), \"%s\", \"testname\"); - return 0; -}" - PTHREAD_SETNAME_NP_THREE_PARAMS - ) endif() endif() endif() diff --git a/common/threads.cpp b/common/threads.cpp index e847d1f8..c782dc35 100644 --- a/common/threads.cpp +++ b/common/threads.cpp @@ -90,30 +90,43 @@ bool semaphore::try_wait() noexcept #else -#if defined(HAVE_PTHREAD_SETNAME_NP) || defined(HAVE_PTHREAD_SET_NAME_NP) #include #ifdef HAVE_PTHREAD_NP_H #include #endif +#include + +namespace { + +using setname_t1 = int(*)(const char*); +using setname_t2 = int(*)(pthread_t, const char*); +using setname_t3 = int(*)(pthread_t, const char*, void*); + +void setname_caller(setname_t1 func, const char *name) +{ func(name); } + +void setname_caller(setname_t2 func, const char *name) +{ func(pthread_self(), name); } + +void setname_caller(setname_t3 func, const char *name) +{ func(pthread_self(), "%s", static_cast(const_cast(name))); } + +} // namespace void althrd_setname(const char *name) { #if defined(HAVE_PTHREAD_SET_NAME_NP) - pthread_set_name_np(pthread_self(), name); -#elif defined(PTHREAD_SETNAME_NP_ONE_PARAM) - pthread_setname_np(name); -#elif defined(PTHREAD_SETNAME_NP_THREE_PARAMS) - pthread_setname_np(pthread_self(), "%s", (void*)name); -#else - pthread_setname_np(pthread_self(), name); + setname_caller(pthread_set_name_np, name); +#elif defined(HAVE_PTHREAD_SETNAME_NP) + setname_caller(pthread_setname_np, name); #endif + /* Avoid unused function/parameter warnings. */ + std::ignore = name; + std::ignore = static_cast(&setname_caller); + std::ignore = static_cast(&setname_caller); + std::ignore = static_cast(&setname_caller); } -#else - -void althrd_setname(const char*) { } -#endif - #ifdef __APPLE__ namespace al { diff --git a/config.h.in b/config.h.in index 588d45a5..416b87d4 100644 --- a/config.h.in +++ b/config.h.in @@ -112,11 +112,5 @@ /* Define if we have pthread_setname_np() */ #cmakedefine HAVE_PTHREAD_SETNAME_NP -/* Define if pthread_setname_np() only accepts one parameter */ -#cmakedefine PTHREAD_SETNAME_NP_ONE_PARAM - -/* Define if pthread_setname_np() accepts three parameters */ -#cmakedefine PTHREAD_SETNAME_NP_THREE_PARAMS - /* Define if we have pthread_set_name_np() */ #cmakedefine HAVE_PTHREAD_SET_NAME_NP -- cgit v1.2.3 From bdf18d60db8d339c9fa3506d3eac259f9dd244fc Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 23 Mar 2022 23:40:54 -0700 Subject: Be more careful about checking compiler flags Certain combinations of MSVC and CMake seem to allow all compiler flag checks to pass, which was causing certain GCC attributes to be used on MSVC. --- CMakeLists.txt | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index ad956b10..4e3ca446 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -355,17 +355,17 @@ endif() set(SSE2_SWITCH "") -set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) if(NOT MSVC) + set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) # Yes GCC, really don't accept command line options you don't support set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Werror") + check_c_compiler_flag(-msse2 HAVE_MSSE2_SWITCH) + if(HAVE_MSSE2_SWITCH) + set(SSE2_SWITCH "-msse2") + endif() + set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS}) + unset(OLD_REQUIRED_FLAGS) endif() -check_c_compiler_flag(-msse2 HAVE_MSSE2_SWITCH) -if(HAVE_MSSE2_SWITCH) - set(SSE2_SWITCH "-msse2") -endif() -set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS}) -unset(OLD_REQUIRED_FLAGS) check_include_file(xmmintrin.h HAVE_XMMINTRIN_H) check_include_file(emmintrin.h HAVE_EMMINTRIN_H) @@ -450,7 +450,14 @@ set(FPMATH_SET "0") if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2) option(ALSOFT_ENABLE_SSE2_CODEGEN "Enable SSE2 code generation instead of x87 for 32-bit targets." TRUE) if(ALSOFT_ENABLE_SSE2_CODEGEN) - if(SSE2_SWITCH OR NOT MSVC) + if(MSVC) + check_c_compiler_flag("/arch:SSE2" HAVE_ARCH_SSE2) + if(HAVE_ARCH_SSE2) + set(SSE_FLAGS ${SSE_FLAGS} "/arch:SSE2") + set(C_FLAGS ${C_FLAGS} ${SSE_FLAGS}) + set(FPMATH_SET 2) + endif() + elseif(SSE2_SWITCH) check_c_compiler_flag("${SSE2_SWITCH} -mfpmath=sse" HAVE_MFPMATH_SSE_2) if(HAVE_MFPMATH_SSE_2) set(SSE_FLAGS ${SSE_FLAGS} ${SSE2_SWITCH} -mfpmath=sse) @@ -462,13 +469,6 @@ if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2) # OSs don't guarantee this on 32-bit, so externally-callable # functions need to ensure an aligned stack. set(EXPORT_DECL "${EXPORT_DECL} __attribute__((force_align_arg_pointer))") - elseif(MSVC) - check_c_compiler_flag("/arch:SSE2" HAVE_ARCH_SSE2) - if(HAVE_ARCH_SSE2) - set(SSE_FLAGS ${SSE_FLAGS} "/arch:SSE2") - set(C_FLAGS ${C_FLAGS} ${SSE_FLAGS}) - set(FPMATH_SET 2) - endif() endif() endif() endif() -- cgit v1.2.3 From c1c63a27de66cd44ef756b190a73bfa8bc6dbbab Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 22 Apr 2022 18:46:14 -0700 Subject: Release 1.22.0 --- CMakeLists.txt | 4 ++-- appveyor.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e3ca446..2edb30a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,8 +150,8 @@ if(NOT LIBTYPE) endif() set(LIB_MAJOR_VERSION "1") -set(LIB_MINOR_VERSION "21") -set(LIB_REVISION "1") +set(LIB_MINOR_VERSION "22") +set(LIB_REVISION "0") set(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}") set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0) diff --git a/appveyor.yml b/appveyor.yml index 2183b7b5..c468d4e9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 1.21.1.{build} +version: 1.22.0.{build} environment: APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 -- cgit v1.2.3 From 2ea5bb91686ee1a7350672bf15b7b63649dbf111 Mon Sep 17 00:00:00 2001 From: ilya-fedin Date: Tue, 26 Apr 2022 04:32:01 +0400 Subject: Fallback to pkg-config for dbus (#689) --- CMakeLists.txt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 2edb30a4..d52754c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -679,12 +679,21 @@ set(CORE_OBJS set(HAVE_RTKIT 0) option(ALSOFT_REQUIRE_RTKIT "Require RTKit/D-Bus support" FALSE) find_package(DBus1 QUIET) -if(DBus1_FOUND) +if(NOT DBus1_FOUND AND PkgConfig_FOUND) + pkg_check_modules(DBUS dbus-1) +endif() +if(DBus1_FOUND OR DBUS_FOUND) option(ALSOFT_RTKIT "Enable RTKit support" ON) if(ALSOFT_RTKIT) set(HAVE_RTKIT 1) set(CORE_OBJS ${CORE_OBJS} core/dbus_wrap.cpp core/dbus_wrap.h core/rtkit.cpp core/rtkit.h) - if(WIN32 OR HAVE_DLFCN_H) + if(NOT DBus1_FOUND) + set(INC_PATHS ${INC_PATHS} ${DBUS_INCLUDE_DIRS}) + set(CPP_DEFS ${CPP_DEFS} ${DBUS_CFLAGS_OTHER}) + if(NOT WIN32 AND NOT HAVE_DLFCN_H) + set(EXTRA_LIBS ${EXTRA_LIBS} ${DBUS_LINK_LIBRARIES}) + endif() + elseif(WIN32 OR HAVE_DLFCN_H) set(INC_PATHS ${INC_PATHS} ${DBus1_INCLUDE_DIRS}) set(CPP_DEFS ${CPP_DEFS} ${DBus1_DEFINITIONS}) else() -- cgit v1.2.3 From 22e6c0df6017aa86e2cd83d8073e4d490c55dd11 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 27 Apr 2022 01:25:15 -0700 Subject: Check for a minimum PipeWire version The backend is apparently using some stuff that wasn't in earlier headers, but it's not clear what was introduced in which versions. 0.3.23 should work, though it may need to be higher or it could go a bit lower. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index d52754c9..ea646617 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -912,7 +912,7 @@ endif() # Check PipeWire backend option(ALSOFT_REQUIRE_PIPEWIRE "Require PipeWire backend" OFF) if(PkgConfig_FOUND) - pkg_check_modules(PIPEWIRE libpipewire-0.3) + pkg_check_modules(PIPEWIRE libpipewire-0.3>=0.3.23) if(PIPEWIRE_FOUND) option(ALSOFT_BACKEND_PIPEWIRE "Enable PipeWire backend" ON) if(ALSOFT_BACKEND_PIPEWIRE) -- cgit v1.2.3 From c870e550fa37d10e8a9d7d15ef1d6370d1e0399e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 9 May 2022 12:38:12 -0700 Subject: Don't enable RTKit/D-Bus support on Windows --- CMakeLists.txt | 63 +++++++++++++++++++++++++++++--------------------------- core/helpers.cpp | 23 ++++++++++----------- 2 files changed, 44 insertions(+), 42 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index ea646617..d3945e95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -677,39 +677,42 @@ set(CORE_OBJS core/voice_change.h) set(HAVE_RTKIT 0) -option(ALSOFT_REQUIRE_RTKIT "Require RTKit/D-Bus support" FALSE) -find_package(DBus1 QUIET) -if(NOT DBus1_FOUND AND PkgConfig_FOUND) - pkg_check_modules(DBUS dbus-1) -endif() -if(DBus1_FOUND OR DBUS_FOUND) - option(ALSOFT_RTKIT "Enable RTKit support" ON) - if(ALSOFT_RTKIT) - set(HAVE_RTKIT 1) - set(CORE_OBJS ${CORE_OBJS} core/dbus_wrap.cpp core/dbus_wrap.h core/rtkit.cpp core/rtkit.h) - if(NOT DBus1_FOUND) - set(INC_PATHS ${INC_PATHS} ${DBUS_INCLUDE_DIRS}) - set(CPP_DEFS ${CPP_DEFS} ${DBUS_CFLAGS_OTHER}) - if(NOT WIN32 AND NOT HAVE_DLFCN_H) - set(EXTRA_LIBS ${EXTRA_LIBS} ${DBUS_LINK_LIBRARIES}) +if(NOT WIN32) + option(ALSOFT_REQUIRE_RTKIT "Require RTKit/D-Bus support" FALSE) + find_package(DBus1 QUIET) + if(NOT DBus1_FOUND AND PkgConfig_FOUND) + pkg_check_modules(DBUS dbus-1) + endif() + if(DBus1_FOUND OR DBUS_FOUND) + option(ALSOFT_RTKIT "Enable RTKit support" ON) + if(ALSOFT_RTKIT) + set(HAVE_RTKIT 1) + set(CORE_OBJS ${CORE_OBJS} core/dbus_wrap.cpp core/dbus_wrap.h + core/rtkit.cpp core/rtkit.h) + if(NOT DBus1_FOUND) + set(INC_PATHS ${INC_PATHS} ${DBUS_INCLUDE_DIRS}) + set(CPP_DEFS ${CPP_DEFS} ${DBUS_CFLAGS_OTHER}) + if(NOT HAVE_DLFCN_H) + set(EXTRA_LIBS ${EXTRA_LIBS} ${DBUS_LINK_LIBRARIES}) + endif() + elseif(HAVE_DLFCN_H) + set(INC_PATHS ${INC_PATHS} ${DBus1_INCLUDE_DIRS}) + set(CPP_DEFS ${CPP_DEFS} ${DBus1_DEFINITIONS}) + else() + set(EXTRA_LIBS ${EXTRA_LIBS} ${DBus1_LIBRARIES}) endif() - elseif(WIN32 OR HAVE_DLFCN_H) - set(INC_PATHS ${INC_PATHS} ${DBus1_INCLUDE_DIRS}) - set(CPP_DEFS ${CPP_DEFS} ${DBus1_DEFINITIONS}) - else() - set(EXTRA_LIBS ${EXTRA_LIBS} ${DBus1_LIBRARIES}) endif() + else() + set(MISSING_VARS "") + if(NOT DBus1_INCLUDE_DIRS) + set(MISSING_VARS "${MISSING_VARS} DBus1_INCLUDE_DIRS") + endif() + if(NOT DBus1_LIBRARIES) + set(MISSING_VARS "${MISSING_VARS} DBus1_LIBRARIES") + endif() + message(STATUS "Could NOT find DBus1 (missing:${MISSING_VARS})") + unset(MISSING_VARS) endif() -else() - set(MISSING_VARS "") - if(NOT DBus1_INCLUDE_DIRS) - set(MISSING_VARS "${MISSING_VARS} DBus1_INCLUDE_DIRS") - endif() - if(NOT DBus1_LIBRARIES) - set(MISSING_VARS "${MISSING_VARS} DBus1_LIBRARIES") - endif() - message(STATUS "Could NOT find DBus1 (missing:${MISSING_VARS})") - unset(MISSING_VARS) endif() if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT) message(FATAL_ERROR "Failed to enabled required RTKit support") diff --git a/core/helpers.cpp b/core/helpers.cpp index e4a94fe5..c7e45a8b 100644 --- a/core/helpers.cpp +++ b/core/helpers.cpp @@ -461,6 +461,17 @@ bool SetRTPriorityRTKit(int prio) /* Don't stupidly exit if the connection dies while doing this. */ dbus_connection_set_exit_on_disconnect(conn.get(), false); + int nicemin{}; + int err{rtkit_get_min_nice_level(conn.get(), &nicemin)}; + if(err == -ENOENT) + { + err = std::abs(err); + ERR("Could not query RTKit: %s (%d)\n", std::strerror(err), err); + return false; + } + int rtmax{rtkit_get_max_realtime_priority(conn.get())}; + TRACE("Maximum real-time priority: %d, minimum niceness: %d\n", rtmax, nicemin); + auto limit_rttime = [](DBusConnection *c) -> int { using ulonglong = unsigned long long; @@ -483,18 +494,6 @@ bool SetRTPriorityRTKit(int prio) } return 0; }; - - int nicemin{}; - int err{rtkit_get_min_nice_level(conn.get(), &nicemin)}; - if(err == -ENOENT) - { - err = std::abs(err); - ERR("Could not query RTKit: %s (%d)\n", std::strerror(err), err); - return false; - } - int rtmax{rtkit_get_max_realtime_priority(conn.get())}; - TRACE("Maximum real-time priority: %d, minimum niceness: %d\n", rtmax, nicemin); - if(rtmax > 0) { if(AllowRTTimeLimit) -- cgit v1.2.3 From e0d26ba25ab84f5a04a6ad2ef497cd0e93684a40 Mon Sep 17 00:00:00 2001 From: ilya-fedin Date: Sat, 14 May 2022 00:55:17 +0400 Subject: Search the installation data directory (#693) That allows the logic to work on non-FHS distros like NixOS --- CMakeLists.txt | 5 +++++ config.h.in | 3 +++ core/helpers.cpp | 14 ++++++++++++++ 3 files changed, 22 insertions(+) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index d3945e95..ebfcc4e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,11 @@ option(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON) option(ALSOFT_EAX "Enable legacy EAX extensions" ${WIN32}) +option(ALSOFT_SEARCH_INSTALL_DATADIR "Search the installation data directory" OFF) +if(ALSOFT_SEARCH_INSTALL_DATADIR) + set(ALSOFT_INSTALL_DATADIR ${CMAKE_INSTALL_FULL_DATADIR}) +endif() + if(DEFINED SHARE_INSTALL_DIR) message(WARNING "SHARE_INSTALL_DIR is deprecated. Use the variables provided by the GNUInstallDirs module instead") set(CMAKE_INSTALL_DATADIR "${SHARE_INSTALL_DIR}") diff --git a/config.h.in b/config.h.in index 416b87d4..477d8c77 100644 --- a/config.h.in +++ b/config.h.in @@ -114,3 +114,6 @@ /* Define if we have pthread_set_name_np() */ #cmakedefine HAVE_PTHREAD_SET_NAME_NP + +/* Define the installation data directory */ +#cmakedefine ALSOFT_INSTALL_DATADIR "@ALSOFT_INSTALL_DATADIR@" diff --git a/core/helpers.cpp b/core/helpers.cpp index c7e45a8b..6d0863ca 100644 --- a/core/helpers.cpp +++ b/core/helpers.cpp @@ -408,6 +408,20 @@ al::vector SearchDataFiles(const char *ext, const char *subdir) DirectorySearch(path.c_str(), ext, &results); } +#ifdef ALSOFT_INSTALL_DATADIR + // Search the installation data directory + { + std::string path{ALSOFT_INSTALL_DATADIR}; + if(!path.empty()) + { + if(path.back() != '/') + path += '/'; + path += subdir; + DirectorySearch(path.c_str(), ext, &results); + } + } +#endif + return results; } -- cgit v1.2.3 From 0bf2abae9b2121c3bc5a56dab30eca308136bc29 Mon Sep 17 00:00:00 2001 From: ilya-fedin Date: Sat, 14 May 2022 02:39:43 +0400 Subject: Fix pkg-config file when libdir/bindir/includedir specified as absolute paths (#696) Currently it makes the pkg-config file unusable as prefix is specified twice. FULL variables do the necessary parsing and automatically prepend the prefix if needed. --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index ebfcc4e6..b1c7ede0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1261,9 +1261,9 @@ endif() # Needed for openal.pc.in set(prefix ${CMAKE_INSTALL_PREFIX}) set(exec_prefix "\${prefix}") -set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") -set(bindir "\${exec_prefix}/${CMAKE_INSTALL_BINDIR}") -set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") +set(libdir "${CMAKE_INSTALL_FULL_LIBDIR}") +set(bindir "${CMAKE_INSTALL_FULL_BINDIR}") +set(includedir "${CMAKE_INSTALL_FULL_INCLUDEDIR}") set(PACKAGE_VERSION "${LIB_VERSION}") set(PKG_CONFIG_CFLAGS ) set(PKG_CONFIG_PRIVATE_LIBS ) -- cgit v1.2.3 From 65e4c20c27f2acf853e58fd4c26ebc0e3eb926c6 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 16 May 2022 02:08:18 -0700 Subject: Move EAX files to their own sub-directory --- CMakeLists.txt | 36 +- al/auxeffectslot.cpp | 4 +- al/auxeffectslot.h | 6 +- al/buffer.cpp | 4 +- al/buffer.h | 2 +- al/eax/api.cpp | 1213 +++++++++++++++++++++++++++++++++++ al/eax/api.h | 1557 +++++++++++++++++++++++++++++++++++++++++++++ al/eax/eax_call.cpp | 323 ++++++++++ al/eax/eax_call.h | 117 ++++ al/eax/effect.cpp | 3 + al/eax/effect.h | 44 ++ al/eax/exception.cpp | 62 ++ al/eax/exception.h | 25 + al/eax/fx_slot_index.cpp | 71 +++ al/eax/fx_slot_index.h | 41 ++ al/eax/fx_slots.cpp | 83 +++ al/eax/fx_slots.h | 53 ++ al/eax/globals.cpp | 21 + al/eax/globals.h | 22 + al/eax/utils.cpp | 36 ++ al/eax/utils.h | 132 ++++ al/eax/x_ram.cpp | 3 + al/eax/x_ram.h | 38 ++ al/eax_api.cpp | 1213 ----------------------------------- al/eax_api.h | 1557 --------------------------------------------- al/eax_eax_call.cpp | 324 ---------- al/eax_eax_call.h | 117 ---- al/eax_effect.cpp | 3 - al/eax_effect.h | 44 -- al/eax_exception.cpp | 63 -- al/eax_exception.h | 25 - al/eax_fx_slot_index.cpp | 71 --- al/eax_fx_slot_index.h | 41 -- al/eax_fx_slots.cpp | 84 --- al/eax_fx_slots.h | 54 -- al/eax_globals.cpp | 21 - al/eax_globals.h | 22 - al/eax_utils.cpp | 36 -- al/eax_utils.h | 132 ---- al/eax_x_ram.cpp | 3 - al/eax_x_ram.h | 38 -- al/effect.cpp | 2 +- al/effects/autowah.cpp | 4 +- al/effects/chorus.cpp | 4 +- al/effects/compressor.cpp | 4 +- al/effects/distortion.cpp | 4 +- al/effects/echo.cpp | 4 +- al/effects/effects.h | 2 +- al/effects/equalizer.cpp | 4 +- al/effects/fshifter.cpp | 4 +- al/effects/modulator.cpp | 4 +- al/effects/null.cpp | 2 +- al/effects/pshifter.cpp | 4 +- al/effects/reverb.cpp | 9 +- al/effects/vmorpher.cpp | 4 +- al/source.cpp | 2 +- al/source.h | 6 +- al/state.cpp | 4 +- alc/alc.cpp | 4 +- alc/context.cpp | 4 +- alc/context.h | 8 +- alc/device.h | 2 +- 62 files changed, 3914 insertions(+), 3915 deletions(-) create mode 100644 al/eax/api.cpp create mode 100644 al/eax/api.h create mode 100644 al/eax/eax_call.cpp create mode 100644 al/eax/eax_call.h create mode 100644 al/eax/effect.cpp create mode 100644 al/eax/effect.h create mode 100644 al/eax/exception.cpp create mode 100644 al/eax/exception.h create mode 100644 al/eax/fx_slot_index.cpp create mode 100644 al/eax/fx_slot_index.h create mode 100644 al/eax/fx_slots.cpp create mode 100644 al/eax/fx_slots.h create mode 100644 al/eax/globals.cpp create mode 100644 al/eax/globals.h create mode 100644 al/eax/utils.cpp create mode 100644 al/eax/utils.h create mode 100644 al/eax/x_ram.cpp create mode 100644 al/eax/x_ram.h delete mode 100644 al/eax_api.cpp delete mode 100644 al/eax_api.h delete mode 100644 al/eax_eax_call.cpp delete mode 100644 al/eax_eax_call.h delete mode 100644 al/eax_effect.cpp delete mode 100644 al/eax_effect.h delete mode 100644 al/eax_exception.cpp delete mode 100644 al/eax_exception.h delete mode 100644 al/eax_fx_slot_index.cpp delete mode 100644 al/eax_fx_slot_index.h delete mode 100644 al/eax_fx_slots.cpp delete mode 100644 al/eax_fx_slots.h delete mode 100644 al/eax_globals.cpp delete mode 100644 al/eax_globals.h delete mode 100644 al/eax_utils.cpp delete mode 100644 al/eax_utils.h delete mode 100644 al/eax_x_ram.cpp delete mode 100644 al/eax_x_ram.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index b1c7ede0..52d61ff0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -798,24 +798,24 @@ set(ALC_OBJS if (ALSOFT_EAX) set(OPENAL_OBJS ${OPENAL_OBJS} - al/eax_api.cpp - al/eax_api.h - al/eax_eax_call.cpp - al/eax_eax_call.h - al/eax_effect.cpp - al/eax_effect.h - al/eax_exception.cpp - al/eax_exception.h - al/eax_fx_slot_index.cpp - al/eax_fx_slot_index.h - al/eax_fx_slots.cpp - al/eax_fx_slots.h - al/eax_globals.cpp - al/eax_globals.h - al/eax_utils.cpp - al/eax_utils.h - al/eax_x_ram.cpp - al/eax_x_ram.h + al/eax/api.cpp + al/eax/api.h + al/eax/eax_call.cpp + al/eax/eax_call.h + al/eax/effect.cpp + al/eax/effect.h + al/eax/exception.cpp + al/eax/exception.h + al/eax/fx_slot_index.cpp + al/eax/fx_slot_index.h + al/eax/fx_slots.cpp + al/eax/fx_slots.h + al/eax/globals.cpp + al/eax/globals.h + al/eax/utils.cpp + al/eax/utils.h + al/eax/x_ram.cpp + al/eax/x_ram.h ) endif () diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp index c33fb149..455a1072 100644 --- a/al/auxeffectslot.cpp +++ b/al/auxeffectslot.cpp @@ -51,8 +51,8 @@ #include "opthelpers.h" #ifdef ALSOFT_EAX -#include "eax_exception.h" -#include "eax_utils.h" +#include "eax/exception.h" +#include "eax/utils.h" #endif // ALSOFT_EAX namespace { diff --git a/al/auxeffectslot.h b/al/auxeffectslot.h index ca0dcd31..5336a2a8 100644 --- a/al/auxeffectslot.h +++ b/al/auxeffectslot.h @@ -19,9 +19,9 @@ #ifdef ALSOFT_EAX #include -#include "eax_eax_call.h" -#include "eax_effect.h" -#include "eax_fx_slot_index.h" +#include "eax/eax_call.h" +#include "eax/effect.h" +#include "eax/fx_slot_index.h" #endif // ALSOFT_EAX struct ALbuffer; diff --git a/al/buffer.cpp b/al/buffer.cpp index 13407103..9b455b9c 100644 --- a/al/buffer.cpp +++ b/al/buffer.cpp @@ -57,8 +57,8 @@ #include "opthelpers.h" #ifdef ALSOFT_EAX -#include "eax_globals.h" -#include "eax_x_ram.h" +#include "eax/globals.h" +#include "eax/x_ram.h" #endif // ALSOFT_EAX diff --git a/al/buffer.h b/al/buffer.h index b3a0f0d8..7ded83bd 100644 --- a/al/buffer.h +++ b/al/buffer.h @@ -13,7 +13,7 @@ #include "vector.h" #ifdef ALSOFT_EAX -#include "eax_x_ram.h" +#include "eax/x_ram.h" #endif // ALSOFT_EAX /* User formats */ diff --git a/al/eax/api.cpp b/al/eax/api.cpp new file mode 100644 index 00000000..f859a1c4 --- /dev/null +++ b/al/eax/api.cpp @@ -0,0 +1,1213 @@ +// +// EAX API. +// +// Based on headers `eax[2-5].h` included in Doom 3 source code: +// https://github.com/id-Software/DOOM-3/tree/master/neo/openal/include +// + +#include "config.h" + +#include + +#include "api.h" + + +const GUID DSPROPSETID_EAX_ReverbProperties = +{ + 0x4A4E6FC1, + 0xC341, + 0x11D1, + {0xB7, 0x3A, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} +}; + +const GUID DSPROPSETID_EAXBUFFER_ReverbProperties = +{ + 0x4A4E6FC0, + 0xC341, + 0x11D1, + {0xB7, 0x3A, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} +}; + +const GUID DSPROPSETID_EAX20_ListenerProperties = +{ + 0x306A6A8, + 0xB224, + 0x11D2, + {0x99, 0xE5, 0x00, 0x00, 0xE8, 0xD8, 0xC7, 0x22} +}; + +const GUID DSPROPSETID_EAX20_BufferProperties = +{ + 0x306A6A7, + 0xB224, + 0x11D2, + {0x99, 0xE5, 0x00, 0x00, 0xE8, 0xD8, 0xC7, 0x22} +}; + +const GUID DSPROPSETID_EAX30_ListenerProperties = +{ + 0xA8FA6882, + 0xB476, + 0x11D3, + {0xBD, 0xB9, 0x00, 0xC0, 0xF0, 0x2D, 0xDF, 0x87} +}; + +const GUID DSPROPSETID_EAX30_BufferProperties = +{ + 0xA8FA6881, + 0xB476, + 0x11D3, + {0xBD, 0xB9, 0x00, 0xC0, 0xF0, 0x2D, 0xDF, 0x87} +}; + +const GUID EAX_NULL_GUID = +{ + 0x00000000, + 0x0000, + 0x0000, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +const GUID EAX_PrimaryFXSlotID = +{ + 0xF317866D, + 0x924C, + 0x450C, + {0x86, 0x1B, 0xE6, 0xDA, 0xA2, 0x5E, 0x7C, 0x20} +}; + +const GUID EAXPROPERTYID_EAX40_Context = +{ + 0x1D4870AD, + 0xDEF, + 0x43C0, + {0xA4, 0xC, 0x52, 0x36, 0x32, 0x29, 0x63, 0x42} +}; + +const GUID EAXPROPERTYID_EAX50_Context = +{ + 0x57E13437, + 0xB932, + 0x4AB2, + {0xB8, 0xBD, 0x52, 0x66, 0xC1, 0xA8, 0x87, 0xEE} +}; + +const GUID EAXPROPERTYID_EAX40_FXSlot0 = +{ + 0xC4D79F1E, + 0xF1AC, + 0x436B, + {0xA8, 0x1D, 0xA7, 0x38, 0xE7, 0x04, 0x54, 0x69} +}; + +const GUID EAXPROPERTYID_EAX50_FXSlot0 = +{ + 0x91F9590F, + 0xC388, + 0x407A, + {0x84, 0xB0, 0x1B, 0xAE, 0xE, 0xF7, 0x1A, 0xBC} +}; + +const GUID EAXPROPERTYID_EAX40_FXSlot1 = +{ + 0x8C00E96, + 0x74BE, + 0x4491, + {0x93, 0xAA, 0xE8, 0xAD, 0x35, 0xA4, 0x91, 0x17} +}; + +const GUID EAXPROPERTYID_EAX50_FXSlot1 = +{ + 0x8F5F7ACA, + 0x9608, + 0x4965, + {0x81, 0x37, 0x82, 0x13, 0xC7, 0xB9, 0xD9, 0xDE} +}; + +const GUID EAXPROPERTYID_EAX40_FXSlot2 = +{ + 0x1D433B88, + 0xF0F6, + 0x4637, + {0x91, 0x9F, 0x60, 0xE7, 0xE0, 0x6B, 0x5E, 0xDD} +}; + +const GUID EAXPROPERTYID_EAX50_FXSlot2 = +{ + 0x3C0F5252, + 0x9834, + 0x46F0, + {0xA1, 0xD8, 0x5B, 0x95, 0xC4, 0xA0, 0xA, 0x30} +}; + +const GUID EAXPROPERTYID_EAX40_FXSlot3 = +{ + 0xEFFF08EA, + 0xC7D8, + 0x44AB, + {0x93, 0xAD, 0x6D, 0xBD, 0x5F, 0x91, 0x00, 0x64} +}; + +const GUID EAXPROPERTYID_EAX50_FXSlot3 = +{ + 0xE2EB0EAA, + 0xE806, + 0x45E7, + {0x9F, 0x86, 0x06, 0xC1, 0x57, 0x1A, 0x6F, 0xA3} +}; + +const GUID EAXPROPERTYID_EAX40_Source = +{ + 0x1B86B823, + 0x22DF, + 0x4EAE, + {0x8B, 0x3C, 0x12, 0x78, 0xCE, 0x54, 0x42, 0x27} +}; + +const GUID EAXPROPERTYID_EAX50_Source = +{ + 0x5EDF82F0, + 0x24A7, + 0x4F38, + {0x8E, 0x64, 0x2F, 0x09, 0xCA, 0x05, 0xDE, 0xE1} +}; + +const GUID EAX_REVERB_EFFECT = +{ + 0xCF95C8F, + 0xA3CC, + 0x4849, + {0xB0, 0xB6, 0x83, 0x2E, 0xCC, 0x18, 0x22, 0xDF} +}; + +const GUID EAX_AGCCOMPRESSOR_EFFECT = +{ + 0xBFB7A01E, + 0x7825, + 0x4039, + {0x92, 0x7F, 0x03, 0xAA, 0xBD, 0xA0, 0xC5, 0x60} +}; + +const GUID EAX_AUTOWAH_EFFECT = +{ + 0xEC3130C0, + 0xAC7A, + 0x11D2, + {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + +const GUID EAX_CHORUS_EFFECT = +{ + 0xDE6D6FE0, + 0xAC79, + 0x11D2, + {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + +const GUID EAX_DISTORTION_EFFECT = +{ + 0x975A4CE0, + 0xAC7E, + 0x11D2, + {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + +const GUID EAX_ECHO_EFFECT = +{ + 0xE9F1BC0, + 0xAC82, + 0x11D2, + {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + +const GUID EAX_EQUALIZER_EFFECT = +{ + 0x65F94CE0, + 0x9793, + 0x11D3, + {0x93, 0x9D, 0x00, 0xC0, 0xF0, 0x2D, 0xD6, 0xF0} +}; + +const GUID EAX_FLANGER_EFFECT = +{ + 0xA70007C0, + 0x7D2, + 0x11D3, + {0x9B, 0x1E, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + +const GUID EAX_FREQUENCYSHIFTER_EFFECT = +{ + 0xDC3E1880, + 0x9212, + 0x11D3, + {0x93, 0x9D, 0x00, 0xC0, 0xF0, 0x2D, 0xD6, 0xF0} +}; + +const GUID EAX_VOCALMORPHER_EFFECT = +{ + 0xE41CF10C, + 0x3383, + 0x11D2, + {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + +const GUID EAX_PITCHSHIFTER_EFFECT = +{ + 0xE7905100, + 0xAFB2, + 0x11D2, + {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + +const GUID EAX_RINGMODULATOR_EFFECT = +{ + 0xB89FE60, + 0xAFB5, + 0x11D2, + {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} +}; + + +bool operator==( + const EAX40CONTEXTPROPERTIES& lhs, + const EAX40CONTEXTPROPERTIES& rhs) noexcept +{ + return + lhs.guidPrimaryFXSlotID == rhs.guidPrimaryFXSlotID && + lhs.flDistanceFactor == rhs.flDistanceFactor && + lhs.flAirAbsorptionHF == rhs.flAirAbsorptionHF && + lhs.flHFReference == rhs.flHFReference; +} + +bool operator==( + const EAX50CONTEXTPROPERTIES& lhs, + const EAX50CONTEXTPROPERTIES& rhs) noexcept +{ + return + static_cast(lhs) == static_cast(rhs) && + lhs.flMacroFXFactor == rhs.flMacroFXFactor; +} + + +const GUID EAXCONTEXT_DEFAULTPRIMARYFXSLOTID = EAXPROPERTYID_EAX40_FXSlot0; + +bool operator==( + const EAX40FXSLOTPROPERTIES& lhs, + const EAX40FXSLOTPROPERTIES& rhs) noexcept +{ + return + lhs.guidLoadEffect == rhs.guidLoadEffect && + lhs.lVolume == rhs.lVolume && + lhs.lLock == rhs.lLock && + lhs.ulFlags == rhs.ulFlags; +} + +bool operator==( + const EAX50FXSLOTPROPERTIES& lhs, + const EAX50FXSLOTPROPERTIES& rhs) noexcept +{ + return + static_cast(lhs) == static_cast(rhs) && + lhs.lOcclusion == rhs.lOcclusion && + lhs.flOcclusionLFRatio == rhs.flOcclusionLFRatio; +} + +const EAX50ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID = EAX50ACTIVEFXSLOTS +{{ + EAX_NULL_GUID, + EAXPROPERTYID_EAX40_FXSlot0, +}}; + +bool operator==( + const EAX50ACTIVEFXSLOTS& lhs, + const EAX50ACTIVEFXSLOTS& rhs) noexcept +{ + return std::equal( + std::cbegin(lhs.guidActiveFXSlots), + std::cend(lhs.guidActiveFXSlots), + std::begin(rhs.guidActiveFXSlots)); +} + +bool operator!=( + const EAX50ACTIVEFXSLOTS& lhs, + const EAX50ACTIVEFXSLOTS& rhs) noexcept +{ + return !(lhs == rhs); +} + + +const EAX50ACTIVEFXSLOTS EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID = EAX50ACTIVEFXSLOTS +{{ + EAX_NULL_GUID, + EAX_PrimaryFXSlotID, + EAX_NULL_GUID, + EAX_NULL_GUID, +}}; + + +const EAX50ACTIVEFXSLOTS EAX50SOURCE_2DDEFAULTACTIVEFXSLOTID = EAX50ACTIVEFXSLOTS +{{ + EAX_NULL_GUID, + EAX_NULL_GUID, + EAX_NULL_GUID, + EAX_NULL_GUID, +}}; + +bool operator==( + const EAXREVERBPROPERTIES& lhs, + const EAXREVERBPROPERTIES& rhs) noexcept +{ + return + lhs.ulEnvironment == rhs.ulEnvironment && + lhs.flEnvironmentSize == rhs.flEnvironmentSize && + lhs.flEnvironmentDiffusion == rhs.flEnvironmentDiffusion && + lhs.lRoom == rhs.lRoom && + lhs.lRoomHF == rhs.lRoomHF && + lhs.lRoomLF == rhs.lRoomLF && + lhs.flDecayTime == rhs.flDecayTime && + lhs.flDecayHFRatio == rhs.flDecayHFRatio && + lhs.flDecayLFRatio == rhs.flDecayLFRatio && + lhs.lReflections == rhs.lReflections && + lhs.flReflectionsDelay == rhs.flReflectionsDelay && + lhs.vReflectionsPan == rhs.vReflectionsPan && + lhs.lReverb == rhs.lReverb && + lhs.flReverbDelay == rhs.flReverbDelay && + lhs.vReverbPan == rhs.vReverbPan && + lhs.flEchoTime == rhs.flEchoTime && + lhs.flEchoDepth == rhs.flEchoDepth && + lhs.flModulationTime == rhs.flModulationTime && + lhs.flModulationDepth == rhs.flModulationDepth && + lhs.flAirAbsorptionHF == rhs.flAirAbsorptionHF && + lhs.flHFReference == rhs.flHFReference && + lhs.flLFReference == rhs.flLFReference && + lhs.flRoomRolloffFactor == rhs.flRoomRolloffFactor && + lhs.ulFlags == rhs.ulFlags; +} + +bool operator!=( + const EAXREVERBPROPERTIES& lhs, + const EAXREVERBPROPERTIES& rhs) noexcept +{ + return !(lhs == rhs); +} + + +namespace { + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_GENERIC = +{ + EAXREVERB_DEFAULTENVIRONMENT, + EAXREVERB_DEFAULTENVIRONMENTSIZE, + EAXREVERB_DEFAULTENVIRONMENTDIFFUSION, + EAXREVERB_DEFAULTROOM, + EAXREVERB_DEFAULTROOMHF, + EAXREVERB_DEFAULTROOMLF, + EAXREVERB_DEFAULTDECAYTIME, + EAXREVERB_DEFAULTDECAYHFRATIO, + EAXREVERB_DEFAULTDECAYLFRATIO, + EAXREVERB_DEFAULTREFLECTIONS, + EAXREVERB_DEFAULTREFLECTIONSDELAY, + EAXREVERB_DEFAULTREFLECTIONSPAN, + EAXREVERB_DEFAULTREVERB, + EAXREVERB_DEFAULTREVERBDELAY, + EAXREVERB_DEFAULTREVERBPAN, + EAXREVERB_DEFAULTECHOTIME, + EAXREVERB_DEFAULTECHODEPTH, + EAXREVERB_DEFAULTMODULATIONTIME, + EAXREVERB_DEFAULTMODULATIONDEPTH, + EAXREVERB_DEFAULTAIRABSORPTIONHF, + EAXREVERB_DEFAULTHFREFERENCE, + EAXREVERB_DEFAULTLFREFERENCE, + EAXREVERB_DEFAULTROOMROLLOFFFACTOR, + EAXREVERB_DEFAULTFLAGS, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_PADDEDCELL = +{ + EAX_ENVIRONMENT_PADDEDCELL, + 1.4F, + 1.0F, + -1'000L, + -6'000L, + 0L, + 0.17F, + 0.10F, + 1.0F, + -1'204L, + 0.001F, + EAXVECTOR{}, + 207L, + 0.002F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_ROOM = +{ + EAX_ENVIRONMENT_ROOM, + 1.9F, + 1.0F, + -1'000L, + -454L, + 0L, + 0.40F, + 0.83F, + 1.0F, + -1'646L, + 0.002F, + EAXVECTOR{}, + 53L, + 0.003F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_BATHROOM = +{ + EAX_ENVIRONMENT_BATHROOM, + 1.4F, + 1.0F, + -1'000L, + -1'200L, + 0L, + 1.49F, + 0.54F, + 1.0F, + -370L, + 0.007F, + EAXVECTOR{}, + 1'030L, + 0.011F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_LIVINGROOM = +{ + EAX_ENVIRONMENT_LIVINGROOM, + 2.5F, + 1.0F, + -1'000L, + -6'000L, + 0L, + 0.50F, + 0.10F, + 1.0F, + -1'376, + 0.003F, + EAXVECTOR{}, + -1'104L, + 0.004F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_STONEROOM = +{ + EAX_ENVIRONMENT_STONEROOM, + 11.6F, + 1.0F, + -1'000L, + -300L, + 0L, + 2.31F, + 0.64F, + 1.0F, + -711L, + 0.012F, + EAXVECTOR{}, + 83L, + 0.017F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_AUDITORIUM = +{ + EAX_ENVIRONMENT_AUDITORIUM, + 21.6F, + 1.0F, + -1'000L, + -476L, + 0L, + 4.32F, + 0.59F, + 1.0F, + -789L, + 0.020F, + EAXVECTOR{}, + -289L, + 0.030F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_CONCERTHALL = +{ + EAX_ENVIRONMENT_CONCERTHALL, + 19.6F, + 1.0F, + -1'000L, + -500L, + 0L, + 3.92F, + 0.70F, + 1.0F, + -1'230L, + 0.020F, + EAXVECTOR{}, + -2L, + 0.029F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_CAVE = +{ + EAX_ENVIRONMENT_CAVE, + 14.6F, + 1.0F, + -1'000L, + 0L, + 0L, + 2.91F, + 1.30F, + 1.0F, + -602L, + 0.015F, + EAXVECTOR{}, + -302L, + 0.022F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x1FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_ARENA = +{ + EAX_ENVIRONMENT_ARENA, + 36.2F, + 1.0F, + -1'000L, + -698L, + 0L, + 7.24F, + 0.33F, + 1.0F, + -1'166L, + 0.020F, + EAXVECTOR{}, + 16L, + 0.030F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_HANGAR = +{ + EAX_ENVIRONMENT_HANGAR, + 50.3F, + 1.0F, + -1'000L, + -1'000L, + 0L, + 10.05F, + 0.23F, + 1.0F, + -602L, + 0.020F, + EAXVECTOR{}, + 198L, + 0.030F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_CARPETTEDHALLWAY = +{ + EAX_ENVIRONMENT_CARPETEDHALLWAY, + 1.9F, + 1.0F, + -1'000L, + -4'000L, + 0L, + 0.30F, + 0.10F, + 1.0F, + -1'831L, + 0.002F, + EAXVECTOR{}, + -1'630L, + 0.030F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_HALLWAY = +{ + EAX_ENVIRONMENT_HALLWAY, + 1.8F, + 1.0F, + -1'000L, + -300L, + 0L, + 1.49F, + 0.59F, + 1.0F, + -1'219L, + 0.007F, + EAXVECTOR{}, + 441L, + 0.011F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_STONECORRIDOR = +{ + EAX_ENVIRONMENT_STONECORRIDOR, + 13.5F, + 1.0F, + -1'000L, + -237L, + 0L, + 2.70F, + 0.79F, + 1.0F, + -1'214L, + 0.013F, + EAXVECTOR{}, + 395L, + 0.020F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_ALLEY = +{ + EAX_ENVIRONMENT_ALLEY, + 7.5F, + 0.300F, + -1'000L, + -270L, + 0L, + 1.49F, + 0.86F, + 1.0F, + -1'204L, + 0.007F, + EAXVECTOR{}, + -4L, + 0.011F, + EAXVECTOR{}, + 0.125F, + 0.950F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_FOREST = +{ + EAX_ENVIRONMENT_FOREST, + 38.0F, + 0.300F, + -1'000L, + -3'300L, + 0L, + 1.49F, + 0.54F, + 1.0F, + -2'560L, + 0.162F, + EAXVECTOR{}, + -229L, + 0.088F, + EAXVECTOR{}, + 0.125F, + 1.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_CITY = +{ + EAX_ENVIRONMENT_CITY, + 7.5F, + 0.500F, + -1'000L, + -800L, + 0L, + 1.49F, + 0.67F, + 1.0F, + -2'273L, + 0.007F, + EAXVECTOR{}, + -1'691L, + 0.011F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_MOUNTAINS = +{ + EAX_ENVIRONMENT_MOUNTAINS, + 100.0F, + 0.270F, + -1'000L, + -2'500L, + 0L, + 1.49F, + 0.21F, + 1.0F, + -2'780L, + 0.300F, + EAXVECTOR{}, + -1'434L, + 0.100F, + EAXVECTOR{}, + 0.250F, + 1.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x1FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_QUARRY = +{ + EAX_ENVIRONMENT_QUARRY, + 17.5F, + 1.0F, + -1'000L, + -1'000L, + 0L, + 1.49F, + 0.83F, + 1.0F, + -10'000L, + 0.061F, + EAXVECTOR{}, + 500L, + 0.025F, + EAXVECTOR{}, + 0.125F, + 0.700F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_PLAIN = +{ + EAX_ENVIRONMENT_PLAIN, + 42.5F, + 0.210F, + -1'000L, + -2'000L, + 0L, + 1.49F, + 0.50F, + 1.0F, + -2'466L, + 0.179F, + EAXVECTOR{}, + -1'926L, + 0.100F, + EAXVECTOR{}, + 0.250F, + 1.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_PARKINGLOT = +{ + EAX_ENVIRONMENT_PARKINGLOT, + 8.3F, + 1.0F, + -1'000L, + 0L, + 0L, + 1.65F, + 1.50F, + 1.0F, + -1'363L, + 0.008F, + EAXVECTOR{}, + -1'153L, + 0.012F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x1FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_SEWERPIPE = +{ + EAX_ENVIRONMENT_SEWERPIPE, + 1.7F, + 0.800F, + -1'000L, + -1'000L, + 0L, + 2.81F, + 0.14F, + 1.0F, + 429L, + 0.014F, + EAXVECTOR{}, + 1'023L, + 0.021F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 0.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_UNDERWATER = +{ + EAX_ENVIRONMENT_UNDERWATER, + 1.8F, + 1.0F, + -1'000L, + -4'000L, + 0L, + 1.49F, + 0.10F, + 1.0F, + -449L, + 0.007F, + EAXVECTOR{}, + 1'700L, + 0.011F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 1.180F, + 0.348F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x3FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_DRUGGED = +{ + EAX_ENVIRONMENT_DRUGGED, + 1.9F, + 0.500F, + -1'000L, + 0L, + 0L, + 8.39F, + 1.39F, + 1.0F, + -115L, + 0.002F, + EAXVECTOR{}, + 985L, + 0.030F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 0.250F, + 1.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x1FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_DIZZY = +{ + EAX_ENVIRONMENT_DIZZY, + 1.8F, + 0.600F, + -1'000L, + -400L, + 0L, + 17.23F, + 0.56F, + 1.0F, + -1'713L, + 0.020F, + EAXVECTOR{}, + -613L, + 0.030F, + EAXVECTOR{}, + 0.250F, + 1.0F, + 0.810F, + 0.310F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x1FUL, +}; + +constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_PSYCHOTIC = +{ + EAX_ENVIRONMENT_PSYCHOTIC, + 1.0F, + 0.500F, + -1'000L, + -151L, + 0L, + 7.56F, + 0.91F, + 1.0F, + -626L, + 0.020F, + EAXVECTOR{}, + 774L, + 0.030F, + EAXVECTOR{}, + 0.250F, + 0.0F, + 4.0F, + 1.0F, + -5.0F, + 5'000.0F, + 250.0F, + 0.0F, + 0x1FUL, +}; + +} // namespace + +const EaxReverbPresets EAXREVERB_PRESETS{{ + EAXREVERB_PRESET_GENERIC, + EAXREVERB_PRESET_PADDEDCELL, + EAXREVERB_PRESET_ROOM, + EAXREVERB_PRESET_BATHROOM, + EAXREVERB_PRESET_LIVINGROOM, + EAXREVERB_PRESET_STONEROOM, + EAXREVERB_PRESET_AUDITORIUM, + EAXREVERB_PRESET_CONCERTHALL, + EAXREVERB_PRESET_CAVE, + EAXREVERB_PRESET_ARENA, + EAXREVERB_PRESET_HANGAR, + EAXREVERB_PRESET_CARPETTEDHALLWAY, + EAXREVERB_PRESET_HALLWAY, + EAXREVERB_PRESET_STONECORRIDOR, + EAXREVERB_PRESET_ALLEY, + EAXREVERB_PRESET_FOREST, + EAXREVERB_PRESET_CITY, + EAXREVERB_PRESET_MOUNTAINS, + EAXREVERB_PRESET_QUARRY, + EAXREVERB_PRESET_PLAIN, + EAXREVERB_PRESET_PARKINGLOT, + EAXREVERB_PRESET_SEWERPIPE, + EAXREVERB_PRESET_UNDERWATER, + EAXREVERB_PRESET_DRUGGED, + EAXREVERB_PRESET_DIZZY, + EAXREVERB_PRESET_PSYCHOTIC, +}}; + +namespace { +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_GENERIC = {EAX_ENVIRONMENT_GENERIC, 0.5F, 1.493F, 0.5F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PADDEDCELL = {EAX_ENVIRONMENT_PADDEDCELL, 0.25F, 0.1F, 0.0F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ROOM = {EAX_ENVIRONMENT_ROOM, 0.417F, 0.4F, 0.666F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_BATHROOM = {EAX_ENVIRONMENT_BATHROOM, 0.653F, 1.499F, 0.166F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_LIVINGROOM = {EAX_ENVIRONMENT_LIVINGROOM, 0.208F, 0.478F, 0.0F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_STONEROOM = {EAX_ENVIRONMENT_STONEROOM, 0.5F, 2.309F, 0.888F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_AUDITORIUM = {EAX_ENVIRONMENT_AUDITORIUM, 0.403F, 4.279F, 0.5F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CONCERTHALL = {EAX_ENVIRONMENT_CONCERTHALL, 0.5F, 3.961F, 0.5F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CAVE = {EAX_ENVIRONMENT_CAVE, 0.5F, 2.886F, 1.304F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ARENA = {EAX_ENVIRONMENT_ARENA, 0.361F, 7.284F, 0.332F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_HANGAR = {EAX_ENVIRONMENT_HANGAR, 0.5F, 10.0F, 0.3F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CARPETTEDHALLWAY = {EAX_ENVIRONMENT_CARPETEDHALLWAY, 0.153F, 0.259F, 2.0F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_HALLWAY = {EAX_ENVIRONMENT_HALLWAY, 0.361F, 1.493F, 0.0F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_STONECORRIDOR = {EAX_ENVIRONMENT_STONECORRIDOR, 0.444F, 2.697F, 0.638F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ALLEY = {EAX_ENVIRONMENT_ALLEY, 0.25F, 1.752F, 0.776F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_FOREST = {EAX_ENVIRONMENT_FOREST, 0.111F, 3.145F, 0.472F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CITY = {EAX_ENVIRONMENT_CITY, 0.111F, 2.767F, 0.224F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_MOUNTAINS = {EAX_ENVIRONMENT_MOUNTAINS, 0.194F, 7.841F, 0.472F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_QUARRY = {EAX_ENVIRONMENT_QUARRY, 1.0F, 1.499F, 0.5F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PLAIN = {EAX_ENVIRONMENT_PLAIN, 0.097F, 2.767F, 0.224F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PARKINGLOT = {EAX_ENVIRONMENT_PARKINGLOT, 0.208F, 1.652F, 1.5F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_SEWERPIPE = {EAX_ENVIRONMENT_SEWERPIPE, 0.652F, 2.886F, 0.25F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_UNDERWATER = {EAX_ENVIRONMENT_UNDERWATER, 1.0F, 1.499F, 0.0F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_DRUGGED = {EAX_ENVIRONMENT_DRUGGED, 0.875F, 8.392F, 1.388F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_DIZZY = {EAX_ENVIRONMENT_DIZZY, 0.139F, 17.234F, 0.666F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PSYCHOTIC = {EAX_ENVIRONMENT_PSYCHOTIC, 0.486F, 7.563F, 0.806F}; +} // namespace + +const Eax1ReverbPresets EAX1REVERB_PRESETS{{ + EAX1REVERB_PRESET_GENERIC, + EAX1REVERB_PRESET_PADDEDCELL, + EAX1REVERB_PRESET_ROOM, + EAX1REVERB_PRESET_BATHROOM, + EAX1REVERB_PRESET_LIVINGROOM, + EAX1REVERB_PRESET_STONEROOM, + EAX1REVERB_PRESET_AUDITORIUM, + EAX1REVERB_PRESET_CONCERTHALL, + EAX1REVERB_PRESET_CAVE, + EAX1REVERB_PRESET_ARENA, + EAX1REVERB_PRESET_HANGAR, + EAX1REVERB_PRESET_CARPETTEDHALLWAY, + EAX1REVERB_PRESET_HALLWAY, + EAX1REVERB_PRESET_STONECORRIDOR, + EAX1REVERB_PRESET_ALLEY, + EAX1REVERB_PRESET_FOREST, + EAX1REVERB_PRESET_CITY, + EAX1REVERB_PRESET_MOUNTAINS, + EAX1REVERB_PRESET_QUARRY, + EAX1REVERB_PRESET_PLAIN, + EAX1REVERB_PRESET_PARKINGLOT, + EAX1REVERB_PRESET_SEWERPIPE, + EAX1REVERB_PRESET_UNDERWATER, + EAX1REVERB_PRESET_DRUGGED, + EAX1REVERB_PRESET_DIZZY, + EAX1REVERB_PRESET_PSYCHOTIC, +}}; diff --git a/al/eax/api.h b/al/eax/api.h new file mode 100644 index 00000000..d0737d1d --- /dev/null +++ b/al/eax/api.h @@ -0,0 +1,1557 @@ +#ifndef EAX_API_INCLUDED +#define EAX_API_INCLUDED + + +// +// EAX API. +// +// Based on headers `eax[2-5].h` included in Doom 3 source code: +// https://github.com/id-Software/DOOM-3/tree/master/neo/openal/include +// + + +#include +#include +#include + +#include + +#include "AL/al.h" + + +#ifndef GUID_DEFINED +#define GUID_DEFINED +typedef struct _GUID +{ + std::uint32_t Data1; + std::uint16_t Data2; + std::uint16_t Data3; + std::uint8_t Data4[8]; +} GUID; + +inline bool operator==(const GUID& lhs, const GUID& rhs) noexcept +{ + return std::memcmp(&lhs, &rhs, sizeof(GUID)) == 0; +} + +inline bool operator!=(const GUID& lhs, const GUID& rhs) noexcept +{ + return !(lhs == rhs); +} +#endif // GUID_DEFINED + + +extern const GUID DSPROPSETID_EAX_ReverbProperties; + +enum DSPROPERTY_EAX_REVERBPROPERTY : unsigned int +{ + DSPROPERTY_EAX_ALL, + DSPROPERTY_EAX_ENVIRONMENT, + DSPROPERTY_EAX_VOLUME, + DSPROPERTY_EAX_DECAYTIME, + DSPROPERTY_EAX_DAMPING, +}; // DSPROPERTY_EAX_REVERBPROPERTY + +struct EAX_REVERBPROPERTIES +{ + unsigned long environment; + float fVolume; + float fDecayTime_sec; + float fDamping; +}; // EAX_REVERBPROPERTIES + +inline bool operator==(const EAX_REVERBPROPERTIES& lhs, const EAX_REVERBPROPERTIES& rhs) noexcept +{ + return std::memcmp(&lhs, &rhs, sizeof(EAX_REVERBPROPERTIES)) == 0; +} + +extern const GUID DSPROPSETID_EAXBUFFER_ReverbProperties; + +enum DSPROPERTY_EAXBUFFER_REVERBPROPERTY : unsigned int +{ + DSPROPERTY_EAXBUFFER_ALL, + DSPROPERTY_EAXBUFFER_REVERBMIX, +}; // DSPROPERTY_EAXBUFFER_REVERBPROPERTY + +struct EAXBUFFER_REVERBPROPERTIES +{ + float fMix; +}; + +inline bool operator==(const EAXBUFFER_REVERBPROPERTIES& lhs, const EAXBUFFER_REVERBPROPERTIES& rhs) noexcept +{ + return lhs.fMix == rhs.fMix; +} + +constexpr auto EAX_BUFFER_MINREVERBMIX = 0.0F; +constexpr auto EAX_BUFFER_MAXREVERBMIX = 1.0F; +constexpr auto EAX_REVERBMIX_USEDISTANCE = -1.0F; + + +extern const GUID DSPROPSETID_EAX20_ListenerProperties; + +enum DSPROPERTY_EAX20_LISTENERPROPERTY : + unsigned int +{ + DSPROPERTY_EAX20LISTENER_NONE, + DSPROPERTY_EAX20LISTENER_ALLPARAMETERS, + DSPROPERTY_EAX20LISTENER_ROOM, + DSPROPERTY_EAX20LISTENER_ROOMHF, + DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAX20LISTENER_DECAYTIME, + DSPROPERTY_EAX20LISTENER_DECAYHFRATIO, + DSPROPERTY_EAX20LISTENER_REFLECTIONS, + DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY, + DSPROPERTY_EAX20LISTENER_REVERB, + DSPROPERTY_EAX20LISTENER_REVERBDELAY, + DSPROPERTY_EAX20LISTENER_ENVIRONMENT, + DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE, + DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION, + DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF, + DSPROPERTY_EAX20LISTENER_FLAGS +}; // DSPROPERTY_EAX20_LISTENERPROPERTY + +struct EAX20LISTENERPROPERTIES +{ + long lRoom; // room effect level at low frequencies + long lRoomHF; // room effect high-frequency level re. low frequency level + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flDecayTime; // reverberation decay time at low frequencies + float flDecayHFRatio; // high-frequency to low-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + unsigned long dwEnvironment; // sets all listener properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + float flAirAbsorptionHF; // change in level per meter at 5 kHz + unsigned long dwFlags; // modifies the behavior of properties +}; // EAX20LISTENERPROPERTIES + + +extern const GUID DSPROPSETID_EAX20_BufferProperties; + + +enum DSPROPERTY_EAX20_BUFFERPROPERTY : + unsigned int +{ + DSPROPERTY_EAX20BUFFER_NONE, + DSPROPERTY_EAX20BUFFER_ALLPARAMETERS, + DSPROPERTY_EAX20BUFFER_DIRECT, + DSPROPERTY_EAX20BUFFER_DIRECTHF, + DSPROPERTY_EAX20BUFFER_ROOM, + DSPROPERTY_EAX20BUFFER_ROOMHF, + DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAX20BUFFER_OBSTRUCTION, + DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO, + DSPROPERTY_EAX20BUFFER_OCCLUSION, + DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO, + DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO, + DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF, + DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR, + DSPROPERTY_EAX20BUFFER_FLAGS +}; // DSPROPERTY_EAX20_BUFFERPROPERTY + + +struct EAX20BUFFERPROPERTIES +{ + long lDirect; // direct path level + long lDirectHF; // direct path level at high frequencies + long lRoom; // room effect level + long lRoomHF; // room effect level at high frequencies + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // occlusion room effect level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF + unsigned long dwFlags; // modifies the behavior of properties +}; // EAX20BUFFERPROPERTIES + + +extern const GUID DSPROPSETID_EAX30_ListenerProperties; + +extern const GUID DSPROPSETID_EAX30_BufferProperties; + + +constexpr auto EAX_MAX_FXSLOTS = 4; + +constexpr auto EAX40_MAX_ACTIVE_FXSLOTS = 2; +constexpr auto EAX50_MAX_ACTIVE_FXSLOTS = 4; + + +constexpr auto EAX_OK = 0L; +constexpr auto EAXERR_INVALID_OPERATION = -1L; +constexpr auto EAXERR_INVALID_VALUE = -2L; +constexpr auto EAXERR_NO_EFFECT_LOADED = -3L; +constexpr auto EAXERR_UNKNOWN_EFFECT = -4L; +constexpr auto EAXERR_INCOMPATIBLE_SOURCE_TYPE = -5L; +constexpr auto EAXERR_INCOMPATIBLE_EAX_VERSION = -6L; + + +extern const GUID EAX_NULL_GUID; + +extern const GUID EAX_PrimaryFXSlotID; + + +struct EAXVECTOR +{ + float x; + float y; + float z; +}; // EAXVECTOR + +inline bool operator==(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept +{ return std::memcmp(&lhs, &rhs, sizeof(EAXVECTOR)) == 0; } + +inline bool operator!=(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept +{ return !(lhs == rhs); } + + +extern const GUID EAXPROPERTYID_EAX40_Context; + +extern const GUID EAXPROPERTYID_EAX50_Context; + +// EAX50 +enum : + unsigned long +{ + HEADPHONES = 0, + SPEAKERS_2, + SPEAKERS_4, + SPEAKERS_5, // 5.1 speakers + SPEAKERS_6, // 6.1 speakers + SPEAKERS_7, // 7.1 speakers +}; + +// EAX50 +enum : + unsigned long +{ + EAX_40 = 5, // EAX 4.0 + EAX_50 = 6, // EAX 5.0 +}; + +constexpr auto EAXCONTEXT_MINEAXSESSION = EAX_40; +constexpr auto EAXCONTEXT_MAXEAXSESSION = EAX_50; +constexpr auto EAXCONTEXT_DEFAULTEAXSESSION = EAX_40; + +constexpr auto EAXCONTEXT_MINMAXACTIVESENDS = 2UL; +constexpr auto EAXCONTEXT_MAXMAXACTIVESENDS = 4UL; +constexpr auto EAXCONTEXT_DEFAULTMAXACTIVESENDS = 2UL; + +// EAX50 +struct EAXSESSIONPROPERTIES +{ + unsigned long ulEAXVersion; + unsigned long ulMaxActiveSends; +}; // EAXSESSIONPROPERTIES + +enum EAXCONTEXT_PROPERTY : + unsigned int +{ + EAXCONTEXT_NONE = 0, + EAXCONTEXT_ALLPARAMETERS, + EAXCONTEXT_PRIMARYFXSLOTID, + EAXCONTEXT_DISTANCEFACTOR, + EAXCONTEXT_AIRABSORPTIONHF, + EAXCONTEXT_HFREFERENCE, + EAXCONTEXT_LASTERROR, + + // EAX50 + EAXCONTEXT_SPEAKERCONFIG, + EAXCONTEXT_EAXSESSION, + EAXCONTEXT_MACROFXFACTOR, +}; // EAXCONTEXT_PROPERTY + +struct EAX40CONTEXTPROPERTIES +{ + GUID guidPrimaryFXSlotID; + float flDistanceFactor; + float flAirAbsorptionHF; + float flHFReference; +}; // EAX40CONTEXTPROPERTIES + +struct EAX50CONTEXTPROPERTIES : + public EAX40CONTEXTPROPERTIES +{ + float flMacroFXFactor; +}; // EAX40CONTEXTPROPERTIES + + +bool operator==( + const EAX40CONTEXTPROPERTIES& lhs, + const EAX40CONTEXTPROPERTIES& rhs) noexcept; + +bool operator==( + const EAX50CONTEXTPROPERTIES& lhs, + const EAX50CONTEXTPROPERTIES& rhs) noexcept; + + +constexpr auto EAXCONTEXT_MINDISTANCEFACTOR = FLT_MIN; +constexpr auto EAXCONTEXT_MAXDISTANCEFACTOR = FLT_MAX; +constexpr auto EAXCONTEXT_DEFAULTDISTANCEFACTOR = 1.0F; + +constexpr auto EAXCONTEXT_MINAIRABSORPTIONHF = -100.0F; +constexpr auto EAXCONTEXT_MAXAIRABSORPTIONHF = 0.0F; +constexpr auto EAXCONTEXT_DEFAULTAIRABSORPTIONHF = -5.0F; + +constexpr auto EAXCONTEXT_MINHFREFERENCE = 1000.0F; +constexpr auto EAXCONTEXT_MAXHFREFERENCE = 20000.0F; +constexpr auto EAXCONTEXT_DEFAULTHFREFERENCE = 5000.0F; + +constexpr auto EAXCONTEXT_MINMACROFXFACTOR = 0.0F; +constexpr auto EAXCONTEXT_MAXMACROFXFACTOR = 1.0F; +constexpr auto EAXCONTEXT_DEFAULTMACROFXFACTOR = 0.0F; + + +extern const GUID EAXPROPERTYID_EAX40_FXSlot0; + +extern const GUID EAXPROPERTYID_EAX50_FXSlot0; + +extern const GUID EAXPROPERTYID_EAX40_FXSlot1; + +extern const GUID EAXPROPERTYID_EAX50_FXSlot1; + +extern const GUID EAXPROPERTYID_EAX40_FXSlot2; + +extern const GUID EAXPROPERTYID_EAX50_FXSlot2; + +extern const GUID EAXPROPERTYID_EAX40_FXSlot3; + +extern const GUID EAXPROPERTYID_EAX50_FXSlot3; + +extern const GUID EAXCONTEXT_DEFAULTPRIMARYFXSLOTID; + +enum EAXFXSLOT_PROPERTY : + unsigned int +{ + EAXFXSLOT_PARAMETER = 0, + + EAXFXSLOT_NONE = 0x10000, + EAXFXSLOT_ALLPARAMETERS, + EAXFXSLOT_LOADEFFECT, + EAXFXSLOT_VOLUME, + EAXFXSLOT_LOCK, + EAXFXSLOT_FLAGS, + + // EAX50 + EAXFXSLOT_OCCLUSION, + EAXFXSLOT_OCCLUSIONLFRATIO, +}; // EAXFXSLOT_PROPERTY + +constexpr auto EAXFXSLOTFLAGS_ENVIRONMENT = 0x00000001UL; +// EAX50 +constexpr auto EAXFXSLOTFLAGS_UPMIX = 0x00000002UL; + +constexpr auto EAX40FXSLOTFLAGS_RESERVED = 0xFFFFFFFEUL; // reserved future use +constexpr auto EAX50FXSLOTFLAGS_RESERVED = 0xFFFFFFFCUL; // reserved future use + + +constexpr auto EAXFXSLOT_MINVOLUME = -10'000L; +constexpr auto EAXFXSLOT_MAXVOLUME = 0L; +constexpr auto EAXFXSLOT_DEFAULTVOLUME = 0L; + +constexpr auto EAXFXSLOT_MINLOCK = 0L; +constexpr auto EAXFXSLOT_MAXLOCK = 1L; + +enum : + long +{ + EAXFXSLOT_UNLOCKED = 0, + EAXFXSLOT_LOCKED = 1 +}; + +constexpr auto EAXFXSLOT_MINOCCLUSION = -10'000L; +constexpr auto EAXFXSLOT_MAXOCCLUSION = 0L; +constexpr auto EAXFXSLOT_DEFAULTOCCLUSION = 0L; + +constexpr auto EAXFXSLOT_MINOCCLUSIONLFRATIO = 0.0F; +constexpr auto EAXFXSLOT_MAXOCCLUSIONLFRATIO = 1.0F; +constexpr auto EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO = 0.25F; + +constexpr auto EAX40FXSLOT_DEFAULTFLAGS = EAXFXSLOTFLAGS_ENVIRONMENT; + +constexpr auto EAX50FXSLOT_DEFAULTFLAGS = + EAXFXSLOTFLAGS_ENVIRONMENT | + EAXFXSLOTFLAGS_UPMIX; // ignored for reverb; + +struct EAX40FXSLOTPROPERTIES +{ + GUID guidLoadEffect; + long lVolume; + long lLock; + unsigned long ulFlags; +}; // EAX40FXSLOTPROPERTIES + +struct EAX50FXSLOTPROPERTIES : + public EAX40FXSLOTPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; +}; // EAX50FXSLOTPROPERTIES + +bool operator==( + const EAX40FXSLOTPROPERTIES& lhs, + const EAX40FXSLOTPROPERTIES& rhs) noexcept; + +bool operator==( + const EAX50FXSLOTPROPERTIES& lhs, + const EAX50FXSLOTPROPERTIES& rhs) noexcept; + +extern const GUID EAXPROPERTYID_EAX40_Source; + +extern const GUID EAXPROPERTYID_EAX50_Source; + +// Source object properties +enum EAXSOURCE_PROPERTY : + unsigned int +{ + // EAX30 + + EAXSOURCE_NONE, + EAXSOURCE_ALLPARAMETERS, + EAXSOURCE_OBSTRUCTIONPARAMETERS, + EAXSOURCE_OCCLUSIONPARAMETERS, + EAXSOURCE_EXCLUSIONPARAMETERS, + EAXSOURCE_DIRECT, + EAXSOURCE_DIRECTHF, + EAXSOURCE_ROOM, + EAXSOURCE_ROOMHF, + EAXSOURCE_OBSTRUCTION, + EAXSOURCE_OBSTRUCTIONLFRATIO, + EAXSOURCE_OCCLUSION, + EAXSOURCE_OCCLUSIONLFRATIO, + EAXSOURCE_OCCLUSIONROOMRATIO, + EAXSOURCE_OCCLUSIONDIRECTRATIO, + EAXSOURCE_EXCLUSION, + EAXSOURCE_EXCLUSIONLFRATIO, + EAXSOURCE_OUTSIDEVOLUMEHF, + EAXSOURCE_DOPPLERFACTOR, + EAXSOURCE_ROLLOFFFACTOR, + EAXSOURCE_ROOMROLLOFFFACTOR, + EAXSOURCE_AIRABSORPTIONFACTOR, + EAXSOURCE_FLAGS, + + // EAX40 + + EAXSOURCE_SENDPARAMETERS, + EAXSOURCE_ALLSENDPARAMETERS, + EAXSOURCE_OCCLUSIONSENDPARAMETERS, + EAXSOURCE_EXCLUSIONSENDPARAMETERS, + EAXSOURCE_ACTIVEFXSLOTID, + + // EAX50 + + EAXSOURCE_MACROFXFACTOR, + EAXSOURCE_SPEAKERLEVELS, + EAXSOURCE_ALL2DPARAMETERS, +}; // EAXSOURCE_PROPERTY + + +constexpr auto EAXSOURCEFLAGS_DIRECTHFAUTO = 0x00000001UL; // relates to EAXSOURCE_DIRECTHF +constexpr auto EAXSOURCEFLAGS_ROOMAUTO = 0x00000002UL; // relates to EAXSOURCE_ROOM +constexpr auto EAXSOURCEFLAGS_ROOMHFAUTO = 0x00000004UL; // relates to EAXSOURCE_ROOMHF +// EAX50 +constexpr auto EAXSOURCEFLAGS_3DELEVATIONFILTER = 0x00000008UL; +// EAX50 +constexpr auto EAXSOURCEFLAGS_UPMIX = 0x00000010UL; +// EAX50 +constexpr auto EAXSOURCEFLAGS_APPLYSPEAKERLEVELS = 0x00000020UL; + +constexpr auto EAX20SOURCEFLAGS_RESERVED = 0xFFFFFFF8UL; // reserved future use +constexpr auto EAX50SOURCEFLAGS_RESERVED = 0xFFFFFFC0UL; // reserved future use + + +constexpr auto EAXSOURCE_MINSEND = -10'000L; +constexpr auto EAXSOURCE_MAXSEND = 0L; +constexpr auto EAXSOURCE_DEFAULTSEND = 0L; + +constexpr auto EAXSOURCE_MINSENDHF = -10'000L; +constexpr auto EAXSOURCE_MAXSENDHF = 0L; +constexpr auto EAXSOURCE_DEFAULTSENDHF = 0L; + +constexpr auto EAXSOURCE_MINDIRECT = -10'000L; +constexpr auto EAXSOURCE_MAXDIRECT = 1'000L; +constexpr auto EAXSOURCE_DEFAULTDIRECT = 0L; + +constexpr auto EAXSOURCE_MINDIRECTHF = -10'000L; +constexpr auto EAXSOURCE_MAXDIRECTHF = 0L; +constexpr auto EAXSOURCE_DEFAULTDIRECTHF = 0L; + +constexpr auto EAXSOURCE_MINROOM = -10'000L; +constexpr auto EAXSOURCE_MAXROOM = 1'000L; +constexpr auto EAXSOURCE_DEFAULTROOM = 0L; + +constexpr auto EAXSOURCE_MINROOMHF = -10'000L; +constexpr auto EAXSOURCE_MAXROOMHF = 0L; +constexpr auto EAXSOURCE_DEFAULTROOMHF = 0L; + +constexpr auto EAXSOURCE_MINOBSTRUCTION = -10'000L; +constexpr auto EAXSOURCE_MAXOBSTRUCTION = 0L; +constexpr auto EAXSOURCE_DEFAULTOBSTRUCTION = 0L; + +constexpr auto EAXSOURCE_MINOBSTRUCTIONLFRATIO = 0.0F; +constexpr auto EAXSOURCE_MAXOBSTRUCTIONLFRATIO = 1.0F; +constexpr auto EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO = 0.0F; + +constexpr auto EAXSOURCE_MINOCCLUSION = -10'000L; +constexpr auto EAXSOURCE_MAXOCCLUSION = 0L; +constexpr auto EAXSOURCE_DEFAULTOCCLUSION = 0L; + +constexpr auto EAXSOURCE_MINOCCLUSIONLFRATIO = 0.0F; +constexpr auto EAXSOURCE_MAXOCCLUSIONLFRATIO = 1.0F; +constexpr auto EAXSOURCE_DEFAULTOCCLUSIONLFRATIO = 0.25F; + +constexpr auto EAXSOURCE_MINOCCLUSIONROOMRATIO = 0.0F; +constexpr auto EAXSOURCE_MAXOCCLUSIONROOMRATIO = 10.0F; +constexpr auto EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO = 1.5F; + +constexpr auto EAXSOURCE_MINOCCLUSIONDIRECTRATIO = 0.0F; +constexpr auto EAXSOURCE_MAXOCCLUSIONDIRECTRATIO = 10.0F; +constexpr auto EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO = 1.0F; + +constexpr auto EAXSOURCE_MINEXCLUSION = -10'000L; +constexpr auto EAXSOURCE_MAXEXCLUSION = 0L; +constexpr auto EAXSOURCE_DEFAULTEXCLUSION = 0L; + +constexpr auto EAXSOURCE_MINEXCLUSIONLFRATIO = 0.0F; +constexpr auto EAXSOURCE_MAXEXCLUSIONLFRATIO = 1.0F; +constexpr auto EAXSOURCE_DEFAULTEXCLUSIONLFRATIO = 1.0F; + +constexpr auto EAXSOURCE_MINOUTSIDEVOLUMEHF = -10'000L; +constexpr auto EAXSOURCE_MAXOUTSIDEVOLUMEHF = 0L; +constexpr auto EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF = 0L; + +constexpr auto EAXSOURCE_MINDOPPLERFACTOR = 0.0F; +constexpr auto EAXSOURCE_MAXDOPPLERFACTOR = 10.0F; +constexpr auto EAXSOURCE_DEFAULTDOPPLERFACTOR = 1.0F; + +constexpr auto EAXSOURCE_MINROLLOFFFACTOR = 0.0F; +constexpr auto EAXSOURCE_MAXROLLOFFFACTOR = 10.0F; +constexpr auto EAXSOURCE_DEFAULTROLLOFFFACTOR = 0.0F; + +constexpr auto EAXSOURCE_MINROOMROLLOFFFACTOR = 0.0F; +constexpr auto EAXSOURCE_MAXROOMROLLOFFFACTOR = 10.0F; +constexpr auto EAXSOURCE_DEFAULTROOMROLLOFFFACTOR = 0.0F; + +constexpr auto EAXSOURCE_MINAIRABSORPTIONFACTOR = 0.0F; +constexpr auto EAXSOURCE_MAXAIRABSORPTIONFACTOR = 10.0F; +constexpr auto EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR = 0.0F; + +// EAX50 + +constexpr auto EAXSOURCE_MINMACROFXFACTOR = 0.0F; +constexpr auto EAXSOURCE_MAXMACROFXFACTOR = 1.0F; +constexpr auto EAXSOURCE_DEFAULTMACROFXFACTOR = 1.0F; + +// EAX50 + +constexpr auto EAXSOURCE_MINSPEAKERLEVEL = -10'000L; +constexpr auto EAXSOURCE_MAXSPEAKERLEVEL = 0L; +constexpr auto EAXSOURCE_DEFAULTSPEAKERLEVEL = -10'000L; + +constexpr auto EAXSOURCE_DEFAULTFLAGS = + EAXSOURCEFLAGS_DIRECTHFAUTO | + EAXSOURCEFLAGS_ROOMAUTO | + EAXSOURCEFLAGS_ROOMHFAUTO; + +enum : + long +{ + EAXSPEAKER_FRONT_LEFT = 1, + EAXSPEAKER_FRONT_CENTER = 2, + EAXSPEAKER_FRONT_RIGHT = 3, + EAXSPEAKER_SIDE_RIGHT = 4, + EAXSPEAKER_REAR_RIGHT = 5, + EAXSPEAKER_REAR_CENTER = 6, + EAXSPEAKER_REAR_LEFT = 7, + EAXSPEAKER_SIDE_LEFT = 8, + EAXSPEAKER_LOW_FREQUENCY = 9 +}; + +// EAXSOURCEFLAGS_DIRECTHFAUTO, EAXSOURCEFLAGS_ROOMAUTO and EAXSOURCEFLAGS_ROOMHFAUTO are ignored for 2D sources +// EAXSOURCEFLAGS_UPMIX is ignored for 3D sources +constexpr auto EAX50SOURCE_DEFAULTFLAGS = + EAXSOURCEFLAGS_DIRECTHFAUTO | + EAXSOURCEFLAGS_ROOMAUTO | + EAXSOURCEFLAGS_ROOMHFAUTO | + EAXSOURCEFLAGS_UPMIX; + +struct EAX30SOURCEPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // relative occlusion control for room effect + float flOcclusionDirectRatio; // relative occlusion control for direct path + long lExclusion; // main exlusion control (attenuation at high frequencies) + float flExclusionLFRatio; // exclusion low-frequency level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flDopplerFactor; // like DS3D flDopplerFactor but per source + float flRolloffFactor; // like DS3D flRolloffFactor but per source + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flAirAbsorptionFactor; // multiplies EAXREVERB_AIRABSORPTIONHF + unsigned long ulFlags; // modifies the behavior of properties +}; // EAX30SOURCEPROPERTIES + +struct EAX50SOURCEPROPERTIES : + public EAX30SOURCEPROPERTIES +{ + float flMacroFXFactor; +}; // EAX50SOURCEPROPERTIES + +struct EAXSOURCEALLSENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lSend; // send level (at low and mid frequencies) + long lSendHF; // relative send level at high frequencies + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; + long lExclusion; + float flExclusionLFRatio; +}; // EAXSOURCEALLSENDPROPERTIES + +struct EAXSOURCE2DPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + unsigned long ulFlags; // modifies the behavior of properties +}; // EAXSOURCE2DPROPERTIES + +struct EAXSPEAKERLEVELPROPERTIES +{ + long lSpeakerID; + long lLevel; +}; // EAXSPEAKERLEVELPROPERTIES + +struct EAX40ACTIVEFXSLOTS +{ + GUID guidActiveFXSlots[EAX40_MAX_ACTIVE_FXSLOTS]; +}; // EAX40ACTIVEFXSLOTS + +struct EAX50ACTIVEFXSLOTS +{ + GUID guidActiveFXSlots[EAX50_MAX_ACTIVE_FXSLOTS]; +}; // EAX50ACTIVEFXSLOTS + +bool operator==( + const EAX50ACTIVEFXSLOTS& lhs, + const EAX50ACTIVEFXSLOTS& rhs) noexcept; + +bool operator!=( + const EAX50ACTIVEFXSLOTS& lhs, + const EAX50ACTIVEFXSLOTS& rhs) noexcept; + +// Use this structure for EAXSOURCE_OBSTRUCTIONPARAMETERS property. +struct EAXOBSTRUCTIONPROPERTIES +{ + long lObstruction; + float flObstructionLFRatio; +}; // EAXOBSTRUCTIONPROPERTIES + +// Use this structure for EAXSOURCE_OCCLUSIONPARAMETERS property. +struct EAXOCCLUSIONPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +}; // EAXOCCLUSIONPROPERTIES + +// Use this structure for EAXSOURCE_EXCLUSIONPARAMETERS property. +struct EAXEXCLUSIONPROPERTIES +{ + long lExclusion; + float flExclusionLFRatio; +}; // EAXEXCLUSIONPROPERTIES + +// Use this structure for EAXSOURCE_SENDPARAMETERS properties. +struct EAXSOURCESENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lSend; + long lSendHF; +}; // EAXSOURCESENDPROPERTIES + +// Use this structure for EAXSOURCE_OCCLUSIONSENDPARAMETERS +struct EAXSOURCEOCCLUSIONSENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +}; // EAXSOURCEOCCLUSIONSENDPROPERTIES + +// Use this structure for EAXSOURCE_EXCLUSIONSENDPARAMETERS +struct EAXSOURCEEXCLUSIONSENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lExclusion; + float flExclusionLFRatio; +}; // EAXSOURCEEXCLUSIONSENDPROPERTIES + +extern const EAX50ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID; + +extern const EAX50ACTIVEFXSLOTS EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID; + +extern const EAX50ACTIVEFXSLOTS EAX50SOURCE_2DDEFAULTACTIVEFXSLOTID; + + +// EAX Reverb Effect + +extern const GUID EAX_REVERB_EFFECT; + +// Reverb effect properties +enum EAXREVERB_PROPERTY : + unsigned int +{ + EAXREVERB_NONE, + EAXREVERB_ALLPARAMETERS, + EAXREVERB_ENVIRONMENT, + EAXREVERB_ENVIRONMENTSIZE, + EAXREVERB_ENVIRONMENTDIFFUSION, + EAXREVERB_ROOM, + EAXREVERB_ROOMHF, + EAXREVERB_ROOMLF, + EAXREVERB_DECAYTIME, + EAXREVERB_DECAYHFRATIO, + EAXREVERB_DECAYLFRATIO, + EAXREVERB_REFLECTIONS, + EAXREVERB_REFLECTIONSDELAY, + EAXREVERB_REFLECTIONSPAN, + EAXREVERB_REVERB, + EAXREVERB_REVERBDELAY, + EAXREVERB_REVERBPAN, + EAXREVERB_ECHOTIME, + EAXREVERB_ECHODEPTH, + EAXREVERB_MODULATIONTIME, + EAXREVERB_MODULATIONDEPTH, + EAXREVERB_AIRABSORPTIONHF, + EAXREVERB_HFREFERENCE, + EAXREVERB_LFREFERENCE, + EAXREVERB_ROOMROLLOFFFACTOR, + EAXREVERB_FLAGS, +}; // EAXREVERB_PROPERTY + +// used by EAXREVERB_ENVIRONMENT +enum : + unsigned long +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX1_ENVIRONMENT_COUNT, + + // EAX30 + EAX_ENVIRONMENT_UNDEFINED = EAX1_ENVIRONMENT_COUNT, + + EAX3_ENVIRONMENT_COUNT, +}; + + +// reverberation decay time +constexpr auto EAXREVERBFLAGS_DECAYTIMESCALE = 0x00000001UL; + +// reflection level +constexpr auto EAXREVERBFLAGS_REFLECTIONSSCALE = 0x00000002UL; + +// initial reflection delay time +constexpr auto EAXREVERBFLAGS_REFLECTIONSDELAYSCALE = 0x00000004UL; + +// reflections level +constexpr auto EAXREVERBFLAGS_REVERBSCALE = 0x00000008UL; + +// late reverberation delay time +constexpr auto EAXREVERBFLAGS_REVERBDELAYSCALE = 0x00000010UL; + +// echo time +// EAX30+ +constexpr auto EAXREVERBFLAGS_ECHOTIMESCALE = 0x00000040UL; + +// modulation time +// EAX30+ +constexpr auto EAXREVERBFLAGS_MODULATIONTIMESCALE = 0x00000080UL; + +// This flag limits high-frequency decay time according to air absorption. +constexpr auto EAXREVERBFLAGS_DECAYHFLIMIT = 0x00000020UL; + +constexpr auto EAXREVERBFLAGS_RESERVED = 0xFFFFFF00UL; // reserved future use + + +struct EAXREVERBPROPERTIES +{ + unsigned long ulEnvironment; // sets all reverb properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + long lRoom; // room effect level (at mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lRoomLF; // relative room effect level at low frequencies + float flDecayTime; // reverberation decay time at mid frequencies + float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio + float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + EAXVECTOR vReflectionsPan; // early reflections panning vector + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + EAXVECTOR vReverbPan; // late reverberation panning vector + float flEchoTime; // echo time + float flEchoDepth; // echo depth + float flModulationTime; // modulation time + float flModulationDepth; // modulation depth + float flAirAbsorptionHF; // change in level per meter at high frequencies + float flHFReference; // reference high frequency + float flLFReference; // reference low frequency + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + unsigned long ulFlags; // modifies the behavior of properties +}; // EAXREVERBPROPERTIES + +bool operator==( + const EAXREVERBPROPERTIES& lhs, + const EAXREVERBPROPERTIES& rhs) noexcept; + +bool operator!=( + const EAXREVERBPROPERTIES& lhs, + const EAXREVERBPROPERTIES& rhs) noexcept; + + +constexpr auto EAXREVERB_MINENVIRONMENT = static_cast(EAX_ENVIRONMENT_GENERIC); +constexpr auto EAX1REVERB_MAXENVIRONMENT = static_cast(EAX_ENVIRONMENT_PSYCHOTIC); +constexpr auto EAX30REVERB_MAXENVIRONMENT = static_cast(EAX_ENVIRONMENT_UNDEFINED); +constexpr auto EAXREVERB_DEFAULTENVIRONMENT = static_cast(EAX_ENVIRONMENT_GENERIC); + +constexpr auto EAXREVERB_MINENVIRONMENTSIZE = 1.0F; +constexpr auto EAXREVERB_MAXENVIRONMENTSIZE = 100.0F; +constexpr auto EAXREVERB_DEFAULTENVIRONMENTSIZE = 7.5F; + +constexpr auto EAXREVERB_MINENVIRONMENTDIFFUSION = 0.0F; +constexpr auto EAXREVERB_MAXENVIRONMENTDIFFUSION = 1.0F; +constexpr auto EAXREVERB_DEFAULTENVIRONMENTDIFFUSION = 1.0F; + +constexpr auto EAXREVERB_MINROOM = -10'000L; +constexpr auto EAXREVERB_MAXROOM = 0L; +constexpr auto EAXREVERB_DEFAULTROOM = -1'000L; + +constexpr auto EAXREVERB_MINROOMHF = -10'000L; +constexpr auto EAXREVERB_MAXROOMHF = 0L; +constexpr auto EAXREVERB_DEFAULTROOMHF = -100L; + +constexpr auto EAXREVERB_MINROOMLF = -10'000L; +constexpr auto EAXREVERB_MAXROOMLF = 0L; +constexpr auto EAXREVERB_DEFAULTROOMLF = 0L; + +constexpr auto EAXREVERB_MINDECAYTIME = 0.1F; +constexpr auto EAXREVERB_MAXDECAYTIME = 20.0F; +constexpr auto EAXREVERB_DEFAULTDECAYTIME = 1.49F; + +constexpr auto EAXREVERB_MINDECAYHFRATIO = 0.1F; +constexpr auto EAXREVERB_MAXDECAYHFRATIO = 2.0F; +constexpr auto EAXREVERB_DEFAULTDECAYHFRATIO = 0.83F; + +constexpr auto EAXREVERB_MINDECAYLFRATIO = 0.1F; +constexpr auto EAXREVERB_MAXDECAYLFRATIO = 2.0F; +constexpr auto EAXREVERB_DEFAULTDECAYLFRATIO = 1.0F; + +constexpr auto EAXREVERB_MINREFLECTIONS = -10'000L; +constexpr auto EAXREVERB_MAXREFLECTIONS = 1'000L; +constexpr auto EAXREVERB_DEFAULTREFLECTIONS = -2'602L; + +constexpr auto EAXREVERB_MINREFLECTIONSDELAY = 0.0F; +constexpr auto EAXREVERB_MAXREFLECTIONSDELAY = 0.3F; +constexpr auto EAXREVERB_DEFAULTREFLECTIONSDELAY = 0.007F; + +constexpr auto EAXREVERB_DEFAULTREFLECTIONSPAN = EAXVECTOR{0.0F, 0.0F, 0.0F}; + +constexpr auto EAXREVERB_MINREVERB = -10'000L; +constexpr auto EAXREVERB_MAXREVERB = 2'000L; +constexpr auto EAXREVERB_DEFAULTREVERB = 200L; + +constexpr auto EAXREVERB_MINREVERBDELAY = 0.0F; +constexpr auto EAXREVERB_MAXREVERBDELAY = 0.1F; +constexpr auto EAXREVERB_DEFAULTREVERBDELAY = 0.011F; + +constexpr auto EAXREVERB_DEFAULTREVERBPAN = EAXVECTOR{0.0F, 0.0F, 0.0F}; + +constexpr auto EAXREVERB_MINECHOTIME = 0.075F; +constexpr auto EAXREVERB_MAXECHOTIME = 0.25F; +constexpr auto EAXREVERB_DEFAULTECHOTIME = 0.25F; + +constexpr auto EAXREVERB_MINECHODEPTH = 0.0F; +constexpr auto EAXREVERB_MAXECHODEPTH = 1.0F; +constexpr auto EAXREVERB_DEFAULTECHODEPTH = 0.0F; + +constexpr auto EAXREVERB_MINMODULATIONTIME = 0.04F; +constexpr auto EAXREVERB_MAXMODULATIONTIME = 4.0F; +constexpr auto EAXREVERB_DEFAULTMODULATIONTIME = 0.25F; + +constexpr auto EAXREVERB_MINMODULATIONDEPTH = 0.0F; +constexpr auto EAXREVERB_MAXMODULATIONDEPTH = 1.0F; +constexpr auto EAXREVERB_DEFAULTMODULATIONDEPTH = 0.0F; + +constexpr auto EAXREVERB_MINAIRABSORPTIONHF = -100.0F; +constexpr auto EAXREVERB_MAXAIRABSORPTIONHF = 0.0F; +constexpr auto EAXREVERB_DEFAULTAIRABSORPTIONHF = -5.0F; + +constexpr auto EAXREVERB_MINHFREFERENCE = 1'000.0F; +constexpr auto EAXREVERB_MAXHFREFERENCE = 20'000.0F; +constexpr auto EAXREVERB_DEFAULTHFREFERENCE = 5'000.0F; + +constexpr auto EAXREVERB_MINLFREFERENCE = 20.0F; +constexpr auto EAXREVERB_MAXLFREFERENCE = 1'000.0F; +constexpr auto EAXREVERB_DEFAULTLFREFERENCE = 250.0F; + +constexpr auto EAXREVERB_MINROOMROLLOFFFACTOR = 0.0F; +constexpr auto EAXREVERB_MAXROOMROLLOFFFACTOR = 10.0F; +constexpr auto EAXREVERB_DEFAULTROOMROLLOFFFACTOR = 0.0F; + +constexpr auto EAX1REVERB_MINVOLUME = 0.0F; +constexpr auto EAX1REVERB_MAXVOLUME = 1.0F; + +constexpr auto EAX1REVERB_MINDAMPING = 0.0F; +constexpr auto EAX1REVERB_MAXDAMPING = 2.0F; + +constexpr auto EAXREVERB_DEFAULTFLAGS = + EAXREVERBFLAGS_DECAYTIMESCALE | + EAXREVERBFLAGS_REFLECTIONSSCALE | + EAXREVERBFLAGS_REFLECTIONSDELAYSCALE | + EAXREVERBFLAGS_REVERBSCALE | + EAXREVERBFLAGS_REVERBDELAYSCALE | + EAXREVERBFLAGS_DECAYHFLIMIT; + + +using EaxReverbPresets = std::array; +extern const EaxReverbPresets EAXREVERB_PRESETS; + + +using Eax1ReverbPresets = std::array; +extern const Eax1ReverbPresets EAX1REVERB_PRESETS; + + +// AGC Compressor Effect + +extern const GUID EAX_AGCCOMPRESSOR_EFFECT; + +enum EAXAGCCOMPRESSOR_PROPERTY : + unsigned int +{ + EAXAGCCOMPRESSOR_NONE, + EAXAGCCOMPRESSOR_ALLPARAMETERS, + EAXAGCCOMPRESSOR_ONOFF, +}; // EAXAGCCOMPRESSOR_PROPERTY + +struct EAXAGCCOMPRESSORPROPERTIES +{ + unsigned long ulOnOff; // Switch Compressor on or off +}; // EAXAGCCOMPRESSORPROPERTIES + + +constexpr auto EAXAGCCOMPRESSOR_MINONOFF = 0UL; +constexpr auto EAXAGCCOMPRESSOR_MAXONOFF = 1UL; +constexpr auto EAXAGCCOMPRESSOR_DEFAULTONOFF = EAXAGCCOMPRESSOR_MAXONOFF; + + +// Autowah Effect + +extern const GUID EAX_AUTOWAH_EFFECT; + +enum EAXAUTOWAH_PROPERTY : + unsigned int +{ + EAXAUTOWAH_NONE, + EAXAUTOWAH_ALLPARAMETERS, + EAXAUTOWAH_ATTACKTIME, + EAXAUTOWAH_RELEASETIME, + EAXAUTOWAH_RESONANCE, + EAXAUTOWAH_PEAKLEVEL, +}; // EAXAUTOWAH_PROPERTY + +struct EAXAUTOWAHPROPERTIES +{ + float flAttackTime; // Attack time (seconds) + float flReleaseTime; // Release time (seconds) + long lResonance; // Resonance (mB) + long lPeakLevel; // Peak level (mB) +}; // EAXAUTOWAHPROPERTIES + + +constexpr auto EAXAUTOWAH_MINATTACKTIME = 0.0001F; +constexpr auto EAXAUTOWAH_MAXATTACKTIME = 1.0F; +constexpr auto EAXAUTOWAH_DEFAULTATTACKTIME = 0.06F; + +constexpr auto EAXAUTOWAH_MINRELEASETIME = 0.0001F; +constexpr auto EAXAUTOWAH_MAXRELEASETIME = 1.0F; +constexpr auto EAXAUTOWAH_DEFAULTRELEASETIME = 0.06F; + +constexpr auto EAXAUTOWAH_MINRESONANCE = 600L; +constexpr auto EAXAUTOWAH_MAXRESONANCE = 6000L; +constexpr auto EAXAUTOWAH_DEFAULTRESONANCE = 6000L; + +constexpr auto EAXAUTOWAH_MINPEAKLEVEL = -9000L; +constexpr auto EAXAUTOWAH_MAXPEAKLEVEL = 9000L; +constexpr auto EAXAUTOWAH_DEFAULTPEAKLEVEL = 2100L; + + +// Chorus Effect + +extern const GUID EAX_CHORUS_EFFECT; + + +enum EAXCHORUS_PROPERTY : + unsigned int +{ + EAXCHORUS_NONE, + EAXCHORUS_ALLPARAMETERS, + EAXCHORUS_WAVEFORM, + EAXCHORUS_PHASE, + EAXCHORUS_RATE, + EAXCHORUS_DEPTH, + EAXCHORUS_FEEDBACK, + EAXCHORUS_DELAY, +}; // EAXCHORUS_PROPERTY + +enum : + unsigned long +{ + EAX_CHORUS_SINUSOID, + EAX_CHORUS_TRIANGLE, +}; + +struct EAXCHORUSPROPERTIES +{ + unsigned long ulWaveform; // Waveform selector - see enum above + long lPhase; // Phase (Degrees) + float flRate; // Rate (Hz) + float flDepth; // Depth (0 to 1) + float flFeedback; // Feedback (-1 to 1) + float flDelay; // Delay (seconds) +}; // EAXCHORUSPROPERTIES + + +constexpr auto EAXCHORUS_MINWAVEFORM = 0UL; +constexpr auto EAXCHORUS_MAXWAVEFORM = 1UL; +constexpr auto EAXCHORUS_DEFAULTWAVEFORM = 1UL; + +constexpr auto EAXCHORUS_MINPHASE = -180L; +constexpr auto EAXCHORUS_MAXPHASE = 180L; +constexpr auto EAXCHORUS_DEFAULTPHASE = 90L; + +constexpr auto EAXCHORUS_MINRATE = 0.0F; +constexpr auto EAXCHORUS_MAXRATE = 10.0F; +constexpr auto EAXCHORUS_DEFAULTRATE = 1.1F; + +constexpr auto EAXCHORUS_MINDEPTH = 0.0F; +constexpr auto EAXCHORUS_MAXDEPTH = 1.0F; +constexpr auto EAXCHORUS_DEFAULTDEPTH = 0.1F; + +constexpr auto EAXCHORUS_MINFEEDBACK = -1.0F; +constexpr auto EAXCHORUS_MAXFEEDBACK = 1.0F; +constexpr auto EAXCHORUS_DEFAULTFEEDBACK = 0.25F; + +constexpr auto EAXCHORUS_MINDELAY = 0.0002F; +constexpr auto EAXCHORUS_MAXDELAY = 0.016F; +constexpr auto EAXCHORUS_DEFAULTDELAY = 0.016F; + + +// Distortion Effect + +extern const GUID EAX_DISTORTION_EFFECT; + +enum EAXDISTORTION_PROPERTY : + unsigned int +{ + EAXDISTORTION_NONE, + EAXDISTORTION_ALLPARAMETERS, + EAXDISTORTION_EDGE, + EAXDISTORTION_GAIN, + EAXDISTORTION_LOWPASSCUTOFF, + EAXDISTORTION_EQCENTER, + EAXDISTORTION_EQBANDWIDTH, +}; // EAXDISTORTION_PROPERTY + + +struct EAXDISTORTIONPROPERTIES +{ + float flEdge; // Controls the shape of the distortion (0 to 1) + long lGain; // Controls the post distortion gain (mB) + float flLowPassCutOff; // Controls the cut-off of the filter pre-distortion (Hz) + float flEQCenter; // Controls the center frequency of the EQ post-distortion (Hz) + float flEQBandwidth; // Controls the bandwidth of the EQ post-distortion (Hz) +}; // EAXDISTORTIONPROPERTIES + + +constexpr auto EAXDISTORTION_MINEDGE = 0.0F; +constexpr auto EAXDISTORTION_MAXEDGE = 1.0F; +constexpr auto EAXDISTORTION_DEFAULTEDGE = 0.2F; + +constexpr auto EAXDISTORTION_MINGAIN = -6000L; +constexpr auto EAXDISTORTION_MAXGAIN = 0L; +constexpr auto EAXDISTORTION_DEFAULTGAIN = -2600L; + +constexpr auto EAXDISTORTION_MINLOWPASSCUTOFF = 80.0F; +constexpr auto EAXDISTORTION_MAXLOWPASSCUTOFF = 24000.0F; +constexpr auto EAXDISTORTION_DEFAULTLOWPASSCUTOFF = 8000.0F; + +constexpr auto EAXDISTORTION_MINEQCENTER = 80.0F; +constexpr auto EAXDISTORTION_MAXEQCENTER = 24000.0F; +constexpr auto EAXDISTORTION_DEFAULTEQCENTER = 3600.0F; + +constexpr auto EAXDISTORTION_MINEQBANDWIDTH = 80.0F; +constexpr auto EAXDISTORTION_MAXEQBANDWIDTH = 24000.0F; +constexpr auto EAXDISTORTION_DEFAULTEQBANDWIDTH = 3600.0F; + + +// Echo Effect + +extern const GUID EAX_ECHO_EFFECT; + + +enum EAXECHO_PROPERTY : + unsigned int +{ + EAXECHO_NONE, + EAXECHO_ALLPARAMETERS, + EAXECHO_DELAY, + EAXECHO_LRDELAY, + EAXECHO_DAMPING, + EAXECHO_FEEDBACK, + EAXECHO_SPREAD, +}; // EAXECHO_PROPERTY + + +struct EAXECHOPROPERTIES +{ + float flDelay; // Controls the initial delay time (seconds) + float flLRDelay; // Controls the delay time between the first and second taps (seconds) + float flDamping; // Controls a low-pass filter that dampens the echoes (0 to 1) + float flFeedback; // Controls the duration of echo repetition (0 to 1) + float flSpread; // Controls the left-right spread of the echoes +}; // EAXECHOPROPERTIES + + +constexpr auto EAXECHO_MINDAMPING = 0.0F; +constexpr auto EAXECHO_MAXDAMPING = 0.99F; +constexpr auto EAXECHO_DEFAULTDAMPING = 0.5F; + +constexpr auto EAXECHO_MINDELAY = 0.002F; +constexpr auto EAXECHO_MAXDELAY = 0.207F; +constexpr auto EAXECHO_DEFAULTDELAY = 0.1F; + +constexpr auto EAXECHO_MINLRDELAY = 0.0F; +constexpr auto EAXECHO_MAXLRDELAY = 0.404F; +constexpr auto EAXECHO_DEFAULTLRDELAY = 0.1F; + +constexpr auto EAXECHO_MINFEEDBACK = 0.0F; +constexpr auto EAXECHO_MAXFEEDBACK = 1.0F; +constexpr auto EAXECHO_DEFAULTFEEDBACK = 0.5F; + +constexpr auto EAXECHO_MINSPREAD = -1.0F; +constexpr auto EAXECHO_MAXSPREAD = 1.0F; +constexpr auto EAXECHO_DEFAULTSPREAD = -1.0F; + + +// Equalizer Effect + +extern const GUID EAX_EQUALIZER_EFFECT; + + +enum EAXEQUALIZER_PROPERTY : + unsigned int +{ + EAXEQUALIZER_NONE, + EAXEQUALIZER_ALLPARAMETERS, + EAXEQUALIZER_LOWGAIN, + EAXEQUALIZER_LOWCUTOFF, + EAXEQUALIZER_MID1GAIN, + EAXEQUALIZER_MID1CENTER, + EAXEQUALIZER_MID1WIDTH, + EAXEQUALIZER_MID2GAIN, + EAXEQUALIZER_MID2CENTER, + EAXEQUALIZER_MID2WIDTH, + EAXEQUALIZER_HIGHGAIN, + EAXEQUALIZER_HIGHCUTOFF, +}; // EAXEQUALIZER_PROPERTY + + +struct EAXEQUALIZERPROPERTIES +{ + long lLowGain; // (mB) + float flLowCutOff; // (Hz) + long lMid1Gain; // (mB) + float flMid1Center; // (Hz) + float flMid1Width; // (octaves) + long lMid2Gain; // (mB) + float flMid2Center; // (Hz) + float flMid2Width; // (octaves) + long lHighGain; // (mB) + float flHighCutOff; // (Hz) +}; // EAXEQUALIZERPROPERTIES + + +constexpr auto EAXEQUALIZER_MINLOWGAIN = -1800L; +constexpr auto EAXEQUALIZER_MAXLOWGAIN = 1800L; +constexpr auto EAXEQUALIZER_DEFAULTLOWGAIN = 0L; + +constexpr auto EAXEQUALIZER_MINLOWCUTOFF = 50.0F; +constexpr auto EAXEQUALIZER_MAXLOWCUTOFF = 800.0F; +constexpr auto EAXEQUALIZER_DEFAULTLOWCUTOFF = 200.0F; + +constexpr auto EAXEQUALIZER_MINMID1GAIN = -1800L; +constexpr auto EAXEQUALIZER_MAXMID1GAIN = 1800L; +constexpr auto EAXEQUALIZER_DEFAULTMID1GAIN = 0L; + +constexpr auto EAXEQUALIZER_MINMID1CENTER = 200.0F; +constexpr auto EAXEQUALIZER_MAXMID1CENTER = 3000.0F; +constexpr auto EAXEQUALIZER_DEFAULTMID1CENTER = 500.0F; + +constexpr auto EAXEQUALIZER_MINMID1WIDTH = 0.01F; +constexpr auto EAXEQUALIZER_MAXMID1WIDTH = 1.0F; +constexpr auto EAXEQUALIZER_DEFAULTMID1WIDTH = 1.0F; + +constexpr auto EAXEQUALIZER_MINMID2GAIN = -1800L; +constexpr auto EAXEQUALIZER_MAXMID2GAIN = 1800L; +constexpr auto EAXEQUALIZER_DEFAULTMID2GAIN = 0L; + +constexpr auto EAXEQUALIZER_MINMID2CENTER = 1000.0F; +constexpr auto EAXEQUALIZER_MAXMID2CENTER = 8000.0F; +constexpr auto EAXEQUALIZER_DEFAULTMID2CENTER = 3000.0F; + +constexpr auto EAXEQUALIZER_MINMID2WIDTH = 0.01F; +constexpr auto EAXEQUALIZER_MAXMID2WIDTH = 1.0F; +constexpr auto EAXEQUALIZER_DEFAULTMID2WIDTH = 1.0F; + +constexpr auto EAXEQUALIZER_MINHIGHGAIN = -1800L; +constexpr auto EAXEQUALIZER_MAXHIGHGAIN = 1800L; +constexpr auto EAXEQUALIZER_DEFAULTHIGHGAIN = 0L; + +constexpr auto EAXEQUALIZER_MINHIGHCUTOFF = 4000.0F; +constexpr auto EAXEQUALIZER_MAXHIGHCUTOFF = 16000.0F; +constexpr auto EAXEQUALIZER_DEFAULTHIGHCUTOFF = 6000.0F; + + +// Flanger Effect + +extern const GUID EAX_FLANGER_EFFECT; + +enum EAXFLANGER_PROPERTY : + unsigned int +{ + EAXFLANGER_NONE, + EAXFLANGER_ALLPARAMETERS, + EAXFLANGER_WAVEFORM, + EAXFLANGER_PHASE, + EAXFLANGER_RATE, + EAXFLANGER_DEPTH, + EAXFLANGER_FEEDBACK, + EAXFLANGER_DELAY, +}; // EAXFLANGER_PROPERTY + +enum : + unsigned long +{ + EAX_FLANGER_SINUSOID, + EAX_FLANGER_TRIANGLE, +}; + +struct EAXFLANGERPROPERTIES +{ + unsigned long ulWaveform; // Waveform selector - see enum above + long lPhase; // Phase (Degrees) + float flRate; // Rate (Hz) + float flDepth; // Depth (0 to 1) + float flFeedback; // Feedback (0 to 1) + float flDelay; // Delay (seconds) +}; // EAXFLANGERPROPERTIES + + +constexpr auto EAXFLANGER_MINWAVEFORM = 0UL; +constexpr auto EAXFLANGER_MAXWAVEFORM = 1UL; +constexpr auto EAXFLANGER_DEFAULTWAVEFORM = 1UL; + +constexpr auto EAXFLANGER_MINPHASE = -180L; +constexpr auto EAXFLANGER_MAXPHASE = 180L; +constexpr auto EAXFLANGER_DEFAULTPHASE = 0L; + +constexpr auto EAXFLANGER_MINRATE = 0.0F; +constexpr auto EAXFLANGER_MAXRATE = 10.0F; +constexpr auto EAXFLANGER_DEFAULTRATE = 0.27F; + +constexpr auto EAXFLANGER_MINDEPTH = 0.0F; +constexpr auto EAXFLANGER_MAXDEPTH = 1.0F; +constexpr auto EAXFLANGER_DEFAULTDEPTH = 1.0F; + +constexpr auto EAXFLANGER_MINFEEDBACK = -1.0F; +constexpr auto EAXFLANGER_MAXFEEDBACK = 1.0F; +constexpr auto EAXFLANGER_DEFAULTFEEDBACK = -0.5F; + +constexpr auto EAXFLANGER_MINDELAY = 0.0002F; +constexpr auto EAXFLANGER_MAXDELAY = 0.004F; +constexpr auto EAXFLANGER_DEFAULTDELAY = 0.002F; + + +// Frequency Shifter Effect + +extern const GUID EAX_FREQUENCYSHIFTER_EFFECT; + +enum EAXFREQUENCYSHIFTER_PROPERTY : + unsigned int +{ + EAXFREQUENCYSHIFTER_NONE, + EAXFREQUENCYSHIFTER_ALLPARAMETERS, + EAXFREQUENCYSHIFTER_FREQUENCY, + EAXFREQUENCYSHIFTER_LEFTDIRECTION, + EAXFREQUENCYSHIFTER_RIGHTDIRECTION, +}; // EAXFREQUENCYSHIFTER_PROPERTY + +enum : + unsigned long +{ + EAX_FREQUENCYSHIFTER_DOWN, + EAX_FREQUENCYSHIFTER_UP, + EAX_FREQUENCYSHIFTER_OFF +}; + +struct EAXFREQUENCYSHIFTERPROPERTIES +{ + float flFrequency; // (Hz) + unsigned long ulLeftDirection; // see enum above + unsigned long ulRightDirection; // see enum above +}; // EAXFREQUENCYSHIFTERPROPERTIES + + +constexpr auto EAXFREQUENCYSHIFTER_MINFREQUENCY = 0.0F; +constexpr auto EAXFREQUENCYSHIFTER_MAXFREQUENCY = 24000.0F; +constexpr auto EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY = EAXFREQUENCYSHIFTER_MINFREQUENCY; + +constexpr auto EAXFREQUENCYSHIFTER_MINLEFTDIRECTION = 0UL; +constexpr auto EAXFREQUENCYSHIFTER_MAXLEFTDIRECTION = 2UL; +constexpr auto EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION = EAXFREQUENCYSHIFTER_MINLEFTDIRECTION; + +constexpr auto EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION = 0UL; +constexpr auto EAXFREQUENCYSHIFTER_MAXRIGHTDIRECTION = 2UL; +constexpr auto EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION = EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION; + + +// Vocal Morpher Effect + +extern const GUID EAX_VOCALMORPHER_EFFECT; + +enum EAXVOCALMORPHER_PROPERTY : + unsigned int +{ + EAXVOCALMORPHER_NONE, + EAXVOCALMORPHER_ALLPARAMETERS, + EAXVOCALMORPHER_PHONEMEA, + EAXVOCALMORPHER_PHONEMEACOARSETUNING, + EAXVOCALMORPHER_PHONEMEB, + EAXVOCALMORPHER_PHONEMEBCOARSETUNING, + EAXVOCALMORPHER_WAVEFORM, + EAXVOCALMORPHER_RATE, +}; // EAXVOCALMORPHER_PROPERTY + +enum : + unsigned long +{ + A, + E, + I, + O, + U, + AA, + AE, + AH, + AO, + EH, + ER, + IH, + IY, + UH, + UW, + B, + D, + F, + G, + J, + K, + L, + M, + N, + P, + R, + S, + T, + V, + Z, +}; + +enum : + unsigned long +{ + EAX_VOCALMORPHER_SINUSOID, + EAX_VOCALMORPHER_TRIANGLE, + EAX_VOCALMORPHER_SAWTOOTH +}; + +// Use this structure for EAXVOCALMORPHER_ALLPARAMETERS +struct EAXVOCALMORPHERPROPERTIES +{ + unsigned long ulPhonemeA; // see enum above + long lPhonemeACoarseTuning; // (semitones) + unsigned long ulPhonemeB; // see enum above + long lPhonemeBCoarseTuning; // (semitones) + unsigned long ulWaveform; // Waveform selector - see enum above + float flRate; // (Hz) +}; // EAXVOCALMORPHERPROPERTIES + + +constexpr auto EAXVOCALMORPHER_MINPHONEMEA = 0UL; +constexpr auto EAXVOCALMORPHER_MAXPHONEMEA = 29UL; +constexpr auto EAXVOCALMORPHER_DEFAULTPHONEMEA = EAXVOCALMORPHER_MINPHONEMEA; + +constexpr auto EAXVOCALMORPHER_MINPHONEMEACOARSETUNING = -24L; +constexpr auto EAXVOCALMORPHER_MAXPHONEMEACOARSETUNING = 24L; +constexpr auto EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING = 0L; + +constexpr auto EAXVOCALMORPHER_MINPHONEMEB = 0UL; +constexpr auto EAXVOCALMORPHER_MAXPHONEMEB = 29UL; +constexpr auto EAXVOCALMORPHER_DEFAULTPHONEMEB = 10UL; + +constexpr auto EAXVOCALMORPHER_MINPHONEMEBCOARSETUNING = -24L; +constexpr auto EAXVOCALMORPHER_MAXPHONEMEBCOARSETUNING = 24L; +constexpr auto EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING = 0L; + +constexpr auto EAXVOCALMORPHER_MINWAVEFORM = 0UL; +constexpr auto EAXVOCALMORPHER_MAXWAVEFORM = 2UL; +constexpr auto EAXVOCALMORPHER_DEFAULTWAVEFORM = EAXVOCALMORPHER_MINWAVEFORM; + +constexpr auto EAXVOCALMORPHER_MINRATE = 0.0F; +constexpr auto EAXVOCALMORPHER_MAXRATE = 10.0F; +constexpr auto EAXVOCALMORPHER_DEFAULTRATE = 1.41F; + + +// Pitch Shifter Effect + +extern const GUID EAX_PITCHSHIFTER_EFFECT; + +enum EAXPITCHSHIFTER_PROPERTY : + unsigned int +{ + EAXPITCHSHIFTER_NONE, + EAXPITCHSHIFTER_ALLPARAMETERS, + EAXPITCHSHIFTER_COARSETUNE, + EAXPITCHSHIFTER_FINETUNE, +}; // EAXPITCHSHIFTER_PROPERTY + +struct EAXPITCHSHIFTERPROPERTIES +{ + long lCoarseTune; // Amount of pitch shift (semitones) + long lFineTune; // Amount of pitch shift (cents) +}; // EAXPITCHSHIFTERPROPERTIES + + +constexpr auto EAXPITCHSHIFTER_MINCOARSETUNE = -12L; +constexpr auto EAXPITCHSHIFTER_MAXCOARSETUNE = 12L; +constexpr auto EAXPITCHSHIFTER_DEFAULTCOARSETUNE = 12L; + +constexpr auto EAXPITCHSHIFTER_MINFINETUNE = -50L; +constexpr auto EAXPITCHSHIFTER_MAXFINETUNE = 50L; +constexpr auto EAXPITCHSHIFTER_DEFAULTFINETUNE = 0L; + + +// Ring Modulator Effect + +extern const GUID EAX_RINGMODULATOR_EFFECT; + +enum EAXRINGMODULATOR_PROPERTY : + unsigned int +{ + EAXRINGMODULATOR_NONE, + EAXRINGMODULATOR_ALLPARAMETERS, + EAXRINGMODULATOR_FREQUENCY, + EAXRINGMODULATOR_HIGHPASSCUTOFF, + EAXRINGMODULATOR_WAVEFORM, +}; // EAXRINGMODULATOR_PROPERTY + +enum : + unsigned long +{ + EAX_RINGMODULATOR_SINUSOID, + EAX_RINGMODULATOR_SAWTOOTH, + EAX_RINGMODULATOR_SQUARE, +}; + +// Use this structure for EAXRINGMODULATOR_ALLPARAMETERS +struct EAXRINGMODULATORPROPERTIES +{ + float flFrequency; // Frequency of modulation (Hz) + float flHighPassCutOff; // Cut-off frequency of high-pass filter (Hz) + unsigned long ulWaveform; // Waveform selector - see enum above +}; // EAXRINGMODULATORPROPERTIES + + +constexpr auto EAXRINGMODULATOR_MINFREQUENCY = 0.0F; +constexpr auto EAXRINGMODULATOR_MAXFREQUENCY = 8000.0F; +constexpr auto EAXRINGMODULATOR_DEFAULTFREQUENCY = 440.0F; + +constexpr auto EAXRINGMODULATOR_MINHIGHPASSCUTOFF = 0.0F; +constexpr auto EAXRINGMODULATOR_MAXHIGHPASSCUTOFF = 24000.0F; +constexpr auto EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF = 800.0F; + +constexpr auto EAXRINGMODULATOR_MINWAVEFORM = 0UL; +constexpr auto EAXRINGMODULATOR_MAXWAVEFORM = 2UL; +constexpr auto EAXRINGMODULATOR_DEFAULTWAVEFORM = EAXRINGMODULATOR_MINWAVEFORM; + + +using LPEAXSET = ALenum(AL_APIENTRY*)( + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size); + +using LPEAXGET = ALenum(AL_APIENTRY*)( + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size); + + +#endif // !EAX_API_INCLUDED diff --git a/al/eax/eax_call.cpp b/al/eax/eax_call.cpp new file mode 100644 index 00000000..19565852 --- /dev/null +++ b/al/eax/eax_call.cpp @@ -0,0 +1,323 @@ +#include "config.h" + +#include "eax_call.h" +#include "exception.h" + + +namespace { + +constexpr auto deferred_flag = 0x80000000U; + +class EaxEaxCallException : + public EaxException +{ +public: + explicit EaxEaxCallException( + const char* message) + : + EaxException{"EAX_EAX_CALL", message} + { + } +}; // EaxEaxCallException + +} // namespace + + +EaxEaxCall::EaxEaxCall( + bool is_get, + const GUID& property_set_guid, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size) + : is_get_{is_get}, version_{0}, property_set_id_{EaxEaxCallPropertySetId::none} + , property_id_{property_id & ~deferred_flag}, property_source_id_{property_source_id} + , property_buffer_{property_buffer}, property_size_{property_size} +{ + if (false) + { + } + else if (property_set_guid == EAXPROPERTYID_EAX40_Context) + { + version_ = 4; + property_set_id_ = EaxEaxCallPropertySetId::context; + } + else if (property_set_guid == EAXPROPERTYID_EAX50_Context) + { + version_ = 5; + property_set_id_ = EaxEaxCallPropertySetId::context; + } + else if (property_set_guid == DSPROPSETID_EAX20_ListenerProperties) + { + version_ = 2; + fx_slot_index_ = 0u; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect; + property_id_ = convert_eax_v2_0_listener_property_id(property_id_); + } + else if (property_set_guid == DSPROPSETID_EAX30_ListenerProperties) + { + version_ = 3; + fx_slot_index_ = 0u; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect; + } + else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot0) + { + version_ = 4; + fx_slot_index_ = 0u; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot0) + { + version_ = 5; + fx_slot_index_ = 0u; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot1) + { + version_ = 4; + fx_slot_index_ = 1u; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot1) + { + version_ = 5; + fx_slot_index_ = 1u; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot2) + { + version_ = 4; + fx_slot_index_ = 2u; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot2) + { + version_ = 5; + fx_slot_index_ = 2u; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot3) + { + version_ = 4; + fx_slot_index_ = 3u; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot3) + { + version_ = 5; + fx_slot_index_ = 3u; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == DSPROPSETID_EAX20_BufferProperties) + { + version_ = 2; + property_set_id_ = EaxEaxCallPropertySetId::source; + property_id_ = convert_eax_v2_0_buffer_property_id(property_id_); + } + else if (property_set_guid == DSPROPSETID_EAX30_BufferProperties) + { + version_ = 3; + property_set_id_ = EaxEaxCallPropertySetId::source; + } + else if (property_set_guid == EAXPROPERTYID_EAX40_Source) + { + version_ = 4; + property_set_id_ = EaxEaxCallPropertySetId::source; + } + else if (property_set_guid == EAXPROPERTYID_EAX50_Source) + { + version_ = 5; + property_set_id_ = EaxEaxCallPropertySetId::source; + } + else if (property_set_guid == DSPROPSETID_EAX_ReverbProperties) + { + version_ = 1; + fx_slot_index_ = 0u; + property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect; + } + else if (property_set_guid == DSPROPSETID_EAXBUFFER_ReverbProperties) + { + version_ = 1; + property_set_id_ = EaxEaxCallPropertySetId::source; + } + else + { + fail("Unsupported property set id."); + } + + if (version_ < 1 || version_ > 5) + { + fail("EAX version out of range."); + } + + if(!(property_id&deferred_flag)) + { + if(property_set_id_ != EaxEaxCallPropertySetId::fx_slot && property_id_ != 0) + { + if (!property_buffer) + { + fail("Null property buffer."); + } + + if (property_size == 0) + { + fail("Empty property."); + } + } + } + + if(property_set_id_ == EaxEaxCallPropertySetId::source && property_source_id_ == 0) + { + fail("Null AL source id."); + } + + if (property_set_id_ == EaxEaxCallPropertySetId::fx_slot) + { + if (property_id_ < EAXFXSLOT_NONE) + { + property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect; + } + } +} + +[[noreturn]] +void EaxEaxCall::fail( + const char* message) +{ + throw EaxEaxCallException{message}; +} + +ALuint EaxEaxCall::convert_eax_v2_0_listener_property_id( + ALuint property_id) +{ + switch (property_id) + { + case DSPROPERTY_EAX20LISTENER_NONE: + return EAXREVERB_NONE; + + case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS: + return EAXREVERB_ALLPARAMETERS; + + case DSPROPERTY_EAX20LISTENER_ROOM: + return EAXREVERB_ROOM; + + case DSPROPERTY_EAX20LISTENER_ROOMHF: + return EAXREVERB_ROOMHF; + + case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR: + return EAXREVERB_ROOMROLLOFFFACTOR; + + case DSPROPERTY_EAX20LISTENER_DECAYTIME: + return EAXREVERB_DECAYTIME; + + case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO: + return EAXREVERB_DECAYHFRATIO; + + case DSPROPERTY_EAX20LISTENER_REFLECTIONS: + return EAXREVERB_REFLECTIONS; + + case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY: + return EAXREVERB_REFLECTIONSDELAY; + + case DSPROPERTY_EAX20LISTENER_REVERB: + return EAXREVERB_REVERB; + + case DSPROPERTY_EAX20LISTENER_REVERBDELAY: + return EAXREVERB_REVERBDELAY; + + case DSPROPERTY_EAX20LISTENER_ENVIRONMENT: + return EAXREVERB_ENVIRONMENT; + + case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE: + return EAXREVERB_ENVIRONMENTSIZE; + + case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION: + return EAXREVERB_ENVIRONMENTDIFFUSION; + + case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF: + return EAXREVERB_AIRABSORPTIONHF; + + case DSPROPERTY_EAX20LISTENER_FLAGS: + return EAXREVERB_FLAGS; + + default: + fail("Unsupported EAX 2.0 listener property id."); + } +} + +ALuint EaxEaxCall::convert_eax_v2_0_buffer_property_id( + ALuint property_id) +{ + switch (property_id) + { + case DSPROPERTY_EAX20BUFFER_NONE: + return EAXSOURCE_NONE; + + case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS: + return EAXSOURCE_ALLPARAMETERS; + + case DSPROPERTY_EAX20BUFFER_DIRECT: + return EAXSOURCE_DIRECT; + + case DSPROPERTY_EAX20BUFFER_DIRECTHF: + return EAXSOURCE_DIRECTHF; + + case DSPROPERTY_EAX20BUFFER_ROOM: + return EAXSOURCE_ROOM; + + case DSPROPERTY_EAX20BUFFER_ROOMHF: + return EAXSOURCE_ROOMHF; + + case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR: + return EAXSOURCE_ROOMROLLOFFFACTOR; + + case DSPROPERTY_EAX20BUFFER_OBSTRUCTION: + return EAXSOURCE_OBSTRUCTION; + + case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO: + return EAXSOURCE_OBSTRUCTIONLFRATIO; + + case DSPROPERTY_EAX20BUFFER_OCCLUSION: + return EAXSOURCE_OCCLUSION; + + case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO: + return EAXSOURCE_OCCLUSIONLFRATIO; + + case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO: + return EAXSOURCE_OCCLUSIONROOMRATIO; + + case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF: + return EAXSOURCE_OUTSIDEVOLUMEHF; + + case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR: + return EAXSOURCE_AIRABSORPTIONFACTOR; + + case DSPROPERTY_EAX20BUFFER_FLAGS: + return EAXSOURCE_FLAGS; + + default: + fail("Unsupported EAX 2.0 buffer property id."); + } +} + + +EaxEaxCall create_eax_call( + bool is_get, + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size) +{ + if(!property_set_id) + throw EaxEaxCallException{"Null property set ID."}; + + return EaxEaxCall{ + is_get, + *property_set_id, + property_id, + property_source_id, + property_buffer, + property_size + }; +} diff --git a/al/eax/eax_call.h b/al/eax/eax_call.h new file mode 100644 index 00000000..2c90bdc3 --- /dev/null +++ b/al/eax/eax_call.h @@ -0,0 +1,117 @@ +#ifndef EAX_EAX_CALL_INCLUDED +#define EAX_EAX_CALL_INCLUDED + + +#include "AL/al.h" + +#include "alspan.h" + +#include "api.h" +#include "fx_slot_index.h" + + +enum class EaxEaxCallPropertySetId +{ + none, + + context, + fx_slot, + source, + fx_slot_effect, +}; // EaxEaxCallPropertySetId + + +class EaxEaxCall +{ +public: + EaxEaxCall( + bool is_get, + const GUID& property_set_guid, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size); + + bool is_get() const noexcept { return is_get_; } + int get_version() const noexcept { return version_; } + EaxEaxCallPropertySetId get_property_set_id() const noexcept { return property_set_id_; } + ALuint get_property_id() const noexcept { return property_id_; } + ALuint get_property_al_name() const noexcept { return property_source_id_; } + EaxFxSlotIndex get_fx_slot_index() const noexcept { return fx_slot_index_; } + + template< + typename TException, + typename TValue + > + TValue& get_value() const + { + if (property_size_ < static_cast(sizeof(TValue))) + { + throw TException{"Property buffer too small."}; + } + + return *static_cast(property_buffer_); + } + + template< + typename TException, + typename TValue + > + al::span get_values() const + { + if (property_size_ < static_cast(sizeof(TValue))) + { + throw TException{"Property buffer too small."}; + } + + const auto count = property_size_ / sizeof(TValue); + + return al::span{static_cast(property_buffer_), count}; + } + + template< + typename TException, + typename TValue + > + void set_value( + const TValue& value) const + { + get_value() = value; + } + + +private: + const bool is_get_; + int version_; + EaxFxSlotIndex fx_slot_index_; + EaxEaxCallPropertySetId property_set_id_; + + ALuint property_id_; + const ALuint property_source_id_; + ALvoid*const property_buffer_; + const ALuint property_size_; + + + [[noreturn]] + static void fail( + const char* message); + + + static ALuint convert_eax_v2_0_listener_property_id( + ALuint property_id); + + static ALuint convert_eax_v2_0_buffer_property_id( + ALuint property_id); +}; // EaxEaxCall + + +EaxEaxCall create_eax_call( + bool is_get, + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size); + + +#endif // !EAX_EAX_CALL_INCLUDED diff --git a/al/eax/effect.cpp b/al/eax/effect.cpp new file mode 100644 index 00000000..4e8faa73 --- /dev/null +++ b/al/eax/effect.cpp @@ -0,0 +1,3 @@ +#include "config.h" + +#include "effect.h" diff --git a/al/eax/effect.h b/al/eax/effect.h new file mode 100644 index 00000000..9c9fdef4 --- /dev/null +++ b/al/eax/effect.h @@ -0,0 +1,44 @@ +#ifndef EAX_EFFECT_INCLUDED +#define EAX_EFFECT_INCLUDED + + +#include + +#include "AL/al.h" +#include "core/effects/base.h" +#include "eax_call.h" + +class EaxEffect +{ +public: + EaxEffect(ALenum type) : al_effect_type_{type} { } + virtual ~EaxEffect() = default; + + const ALenum al_effect_type_; + EffectProps al_effect_props_{}; + + virtual void dispatch(const EaxEaxCall& eax_call) = 0; + + // Returns "true" if any immediated property was changed. + // [[nodiscard]] + virtual bool apply_deferred() = 0; +}; // EaxEffect + + +using EaxEffectUPtr = std::unique_ptr; + +EaxEffectUPtr eax_create_eax_null_effect(); +EaxEffectUPtr eax_create_eax_chorus_effect(); +EaxEffectUPtr eax_create_eax_distortion_effect(); +EaxEffectUPtr eax_create_eax_echo_effect(); +EaxEffectUPtr eax_create_eax_flanger_effect(); +EaxEffectUPtr eax_create_eax_frequency_shifter_effect(); +EaxEffectUPtr eax_create_eax_vocal_morpher_effect(); +EaxEffectUPtr eax_create_eax_pitch_shifter_effect(); +EaxEffectUPtr eax_create_eax_ring_modulator_effect(); +EaxEffectUPtr eax_create_eax_auto_wah_effect(); +EaxEffectUPtr eax_create_eax_compressor_effect(); +EaxEffectUPtr eax_create_eax_equalizer_effect(); +EaxEffectUPtr eax_create_eax_reverb_effect(); + +#endif // !EAX_EFFECT_INCLUDED diff --git a/al/eax/exception.cpp b/al/eax/exception.cpp new file mode 100644 index 00000000..3b319648 --- /dev/null +++ b/al/eax/exception.cpp @@ -0,0 +1,62 @@ +#include "config.h" + +#include "exception.h" + +#include +#include + + +EaxException::EaxException( + const char* context, + const char* message) + : + std::runtime_error{make_message(context, message)} +{ +} + +std::string EaxException::make_message( + const char* context, + const char* message) +{ + const auto context_size = (context ? std::string::traits_type::length(context) : 0); + const auto has_contex = (context_size > 0); + + const auto message_size = (message ? std::string::traits_type::length(message) : 0); + const auto has_message = (message_size > 0); + + if (!has_contex && !has_message) + { + return std::string{}; + } + + static constexpr char left_prefix[] = "["; + const auto left_prefix_size = std::string::traits_type::length(left_prefix); + + static constexpr char right_prefix[] = "] "; + const auto right_prefix_size = std::string::traits_type::length(right_prefix); + + const auto what_size = + ( + has_contex ? + left_prefix_size + context_size + right_prefix_size : + 0) + + message_size + + 1; + + auto what = std::string{}; + what.reserve(what_size); + + if (has_contex) + { + what.append(left_prefix, left_prefix_size); + what.append(context, context_size); + what.append(right_prefix, right_prefix_size); + } + + if (has_message) + { + what.append(message, message_size); + } + + return what; +} diff --git a/al/eax/exception.h b/al/eax/exception.h new file mode 100644 index 00000000..9a7acf71 --- /dev/null +++ b/al/eax/exception.h @@ -0,0 +1,25 @@ +#ifndef EAX_EXCEPTION_INCLUDED +#define EAX_EXCEPTION_INCLUDED + + +#include +#include + + +class EaxException : + public std::runtime_error +{ +public: + EaxException( + const char* context, + const char* message); + + +private: + static std::string make_message( + const char* context, + const char* message); +}; // EaxException + + +#endif // !EAX_EXCEPTION_INCLUDED diff --git a/al/eax/fx_slot_index.cpp b/al/eax/fx_slot_index.cpp new file mode 100644 index 00000000..28b11882 --- /dev/null +++ b/al/eax/fx_slot_index.cpp @@ -0,0 +1,71 @@ +#include "config.h" + +#include "fx_slot_index.h" + +#include "exception.h" + + +namespace +{ + + +class EaxFxSlotIndexException : + public EaxException +{ +public: + explicit EaxFxSlotIndexException( + const char* message) + : + EaxException{"EAX_FX_SLOT_INDEX", message} + { + } +}; // EaxFxSlotIndexException + + +} // namespace + + +void EaxFxSlotIndex::set(EaxFxSlotIndexValue index) +{ + if(index >= EaxFxSlotIndexValue{EAX_MAX_FXSLOTS}) + fail("Index out of range."); + + emplace(index); +} + +void EaxFxSlotIndex::set(const GUID &guid) +{ + if (false) + { + } + else if (guid == EAX_NULL_GUID) + { + reset(); + } + else if (guid == EAXPROPERTYID_EAX40_FXSlot0 || guid == EAXPROPERTYID_EAX50_FXSlot0) + { + emplace(0u); + } + else if (guid == EAXPROPERTYID_EAX40_FXSlot1 || guid == EAXPROPERTYID_EAX50_FXSlot1) + { + emplace(1u); + } + else if (guid == EAXPROPERTYID_EAX40_FXSlot2 || guid == EAXPROPERTYID_EAX50_FXSlot2) + { + emplace(2u); + } + else if (guid == EAXPROPERTYID_EAX40_FXSlot3 || guid == EAXPROPERTYID_EAX50_FXSlot3) + { + emplace(3u); + } + else + { + fail("Unsupported GUID."); + } +} + +[[noreturn]] +void EaxFxSlotIndex::fail(const char* message) +{ + throw EaxFxSlotIndexException{message}; +} diff --git a/al/eax/fx_slot_index.h b/al/eax/fx_slot_index.h new file mode 100644 index 00000000..63dba037 --- /dev/null +++ b/al/eax/fx_slot_index.h @@ -0,0 +1,41 @@ +#ifndef EAX_FX_SLOT_INDEX_INCLUDED +#define EAX_FX_SLOT_INDEX_INCLUDED + + +#include + +#include "aloptional.h" +#include "api.h" + + +using EaxFxSlotIndexValue = std::size_t; + +class EaxFxSlotIndex : public al::optional +{ +public: + using al::optional::optional; + + EaxFxSlotIndex& operator=(const EaxFxSlotIndexValue &value) { set(value); return *this; } + EaxFxSlotIndex& operator=(const GUID &guid) { set(guid); return *this; } + + void set(EaxFxSlotIndexValue index); + void set(const GUID& guid); + +private: + [[noreturn]] + static void fail(const char *message); +}; // EaxFxSlotIndex + +inline bool operator==(const EaxFxSlotIndex& lhs, const EaxFxSlotIndex& rhs) noexcept +{ + if(lhs.has_value() != rhs.has_value()) + return false; + if(lhs.has_value()) + return *lhs == *rhs; + return true; +} + +inline bool operator!=(const EaxFxSlotIndex& lhs, const EaxFxSlotIndex& rhs) noexcept +{ return !(lhs == rhs); } + +#endif // !EAX_FX_SLOT_INDEX_INCLUDED diff --git a/al/eax/fx_slots.cpp b/al/eax/fx_slots.cpp new file mode 100644 index 00000000..5897e951 --- /dev/null +++ b/al/eax/fx_slots.cpp @@ -0,0 +1,83 @@ +#include "config.h" + +#include "fx_slots.h" + +#include + +#include "api.h" +#include "exception.h" + + +namespace +{ + + +class EaxFxSlotsException : + public EaxException +{ +public: + explicit EaxFxSlotsException( + const char* message) + : + EaxException{"EAX_FX_SLOTS", message} + { + } +}; // EaxFxSlotsException + + +} // namespace + + +void EaxFxSlots::initialize( + ALCcontext& al_context) +{ + initialize_fx_slots(al_context); +} + +void EaxFxSlots::uninitialize() noexcept +{ + for (auto& fx_slot : fx_slots_) + { + fx_slot = nullptr; + } +} + +const ALeffectslot& EaxFxSlots::get(EaxFxSlotIndex index) const +{ + if(!index.has_value()) + fail("Empty index."); + return *fx_slots_[index.value()]; +} + +ALeffectslot& EaxFxSlots::get(EaxFxSlotIndex index) +{ + if(!index.has_value()) + fail("Empty index."); + return *fx_slots_[index.value()]; +} + +void EaxFxSlots::unlock_legacy() noexcept +{ + fx_slots_[0]->eax_unlock_legacy(); + fx_slots_[1]->eax_unlock_legacy(); +} + +[[noreturn]] +void EaxFxSlots::fail( + const char* message) +{ + throw EaxFxSlotsException{message}; +} + +void EaxFxSlots::initialize_fx_slots( + ALCcontext& al_context) +{ + auto fx_slot_index = EaxFxSlotIndexValue{}; + + for (auto& fx_slot : fx_slots_) + { + fx_slot = eax_create_al_effect_slot(al_context); + fx_slot->eax_initialize(al_context, fx_slot_index); + fx_slot_index += 1; + } +} diff --git a/al/eax/fx_slots.h b/al/eax/fx_slots.h new file mode 100644 index 00000000..49cabd75 --- /dev/null +++ b/al/eax/fx_slots.h @@ -0,0 +1,53 @@ +#ifndef EAX_FX_SLOTS_INCLUDED +#define EAX_FX_SLOTS_INCLUDED + + +#include + +#include "al/auxeffectslot.h" + +#include "api.h" +#include "fx_slot_index.h" + + +class EaxFxSlots +{ +public: + void initialize( + ALCcontext& al_context); + + void uninitialize() noexcept; + + void commit() + { + for(auto& fx_slot : fx_slots_) + fx_slot->eax_commit(); + } + + + const ALeffectslot& get( + EaxFxSlotIndex index) const; + + ALeffectslot& get( + EaxFxSlotIndex index); + + void unlock_legacy() noexcept; + + +private: + using Items = std::array; + + + Items fx_slots_{}; + + + [[noreturn]] + static void fail( + const char* message); + + void initialize_fx_slots( + ALCcontext& al_context); +}; // EaxFxSlots + + +#endif // !EAX_FX_SLOTS_INCLUDED diff --git a/al/eax/globals.cpp b/al/eax/globals.cpp new file mode 100644 index 00000000..80e9dbfe --- /dev/null +++ b/al/eax/globals.cpp @@ -0,0 +1,21 @@ +#include "config.h" + +#include "globals.h" + + +bool eax_g_is_enabled = true; + + +const char eax1_ext_name[] = "EAX"; +const char eax2_ext_name[] = "EAX2.0"; +const char eax3_ext_name[] = "EAX3.0"; +const char eax4_ext_name[] = "EAX4.0"; +const char eax5_ext_name[] = "EAX5.0"; + +const char eax_x_ram_ext_name[] = "EAX-RAM"; + +const char eax_eax_set_func_name[] = "EAXSet"; +const char eax_eax_get_func_name[] = "EAXGet"; + +const char eax_eax_set_buffer_mode_func_name[] = "EAXSetBufferMode"; +const char eax_eax_get_buffer_mode_func_name[] = "EAXGetBufferMode"; diff --git a/al/eax/globals.h b/al/eax/globals.h new file mode 100644 index 00000000..1b4d63b8 --- /dev/null +++ b/al/eax/globals.h @@ -0,0 +1,22 @@ +#ifndef EAX_GLOBALS_INCLUDED +#define EAX_GLOBALS_INCLUDED + + +extern bool eax_g_is_enabled; + + +extern const char eax1_ext_name[]; +extern const char eax2_ext_name[]; +extern const char eax3_ext_name[]; +extern const char eax4_ext_name[]; +extern const char eax5_ext_name[]; + +extern const char eax_x_ram_ext_name[]; + +extern const char eax_eax_set_func_name[]; +extern const char eax_eax_get_func_name[]; + +extern const char eax_eax_set_buffer_mode_func_name[]; +extern const char eax_eax_get_buffer_mode_func_name[]; + +#endif // !EAX_GLOBALS_INCLUDED diff --git a/al/eax/utils.cpp b/al/eax/utils.cpp new file mode 100644 index 00000000..9fa2871d --- /dev/null +++ b/al/eax/utils.cpp @@ -0,0 +1,36 @@ +#include "config.h" + +#include "utils.h" + +#include +#include + +#include "core/logging.h" + + +void eax_log_exception( + const char* message) noexcept +{ + const auto exception_ptr = std::current_exception(); + + assert(exception_ptr); + + if (message) + { + ERR("%s\n", message); + } + + try + { + std::rethrow_exception(exception_ptr); + } + catch (const std::exception& ex) + { + const auto ex_message = ex.what(); + ERR("%s\n", ex_message); + } + catch (...) + { + ERR("%s\n", "Generic exception."); + } +} diff --git a/al/eax/utils.h b/al/eax/utils.h new file mode 100644 index 00000000..d3d4a196 --- /dev/null +++ b/al/eax/utils.h @@ -0,0 +1,132 @@ +#ifndef EAX_UTILS_INCLUDED +#define EAX_UTILS_INCLUDED + +#include +#include +#include +#include + + +struct EaxAlLowPassParam +{ + float gain; + float gain_hf; +}; // EaxAlLowPassParam + + +void eax_log_exception( + const char* message = nullptr) noexcept; + + +template< + typename TException, + typename TValue +> +void eax_validate_range( + const char* value_name, + const TValue& value, + const TValue& min_value, + const TValue& max_value) +{ + if (value >= min_value && value <= max_value) + { + return; + } + + const auto message = + std::string{value_name} + + " out of range (value: " + + std::to_string(value) + "; min: " + + std::to_string(min_value) + "; max: " + + std::to_string(max_value) + ")."; + + throw TException{message.c_str()}; +} + + +namespace detail +{ + + +template< + typename T +> +struct EaxIsBitFieldStruct +{ +private: + using yes = std::true_type; + using no = std::false_type; + + template< + typename U + > + static auto test(int) -> decltype(std::declval(), yes{}); + + template< + typename + > + static no test(...); + + +public: + static constexpr auto value = std::is_same(0)), yes>::value; +}; // EaxIsBitFieldStruct + + +template< + typename T, + typename TValue +> +inline bool eax_bit_fields_are_equal( + const T& lhs, + const T& rhs) noexcept +{ + static_assert(sizeof(T) == sizeof(TValue), "Invalid type size."); + + return reinterpret_cast(lhs) == reinterpret_cast(rhs); +} + + +} // namespace detail + + +template< + typename T, + std::enable_if_t::value, int> = 0 +> +inline bool operator==( + const T& lhs, + const T& rhs) noexcept +{ + using Value = std::conditional_t< + sizeof(T) == 1, + std::uint8_t, + std::conditional_t< + sizeof(T) == 2, + std::uint16_t, + std::conditional_t< + sizeof(T) == 4, + std::uint32_t, + void + > + > + >; + + static_assert(!std::is_same::value, "Unsupported type."); + + return detail::eax_bit_fields_are_equal(lhs, rhs); +} + +template< + typename T, + std::enable_if_t::value, int> = 0 +> +inline bool operator!=( + const T& lhs, + const T& rhs) noexcept +{ + return !(lhs == rhs); +} + + +#endif // !EAX_UTILS_INCLUDED diff --git a/al/eax/x_ram.cpp b/al/eax/x_ram.cpp new file mode 100644 index 00000000..7332c82e --- /dev/null +++ b/al/eax/x_ram.cpp @@ -0,0 +1,3 @@ +#include "config.h" + +#include "x_ram.h" diff --git a/al/eax/x_ram.h b/al/eax/x_ram.h new file mode 100644 index 00000000..438b9916 --- /dev/null +++ b/al/eax/x_ram.h @@ -0,0 +1,38 @@ +#ifndef EAX_X_RAM_INCLUDED +#define EAX_X_RAM_INCLUDED + + +#include "AL/al.h" + + +constexpr auto eax_x_ram_min_size = ALsizei{}; +constexpr auto eax_x_ram_max_size = ALsizei{64 * 1'024 * 1'024}; + + +constexpr auto AL_EAX_RAM_SIZE = ALenum{0x202201}; +constexpr auto AL_EAX_RAM_FREE = ALenum{0x202202}; + +constexpr auto AL_STORAGE_AUTOMATIC = ALenum{0x202203}; +constexpr auto AL_STORAGE_HARDWARE = ALenum{0x202204}; +constexpr auto AL_STORAGE_ACCESSIBLE = ALenum{0x202205}; + + +constexpr auto AL_EAX_RAM_SIZE_NAME = "AL_EAX_RAM_SIZE"; +constexpr auto AL_EAX_RAM_FREE_NAME = "AL_EAX_RAM_FREE"; + +constexpr auto AL_STORAGE_AUTOMATIC_NAME = "AL_STORAGE_AUTOMATIC"; +constexpr auto AL_STORAGE_HARDWARE_NAME = "AL_STORAGE_HARDWARE"; +constexpr auto AL_STORAGE_ACCESSIBLE_NAME = "AL_STORAGE_ACCESSIBLE"; + + +ALboolean AL_APIENTRY EAXSetBufferMode( + ALsizei n, + const ALuint* buffers, + ALint value); + +ALenum AL_APIENTRY EAXGetBufferMode( + ALuint buffer, + ALint* pReserved); + + +#endif // !EAX_X_RAM_INCLUDED diff --git a/al/eax_api.cpp b/al/eax_api.cpp deleted file mode 100644 index 6b1f7fcf..00000000 --- a/al/eax_api.cpp +++ /dev/null @@ -1,1213 +0,0 @@ -// -// EAX API. -// -// Based on headers `eax[2-5].h` included in Doom 3 source code: -// https://github.com/id-Software/DOOM-3/tree/master/neo/openal/include -// - -#include "config.h" - -#include - -#include "al/eax_api.h" - - -const GUID DSPROPSETID_EAX_ReverbProperties = -{ - 0x4A4E6FC1, - 0xC341, - 0x11D1, - {0xB7, 0x3A, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} -}; - -const GUID DSPROPSETID_EAXBUFFER_ReverbProperties = -{ - 0x4A4E6FC0, - 0xC341, - 0x11D1, - {0xB7, 0x3A, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} -}; - -const GUID DSPROPSETID_EAX20_ListenerProperties = -{ - 0x306A6A8, - 0xB224, - 0x11D2, - {0x99, 0xE5, 0x00, 0x00, 0xE8, 0xD8, 0xC7, 0x22} -}; - -const GUID DSPROPSETID_EAX20_BufferProperties = -{ - 0x306A6A7, - 0xB224, - 0x11D2, - {0x99, 0xE5, 0x00, 0x00, 0xE8, 0xD8, 0xC7, 0x22} -}; - -const GUID DSPROPSETID_EAX30_ListenerProperties = -{ - 0xA8FA6882, - 0xB476, - 0x11D3, - {0xBD, 0xB9, 0x00, 0xC0, 0xF0, 0x2D, 0xDF, 0x87} -}; - -const GUID DSPROPSETID_EAX30_BufferProperties = -{ - 0xA8FA6881, - 0xB476, - 0x11D3, - {0xBD, 0xB9, 0x00, 0xC0, 0xF0, 0x2D, 0xDF, 0x87} -}; - -const GUID EAX_NULL_GUID = -{ - 0x00000000, - 0x0000, - 0x0000, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} -}; - -const GUID EAX_PrimaryFXSlotID = -{ - 0xF317866D, - 0x924C, - 0x450C, - {0x86, 0x1B, 0xE6, 0xDA, 0xA2, 0x5E, 0x7C, 0x20} -}; - -const GUID EAXPROPERTYID_EAX40_Context = -{ - 0x1D4870AD, - 0xDEF, - 0x43C0, - {0xA4, 0xC, 0x52, 0x36, 0x32, 0x29, 0x63, 0x42} -}; - -const GUID EAXPROPERTYID_EAX50_Context = -{ - 0x57E13437, - 0xB932, - 0x4AB2, - {0xB8, 0xBD, 0x52, 0x66, 0xC1, 0xA8, 0x87, 0xEE} -}; - -const GUID EAXPROPERTYID_EAX40_FXSlot0 = -{ - 0xC4D79F1E, - 0xF1AC, - 0x436B, - {0xA8, 0x1D, 0xA7, 0x38, 0xE7, 0x04, 0x54, 0x69} -}; - -const GUID EAXPROPERTYID_EAX50_FXSlot0 = -{ - 0x91F9590F, - 0xC388, - 0x407A, - {0x84, 0xB0, 0x1B, 0xAE, 0xE, 0xF7, 0x1A, 0xBC} -}; - -const GUID EAXPROPERTYID_EAX40_FXSlot1 = -{ - 0x8C00E96, - 0x74BE, - 0x4491, - {0x93, 0xAA, 0xE8, 0xAD, 0x35, 0xA4, 0x91, 0x17} -}; - -const GUID EAXPROPERTYID_EAX50_FXSlot1 = -{ - 0x8F5F7ACA, - 0x9608, - 0x4965, - {0x81, 0x37, 0x82, 0x13, 0xC7, 0xB9, 0xD9, 0xDE} -}; - -const GUID EAXPROPERTYID_EAX40_FXSlot2 = -{ - 0x1D433B88, - 0xF0F6, - 0x4637, - {0x91, 0x9F, 0x60, 0xE7, 0xE0, 0x6B, 0x5E, 0xDD} -}; - -const GUID EAXPROPERTYID_EAX50_FXSlot2 = -{ - 0x3C0F5252, - 0x9834, - 0x46F0, - {0xA1, 0xD8, 0x5B, 0x95, 0xC4, 0xA0, 0xA, 0x30} -}; - -const GUID EAXPROPERTYID_EAX40_FXSlot3 = -{ - 0xEFFF08EA, - 0xC7D8, - 0x44AB, - {0x93, 0xAD, 0x6D, 0xBD, 0x5F, 0x91, 0x00, 0x64} -}; - -const GUID EAXPROPERTYID_EAX50_FXSlot3 = -{ - 0xE2EB0EAA, - 0xE806, - 0x45E7, - {0x9F, 0x86, 0x06, 0xC1, 0x57, 0x1A, 0x6F, 0xA3} -}; - -const GUID EAXPROPERTYID_EAX40_Source = -{ - 0x1B86B823, - 0x22DF, - 0x4EAE, - {0x8B, 0x3C, 0x12, 0x78, 0xCE, 0x54, 0x42, 0x27} -}; - -const GUID EAXPROPERTYID_EAX50_Source = -{ - 0x5EDF82F0, - 0x24A7, - 0x4F38, - {0x8E, 0x64, 0x2F, 0x09, 0xCA, 0x05, 0xDE, 0xE1} -}; - -const GUID EAX_REVERB_EFFECT = -{ - 0xCF95C8F, - 0xA3CC, - 0x4849, - {0xB0, 0xB6, 0x83, 0x2E, 0xCC, 0x18, 0x22, 0xDF} -}; - -const GUID EAX_AGCCOMPRESSOR_EFFECT = -{ - 0xBFB7A01E, - 0x7825, - 0x4039, - {0x92, 0x7F, 0x03, 0xAA, 0xBD, 0xA0, 0xC5, 0x60} -}; - -const GUID EAX_AUTOWAH_EFFECT = -{ - 0xEC3130C0, - 0xAC7A, - 0x11D2, - {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} -}; - -const GUID EAX_CHORUS_EFFECT = -{ - 0xDE6D6FE0, - 0xAC79, - 0x11D2, - {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} -}; - -const GUID EAX_DISTORTION_EFFECT = -{ - 0x975A4CE0, - 0xAC7E, - 0x11D2, - {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} -}; - -const GUID EAX_ECHO_EFFECT = -{ - 0xE9F1BC0, - 0xAC82, - 0x11D2, - {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} -}; - -const GUID EAX_EQUALIZER_EFFECT = -{ - 0x65F94CE0, - 0x9793, - 0x11D3, - {0x93, 0x9D, 0x00, 0xC0, 0xF0, 0x2D, 0xD6, 0xF0} -}; - -const GUID EAX_FLANGER_EFFECT = -{ - 0xA70007C0, - 0x7D2, - 0x11D3, - {0x9B, 0x1E, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} -}; - -const GUID EAX_FREQUENCYSHIFTER_EFFECT = -{ - 0xDC3E1880, - 0x9212, - 0x11D3, - {0x93, 0x9D, 0x00, 0xC0, 0xF0, 0x2D, 0xD6, 0xF0} -}; - -const GUID EAX_VOCALMORPHER_EFFECT = -{ - 0xE41CF10C, - 0x3383, - 0x11D2, - {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} -}; - -const GUID EAX_PITCHSHIFTER_EFFECT = -{ - 0xE7905100, - 0xAFB2, - 0x11D2, - {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} -}; - -const GUID EAX_RINGMODULATOR_EFFECT = -{ - 0xB89FE60, - 0xAFB5, - 0x11D2, - {0x88, 0xDD, 0x00, 0xA0, 0x24, 0xD1, 0x3C, 0xE1} -}; - - -bool operator==( - const EAX40CONTEXTPROPERTIES& lhs, - const EAX40CONTEXTPROPERTIES& rhs) noexcept -{ - return - lhs.guidPrimaryFXSlotID == rhs.guidPrimaryFXSlotID && - lhs.flDistanceFactor == rhs.flDistanceFactor && - lhs.flAirAbsorptionHF == rhs.flAirAbsorptionHF && - lhs.flHFReference == rhs.flHFReference; -} - -bool operator==( - const EAX50CONTEXTPROPERTIES& lhs, - const EAX50CONTEXTPROPERTIES& rhs) noexcept -{ - return - static_cast(lhs) == static_cast(rhs) && - lhs.flMacroFXFactor == rhs.flMacroFXFactor; -} - - -const GUID EAXCONTEXT_DEFAULTPRIMARYFXSLOTID = EAXPROPERTYID_EAX40_FXSlot0; - -bool operator==( - const EAX40FXSLOTPROPERTIES& lhs, - const EAX40FXSLOTPROPERTIES& rhs) noexcept -{ - return - lhs.guidLoadEffect == rhs.guidLoadEffect && - lhs.lVolume == rhs.lVolume && - lhs.lLock == rhs.lLock && - lhs.ulFlags == rhs.ulFlags; -} - -bool operator==( - const EAX50FXSLOTPROPERTIES& lhs, - const EAX50FXSLOTPROPERTIES& rhs) noexcept -{ - return - static_cast(lhs) == static_cast(rhs) && - lhs.lOcclusion == rhs.lOcclusion && - lhs.flOcclusionLFRatio == rhs.flOcclusionLFRatio; -} - -const EAX50ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID = EAX50ACTIVEFXSLOTS -{{ - EAX_NULL_GUID, - EAXPROPERTYID_EAX40_FXSlot0, -}}; - -bool operator==( - const EAX50ACTIVEFXSLOTS& lhs, - const EAX50ACTIVEFXSLOTS& rhs) noexcept -{ - return std::equal( - std::cbegin(lhs.guidActiveFXSlots), - std::cend(lhs.guidActiveFXSlots), - std::begin(rhs.guidActiveFXSlots)); -} - -bool operator!=( - const EAX50ACTIVEFXSLOTS& lhs, - const EAX50ACTIVEFXSLOTS& rhs) noexcept -{ - return !(lhs == rhs); -} - - -const EAX50ACTIVEFXSLOTS EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID = EAX50ACTIVEFXSLOTS -{{ - EAX_NULL_GUID, - EAX_PrimaryFXSlotID, - EAX_NULL_GUID, - EAX_NULL_GUID, -}}; - - -const EAX50ACTIVEFXSLOTS EAX50SOURCE_2DDEFAULTACTIVEFXSLOTID = EAX50ACTIVEFXSLOTS -{{ - EAX_NULL_GUID, - EAX_NULL_GUID, - EAX_NULL_GUID, - EAX_NULL_GUID, -}}; - -bool operator==( - const EAXREVERBPROPERTIES& lhs, - const EAXREVERBPROPERTIES& rhs) noexcept -{ - return - lhs.ulEnvironment == rhs.ulEnvironment && - lhs.flEnvironmentSize == rhs.flEnvironmentSize && - lhs.flEnvironmentDiffusion == rhs.flEnvironmentDiffusion && - lhs.lRoom == rhs.lRoom && - lhs.lRoomHF == rhs.lRoomHF && - lhs.lRoomLF == rhs.lRoomLF && - lhs.flDecayTime == rhs.flDecayTime && - lhs.flDecayHFRatio == rhs.flDecayHFRatio && - lhs.flDecayLFRatio == rhs.flDecayLFRatio && - lhs.lReflections == rhs.lReflections && - lhs.flReflectionsDelay == rhs.flReflectionsDelay && - lhs.vReflectionsPan == rhs.vReflectionsPan && - lhs.lReverb == rhs.lReverb && - lhs.flReverbDelay == rhs.flReverbDelay && - lhs.vReverbPan == rhs.vReverbPan && - lhs.flEchoTime == rhs.flEchoTime && - lhs.flEchoDepth == rhs.flEchoDepth && - lhs.flModulationTime == rhs.flModulationTime && - lhs.flModulationDepth == rhs.flModulationDepth && - lhs.flAirAbsorptionHF == rhs.flAirAbsorptionHF && - lhs.flHFReference == rhs.flHFReference && - lhs.flLFReference == rhs.flLFReference && - lhs.flRoomRolloffFactor == rhs.flRoomRolloffFactor && - lhs.ulFlags == rhs.ulFlags; -} - -bool operator!=( - const EAXREVERBPROPERTIES& lhs, - const EAXREVERBPROPERTIES& rhs) noexcept -{ - return !(lhs == rhs); -} - - -namespace { - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_GENERIC = -{ - EAXREVERB_DEFAULTENVIRONMENT, - EAXREVERB_DEFAULTENVIRONMENTSIZE, - EAXREVERB_DEFAULTENVIRONMENTDIFFUSION, - EAXREVERB_DEFAULTROOM, - EAXREVERB_DEFAULTROOMHF, - EAXREVERB_DEFAULTROOMLF, - EAXREVERB_DEFAULTDECAYTIME, - EAXREVERB_DEFAULTDECAYHFRATIO, - EAXREVERB_DEFAULTDECAYLFRATIO, - EAXREVERB_DEFAULTREFLECTIONS, - EAXREVERB_DEFAULTREFLECTIONSDELAY, - EAXREVERB_DEFAULTREFLECTIONSPAN, - EAXREVERB_DEFAULTREVERB, - EAXREVERB_DEFAULTREVERBDELAY, - EAXREVERB_DEFAULTREVERBPAN, - EAXREVERB_DEFAULTECHOTIME, - EAXREVERB_DEFAULTECHODEPTH, - EAXREVERB_DEFAULTMODULATIONTIME, - EAXREVERB_DEFAULTMODULATIONDEPTH, - EAXREVERB_DEFAULTAIRABSORPTIONHF, - EAXREVERB_DEFAULTHFREFERENCE, - EAXREVERB_DEFAULTLFREFERENCE, - EAXREVERB_DEFAULTROOMROLLOFFFACTOR, - EAXREVERB_DEFAULTFLAGS, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_PADDEDCELL = -{ - EAX_ENVIRONMENT_PADDEDCELL, - 1.4F, - 1.0F, - -1'000L, - -6'000L, - 0L, - 0.17F, - 0.10F, - 1.0F, - -1'204L, - 0.001F, - EAXVECTOR{}, - 207L, - 0.002F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_ROOM = -{ - EAX_ENVIRONMENT_ROOM, - 1.9F, - 1.0F, - -1'000L, - -454L, - 0L, - 0.40F, - 0.83F, - 1.0F, - -1'646L, - 0.002F, - EAXVECTOR{}, - 53L, - 0.003F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_BATHROOM = -{ - EAX_ENVIRONMENT_BATHROOM, - 1.4F, - 1.0F, - -1'000L, - -1'200L, - 0L, - 1.49F, - 0.54F, - 1.0F, - -370L, - 0.007F, - EAXVECTOR{}, - 1'030L, - 0.011F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_LIVINGROOM = -{ - EAX_ENVIRONMENT_LIVINGROOM, - 2.5F, - 1.0F, - -1'000L, - -6'000L, - 0L, - 0.50F, - 0.10F, - 1.0F, - -1'376, - 0.003F, - EAXVECTOR{}, - -1'104L, - 0.004F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_STONEROOM = -{ - EAX_ENVIRONMENT_STONEROOM, - 11.6F, - 1.0F, - -1'000L, - -300L, - 0L, - 2.31F, - 0.64F, - 1.0F, - -711L, - 0.012F, - EAXVECTOR{}, - 83L, - 0.017F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_AUDITORIUM = -{ - EAX_ENVIRONMENT_AUDITORIUM, - 21.6F, - 1.0F, - -1'000L, - -476L, - 0L, - 4.32F, - 0.59F, - 1.0F, - -789L, - 0.020F, - EAXVECTOR{}, - -289L, - 0.030F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_CONCERTHALL = -{ - EAX_ENVIRONMENT_CONCERTHALL, - 19.6F, - 1.0F, - -1'000L, - -500L, - 0L, - 3.92F, - 0.70F, - 1.0F, - -1'230L, - 0.020F, - EAXVECTOR{}, - -2L, - 0.029F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_CAVE = -{ - EAX_ENVIRONMENT_CAVE, - 14.6F, - 1.0F, - -1'000L, - 0L, - 0L, - 2.91F, - 1.30F, - 1.0F, - -602L, - 0.015F, - EAXVECTOR{}, - -302L, - 0.022F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x1FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_ARENA = -{ - EAX_ENVIRONMENT_ARENA, - 36.2F, - 1.0F, - -1'000L, - -698L, - 0L, - 7.24F, - 0.33F, - 1.0F, - -1'166L, - 0.020F, - EAXVECTOR{}, - 16L, - 0.030F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_HANGAR = -{ - EAX_ENVIRONMENT_HANGAR, - 50.3F, - 1.0F, - -1'000L, - -1'000L, - 0L, - 10.05F, - 0.23F, - 1.0F, - -602L, - 0.020F, - EAXVECTOR{}, - 198L, - 0.030F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_CARPETTEDHALLWAY = -{ - EAX_ENVIRONMENT_CARPETEDHALLWAY, - 1.9F, - 1.0F, - -1'000L, - -4'000L, - 0L, - 0.30F, - 0.10F, - 1.0F, - -1'831L, - 0.002F, - EAXVECTOR{}, - -1'630L, - 0.030F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_HALLWAY = -{ - EAX_ENVIRONMENT_HALLWAY, - 1.8F, - 1.0F, - -1'000L, - -300L, - 0L, - 1.49F, - 0.59F, - 1.0F, - -1'219L, - 0.007F, - EAXVECTOR{}, - 441L, - 0.011F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_STONECORRIDOR = -{ - EAX_ENVIRONMENT_STONECORRIDOR, - 13.5F, - 1.0F, - -1'000L, - -237L, - 0L, - 2.70F, - 0.79F, - 1.0F, - -1'214L, - 0.013F, - EAXVECTOR{}, - 395L, - 0.020F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_ALLEY = -{ - EAX_ENVIRONMENT_ALLEY, - 7.5F, - 0.300F, - -1'000L, - -270L, - 0L, - 1.49F, - 0.86F, - 1.0F, - -1'204L, - 0.007F, - EAXVECTOR{}, - -4L, - 0.011F, - EAXVECTOR{}, - 0.125F, - 0.950F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_FOREST = -{ - EAX_ENVIRONMENT_FOREST, - 38.0F, - 0.300F, - -1'000L, - -3'300L, - 0L, - 1.49F, - 0.54F, - 1.0F, - -2'560L, - 0.162F, - EAXVECTOR{}, - -229L, - 0.088F, - EAXVECTOR{}, - 0.125F, - 1.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_CITY = -{ - EAX_ENVIRONMENT_CITY, - 7.5F, - 0.500F, - -1'000L, - -800L, - 0L, - 1.49F, - 0.67F, - 1.0F, - -2'273L, - 0.007F, - EAXVECTOR{}, - -1'691L, - 0.011F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_MOUNTAINS = -{ - EAX_ENVIRONMENT_MOUNTAINS, - 100.0F, - 0.270F, - -1'000L, - -2'500L, - 0L, - 1.49F, - 0.21F, - 1.0F, - -2'780L, - 0.300F, - EAXVECTOR{}, - -1'434L, - 0.100F, - EAXVECTOR{}, - 0.250F, - 1.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x1FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_QUARRY = -{ - EAX_ENVIRONMENT_QUARRY, - 17.5F, - 1.0F, - -1'000L, - -1'000L, - 0L, - 1.49F, - 0.83F, - 1.0F, - -10'000L, - 0.061F, - EAXVECTOR{}, - 500L, - 0.025F, - EAXVECTOR{}, - 0.125F, - 0.700F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_PLAIN = -{ - EAX_ENVIRONMENT_PLAIN, - 42.5F, - 0.210F, - -1'000L, - -2'000L, - 0L, - 1.49F, - 0.50F, - 1.0F, - -2'466L, - 0.179F, - EAXVECTOR{}, - -1'926L, - 0.100F, - EAXVECTOR{}, - 0.250F, - 1.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_PARKINGLOT = -{ - EAX_ENVIRONMENT_PARKINGLOT, - 8.3F, - 1.0F, - -1'000L, - 0L, - 0L, - 1.65F, - 1.50F, - 1.0F, - -1'363L, - 0.008F, - EAXVECTOR{}, - -1'153L, - 0.012F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x1FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_SEWERPIPE = -{ - EAX_ENVIRONMENT_SEWERPIPE, - 1.7F, - 0.800F, - -1'000L, - -1'000L, - 0L, - 2.81F, - 0.14F, - 1.0F, - 429L, - 0.014F, - EAXVECTOR{}, - 1'023L, - 0.021F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 0.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_UNDERWATER = -{ - EAX_ENVIRONMENT_UNDERWATER, - 1.8F, - 1.0F, - -1'000L, - -4'000L, - 0L, - 1.49F, - 0.10F, - 1.0F, - -449L, - 0.007F, - EAXVECTOR{}, - 1'700L, - 0.011F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 1.180F, - 0.348F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x3FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_DRUGGED = -{ - EAX_ENVIRONMENT_DRUGGED, - 1.9F, - 0.500F, - -1'000L, - 0L, - 0L, - 8.39F, - 1.39F, - 1.0F, - -115L, - 0.002F, - EAXVECTOR{}, - 985L, - 0.030F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 0.250F, - 1.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x1FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_DIZZY = -{ - EAX_ENVIRONMENT_DIZZY, - 1.8F, - 0.600F, - -1'000L, - -400L, - 0L, - 17.23F, - 0.56F, - 1.0F, - -1'713L, - 0.020F, - EAXVECTOR{}, - -613L, - 0.030F, - EAXVECTOR{}, - 0.250F, - 1.0F, - 0.810F, - 0.310F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x1FUL, -}; - -constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_PSYCHOTIC = -{ - EAX_ENVIRONMENT_PSYCHOTIC, - 1.0F, - 0.500F, - -1'000L, - -151L, - 0L, - 7.56F, - 0.91F, - 1.0F, - -626L, - 0.020F, - EAXVECTOR{}, - 774L, - 0.030F, - EAXVECTOR{}, - 0.250F, - 0.0F, - 4.0F, - 1.0F, - -5.0F, - 5'000.0F, - 250.0F, - 0.0F, - 0x1FUL, -}; - -} // namespace - -const EaxReverbPresets EAXREVERB_PRESETS{{ - EAXREVERB_PRESET_GENERIC, - EAXREVERB_PRESET_PADDEDCELL, - EAXREVERB_PRESET_ROOM, - EAXREVERB_PRESET_BATHROOM, - EAXREVERB_PRESET_LIVINGROOM, - EAXREVERB_PRESET_STONEROOM, - EAXREVERB_PRESET_AUDITORIUM, - EAXREVERB_PRESET_CONCERTHALL, - EAXREVERB_PRESET_CAVE, - EAXREVERB_PRESET_ARENA, - EAXREVERB_PRESET_HANGAR, - EAXREVERB_PRESET_CARPETTEDHALLWAY, - EAXREVERB_PRESET_HALLWAY, - EAXREVERB_PRESET_STONECORRIDOR, - EAXREVERB_PRESET_ALLEY, - EAXREVERB_PRESET_FOREST, - EAXREVERB_PRESET_CITY, - EAXREVERB_PRESET_MOUNTAINS, - EAXREVERB_PRESET_QUARRY, - EAXREVERB_PRESET_PLAIN, - EAXREVERB_PRESET_PARKINGLOT, - EAXREVERB_PRESET_SEWERPIPE, - EAXREVERB_PRESET_UNDERWATER, - EAXREVERB_PRESET_DRUGGED, - EAXREVERB_PRESET_DIZZY, - EAXREVERB_PRESET_PSYCHOTIC, -}}; - -namespace { -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_GENERIC = {EAX_ENVIRONMENT_GENERIC, 0.5F, 1.493F, 0.5F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PADDEDCELL = {EAX_ENVIRONMENT_PADDEDCELL, 0.25F, 0.1F, 0.0F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ROOM = {EAX_ENVIRONMENT_ROOM, 0.417F, 0.4F, 0.666F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_BATHROOM = {EAX_ENVIRONMENT_BATHROOM, 0.653F, 1.499F, 0.166F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_LIVINGROOM = {EAX_ENVIRONMENT_LIVINGROOM, 0.208F, 0.478F, 0.0F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_STONEROOM = {EAX_ENVIRONMENT_STONEROOM, 0.5F, 2.309F, 0.888F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_AUDITORIUM = {EAX_ENVIRONMENT_AUDITORIUM, 0.403F, 4.279F, 0.5F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CONCERTHALL = {EAX_ENVIRONMENT_CONCERTHALL, 0.5F, 3.961F, 0.5F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CAVE = {EAX_ENVIRONMENT_CAVE, 0.5F, 2.886F, 1.304F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ARENA = {EAX_ENVIRONMENT_ARENA, 0.361F, 7.284F, 0.332F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_HANGAR = {EAX_ENVIRONMENT_HANGAR, 0.5F, 10.0F, 0.3F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CARPETTEDHALLWAY = {EAX_ENVIRONMENT_CARPETEDHALLWAY, 0.153F, 0.259F, 2.0F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_HALLWAY = {EAX_ENVIRONMENT_HALLWAY, 0.361F, 1.493F, 0.0F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_STONECORRIDOR = {EAX_ENVIRONMENT_STONECORRIDOR, 0.444F, 2.697F, 0.638F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ALLEY = {EAX_ENVIRONMENT_ALLEY, 0.25F, 1.752F, 0.776F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_FOREST = {EAX_ENVIRONMENT_FOREST, 0.111F, 3.145F, 0.472F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CITY = {EAX_ENVIRONMENT_CITY, 0.111F, 2.767F, 0.224F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_MOUNTAINS = {EAX_ENVIRONMENT_MOUNTAINS, 0.194F, 7.841F, 0.472F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_QUARRY = {EAX_ENVIRONMENT_QUARRY, 1.0F, 1.499F, 0.5F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PLAIN = {EAX_ENVIRONMENT_PLAIN, 0.097F, 2.767F, 0.224F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PARKINGLOT = {EAX_ENVIRONMENT_PARKINGLOT, 0.208F, 1.652F, 1.5F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_SEWERPIPE = {EAX_ENVIRONMENT_SEWERPIPE, 0.652F, 2.886F, 0.25F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_UNDERWATER = {EAX_ENVIRONMENT_UNDERWATER, 1.0F, 1.499F, 0.0F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_DRUGGED = {EAX_ENVIRONMENT_DRUGGED, 0.875F, 8.392F, 1.388F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_DIZZY = {EAX_ENVIRONMENT_DIZZY, 0.139F, 17.234F, 0.666F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PSYCHOTIC = {EAX_ENVIRONMENT_PSYCHOTIC, 0.486F, 7.563F, 0.806F}; -} // namespace - -const Eax1ReverbPresets EAX1REVERB_PRESETS{{ - EAX1REVERB_PRESET_GENERIC, - EAX1REVERB_PRESET_PADDEDCELL, - EAX1REVERB_PRESET_ROOM, - EAX1REVERB_PRESET_BATHROOM, - EAX1REVERB_PRESET_LIVINGROOM, - EAX1REVERB_PRESET_STONEROOM, - EAX1REVERB_PRESET_AUDITORIUM, - EAX1REVERB_PRESET_CONCERTHALL, - EAX1REVERB_PRESET_CAVE, - EAX1REVERB_PRESET_ARENA, - EAX1REVERB_PRESET_HANGAR, - EAX1REVERB_PRESET_CARPETTEDHALLWAY, - EAX1REVERB_PRESET_HALLWAY, - EAX1REVERB_PRESET_STONECORRIDOR, - EAX1REVERB_PRESET_ALLEY, - EAX1REVERB_PRESET_FOREST, - EAX1REVERB_PRESET_CITY, - EAX1REVERB_PRESET_MOUNTAINS, - EAX1REVERB_PRESET_QUARRY, - EAX1REVERB_PRESET_PLAIN, - EAX1REVERB_PRESET_PARKINGLOT, - EAX1REVERB_PRESET_SEWERPIPE, - EAX1REVERB_PRESET_UNDERWATER, - EAX1REVERB_PRESET_DRUGGED, - EAX1REVERB_PRESET_DIZZY, - EAX1REVERB_PRESET_PSYCHOTIC, -}}; diff --git a/al/eax_api.h b/al/eax_api.h deleted file mode 100644 index d0737d1d..00000000 --- a/al/eax_api.h +++ /dev/null @@ -1,1557 +0,0 @@ -#ifndef EAX_API_INCLUDED -#define EAX_API_INCLUDED - - -// -// EAX API. -// -// Based on headers `eax[2-5].h` included in Doom 3 source code: -// https://github.com/id-Software/DOOM-3/tree/master/neo/openal/include -// - - -#include -#include -#include - -#include - -#include "AL/al.h" - - -#ifndef GUID_DEFINED -#define GUID_DEFINED -typedef struct _GUID -{ - std::uint32_t Data1; - std::uint16_t Data2; - std::uint16_t Data3; - std::uint8_t Data4[8]; -} GUID; - -inline bool operator==(const GUID& lhs, const GUID& rhs) noexcept -{ - return std::memcmp(&lhs, &rhs, sizeof(GUID)) == 0; -} - -inline bool operator!=(const GUID& lhs, const GUID& rhs) noexcept -{ - return !(lhs == rhs); -} -#endif // GUID_DEFINED - - -extern const GUID DSPROPSETID_EAX_ReverbProperties; - -enum DSPROPERTY_EAX_REVERBPROPERTY : unsigned int -{ - DSPROPERTY_EAX_ALL, - DSPROPERTY_EAX_ENVIRONMENT, - DSPROPERTY_EAX_VOLUME, - DSPROPERTY_EAX_DECAYTIME, - DSPROPERTY_EAX_DAMPING, -}; // DSPROPERTY_EAX_REVERBPROPERTY - -struct EAX_REVERBPROPERTIES -{ - unsigned long environment; - float fVolume; - float fDecayTime_sec; - float fDamping; -}; // EAX_REVERBPROPERTIES - -inline bool operator==(const EAX_REVERBPROPERTIES& lhs, const EAX_REVERBPROPERTIES& rhs) noexcept -{ - return std::memcmp(&lhs, &rhs, sizeof(EAX_REVERBPROPERTIES)) == 0; -} - -extern const GUID DSPROPSETID_EAXBUFFER_ReverbProperties; - -enum DSPROPERTY_EAXBUFFER_REVERBPROPERTY : unsigned int -{ - DSPROPERTY_EAXBUFFER_ALL, - DSPROPERTY_EAXBUFFER_REVERBMIX, -}; // DSPROPERTY_EAXBUFFER_REVERBPROPERTY - -struct EAXBUFFER_REVERBPROPERTIES -{ - float fMix; -}; - -inline bool operator==(const EAXBUFFER_REVERBPROPERTIES& lhs, const EAXBUFFER_REVERBPROPERTIES& rhs) noexcept -{ - return lhs.fMix == rhs.fMix; -} - -constexpr auto EAX_BUFFER_MINREVERBMIX = 0.0F; -constexpr auto EAX_BUFFER_MAXREVERBMIX = 1.0F; -constexpr auto EAX_REVERBMIX_USEDISTANCE = -1.0F; - - -extern const GUID DSPROPSETID_EAX20_ListenerProperties; - -enum DSPROPERTY_EAX20_LISTENERPROPERTY : - unsigned int -{ - DSPROPERTY_EAX20LISTENER_NONE, - DSPROPERTY_EAX20LISTENER_ALLPARAMETERS, - DSPROPERTY_EAX20LISTENER_ROOM, - DSPROPERTY_EAX20LISTENER_ROOMHF, - DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR, - DSPROPERTY_EAX20LISTENER_DECAYTIME, - DSPROPERTY_EAX20LISTENER_DECAYHFRATIO, - DSPROPERTY_EAX20LISTENER_REFLECTIONS, - DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY, - DSPROPERTY_EAX20LISTENER_REVERB, - DSPROPERTY_EAX20LISTENER_REVERBDELAY, - DSPROPERTY_EAX20LISTENER_ENVIRONMENT, - DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE, - DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION, - DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF, - DSPROPERTY_EAX20LISTENER_FLAGS -}; // DSPROPERTY_EAX20_LISTENERPROPERTY - -struct EAX20LISTENERPROPERTIES -{ - long lRoom; // room effect level at low frequencies - long lRoomHF; // room effect high-frequency level re. low frequency level - float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect - float flDecayTime; // reverberation decay time at low frequencies - float flDecayHFRatio; // high-frequency to low-frequency decay time ratio - long lReflections; // early reflections level relative to room effect - float flReflectionsDelay; // initial reflection delay time - long lReverb; // late reverberation level relative to room effect - float flReverbDelay; // late reverberation delay time relative to initial reflection - unsigned long dwEnvironment; // sets all listener properties - float flEnvironmentSize; // environment size in meters - float flEnvironmentDiffusion; // environment diffusion - float flAirAbsorptionHF; // change in level per meter at 5 kHz - unsigned long dwFlags; // modifies the behavior of properties -}; // EAX20LISTENERPROPERTIES - - -extern const GUID DSPROPSETID_EAX20_BufferProperties; - - -enum DSPROPERTY_EAX20_BUFFERPROPERTY : - unsigned int -{ - DSPROPERTY_EAX20BUFFER_NONE, - DSPROPERTY_EAX20BUFFER_ALLPARAMETERS, - DSPROPERTY_EAX20BUFFER_DIRECT, - DSPROPERTY_EAX20BUFFER_DIRECTHF, - DSPROPERTY_EAX20BUFFER_ROOM, - DSPROPERTY_EAX20BUFFER_ROOMHF, - DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR, - DSPROPERTY_EAX20BUFFER_OBSTRUCTION, - DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO, - DSPROPERTY_EAX20BUFFER_OCCLUSION, - DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO, - DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO, - DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF, - DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR, - DSPROPERTY_EAX20BUFFER_FLAGS -}; // DSPROPERTY_EAX20_BUFFERPROPERTY - - -struct EAX20BUFFERPROPERTIES -{ - long lDirect; // direct path level - long lDirectHF; // direct path level at high frequencies - long lRoom; // room effect level - long lRoomHF; // room effect level at high frequencies - float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect - long lObstruction; // main obstruction control (attenuation at high frequencies) - float flObstructionLFRatio; // obstruction low-frequency level re. main control - long lOcclusion; // main occlusion control (attenuation at high frequencies) - float flOcclusionLFRatio; // occlusion low-frequency level re. main control - float flOcclusionRoomRatio; // occlusion room effect level re. main control - long lOutsideVolumeHF; // outside sound cone level at high frequencies - float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF - unsigned long dwFlags; // modifies the behavior of properties -}; // EAX20BUFFERPROPERTIES - - -extern const GUID DSPROPSETID_EAX30_ListenerProperties; - -extern const GUID DSPROPSETID_EAX30_BufferProperties; - - -constexpr auto EAX_MAX_FXSLOTS = 4; - -constexpr auto EAX40_MAX_ACTIVE_FXSLOTS = 2; -constexpr auto EAX50_MAX_ACTIVE_FXSLOTS = 4; - - -constexpr auto EAX_OK = 0L; -constexpr auto EAXERR_INVALID_OPERATION = -1L; -constexpr auto EAXERR_INVALID_VALUE = -2L; -constexpr auto EAXERR_NO_EFFECT_LOADED = -3L; -constexpr auto EAXERR_UNKNOWN_EFFECT = -4L; -constexpr auto EAXERR_INCOMPATIBLE_SOURCE_TYPE = -5L; -constexpr auto EAXERR_INCOMPATIBLE_EAX_VERSION = -6L; - - -extern const GUID EAX_NULL_GUID; - -extern const GUID EAX_PrimaryFXSlotID; - - -struct EAXVECTOR -{ - float x; - float y; - float z; -}; // EAXVECTOR - -inline bool operator==(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept -{ return std::memcmp(&lhs, &rhs, sizeof(EAXVECTOR)) == 0; } - -inline bool operator!=(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept -{ return !(lhs == rhs); } - - -extern const GUID EAXPROPERTYID_EAX40_Context; - -extern const GUID EAXPROPERTYID_EAX50_Context; - -// EAX50 -enum : - unsigned long -{ - HEADPHONES = 0, - SPEAKERS_2, - SPEAKERS_4, - SPEAKERS_5, // 5.1 speakers - SPEAKERS_6, // 6.1 speakers - SPEAKERS_7, // 7.1 speakers -}; - -// EAX50 -enum : - unsigned long -{ - EAX_40 = 5, // EAX 4.0 - EAX_50 = 6, // EAX 5.0 -}; - -constexpr auto EAXCONTEXT_MINEAXSESSION = EAX_40; -constexpr auto EAXCONTEXT_MAXEAXSESSION = EAX_50; -constexpr auto EAXCONTEXT_DEFAULTEAXSESSION = EAX_40; - -constexpr auto EAXCONTEXT_MINMAXACTIVESENDS = 2UL; -constexpr auto EAXCONTEXT_MAXMAXACTIVESENDS = 4UL; -constexpr auto EAXCONTEXT_DEFAULTMAXACTIVESENDS = 2UL; - -// EAX50 -struct EAXSESSIONPROPERTIES -{ - unsigned long ulEAXVersion; - unsigned long ulMaxActiveSends; -}; // EAXSESSIONPROPERTIES - -enum EAXCONTEXT_PROPERTY : - unsigned int -{ - EAXCONTEXT_NONE = 0, - EAXCONTEXT_ALLPARAMETERS, - EAXCONTEXT_PRIMARYFXSLOTID, - EAXCONTEXT_DISTANCEFACTOR, - EAXCONTEXT_AIRABSORPTIONHF, - EAXCONTEXT_HFREFERENCE, - EAXCONTEXT_LASTERROR, - - // EAX50 - EAXCONTEXT_SPEAKERCONFIG, - EAXCONTEXT_EAXSESSION, - EAXCONTEXT_MACROFXFACTOR, -}; // EAXCONTEXT_PROPERTY - -struct EAX40CONTEXTPROPERTIES -{ - GUID guidPrimaryFXSlotID; - float flDistanceFactor; - float flAirAbsorptionHF; - float flHFReference; -}; // EAX40CONTEXTPROPERTIES - -struct EAX50CONTEXTPROPERTIES : - public EAX40CONTEXTPROPERTIES -{ - float flMacroFXFactor; -}; // EAX40CONTEXTPROPERTIES - - -bool operator==( - const EAX40CONTEXTPROPERTIES& lhs, - const EAX40CONTEXTPROPERTIES& rhs) noexcept; - -bool operator==( - const EAX50CONTEXTPROPERTIES& lhs, - const EAX50CONTEXTPROPERTIES& rhs) noexcept; - - -constexpr auto EAXCONTEXT_MINDISTANCEFACTOR = FLT_MIN; -constexpr auto EAXCONTEXT_MAXDISTANCEFACTOR = FLT_MAX; -constexpr auto EAXCONTEXT_DEFAULTDISTANCEFACTOR = 1.0F; - -constexpr auto EAXCONTEXT_MINAIRABSORPTIONHF = -100.0F; -constexpr auto EAXCONTEXT_MAXAIRABSORPTIONHF = 0.0F; -constexpr auto EAXCONTEXT_DEFAULTAIRABSORPTIONHF = -5.0F; - -constexpr auto EAXCONTEXT_MINHFREFERENCE = 1000.0F; -constexpr auto EAXCONTEXT_MAXHFREFERENCE = 20000.0F; -constexpr auto EAXCONTEXT_DEFAULTHFREFERENCE = 5000.0F; - -constexpr auto EAXCONTEXT_MINMACROFXFACTOR = 0.0F; -constexpr auto EAXCONTEXT_MAXMACROFXFACTOR = 1.0F; -constexpr auto EAXCONTEXT_DEFAULTMACROFXFACTOR = 0.0F; - - -extern const GUID EAXPROPERTYID_EAX40_FXSlot0; - -extern const GUID EAXPROPERTYID_EAX50_FXSlot0; - -extern const GUID EAXPROPERTYID_EAX40_FXSlot1; - -extern const GUID EAXPROPERTYID_EAX50_FXSlot1; - -extern const GUID EAXPROPERTYID_EAX40_FXSlot2; - -extern const GUID EAXPROPERTYID_EAX50_FXSlot2; - -extern const GUID EAXPROPERTYID_EAX40_FXSlot3; - -extern const GUID EAXPROPERTYID_EAX50_FXSlot3; - -extern const GUID EAXCONTEXT_DEFAULTPRIMARYFXSLOTID; - -enum EAXFXSLOT_PROPERTY : - unsigned int -{ - EAXFXSLOT_PARAMETER = 0, - - EAXFXSLOT_NONE = 0x10000, - EAXFXSLOT_ALLPARAMETERS, - EAXFXSLOT_LOADEFFECT, - EAXFXSLOT_VOLUME, - EAXFXSLOT_LOCK, - EAXFXSLOT_FLAGS, - - // EAX50 - EAXFXSLOT_OCCLUSION, - EAXFXSLOT_OCCLUSIONLFRATIO, -}; // EAXFXSLOT_PROPERTY - -constexpr auto EAXFXSLOTFLAGS_ENVIRONMENT = 0x00000001UL; -// EAX50 -constexpr auto EAXFXSLOTFLAGS_UPMIX = 0x00000002UL; - -constexpr auto EAX40FXSLOTFLAGS_RESERVED = 0xFFFFFFFEUL; // reserved future use -constexpr auto EAX50FXSLOTFLAGS_RESERVED = 0xFFFFFFFCUL; // reserved future use - - -constexpr auto EAXFXSLOT_MINVOLUME = -10'000L; -constexpr auto EAXFXSLOT_MAXVOLUME = 0L; -constexpr auto EAXFXSLOT_DEFAULTVOLUME = 0L; - -constexpr auto EAXFXSLOT_MINLOCK = 0L; -constexpr auto EAXFXSLOT_MAXLOCK = 1L; - -enum : - long -{ - EAXFXSLOT_UNLOCKED = 0, - EAXFXSLOT_LOCKED = 1 -}; - -constexpr auto EAXFXSLOT_MINOCCLUSION = -10'000L; -constexpr auto EAXFXSLOT_MAXOCCLUSION = 0L; -constexpr auto EAXFXSLOT_DEFAULTOCCLUSION = 0L; - -constexpr auto EAXFXSLOT_MINOCCLUSIONLFRATIO = 0.0F; -constexpr auto EAXFXSLOT_MAXOCCLUSIONLFRATIO = 1.0F; -constexpr auto EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO = 0.25F; - -constexpr auto EAX40FXSLOT_DEFAULTFLAGS = EAXFXSLOTFLAGS_ENVIRONMENT; - -constexpr auto EAX50FXSLOT_DEFAULTFLAGS = - EAXFXSLOTFLAGS_ENVIRONMENT | - EAXFXSLOTFLAGS_UPMIX; // ignored for reverb; - -struct EAX40FXSLOTPROPERTIES -{ - GUID guidLoadEffect; - long lVolume; - long lLock; - unsigned long ulFlags; -}; // EAX40FXSLOTPROPERTIES - -struct EAX50FXSLOTPROPERTIES : - public EAX40FXSLOTPROPERTIES -{ - long lOcclusion; - float flOcclusionLFRatio; -}; // EAX50FXSLOTPROPERTIES - -bool operator==( - const EAX40FXSLOTPROPERTIES& lhs, - const EAX40FXSLOTPROPERTIES& rhs) noexcept; - -bool operator==( - const EAX50FXSLOTPROPERTIES& lhs, - const EAX50FXSLOTPROPERTIES& rhs) noexcept; - -extern const GUID EAXPROPERTYID_EAX40_Source; - -extern const GUID EAXPROPERTYID_EAX50_Source; - -// Source object properties -enum EAXSOURCE_PROPERTY : - unsigned int -{ - // EAX30 - - EAXSOURCE_NONE, - EAXSOURCE_ALLPARAMETERS, - EAXSOURCE_OBSTRUCTIONPARAMETERS, - EAXSOURCE_OCCLUSIONPARAMETERS, - EAXSOURCE_EXCLUSIONPARAMETERS, - EAXSOURCE_DIRECT, - EAXSOURCE_DIRECTHF, - EAXSOURCE_ROOM, - EAXSOURCE_ROOMHF, - EAXSOURCE_OBSTRUCTION, - EAXSOURCE_OBSTRUCTIONLFRATIO, - EAXSOURCE_OCCLUSION, - EAXSOURCE_OCCLUSIONLFRATIO, - EAXSOURCE_OCCLUSIONROOMRATIO, - EAXSOURCE_OCCLUSIONDIRECTRATIO, - EAXSOURCE_EXCLUSION, - EAXSOURCE_EXCLUSIONLFRATIO, - EAXSOURCE_OUTSIDEVOLUMEHF, - EAXSOURCE_DOPPLERFACTOR, - EAXSOURCE_ROLLOFFFACTOR, - EAXSOURCE_ROOMROLLOFFFACTOR, - EAXSOURCE_AIRABSORPTIONFACTOR, - EAXSOURCE_FLAGS, - - // EAX40 - - EAXSOURCE_SENDPARAMETERS, - EAXSOURCE_ALLSENDPARAMETERS, - EAXSOURCE_OCCLUSIONSENDPARAMETERS, - EAXSOURCE_EXCLUSIONSENDPARAMETERS, - EAXSOURCE_ACTIVEFXSLOTID, - - // EAX50 - - EAXSOURCE_MACROFXFACTOR, - EAXSOURCE_SPEAKERLEVELS, - EAXSOURCE_ALL2DPARAMETERS, -}; // EAXSOURCE_PROPERTY - - -constexpr auto EAXSOURCEFLAGS_DIRECTHFAUTO = 0x00000001UL; // relates to EAXSOURCE_DIRECTHF -constexpr auto EAXSOURCEFLAGS_ROOMAUTO = 0x00000002UL; // relates to EAXSOURCE_ROOM -constexpr auto EAXSOURCEFLAGS_ROOMHFAUTO = 0x00000004UL; // relates to EAXSOURCE_ROOMHF -// EAX50 -constexpr auto EAXSOURCEFLAGS_3DELEVATIONFILTER = 0x00000008UL; -// EAX50 -constexpr auto EAXSOURCEFLAGS_UPMIX = 0x00000010UL; -// EAX50 -constexpr auto EAXSOURCEFLAGS_APPLYSPEAKERLEVELS = 0x00000020UL; - -constexpr auto EAX20SOURCEFLAGS_RESERVED = 0xFFFFFFF8UL; // reserved future use -constexpr auto EAX50SOURCEFLAGS_RESERVED = 0xFFFFFFC0UL; // reserved future use - - -constexpr auto EAXSOURCE_MINSEND = -10'000L; -constexpr auto EAXSOURCE_MAXSEND = 0L; -constexpr auto EAXSOURCE_DEFAULTSEND = 0L; - -constexpr auto EAXSOURCE_MINSENDHF = -10'000L; -constexpr auto EAXSOURCE_MAXSENDHF = 0L; -constexpr auto EAXSOURCE_DEFAULTSENDHF = 0L; - -constexpr auto EAXSOURCE_MINDIRECT = -10'000L; -constexpr auto EAXSOURCE_MAXDIRECT = 1'000L; -constexpr auto EAXSOURCE_DEFAULTDIRECT = 0L; - -constexpr auto EAXSOURCE_MINDIRECTHF = -10'000L; -constexpr auto EAXSOURCE_MAXDIRECTHF = 0L; -constexpr auto EAXSOURCE_DEFAULTDIRECTHF = 0L; - -constexpr auto EAXSOURCE_MINROOM = -10'000L; -constexpr auto EAXSOURCE_MAXROOM = 1'000L; -constexpr auto EAXSOURCE_DEFAULTROOM = 0L; - -constexpr auto EAXSOURCE_MINROOMHF = -10'000L; -constexpr auto EAXSOURCE_MAXROOMHF = 0L; -constexpr auto EAXSOURCE_DEFAULTROOMHF = 0L; - -constexpr auto EAXSOURCE_MINOBSTRUCTION = -10'000L; -constexpr auto EAXSOURCE_MAXOBSTRUCTION = 0L; -constexpr auto EAXSOURCE_DEFAULTOBSTRUCTION = 0L; - -constexpr auto EAXSOURCE_MINOBSTRUCTIONLFRATIO = 0.0F; -constexpr auto EAXSOURCE_MAXOBSTRUCTIONLFRATIO = 1.0F; -constexpr auto EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO = 0.0F; - -constexpr auto EAXSOURCE_MINOCCLUSION = -10'000L; -constexpr auto EAXSOURCE_MAXOCCLUSION = 0L; -constexpr auto EAXSOURCE_DEFAULTOCCLUSION = 0L; - -constexpr auto EAXSOURCE_MINOCCLUSIONLFRATIO = 0.0F; -constexpr auto EAXSOURCE_MAXOCCLUSIONLFRATIO = 1.0F; -constexpr auto EAXSOURCE_DEFAULTOCCLUSIONLFRATIO = 0.25F; - -constexpr auto EAXSOURCE_MINOCCLUSIONROOMRATIO = 0.0F; -constexpr auto EAXSOURCE_MAXOCCLUSIONROOMRATIO = 10.0F; -constexpr auto EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO = 1.5F; - -constexpr auto EAXSOURCE_MINOCCLUSIONDIRECTRATIO = 0.0F; -constexpr auto EAXSOURCE_MAXOCCLUSIONDIRECTRATIO = 10.0F; -constexpr auto EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO = 1.0F; - -constexpr auto EAXSOURCE_MINEXCLUSION = -10'000L; -constexpr auto EAXSOURCE_MAXEXCLUSION = 0L; -constexpr auto EAXSOURCE_DEFAULTEXCLUSION = 0L; - -constexpr auto EAXSOURCE_MINEXCLUSIONLFRATIO = 0.0F; -constexpr auto EAXSOURCE_MAXEXCLUSIONLFRATIO = 1.0F; -constexpr auto EAXSOURCE_DEFAULTEXCLUSIONLFRATIO = 1.0F; - -constexpr auto EAXSOURCE_MINOUTSIDEVOLUMEHF = -10'000L; -constexpr auto EAXSOURCE_MAXOUTSIDEVOLUMEHF = 0L; -constexpr auto EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF = 0L; - -constexpr auto EAXSOURCE_MINDOPPLERFACTOR = 0.0F; -constexpr auto EAXSOURCE_MAXDOPPLERFACTOR = 10.0F; -constexpr auto EAXSOURCE_DEFAULTDOPPLERFACTOR = 1.0F; - -constexpr auto EAXSOURCE_MINROLLOFFFACTOR = 0.0F; -constexpr auto EAXSOURCE_MAXROLLOFFFACTOR = 10.0F; -constexpr auto EAXSOURCE_DEFAULTROLLOFFFACTOR = 0.0F; - -constexpr auto EAXSOURCE_MINROOMROLLOFFFACTOR = 0.0F; -constexpr auto EAXSOURCE_MAXROOMROLLOFFFACTOR = 10.0F; -constexpr auto EAXSOURCE_DEFAULTROOMROLLOFFFACTOR = 0.0F; - -constexpr auto EAXSOURCE_MINAIRABSORPTIONFACTOR = 0.0F; -constexpr auto EAXSOURCE_MAXAIRABSORPTIONFACTOR = 10.0F; -constexpr auto EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR = 0.0F; - -// EAX50 - -constexpr auto EAXSOURCE_MINMACROFXFACTOR = 0.0F; -constexpr auto EAXSOURCE_MAXMACROFXFACTOR = 1.0F; -constexpr auto EAXSOURCE_DEFAULTMACROFXFACTOR = 1.0F; - -// EAX50 - -constexpr auto EAXSOURCE_MINSPEAKERLEVEL = -10'000L; -constexpr auto EAXSOURCE_MAXSPEAKERLEVEL = 0L; -constexpr auto EAXSOURCE_DEFAULTSPEAKERLEVEL = -10'000L; - -constexpr auto EAXSOURCE_DEFAULTFLAGS = - EAXSOURCEFLAGS_DIRECTHFAUTO | - EAXSOURCEFLAGS_ROOMAUTO | - EAXSOURCEFLAGS_ROOMHFAUTO; - -enum : - long -{ - EAXSPEAKER_FRONT_LEFT = 1, - EAXSPEAKER_FRONT_CENTER = 2, - EAXSPEAKER_FRONT_RIGHT = 3, - EAXSPEAKER_SIDE_RIGHT = 4, - EAXSPEAKER_REAR_RIGHT = 5, - EAXSPEAKER_REAR_CENTER = 6, - EAXSPEAKER_REAR_LEFT = 7, - EAXSPEAKER_SIDE_LEFT = 8, - EAXSPEAKER_LOW_FREQUENCY = 9 -}; - -// EAXSOURCEFLAGS_DIRECTHFAUTO, EAXSOURCEFLAGS_ROOMAUTO and EAXSOURCEFLAGS_ROOMHFAUTO are ignored for 2D sources -// EAXSOURCEFLAGS_UPMIX is ignored for 3D sources -constexpr auto EAX50SOURCE_DEFAULTFLAGS = - EAXSOURCEFLAGS_DIRECTHFAUTO | - EAXSOURCEFLAGS_ROOMAUTO | - EAXSOURCEFLAGS_ROOMHFAUTO | - EAXSOURCEFLAGS_UPMIX; - -struct EAX30SOURCEPROPERTIES -{ - long lDirect; // direct path level (at low and mid frequencies) - long lDirectHF; // relative direct path level at high frequencies - long lRoom; // room effect level (at low and mid frequencies) - long lRoomHF; // relative room effect level at high frequencies - long lObstruction; // main obstruction control (attenuation at high frequencies) - float flObstructionLFRatio; // obstruction low-frequency level re. main control - long lOcclusion; // main occlusion control (attenuation at high frequencies) - float flOcclusionLFRatio; // occlusion low-frequency level re. main control - float flOcclusionRoomRatio; // relative occlusion control for room effect - float flOcclusionDirectRatio; // relative occlusion control for direct path - long lExclusion; // main exlusion control (attenuation at high frequencies) - float flExclusionLFRatio; // exclusion low-frequency level re. main control - long lOutsideVolumeHF; // outside sound cone level at high frequencies - float flDopplerFactor; // like DS3D flDopplerFactor but per source - float flRolloffFactor; // like DS3D flRolloffFactor but per source - float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect - float flAirAbsorptionFactor; // multiplies EAXREVERB_AIRABSORPTIONHF - unsigned long ulFlags; // modifies the behavior of properties -}; // EAX30SOURCEPROPERTIES - -struct EAX50SOURCEPROPERTIES : - public EAX30SOURCEPROPERTIES -{ - float flMacroFXFactor; -}; // EAX50SOURCEPROPERTIES - -struct EAXSOURCEALLSENDPROPERTIES -{ - GUID guidReceivingFXSlotID; - long lSend; // send level (at low and mid frequencies) - long lSendHF; // relative send level at high frequencies - long lOcclusion; - float flOcclusionLFRatio; - float flOcclusionRoomRatio; - float flOcclusionDirectRatio; - long lExclusion; - float flExclusionLFRatio; -}; // EAXSOURCEALLSENDPROPERTIES - -struct EAXSOURCE2DPROPERTIES -{ - long lDirect; // direct path level (at low and mid frequencies) - long lDirectHF; // relative direct path level at high frequencies - long lRoom; // room effect level (at low and mid frequencies) - long lRoomHF; // relative room effect level at high frequencies - unsigned long ulFlags; // modifies the behavior of properties -}; // EAXSOURCE2DPROPERTIES - -struct EAXSPEAKERLEVELPROPERTIES -{ - long lSpeakerID; - long lLevel; -}; // EAXSPEAKERLEVELPROPERTIES - -struct EAX40ACTIVEFXSLOTS -{ - GUID guidActiveFXSlots[EAX40_MAX_ACTIVE_FXSLOTS]; -}; // EAX40ACTIVEFXSLOTS - -struct EAX50ACTIVEFXSLOTS -{ - GUID guidActiveFXSlots[EAX50_MAX_ACTIVE_FXSLOTS]; -}; // EAX50ACTIVEFXSLOTS - -bool operator==( - const EAX50ACTIVEFXSLOTS& lhs, - const EAX50ACTIVEFXSLOTS& rhs) noexcept; - -bool operator!=( - const EAX50ACTIVEFXSLOTS& lhs, - const EAX50ACTIVEFXSLOTS& rhs) noexcept; - -// Use this structure for EAXSOURCE_OBSTRUCTIONPARAMETERS property. -struct EAXOBSTRUCTIONPROPERTIES -{ - long lObstruction; - float flObstructionLFRatio; -}; // EAXOBSTRUCTIONPROPERTIES - -// Use this structure for EAXSOURCE_OCCLUSIONPARAMETERS property. -struct EAXOCCLUSIONPROPERTIES -{ - long lOcclusion; - float flOcclusionLFRatio; - float flOcclusionRoomRatio; - float flOcclusionDirectRatio; -}; // EAXOCCLUSIONPROPERTIES - -// Use this structure for EAXSOURCE_EXCLUSIONPARAMETERS property. -struct EAXEXCLUSIONPROPERTIES -{ - long lExclusion; - float flExclusionLFRatio; -}; // EAXEXCLUSIONPROPERTIES - -// Use this structure for EAXSOURCE_SENDPARAMETERS properties. -struct EAXSOURCESENDPROPERTIES -{ - GUID guidReceivingFXSlotID; - long lSend; - long lSendHF; -}; // EAXSOURCESENDPROPERTIES - -// Use this structure for EAXSOURCE_OCCLUSIONSENDPARAMETERS -struct EAXSOURCEOCCLUSIONSENDPROPERTIES -{ - GUID guidReceivingFXSlotID; - long lOcclusion; - float flOcclusionLFRatio; - float flOcclusionRoomRatio; - float flOcclusionDirectRatio; -}; // EAXSOURCEOCCLUSIONSENDPROPERTIES - -// Use this structure for EAXSOURCE_EXCLUSIONSENDPARAMETERS -struct EAXSOURCEEXCLUSIONSENDPROPERTIES -{ - GUID guidReceivingFXSlotID; - long lExclusion; - float flExclusionLFRatio; -}; // EAXSOURCEEXCLUSIONSENDPROPERTIES - -extern const EAX50ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID; - -extern const EAX50ACTIVEFXSLOTS EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID; - -extern const EAX50ACTIVEFXSLOTS EAX50SOURCE_2DDEFAULTACTIVEFXSLOTID; - - -// EAX Reverb Effect - -extern const GUID EAX_REVERB_EFFECT; - -// Reverb effect properties -enum EAXREVERB_PROPERTY : - unsigned int -{ - EAXREVERB_NONE, - EAXREVERB_ALLPARAMETERS, - EAXREVERB_ENVIRONMENT, - EAXREVERB_ENVIRONMENTSIZE, - EAXREVERB_ENVIRONMENTDIFFUSION, - EAXREVERB_ROOM, - EAXREVERB_ROOMHF, - EAXREVERB_ROOMLF, - EAXREVERB_DECAYTIME, - EAXREVERB_DECAYHFRATIO, - EAXREVERB_DECAYLFRATIO, - EAXREVERB_REFLECTIONS, - EAXREVERB_REFLECTIONSDELAY, - EAXREVERB_REFLECTIONSPAN, - EAXREVERB_REVERB, - EAXREVERB_REVERBDELAY, - EAXREVERB_REVERBPAN, - EAXREVERB_ECHOTIME, - EAXREVERB_ECHODEPTH, - EAXREVERB_MODULATIONTIME, - EAXREVERB_MODULATIONDEPTH, - EAXREVERB_AIRABSORPTIONHF, - EAXREVERB_HFREFERENCE, - EAXREVERB_LFREFERENCE, - EAXREVERB_ROOMROLLOFFFACTOR, - EAXREVERB_FLAGS, -}; // EAXREVERB_PROPERTY - -// used by EAXREVERB_ENVIRONMENT -enum : - unsigned long -{ - EAX_ENVIRONMENT_GENERIC, - EAX_ENVIRONMENT_PADDEDCELL, - EAX_ENVIRONMENT_ROOM, - EAX_ENVIRONMENT_BATHROOM, - EAX_ENVIRONMENT_LIVINGROOM, - EAX_ENVIRONMENT_STONEROOM, - EAX_ENVIRONMENT_AUDITORIUM, - EAX_ENVIRONMENT_CONCERTHALL, - EAX_ENVIRONMENT_CAVE, - EAX_ENVIRONMENT_ARENA, - EAX_ENVIRONMENT_HANGAR, - EAX_ENVIRONMENT_CARPETEDHALLWAY, - EAX_ENVIRONMENT_HALLWAY, - EAX_ENVIRONMENT_STONECORRIDOR, - EAX_ENVIRONMENT_ALLEY, - EAX_ENVIRONMENT_FOREST, - EAX_ENVIRONMENT_CITY, - EAX_ENVIRONMENT_MOUNTAINS, - EAX_ENVIRONMENT_QUARRY, - EAX_ENVIRONMENT_PLAIN, - EAX_ENVIRONMENT_PARKINGLOT, - EAX_ENVIRONMENT_SEWERPIPE, - EAX_ENVIRONMENT_UNDERWATER, - EAX_ENVIRONMENT_DRUGGED, - EAX_ENVIRONMENT_DIZZY, - EAX_ENVIRONMENT_PSYCHOTIC, - - EAX1_ENVIRONMENT_COUNT, - - // EAX30 - EAX_ENVIRONMENT_UNDEFINED = EAX1_ENVIRONMENT_COUNT, - - EAX3_ENVIRONMENT_COUNT, -}; - - -// reverberation decay time -constexpr auto EAXREVERBFLAGS_DECAYTIMESCALE = 0x00000001UL; - -// reflection level -constexpr auto EAXREVERBFLAGS_REFLECTIONSSCALE = 0x00000002UL; - -// initial reflection delay time -constexpr auto EAXREVERBFLAGS_REFLECTIONSDELAYSCALE = 0x00000004UL; - -// reflections level -constexpr auto EAXREVERBFLAGS_REVERBSCALE = 0x00000008UL; - -// late reverberation delay time -constexpr auto EAXREVERBFLAGS_REVERBDELAYSCALE = 0x00000010UL; - -// echo time -// EAX30+ -constexpr auto EAXREVERBFLAGS_ECHOTIMESCALE = 0x00000040UL; - -// modulation time -// EAX30+ -constexpr auto EAXREVERBFLAGS_MODULATIONTIMESCALE = 0x00000080UL; - -// This flag limits high-frequency decay time according to air absorption. -constexpr auto EAXREVERBFLAGS_DECAYHFLIMIT = 0x00000020UL; - -constexpr auto EAXREVERBFLAGS_RESERVED = 0xFFFFFF00UL; // reserved future use - - -struct EAXREVERBPROPERTIES -{ - unsigned long ulEnvironment; // sets all reverb properties - float flEnvironmentSize; // environment size in meters - float flEnvironmentDiffusion; // environment diffusion - long lRoom; // room effect level (at mid frequencies) - long lRoomHF; // relative room effect level at high frequencies - long lRoomLF; // relative room effect level at low frequencies - float flDecayTime; // reverberation decay time at mid frequencies - float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio - float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio - long lReflections; // early reflections level relative to room effect - float flReflectionsDelay; // initial reflection delay time - EAXVECTOR vReflectionsPan; // early reflections panning vector - long lReverb; // late reverberation level relative to room effect - float flReverbDelay; // late reverberation delay time relative to initial reflection - EAXVECTOR vReverbPan; // late reverberation panning vector - float flEchoTime; // echo time - float flEchoDepth; // echo depth - float flModulationTime; // modulation time - float flModulationDepth; // modulation depth - float flAirAbsorptionHF; // change in level per meter at high frequencies - float flHFReference; // reference high frequency - float flLFReference; // reference low frequency - float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect - unsigned long ulFlags; // modifies the behavior of properties -}; // EAXREVERBPROPERTIES - -bool operator==( - const EAXREVERBPROPERTIES& lhs, - const EAXREVERBPROPERTIES& rhs) noexcept; - -bool operator!=( - const EAXREVERBPROPERTIES& lhs, - const EAXREVERBPROPERTIES& rhs) noexcept; - - -constexpr auto EAXREVERB_MINENVIRONMENT = static_cast(EAX_ENVIRONMENT_GENERIC); -constexpr auto EAX1REVERB_MAXENVIRONMENT = static_cast(EAX_ENVIRONMENT_PSYCHOTIC); -constexpr auto EAX30REVERB_MAXENVIRONMENT = static_cast(EAX_ENVIRONMENT_UNDEFINED); -constexpr auto EAXREVERB_DEFAULTENVIRONMENT = static_cast(EAX_ENVIRONMENT_GENERIC); - -constexpr auto EAXREVERB_MINENVIRONMENTSIZE = 1.0F; -constexpr auto EAXREVERB_MAXENVIRONMENTSIZE = 100.0F; -constexpr auto EAXREVERB_DEFAULTENVIRONMENTSIZE = 7.5F; - -constexpr auto EAXREVERB_MINENVIRONMENTDIFFUSION = 0.0F; -constexpr auto EAXREVERB_MAXENVIRONMENTDIFFUSION = 1.0F; -constexpr auto EAXREVERB_DEFAULTENVIRONMENTDIFFUSION = 1.0F; - -constexpr auto EAXREVERB_MINROOM = -10'000L; -constexpr auto EAXREVERB_MAXROOM = 0L; -constexpr auto EAXREVERB_DEFAULTROOM = -1'000L; - -constexpr auto EAXREVERB_MINROOMHF = -10'000L; -constexpr auto EAXREVERB_MAXROOMHF = 0L; -constexpr auto EAXREVERB_DEFAULTROOMHF = -100L; - -constexpr auto EAXREVERB_MINROOMLF = -10'000L; -constexpr auto EAXREVERB_MAXROOMLF = 0L; -constexpr auto EAXREVERB_DEFAULTROOMLF = 0L; - -constexpr auto EAXREVERB_MINDECAYTIME = 0.1F; -constexpr auto EAXREVERB_MAXDECAYTIME = 20.0F; -constexpr auto EAXREVERB_DEFAULTDECAYTIME = 1.49F; - -constexpr auto EAXREVERB_MINDECAYHFRATIO = 0.1F; -constexpr auto EAXREVERB_MAXDECAYHFRATIO = 2.0F; -constexpr auto EAXREVERB_DEFAULTDECAYHFRATIO = 0.83F; - -constexpr auto EAXREVERB_MINDECAYLFRATIO = 0.1F; -constexpr auto EAXREVERB_MAXDECAYLFRATIO = 2.0F; -constexpr auto EAXREVERB_DEFAULTDECAYLFRATIO = 1.0F; - -constexpr auto EAXREVERB_MINREFLECTIONS = -10'000L; -constexpr auto EAXREVERB_MAXREFLECTIONS = 1'000L; -constexpr auto EAXREVERB_DEFAULTREFLECTIONS = -2'602L; - -constexpr auto EAXREVERB_MINREFLECTIONSDELAY = 0.0F; -constexpr auto EAXREVERB_MAXREFLECTIONSDELAY = 0.3F; -constexpr auto EAXREVERB_DEFAULTREFLECTIONSDELAY = 0.007F; - -constexpr auto EAXREVERB_DEFAULTREFLECTIONSPAN = EAXVECTOR{0.0F, 0.0F, 0.0F}; - -constexpr auto EAXREVERB_MINREVERB = -10'000L; -constexpr auto EAXREVERB_MAXREVERB = 2'000L; -constexpr auto EAXREVERB_DEFAULTREVERB = 200L; - -constexpr auto EAXREVERB_MINREVERBDELAY = 0.0F; -constexpr auto EAXREVERB_MAXREVERBDELAY = 0.1F; -constexpr auto EAXREVERB_DEFAULTREVERBDELAY = 0.011F; - -constexpr auto EAXREVERB_DEFAULTREVERBPAN = EAXVECTOR{0.0F, 0.0F, 0.0F}; - -constexpr auto EAXREVERB_MINECHOTIME = 0.075F; -constexpr auto EAXREVERB_MAXECHOTIME = 0.25F; -constexpr auto EAXREVERB_DEFAULTECHOTIME = 0.25F; - -constexpr auto EAXREVERB_MINECHODEPTH = 0.0F; -constexpr auto EAXREVERB_MAXECHODEPTH = 1.0F; -constexpr auto EAXREVERB_DEFAULTECHODEPTH = 0.0F; - -constexpr auto EAXREVERB_MINMODULATIONTIME = 0.04F; -constexpr auto EAXREVERB_MAXMODULATIONTIME = 4.0F; -constexpr auto EAXREVERB_DEFAULTMODULATIONTIME = 0.25F; - -constexpr auto EAXREVERB_MINMODULATIONDEPTH = 0.0F; -constexpr auto EAXREVERB_MAXMODULATIONDEPTH = 1.0F; -constexpr auto EAXREVERB_DEFAULTMODULATIONDEPTH = 0.0F; - -constexpr auto EAXREVERB_MINAIRABSORPTIONHF = -100.0F; -constexpr auto EAXREVERB_MAXAIRABSORPTIONHF = 0.0F; -constexpr auto EAXREVERB_DEFAULTAIRABSORPTIONHF = -5.0F; - -constexpr auto EAXREVERB_MINHFREFERENCE = 1'000.0F; -constexpr auto EAXREVERB_MAXHFREFERENCE = 20'000.0F; -constexpr auto EAXREVERB_DEFAULTHFREFERENCE = 5'000.0F; - -constexpr auto EAXREVERB_MINLFREFERENCE = 20.0F; -constexpr auto EAXREVERB_MAXLFREFERENCE = 1'000.0F; -constexpr auto EAXREVERB_DEFAULTLFREFERENCE = 250.0F; - -constexpr auto EAXREVERB_MINROOMROLLOFFFACTOR = 0.0F; -constexpr auto EAXREVERB_MAXROOMROLLOFFFACTOR = 10.0F; -constexpr auto EAXREVERB_DEFAULTROOMROLLOFFFACTOR = 0.0F; - -constexpr auto EAX1REVERB_MINVOLUME = 0.0F; -constexpr auto EAX1REVERB_MAXVOLUME = 1.0F; - -constexpr auto EAX1REVERB_MINDAMPING = 0.0F; -constexpr auto EAX1REVERB_MAXDAMPING = 2.0F; - -constexpr auto EAXREVERB_DEFAULTFLAGS = - EAXREVERBFLAGS_DECAYTIMESCALE | - EAXREVERBFLAGS_REFLECTIONSSCALE | - EAXREVERBFLAGS_REFLECTIONSDELAYSCALE | - EAXREVERBFLAGS_REVERBSCALE | - EAXREVERBFLAGS_REVERBDELAYSCALE | - EAXREVERBFLAGS_DECAYHFLIMIT; - - -using EaxReverbPresets = std::array; -extern const EaxReverbPresets EAXREVERB_PRESETS; - - -using Eax1ReverbPresets = std::array; -extern const Eax1ReverbPresets EAX1REVERB_PRESETS; - - -// AGC Compressor Effect - -extern const GUID EAX_AGCCOMPRESSOR_EFFECT; - -enum EAXAGCCOMPRESSOR_PROPERTY : - unsigned int -{ - EAXAGCCOMPRESSOR_NONE, - EAXAGCCOMPRESSOR_ALLPARAMETERS, - EAXAGCCOMPRESSOR_ONOFF, -}; // EAXAGCCOMPRESSOR_PROPERTY - -struct EAXAGCCOMPRESSORPROPERTIES -{ - unsigned long ulOnOff; // Switch Compressor on or off -}; // EAXAGCCOMPRESSORPROPERTIES - - -constexpr auto EAXAGCCOMPRESSOR_MINONOFF = 0UL; -constexpr auto EAXAGCCOMPRESSOR_MAXONOFF = 1UL; -constexpr auto EAXAGCCOMPRESSOR_DEFAULTONOFF = EAXAGCCOMPRESSOR_MAXONOFF; - - -// Autowah Effect - -extern const GUID EAX_AUTOWAH_EFFECT; - -enum EAXAUTOWAH_PROPERTY : - unsigned int -{ - EAXAUTOWAH_NONE, - EAXAUTOWAH_ALLPARAMETERS, - EAXAUTOWAH_ATTACKTIME, - EAXAUTOWAH_RELEASETIME, - EAXAUTOWAH_RESONANCE, - EAXAUTOWAH_PEAKLEVEL, -}; // EAXAUTOWAH_PROPERTY - -struct EAXAUTOWAHPROPERTIES -{ - float flAttackTime; // Attack time (seconds) - float flReleaseTime; // Release time (seconds) - long lResonance; // Resonance (mB) - long lPeakLevel; // Peak level (mB) -}; // EAXAUTOWAHPROPERTIES - - -constexpr auto EAXAUTOWAH_MINATTACKTIME = 0.0001F; -constexpr auto EAXAUTOWAH_MAXATTACKTIME = 1.0F; -constexpr auto EAXAUTOWAH_DEFAULTATTACKTIME = 0.06F; - -constexpr auto EAXAUTOWAH_MINRELEASETIME = 0.0001F; -constexpr auto EAXAUTOWAH_MAXRELEASETIME = 1.0F; -constexpr auto EAXAUTOWAH_DEFAULTRELEASETIME = 0.06F; - -constexpr auto EAXAUTOWAH_MINRESONANCE = 600L; -constexpr auto EAXAUTOWAH_MAXRESONANCE = 6000L; -constexpr auto EAXAUTOWAH_DEFAULTRESONANCE = 6000L; - -constexpr auto EAXAUTOWAH_MINPEAKLEVEL = -9000L; -constexpr auto EAXAUTOWAH_MAXPEAKLEVEL = 9000L; -constexpr auto EAXAUTOWAH_DEFAULTPEAKLEVEL = 2100L; - - -// Chorus Effect - -extern const GUID EAX_CHORUS_EFFECT; - - -enum EAXCHORUS_PROPERTY : - unsigned int -{ - EAXCHORUS_NONE, - EAXCHORUS_ALLPARAMETERS, - EAXCHORUS_WAVEFORM, - EAXCHORUS_PHASE, - EAXCHORUS_RATE, - EAXCHORUS_DEPTH, - EAXCHORUS_FEEDBACK, - EAXCHORUS_DELAY, -}; // EAXCHORUS_PROPERTY - -enum : - unsigned long -{ - EAX_CHORUS_SINUSOID, - EAX_CHORUS_TRIANGLE, -}; - -struct EAXCHORUSPROPERTIES -{ - unsigned long ulWaveform; // Waveform selector - see enum above - long lPhase; // Phase (Degrees) - float flRate; // Rate (Hz) - float flDepth; // Depth (0 to 1) - float flFeedback; // Feedback (-1 to 1) - float flDelay; // Delay (seconds) -}; // EAXCHORUSPROPERTIES - - -constexpr auto EAXCHORUS_MINWAVEFORM = 0UL; -constexpr auto EAXCHORUS_MAXWAVEFORM = 1UL; -constexpr auto EAXCHORUS_DEFAULTWAVEFORM = 1UL; - -constexpr auto EAXCHORUS_MINPHASE = -180L; -constexpr auto EAXCHORUS_MAXPHASE = 180L; -constexpr auto EAXCHORUS_DEFAULTPHASE = 90L; - -constexpr auto EAXCHORUS_MINRATE = 0.0F; -constexpr auto EAXCHORUS_MAXRATE = 10.0F; -constexpr auto EAXCHORUS_DEFAULTRATE = 1.1F; - -constexpr auto EAXCHORUS_MINDEPTH = 0.0F; -constexpr auto EAXCHORUS_MAXDEPTH = 1.0F; -constexpr auto EAXCHORUS_DEFAULTDEPTH = 0.1F; - -constexpr auto EAXCHORUS_MINFEEDBACK = -1.0F; -constexpr auto EAXCHORUS_MAXFEEDBACK = 1.0F; -constexpr auto EAXCHORUS_DEFAULTFEEDBACK = 0.25F; - -constexpr auto EAXCHORUS_MINDELAY = 0.0002F; -constexpr auto EAXCHORUS_MAXDELAY = 0.016F; -constexpr auto EAXCHORUS_DEFAULTDELAY = 0.016F; - - -// Distortion Effect - -extern const GUID EAX_DISTORTION_EFFECT; - -enum EAXDISTORTION_PROPERTY : - unsigned int -{ - EAXDISTORTION_NONE, - EAXDISTORTION_ALLPARAMETERS, - EAXDISTORTION_EDGE, - EAXDISTORTION_GAIN, - EAXDISTORTION_LOWPASSCUTOFF, - EAXDISTORTION_EQCENTER, - EAXDISTORTION_EQBANDWIDTH, -}; // EAXDISTORTION_PROPERTY - - -struct EAXDISTORTIONPROPERTIES -{ - float flEdge; // Controls the shape of the distortion (0 to 1) - long lGain; // Controls the post distortion gain (mB) - float flLowPassCutOff; // Controls the cut-off of the filter pre-distortion (Hz) - float flEQCenter; // Controls the center frequency of the EQ post-distortion (Hz) - float flEQBandwidth; // Controls the bandwidth of the EQ post-distortion (Hz) -}; // EAXDISTORTIONPROPERTIES - - -constexpr auto EAXDISTORTION_MINEDGE = 0.0F; -constexpr auto EAXDISTORTION_MAXEDGE = 1.0F; -constexpr auto EAXDISTORTION_DEFAULTEDGE = 0.2F; - -constexpr auto EAXDISTORTION_MINGAIN = -6000L; -constexpr auto EAXDISTORTION_MAXGAIN = 0L; -constexpr auto EAXDISTORTION_DEFAULTGAIN = -2600L; - -constexpr auto EAXDISTORTION_MINLOWPASSCUTOFF = 80.0F; -constexpr auto EAXDISTORTION_MAXLOWPASSCUTOFF = 24000.0F; -constexpr auto EAXDISTORTION_DEFAULTLOWPASSCUTOFF = 8000.0F; - -constexpr auto EAXDISTORTION_MINEQCENTER = 80.0F; -constexpr auto EAXDISTORTION_MAXEQCENTER = 24000.0F; -constexpr auto EAXDISTORTION_DEFAULTEQCENTER = 3600.0F; - -constexpr auto EAXDISTORTION_MINEQBANDWIDTH = 80.0F; -constexpr auto EAXDISTORTION_MAXEQBANDWIDTH = 24000.0F; -constexpr auto EAXDISTORTION_DEFAULTEQBANDWIDTH = 3600.0F; - - -// Echo Effect - -extern const GUID EAX_ECHO_EFFECT; - - -enum EAXECHO_PROPERTY : - unsigned int -{ - EAXECHO_NONE, - EAXECHO_ALLPARAMETERS, - EAXECHO_DELAY, - EAXECHO_LRDELAY, - EAXECHO_DAMPING, - EAXECHO_FEEDBACK, - EAXECHO_SPREAD, -}; // EAXECHO_PROPERTY - - -struct EAXECHOPROPERTIES -{ - float flDelay; // Controls the initial delay time (seconds) - float flLRDelay; // Controls the delay time between the first and second taps (seconds) - float flDamping; // Controls a low-pass filter that dampens the echoes (0 to 1) - float flFeedback; // Controls the duration of echo repetition (0 to 1) - float flSpread; // Controls the left-right spread of the echoes -}; // EAXECHOPROPERTIES - - -constexpr auto EAXECHO_MINDAMPING = 0.0F; -constexpr auto EAXECHO_MAXDAMPING = 0.99F; -constexpr auto EAXECHO_DEFAULTDAMPING = 0.5F; - -constexpr auto EAXECHO_MINDELAY = 0.002F; -constexpr auto EAXECHO_MAXDELAY = 0.207F; -constexpr auto EAXECHO_DEFAULTDELAY = 0.1F; - -constexpr auto EAXECHO_MINLRDELAY = 0.0F; -constexpr auto EAXECHO_MAXLRDELAY = 0.404F; -constexpr auto EAXECHO_DEFAULTLRDELAY = 0.1F; - -constexpr auto EAXECHO_MINFEEDBACK = 0.0F; -constexpr auto EAXECHO_MAXFEEDBACK = 1.0F; -constexpr auto EAXECHO_DEFAULTFEEDBACK = 0.5F; - -constexpr auto EAXECHO_MINSPREAD = -1.0F; -constexpr auto EAXECHO_MAXSPREAD = 1.0F; -constexpr auto EAXECHO_DEFAULTSPREAD = -1.0F; - - -// Equalizer Effect - -extern const GUID EAX_EQUALIZER_EFFECT; - - -enum EAXEQUALIZER_PROPERTY : - unsigned int -{ - EAXEQUALIZER_NONE, - EAXEQUALIZER_ALLPARAMETERS, - EAXEQUALIZER_LOWGAIN, - EAXEQUALIZER_LOWCUTOFF, - EAXEQUALIZER_MID1GAIN, - EAXEQUALIZER_MID1CENTER, - EAXEQUALIZER_MID1WIDTH, - EAXEQUALIZER_MID2GAIN, - EAXEQUALIZER_MID2CENTER, - EAXEQUALIZER_MID2WIDTH, - EAXEQUALIZER_HIGHGAIN, - EAXEQUALIZER_HIGHCUTOFF, -}; // EAXEQUALIZER_PROPERTY - - -struct EAXEQUALIZERPROPERTIES -{ - long lLowGain; // (mB) - float flLowCutOff; // (Hz) - long lMid1Gain; // (mB) - float flMid1Center; // (Hz) - float flMid1Width; // (octaves) - long lMid2Gain; // (mB) - float flMid2Center; // (Hz) - float flMid2Width; // (octaves) - long lHighGain; // (mB) - float flHighCutOff; // (Hz) -}; // EAXEQUALIZERPROPERTIES - - -constexpr auto EAXEQUALIZER_MINLOWGAIN = -1800L; -constexpr auto EAXEQUALIZER_MAXLOWGAIN = 1800L; -constexpr auto EAXEQUALIZER_DEFAULTLOWGAIN = 0L; - -constexpr auto EAXEQUALIZER_MINLOWCUTOFF = 50.0F; -constexpr auto EAXEQUALIZER_MAXLOWCUTOFF = 800.0F; -constexpr auto EAXEQUALIZER_DEFAULTLOWCUTOFF = 200.0F; - -constexpr auto EAXEQUALIZER_MINMID1GAIN = -1800L; -constexpr auto EAXEQUALIZER_MAXMID1GAIN = 1800L; -constexpr auto EAXEQUALIZER_DEFAULTMID1GAIN = 0L; - -constexpr auto EAXEQUALIZER_MINMID1CENTER = 200.0F; -constexpr auto EAXEQUALIZER_MAXMID1CENTER = 3000.0F; -constexpr auto EAXEQUALIZER_DEFAULTMID1CENTER = 500.0F; - -constexpr auto EAXEQUALIZER_MINMID1WIDTH = 0.01F; -constexpr auto EAXEQUALIZER_MAXMID1WIDTH = 1.0F; -constexpr auto EAXEQUALIZER_DEFAULTMID1WIDTH = 1.0F; - -constexpr auto EAXEQUALIZER_MINMID2GAIN = -1800L; -constexpr auto EAXEQUALIZER_MAXMID2GAIN = 1800L; -constexpr auto EAXEQUALIZER_DEFAULTMID2GAIN = 0L; - -constexpr auto EAXEQUALIZER_MINMID2CENTER = 1000.0F; -constexpr auto EAXEQUALIZER_MAXMID2CENTER = 8000.0F; -constexpr auto EAXEQUALIZER_DEFAULTMID2CENTER = 3000.0F; - -constexpr auto EAXEQUALIZER_MINMID2WIDTH = 0.01F; -constexpr auto EAXEQUALIZER_MAXMID2WIDTH = 1.0F; -constexpr auto EAXEQUALIZER_DEFAULTMID2WIDTH = 1.0F; - -constexpr auto EAXEQUALIZER_MINHIGHGAIN = -1800L; -constexpr auto EAXEQUALIZER_MAXHIGHGAIN = 1800L; -constexpr auto EAXEQUALIZER_DEFAULTHIGHGAIN = 0L; - -constexpr auto EAXEQUALIZER_MINHIGHCUTOFF = 4000.0F; -constexpr auto EAXEQUALIZER_MAXHIGHCUTOFF = 16000.0F; -constexpr auto EAXEQUALIZER_DEFAULTHIGHCUTOFF = 6000.0F; - - -// Flanger Effect - -extern const GUID EAX_FLANGER_EFFECT; - -enum EAXFLANGER_PROPERTY : - unsigned int -{ - EAXFLANGER_NONE, - EAXFLANGER_ALLPARAMETERS, - EAXFLANGER_WAVEFORM, - EAXFLANGER_PHASE, - EAXFLANGER_RATE, - EAXFLANGER_DEPTH, - EAXFLANGER_FEEDBACK, - EAXFLANGER_DELAY, -}; // EAXFLANGER_PROPERTY - -enum : - unsigned long -{ - EAX_FLANGER_SINUSOID, - EAX_FLANGER_TRIANGLE, -}; - -struct EAXFLANGERPROPERTIES -{ - unsigned long ulWaveform; // Waveform selector - see enum above - long lPhase; // Phase (Degrees) - float flRate; // Rate (Hz) - float flDepth; // Depth (0 to 1) - float flFeedback; // Feedback (0 to 1) - float flDelay; // Delay (seconds) -}; // EAXFLANGERPROPERTIES - - -constexpr auto EAXFLANGER_MINWAVEFORM = 0UL; -constexpr auto EAXFLANGER_MAXWAVEFORM = 1UL; -constexpr auto EAXFLANGER_DEFAULTWAVEFORM = 1UL; - -constexpr auto EAXFLANGER_MINPHASE = -180L; -constexpr auto EAXFLANGER_MAXPHASE = 180L; -constexpr auto EAXFLANGER_DEFAULTPHASE = 0L; - -constexpr auto EAXFLANGER_MINRATE = 0.0F; -constexpr auto EAXFLANGER_MAXRATE = 10.0F; -constexpr auto EAXFLANGER_DEFAULTRATE = 0.27F; - -constexpr auto EAXFLANGER_MINDEPTH = 0.0F; -constexpr auto EAXFLANGER_MAXDEPTH = 1.0F; -constexpr auto EAXFLANGER_DEFAULTDEPTH = 1.0F; - -constexpr auto EAXFLANGER_MINFEEDBACK = -1.0F; -constexpr auto EAXFLANGER_MAXFEEDBACK = 1.0F; -constexpr auto EAXFLANGER_DEFAULTFEEDBACK = -0.5F; - -constexpr auto EAXFLANGER_MINDELAY = 0.0002F; -constexpr auto EAXFLANGER_MAXDELAY = 0.004F; -constexpr auto EAXFLANGER_DEFAULTDELAY = 0.002F; - - -// Frequency Shifter Effect - -extern const GUID EAX_FREQUENCYSHIFTER_EFFECT; - -enum EAXFREQUENCYSHIFTER_PROPERTY : - unsigned int -{ - EAXFREQUENCYSHIFTER_NONE, - EAXFREQUENCYSHIFTER_ALLPARAMETERS, - EAXFREQUENCYSHIFTER_FREQUENCY, - EAXFREQUENCYSHIFTER_LEFTDIRECTION, - EAXFREQUENCYSHIFTER_RIGHTDIRECTION, -}; // EAXFREQUENCYSHIFTER_PROPERTY - -enum : - unsigned long -{ - EAX_FREQUENCYSHIFTER_DOWN, - EAX_FREQUENCYSHIFTER_UP, - EAX_FREQUENCYSHIFTER_OFF -}; - -struct EAXFREQUENCYSHIFTERPROPERTIES -{ - float flFrequency; // (Hz) - unsigned long ulLeftDirection; // see enum above - unsigned long ulRightDirection; // see enum above -}; // EAXFREQUENCYSHIFTERPROPERTIES - - -constexpr auto EAXFREQUENCYSHIFTER_MINFREQUENCY = 0.0F; -constexpr auto EAXFREQUENCYSHIFTER_MAXFREQUENCY = 24000.0F; -constexpr auto EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY = EAXFREQUENCYSHIFTER_MINFREQUENCY; - -constexpr auto EAXFREQUENCYSHIFTER_MINLEFTDIRECTION = 0UL; -constexpr auto EAXFREQUENCYSHIFTER_MAXLEFTDIRECTION = 2UL; -constexpr auto EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION = EAXFREQUENCYSHIFTER_MINLEFTDIRECTION; - -constexpr auto EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION = 0UL; -constexpr auto EAXFREQUENCYSHIFTER_MAXRIGHTDIRECTION = 2UL; -constexpr auto EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION = EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION; - - -// Vocal Morpher Effect - -extern const GUID EAX_VOCALMORPHER_EFFECT; - -enum EAXVOCALMORPHER_PROPERTY : - unsigned int -{ - EAXVOCALMORPHER_NONE, - EAXVOCALMORPHER_ALLPARAMETERS, - EAXVOCALMORPHER_PHONEMEA, - EAXVOCALMORPHER_PHONEMEACOARSETUNING, - EAXVOCALMORPHER_PHONEMEB, - EAXVOCALMORPHER_PHONEMEBCOARSETUNING, - EAXVOCALMORPHER_WAVEFORM, - EAXVOCALMORPHER_RATE, -}; // EAXVOCALMORPHER_PROPERTY - -enum : - unsigned long -{ - A, - E, - I, - O, - U, - AA, - AE, - AH, - AO, - EH, - ER, - IH, - IY, - UH, - UW, - B, - D, - F, - G, - J, - K, - L, - M, - N, - P, - R, - S, - T, - V, - Z, -}; - -enum : - unsigned long -{ - EAX_VOCALMORPHER_SINUSOID, - EAX_VOCALMORPHER_TRIANGLE, - EAX_VOCALMORPHER_SAWTOOTH -}; - -// Use this structure for EAXVOCALMORPHER_ALLPARAMETERS -struct EAXVOCALMORPHERPROPERTIES -{ - unsigned long ulPhonemeA; // see enum above - long lPhonemeACoarseTuning; // (semitones) - unsigned long ulPhonemeB; // see enum above - long lPhonemeBCoarseTuning; // (semitones) - unsigned long ulWaveform; // Waveform selector - see enum above - float flRate; // (Hz) -}; // EAXVOCALMORPHERPROPERTIES - - -constexpr auto EAXVOCALMORPHER_MINPHONEMEA = 0UL; -constexpr auto EAXVOCALMORPHER_MAXPHONEMEA = 29UL; -constexpr auto EAXVOCALMORPHER_DEFAULTPHONEMEA = EAXVOCALMORPHER_MINPHONEMEA; - -constexpr auto EAXVOCALMORPHER_MINPHONEMEACOARSETUNING = -24L; -constexpr auto EAXVOCALMORPHER_MAXPHONEMEACOARSETUNING = 24L; -constexpr auto EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING = 0L; - -constexpr auto EAXVOCALMORPHER_MINPHONEMEB = 0UL; -constexpr auto EAXVOCALMORPHER_MAXPHONEMEB = 29UL; -constexpr auto EAXVOCALMORPHER_DEFAULTPHONEMEB = 10UL; - -constexpr auto EAXVOCALMORPHER_MINPHONEMEBCOARSETUNING = -24L; -constexpr auto EAXVOCALMORPHER_MAXPHONEMEBCOARSETUNING = 24L; -constexpr auto EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING = 0L; - -constexpr auto EAXVOCALMORPHER_MINWAVEFORM = 0UL; -constexpr auto EAXVOCALMORPHER_MAXWAVEFORM = 2UL; -constexpr auto EAXVOCALMORPHER_DEFAULTWAVEFORM = EAXVOCALMORPHER_MINWAVEFORM; - -constexpr auto EAXVOCALMORPHER_MINRATE = 0.0F; -constexpr auto EAXVOCALMORPHER_MAXRATE = 10.0F; -constexpr auto EAXVOCALMORPHER_DEFAULTRATE = 1.41F; - - -// Pitch Shifter Effect - -extern const GUID EAX_PITCHSHIFTER_EFFECT; - -enum EAXPITCHSHIFTER_PROPERTY : - unsigned int -{ - EAXPITCHSHIFTER_NONE, - EAXPITCHSHIFTER_ALLPARAMETERS, - EAXPITCHSHIFTER_COARSETUNE, - EAXPITCHSHIFTER_FINETUNE, -}; // EAXPITCHSHIFTER_PROPERTY - -struct EAXPITCHSHIFTERPROPERTIES -{ - long lCoarseTune; // Amount of pitch shift (semitones) - long lFineTune; // Amount of pitch shift (cents) -}; // EAXPITCHSHIFTERPROPERTIES - - -constexpr auto EAXPITCHSHIFTER_MINCOARSETUNE = -12L; -constexpr auto EAXPITCHSHIFTER_MAXCOARSETUNE = 12L; -constexpr auto EAXPITCHSHIFTER_DEFAULTCOARSETUNE = 12L; - -constexpr auto EAXPITCHSHIFTER_MINFINETUNE = -50L; -constexpr auto EAXPITCHSHIFTER_MAXFINETUNE = 50L; -constexpr auto EAXPITCHSHIFTER_DEFAULTFINETUNE = 0L; - - -// Ring Modulator Effect - -extern const GUID EAX_RINGMODULATOR_EFFECT; - -enum EAXRINGMODULATOR_PROPERTY : - unsigned int -{ - EAXRINGMODULATOR_NONE, - EAXRINGMODULATOR_ALLPARAMETERS, - EAXRINGMODULATOR_FREQUENCY, - EAXRINGMODULATOR_HIGHPASSCUTOFF, - EAXRINGMODULATOR_WAVEFORM, -}; // EAXRINGMODULATOR_PROPERTY - -enum : - unsigned long -{ - EAX_RINGMODULATOR_SINUSOID, - EAX_RINGMODULATOR_SAWTOOTH, - EAX_RINGMODULATOR_SQUARE, -}; - -// Use this structure for EAXRINGMODULATOR_ALLPARAMETERS -struct EAXRINGMODULATORPROPERTIES -{ - float flFrequency; // Frequency of modulation (Hz) - float flHighPassCutOff; // Cut-off frequency of high-pass filter (Hz) - unsigned long ulWaveform; // Waveform selector - see enum above -}; // EAXRINGMODULATORPROPERTIES - - -constexpr auto EAXRINGMODULATOR_MINFREQUENCY = 0.0F; -constexpr auto EAXRINGMODULATOR_MAXFREQUENCY = 8000.0F; -constexpr auto EAXRINGMODULATOR_DEFAULTFREQUENCY = 440.0F; - -constexpr auto EAXRINGMODULATOR_MINHIGHPASSCUTOFF = 0.0F; -constexpr auto EAXRINGMODULATOR_MAXHIGHPASSCUTOFF = 24000.0F; -constexpr auto EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF = 800.0F; - -constexpr auto EAXRINGMODULATOR_MINWAVEFORM = 0UL; -constexpr auto EAXRINGMODULATOR_MAXWAVEFORM = 2UL; -constexpr auto EAXRINGMODULATOR_DEFAULTWAVEFORM = EAXRINGMODULATOR_MINWAVEFORM; - - -using LPEAXSET = ALenum(AL_APIENTRY*)( - const GUID* property_set_id, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_buffer, - ALuint property_size); - -using LPEAXGET = ALenum(AL_APIENTRY*)( - const GUID* property_set_id, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_buffer, - ALuint property_size); - - -#endif // !EAX_API_INCLUDED diff --git a/al/eax_eax_call.cpp b/al/eax_eax_call.cpp deleted file mode 100644 index 914d2fbf..00000000 --- a/al/eax_eax_call.cpp +++ /dev/null @@ -1,324 +0,0 @@ -#include "config.h" - -#include "al/eax_eax_call.h" - -#include "al/eax_exception.h" - - -namespace { - -constexpr auto deferred_flag = 0x80000000U; - -class EaxEaxCallException : - public EaxException -{ -public: - explicit EaxEaxCallException( - const char* message) - : - EaxException{"EAX_EAX_CALL", message} - { - } -}; // EaxEaxCallException - -} // namespace - - -EaxEaxCall::EaxEaxCall( - bool is_get, - const GUID& property_set_guid, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_buffer, - ALuint property_size) - : is_get_{is_get}, version_{0}, property_set_id_{EaxEaxCallPropertySetId::none} - , property_id_{property_id & ~deferred_flag}, property_source_id_{property_source_id} - , property_buffer_{property_buffer}, property_size_{property_size} -{ - if (false) - { - } - else if (property_set_guid == EAXPROPERTYID_EAX40_Context) - { - version_ = 4; - property_set_id_ = EaxEaxCallPropertySetId::context; - } - else if (property_set_guid == EAXPROPERTYID_EAX50_Context) - { - version_ = 5; - property_set_id_ = EaxEaxCallPropertySetId::context; - } - else if (property_set_guid == DSPROPSETID_EAX20_ListenerProperties) - { - version_ = 2; - fx_slot_index_ = 0u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect; - property_id_ = convert_eax_v2_0_listener_property_id(property_id_); - } - else if (property_set_guid == DSPROPSETID_EAX30_ListenerProperties) - { - version_ = 3; - fx_slot_index_ = 0u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect; - } - else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot0) - { - version_ = 4; - fx_slot_index_ = 0u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot0) - { - version_ = 5; - fx_slot_index_ = 0u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot1) - { - version_ = 4; - fx_slot_index_ = 1u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot1) - { - version_ = 5; - fx_slot_index_ = 1u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot2) - { - version_ = 4; - fx_slot_index_ = 2u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot2) - { - version_ = 5; - fx_slot_index_ = 2u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot3) - { - version_ = 4; - fx_slot_index_ = 3u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot3) - { - version_ = 5; - fx_slot_index_ = 3u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == DSPROPSETID_EAX20_BufferProperties) - { - version_ = 2; - property_set_id_ = EaxEaxCallPropertySetId::source; - property_id_ = convert_eax_v2_0_buffer_property_id(property_id_); - } - else if (property_set_guid == DSPROPSETID_EAX30_BufferProperties) - { - version_ = 3; - property_set_id_ = EaxEaxCallPropertySetId::source; - } - else if (property_set_guid == EAXPROPERTYID_EAX40_Source) - { - version_ = 4; - property_set_id_ = EaxEaxCallPropertySetId::source; - } - else if (property_set_guid == EAXPROPERTYID_EAX50_Source) - { - version_ = 5; - property_set_id_ = EaxEaxCallPropertySetId::source; - } - else if (property_set_guid == DSPROPSETID_EAX_ReverbProperties) - { - version_ = 1; - fx_slot_index_ = 0u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect; - } - else if (property_set_guid == DSPROPSETID_EAXBUFFER_ReverbProperties) - { - version_ = 1; - property_set_id_ = EaxEaxCallPropertySetId::source; - } - else - { - fail("Unsupported property set id."); - } - - if (version_ < 1 || version_ > 5) - { - fail("EAX version out of range."); - } - - if(!(property_id&deferred_flag)) - { - if(property_set_id_ != EaxEaxCallPropertySetId::fx_slot && property_id_ != 0) - { - if (!property_buffer) - { - fail("Null property buffer."); - } - - if (property_size == 0) - { - fail("Empty property."); - } - } - } - - if(property_set_id_ == EaxEaxCallPropertySetId::source && property_source_id_ == 0) - { - fail("Null AL source id."); - } - - if (property_set_id_ == EaxEaxCallPropertySetId::fx_slot) - { - if (property_id_ < EAXFXSLOT_NONE) - { - property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect; - } - } -} - -[[noreturn]] -void EaxEaxCall::fail( - const char* message) -{ - throw EaxEaxCallException{message}; -} - -ALuint EaxEaxCall::convert_eax_v2_0_listener_property_id( - ALuint property_id) -{ - switch (property_id) - { - case DSPROPERTY_EAX20LISTENER_NONE: - return EAXREVERB_NONE; - - case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS: - return EAXREVERB_ALLPARAMETERS; - - case DSPROPERTY_EAX20LISTENER_ROOM: - return EAXREVERB_ROOM; - - case DSPROPERTY_EAX20LISTENER_ROOMHF: - return EAXREVERB_ROOMHF; - - case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR: - return EAXREVERB_ROOMROLLOFFFACTOR; - - case DSPROPERTY_EAX20LISTENER_DECAYTIME: - return EAXREVERB_DECAYTIME; - - case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO: - return EAXREVERB_DECAYHFRATIO; - - case DSPROPERTY_EAX20LISTENER_REFLECTIONS: - return EAXREVERB_REFLECTIONS; - - case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY: - return EAXREVERB_REFLECTIONSDELAY; - - case DSPROPERTY_EAX20LISTENER_REVERB: - return EAXREVERB_REVERB; - - case DSPROPERTY_EAX20LISTENER_REVERBDELAY: - return EAXREVERB_REVERBDELAY; - - case DSPROPERTY_EAX20LISTENER_ENVIRONMENT: - return EAXREVERB_ENVIRONMENT; - - case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE: - return EAXREVERB_ENVIRONMENTSIZE; - - case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION: - return EAXREVERB_ENVIRONMENTDIFFUSION; - - case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF: - return EAXREVERB_AIRABSORPTIONHF; - - case DSPROPERTY_EAX20LISTENER_FLAGS: - return EAXREVERB_FLAGS; - - default: - fail("Unsupported EAX 2.0 listener property id."); - } -} - -ALuint EaxEaxCall::convert_eax_v2_0_buffer_property_id( - ALuint property_id) -{ - switch (property_id) - { - case DSPROPERTY_EAX20BUFFER_NONE: - return EAXSOURCE_NONE; - - case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS: - return EAXSOURCE_ALLPARAMETERS; - - case DSPROPERTY_EAX20BUFFER_DIRECT: - return EAXSOURCE_DIRECT; - - case DSPROPERTY_EAX20BUFFER_DIRECTHF: - return EAXSOURCE_DIRECTHF; - - case DSPROPERTY_EAX20BUFFER_ROOM: - return EAXSOURCE_ROOM; - - case DSPROPERTY_EAX20BUFFER_ROOMHF: - return EAXSOURCE_ROOMHF; - - case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR: - return EAXSOURCE_ROOMROLLOFFFACTOR; - - case DSPROPERTY_EAX20BUFFER_OBSTRUCTION: - return EAXSOURCE_OBSTRUCTION; - - case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO: - return EAXSOURCE_OBSTRUCTIONLFRATIO; - - case DSPROPERTY_EAX20BUFFER_OCCLUSION: - return EAXSOURCE_OCCLUSION; - - case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO: - return EAXSOURCE_OCCLUSIONLFRATIO; - - case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO: - return EAXSOURCE_OCCLUSIONROOMRATIO; - - case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF: - return EAXSOURCE_OUTSIDEVOLUMEHF; - - case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR: - return EAXSOURCE_AIRABSORPTIONFACTOR; - - case DSPROPERTY_EAX20BUFFER_FLAGS: - return EAXSOURCE_FLAGS; - - default: - fail("Unsupported EAX 2.0 buffer property id."); - } -} - - -EaxEaxCall create_eax_call( - bool is_get, - const GUID* property_set_id, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_buffer, - ALuint property_size) -{ - if(!property_set_id) - throw EaxEaxCallException{"Null property set ID."}; - - return EaxEaxCall{ - is_get, - *property_set_id, - property_id, - property_source_id, - property_buffer, - property_size - }; -} diff --git a/al/eax_eax_call.h b/al/eax_eax_call.h deleted file mode 100644 index 7b990d87..00000000 --- a/al/eax_eax_call.h +++ /dev/null @@ -1,117 +0,0 @@ -#ifndef EAX_EAX_CALL_INCLUDED -#define EAX_EAX_CALL_INCLUDED - - -#include "AL/al.h" - -#include "alspan.h" - -#include "eax_api.h" -#include "eax_fx_slot_index.h" - - -enum class EaxEaxCallPropertySetId -{ - none, - - context, - fx_slot, - source, - fx_slot_effect, -}; // EaxEaxCallPropertySetId - - -class EaxEaxCall -{ -public: - EaxEaxCall( - bool is_get, - const GUID& property_set_guid, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_buffer, - ALuint property_size); - - bool is_get() const noexcept { return is_get_; } - int get_version() const noexcept { return version_; } - EaxEaxCallPropertySetId get_property_set_id() const noexcept { return property_set_id_; } - ALuint get_property_id() const noexcept { return property_id_; } - ALuint get_property_al_name() const noexcept { return property_source_id_; } - EaxFxSlotIndex get_fx_slot_index() const noexcept { return fx_slot_index_; } - - template< - typename TException, - typename TValue - > - TValue& get_value() const - { - if (property_size_ < static_cast(sizeof(TValue))) - { - throw TException{"Property buffer too small."}; - } - - return *static_cast(property_buffer_); - } - - template< - typename TException, - typename TValue - > - al::span get_values() const - { - if (property_size_ < static_cast(sizeof(TValue))) - { - throw TException{"Property buffer too small."}; - } - - const auto count = property_size_ / sizeof(TValue); - - return al::span{static_cast(property_buffer_), count}; - } - - template< - typename TException, - typename TValue - > - void set_value( - const TValue& value) const - { - get_value() = value; - } - - -private: - const bool is_get_; - int version_; - EaxFxSlotIndex fx_slot_index_; - EaxEaxCallPropertySetId property_set_id_; - - ALuint property_id_; - const ALuint property_source_id_; - ALvoid*const property_buffer_; - const ALuint property_size_; - - - [[noreturn]] - static void fail( - const char* message); - - - static ALuint convert_eax_v2_0_listener_property_id( - ALuint property_id); - - static ALuint convert_eax_v2_0_buffer_property_id( - ALuint property_id); -}; // EaxEaxCall - - -EaxEaxCall create_eax_call( - bool is_get, - const GUID* property_set_id, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_buffer, - ALuint property_size); - - -#endif // !EAX_EAX_CALL_INCLUDED diff --git a/al/eax_effect.cpp b/al/eax_effect.cpp deleted file mode 100644 index 9cbf4c13..00000000 --- a/al/eax_effect.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "config.h" - -#include "eax_effect.h" diff --git a/al/eax_effect.h b/al/eax_effect.h deleted file mode 100644 index 45315ca6..00000000 --- a/al/eax_effect.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef EAX_EFFECT_INCLUDED -#define EAX_EFFECT_INCLUDED - - -#include - -#include "AL/al.h" -#include "core/effects/base.h" -#include "eax_eax_call.h" - -class EaxEffect -{ -public: - EaxEffect(ALenum type) : al_effect_type_{type} { } - virtual ~EaxEffect() = default; - - const ALenum al_effect_type_; - EffectProps al_effect_props_{}; - - virtual void dispatch(const EaxEaxCall& eax_call) = 0; - - // Returns "true" if any immediated property was changed. - // [[nodiscard]] - virtual bool apply_deferred() = 0; -}; // EaxEffect - - -using EaxEffectUPtr = std::unique_ptr; - -EaxEffectUPtr eax_create_eax_null_effect(); -EaxEffectUPtr eax_create_eax_chorus_effect(); -EaxEffectUPtr eax_create_eax_distortion_effect(); -EaxEffectUPtr eax_create_eax_echo_effect(); -EaxEffectUPtr eax_create_eax_flanger_effect(); -EaxEffectUPtr eax_create_eax_frequency_shifter_effect(); -EaxEffectUPtr eax_create_eax_vocal_morpher_effect(); -EaxEffectUPtr eax_create_eax_pitch_shifter_effect(); -EaxEffectUPtr eax_create_eax_ring_modulator_effect(); -EaxEffectUPtr eax_create_eax_auto_wah_effect(); -EaxEffectUPtr eax_create_eax_compressor_effect(); -EaxEffectUPtr eax_create_eax_equalizer_effect(); -EaxEffectUPtr eax_create_eax_reverb_effect(); - -#endif // !EAX_EFFECT_INCLUDED diff --git a/al/eax_exception.cpp b/al/eax_exception.cpp deleted file mode 100644 index fdeecaaa..00000000 --- a/al/eax_exception.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "config.h" - -#include "eax_exception.h" - -#include - -#include - - -EaxException::EaxException( - const char* context, - const char* message) - : - std::runtime_error{make_message(context, message)} -{ -} - -std::string EaxException::make_message( - const char* context, - const char* message) -{ - const auto context_size = (context ? std::string::traits_type::length(context) : 0); - const auto has_contex = (context_size > 0); - - const auto message_size = (message ? std::string::traits_type::length(message) : 0); - const auto has_message = (message_size > 0); - - if (!has_contex && !has_message) - { - return std::string{}; - } - - static constexpr char left_prefix[] = "["; - const auto left_prefix_size = std::string::traits_type::length(left_prefix); - - static constexpr char right_prefix[] = "] "; - const auto right_prefix_size = std::string::traits_type::length(right_prefix); - - const auto what_size = - ( - has_contex ? - left_prefix_size + context_size + right_prefix_size : - 0) + - message_size + - 1; - - auto what = std::string{}; - what.reserve(what_size); - - if (has_contex) - { - what.append(left_prefix, left_prefix_size); - what.append(context, context_size); - what.append(right_prefix, right_prefix_size); - } - - if (has_message) - { - what.append(message, message_size); - } - - return what; -} diff --git a/al/eax_exception.h b/al/eax_exception.h deleted file mode 100644 index 9a7acf71..00000000 --- a/al/eax_exception.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef EAX_EXCEPTION_INCLUDED -#define EAX_EXCEPTION_INCLUDED - - -#include -#include - - -class EaxException : - public std::runtime_error -{ -public: - EaxException( - const char* context, - const char* message); - - -private: - static std::string make_message( - const char* context, - const char* message); -}; // EaxException - - -#endif // !EAX_EXCEPTION_INCLUDED diff --git a/al/eax_fx_slot_index.cpp b/al/eax_fx_slot_index.cpp deleted file mode 100644 index 9aa695ad..00000000 --- a/al/eax_fx_slot_index.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "config.h" - -#include "eax_fx_slot_index.h" - -#include "eax_exception.h" - - -namespace -{ - - -class EaxFxSlotIndexException : - public EaxException -{ -public: - explicit EaxFxSlotIndexException( - const char* message) - : - EaxException{"EAX_FX_SLOT_INDEX", message} - { - } -}; // EaxFxSlotIndexException - - -} // namespace - - -void EaxFxSlotIndex::set(EaxFxSlotIndexValue index) -{ - if(index >= EaxFxSlotIndexValue{EAX_MAX_FXSLOTS}) - fail("Index out of range."); - - emplace(index); -} - -void EaxFxSlotIndex::set(const GUID &guid) -{ - if (false) - { - } - else if (guid == EAX_NULL_GUID) - { - reset(); - } - else if (guid == EAXPROPERTYID_EAX40_FXSlot0 || guid == EAXPROPERTYID_EAX50_FXSlot0) - { - emplace(0u); - } - else if (guid == EAXPROPERTYID_EAX40_FXSlot1 || guid == EAXPROPERTYID_EAX50_FXSlot1) - { - emplace(1u); - } - else if (guid == EAXPROPERTYID_EAX40_FXSlot2 || guid == EAXPROPERTYID_EAX50_FXSlot2) - { - emplace(2u); - } - else if (guid == EAXPROPERTYID_EAX40_FXSlot3 || guid == EAXPROPERTYID_EAX50_FXSlot3) - { - emplace(3u); - } - else - { - fail("Unsupported GUID."); - } -} - -[[noreturn]] -void EaxFxSlotIndex::fail(const char* message) -{ - throw EaxFxSlotIndexException{message}; -} diff --git a/al/eax_fx_slot_index.h b/al/eax_fx_slot_index.h deleted file mode 100644 index e1e5475a..00000000 --- a/al/eax_fx_slot_index.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef EAX_FX_SLOT_INDEX_INCLUDED -#define EAX_FX_SLOT_INDEX_INCLUDED - - -#include - -#include "aloptional.h" -#include "eax_api.h" - - -using EaxFxSlotIndexValue = std::size_t; - -class EaxFxSlotIndex : public al::optional -{ -public: - using al::optional::optional; - - EaxFxSlotIndex& operator=(const EaxFxSlotIndexValue &value) { set(value); return *this; } - EaxFxSlotIndex& operator=(const GUID &guid) { set(guid); return *this; } - - void set(EaxFxSlotIndexValue index); - void set(const GUID& guid); - -private: - [[noreturn]] - static void fail(const char *message); -}; // EaxFxSlotIndex - -inline bool operator==(const EaxFxSlotIndex& lhs, const EaxFxSlotIndex& rhs) noexcept -{ - if(lhs.has_value() != rhs.has_value()) - return false; - if(lhs.has_value()) - return *lhs == *rhs; - return true; -} - -inline bool operator!=(const EaxFxSlotIndex& lhs, const EaxFxSlotIndex& rhs) noexcept -{ return !(lhs == rhs); } - -#endif // !EAX_FX_SLOT_INDEX_INCLUDED diff --git a/al/eax_fx_slots.cpp b/al/eax_fx_slots.cpp deleted file mode 100644 index 3a27dabd..00000000 --- a/al/eax_fx_slots.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "config.h" - -#include "eax_fx_slots.h" - -#include - -#include "eax_exception.h" - -#include "eax_api.h" - - -namespace -{ - - -class EaxFxSlotsException : - public EaxException -{ -public: - explicit EaxFxSlotsException( - const char* message) - : - EaxException{"EAX_FX_SLOTS", message} - { - } -}; // EaxFxSlotsException - - -} // namespace - - -void EaxFxSlots::initialize( - ALCcontext& al_context) -{ - initialize_fx_slots(al_context); -} - -void EaxFxSlots::uninitialize() noexcept -{ - for (auto& fx_slot : fx_slots_) - { - fx_slot = nullptr; - } -} - -const ALeffectslot& EaxFxSlots::get(EaxFxSlotIndex index) const -{ - if(!index.has_value()) - fail("Empty index."); - return *fx_slots_[index.value()]; -} - -ALeffectslot& EaxFxSlots::get(EaxFxSlotIndex index) -{ - if(!index.has_value()) - fail("Empty index."); - return *fx_slots_[index.value()]; -} - -void EaxFxSlots::unlock_legacy() noexcept -{ - fx_slots_[0]->eax_unlock_legacy(); - fx_slots_[1]->eax_unlock_legacy(); -} - -[[noreturn]] -void EaxFxSlots::fail( - const char* message) -{ - throw EaxFxSlotsException{message}; -} - -void EaxFxSlots::initialize_fx_slots( - ALCcontext& al_context) -{ - auto fx_slot_index = EaxFxSlotIndexValue{}; - - for (auto& fx_slot : fx_slots_) - { - fx_slot = eax_create_al_effect_slot(al_context); - fx_slot->eax_initialize(al_context, fx_slot_index); - fx_slot_index += 1; - } -} diff --git a/al/eax_fx_slots.h b/al/eax_fx_slots.h deleted file mode 100644 index a104c6ab..00000000 --- a/al/eax_fx_slots.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef EAX_FX_SLOTS_INCLUDED -#define EAX_FX_SLOTS_INCLUDED - - -#include - -#include "al/auxeffectslot.h" - -#include "eax_api.h" - -#include "eax_fx_slot_index.h" - - -class EaxFxSlots -{ -public: - void initialize( - ALCcontext& al_context); - - void uninitialize() noexcept; - - void commit() - { - for(auto& fx_slot : fx_slots_) - fx_slot->eax_commit(); - } - - - const ALeffectslot& get( - EaxFxSlotIndex index) const; - - ALeffectslot& get( - EaxFxSlotIndex index); - - void unlock_legacy() noexcept; - - -private: - using Items = std::array; - - - Items fx_slots_{}; - - - [[noreturn]] - static void fail( - const char* message); - - void initialize_fx_slots( - ALCcontext& al_context); -}; // EaxFxSlots - - -#endif // !EAX_FX_SLOTS_INCLUDED diff --git a/al/eax_globals.cpp b/al/eax_globals.cpp deleted file mode 100644 index 07909459..00000000 --- a/al/eax_globals.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "config.h" - -#include "eax_globals.h" - - -bool eax_g_is_enabled = true; - - -const char eax1_ext_name[] = "EAX"; -const char eax2_ext_name[] = "EAX2.0"; -const char eax3_ext_name[] = "EAX3.0"; -const char eax4_ext_name[] = "EAX4.0"; -const char eax5_ext_name[] = "EAX5.0"; - -const char eax_x_ram_ext_name[] = "EAX-RAM"; - -const char eax_eax_set_func_name[] = "EAXSet"; -const char eax_eax_get_func_name[] = "EAXGet"; - -const char eax_eax_set_buffer_mode_func_name[] = "EAXSetBufferMode"; -const char eax_eax_get_buffer_mode_func_name[] = "EAXGetBufferMode"; diff --git a/al/eax_globals.h b/al/eax_globals.h deleted file mode 100644 index 1b4d63b8..00000000 --- a/al/eax_globals.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef EAX_GLOBALS_INCLUDED -#define EAX_GLOBALS_INCLUDED - - -extern bool eax_g_is_enabled; - - -extern const char eax1_ext_name[]; -extern const char eax2_ext_name[]; -extern const char eax3_ext_name[]; -extern const char eax4_ext_name[]; -extern const char eax5_ext_name[]; - -extern const char eax_x_ram_ext_name[]; - -extern const char eax_eax_set_func_name[]; -extern const char eax_eax_get_func_name[]; - -extern const char eax_eax_set_buffer_mode_func_name[]; -extern const char eax_eax_get_buffer_mode_func_name[]; - -#endif // !EAX_GLOBALS_INCLUDED diff --git a/al/eax_utils.cpp b/al/eax_utils.cpp deleted file mode 100644 index 67389de4..00000000 --- a/al/eax_utils.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "config.h" - -#include "eax_utils.h" - -#include -#include - -#include "core/logging.h" - - -void eax_log_exception( - const char* message) noexcept -{ - const auto exception_ptr = std::current_exception(); - - assert(exception_ptr); - - if (message) - { - ERR("%s\n", message); - } - - try - { - std::rethrow_exception(exception_ptr); - } - catch (const std::exception& ex) - { - const auto ex_message = ex.what(); - ERR("%s\n", ex_message); - } - catch (...) - { - ERR("%s\n", "Generic exception."); - } -} diff --git a/al/eax_utils.h b/al/eax_utils.h deleted file mode 100644 index d3d4a196..00000000 --- a/al/eax_utils.h +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef EAX_UTILS_INCLUDED -#define EAX_UTILS_INCLUDED - -#include -#include -#include -#include - - -struct EaxAlLowPassParam -{ - float gain; - float gain_hf; -}; // EaxAlLowPassParam - - -void eax_log_exception( - const char* message = nullptr) noexcept; - - -template< - typename TException, - typename TValue -> -void eax_validate_range( - const char* value_name, - const TValue& value, - const TValue& min_value, - const TValue& max_value) -{ - if (value >= min_value && value <= max_value) - { - return; - } - - const auto message = - std::string{value_name} + - " out of range (value: " + - std::to_string(value) + "; min: " + - std::to_string(min_value) + "; max: " + - std::to_string(max_value) + ")."; - - throw TException{message.c_str()}; -} - - -namespace detail -{ - - -template< - typename T -> -struct EaxIsBitFieldStruct -{ -private: - using yes = std::true_type; - using no = std::false_type; - - template< - typename U - > - static auto test(int) -> decltype(std::declval(), yes{}); - - template< - typename - > - static no test(...); - - -public: - static constexpr auto value = std::is_same(0)), yes>::value; -}; // EaxIsBitFieldStruct - - -template< - typename T, - typename TValue -> -inline bool eax_bit_fields_are_equal( - const T& lhs, - const T& rhs) noexcept -{ - static_assert(sizeof(T) == sizeof(TValue), "Invalid type size."); - - return reinterpret_cast(lhs) == reinterpret_cast(rhs); -} - - -} // namespace detail - - -template< - typename T, - std::enable_if_t::value, int> = 0 -> -inline bool operator==( - const T& lhs, - const T& rhs) noexcept -{ - using Value = std::conditional_t< - sizeof(T) == 1, - std::uint8_t, - std::conditional_t< - sizeof(T) == 2, - std::uint16_t, - std::conditional_t< - sizeof(T) == 4, - std::uint32_t, - void - > - > - >; - - static_assert(!std::is_same::value, "Unsupported type."); - - return detail::eax_bit_fields_are_equal(lhs, rhs); -} - -template< - typename T, - std::enable_if_t::value, int> = 0 -> -inline bool operator!=( - const T& lhs, - const T& rhs) noexcept -{ - return !(lhs == rhs); -} - - -#endif // !EAX_UTILS_INCLUDED diff --git a/al/eax_x_ram.cpp b/al/eax_x_ram.cpp deleted file mode 100644 index ac3e7ebb..00000000 --- a/al/eax_x_ram.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "config.h" - -#include "eax_x_ram.h" diff --git a/al/eax_x_ram.h b/al/eax_x_ram.h deleted file mode 100644 index 438b9916..00000000 --- a/al/eax_x_ram.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef EAX_X_RAM_INCLUDED -#define EAX_X_RAM_INCLUDED - - -#include "AL/al.h" - - -constexpr auto eax_x_ram_min_size = ALsizei{}; -constexpr auto eax_x_ram_max_size = ALsizei{64 * 1'024 * 1'024}; - - -constexpr auto AL_EAX_RAM_SIZE = ALenum{0x202201}; -constexpr auto AL_EAX_RAM_FREE = ALenum{0x202202}; - -constexpr auto AL_STORAGE_AUTOMATIC = ALenum{0x202203}; -constexpr auto AL_STORAGE_HARDWARE = ALenum{0x202204}; -constexpr auto AL_STORAGE_ACCESSIBLE = ALenum{0x202205}; - - -constexpr auto AL_EAX_RAM_SIZE_NAME = "AL_EAX_RAM_SIZE"; -constexpr auto AL_EAX_RAM_FREE_NAME = "AL_EAX_RAM_FREE"; - -constexpr auto AL_STORAGE_AUTOMATIC_NAME = "AL_STORAGE_AUTOMATIC"; -constexpr auto AL_STORAGE_HARDWARE_NAME = "AL_STORAGE_HARDWARE"; -constexpr auto AL_STORAGE_ACCESSIBLE_NAME = "AL_STORAGE_ACCESSIBLE"; - - -ALboolean AL_APIENTRY EAXSetBufferMode( - ALsizei n, - const ALuint* buffers, - ALint value); - -ALenum AL_APIENTRY EAXGetBufferMode( - ALuint buffer, - ALint* pReserved); - - -#endif // !EAX_X_RAM_INCLUDED diff --git a/al/effect.cpp b/al/effect.cpp index 5a74ca53..90a4dfde 100644 --- a/al/effect.cpp +++ b/al/effect.cpp @@ -54,7 +54,7 @@ #ifdef ALSOFT_EAX #include -#include "eax_exception.h" +#include "eax/exception.h" #endif // ALSOFT_EAX const EffectList gEffectList[16]{ diff --git a/al/effects/autowah.cpp b/al/effects/autowah.cpp index 273ec7ae..23f43305 100644 --- a/al/effects/autowah.cpp +++ b/al/effects/autowah.cpp @@ -14,8 +14,8 @@ #ifdef ALSOFT_EAX #include "alnumeric.h" -#include "al/eax_exception.h" -#include "al/eax_utils.h" +#include "al/eax/exception.h" +#include "al/eax/utils.h" #endif // ALSOFT_EAX diff --git a/al/effects/chorus.cpp b/al/effects/chorus.cpp index 56318095..b612a6c1 100644 --- a/al/effects/chorus.cpp +++ b/al/effects/chorus.cpp @@ -16,8 +16,8 @@ #include "alnumeric.h" -#include "al/eax_exception.h" -#include "al/eax_utils.h" +#include "al/eax/exception.h" +#include "al/eax/utils.h" #endif // ALSOFT_EAX diff --git a/al/effects/compressor.cpp b/al/effects/compressor.cpp index bb5dfa3e..a18609ca 100644 --- a/al/effects/compressor.cpp +++ b/al/effects/compressor.cpp @@ -10,8 +10,8 @@ #ifdef ALSOFT_EAX #include "alnumeric.h" -#include "al/eax_exception.h" -#include "al/eax_utils.h" +#include "al/eax/exception.h" +#include "al/eax/utils.h" #endif // ALSOFT_EAX diff --git a/al/effects/distortion.cpp b/al/effects/distortion.cpp index 13b1f23d..441b89a0 100644 --- a/al/effects/distortion.cpp +++ b/al/effects/distortion.cpp @@ -10,8 +10,8 @@ #ifdef ALSOFT_EAX #include "alnumeric.h" -#include "al/eax_exception.h" -#include "al/eax_utils.h" +#include "al/eax/exception.h" +#include "al/eax/utils.h" #endif // ALSOFT_EAX diff --git a/al/effects/echo.cpp b/al/effects/echo.cpp index 61adad7f..56849b9d 100644 --- a/al/effects/echo.cpp +++ b/al/effects/echo.cpp @@ -10,8 +10,8 @@ #ifdef ALSOFT_EAX #include "alnumeric.h" -#include "al/eax_exception.h" -#include "al/eax_utils.h" +#include "al/eax/exception.h" +#include "al/eax/utils.h" #endif // ALSOFT_EAX diff --git a/al/effects/effects.h b/al/effects/effects.h index 830e7191..f8b97eeb 100644 --- a/al/effects/effects.h +++ b/al/effects/effects.h @@ -6,7 +6,7 @@ #include "core/except.h" #ifdef ALSOFT_EAX -#include "al/eax_effect.h" +#include "al/eax/effect.h" #endif // ALSOFT_EAX union EffectProps; diff --git a/al/effects/equalizer.cpp b/al/effects/equalizer.cpp index f829328c..ccc00f67 100644 --- a/al/effects/equalizer.cpp +++ b/al/effects/equalizer.cpp @@ -10,8 +10,8 @@ #ifdef ALSOFT_EAX #include "alnumeric.h" -#include "al/eax_exception.h" -#include "al/eax_utils.h" +#include "al/eax/exception.h" +#include "al/eax/utils.h" #endif // ALSOFT_EAX diff --git a/al/effects/fshifter.cpp b/al/effects/fshifter.cpp index d334890b..1ca97ecb 100644 --- a/al/effects/fshifter.cpp +++ b/al/effects/fshifter.cpp @@ -15,8 +15,8 @@ #include "alnumeric.h" -#include "al/eax_exception.h" -#include "al/eax_utils.h" +#include "al/eax/exception.h" +#include "al/eax/utils.h" #endif // ALSOFT_EAX diff --git a/al/effects/modulator.cpp b/al/effects/modulator.cpp index 800b892d..082597cd 100644 --- a/al/effects/modulator.cpp +++ b/al/effects/modulator.cpp @@ -15,8 +15,8 @@ #include "alnumeric.h" -#include "al/eax_exception.h" -#include "al/eax_utils.h" +#include "al/eax/exception.h" +#include "al/eax/utils.h" #endif // ALSOFT_EAX diff --git a/al/effects/null.cpp b/al/effects/null.cpp index a0eb2247..0ec387d4 100644 --- a/al/effects/null.cpp +++ b/al/effects/null.cpp @@ -8,7 +8,7 @@ #include "effects.h" #ifdef ALSOFT_EAX -#include "al/eax_exception.h" +#include "al/eax/exception.h" #endif // ALSOFT_EAX diff --git a/al/effects/pshifter.cpp b/al/effects/pshifter.cpp index 1b2dcff0..cb81d831 100644 --- a/al/effects/pshifter.cpp +++ b/al/effects/pshifter.cpp @@ -10,8 +10,8 @@ #ifdef ALSOFT_EAX #include "alnumeric.h" -#include "al/eax_exception.h" -#include "al/eax_utils.h" +#include "al/eax/exception.h" +#include "al/eax/utils.h" #endif // ALSOFT_EAX diff --git a/al/effects/reverb.cpp b/al/effects/reverb.cpp index 197ea500..4184eda0 100644 --- a/al/effects/reverb.cpp +++ b/al/effects/reverb.cpp @@ -11,10 +11,13 @@ #ifdef ALSOFT_EAX #include -#include "alnumeric.h" + #include "AL/efx-presets.h" -#include "al/eax_exception.h" -#include "al/eax_utils.h" + +#include "alnumeric.h" + +#include "al/eax/exception.h" +#include "al/eax/utils.h" #endif // ALSOFT_EAX diff --git a/al/effects/vmorpher.cpp b/al/effects/vmorpher.cpp index 8c0b3adb..da513015 100644 --- a/al/effects/vmorpher.cpp +++ b/al/effects/vmorpher.cpp @@ -15,8 +15,8 @@ #include "alnumeric.h" -#include "al/eax_exception.h" -#include "al/eax_utils.h" +#include "al/eax/exception.h" +#include "al/eax/utils.h" #endif // ALSOFT_EAX diff --git a/al/source.cpp b/al/source.cpp index d2ccfe08..604d4566 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -73,7 +73,7 @@ #include "threads.h" #ifdef ALSOFT_EAX -#include "eax_exception.h" +#include "eax/exception.h" #endif // ALSOFT_EAX namespace { diff --git a/al/source.h b/al/source.h index 6db6bfa7..6fc1c1d4 100644 --- a/al/source.h +++ b/al/source.h @@ -22,9 +22,9 @@ #include "vector.h" #ifdef ALSOFT_EAX -#include "eax_eax_call.h" -#include "eax_fx_slot_index.h" -#include "eax_utils.h" +#include "eax/eax_call.h" +#include "eax/fx_slot_index.h" +#include "eax/utils.h" #endif // ALSOFT_EAX struct ALbuffer; diff --git a/al/state.cpp b/al/state.cpp index 1a07f624..f41016d6 100644 --- a/al/state.cpp +++ b/al/state.cpp @@ -49,8 +49,8 @@ #ifdef ALSOFT_EAX #include "alc/device.h" -#include "eax_globals.h" -#include "eax_x_ram.h" +#include "eax/globals.h" +#include "eax/x_ram.h" #endif // ALSOFT_EAX diff --git a/alc/alc.cpp b/alc/alc.cpp index 686b794e..3fa0d353 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -156,8 +156,8 @@ #endif #ifdef ALSOFT_EAX -#include "al/eax_globals.h" -#include "al/eax_x_ram.h" +#include "al/eax/globals.h" +#include "al/eax/x_ram.h" #endif // ALSOFT_EAX diff --git a/alc/context.cpp b/alc/context.cpp index 34da3784..5fe03e78 100644 --- a/alc/context.cpp +++ b/alc/context.cpp @@ -34,8 +34,8 @@ #include #include "alstring.h" -#include "al/eax_exception.h" -#include "al/eax_globals.h" +#include "al/eax/exception.h" +#include "al/eax/globals.h" #endif // ALSOFT_EAX namespace { diff --git a/alc/context.h b/alc/context.h index 72b259e9..b3f1ea09 100644 --- a/alc/context.h +++ b/alc/context.h @@ -20,10 +20,10 @@ #include "vector.h" #ifdef ALSOFT_EAX -#include "al/eax_eax_call.h" -#include "al/eax_fx_slot_index.h" -#include "al/eax_fx_slots.h" -#include "al/eax_utils.h" +#include "al/eax/eax_call.h" +#include "al/eax/fx_slot_index.h" +#include "al/eax/fx_slots.h" +#include "al/eax/utils.h" using EaxContextSharedDirtyFlagsValue = std::uint_least8_t; diff --git a/alc/device.h b/alc/device.h index 434d4d8f..ef50f53e 100644 --- a/alc/device.h +++ b/alc/device.h @@ -20,7 +20,7 @@ #include "vector.h" #ifdef ALSOFT_EAX -#include "al/eax_x_ram.h" +#include "al/eax/x_ram.h" #endif // ALSOFT_EAX struct ALbuffer; -- cgit v1.2.3 From 074dbef2e3129e9f7ee4895c44721c2a0a4b045b Mon Sep 17 00:00:00 2001 From: "Boris I. Bendovsky" Date: Tue, 24 May 2022 12:19:14 +0300 Subject: [EAX] Add separate effect state for each version (#705) * [EAX] Add separate effect state for each version * [EAX] Don't use EAX call as data member --- CMakeLists.txt | 6 +- al/auxeffectslot.cpp | 194 ++-- al/auxeffectslot.h | 99 +- al/eax/api.cpp | 621 ++++++++++- al/eax/api.h | 114 +- al/eax/call.cpp | 212 ++++ al/eax/call.h | 92 ++ al/eax/eax_call.cpp | 323 ------ al/eax/eax_call.h | 117 -- al/eax/effect.cpp | 3 - al/eax/effect.h | 165 ++- al/eax/fx_slots.cpp | 6 +- al/eax/fx_slots.h | 3 + al/eax/x_ram.cpp | 3 - al/effects/autowah.cpp | 482 +++----- al/effects/chorus.cpp | 1399 +++++++---------------- al/effects/compressor.cpp | 230 ++-- al/effects/distortion.cpp | 510 +++------ al/effects/echo.cpp | 511 +++------ al/effects/effects.cpp | 26 +- al/effects/effects.h | 3 +- al/effects/equalizer.cpp | 862 +++++---------- al/effects/fshifter.cpp | 359 ++---- al/effects/modulator.cpp | 365 ++---- al/effects/null.cpp | 36 +- al/effects/pshifter.cpp | 301 ++--- al/effects/reverb.cpp | 2696 ++++++++++++++++++--------------------------- al/effects/vmorpher.cpp | 552 +++------- al/source.cpp | 363 +++--- al/source.h | 103 +- alc/context.cpp | 214 ++-- alc/context.h | 68 +- 32 files changed, 4299 insertions(+), 6739 deletions(-) create mode 100644 al/eax/call.cpp create mode 100644 al/eax/call.h delete mode 100644 al/eax/eax_call.cpp delete mode 100644 al/eax/eax_call.h delete mode 100644 al/eax/effect.cpp delete mode 100644 al/eax/x_ram.cpp (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 52d61ff0..4a3ce192 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -800,9 +800,8 @@ if (ALSOFT_EAX) ${OPENAL_OBJS} al/eax/api.cpp al/eax/api.h - al/eax/eax_call.cpp - al/eax/eax_call.h - al/eax/effect.cpp + al/eax/call.cpp + al/eax/call.h al/eax/effect.h al/eax/exception.cpp al/eax/exception.h @@ -814,7 +813,6 @@ if (ALSOFT_EAX) al/eax/globals.h al/eax/utils.cpp al/eax/utils.h - al/eax/x_ram.cpp al/eax/x_ram.h ) endif () diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp index 455a1072..d37168e7 100644 --- a/al/auxeffectslot.cpp +++ b/al/auxeffectslot.cpp @@ -1076,6 +1076,7 @@ public: void ALeffectslot::eax_initialize( + const EaxCall& call, ALCcontext& al_context, EaxFxSlotIndexValue index) { @@ -1090,7 +1091,7 @@ void ALeffectslot::eax_initialize( eax_initialize_eax(); eax_initialize_lock(); - eax_initialize_effects(); + eax_initialize_effects(call); } const EAX50FXSLOTPROPERTIES& ALeffectslot::eax_get_eax_fx_slot() const noexcept @@ -1104,8 +1105,7 @@ void ALeffectslot::eax_ensure_is_unlocked() const eax_fail("Locked."); } -void ALeffectslot::eax_validate_fx_slot_effect( - const GUID& eax_effect_id) +void ALeffectslot::eax_validate_fx_slot_effect(const GUID& eax_effect_id) { eax_ensure_is_unlocked(); @@ -1127,8 +1127,7 @@ void ALeffectslot::eax_validate_fx_slot_effect( } } -void ALeffectslot::eax_validate_fx_slot_volume( - long eax_volume) +void ALeffectslot::eax_validate_fx_slot_volume(long eax_volume) { eax_validate_range( "Volume", @@ -1137,8 +1136,7 @@ void ALeffectslot::eax_validate_fx_slot_volume( EAXFXSLOT_MAXVOLUME); } -void ALeffectslot::eax_validate_fx_slot_lock( - long eax_lock) +void ALeffectslot::eax_validate_fx_slot_lock(long eax_lock) { eax_ensure_is_unlocked(); @@ -1149,19 +1147,16 @@ void ALeffectslot::eax_validate_fx_slot_lock( EAXFXSLOT_MAXLOCK); } -void ALeffectslot::eax_validate_fx_slot_flags( - unsigned long eax_flags, - int eax_version) +void ALeffectslot::eax_validate_fx_slot_flags(const EaxCall& call, unsigned long eax_flags) { eax_validate_range( "Flags", eax_flags, 0UL, - ~(eax_version == 4 ? EAX40FXSLOTFLAGS_RESERVED : EAX50FXSLOTFLAGS_RESERVED)); + ~(call.get_version() == 4 ? EAX40FXSLOTFLAGS_RESERVED : EAX50FXSLOTFLAGS_RESERVED)); } -void ALeffectslot::eax_validate_fx_slot_occlusion( - long eax_occlusion) +void ALeffectslot::eax_validate_fx_slot_occlusion(long eax_occlusion) { eax_validate_range( "Occlusion", @@ -1170,8 +1165,7 @@ void ALeffectslot::eax_validate_fx_slot_occlusion( EAXFXSLOT_MAXOCCLUSION); } -void ALeffectslot::eax_validate_fx_slot_occlusion_lf_ratio( - float eax_occlusion_lf_ratio) +void ALeffectslot::eax_validate_fx_slot_occlusion_lf_ratio(float eax_occlusion_lf_ratio) { eax_validate_range( "Occlusion LF Ratio", @@ -1180,28 +1174,22 @@ void ALeffectslot::eax_validate_fx_slot_occlusion_lf_ratio( EAXFXSLOT_MAXOCCLUSIONLFRATIO); } -void ALeffectslot::eax_validate_fx_slot_all( - const EAX40FXSLOTPROPERTIES& fx_slot, - int eax_version) +void ALeffectslot::eax_validate_fx_slot_all(const EaxCall& call, const EAX40FXSLOTPROPERTIES& fx_slot) { eax_validate_fx_slot_effect(fx_slot.guidLoadEffect); eax_validate_fx_slot_volume(fx_slot.lVolume); eax_validate_fx_slot_lock(fx_slot.lLock); - eax_validate_fx_slot_flags(fx_slot.ulFlags, eax_version); + eax_validate_fx_slot_flags(call, fx_slot.ulFlags); } -void ALeffectslot::eax_validate_fx_slot_all( - const EAX50FXSLOTPROPERTIES& fx_slot, - int eax_version) +void ALeffectslot::eax_validate_fx_slot_all(const EaxCall& call, const EAX50FXSLOTPROPERTIES& fx_slot) { - eax_validate_fx_slot_all(static_cast(fx_slot), eax_version); - + eax_validate_fx_slot_all(call, static_cast(fx_slot)); eax_validate_fx_slot_occlusion(fx_slot.lOcclusion); eax_validate_fx_slot_occlusion_lf_ratio(fx_slot.flOcclusionLFRatio); } -void ALeffectslot::eax_set_fx_slot_effect( - const GUID& eax_effect_id) +void ALeffectslot::eax_set_fx_slot_effect(const EaxCall& call, const GUID& eax_effect_id) { if (eax_eax_fx_slot_.guidLoadEffect == eax_effect_id) { @@ -1210,7 +1198,7 @@ void ALeffectslot::eax_set_fx_slot_effect( eax_eax_fx_slot_.guidLoadEffect = eax_effect_id; - eax_set_fx_slot_effect(); + eax_set_fx_slot_effect(call); } void ALeffectslot::eax_set_fx_slot_volume( @@ -1278,20 +1266,18 @@ bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio( return true; } -void ALeffectslot::eax_set_fx_slot_all( - const EAX40FXSLOTPROPERTIES& eax_fx_slot) +void ALeffectslot::eax_set_fx_slot_all(const EaxCall& call, const EAX40FXSLOTPROPERTIES& eax_fx_slot) { - eax_set_fx_slot_effect(eax_fx_slot.guidLoadEffect); + eax_set_fx_slot_effect(call, eax_fx_slot.guidLoadEffect); eax_set_fx_slot_volume(eax_fx_slot.lVolume); eax_set_fx_slot_lock(eax_fx_slot.lLock); eax_set_fx_slot_flags(eax_fx_slot.ulFlags); } // [[nodiscard]] -bool ALeffectslot::eax_set_fx_slot_all( - const EAX50FXSLOTPROPERTIES& eax_fx_slot) +bool ALeffectslot::eax_set_fx_slot_all(const EaxCall& call, const EAX50FXSLOTPROPERTIES& eax_fx_slot) { - eax_set_fx_slot_all(static_cast(eax_fx_slot)); + eax_set_fx_slot_all(call, static_cast(eax_fx_slot)); const auto is_occlusion_modified = eax_set_fx_slot_occlusion(eax_fx_slot.lOcclusion); const auto is_occlusion_lf_ratio_modified = eax_set_fx_slot_occlusion_lf_ratio(eax_fx_slot.flOcclusionLFRatio); @@ -1348,22 +1334,21 @@ void ALeffectslot::eax_initialize_lock() eax_is_locked_ = (eax_fx_slot_index_ < 2); } -void ALeffectslot::eax_initialize_effects() +void ALeffectslot::eax_initialize_effects(const EaxCall& call) { - eax_set_fx_slot_effect(); + eax_set_fx_slot_effect(call); } -void ALeffectslot::eax_get_fx_slot_all( - const EaxEaxCall& eax_call) const +void ALeffectslot::eax_get_fx_slot_all(const EaxCall& call) const { - switch (eax_call.get_version()) + switch (call.get_version()) { case 4: - eax_call.set_value(eax_eax_fx_slot_); + call.set_value(eax_eax_fx_slot_); break; case 5: - eax_call.set_value(eax_eax_fx_slot_); + call.set_value(eax_eax_fx_slot_); break; default: @@ -1371,37 +1356,36 @@ void ALeffectslot::eax_get_fx_slot_all( } } -void ALeffectslot::eax_get_fx_slot( - const EaxEaxCall& eax_call) const +void ALeffectslot::eax_get_fx_slot(const EaxCall& call) const { - switch (eax_call.get_property_id()) + switch (call.get_property_id()) { case EAXFXSLOT_ALLPARAMETERS: - eax_get_fx_slot_all(eax_call); + eax_get_fx_slot_all(call); break; case EAXFXSLOT_LOADEFFECT: - eax_call.set_value(eax_eax_fx_slot_.guidLoadEffect); + call.set_value(eax_eax_fx_slot_.guidLoadEffect); break; case EAXFXSLOT_VOLUME: - eax_call.set_value(eax_eax_fx_slot_.lVolume); + call.set_value(eax_eax_fx_slot_.lVolume); break; case EAXFXSLOT_LOCK: - eax_call.set_value(eax_eax_fx_slot_.lLock); + call.set_value(eax_eax_fx_slot_.lLock); break; case EAXFXSLOT_FLAGS: - eax_call.set_value(eax_eax_fx_slot_.ulFlags); + call.set_value(eax_eax_fx_slot_.ulFlags); break; case EAXFXSLOT_OCCLUSION: - eax_call.set_value(eax_eax_fx_slot_.lOcclusion); + call.set_value(eax_eax_fx_slot_.lOcclusion); break; case EAXFXSLOT_OCCLUSIONLFRATIO: - eax_call.set_value(eax_eax_fx_slot_.flOcclusionLFRatio); + call.set_value(eax_eax_fx_slot_.flOcclusionLFRatio); break; default: @@ -1410,17 +1394,16 @@ void ALeffectslot::eax_get_fx_slot( } // [[nodiscard]] -bool ALeffectslot::eax_get( - const EaxEaxCall& eax_call) +bool ALeffectslot::eax_get(const EaxCall& call) { - switch (eax_call.get_property_set_id()) + switch (call.get_property_set_id()) { - case EaxEaxCallPropertySetId::fx_slot: - eax_get_fx_slot(eax_call); + case EaxCallPropertySetId::fx_slot: + eax_get_fx_slot(call); break; - case EaxEaxCallPropertySetId::fx_slot_effect: - eax_dispatch_effect(eax_call); + case EaxCallPropertySetId::fx_slot_effect: + eax_dispatch_effect(call); break; default: @@ -1430,19 +1413,18 @@ bool ALeffectslot::eax_get( return false; } -void ALeffectslot::eax_set_fx_slot_effect( - ALenum al_effect_type) +void ALeffectslot::eax_set_fx_slot_effect(const EaxCall& call, ALenum al_effect_type) { if(!IsValidEffectType(al_effect_type)) eax_fail("Unsupported effect."); eax_effect_ = nullptr; - eax_effect_ = eax_create_eax_effect(al_effect_type); + eax_effect_ = eax_create_eax_effect(al_effect_type, call); eax_set_effect_slot_effect(*eax_effect_); } -void ALeffectslot::eax_set_fx_slot_effect() +void ALeffectslot::eax_set_fx_slot_effect(const EaxCall& call) { auto al_effect_type = ALenum{}; @@ -1506,7 +1488,7 @@ void ALeffectslot::eax_set_fx_slot_effect() eax_fail("Unsupported effect."); } - eax_set_fx_slot_effect(al_effect_type); + eax_set_fx_slot_effect(call, al_effect_type); } void ALeffectslot::eax_set_efx_effect_slot_gain() @@ -1535,52 +1517,47 @@ void ALeffectslot::eax_set_fx_slot_flags() eax_set_effect_slot_send_auto(); } -void ALeffectslot::eax_set_fx_slot_effect( - const EaxEaxCall& eax_call) +void ALeffectslot::eax_defer_fx_slot_effect(const EaxCall& call) { const auto& eax_effect_id = - eax_call.get_value(); + call.get_value(); eax_validate_fx_slot_effect(eax_effect_id); - eax_set_fx_slot_effect(eax_effect_id); + eax_set_fx_slot_effect(call, eax_effect_id); } -void ALeffectslot::eax_set_fx_slot_volume( - const EaxEaxCall& eax_call) +void ALeffectslot::eax_defer_fx_slot_volume(const EaxCall& call) { const auto& eax_volume = - eax_call.get_value(); + call.get_value(); eax_validate_fx_slot_volume(eax_volume); eax_set_fx_slot_volume(eax_volume); } -void ALeffectslot::eax_set_fx_slot_lock( - const EaxEaxCall& eax_call) +void ALeffectslot::eax_defer_fx_slot_lock(const EaxCall& call) { const auto& eax_lock = - eax_call.get_value(); + call.get_value(); eax_validate_fx_slot_lock(eax_lock); eax_set_fx_slot_lock(eax_lock); } -void ALeffectslot::eax_set_fx_slot_flags( - const EaxEaxCall& eax_call) +void ALeffectslot::eax_defer_fx_slot_flags(const EaxCall& call) { const auto& eax_flags = - eax_call.get_value(); + call.get_value(); - eax_validate_fx_slot_flags(eax_flags, eax_call.get_version()); + eax_validate_fx_slot_flags(call, eax_flags); eax_set_fx_slot_flags(eax_flags); } // [[nodiscard]] -bool ALeffectslot::eax_set_fx_slot_occlusion( - const EaxEaxCall& eax_call) +bool ALeffectslot::eax_defer_fx_slot_occlusion(const EaxCall& call) { const auto& eax_occlusion = - eax_call.get_value(); + call.get_value(); eax_validate_fx_slot_occlusion(eax_occlusion); @@ -1588,11 +1565,10 @@ bool ALeffectslot::eax_set_fx_slot_occlusion( } // [[nodiscard]] -bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio( - const EaxEaxCall& eax_call) +bool ALeffectslot::eax_defer_fx_slot_occlusion_lf_ratio(const EaxCall& call) { const auto& eax_occlusion_lf_ratio = - eax_call.get_value(); + call.get_value(); eax_validate_fx_slot_occlusion_lf_ratio(eax_occlusion_lf_ratio); @@ -1600,18 +1576,17 @@ bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio( } // [[nodiscard]] -bool ALeffectslot::eax_set_fx_slot_all( - const EaxEaxCall& eax_call) +bool ALeffectslot::eax_defer_fx_slot_all(const EaxCall& call) { - switch (eax_call.get_version()) + switch (call.get_version()) { case 4: { const auto& eax_all = - eax_call.get_value(); + call.get_value(); - eax_validate_fx_slot_all(eax_all, eax_call.get_version()); - eax_set_fx_slot_all(eax_all); + eax_validate_fx_slot_all(call, eax_all); + eax_set_fx_slot_all(call, eax_all); return false; } @@ -1619,10 +1594,10 @@ bool ALeffectslot::eax_set_fx_slot_all( case 5: { const auto& eax_all = - eax_call.get_value(); + call.get_value(); - eax_validate_fx_slot_all(eax_all, eax_call.get_version()); - return eax_set_fx_slot_all(eax_all); + eax_validate_fx_slot_all(call, eax_all); + return eax_set_fx_slot_all(call, eax_all); } default: @@ -1630,38 +1605,37 @@ bool ALeffectslot::eax_set_fx_slot_all( } } -bool ALeffectslot::eax_set_fx_slot( - const EaxEaxCall& eax_call) +bool ALeffectslot::eax_set_fx_slot(const EaxCall& call) { - switch (eax_call.get_property_id()) + switch (call.get_property_id()) { case EAXFXSLOT_NONE: return false; case EAXFXSLOT_ALLPARAMETERS: - return eax_set_fx_slot_all(eax_call); + return eax_defer_fx_slot_all(call); case EAXFXSLOT_LOADEFFECT: - eax_set_fx_slot_effect(eax_call); + eax_defer_fx_slot_effect(call); return false; case EAXFXSLOT_VOLUME: - eax_set_fx_slot_volume(eax_call); + eax_defer_fx_slot_volume(call); return false; case EAXFXSLOT_LOCK: - eax_set_fx_slot_lock(eax_call); + eax_defer_fx_slot_lock(call); return false; case EAXFXSLOT_FLAGS: - eax_set_fx_slot_flags(eax_call); + eax_defer_fx_slot_flags(call); return false; case EAXFXSLOT_OCCLUSION: - return eax_set_fx_slot_occlusion(eax_call); + return eax_defer_fx_slot_occlusion(call); case EAXFXSLOT_OCCLUSIONLFRATIO: - return eax_set_fx_slot_occlusion_lf_ratio(eax_call); + return eax_defer_fx_slot_occlusion_lf_ratio(call); default: @@ -1670,15 +1644,15 @@ bool ALeffectslot::eax_set_fx_slot( } // [[nodiscard]] -bool ALeffectslot::eax_set(const EaxEaxCall& eax_call) +bool ALeffectslot::eax_set(const EaxCall& call) { - switch(eax_call.get_property_set_id()) + switch(call.get_property_set_id()) { - case EaxEaxCallPropertySetId::fx_slot: - return eax_set_fx_slot(eax_call); + case EaxCallPropertySetId::fx_slot: + return eax_set_fx_slot(call); - case EaxEaxCallPropertySetId::fx_slot_effect: - eax_dispatch_effect(eax_call); + case EaxCallPropertySetId::fx_slot_effect: + eax_dispatch_effect(call); break; default: @@ -1688,8 +1662,8 @@ bool ALeffectslot::eax_set(const EaxEaxCall& eax_call) return false; } -void ALeffectslot::eax_dispatch_effect(const EaxEaxCall& eax_call) -{ if(eax_effect_) eax_effect_->dispatch(eax_call); } +void ALeffectslot::eax_dispatch_effect(const EaxCall& call) +{ if(eax_effect_) eax_effect_->dispatch(call); } void ALeffectslot::eax_apply_deferred() { @@ -1697,7 +1671,7 @@ void ALeffectslot::eax_apply_deferred() auto is_changed = false; if(eax_effect_) - is_changed = eax_effect_->apply_deferred(); + is_changed = eax_effect_->commit(); if(is_changed) eax_set_effect_slot_effect(*eax_effect_); } diff --git a/al/auxeffectslot.h b/al/auxeffectslot.h index 5336a2a8..83711372 100644 --- a/al/auxeffectslot.h +++ b/al/auxeffectslot.h @@ -18,8 +18,7 @@ #ifdef ALSOFT_EAX #include - -#include "eax/eax_call.h" +#include "eax/call.h" #include "eax/effect.h" #include "eax/fx_slot_index.h" #endif // ALSOFT_EAX @@ -74,6 +73,7 @@ struct ALeffectslot { #ifdef ALSOFT_EAX public: void eax_initialize( + const EaxCall& call, ALCcontext& al_context, EaxFxSlotIndexValue index); @@ -81,8 +81,8 @@ public: // [[nodiscard]] - bool eax_dispatch(const EaxEaxCall& eax_call) - { return eax_call.is_get() ? eax_get(eax_call) : eax_set(eax_call); } + bool eax_dispatch(const EaxCall& call) + { return call.is_get() ? eax_get(call) : eax_set(call); } void eax_unlock_legacy() noexcept; @@ -115,24 +115,20 @@ private: void eax_initialize_lock(); - void eax_initialize_effects(); + void eax_initialize_effects(const EaxCall& call); - void eax_get_fx_slot_all( - const EaxEaxCall& eax_call) const; + void eax_get_fx_slot_all(const EaxCall& call) const; - void eax_get_fx_slot( - const EaxEaxCall& eax_call) const; + void eax_get_fx_slot(const EaxCall& call) const; // [[nodiscard]] - bool eax_get( - const EaxEaxCall& eax_call); + bool eax_get(const EaxCall& call); - void eax_set_fx_slot_effect( - ALenum effect_type); + void eax_set_fx_slot_effect(const EaxCall& call, ALenum effect_type); - void eax_set_fx_slot_effect(); + void eax_set_fx_slot_effect(const EaxCall& call); void eax_set_efx_effect_slot_gain(); @@ -147,37 +143,16 @@ private: void eax_ensure_is_unlocked() const; + void eax_validate_fx_slot_effect(const GUID& eax_effect_id); + void eax_validate_fx_slot_volume(long eax_volume); + void eax_validate_fx_slot_lock(long eax_lock); + void eax_validate_fx_slot_flags(const EaxCall& call, unsigned long eax_flags); + void eax_validate_fx_slot_occlusion(long eax_occlusion); + void eax_validate_fx_slot_occlusion_lf_ratio(float eax_occlusion_lf_ratio); + void eax_validate_fx_slot_all(const EaxCall& call, const EAX40FXSLOTPROPERTIES& fx_slot); + void eax_validate_fx_slot_all(const EaxCall& call, const EAX50FXSLOTPROPERTIES& fx_slot); - void eax_validate_fx_slot_effect( - const GUID& eax_effect_id); - - void eax_validate_fx_slot_volume( - long eax_volume); - - void eax_validate_fx_slot_lock( - long eax_lock); - - void eax_validate_fx_slot_flags( - unsigned long eax_flags, - int eax_version); - - void eax_validate_fx_slot_occlusion( - long eax_occlusion); - - void eax_validate_fx_slot_occlusion_lf_ratio( - float eax_occlusion_lf_ratio); - - void eax_validate_fx_slot_all( - const EAX40FXSLOTPROPERTIES& fx_slot, - int eax_version); - - void eax_validate_fx_slot_all( - const EAX50FXSLOTPROPERTIES& fx_slot, - int eax_version); - - - void eax_set_fx_slot_effect( - const GUID& eax_effect_id); + void eax_set_fx_slot_effect(const EaxCall& call, const GUID& eax_effect_id); void eax_set_fx_slot_volume( long eax_volume); @@ -196,50 +171,38 @@ private: bool eax_set_fx_slot_occlusion_lf_ratio( float eax_occlusion_lf_ratio); - void eax_set_fx_slot_all( - const EAX40FXSLOTPROPERTIES& eax_fx_slot); + void eax_set_fx_slot_all(const EaxCall& call, const EAX40FXSLOTPROPERTIES& eax_fx_slot); // [[nodiscard]] - bool eax_set_fx_slot_all( - const EAX50FXSLOTPROPERTIES& eax_fx_slot); + bool eax_set_fx_slot_all(const EaxCall& call, const EAX50FXSLOTPROPERTIES& eax_fx_slot); - void eax_set_fx_slot_effect( - const EaxEaxCall& eax_call); + void eax_defer_fx_slot_effect(const EaxCall& call); - void eax_set_fx_slot_volume( - const EaxEaxCall& eax_call); + void eax_defer_fx_slot_volume(const EaxCall& call); - void eax_set_fx_slot_lock( - const EaxEaxCall& eax_call); + void eax_defer_fx_slot_lock(const EaxCall& call); - void eax_set_fx_slot_flags( - const EaxEaxCall& eax_call); + void eax_defer_fx_slot_flags(const EaxCall& call); // [[nodiscard]] - bool eax_set_fx_slot_occlusion( - const EaxEaxCall& eax_call); + bool eax_defer_fx_slot_occlusion(const EaxCall& call); // [[nodiscard]] - bool eax_set_fx_slot_occlusion_lf_ratio( - const EaxEaxCall& eax_call); + bool eax_defer_fx_slot_occlusion_lf_ratio(const EaxCall& call); // [[nodiscard]] - bool eax_set_fx_slot_all( - const EaxEaxCall& eax_call); + bool eax_defer_fx_slot_all(const EaxCall& call); - bool eax_set_fx_slot( - const EaxEaxCall& eax_call); + bool eax_set_fx_slot(const EaxCall& call); void eax_apply_deferred(); // [[nodiscard]] - bool eax_set( - const EaxEaxCall& eax_call); + bool eax_set(const EaxCall& call); - void eax_dispatch_effect( - const EaxEaxCall& eax_call); + void eax_dispatch_effect(const EaxCall& call); // `alAuxiliaryEffectSloti(effect_slot, AL_EFFECTSLOT_EFFECT, effect)` diff --git a/al/eax/api.cpp b/al/eax/api.cpp index f859a1c4..34ba554f 100644 --- a/al/eax/api.cpp +++ b/al/eax/api.cpp @@ -393,6 +393,569 @@ bool operator!=( } +// EAX1 ===================================================================== + +namespace { +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_GENERIC = {EAX_ENVIRONMENT_GENERIC, 0.5F, 1.493F, 0.5F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PADDEDCELL = {EAX_ENVIRONMENT_PADDEDCELL, 0.25F, 0.1F, 0.0F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ROOM = {EAX_ENVIRONMENT_ROOM, 0.417F, 0.4F, 0.666F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_BATHROOM = {EAX_ENVIRONMENT_BATHROOM, 0.653F, 1.499F, 0.166F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_LIVINGROOM = {EAX_ENVIRONMENT_LIVINGROOM, 0.208F, 0.478F, 0.0F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_STONEROOM = {EAX_ENVIRONMENT_STONEROOM, 0.5F, 2.309F, 0.888F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_AUDITORIUM = {EAX_ENVIRONMENT_AUDITORIUM, 0.403F, 4.279F, 0.5F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CONCERTHALL = {EAX_ENVIRONMENT_CONCERTHALL, 0.5F, 3.961F, 0.5F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CAVE = {EAX_ENVIRONMENT_CAVE, 0.5F, 2.886F, 1.304F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ARENA = {EAX_ENVIRONMENT_ARENA, 0.361F, 7.284F, 0.332F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_HANGAR = {EAX_ENVIRONMENT_HANGAR, 0.5F, 10.0F, 0.3F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CARPETTEDHALLWAY = {EAX_ENVIRONMENT_CARPETEDHALLWAY, 0.153F, 0.259F, 2.0F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_HALLWAY = {EAX_ENVIRONMENT_HALLWAY, 0.361F, 1.493F, 0.0F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_STONECORRIDOR = {EAX_ENVIRONMENT_STONECORRIDOR, 0.444F, 2.697F, 0.638F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ALLEY = {EAX_ENVIRONMENT_ALLEY, 0.25F, 1.752F, 0.776F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_FOREST = {EAX_ENVIRONMENT_FOREST, 0.111F, 3.145F, 0.472F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CITY = {EAX_ENVIRONMENT_CITY, 0.111F, 2.767F, 0.224F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_MOUNTAINS = {EAX_ENVIRONMENT_MOUNTAINS, 0.194F, 7.841F, 0.472F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_QUARRY = {EAX_ENVIRONMENT_QUARRY, 1.0F, 1.499F, 0.5F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PLAIN = {EAX_ENVIRONMENT_PLAIN, 0.097F, 2.767F, 0.224F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PARKINGLOT = {EAX_ENVIRONMENT_PARKINGLOT, 0.208F, 1.652F, 1.5F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_SEWERPIPE = {EAX_ENVIRONMENT_SEWERPIPE, 0.652F, 2.886F, 0.25F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_UNDERWATER = {EAX_ENVIRONMENT_UNDERWATER, 1.0F, 1.499F, 0.0F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_DRUGGED = {EAX_ENVIRONMENT_DRUGGED, 0.875F, 8.392F, 1.388F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_DIZZY = {EAX_ENVIRONMENT_DIZZY, 0.139F, 17.234F, 0.666F}; +constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PSYCHOTIC = {EAX_ENVIRONMENT_PSYCHOTIC, 0.486F, 7.563F, 0.806F}; +} // namespace + +const Eax1ReverbPresets EAX1REVERB_PRESETS{{ + EAX1REVERB_PRESET_GENERIC, + EAX1REVERB_PRESET_PADDEDCELL, + EAX1REVERB_PRESET_ROOM, + EAX1REVERB_PRESET_BATHROOM, + EAX1REVERB_PRESET_LIVINGROOM, + EAX1REVERB_PRESET_STONEROOM, + EAX1REVERB_PRESET_AUDITORIUM, + EAX1REVERB_PRESET_CONCERTHALL, + EAX1REVERB_PRESET_CAVE, + EAX1REVERB_PRESET_ARENA, + EAX1REVERB_PRESET_HANGAR, + EAX1REVERB_PRESET_CARPETTEDHALLWAY, + EAX1REVERB_PRESET_HALLWAY, + EAX1REVERB_PRESET_STONECORRIDOR, + EAX1REVERB_PRESET_ALLEY, + EAX1REVERB_PRESET_FOREST, + EAX1REVERB_PRESET_CITY, + EAX1REVERB_PRESET_MOUNTAINS, + EAX1REVERB_PRESET_QUARRY, + EAX1REVERB_PRESET_PLAIN, + EAX1REVERB_PRESET_PARKINGLOT, + EAX1REVERB_PRESET_SEWERPIPE, + EAX1REVERB_PRESET_UNDERWATER, + EAX1REVERB_PRESET_DRUGGED, + EAX1REVERB_PRESET_DIZZY, + EAX1REVERB_PRESET_PSYCHOTIC, +}}; + +// EAX2 ===================================================================== + +namespace { + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_GENERIC{ + EAX2LISTENER_DEFAULTROOM, + EAX2LISTENER_DEFAULTROOMHF, + EAX2LISTENER_DEFAULTROOMROLLOFFFACTOR, + EAX2LISTENER_DEFAULTDECAYTIME, + EAX2LISTENER_DEFAULTDECAYHFRATIO, + EAX2LISTENER_DEFAULTREFLECTIONS, + EAX2LISTENER_DEFAULTREFLECTIONSDELAY, + EAX2LISTENER_DEFAULTREVERB, + EAX2LISTENER_DEFAULTREVERBDELAY, + EAX2LISTENER_DEFAULTENVIRONMENT, + EAX2LISTENER_DEFAULTENVIRONMENTSIZE, + EAX2LISTENER_DEFAULTENVIRONMENTDIFFUSION, + EAX2LISTENER_DEFAULTAIRABSORPTIONHF, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_PADDEDCELL{ + -1'000L, + -6'000L, + 0.0F, + 0.17F, + 0.1F, + -1'204L, + 0.001F, + 207L, + 0.002F, + EAX2_ENVIRONMENT_PADDEDCELL, + 1.4F, + 1.0F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_ROOM{ + -1'000L, + -454L, + 0.0F, + 0.4F, + 0.83F, + -1'646L, + 0.002F, + 53L, + 0.003F, + EAX2_ENVIRONMENT_ROOM, + 1.9F, + 1.0F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_BATHROOM{ + -1'000L, + -1'200L, + 0.0F, + 1.49F, + 0.54F, + -370L, + 0.007F, + 1'030L, + 0.011F, + EAX2_ENVIRONMENT_BATHROOM, + 1.4F, + 1.0F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_LIVINGROOM{ + -1'000L, + -6'000L, + 0.0F, + 0.5F, + 0.1F, + -1'376L, + 0.003F, + -1'104L, + 0.004F, + EAX2_ENVIRONMENT_LIVINGROOM, + 2.5F, + 1.0F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_STONEROOM{ + -1'000L, + -300L, + 0.0F, + 2.31F, + 0.64F, + -711L, + 0.012F, + 83L, + 0.017F, + EAX2_ENVIRONMENT_STONEROOM, + 11.6F, + 1.0F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_AUDITORIUM{ + -1'000L, + -476L, + 0.0F, + 4.32F, + 0.59F, + -789L, + 0.02F, + -289L, + 0.03F, + EAX2_ENVIRONMENT_AUDITORIUM, + 21.6F, + 1.0F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_CONCERTHALL{ + -1'000L, + -500L, + 0.0F, + 3.92F, + 0.7F, + -1'230L, + 0.02F, + -2L, + 0.029F, + EAX2_ENVIRONMENT_CONCERTHALL, + 19.6F, + 1.0F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_CAVE{ + -1'000L, + 0L, + 0.0F, + 2.91F, + 1.3F, + -602L, + 0.015F, + -302L, + 0.022F, + EAX2_ENVIRONMENT_CAVE, + 14.6F, + 1.0F, + -5.0F, + EAX2LISTENERFLAGS_DECAYTIMESCALE | + EAX2LISTENERFLAGS_REFLECTIONSSCALE | + EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE | + EAX2LISTENERFLAGS_REVERBSCALE | + EAX2LISTENERFLAGS_REVERBDELAYSCALE, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_ARENA{ + -1'000L, + -698L, + 0.0F, + 7.24F, + 0.33F, + -1'166L, + 0.02F, + 16L, + 0.03F, + EAX2_ENVIRONMENT_ARENA, + 36.2F, + 1.0F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_HANGAR{ + -1'000L, + -1'000L, + 0.0F, + 10.05F, + 0.23F, + -602L, + 0.02F, + 198L, + 0.03F, + EAX2_ENVIRONMENT_HANGAR, + 50.3F, + 1.0F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_CARPETTEDHALLWAY{ + -1'000L, + -4'000L, + 0.0F, + 0.3F, + 0.1F, + -1'831L, + 0.002F, + -1'630L, + 0.03F, + EAX2_ENVIRONMENT_CARPETEDHALLWAY, + 1.9F, + 1.0F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_HALLWAY{ + -1'000L, + -300L, + 0.0F, + 1.49F, + 0.59F, + -1'219L, + 0.007F, + 441L, + 0.011F, + EAX2_ENVIRONMENT_HALLWAY, + 1.8F, + 1.0F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_STONECORRIDOR{ + -1'000L, + -237L, + 0.0F, + 2.7F, + 0.79F, + -1'214L, + 0.013F, + 395L, + 0.02F, + EAX2_ENVIRONMENT_STONECORRIDOR, + 13.5F, + 1.0F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_ALLEY{ + -1'000L, + -270L, + 0.0F, + 1.49F, + 0.86F, + -1'204L, + 0.007F, + -4L, + 0.011F, + EAX2_ENVIRONMENT_ALLEY, + 7.5F, + 0.3F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_FOREST{ + -1'000L, + -3'300L, + 0.0F, + 1.49F, + 0.54F, + -2'560L, + 0.162F, + -229L, + 0.088F, + EAX2_ENVIRONMENT_FOREST, + 38.0F, + 0.3F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_CITY{ + -1'000L, + -800L, + 0.0F, + 1.49F, + 0.67F, + -2'273L, + 0.007F, + -1'691L, + 0.011F, + EAX2_ENVIRONMENT_CITY, + 7.5F, + 0.5F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_MOUNTAINS{ + -1'000L, + -2'500L, + 0.0F, + 1.49F, + 0.21F, + -2'780L, + 0.3F, + -1'434L, + 0.1F, + EAX2_ENVIRONMENT_MOUNTAINS, + 100.0F, + 0.27F, + -5.0F, + EAX2LISTENERFLAGS_DECAYTIMESCALE | + EAX2LISTENERFLAGS_REFLECTIONSSCALE | + EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE | + EAX2LISTENERFLAGS_REVERBSCALE | + EAX2LISTENERFLAGS_REVERBDELAYSCALE, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_QUARRY{ + -1'000L, + -1'000L, + 0.0F, + 1.49F, + 0.83F, + -10'000L, + 0.061F, + 500L, + 0.025F, + EAX2_ENVIRONMENT_QUARRY, + 17.5F, + 1.0F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_PLAIN{ + -1'000L, + -2'000L, + 0.0F, + 1.49F, + 0.5F, + -2'466L, + 0.179F, + -1'926L, + 0.1F, + EAX2_ENVIRONMENT_PLAIN, + 42.5F, + 0.21F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_PARKINGLOT{ + -1'000L, + 0L, + 0.0F, + 1.65F, + 1.5F, + -1'363L, + 0.008F, + -1'153L, + 0.012F, + EAX2_ENVIRONMENT_PARKINGLOT, + 8.3F, + 1.0F, + -5.0F, + EAX2LISTENERFLAGS_DECAYTIMESCALE | + EAX2LISTENERFLAGS_REFLECTIONSSCALE | + EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE | + EAX2LISTENERFLAGS_REVERBSCALE | + EAX2LISTENERFLAGS_REVERBDELAYSCALE, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_SEWERPIPE{ + -1'000L, + -1'000L, + 0.0F, + 2.81F, + 0.14F, + 429L, + 0.014F, + 1'023L, + 0.021F, + EAX2_ENVIRONMENT_SEWERPIPE, + 1.7F, + 0.8F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_UNDERWATER{ + -1'000L, + -4'000L, + 0.0F, + 1.49F, + 0.1F, + -449L, + 0.007F, + 1'700L, + 0.011F, + EAX2_ENVIRONMENT_UNDERWATER, + 1.8F, + 1.0F, + -5.0F, + EAX2LISTENER_DEFAULTFLAGS, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_DRUGGED{ + -1'000L, + 0L, + 0.0F, + 8.39F, + 1.39F, + -115L, + 0.002F, + 985L, + 0.03F, + EAX2_ENVIRONMENT_DRUGGED, + 1.9F, + 0.5F, + -5.0F, + EAX2LISTENERFLAGS_DECAYTIMESCALE | + EAX2LISTENERFLAGS_REFLECTIONSSCALE | + EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE | + EAX2LISTENERFLAGS_REVERBSCALE | + EAX2LISTENERFLAGS_REVERBDELAYSCALE, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_DIZZY{ + -1'000L, + -400L, + 0.0F, + 17.23F, + 0.56F, + -1'713L, + 0.02F, + -613L, + 0.03F, + EAX2_ENVIRONMENT_DIZZY, + 1.8F, + 0.6F, + -5.0F, + EAX2LISTENERFLAGS_DECAYTIMESCALE | + EAX2LISTENERFLAGS_REFLECTIONSSCALE | + EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE | + EAX2LISTENERFLAGS_REVERBSCALE | + EAX2LISTENERFLAGS_REVERBDELAYSCALE, +}; + +constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_PSYCHOTIC{ + -1'000L, + -151L, + 0.0F, + 7.56F, + 0.91F, + -626L, + 0.02F, + 774L, + 0.03F, + EAX2_ENVIRONMENT_PSYCHOTIC, + 1.0F, + 0.5F, + -5.0F, + EAX2LISTENERFLAGS_DECAYTIMESCALE | + EAX2LISTENERFLAGS_REFLECTIONSSCALE | + EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE | + EAX2LISTENERFLAGS_REVERBSCALE | + EAX2LISTENERFLAGS_REVERBDELAYSCALE, +}; + +} // namespace + +const Eax2ReverbPresets EAX2REVERB_PRESETS{ + EAX2REVERB_PRESET_GENERIC, + EAX2REVERB_PRESET_PADDEDCELL, + EAX2REVERB_PRESET_ROOM, + EAX2REVERB_PRESET_BATHROOM, + EAX2REVERB_PRESET_LIVINGROOM, + EAX2REVERB_PRESET_STONEROOM, + EAX2REVERB_PRESET_AUDITORIUM, + EAX2REVERB_PRESET_CONCERTHALL, + EAX2REVERB_PRESET_CAVE, + EAX2REVERB_PRESET_ARENA, + EAX2REVERB_PRESET_HANGAR, + EAX2REVERB_PRESET_CARPETTEDHALLWAY, + EAX2REVERB_PRESET_HALLWAY, + EAX2REVERB_PRESET_STONECORRIDOR, + EAX2REVERB_PRESET_ALLEY, + EAX2REVERB_PRESET_FOREST, + EAX2REVERB_PRESET_CITY, + EAX2REVERB_PRESET_MOUNTAINS, + EAX2REVERB_PRESET_QUARRY, + EAX2REVERB_PRESET_PLAIN, + EAX2REVERB_PRESET_PARKINGLOT, + EAX2REVERB_PRESET_SEWERPIPE, + EAX2REVERB_PRESET_UNDERWATER, + EAX2REVERB_PRESET_DRUGGED, + EAX2REVERB_PRESET_DIZZY, + EAX2REVERB_PRESET_PSYCHOTIC, +}; + +// EAX3+ ==================================================================== + namespace { constexpr EAXREVERBPROPERTIES EAXREVERB_PRESET_GENERIC = @@ -1153,61 +1716,3 @@ const EaxReverbPresets EAXREVERB_PRESETS{{ EAXREVERB_PRESET_DIZZY, EAXREVERB_PRESET_PSYCHOTIC, }}; - -namespace { -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_GENERIC = {EAX_ENVIRONMENT_GENERIC, 0.5F, 1.493F, 0.5F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PADDEDCELL = {EAX_ENVIRONMENT_PADDEDCELL, 0.25F, 0.1F, 0.0F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ROOM = {EAX_ENVIRONMENT_ROOM, 0.417F, 0.4F, 0.666F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_BATHROOM = {EAX_ENVIRONMENT_BATHROOM, 0.653F, 1.499F, 0.166F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_LIVINGROOM = {EAX_ENVIRONMENT_LIVINGROOM, 0.208F, 0.478F, 0.0F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_STONEROOM = {EAX_ENVIRONMENT_STONEROOM, 0.5F, 2.309F, 0.888F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_AUDITORIUM = {EAX_ENVIRONMENT_AUDITORIUM, 0.403F, 4.279F, 0.5F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CONCERTHALL = {EAX_ENVIRONMENT_CONCERTHALL, 0.5F, 3.961F, 0.5F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CAVE = {EAX_ENVIRONMENT_CAVE, 0.5F, 2.886F, 1.304F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ARENA = {EAX_ENVIRONMENT_ARENA, 0.361F, 7.284F, 0.332F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_HANGAR = {EAX_ENVIRONMENT_HANGAR, 0.5F, 10.0F, 0.3F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CARPETTEDHALLWAY = {EAX_ENVIRONMENT_CARPETEDHALLWAY, 0.153F, 0.259F, 2.0F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_HALLWAY = {EAX_ENVIRONMENT_HALLWAY, 0.361F, 1.493F, 0.0F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_STONECORRIDOR = {EAX_ENVIRONMENT_STONECORRIDOR, 0.444F, 2.697F, 0.638F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ALLEY = {EAX_ENVIRONMENT_ALLEY, 0.25F, 1.752F, 0.776F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_FOREST = {EAX_ENVIRONMENT_FOREST, 0.111F, 3.145F, 0.472F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CITY = {EAX_ENVIRONMENT_CITY, 0.111F, 2.767F, 0.224F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_MOUNTAINS = {EAX_ENVIRONMENT_MOUNTAINS, 0.194F, 7.841F, 0.472F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_QUARRY = {EAX_ENVIRONMENT_QUARRY, 1.0F, 1.499F, 0.5F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PLAIN = {EAX_ENVIRONMENT_PLAIN, 0.097F, 2.767F, 0.224F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PARKINGLOT = {EAX_ENVIRONMENT_PARKINGLOT, 0.208F, 1.652F, 1.5F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_SEWERPIPE = {EAX_ENVIRONMENT_SEWERPIPE, 0.652F, 2.886F, 0.25F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_UNDERWATER = {EAX_ENVIRONMENT_UNDERWATER, 1.0F, 1.499F, 0.0F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_DRUGGED = {EAX_ENVIRONMENT_DRUGGED, 0.875F, 8.392F, 1.388F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_DIZZY = {EAX_ENVIRONMENT_DIZZY, 0.139F, 17.234F, 0.666F}; -constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PSYCHOTIC = {EAX_ENVIRONMENT_PSYCHOTIC, 0.486F, 7.563F, 0.806F}; -} // namespace - -const Eax1ReverbPresets EAX1REVERB_PRESETS{{ - EAX1REVERB_PRESET_GENERIC, - EAX1REVERB_PRESET_PADDEDCELL, - EAX1REVERB_PRESET_ROOM, - EAX1REVERB_PRESET_BATHROOM, - EAX1REVERB_PRESET_LIVINGROOM, - EAX1REVERB_PRESET_STONEROOM, - EAX1REVERB_PRESET_AUDITORIUM, - EAX1REVERB_PRESET_CONCERTHALL, - EAX1REVERB_PRESET_CAVE, - EAX1REVERB_PRESET_ARENA, - EAX1REVERB_PRESET_HANGAR, - EAX1REVERB_PRESET_CARPETTEDHALLWAY, - EAX1REVERB_PRESET_HALLWAY, - EAX1REVERB_PRESET_STONECORRIDOR, - EAX1REVERB_PRESET_ALLEY, - EAX1REVERB_PRESET_FOREST, - EAX1REVERB_PRESET_CITY, - EAX1REVERB_PRESET_MOUNTAINS, - EAX1REVERB_PRESET_QUARRY, - EAX1REVERB_PRESET_PLAIN, - EAX1REVERB_PRESET_PARKINGLOT, - EAX1REVERB_PRESET_SEWERPIPE, - EAX1REVERB_PRESET_UNDERWATER, - EAX1REVERB_PRESET_DRUGGED, - EAX1REVERB_PRESET_DIZZY, - EAX1REVERB_PRESET_PSYCHOTIC, -}}; diff --git a/al/eax/api.h b/al/eax/api.h index d0737d1d..f4419ddb 100644 --- a/al/eax/api.h +++ b/al/eax/api.h @@ -129,6 +129,110 @@ struct EAX20LISTENERPROPERTIES unsigned long dwFlags; // modifies the behavior of properties }; // EAX20LISTENERPROPERTIES +inline bool operator==(const EAX20LISTENERPROPERTIES& lhs, const EAX20LISTENERPROPERTIES& rhs) noexcept +{ + return std::memcmp(&lhs, &rhs, sizeof(EAX20LISTENERPROPERTIES)) == 0; +} + +enum : unsigned long +{ + EAX2_ENVIRONMENT_GENERIC, + EAX2_ENVIRONMENT_PADDEDCELL, + EAX2_ENVIRONMENT_ROOM, + EAX2_ENVIRONMENT_BATHROOM, + EAX2_ENVIRONMENT_LIVINGROOM, + EAX2_ENVIRONMENT_STONEROOM, + EAX2_ENVIRONMENT_AUDITORIUM, + EAX2_ENVIRONMENT_CONCERTHALL, + EAX2_ENVIRONMENT_CAVE, + EAX2_ENVIRONMENT_ARENA, + EAX2_ENVIRONMENT_HANGAR, + EAX2_ENVIRONMENT_CARPETEDHALLWAY, + EAX2_ENVIRONMENT_HALLWAY, + EAX2_ENVIRONMENT_STONECORRIDOR, + EAX2_ENVIRONMENT_ALLEY, + EAX2_ENVIRONMENT_FOREST, + EAX2_ENVIRONMENT_CITY, + EAX2_ENVIRONMENT_MOUNTAINS, + EAX2_ENVIRONMENT_QUARRY, + EAX2_ENVIRONMENT_PLAIN, + EAX2_ENVIRONMENT_PARKINGLOT, + EAX2_ENVIRONMENT_SEWERPIPE, + EAX2_ENVIRONMENT_UNDERWATER, + EAX2_ENVIRONMENT_DRUGGED, + EAX2_ENVIRONMENT_DIZZY, + EAX2_ENVIRONMENT_PSYCHOTIC, + + EAX2_ENVIRONMENT_COUNT, +}; + +constexpr auto EAX2LISTENERFLAGS_DECAYTIMESCALE = 0x00000001UL; +constexpr auto EAX2LISTENERFLAGS_REFLECTIONSSCALE = 0x00000002UL; +constexpr auto EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE = 0x00000004UL; +constexpr auto EAX2LISTENERFLAGS_REVERBSCALE = 0x00000008UL; +constexpr auto EAX2LISTENERFLAGS_REVERBDELAYSCALE = 0x00000010UL; +constexpr auto EAX2LISTENERFLAGS_DECAYHFLIMIT = 0x00000020UL; +constexpr auto EAX2LISTENERFLAGS_RESERVED = 0xFFFFFFC0UL; + +constexpr auto EAX2LISTENER_MINROOM = -10'000L; +constexpr auto EAX2LISTENER_MAXROOM = 0L; +constexpr auto EAX2LISTENER_DEFAULTROOM = -1'000L; + +constexpr auto EAX2LISTENER_MINROOMHF = -10'000L; +constexpr auto EAX2LISTENER_MAXROOMHF = 0L; +constexpr auto EAX2LISTENER_DEFAULTROOMHF = -100L; + +constexpr auto EAX2LISTENER_MINROOMROLLOFFFACTOR = 0.0F; +constexpr auto EAX2LISTENER_MAXROOMROLLOFFFACTOR = 10.0F; +constexpr auto EAX2LISTENER_DEFAULTROOMROLLOFFFACTOR = 0.0F; + +constexpr auto EAX2LISTENER_MINDECAYTIME = 0.1F; +constexpr auto EAX2LISTENER_MAXDECAYTIME = 20.0F; +constexpr auto EAX2LISTENER_DEFAULTDECAYTIME = 1.49F; + +constexpr auto EAX2LISTENER_MINDECAYHFRATIO = 0.1F; +constexpr auto EAX2LISTENER_MAXDECAYHFRATIO = 2.0F; +constexpr auto EAX2LISTENER_DEFAULTDECAYHFRATIO = 0.83F; + +constexpr auto EAX2LISTENER_MINREFLECTIONS = -10'000L; +constexpr auto EAX2LISTENER_MAXREFLECTIONS = 1'000L; +constexpr auto EAX2LISTENER_DEFAULTREFLECTIONS = -2'602L; + +constexpr auto EAX2LISTENER_MINREFLECTIONSDELAY = 0.0F; +constexpr auto EAX2LISTENER_MAXREFLECTIONSDELAY = 0.3F; +constexpr auto EAX2LISTENER_DEFAULTREFLECTIONSDELAY = 0.007F; + +constexpr auto EAX2LISTENER_MINREVERB = -10'000L; +constexpr auto EAX2LISTENER_MAXREVERB = 2'000L; +constexpr auto EAX2LISTENER_DEFAULTREVERB = 200L; + +constexpr auto EAX2LISTENER_MINREVERBDELAY = 0.0F; +constexpr auto EAX2LISTENER_MAXREVERBDELAY = 0.1F; +constexpr auto EAX2LISTENER_DEFAULTREVERBDELAY = 0.011F; + +constexpr auto EAX2LISTENER_MINENVIRONMENT = 0UL; +constexpr auto EAX2LISTENER_MAXENVIRONMENT = EAX2_ENVIRONMENT_COUNT - 1; +constexpr auto EAX2LISTENER_DEFAULTENVIRONMENT = EAX2_ENVIRONMENT_GENERIC; + +constexpr auto EAX2LISTENER_MINENVIRONMENTSIZE = 1.0F; +constexpr auto EAX2LISTENER_MAXENVIRONMENTSIZE = 100.0F; +constexpr auto EAX2LISTENER_DEFAULTENVIRONMENTSIZE = 7.5F; + +constexpr auto EAX2LISTENER_MINENVIRONMENTDIFFUSION = 0.0F; +constexpr auto EAX2LISTENER_MAXENVIRONMENTDIFFUSION = 1.0F; +constexpr auto EAX2LISTENER_DEFAULTENVIRONMENTDIFFUSION = 1.0F; + +constexpr auto EAX2LISTENER_MINAIRABSORPTIONHF = -100.0F; +constexpr auto EAX2LISTENER_MAXAIRABSORPTIONHF = 0.0F; +constexpr auto EAX2LISTENER_DEFAULTAIRABSORPTIONHF = -5.0F; + +constexpr auto EAX2LISTENER_DEFAULTFLAGS = + EAX2LISTENERFLAGS_DECAYTIMESCALE | + EAX2LISTENERFLAGS_REFLECTIONSSCALE | + EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE | + EAX2LISTENERFLAGS_REVERBSCALE | + EAX2LISTENERFLAGS_REVERBDELAYSCALE | + EAX2LISTENERFLAGS_DECAYHFLIMIT; extern const GUID DSPROPSETID_EAX20_BufferProperties; @@ -957,13 +1061,15 @@ constexpr auto EAXREVERB_DEFAULTFLAGS = EAXREVERBFLAGS_DECAYHFLIMIT; -using EaxReverbPresets = std::array; -extern const EaxReverbPresets EAXREVERB_PRESETS; - - using Eax1ReverbPresets = std::array; extern const Eax1ReverbPresets EAX1REVERB_PRESETS; +using Eax2ReverbPresets = std::array; +extern const Eax2ReverbPresets EAX2REVERB_PRESETS; + +using EaxReverbPresets = std::array; +extern const EaxReverbPresets EAXREVERB_PRESETS; + // AGC Compressor Effect diff --git a/al/eax/call.cpp b/al/eax/call.cpp new file mode 100644 index 00000000..1fd05968 --- /dev/null +++ b/al/eax/call.cpp @@ -0,0 +1,212 @@ +#include "config.h" +#include "call.h" +#include "exception.h" + +namespace { + +constexpr auto deferred_flag = 0x80000000U; + +class EaxCallException : public EaxException { +public: + explicit EaxCallException(const char* message) + : EaxException{"EAX_CALL", message} + {} +}; // EaxCallException + +} // namespace + +EaxCall::EaxCall( + EaxCallType type, + const GUID& property_set_guid, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size) + : type_{type}, version_{0}, property_set_id_{EaxCallPropertySetId::none} + , property_id_{property_id & ~deferred_flag}, property_source_id_{property_source_id} + , property_buffer_{property_buffer}, property_size_{property_size} +{ + switch (type_) + { + case EaxCallType::get: + case EaxCallType::set: + break; + + default: + fail("Invalid type."); + } + + if (false) + { + } + else if (property_set_guid == EAXPROPERTYID_EAX40_Context) + { + version_ = 4; + property_set_id_ = EaxCallPropertySetId::context; + } + else if (property_set_guid == EAXPROPERTYID_EAX50_Context) + { + version_ = 5; + property_set_id_ = EaxCallPropertySetId::context; + } + else if (property_set_guid == DSPROPSETID_EAX20_ListenerProperties) + { + version_ = 2; + fx_slot_index_ = 0u; + property_set_id_ = EaxCallPropertySetId::fx_slot_effect; + } + else if (property_set_guid == DSPROPSETID_EAX30_ListenerProperties) + { + version_ = 3; + fx_slot_index_ = 0u; + property_set_id_ = EaxCallPropertySetId::fx_slot_effect; + } + else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot0) + { + version_ = 4; + fx_slot_index_ = 0u; + property_set_id_ = EaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot0) + { + version_ = 5; + fx_slot_index_ = 0u; + property_set_id_ = EaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot1) + { + version_ = 4; + fx_slot_index_ = 1u; + property_set_id_ = EaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot1) + { + version_ = 5; + fx_slot_index_ = 1u; + property_set_id_ = EaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot2) + { + version_ = 4; + fx_slot_index_ = 2u; + property_set_id_ = EaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot2) + { + version_ = 5; + fx_slot_index_ = 2u; + property_set_id_ = EaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot3) + { + version_ = 4; + fx_slot_index_ = 3u; + property_set_id_ = EaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot3) + { + version_ = 5; + fx_slot_index_ = 3u; + property_set_id_ = EaxCallPropertySetId::fx_slot; + } + else if (property_set_guid == DSPROPSETID_EAX20_BufferProperties) + { + version_ = 2; + property_set_id_ = EaxCallPropertySetId::source; + } + else if (property_set_guid == DSPROPSETID_EAX30_BufferProperties) + { + version_ = 3; + property_set_id_ = EaxCallPropertySetId::source; + } + else if (property_set_guid == EAXPROPERTYID_EAX40_Source) + { + version_ = 4; + property_set_id_ = EaxCallPropertySetId::source; + } + else if (property_set_guid == EAXPROPERTYID_EAX50_Source) + { + version_ = 5; + property_set_id_ = EaxCallPropertySetId::source; + } + else if (property_set_guid == DSPROPSETID_EAX_ReverbProperties) + { + version_ = 1; + fx_slot_index_ = 0u; + property_set_id_ = EaxCallPropertySetId::fx_slot_effect; + } + else if (property_set_guid == DSPROPSETID_EAXBUFFER_ReverbProperties) + { + version_ = 1; + property_set_id_ = EaxCallPropertySetId::source; + } + else + { + fail("Unsupported property set id."); + } + + if (version_ < 1 || version_ > 5) + { + fail("EAX version out of range."); + } + + if(!(property_id&deferred_flag)) + { + if(property_set_id_ != EaxCallPropertySetId::fx_slot && property_id_ != 0) + { + if (property_buffer == nullptr) + { + fail("Null property buffer."); + } + + if (property_size == 0) + { + fail("Empty property."); + } + } + } + + if(property_set_id_ == EaxCallPropertySetId::source && property_source_id_ == 0) + { + fail("Null AL source id."); + } + + if (property_set_id_ == EaxCallPropertySetId::fx_slot) + { + if (property_id_ < EAXFXSLOT_NONE) + { + property_set_id_ = EaxCallPropertySetId::fx_slot_effect; + } + } +} + +[[noreturn]] void EaxCall::fail(const char* message) +{ + throw EaxCallException{message}; +} + +[[noreturn]] void EaxCall::fail_too_small() +{ + fail("Property buffer too small."); +} + +EaxCall create_eax_call( + EaxCallType type, + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size) +{ + if(!property_set_id) + throw EaxCallException{"Null property set ID."}; + + return EaxCall{ + type, + *property_set_id, + property_id, + property_source_id, + property_buffer, + property_size + }; +} diff --git a/al/eax/call.h b/al/eax/call.h new file mode 100644 index 00000000..d491d6f9 --- /dev/null +++ b/al/eax/call.h @@ -0,0 +1,92 @@ +#ifndef EAX_EAX_CALL_INCLUDED +#define EAX_EAX_CALL_INCLUDED + +#include "AL/al.h" +#include "alspan.h" +#include "api.h" +#include "fx_slot_index.h" + +enum class EaxCallType { + none, + get, + set, +}; // EaxCallType + +enum class EaxCallPropertySetId { + none, + context, + fx_slot, + source, + fx_slot_effect, +}; // EaxCallPropertySetId + +class EaxCall { +public: + EaxCall( + EaxCallType type, + const GUID& property_set_guid, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size); + + bool is_get() const noexcept { return type_ == EaxCallType::get; } + int get_version() const noexcept { return version_; } + EaxCallPropertySetId get_property_set_id() const noexcept { return property_set_id_; } + ALuint get_property_id() const noexcept { return property_id_; } + ALuint get_property_al_name() const noexcept { return property_source_id_; } + EaxFxSlotIndex get_fx_slot_index() const noexcept { return fx_slot_index_; } + + template + TValue& get_value() const + { + if (property_size_ < static_cast(sizeof(TValue))) + { + fail_too_small(); + } + + return *static_cast(property_buffer_); + } + + template + al::span get_values() const + { + if (property_size_ < static_cast(sizeof(TValue))) + { + fail_too_small(); + } + + const auto count = property_size_ / sizeof(TValue); + return al::span{static_cast(property_buffer_), count}; + } + + template + void set_value(const TValue& value) const + { + get_value() = value; + } + +private: + EaxCallType type_; + int version_; + EaxFxSlotIndex fx_slot_index_; + EaxCallPropertySetId property_set_id_; + + ALuint property_id_; + ALuint property_source_id_; + ALvoid*property_buffer_; + ALuint property_size_; + + [[noreturn]] static void fail(const char* message); + [[noreturn]] static void fail_too_small(); +}; // EaxCall + +EaxCall create_eax_call( + EaxCallType type, + const GUID* property_set_id, + ALuint property_id, + ALuint property_source_id, + ALvoid* property_buffer, + ALuint property_size); + +#endif // !EAX_EAX_CALL_INCLUDED diff --git a/al/eax/eax_call.cpp b/al/eax/eax_call.cpp deleted file mode 100644 index 19565852..00000000 --- a/al/eax/eax_call.cpp +++ /dev/null @@ -1,323 +0,0 @@ -#include "config.h" - -#include "eax_call.h" -#include "exception.h" - - -namespace { - -constexpr auto deferred_flag = 0x80000000U; - -class EaxEaxCallException : - public EaxException -{ -public: - explicit EaxEaxCallException( - const char* message) - : - EaxException{"EAX_EAX_CALL", message} - { - } -}; // EaxEaxCallException - -} // namespace - - -EaxEaxCall::EaxEaxCall( - bool is_get, - const GUID& property_set_guid, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_buffer, - ALuint property_size) - : is_get_{is_get}, version_{0}, property_set_id_{EaxEaxCallPropertySetId::none} - , property_id_{property_id & ~deferred_flag}, property_source_id_{property_source_id} - , property_buffer_{property_buffer}, property_size_{property_size} -{ - if (false) - { - } - else if (property_set_guid == EAXPROPERTYID_EAX40_Context) - { - version_ = 4; - property_set_id_ = EaxEaxCallPropertySetId::context; - } - else if (property_set_guid == EAXPROPERTYID_EAX50_Context) - { - version_ = 5; - property_set_id_ = EaxEaxCallPropertySetId::context; - } - else if (property_set_guid == DSPROPSETID_EAX20_ListenerProperties) - { - version_ = 2; - fx_slot_index_ = 0u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect; - property_id_ = convert_eax_v2_0_listener_property_id(property_id_); - } - else if (property_set_guid == DSPROPSETID_EAX30_ListenerProperties) - { - version_ = 3; - fx_slot_index_ = 0u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect; - } - else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot0) - { - version_ = 4; - fx_slot_index_ = 0u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot0) - { - version_ = 5; - fx_slot_index_ = 0u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot1) - { - version_ = 4; - fx_slot_index_ = 1u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot1) - { - version_ = 5; - fx_slot_index_ = 1u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot2) - { - version_ = 4; - fx_slot_index_ = 2u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot2) - { - version_ = 5; - fx_slot_index_ = 2u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot3) - { - version_ = 4; - fx_slot_index_ = 3u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot3) - { - version_ = 5; - fx_slot_index_ = 3u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot; - } - else if (property_set_guid == DSPROPSETID_EAX20_BufferProperties) - { - version_ = 2; - property_set_id_ = EaxEaxCallPropertySetId::source; - property_id_ = convert_eax_v2_0_buffer_property_id(property_id_); - } - else if (property_set_guid == DSPROPSETID_EAX30_BufferProperties) - { - version_ = 3; - property_set_id_ = EaxEaxCallPropertySetId::source; - } - else if (property_set_guid == EAXPROPERTYID_EAX40_Source) - { - version_ = 4; - property_set_id_ = EaxEaxCallPropertySetId::source; - } - else if (property_set_guid == EAXPROPERTYID_EAX50_Source) - { - version_ = 5; - property_set_id_ = EaxEaxCallPropertySetId::source; - } - else if (property_set_guid == DSPROPSETID_EAX_ReverbProperties) - { - version_ = 1; - fx_slot_index_ = 0u; - property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect; - } - else if (property_set_guid == DSPROPSETID_EAXBUFFER_ReverbProperties) - { - version_ = 1; - property_set_id_ = EaxEaxCallPropertySetId::source; - } - else - { - fail("Unsupported property set id."); - } - - if (version_ < 1 || version_ > 5) - { - fail("EAX version out of range."); - } - - if(!(property_id&deferred_flag)) - { - if(property_set_id_ != EaxEaxCallPropertySetId::fx_slot && property_id_ != 0) - { - if (!property_buffer) - { - fail("Null property buffer."); - } - - if (property_size == 0) - { - fail("Empty property."); - } - } - } - - if(property_set_id_ == EaxEaxCallPropertySetId::source && property_source_id_ == 0) - { - fail("Null AL source id."); - } - - if (property_set_id_ == EaxEaxCallPropertySetId::fx_slot) - { - if (property_id_ < EAXFXSLOT_NONE) - { - property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect; - } - } -} - -[[noreturn]] -void EaxEaxCall::fail( - const char* message) -{ - throw EaxEaxCallException{message}; -} - -ALuint EaxEaxCall::convert_eax_v2_0_listener_property_id( - ALuint property_id) -{ - switch (property_id) - { - case DSPROPERTY_EAX20LISTENER_NONE: - return EAXREVERB_NONE; - - case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS: - return EAXREVERB_ALLPARAMETERS; - - case DSPROPERTY_EAX20LISTENER_ROOM: - return EAXREVERB_ROOM; - - case DSPROPERTY_EAX20LISTENER_ROOMHF: - return EAXREVERB_ROOMHF; - - case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR: - return EAXREVERB_ROOMROLLOFFFACTOR; - - case DSPROPERTY_EAX20LISTENER_DECAYTIME: - return EAXREVERB_DECAYTIME; - - case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO: - return EAXREVERB_DECAYHFRATIO; - - case DSPROPERTY_EAX20LISTENER_REFLECTIONS: - return EAXREVERB_REFLECTIONS; - - case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY: - return EAXREVERB_REFLECTIONSDELAY; - - case DSPROPERTY_EAX20LISTENER_REVERB: - return EAXREVERB_REVERB; - - case DSPROPERTY_EAX20LISTENER_REVERBDELAY: - return EAXREVERB_REVERBDELAY; - - case DSPROPERTY_EAX20LISTENER_ENVIRONMENT: - return EAXREVERB_ENVIRONMENT; - - case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE: - return EAXREVERB_ENVIRONMENTSIZE; - - case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION: - return EAXREVERB_ENVIRONMENTDIFFUSION; - - case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF: - return EAXREVERB_AIRABSORPTIONHF; - - case DSPROPERTY_EAX20LISTENER_FLAGS: - return EAXREVERB_FLAGS; - - default: - fail("Unsupported EAX 2.0 listener property id."); - } -} - -ALuint EaxEaxCall::convert_eax_v2_0_buffer_property_id( - ALuint property_id) -{ - switch (property_id) - { - case DSPROPERTY_EAX20BUFFER_NONE: - return EAXSOURCE_NONE; - - case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS: - return EAXSOURCE_ALLPARAMETERS; - - case DSPROPERTY_EAX20BUFFER_DIRECT: - return EAXSOURCE_DIRECT; - - case DSPROPERTY_EAX20BUFFER_DIRECTHF: - return EAXSOURCE_DIRECTHF; - - case DSPROPERTY_EAX20BUFFER_ROOM: - return EAXSOURCE_ROOM; - - case DSPROPERTY_EAX20BUFFER_ROOMHF: - return EAXSOURCE_ROOMHF; - - case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR: - return EAXSOURCE_ROOMROLLOFFFACTOR; - - case DSPROPERTY_EAX20BUFFER_OBSTRUCTION: - return EAXSOURCE_OBSTRUCTION; - - case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO: - return EAXSOURCE_OBSTRUCTIONLFRATIO; - - case DSPROPERTY_EAX20BUFFER_OCCLUSION: - return EAXSOURCE_OCCLUSION; - - case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO: - return EAXSOURCE_OCCLUSIONLFRATIO; - - case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO: - return EAXSOURCE_OCCLUSIONROOMRATIO; - - case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF: - return EAXSOURCE_OUTSIDEVOLUMEHF; - - case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR: - return EAXSOURCE_AIRABSORPTIONFACTOR; - - case DSPROPERTY_EAX20BUFFER_FLAGS: - return EAXSOURCE_FLAGS; - - default: - fail("Unsupported EAX 2.0 buffer property id."); - } -} - - -EaxEaxCall create_eax_call( - bool is_get, - const GUID* property_set_id, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_buffer, - ALuint property_size) -{ - if(!property_set_id) - throw EaxEaxCallException{"Null property set ID."}; - - return EaxEaxCall{ - is_get, - *property_set_id, - property_id, - property_source_id, - property_buffer, - property_size - }; -} diff --git a/al/eax/eax_call.h b/al/eax/eax_call.h deleted file mode 100644 index 2c90bdc3..00000000 --- a/al/eax/eax_call.h +++ /dev/null @@ -1,117 +0,0 @@ -#ifndef EAX_EAX_CALL_INCLUDED -#define EAX_EAX_CALL_INCLUDED - - -#include "AL/al.h" - -#include "alspan.h" - -#include "api.h" -#include "fx_slot_index.h" - - -enum class EaxEaxCallPropertySetId -{ - none, - - context, - fx_slot, - source, - fx_slot_effect, -}; // EaxEaxCallPropertySetId - - -class EaxEaxCall -{ -public: - EaxEaxCall( - bool is_get, - const GUID& property_set_guid, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_buffer, - ALuint property_size); - - bool is_get() const noexcept { return is_get_; } - int get_version() const noexcept { return version_; } - EaxEaxCallPropertySetId get_property_set_id() const noexcept { return property_set_id_; } - ALuint get_property_id() const noexcept { return property_id_; } - ALuint get_property_al_name() const noexcept { return property_source_id_; } - EaxFxSlotIndex get_fx_slot_index() const noexcept { return fx_slot_index_; } - - template< - typename TException, - typename TValue - > - TValue& get_value() const - { - if (property_size_ < static_cast(sizeof(TValue))) - { - throw TException{"Property buffer too small."}; - } - - return *static_cast(property_buffer_); - } - - template< - typename TException, - typename TValue - > - al::span get_values() const - { - if (property_size_ < static_cast(sizeof(TValue))) - { - throw TException{"Property buffer too small."}; - } - - const auto count = property_size_ / sizeof(TValue); - - return al::span{static_cast(property_buffer_), count}; - } - - template< - typename TException, - typename TValue - > - void set_value( - const TValue& value) const - { - get_value() = value; - } - - -private: - const bool is_get_; - int version_; - EaxFxSlotIndex fx_slot_index_; - EaxEaxCallPropertySetId property_set_id_; - - ALuint property_id_; - const ALuint property_source_id_; - ALvoid*const property_buffer_; - const ALuint property_size_; - - - [[noreturn]] - static void fail( - const char* message); - - - static ALuint convert_eax_v2_0_listener_property_id( - ALuint property_id); - - static ALuint convert_eax_v2_0_buffer_property_id( - ALuint property_id); -}; // EaxEaxCall - - -EaxEaxCall create_eax_call( - bool is_get, - const GUID* property_set_id, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_buffer, - ALuint property_size); - - -#endif // !EAX_EAX_CALL_INCLUDED diff --git a/al/eax/effect.cpp b/al/eax/effect.cpp deleted file mode 100644 index 4e8faa73..00000000 --- a/al/eax/effect.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "config.h" - -#include "effect.h" diff --git a/al/eax/effect.h b/al/eax/effect.h index 9c9fdef4..b57bf240 100644 --- a/al/eax/effect.h +++ b/al/eax/effect.h @@ -2,43 +2,172 @@ #define EAX_EFFECT_INCLUDED +#include #include +#include "alnumeric.h" #include "AL/al.h" #include "core/effects/base.h" -#include "eax_call.h" +#include "call.h" -class EaxEffect +struct EaxEffectErrorMessages { + static constexpr auto unknown_property_id() noexcept { return "Unknown property id."; } + static constexpr auto unknown_version() noexcept { return "Unknown version."; } +}; // EaxEffectErrorMessages + +class EaxEffect { public: - EaxEffect(ALenum type) : al_effect_type_{type} { } + EaxEffect(ALenum type) noexcept : al_effect_type_{type} { } virtual ~EaxEffect() = default; const ALenum al_effect_type_; EffectProps al_effect_props_{}; - virtual void dispatch(const EaxEaxCall& eax_call) = 0; + virtual void dispatch(const EaxCall& call) = 0; // Returns "true" if any immediated property was changed. - // [[nodiscard]] - virtual bool apply_deferred() = 0; + /*[[nodiscard]]*/ virtual bool commit() = 0; }; // EaxEffect +// Base class for EAX4+ effects. +template +class EaxEffect4 : public EaxEffect +{ +public: + EaxEffect4(ALenum type, const EaxCall& call) + : EaxEffect{type}, version_{clamp(call.get_version(), 4, 5)} + {} + + void initialize() + { + set_defaults(); + set_efx_defaults(); + } + + void dispatch(const EaxCall& call) override + { + call.is_get() ? get(call) : set(call); + version_ = call.get_version(); + } + + bool commit() final + { + switch (version_) + { + case 4: return commit_state(state4_); + case 5: return commit_state(state5_); + default: fail_unknown_version(); + } + } + +protected: + using Exception = TException; + using Props = TProps; + + struct State { + Props i; // Immediate. + Props d; // Deferred. + }; // State + + int version_; + Props props_; + State state4_; + State state5_; + + template + static void defer(const EaxCall& call, TProperty& property) + { + const auto& value = call.get_value(); + TValidator{}(value); + property = value; + } + + virtual void set_defaults(Props& props) = 0; + virtual void set_efx_defaults() = 0; + + virtual void get(const EaxCall& call, const Props& props) = 0; + virtual void set(const EaxCall& call, Props& props) = 0; + + virtual bool commit_props(const Props& props) = 0; + + [[noreturn]] static void fail(const char* message) + { + throw Exception{message}; + } + + [[noreturn]] static void fail_unknown_property_id() + { + fail(EaxEffectErrorMessages::unknown_property_id()); + } + + [[noreturn]] static void fail_unknown_version() + { + fail(EaxEffectErrorMessages::unknown_version()); + } + +private: + void set_defaults() + { + set_defaults(props_); + state4_.i = props_; + state4_.d = props_; + state5_.i = props_; + state5_.d = props_; + } + + void get(const EaxCall& call) + { + switch (call.get_version()) + { + case 4: get(call, state4_.i); break; + case 5: get(call, state5_.i); break; + default: fail_unknown_version(); + } + } + + void set(const EaxCall& call) + { + switch (call.get_version()) + { + case 4: set(call, state4_.d); break; + case 5: set(call, state5_.d); break; + default: fail_unknown_version(); + } + } + + bool commit_state(State& state) + { + const auto props = props_; + state.i = state.d; + props_ = state.d; + return commit_props(props); + } +}; // EaxEffect4 using EaxEffectUPtr = std::unique_ptr; +// Creates EAX4+ effect. +template +EaxEffectUPtr eax_create_eax4_effect(const EaxCall& call) +{ + auto effect = std::make_unique(call); + effect->initialize(); + return effect; +} + EaxEffectUPtr eax_create_eax_null_effect(); -EaxEffectUPtr eax_create_eax_chorus_effect(); -EaxEffectUPtr eax_create_eax_distortion_effect(); -EaxEffectUPtr eax_create_eax_echo_effect(); -EaxEffectUPtr eax_create_eax_flanger_effect(); -EaxEffectUPtr eax_create_eax_frequency_shifter_effect(); -EaxEffectUPtr eax_create_eax_vocal_morpher_effect(); -EaxEffectUPtr eax_create_eax_pitch_shifter_effect(); -EaxEffectUPtr eax_create_eax_ring_modulator_effect(); -EaxEffectUPtr eax_create_eax_auto_wah_effect(); -EaxEffectUPtr eax_create_eax_compressor_effect(); -EaxEffectUPtr eax_create_eax_equalizer_effect(); -EaxEffectUPtr eax_create_eax_reverb_effect(); +EaxEffectUPtr eax_create_eax_chorus_effect(const EaxCall& call); +EaxEffectUPtr eax_create_eax_distortion_effect(const EaxCall& call); +EaxEffectUPtr eax_create_eax_echo_effect(const EaxCall& call); +EaxEffectUPtr eax_create_eax_flanger_effect(const EaxCall& call); +EaxEffectUPtr eax_create_eax_frequency_shifter_effect(const EaxCall& call); +EaxEffectUPtr eax_create_eax_vocal_morpher_effect(const EaxCall& call); +EaxEffectUPtr eax_create_eax_pitch_shifter_effect(const EaxCall& call); +EaxEffectUPtr eax_create_eax_ring_modulator_effect(const EaxCall& call); +EaxEffectUPtr eax_create_eax_auto_wah_effect(const EaxCall& call); +EaxEffectUPtr eax_create_eax_compressor_effect(const EaxCall& call); +EaxEffectUPtr eax_create_eax_equalizer_effect(const EaxCall& call); +EaxEffectUPtr eax_create_eax_reverb_effect(const EaxCall& call); #endif // !EAX_EFFECT_INCLUDED diff --git a/al/eax/fx_slots.cpp b/al/eax/fx_slots.cpp index 5897e951..671d2cfb 100644 --- a/al/eax/fx_slots.cpp +++ b/al/eax/fx_slots.cpp @@ -29,9 +29,10 @@ public: void EaxFxSlots::initialize( + const EaxCall& call, ALCcontext& al_context) { - initialize_fx_slots(al_context); + initialize_fx_slots(call, al_context); } void EaxFxSlots::uninitialize() noexcept @@ -70,6 +71,7 @@ void EaxFxSlots::fail( } void EaxFxSlots::initialize_fx_slots( + const EaxCall& call, ALCcontext& al_context) { auto fx_slot_index = EaxFxSlotIndexValue{}; @@ -77,7 +79,7 @@ void EaxFxSlots::initialize_fx_slots( for (auto& fx_slot : fx_slots_) { fx_slot = eax_create_al_effect_slot(al_context); - fx_slot->eax_initialize(al_context, fx_slot_index); + fx_slot->eax_initialize(call, al_context, fx_slot_index); fx_slot_index += 1; } } diff --git a/al/eax/fx_slots.h b/al/eax/fx_slots.h index 49cabd75..e7d1452e 100644 --- a/al/eax/fx_slots.h +++ b/al/eax/fx_slots.h @@ -7,6 +7,7 @@ #include "al/auxeffectslot.h" #include "api.h" +#include "call.h" #include "fx_slot_index.h" @@ -14,6 +15,7 @@ class EaxFxSlots { public: void initialize( + const EaxCall& call, ALCcontext& al_context); void uninitialize() noexcept; @@ -46,6 +48,7 @@ private: const char* message); void initialize_fx_slots( + const EaxCall& call, ALCcontext& al_context); }; // EaxFxSlots diff --git a/al/eax/x_ram.cpp b/al/eax/x_ram.cpp deleted file mode 100644 index 7332c82e..00000000 --- a/al/eax/x_ram.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "config.h" - -#include "x_ram.h" diff --git a/al/effects/autowah.cpp b/al/effects/autowah.cpp index 23f43305..7e0e34aa 100644 --- a/al/effects/autowah.cpp +++ b/al/effects/autowah.cpp @@ -13,7 +13,6 @@ #ifdef ALSOFT_EAX #include "alnumeric.h" - #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -119,178 +118,127 @@ const EffectProps AutowahEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { -using EaxAutoWahEffectDirtyFlagsValue = std::uint_least8_t; - -struct EaxAutoWahEffectDirtyFlags -{ - using EaxIsBitFieldStruct = bool; - - EaxAutoWahEffectDirtyFlagsValue flAttackTime : 1; - EaxAutoWahEffectDirtyFlagsValue flReleaseTime : 1; - EaxAutoWahEffectDirtyFlagsValue lResonance : 1; - EaxAutoWahEffectDirtyFlagsValue lPeakLevel : 1; -}; // EaxAutoWahEffectDirtyFlags - - -class EaxAutoWahEffect final : - public EaxEffect -{ +class EaxAutoWahEffectException : public EaxException { public: - EaxAutoWahEffect(); - - - void dispatch(const EaxEaxCall& eax_call) override; + explicit EaxAutoWahEffectException(const char* message) + : EaxException{"EAX_AUTO_WAH_EFFECT", message} + {} +}; // EaxAutoWahEffectException - // [[nodiscard]] - bool apply_deferred() override; +class EaxAutoWahEffect final : public EaxEffect4 { +public: + EaxAutoWahEffect(const EaxCall& call); private: - EAXAUTOWAHPROPERTIES eax_{}; - EAXAUTOWAHPROPERTIES eax_d_{}; - EaxAutoWahEffectDirtyFlags eax_dirty_flags_{}; - - - void set_eax_defaults(); - - - void set_efx_attack_time(); - - void set_efx_release_time(); - - void set_efx_resonance(); - - void set_efx_peak_gain(); - - void set_efx_defaults(); - - - void get(const EaxEaxCall& eax_call); - - - void validate_attack_time( - float flAttackTime); - - void validate_release_time( - float flReleaseTime); - - void validate_resonance( - long lResonance); - - void validate_peak_level( - long lPeakLevel); - - void validate_all( - const EAXAUTOWAHPROPERTIES& eax_all); - - - void defer_attack_time( - float flAttackTime); - - void defer_release_time( - float flReleaseTime); - - void defer_resonance( - long lResonance); - - void defer_peak_level( - long lPeakLevel); - - void defer_all( - const EAXAUTOWAHPROPERTIES& eax_all); - - - void defer_attack_time( - const EaxEaxCall& eax_call); - - void defer_release_time( - const EaxEaxCall& eax_call); - - void defer_resonance( - const EaxEaxCall& eax_call); - - void defer_peak_level( - const EaxEaxCall& eax_call); - - void defer_all( - const EaxEaxCall& eax_call); - - void set(const EaxEaxCall& eax_call); + struct AttackTimeValidator { + void operator()(float flAttackTime) const + { + eax_validate_range( + "Attack Time", + flAttackTime, + EAXAUTOWAH_MINATTACKTIME, + EAXAUTOWAH_MAXATTACKTIME); + } + }; // AttackTimeValidator + + struct ReleaseTimeValidator { + void operator()(float flReleaseTime) const + { + eax_validate_range( + "Release Time", + flReleaseTime, + EAXAUTOWAH_MINRELEASETIME, + EAXAUTOWAH_MAXRELEASETIME); + } + }; // ReleaseTimeValidator + + struct ResonanceValidator { + void operator()(long lResonance) const + { + eax_validate_range( + "Resonance", + lResonance, + EAXAUTOWAH_MINRESONANCE, + EAXAUTOWAH_MAXRESONANCE); + } + }; // ResonanceValidator + + struct PeakLevelValidator { + void operator()(long lPeakLevel) const + { + eax_validate_range( + "Peak Level", + lPeakLevel, + EAXAUTOWAH_MINPEAKLEVEL, + EAXAUTOWAH_MAXPEAKLEVEL); + } + }; // PeakLevelValidator + + struct AllValidator { + void operator()(const Props& all) const + { + AttackTimeValidator{}(all.flAttackTime); + ReleaseTimeValidator{}(all.flReleaseTime); + ResonanceValidator{}(all.lResonance); + PeakLevelValidator{}(all.lPeakLevel); + } + }; // AllValidator + + void set_defaults(Props& props) override; + + void set_efx_attack_time() noexcept; + void set_efx_release_time() noexcept; + void set_efx_resonance() noexcept; + void set_efx_peak_gain() noexcept; + void set_efx_defaults() override; + + void get(const EaxCall& call, const Props& props) override; + void set(const EaxCall& call, Props& props) override; + bool commit_props(const Props& props) override; }; // EaxAutoWahEffect +EaxAutoWahEffect::EaxAutoWahEffect(const EaxCall& call) + : EaxEffect4{AL_EFFECT_AUTOWAH, call} +{} -class EaxAutoWahEffectException : - public EaxException -{ -public: - explicit EaxAutoWahEffectException( - const char* message) - : - EaxException{"EAX_AUTO_WAH_EFFECT", message} - { - } -}; // EaxAutoWahEffectException - - -EaxAutoWahEffect::EaxAutoWahEffect() - : EaxEffect{AL_EFFECT_AUTOWAH} -{ - set_eax_defaults(); - set_efx_defaults(); -} - -void EaxAutoWahEffect::dispatch(const EaxEaxCall& eax_call) -{ - eax_call.is_get() ? get(eax_call) : set(eax_call); -} - -void EaxAutoWahEffect::set_eax_defaults() +void EaxAutoWahEffect::set_defaults(Props& props) { - eax_.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME; - eax_.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME; - eax_.lResonance = EAXAUTOWAH_DEFAULTRESONANCE; - eax_.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL; - - eax_d_ = eax_; + props.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME; + props.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME; + props.lResonance = EAXAUTOWAH_DEFAULTRESONANCE; + props.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL; } -void EaxAutoWahEffect::set_efx_attack_time() +void EaxAutoWahEffect::set_efx_attack_time() noexcept { - const auto attack_time = clamp( - eax_.flAttackTime, + al_effect_props_.Autowah.AttackTime = clamp( + props_.flAttackTime, AL_AUTOWAH_MIN_ATTACK_TIME, AL_AUTOWAH_MAX_ATTACK_TIME); - - al_effect_props_.Autowah.AttackTime = attack_time; } -void EaxAutoWahEffect::set_efx_release_time() +void EaxAutoWahEffect::set_efx_release_time() noexcept { - const auto release_time = clamp( - eax_.flReleaseTime, + al_effect_props_.Autowah.ReleaseTime = clamp( + props_.flReleaseTime, AL_AUTOWAH_MIN_RELEASE_TIME, AL_AUTOWAH_MAX_RELEASE_TIME); - - al_effect_props_.Autowah.ReleaseTime = release_time; } -void EaxAutoWahEffect::set_efx_resonance() +void EaxAutoWahEffect::set_efx_resonance() noexcept { - const auto resonance = clamp( - level_mb_to_gain(static_cast(eax_.lResonance)), + al_effect_props_.Autowah.Resonance = clamp( + level_mb_to_gain(static_cast(props_.lResonance)), AL_AUTOWAH_MIN_RESONANCE, AL_AUTOWAH_MAX_RESONANCE); - - al_effect_props_.Autowah.Resonance = resonance; } -void EaxAutoWahEffect::set_efx_peak_gain() +void EaxAutoWahEffect::set_efx_peak_gain() noexcept { - const auto peak_gain = clamp( - level_mb_to_gain(static_cast(eax_.lPeakLevel)), + al_effect_props_.Autowah.PeakGain = clamp( + level_mb_to_gain(static_cast(props_.lPeakLevel)), AL_AUTOWAH_MIN_PEAK_GAIN, AL_AUTOWAH_MAX_PEAK_GAIN); - - al_effect_props_.Autowah.PeakGain = peak_gain; } void EaxAutoWahEffect::set_efx_defaults() @@ -301,248 +249,70 @@ void EaxAutoWahEffect::set_efx_defaults() set_efx_peak_gain(); } -void EaxAutoWahEffect::get(const EaxEaxCall& eax_call) +void EaxAutoWahEffect::get(const EaxCall& call, const Props& props) { - switch (eax_call.get_property_id()) + switch (call.get_property_id()) { - case EAXAUTOWAH_NONE: - break; - - case EAXAUTOWAH_ALLPARAMETERS: - eax_call.set_value(eax_); - break; - - case EAXAUTOWAH_ATTACKTIME: - eax_call.set_value(eax_.flAttackTime); - break; - - case EAXAUTOWAH_RELEASETIME: - eax_call.set_value(eax_.flReleaseTime); - break; - - case EAXAUTOWAH_RESONANCE: - eax_call.set_value(eax_.lResonance); - break; - - case EAXAUTOWAH_PEAKLEVEL: - eax_call.set_value(eax_.lPeakLevel); - break; - - default: - throw EaxAutoWahEffectException{"Unsupported property id."}; + case EAXAUTOWAH_NONE: break; + case EAXAUTOWAH_ALLPARAMETERS: call.set_value(props); break; + case EAXAUTOWAH_ATTACKTIME: call.set_value(props.flAttackTime); break; + case EAXAUTOWAH_RELEASETIME: call.set_value(props.flReleaseTime); break; + case EAXAUTOWAH_RESONANCE: call.set_value(props.lResonance); break; + case EAXAUTOWAH_PEAKLEVEL: call.set_value(props.lPeakLevel); break; + default: fail_unknown_property_id(); } } -void EaxAutoWahEffect::validate_attack_time( - float flAttackTime) +void EaxAutoWahEffect::set(const EaxCall& call, Props& props) { - eax_validate_range( - "Attack Time", - flAttackTime, - EAXAUTOWAH_MINATTACKTIME, - EAXAUTOWAH_MAXATTACKTIME); -} - -void EaxAutoWahEffect::validate_release_time( - float flReleaseTime) -{ - eax_validate_range( - "Release Time", - flReleaseTime, - EAXAUTOWAH_MINRELEASETIME, - EAXAUTOWAH_MAXRELEASETIME); -} - -void EaxAutoWahEffect::validate_resonance( - long lResonance) -{ - eax_validate_range( - "Resonance", - lResonance, - EAXAUTOWAH_MINRESONANCE, - EAXAUTOWAH_MAXRESONANCE); -} - -void EaxAutoWahEffect::validate_peak_level( - long lPeakLevel) -{ - eax_validate_range( - "Peak Level", - lPeakLevel, - EAXAUTOWAH_MINPEAKLEVEL, - EAXAUTOWAH_MAXPEAKLEVEL); -} - -void EaxAutoWahEffect::validate_all( - const EAXAUTOWAHPROPERTIES& eax_all) -{ - validate_attack_time(eax_all.flAttackTime); - validate_release_time(eax_all.flReleaseTime); - validate_resonance(eax_all.lResonance); - validate_peak_level(eax_all.lPeakLevel); -} - -void EaxAutoWahEffect::defer_attack_time( - float flAttackTime) -{ - eax_d_.flAttackTime = flAttackTime; - eax_dirty_flags_.flAttackTime = (eax_.flAttackTime != eax_d_.flAttackTime); -} - -void EaxAutoWahEffect::defer_release_time( - float flReleaseTime) -{ - eax_d_.flReleaseTime = flReleaseTime; - eax_dirty_flags_.flReleaseTime = (eax_.flReleaseTime != eax_d_.flReleaseTime); -} - -void EaxAutoWahEffect::defer_resonance( - long lResonance) -{ - eax_d_.lResonance = lResonance; - eax_dirty_flags_.lResonance = (eax_.lResonance != eax_d_.lResonance); -} - -void EaxAutoWahEffect::defer_peak_level( - long lPeakLevel) -{ - eax_d_.lPeakLevel = lPeakLevel; - eax_dirty_flags_.lPeakLevel = (eax_.lPeakLevel != eax_d_.lPeakLevel); -} - -void EaxAutoWahEffect::defer_all( - const EAXAUTOWAHPROPERTIES& eax_all) -{ - validate_all(eax_all); - - defer_attack_time(eax_all.flAttackTime); - defer_release_time(eax_all.flReleaseTime); - defer_resonance(eax_all.lResonance); - defer_peak_level(eax_all.lPeakLevel); -} - -void EaxAutoWahEffect::defer_attack_time( - const EaxEaxCall& eax_call) -{ - const auto& attack_time = - eax_call.get_value(); - - validate_attack_time(attack_time); - defer_attack_time(attack_time); -} - -void EaxAutoWahEffect::defer_release_time( - const EaxEaxCall& eax_call) -{ - const auto& release_time = - eax_call.get_value(); - - validate_release_time(release_time); - defer_release_time(release_time); -} - -void EaxAutoWahEffect::defer_resonance( - const EaxEaxCall& eax_call) -{ - const auto& resonance = - eax_call.get_value(); - - validate_resonance(resonance); - defer_resonance(resonance); -} - -void EaxAutoWahEffect::defer_peak_level( - const EaxEaxCall& eax_call) -{ - const auto& peak_level = - eax_call.get_value(); - - validate_peak_level(peak_level); - defer_peak_level(peak_level); -} - -void EaxAutoWahEffect::defer_all( - const EaxEaxCall& eax_call) -{ - const auto& all = - eax_call.get_value(); - - validate_all(all); - defer_all(all); -} - -// [[nodiscard]] -bool EaxAutoWahEffect::apply_deferred() -{ - if (eax_dirty_flags_ == EaxAutoWahEffectDirtyFlags{}) + switch (call.get_property_id()) { - return false; + case EAXAUTOWAH_NONE: break; + case EAXAUTOWAH_ALLPARAMETERS: defer(call, props); break; + case EAXAUTOWAH_ATTACKTIME: defer(call, props.flAttackTime); break; + case EAXAUTOWAH_RELEASETIME: defer(call, props.flReleaseTime); break; + case EAXAUTOWAH_RESONANCE: defer(call, props.lResonance); break; + case EAXAUTOWAH_PEAKLEVEL: defer(call, props.lPeakLevel); break; + default: fail_unknown_property_id(); } +} - eax_ = eax_d_; +bool EaxAutoWahEffect::commit_props(const Props& props) +{ + auto is_dirty = false; - if (eax_dirty_flags_.flAttackTime) + if (props_.flAttackTime != props.flAttackTime) { + is_dirty = true; set_efx_attack_time(); } - if (eax_dirty_flags_.flReleaseTime) + if (props_.flReleaseTime != props.flReleaseTime) { + is_dirty = true; set_efx_release_time(); } - if (eax_dirty_flags_.lResonance) + if (props_.lResonance != props.lResonance) { + is_dirty = true; set_efx_resonance(); } - if (eax_dirty_flags_.lPeakLevel) + if (props_.lPeakLevel != props.lPeakLevel) { + is_dirty = true; set_efx_peak_gain(); } - eax_dirty_flags_ = EaxAutoWahEffectDirtyFlags{}; - - return true; -} - -void EaxAutoWahEffect::set(const EaxEaxCall& eax_call) -{ - switch (eax_call.get_property_id()) - { - case EAXAUTOWAH_NONE: - break; - - case EAXAUTOWAH_ALLPARAMETERS: - defer_all(eax_call); - break; - - case EAXAUTOWAH_ATTACKTIME: - defer_attack_time(eax_call); - break; - - case EAXAUTOWAH_RELEASETIME: - defer_release_time(eax_call); - break; - - case EAXAUTOWAH_RESONANCE: - defer_resonance(eax_call); - break; - - case EAXAUTOWAH_PEAKLEVEL: - defer_peak_level(eax_call); - break; - - default: - throw EaxAutoWahEffectException{"Unsupported property id."}; - } + return is_dirty; } } // namespace -EaxEffectUPtr eax_create_eax_auto_wah_effect() +EaxEffectUPtr eax_create_eax_auto_wah_effect(const EaxCall& call) { - return std::make_unique<::EaxAutoWahEffect>(); + return eax_create_eax4_effect(call); } #endif // ALSOFT_EAX diff --git a/al/effects/chorus.cpp b/al/effects/chorus.cpp index b612a6c1..0d4283c9 100644 --- a/al/effects/chorus.cpp +++ b/al/effects/chorus.cpp @@ -13,9 +13,7 @@ #ifdef ALSOFT_EAX #include - #include "alnumeric.h" - #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -293,1065 +291,444 @@ const EffectProps FlangerEffectProps{genDefaultFlangerProps()}; #ifdef ALSOFT_EAX namespace { -void eax_set_efx_waveform( - ALenum waveform, - EffectProps& al_effect_props) -{ - const auto efx_waveform = WaveformFromEnum(waveform); - assert(efx_waveform.has_value()); - al_effect_props.Chorus.Waveform = *efx_waveform; -} - -void eax_set_efx_phase( - ALint phase, - EffectProps& al_effect_props) -{ - al_effect_props.Chorus.Phase = phase; -} - -void eax_set_efx_rate( - ALfloat rate, - EffectProps& al_effect_props) -{ - al_effect_props.Chorus.Rate = rate; -} - -void eax_set_efx_depth( - ALfloat depth, - EffectProps& al_effect_props) -{ - al_effect_props.Chorus.Depth = depth; -} - -void eax_set_efx_feedback( - ALfloat feedback, - EffectProps& al_effect_props) -{ - al_effect_props.Chorus.Feedback = feedback; -} - -void eax_set_efx_delay( - ALfloat delay, - EffectProps& al_effect_props) -{ - al_effect_props.Chorus.Delay = delay; -} - - -using EaxChorusEffectDirtyFlagsValue = std::uint_least8_t; - -struct EaxChorusEffectDirtyFlags -{ - using EaxIsBitFieldStruct = bool; - - EaxChorusEffectDirtyFlagsValue ulWaveform : 1; - EaxChorusEffectDirtyFlagsValue lPhase : 1; - EaxChorusEffectDirtyFlagsValue flRate : 1; - EaxChorusEffectDirtyFlagsValue flDepth : 1; - EaxChorusEffectDirtyFlagsValue flFeedback : 1; - EaxChorusEffectDirtyFlagsValue flDelay : 1; -}; // EaxChorusEffectDirtyFlags - +class EaxChorusEffectException : public EaxException { +public: + explicit EaxChorusEffectException(const char* message) + : EaxException{"EAX_CHORUS_EFFECT", message} + {} +}; // EaxChorusEffectException -class EaxChorusEffect final : - public EaxEffect -{ +class EaxFlangerEffectException : public EaxException { public: - EaxChorusEffect(); + explicit EaxFlangerEffectException(const char* message) + : EaxException{"EAX_FLANGER_EFFECT", message} + {} +}; // EaxFlangerEffectException - void dispatch(const EaxEaxCall& eax_call) override; +struct EaxChorusTraits +{ + using Exception = EaxChorusEffectException; + using Props = EAXCHORUSPROPERTIES; + + static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; } + + static constexpr auto eax_none_param_id() { return EAXCHORUS_NONE; } + static constexpr auto eax_allparameters_param_id() { return EAXCHORUS_ALLPARAMETERS; } + static constexpr auto eax_waveform_param_id() { return EAXCHORUS_WAVEFORM; } + static constexpr auto eax_phase_param_id() { return EAXCHORUS_PHASE; } + static constexpr auto eax_rate_param_id() { return EAXCHORUS_RATE; } + static constexpr auto eax_depth_param_id() { return EAXCHORUS_DEPTH; } + static constexpr auto eax_feedback_param_id() { return EAXCHORUS_FEEDBACK; } + static constexpr auto eax_delay_param_id() { return EAXCHORUS_DELAY; } + + static constexpr auto eax_min_waveform() { return EAXCHORUS_MINWAVEFORM; } + static constexpr auto eax_min_phase() { return EAXCHORUS_MINPHASE; } + static constexpr auto eax_min_rate() { return EAXCHORUS_MINRATE; } + static constexpr auto eax_min_depth() { return EAXCHORUS_MINDEPTH; } + static constexpr auto eax_min_feedback() { return EAXCHORUS_MINFEEDBACK; } + static constexpr auto eax_min_delay() { return EAXCHORUS_MINDELAY; } + + static constexpr auto eax_max_waveform() { return EAXCHORUS_MAXWAVEFORM; } + static constexpr auto eax_max_phase() { return EAXCHORUS_MAXPHASE; } + static constexpr auto eax_max_rate() { return EAXCHORUS_MAXRATE; } + static constexpr auto eax_max_depth() { return EAXCHORUS_MAXDEPTH; } + static constexpr auto eax_max_feedback() { return EAXCHORUS_MAXFEEDBACK; } + static constexpr auto eax_max_delay() { return EAXCHORUS_MAXDELAY; } + + static constexpr auto eax_default_waveform() { return EAXCHORUS_DEFAULTWAVEFORM; } + static constexpr auto eax_default_phase() { return EAXCHORUS_DEFAULTPHASE; } + static constexpr auto eax_default_rate() { return EAXCHORUS_DEFAULTRATE; } + static constexpr auto eax_default_depth() { return EAXCHORUS_DEFAULTDEPTH; } + static constexpr auto eax_default_feedback() { return EAXCHORUS_DEFAULTFEEDBACK; } + static constexpr auto eax_default_delay() { return EAXCHORUS_DEFAULTDELAY; } + + static constexpr auto efx_min_waveform() { return AL_CHORUS_MIN_WAVEFORM; } + static constexpr auto efx_min_phase() { return AL_CHORUS_MIN_PHASE; } + static constexpr auto efx_min_rate() { return AL_CHORUS_MIN_RATE; } + static constexpr auto efx_min_depth() { return AL_CHORUS_MIN_DEPTH; } + static constexpr auto efx_min_feedback() { return AL_CHORUS_MIN_FEEDBACK; } + static constexpr auto efx_min_delay() { return AL_CHORUS_MIN_DELAY; } + + static constexpr auto efx_max_waveform() { return AL_CHORUS_MAX_WAVEFORM; } + static constexpr auto efx_max_phase() { return AL_CHORUS_MAX_PHASE; } + static constexpr auto efx_max_rate() { return AL_CHORUS_MAX_RATE; } + static constexpr auto efx_max_depth() { return AL_CHORUS_MAX_DEPTH; } + static constexpr auto efx_max_feedback() { return AL_CHORUS_MAX_FEEDBACK; } + static constexpr auto efx_max_delay() { return AL_CHORUS_MAX_DELAY; } + + static constexpr auto efx_default_waveform() { return AL_CHORUS_DEFAULT_WAVEFORM; } + static constexpr auto efx_default_phase() { return AL_CHORUS_DEFAULT_PHASE; } + static constexpr auto efx_default_rate() { return AL_CHORUS_DEFAULT_RATE; } + static constexpr auto efx_default_depth() { return AL_CHORUS_DEFAULT_DEPTH; } + static constexpr auto efx_default_feedback() { return AL_CHORUS_DEFAULT_FEEDBACK; } + static constexpr auto efx_default_delay() { return AL_CHORUS_DEFAULT_DELAY; } +}; // EaxChorusTraits + +struct EaxFlangerTraits +{ + using Exception = EaxFlangerEffectException; + using Props = EAXFLANGERPROPERTIES; + + static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; } + + static constexpr auto eax_none_param_id() { return EAXFLANGER_NONE; } + static constexpr auto eax_allparameters_param_id() { return EAXFLANGER_ALLPARAMETERS; } + static constexpr auto eax_waveform_param_id() { return EAXFLANGER_WAVEFORM; } + static constexpr auto eax_phase_param_id() { return EAXFLANGER_PHASE; } + static constexpr auto eax_rate_param_id() { return EAXFLANGER_RATE; } + static constexpr auto eax_depth_param_id() { return EAXFLANGER_DEPTH; } + static constexpr auto eax_feedback_param_id() { return EAXFLANGER_FEEDBACK; } + static constexpr auto eax_delay_param_id() { return EAXFLANGER_DELAY; } + + static constexpr auto eax_min_waveform() { return EAXFLANGER_MINWAVEFORM; } + static constexpr auto eax_min_phase() { return EAXFLANGER_MINPHASE; } + static constexpr auto eax_min_rate() { return EAXFLANGER_MINRATE; } + static constexpr auto eax_min_depth() { return EAXFLANGER_MINDEPTH; } + static constexpr auto eax_min_feedback() { return EAXFLANGER_MINFEEDBACK; } + static constexpr auto eax_min_delay() { return EAXFLANGER_MINDELAY; } + + static constexpr auto eax_max_waveform() { return EAXFLANGER_MAXWAVEFORM; } + static constexpr auto eax_max_phase() { return EAXFLANGER_MAXPHASE; } + static constexpr auto eax_max_rate() { return EAXFLANGER_MAXRATE; } + static constexpr auto eax_max_depth() { return EAXFLANGER_MAXDEPTH; } + static constexpr auto eax_max_feedback() { return EAXFLANGER_MAXFEEDBACK; } + static constexpr auto eax_max_delay() { return EAXFLANGER_MAXDELAY; } + + static constexpr auto eax_default_waveform() { return EAXFLANGER_DEFAULTWAVEFORM; } + static constexpr auto eax_default_phase() { return EAXFLANGER_DEFAULTPHASE; } + static constexpr auto eax_default_rate() { return EAXFLANGER_DEFAULTRATE; } + static constexpr auto eax_default_depth() { return EAXFLANGER_DEFAULTDEPTH; } + static constexpr auto eax_default_feedback() { return EAXFLANGER_DEFAULTFEEDBACK; } + static constexpr auto eax_default_delay() { return EAXFLANGER_DEFAULTDELAY; } + + static constexpr auto efx_min_waveform() { return AL_FLANGER_MIN_WAVEFORM; } + static constexpr auto efx_min_phase() { return AL_FLANGER_MIN_PHASE; } + static constexpr auto efx_min_rate() { return AL_FLANGER_MIN_RATE; } + static constexpr auto efx_min_depth() { return AL_FLANGER_MIN_DEPTH; } + static constexpr auto efx_min_feedback() { return AL_FLANGER_MIN_FEEDBACK; } + static constexpr auto efx_min_delay() { return AL_FLANGER_MIN_DELAY; } + + static constexpr auto efx_max_waveform() { return AL_FLANGER_MAX_WAVEFORM; } + static constexpr auto efx_max_phase() { return AL_FLANGER_MAX_PHASE; } + static constexpr auto efx_max_rate() { return AL_FLANGER_MAX_RATE; } + static constexpr auto efx_max_depth() { return AL_FLANGER_MAX_DEPTH; } + static constexpr auto efx_max_feedback() { return AL_FLANGER_MAX_FEEDBACK; } + static constexpr auto efx_max_delay() { return AL_FLANGER_MAX_DELAY; } + + static constexpr auto efx_default_waveform() { return AL_FLANGER_DEFAULT_WAVEFORM; } + static constexpr auto efx_default_phase() { return AL_FLANGER_DEFAULT_PHASE; } + static constexpr auto efx_default_rate() { return AL_FLANGER_DEFAULT_RATE; } + static constexpr auto efx_default_depth() { return AL_FLANGER_DEFAULT_DEPTH; } + static constexpr auto efx_default_feedback() { return AL_FLANGER_DEFAULT_FEEDBACK; } + static constexpr auto efx_default_delay() { return AL_FLANGER_DEFAULT_DELAY; } +}; // EaxFlangerTraits + +template +class EaxChorusFlangerEffect final : public EaxEffect4 { +public: + using Traits = TTraits; + using Base = EaxEffect4; + using typename Base::Exception; + using typename Base::Props; + using typename Base::State; + using Base::defer; - // [[nodiscard]] - bool apply_deferred() override; + EaxChorusFlangerEffect(const EaxCall& call) + : Base{Traits::efx_effect(), call} + {} private: - EAXCHORUSPROPERTIES eax_{}; - EAXCHORUSPROPERTIES eax_d_{}; - EaxChorusEffectDirtyFlags eax_dirty_flags_{}; - - void set_eax_defaults() noexcept; - - void set_efx_waveform(); - void set_efx_phase(); - void set_efx_rate(); - void set_efx_depth(); - void set_efx_feedback(); - void set_efx_delay(); - void set_efx_defaults(); - - void get(const EaxEaxCall& eax_call); - - void validate_waveform(unsigned long ulWaveform); - void validate_phase(long lPhase); - void validate_rate(float flRate); - void validate_depth(float flDepth); - void validate_feedback(float flFeedback); - void validate_delay(float flDelay); - void validate_all(const EAXCHORUSPROPERTIES& eax_all); - - void defer_waveform(unsigned long ulWaveform); - void defer_phase(long lPhase); - void defer_rate(float flRate); - void defer_depth(float flDepth); - void defer_feedback(float flFeedback); - void defer_delay(float flDelay); - void defer_all(const EAXCHORUSPROPERTIES& eax_all); - - void defer_waveform(const EaxEaxCall& eax_call); - void defer_phase(const EaxEaxCall& eax_call); - void defer_rate(const EaxEaxCall& eax_call); - void defer_depth(const EaxEaxCall& eax_call); - void defer_feedback(const EaxEaxCall& eax_call); - void defer_delay(const EaxEaxCall& eax_call); - void defer_all(const EaxEaxCall& eax_call); - - void set(const EaxEaxCall& eax_call); -}; // EaxChorusEffect - - -class EaxChorusEffectException : - public EaxException -{ -public: - explicit EaxChorusEffectException( - const char* message) - : - EaxException{"EAX_CHORUS_EFFECT", message} + struct WaveformValidator { + void operator()(unsigned long ulWaveform) const + { + eax_validate_range( + "Waveform", + ulWaveform, + Traits::eax_min_waveform(), + Traits::eax_max_waveform()); + } + }; // WaveformValidator + + struct PhaseValidator { + void operator()(long lPhase) const + { + eax_validate_range( + "Phase", + lPhase, + Traits::eax_min_phase(), + Traits::eax_max_phase()); + } + }; // PhaseValidator + + struct RateValidator { + void operator()(float flRate) const + { + eax_validate_range( + "Rate", + flRate, + Traits::eax_min_rate(), + Traits::eax_max_rate()); + } + }; // RateValidator + + struct DepthValidator { + void operator()(float flDepth) const + { + eax_validate_range( + "Depth", + flDepth, + Traits::eax_min_depth(), + Traits::eax_max_depth()); + } + }; // DepthValidator + + struct FeedbackValidator { + void operator()(float flFeedback) const + { + eax_validate_range( + "Feedback", + flFeedback, + Traits::eax_min_feedback(), + Traits::eax_max_feedback()); + } + }; // FeedbackValidator + + struct DelayValidator { + void operator()(float flDelay) const + { + eax_validate_range( + "Delay", + flDelay, + Traits::eax_min_delay(), + Traits::eax_max_delay()); + } + }; // DelayValidator + + struct AllValidator { + void operator()(const Props& all) const + { + WaveformValidator{}(all.ulWaveform); + PhaseValidator{}(all.lPhase); + RateValidator{}(all.flRate); + DepthValidator{}(all.flDepth); + FeedbackValidator{}(all.flFeedback); + DelayValidator{}(all.flDelay); + } + }; // AllValidator + + void set_defaults(Props& props) override { + props.ulWaveform = Traits::eax_default_waveform(); + props.lPhase = Traits::eax_default_phase(); + props.flRate = Traits::eax_default_rate(); + props.flDepth = Traits::eax_default_depth(); + props.flFeedback = Traits::eax_default_feedback(); + props.flDelay = Traits::eax_default_delay(); } -}; // EaxChorusEffectException - - -EaxChorusEffect::EaxChorusEffect() - : EaxEffect{AL_EFFECT_CHORUS} -{ - set_eax_defaults(); - set_efx_defaults(); -} - -void EaxChorusEffect::dispatch(const EaxEaxCall& eax_call) -{ - eax_call.is_get() ? get(eax_call) : set(eax_call); -} - -void EaxChorusEffect::set_eax_defaults() noexcept -{ - eax_.ulWaveform = EAXCHORUS_DEFAULTWAVEFORM; - eax_.lPhase = EAXCHORUS_DEFAULTPHASE; - eax_.flRate = EAXCHORUS_DEFAULTRATE; - eax_.flDepth = EAXCHORUS_DEFAULTDEPTH; - eax_.flFeedback = EAXCHORUS_DEFAULTFEEDBACK; - eax_.flDelay = EAXCHORUS_DEFAULTDELAY; - - eax_d_ = eax_; -} - -void EaxChorusEffect::set_efx_waveform() -{ - const auto waveform = clamp( - static_cast(eax_.ulWaveform), - AL_CHORUS_MIN_WAVEFORM, - AL_CHORUS_MAX_WAVEFORM); - eax_set_efx_waveform(waveform, al_effect_props_); -} - -void EaxChorusEffect::set_efx_phase() -{ - const auto phase = clamp( - static_cast(eax_.lPhase), - AL_CHORUS_MIN_PHASE, - AL_CHORUS_MAX_PHASE); - - eax_set_efx_phase(phase, al_effect_props_); -} - -void EaxChorusEffect::set_efx_rate() -{ - const auto rate = clamp( - eax_.flRate, - AL_CHORUS_MIN_RATE, - AL_CHORUS_MAX_RATE); - - eax_set_efx_rate(rate, al_effect_props_); -} - -void EaxChorusEffect::set_efx_depth() -{ - const auto depth = clamp( - eax_.flDepth, - AL_CHORUS_MIN_DEPTH, - AL_CHORUS_MAX_DEPTH); - - eax_set_efx_depth(depth, al_effect_props_); -} - -void EaxChorusEffect::set_efx_feedback() -{ - const auto feedback = clamp( - eax_.flFeedback, - AL_CHORUS_MIN_FEEDBACK, - AL_CHORUS_MAX_FEEDBACK); - - eax_set_efx_feedback(feedback, al_effect_props_); -} - -void EaxChorusEffect::set_efx_delay() -{ - const auto delay = clamp( - eax_.flDelay, - AL_CHORUS_MIN_DELAY, - AL_CHORUS_MAX_DELAY); - - eax_set_efx_delay(delay, al_effect_props_); -} - -void EaxChorusEffect::set_efx_defaults() -{ - set_efx_waveform(); - set_efx_phase(); - set_efx_rate(); - set_efx_depth(); - set_efx_feedback(); - set_efx_delay(); -} - -void EaxChorusEffect::get(const EaxEaxCall& eax_call) -{ - switch(eax_call.get_property_id()) + void set_efx_waveform() { - case EAXCHORUS_NONE: - break; - - case EAXCHORUS_ALLPARAMETERS: - eax_call.set_value(eax_); - break; - - case EAXCHORUS_WAVEFORM: - eax_call.set_value(eax_.ulWaveform); - break; - - case EAXCHORUS_PHASE: - eax_call.set_value(eax_.lPhase); - break; - - case EAXCHORUS_RATE: - eax_call.set_value(eax_.flRate); - break; - - case EAXCHORUS_DEPTH: - eax_call.set_value(eax_.flDepth); - break; - - case EAXCHORUS_FEEDBACK: - eax_call.set_value(eax_.flFeedback); - break; - - case EAXCHORUS_DELAY: - eax_call.set_value(eax_.flDelay); - break; - - default: - throw EaxChorusEffectException{"Unsupported property id."}; + const auto waveform = clamp( + static_cast(Base::props_.ulWaveform), + Traits::efx_min_waveform(), + Traits::efx_max_waveform()); + const auto efx_waveform = WaveformFromEnum(waveform); + assert(efx_waveform.has_value()); + Base::al_effect_props_.Chorus.Waveform = *efx_waveform; } -} - -void EaxChorusEffect::validate_waveform( - unsigned long ulWaveform) -{ - eax_validate_range( - "Waveform", - ulWaveform, - EAXCHORUS_MINWAVEFORM, - EAXCHORUS_MAXWAVEFORM); -} - -void EaxChorusEffect::validate_phase( - long lPhase) -{ - eax_validate_range( - "Phase", - lPhase, - EAXCHORUS_MINPHASE, - EAXCHORUS_MAXPHASE); -} - -void EaxChorusEffect::validate_rate( - float flRate) -{ - eax_validate_range( - "Rate", - flRate, - EAXCHORUS_MINRATE, - EAXCHORUS_MAXRATE); -} - -void EaxChorusEffect::validate_depth( - float flDepth) -{ - eax_validate_range( - "Depth", - flDepth, - EAXCHORUS_MINDEPTH, - EAXCHORUS_MAXDEPTH); -} - -void EaxChorusEffect::validate_feedback( - float flFeedback) -{ - eax_validate_range( - "Feedback", - flFeedback, - EAXCHORUS_MINFEEDBACK, - EAXCHORUS_MAXFEEDBACK); -} - -void EaxChorusEffect::validate_delay( - float flDelay) -{ - eax_validate_range( - "Delay", - flDelay, - EAXCHORUS_MINDELAY, - EAXCHORUS_MAXDELAY); -} - -void EaxChorusEffect::validate_all( - const EAXCHORUSPROPERTIES& eax_all) -{ - validate_waveform(eax_all.ulWaveform); - validate_phase(eax_all.lPhase); - validate_rate(eax_all.flRate); - validate_depth(eax_all.flDepth); - validate_feedback(eax_all.flFeedback); - validate_delay(eax_all.flDelay); -} - -void EaxChorusEffect::defer_waveform( - unsigned long ulWaveform) -{ - eax_d_.ulWaveform = ulWaveform; - eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform); -} - -void EaxChorusEffect::defer_phase( - long lPhase) -{ - eax_d_.lPhase = lPhase; - eax_dirty_flags_.lPhase = (eax_.lPhase != eax_d_.lPhase); -} - -void EaxChorusEffect::defer_rate( - float flRate) -{ - eax_d_.flRate = flRate; - eax_dirty_flags_.flRate = (eax_.flRate != eax_d_.flRate); -} - -void EaxChorusEffect::defer_depth( - float flDepth) -{ - eax_d_.flDepth = flDepth; - eax_dirty_flags_.flDepth = (eax_.flDepth != eax_d_.flDepth); -} - -void EaxChorusEffect::defer_feedback( - float flFeedback) -{ - eax_d_.flFeedback = flFeedback; - eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback); -} - -void EaxChorusEffect::defer_delay( - float flDelay) -{ - eax_d_.flDelay = flDelay; - eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay); -} - -void EaxChorusEffect::defer_all( - const EAXCHORUSPROPERTIES& eax_all) -{ - defer_waveform(eax_all.ulWaveform); - defer_phase(eax_all.lPhase); - defer_rate(eax_all.flRate); - defer_depth(eax_all.flDepth); - defer_feedback(eax_all.flFeedback); - defer_delay(eax_all.flDelay); -} - -void EaxChorusEffect::defer_waveform( - const EaxEaxCall& eax_call) -{ - const auto& waveform = - eax_call.get_value(); - - validate_waveform(waveform); - defer_waveform(waveform); -} - -void EaxChorusEffect::defer_phase( - const EaxEaxCall& eax_call) -{ - const auto& phase = - eax_call.get_value(); - - validate_phase(phase); - defer_phase(phase); -} - -void EaxChorusEffect::defer_rate( - const EaxEaxCall& eax_call) -{ - const auto& rate = - eax_call.get_value(); - - validate_rate(rate); - defer_rate(rate); -} - -void EaxChorusEffect::defer_depth( - const EaxEaxCall& eax_call) -{ - const auto& depth = - eax_call.get_value(); - - validate_depth(depth); - defer_depth(depth); -} - -void EaxChorusEffect::defer_feedback( - const EaxEaxCall& eax_call) -{ - const auto& feedback = - eax_call.get_value(); - - validate_feedback(feedback); - defer_feedback(feedback); -} - -void EaxChorusEffect::defer_delay( - const EaxEaxCall& eax_call) -{ - const auto& delay = - eax_call.get_value(); - validate_delay(delay); - defer_delay(delay); -} - -void EaxChorusEffect::defer_all( - const EaxEaxCall& eax_call) -{ - const auto& all = - eax_call.get_value(); - - validate_all(all); - defer_all(all); -} - -// [[nodiscard]] -bool EaxChorusEffect::apply_deferred() -{ - if (eax_dirty_flags_ == EaxChorusEffectDirtyFlags{}) + void set_efx_phase() noexcept { - return false; + Base::al_effect_props_.Chorus.Phase = clamp( + static_cast(Base::props_.lPhase), + Traits::efx_min_phase(), + Traits::efx_max_phase()); } - eax_ = eax_d_; - - if (eax_dirty_flags_.ulWaveform) + void set_efx_rate() noexcept { - set_efx_waveform(); + Base::al_effect_props_.Chorus.Rate = clamp( + Base::props_.flRate, + Traits::efx_min_rate(), + Traits::efx_max_rate()); } - if (eax_dirty_flags_.lPhase) + void set_efx_depth() noexcept { - set_efx_phase(); + Base::al_effect_props_.Chorus.Depth = clamp( + Base::props_.flDepth, + Traits::efx_min_depth(), + Traits::efx_max_depth()); } - if (eax_dirty_flags_.flRate) + void set_efx_feedback() noexcept { - set_efx_rate(); + Base::al_effect_props_.Chorus.Feedback = clamp( + Base::props_.flFeedback, + Traits::efx_min_feedback(), + Traits::efx_max_feedback()); } - if (eax_dirty_flags_.flDepth) + void set_efx_delay() noexcept { - set_efx_depth(); + Base::al_effect_props_.Chorus.Delay = clamp( + Base::props_.flDelay, + Traits::efx_min_delay(), + Traits::efx_max_delay()); } - if (eax_dirty_flags_.flFeedback) + void set_efx_defaults() override { + set_efx_waveform(); + set_efx_phase(); + set_efx_rate(); + set_efx_depth(); set_efx_feedback(); - } - - if (eax_dirty_flags_.flDelay) - { set_efx_delay(); } - eax_dirty_flags_ = EaxChorusEffectDirtyFlags{}; - - return true; -} - -void EaxChorusEffect::set(const EaxEaxCall& eax_call) -{ - switch(eax_call.get_property_id()) + void get(const EaxCall& call, const Props& props) override { - case EAXCHORUS_NONE: - break; - - case EAXCHORUS_ALLPARAMETERS: - defer_all(eax_call); - break; - - case EAXCHORUS_WAVEFORM: - defer_waveform(eax_call); - break; - - case EAXCHORUS_PHASE: - defer_phase(eax_call); - break; - - case EAXCHORUS_RATE: - defer_rate(eax_call); - break; - - case EAXCHORUS_DEPTH: - defer_depth(eax_call); - break; - - case EAXCHORUS_FEEDBACK: - defer_feedback(eax_call); - break; - - case EAXCHORUS_DELAY: - defer_delay(eax_call); - break; - - default: - throw EaxChorusEffectException{"Unsupported property id."}; + switch(call.get_property_id()) + { + case Traits::eax_none_param_id(): + break; + + case Traits::eax_allparameters_param_id(): + call.template set_value(props); + break; + + case Traits::eax_waveform_param_id(): + call.template set_value(props.ulWaveform); + break; + + case Traits::eax_phase_param_id(): + call.template set_value(props.lPhase); + break; + + case Traits::eax_rate_param_id(): + call.template set_value(props.flRate); + break; + + case Traits::eax_depth_param_id(): + call.template set_value(props.flDepth); + break; + + case Traits::eax_feedback_param_id(): + call.template set_value(props.flFeedback); + break; + + case Traits::eax_delay_param_id(): + call.template set_value(props.flDelay); + break; + + default: + Base::fail_unknown_property_id(); + } } -} - - -} // namespace - -EaxEffectUPtr eax_create_eax_chorus_effect() -{ - return std::make_unique<::EaxChorusEffect>(); -} - - -namespace -{ - - -using EaxFlangerEffectDirtyFlagsValue = std::uint_least8_t; - -struct EaxFlangerEffectDirtyFlags -{ - using EaxIsBitFieldStruct = bool; - - EaxFlangerEffectDirtyFlagsValue ulWaveform : 1; - EaxFlangerEffectDirtyFlagsValue lPhase : 1; - EaxFlangerEffectDirtyFlagsValue flRate : 1; - EaxFlangerEffectDirtyFlagsValue flDepth : 1; - EaxFlangerEffectDirtyFlagsValue flFeedback : 1; - EaxFlangerEffectDirtyFlagsValue flDelay : 1; -}; // EaxFlangerEffectDirtyFlags - - -class EaxFlangerEffect final : - public EaxEffect -{ -public: - EaxFlangerEffect(); - - - void dispatch(const EaxEaxCall& eax_call) override; - - // [[nodiscard]] - bool apply_deferred() override; - -private: - EAXFLANGERPROPERTIES eax_{}; - EAXFLANGERPROPERTIES eax_d_{}; - EaxFlangerEffectDirtyFlags eax_dirty_flags_{}; - - void set_eax_defaults(); - - void set_efx_waveform(); - void set_efx_phase(); - void set_efx_rate(); - void set_efx_depth(); - void set_efx_feedback(); - void set_efx_delay(); - void set_efx_defaults(); - - void get(const EaxEaxCall& eax_call); - - void validate_waveform(unsigned long ulWaveform); - void validate_phase(long lPhase); - void validate_rate(float flRate); - void validate_depth(float flDepth); - void validate_feedback(float flFeedback); - void validate_delay(float flDelay); - void validate_all(const EAXFLANGERPROPERTIES& all); - - void defer_waveform(unsigned long ulWaveform); - void defer_phase(long lPhase); - void defer_rate(float flRate); - void defer_depth(float flDepth); - void defer_feedback(float flFeedback); - void defer_delay(float flDelay); - void defer_all(const EAXFLANGERPROPERTIES& all); - - void defer_waveform(const EaxEaxCall& eax_call); - void defer_phase(const EaxEaxCall& eax_call); - void defer_rate(const EaxEaxCall& eax_call); - void defer_depth(const EaxEaxCall& eax_call); - void defer_feedback(const EaxEaxCall& eax_call); - void defer_delay(const EaxEaxCall& eax_call); - void defer_all(const EaxEaxCall& eax_call); - - void set(const EaxEaxCall& eax_call); -}; // EaxFlangerEffect - - -class EaxFlangerEffectException : - public EaxException -{ -public: - explicit EaxFlangerEffectException( - const char* message) - : - EaxException{"EAX_FLANGER_EFFECT", message} + void set(const EaxCall& call, Props& props) override { + switch(call.get_property_id()) + { + case Traits::eax_none_param_id(): + break; + + case Traits::eax_allparameters_param_id(): + Base::template defer(call, props); + break; + + case Traits::eax_waveform_param_id(): + Base::template defer(call, props.ulWaveform); + break; + + case Traits::eax_phase_param_id(): + Base::template defer(call, props.lPhase); + break; + + case Traits::eax_rate_param_id(): + Base::template defer(call, props.flRate); + break; + + case Traits::eax_depth_param_id(): + Base::template defer(call, props.flDepth); + break; + + case Traits::eax_feedback_param_id(): + Base::template defer(call, props.flFeedback); + break; + + case Traits::eax_delay_param_id(): + Base::template defer(call, props.flDelay); + break; + + default: + Base::fail_unknown_property_id(); + } } -}; // EaxFlangerEffectException - - -EaxFlangerEffect::EaxFlangerEffect() - : EaxEffect{AL_EFFECT_FLANGER} -{ - set_eax_defaults(); - set_efx_defaults(); -} - -void EaxFlangerEffect::dispatch(const EaxEaxCall& eax_call) -{ - eax_call.is_get() ? get(eax_call) : set(eax_call); -} - -void EaxFlangerEffect::set_eax_defaults() -{ - eax_.ulWaveform = EAXFLANGER_DEFAULTWAVEFORM; - eax_.lPhase = EAXFLANGER_DEFAULTPHASE; - eax_.flRate = EAXFLANGER_DEFAULTRATE; - eax_.flDepth = EAXFLANGER_DEFAULTDEPTH; - eax_.flFeedback = EAXFLANGER_DEFAULTFEEDBACK; - eax_.flDelay = EAXFLANGER_DEFAULTDELAY; - - eax_d_ = eax_; -} - -void EaxFlangerEffect::set_efx_waveform() -{ - const auto waveform = clamp( - static_cast(eax_.ulWaveform), - AL_FLANGER_MIN_WAVEFORM, - AL_FLANGER_MAX_WAVEFORM); - - eax_set_efx_waveform(waveform, al_effect_props_); -} - -void EaxFlangerEffect::set_efx_phase() -{ - const auto phase = clamp( - static_cast(eax_.lPhase), - AL_FLANGER_MIN_PHASE, - AL_FLANGER_MAX_PHASE); - - eax_set_efx_phase(phase, al_effect_props_); -} - -void EaxFlangerEffect::set_efx_rate() -{ - const auto rate = clamp( - eax_.flRate, - AL_FLANGER_MIN_RATE, - AL_FLANGER_MAX_RATE); - - eax_set_efx_rate(rate, al_effect_props_); -} - -void EaxFlangerEffect::set_efx_depth() -{ - const auto depth = clamp( - eax_.flDepth, - AL_FLANGER_MIN_DEPTH, - AL_FLANGER_MAX_DEPTH); - - eax_set_efx_depth(depth, al_effect_props_); -} - -void EaxFlangerEffect::set_efx_feedback() -{ - const auto feedback = clamp( - eax_.flFeedback, - AL_FLANGER_MIN_FEEDBACK, - AL_FLANGER_MAX_FEEDBACK); - - eax_set_efx_feedback(feedback, al_effect_props_); -} - -void EaxFlangerEffect::set_efx_delay() -{ - const auto delay = clamp( - eax_.flDelay, - AL_FLANGER_MIN_DELAY, - AL_FLANGER_MAX_DELAY); - eax_set_efx_delay(delay, al_effect_props_); -} - -void EaxFlangerEffect::set_efx_defaults() -{ - set_efx_waveform(); - set_efx_phase(); - set_efx_rate(); - set_efx_depth(); - set_efx_feedback(); - set_efx_delay(); -} - -void EaxFlangerEffect::get(const EaxEaxCall& eax_call) -{ - switch(eax_call.get_property_id()) + bool commit_props(const Props& props) override { - case EAXFLANGER_NONE: - break; - - case EAXFLANGER_ALLPARAMETERS: - eax_call.set_value(eax_); - break; - - case EAXFLANGER_WAVEFORM: - eax_call.set_value(eax_.ulWaveform); - break; - - case EAXFLANGER_PHASE: - eax_call.set_value(eax_.lPhase); - break; - - case EAXFLANGER_RATE: - eax_call.set_value(eax_.flRate); - break; - - case EAXFLANGER_DEPTH: - eax_call.set_value(eax_.flDepth); - break; - - case EAXFLANGER_FEEDBACK: - eax_call.set_value(eax_.flFeedback); - break; - - case EAXFLANGER_DELAY: - eax_call.set_value(eax_.flDelay); - break; - - default: - throw EaxFlangerEffectException{"Unsupported property id."}; + auto is_dirty = false; + + if (Base::props_.ulWaveform != props.ulWaveform) + { + is_dirty = true; + set_efx_waveform(); + } + + if (Base::props_.lPhase != props.lPhase) + { + is_dirty = true; + set_efx_phase(); + } + + if (Base::props_.flRate != props.flRate) + { + is_dirty = true; + set_efx_rate(); + } + + if (Base::props_.flDepth != props.flDepth) + { + is_dirty = true; + set_efx_depth(); + } + + if (Base::props_.flFeedback != props.flFeedback) + { + is_dirty = true; + set_efx_feedback(); + } + + if (Base::props_.flDelay != props.flDelay) + { + is_dirty = true; + set_efx_delay(); + } + + return is_dirty; } -} - -void EaxFlangerEffect::validate_waveform( - unsigned long ulWaveform) -{ - eax_validate_range( - "Waveform", - ulWaveform, - EAXFLANGER_MINWAVEFORM, - EAXFLANGER_MAXWAVEFORM); -} - -void EaxFlangerEffect::validate_phase( - long lPhase) -{ - eax_validate_range( - "Phase", - lPhase, - EAXFLANGER_MINPHASE, - EAXFLANGER_MAXPHASE); -} - -void EaxFlangerEffect::validate_rate( - float flRate) -{ - eax_validate_range( - "Rate", - flRate, - EAXFLANGER_MINRATE, - EAXFLANGER_MAXRATE); -} - -void EaxFlangerEffect::validate_depth( - float flDepth) -{ - eax_validate_range( - "Depth", - flDepth, - EAXFLANGER_MINDEPTH, - EAXFLANGER_MAXDEPTH); -} - -void EaxFlangerEffect::validate_feedback( - float flFeedback) -{ - eax_validate_range( - "Feedback", - flFeedback, - EAXFLANGER_MINFEEDBACK, - EAXFLANGER_MAXFEEDBACK); -} - -void EaxFlangerEffect::validate_delay( - float flDelay) -{ - eax_validate_range( - "Delay", - flDelay, - EAXFLANGER_MINDELAY, - EAXFLANGER_MAXDELAY); -} - -void EaxFlangerEffect::validate_all( - const EAXFLANGERPROPERTIES& all) -{ - validate_waveform(all.ulWaveform); - validate_phase(all.lPhase); - validate_rate(all.flRate); - validate_depth(all.flDepth); - validate_feedback(all.flDelay); - validate_delay(all.flDelay); -} +}; // EaxChorusFlangerEffect -void EaxFlangerEffect::defer_waveform( - unsigned long ulWaveform) +template +EaxEffectUPtr eax_create_eax_chorus_flanger_effect(const EaxCall& call) { - eax_d_.ulWaveform = ulWaveform; - eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform); + return eax_create_eax4_effect>(call); } -void EaxFlangerEffect::defer_phase( - long lPhase) -{ - eax_d_.lPhase = lPhase; - eax_dirty_flags_.lPhase = (eax_.lPhase != eax_d_.lPhase); -} - -void EaxFlangerEffect::defer_rate( - float flRate) -{ - eax_d_.flRate = flRate; - eax_dirty_flags_.flRate = (eax_.flRate != eax_d_.flRate); -} - -void EaxFlangerEffect::defer_depth( - float flDepth) -{ - eax_d_.flDepth = flDepth; - eax_dirty_flags_.flDepth = (eax_.flDepth != eax_d_.flDepth); -} - -void EaxFlangerEffect::defer_feedback( - float flFeedback) -{ - eax_d_.flFeedback = flFeedback; - eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback); -} - -void EaxFlangerEffect::defer_delay( - float flDelay) -{ - eax_d_.flDelay = flDelay; - eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay); -} - -void EaxFlangerEffect::defer_all( - const EAXFLANGERPROPERTIES& all) -{ - defer_waveform(all.ulWaveform); - defer_phase(all.lPhase); - defer_rate(all.flRate); - defer_depth(all.flDepth); - defer_feedback(all.flDelay); - defer_delay(all.flDelay); -} - -void EaxFlangerEffect::defer_waveform( - const EaxEaxCall& eax_call) -{ - const auto& waveform = - eax_call.get_value(); - - validate_waveform(waveform); - defer_waveform(waveform); -} - -void EaxFlangerEffect::defer_phase( - const EaxEaxCall& eax_call) -{ - const auto& phase = - eax_call.get_value(); - - validate_phase(phase); - defer_phase(phase); -} - -void EaxFlangerEffect::defer_rate( - const EaxEaxCall& eax_call) -{ - const auto& rate = - eax_call.get_value(); - - validate_rate(rate); - defer_rate(rate); -} - -void EaxFlangerEffect::defer_depth( - const EaxEaxCall& eax_call) -{ - const auto& depth = - eax_call.get_value(); - - validate_depth(depth); - defer_depth(depth); -} - -void EaxFlangerEffect::defer_feedback( - const EaxEaxCall& eax_call) -{ - const auto& feedback = - eax_call.get_value(); - - validate_feedback(feedback); - defer_feedback(feedback); -} - -void EaxFlangerEffect::defer_delay( - const EaxEaxCall& eax_call) -{ - const auto& delay = - eax_call.get_value(); - - validate_delay(delay); - defer_delay(delay); -} - -void EaxFlangerEffect::defer_all( - const EaxEaxCall& eax_call) -{ - const auto& all = - eax_call.get_value(); - - validate_all(all); - defer_all(all); -} - -// [[nodiscard]] -bool EaxFlangerEffect::apply_deferred() -{ - if (eax_dirty_flags_ == EaxFlangerEffectDirtyFlags{}) - { - return false; - } - - eax_ = eax_d_; - - if (eax_dirty_flags_.ulWaveform) - { - set_efx_waveform(); - } - - if (eax_dirty_flags_.lPhase) - { - set_efx_phase(); - } - - if (eax_dirty_flags_.flRate) - { - set_efx_rate(); - } - - if (eax_dirty_flags_.flDepth) - { - set_efx_depth(); - } - - if (eax_dirty_flags_.flFeedback) - { - set_efx_feedback(); - } - - if (eax_dirty_flags_.flDelay) - { - set_efx_delay(); - } +} // namespace - eax_dirty_flags_ = EaxFlangerEffectDirtyFlags{}; +// ========================================================================== - return true; -} - -void EaxFlangerEffect::set(const EaxEaxCall& eax_call) +EaxEffectUPtr eax_create_eax_chorus_effect(const EaxCall& call) { - switch(eax_call.get_property_id()) - { - case EAXFLANGER_NONE: - break; - - case EAXFLANGER_ALLPARAMETERS: - defer_all(eax_call); - break; - - case EAXFLANGER_WAVEFORM: - defer_waveform(eax_call); - break; - - case EAXFLANGER_PHASE: - defer_phase(eax_call); - break; - - case EAXFLANGER_RATE: - defer_rate(eax_call); - break; - - case EAXFLANGER_DEPTH: - defer_depth(eax_call); - break; - - case EAXFLANGER_FEEDBACK: - defer_feedback(eax_call); - break; - - case EAXFLANGER_DELAY: - defer_delay(eax_call); - break; - - default: - throw EaxFlangerEffectException{"Unsupported property id."}; - } + return eax_create_eax_chorus_flanger_effect(call); } -} // namespace - -EaxEffectUPtr eax_create_eax_flanger_effect() +EaxEffectUPtr eax_create_eax_flanger_effect(const EaxCall& call) { - return std::make_unique(); + return eax_create_eax_chorus_flanger_effect(call); } #endif // ALSOFT_EAX diff --git a/al/effects/compressor.cpp b/al/effects/compressor.cpp index a18609ca..9824d11b 100644 --- a/al/effects/compressor.cpp +++ b/al/effects/compressor.cpp @@ -9,7 +9,6 @@ #ifdef ALSOFT_EAX #include "alnumeric.h" - #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -81,94 +80,63 @@ const EffectProps CompressorEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { -using EaxCompressorEffectDirtyFlagsValue = std::uint_least8_t; - -struct EaxCompressorEffectDirtyFlags +class EaxCompressorEffectException : public EaxException { - using EaxIsBitFieldStruct = bool; - - EaxCompressorEffectDirtyFlagsValue ulOnOff : 1; -}; // EaxCompressorEffectDirtyFlags - +public: + explicit EaxCompressorEffectException(const char* message) + : EaxException{"EAX_COMPRESSOR_EFFECT", message} + {} +}; // EaxCompressorEffectException -class EaxCompressorEffect final : - public EaxEffect +class EaxCompressorEffect final : public EaxEffect4 { public: - EaxCompressorEffect(); - - - void dispatch(const EaxEaxCall& eax_call) override; - - // [[nodiscard]] - bool apply_deferred() override; + EaxCompressorEffect(const EaxCall& call); private: - EAXAGCCOMPRESSORPROPERTIES eax_{}; - EAXAGCCOMPRESSORPROPERTIES eax_d_{}; - EaxCompressorEffectDirtyFlags eax_dirty_flags_{}; - - - void set_eax_defaults(); - - void set_efx_on_off(); - void set_efx_defaults(); - - void get(const EaxEaxCall& eax_call); - - void validate_on_off(unsigned long ulOnOff); - void validate_all(const EAXAGCCOMPRESSORPROPERTIES& eax_all); - - void defer_on_off(unsigned long ulOnOff); - void defer_all(const EAXAGCCOMPRESSORPROPERTIES& eax_all); - - void defer_on_off(const EaxEaxCall& eax_call); - void defer_all(const EaxEaxCall& eax_call); - - void set(const EaxEaxCall& eax_call); + struct OnOffValidator { + void operator()(unsigned long ulOnOff) const + { + eax_validate_range( + "On-Off", + ulOnOff, + EAXAGCCOMPRESSOR_MINONOFF, + EAXAGCCOMPRESSOR_MAXONOFF); + } + }; // OnOffValidator + + struct AllValidator { + void operator()(const Props& all) const + { + OnOffValidator{}(all.ulOnOff); + } + }; // AllValidator + + void set_defaults(Props& props) override; + + void set_efx_on_off() noexcept; + void set_efx_defaults() override; + + void get(const EaxCall& call, const Props& props) override; + void set(const EaxCall& call, Props& props) override; + bool commit_props(const Props& props) override; }; // EaxCompressorEffect +EaxCompressorEffect::EaxCompressorEffect(const EaxCall& call) + : EaxEffect4{AL_EFFECT_COMPRESSOR, call} +{} -class EaxCompressorEffectException : - public EaxException +void EaxCompressorEffect::set_defaults(Props& props) { -public: - explicit EaxCompressorEffectException( - const char* message) - : - EaxException{"EAX_COMPRESSOR_EFFECT", message} - { - } -}; // EaxCompressorEffectException - - -EaxCompressorEffect::EaxCompressorEffect() - : EaxEffect{AL_EFFECT_COMPRESSOR} -{ - set_eax_defaults(); - set_efx_defaults(); -} - -// [[nodiscard]] -void EaxCompressorEffect::dispatch(const EaxEaxCall& eax_call) -{ - eax_call.is_get() ? get(eax_call) : set(eax_call); -} - -void EaxCompressorEffect::set_eax_defaults() -{ - eax_.ulOnOff = EAXAGCCOMPRESSOR_DEFAULTONOFF; - - eax_d_ = eax_; + props.ulOnOff = EAXAGCCOMPRESSOR_DEFAULTONOFF; } -void EaxCompressorEffect::set_efx_on_off() +void EaxCompressorEffect::set_efx_on_off() noexcept { const auto on_off = clamp( - static_cast(eax_.ulOnOff), + static_cast(props_.ulOnOff), AL_COMPRESSOR_MIN_ONOFF, AL_COMPRESSOR_MAX_ONOFF); - al_effect_props_.Compressor.OnOff = (on_off != AL_FALSE); } @@ -177,120 +145,46 @@ void EaxCompressorEffect::set_efx_defaults() set_efx_on_off(); } -void EaxCompressorEffect::get(const EaxEaxCall& eax_call) +void EaxCompressorEffect::get(const EaxCall& call, const Props& props) { - switch(eax_call.get_property_id()) + switch(call.get_property_id()) { - case EAXAGCCOMPRESSOR_NONE: - break; - - case EAXAGCCOMPRESSOR_ALLPARAMETERS: - eax_call.set_value(eax_); - break; - - case EAXAGCCOMPRESSOR_ONOFF: - eax_call.set_value(eax_.ulOnOff); - break; - - default: - throw EaxCompressorEffectException{"Unsupported property id."}; + case EAXAGCCOMPRESSOR_NONE: break; + case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value(props); break; + case EAXAGCCOMPRESSOR_ONOFF: call.set_value(props.ulOnOff); break; + default: fail_unknown_property_id(); } } -void EaxCompressorEffect::validate_on_off( - unsigned long ulOnOff) -{ - eax_validate_range( - "On-Off", - ulOnOff, - EAXAGCCOMPRESSOR_MINONOFF, - EAXAGCCOMPRESSOR_MAXONOFF); -} - -void EaxCompressorEffect::validate_all( - const EAXAGCCOMPRESSORPROPERTIES& eax_all) +void EaxCompressorEffect::set(const EaxCall& call, Props& props) { - validate_on_off(eax_all.ulOnOff); -} - -void EaxCompressorEffect::defer_on_off( - unsigned long ulOnOff) -{ - eax_d_.ulOnOff = ulOnOff; - eax_dirty_flags_.ulOnOff = (eax_.ulOnOff != eax_d_.ulOnOff); -} - -void EaxCompressorEffect::defer_all( - const EAXAGCCOMPRESSORPROPERTIES& eax_all) -{ - defer_on_off(eax_all.ulOnOff); -} - -void EaxCompressorEffect::defer_on_off( - const EaxEaxCall& eax_call) -{ - const auto& on_off = - eax_call.get_value(); - - validate_on_off(on_off); - defer_on_off(on_off); -} - -void EaxCompressorEffect::defer_all( - const EaxEaxCall& eax_call) -{ - const auto& all = - eax_call.get_value(); - - validate_all(all); - defer_all(all); -} - -// [[nodiscard]] -bool EaxCompressorEffect::apply_deferred() -{ - if (eax_dirty_flags_ == EaxCompressorEffectDirtyFlags{}) + switch(call.get_property_id()) { - return false; + case EAXAGCCOMPRESSOR_NONE: break; + case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer(call, props); break; + case EAXAGCCOMPRESSOR_ONOFF: defer(call, props.ulOnOff); break; + default: fail_unknown_property_id(); } +} - eax_ = eax_d_; +bool EaxCompressorEffect::commit_props(const Props& props) +{ + auto is_dirty = false; - if (eax_dirty_flags_.ulOnOff) + if (props_.ulOnOff != props.ulOnOff) { + is_dirty = true; set_efx_on_off(); } - eax_dirty_flags_ = EaxCompressorEffectDirtyFlags{}; - - return true; -} - -void EaxCompressorEffect::set(const EaxEaxCall& eax_call) -{ - switch(eax_call.get_property_id()) - { - case EAXAGCCOMPRESSOR_NONE: - break; - - case EAXAGCCOMPRESSOR_ALLPARAMETERS: - defer_all(eax_call); - break; - - case EAXAGCCOMPRESSOR_ONOFF: - defer_on_off(eax_call); - break; - - default: - throw EaxCompressorEffectException{"Unsupported property id."}; - } + return is_dirty; } } // namespace -EaxEffectUPtr eax_create_eax_compressor_effect() +EaxEffectUPtr eax_create_eax_compressor_effect(const EaxCall& call) { - return std::make_unique(); + return eax_create_eax4_effect(call); } #endif // ALSOFT_EAX diff --git a/al/effects/distortion.cpp b/al/effects/distortion.cpp index 441b89a0..b58412b9 100644 --- a/al/effects/distortion.cpp +++ b/al/effects/distortion.cpp @@ -9,7 +9,6 @@ #ifdef ALSOFT_EAX #include "alnumeric.h" - #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -123,156 +122,151 @@ const EffectProps DistortionEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { -using EaxDistortionEffectDirtyFlagsValue = std::uint_least8_t; - -struct EaxDistortionEffectDirtyFlags +class EaxDistortionEffectException : public EaxException { - using EaxIsBitFieldStruct = bool; - - EaxDistortionEffectDirtyFlagsValue flEdge : 1; - EaxDistortionEffectDirtyFlagsValue lGain : 1; - EaxDistortionEffectDirtyFlagsValue flLowPassCutOff : 1; - EaxDistortionEffectDirtyFlagsValue flEQCenter : 1; - EaxDistortionEffectDirtyFlagsValue flEQBandwidth : 1; -}; // EaxDistortionEffectDirtyFlags - +public: + explicit EaxDistortionEffectException(const char* message) + : EaxException{"EAX_DISTORTION_EFFECT", message} + {} +}; // EaxDistortionEffectException -class EaxDistortionEffect final : - public EaxEffect +class EaxDistortionEffect final : public EaxEffect4 { public: - EaxDistortionEffect(); - - void dispatch(const EaxEaxCall& eax_call) override; - - // [[nodiscard]] - bool apply_deferred() override; + EaxDistortionEffect(const EaxCall& call); private: - EAXDISTORTIONPROPERTIES eax_{}; - EAXDISTORTIONPROPERTIES eax_d_{}; - EaxDistortionEffectDirtyFlags eax_dirty_flags_{}; - - void set_eax_defaults(); - - void set_efx_edge(); - void set_efx_gain(); - void set_efx_lowpass_cutoff(); - void set_efx_eq_center(); - void set_efx_eq_bandwidth(); - void set_efx_defaults(); - - void get(const EaxEaxCall& eax_call); - - void validate_edge(float flEdge); - void validate_gain(long lGain); - void validate_lowpass_cutoff(float flLowPassCutOff); - void validate_eq_center(float flEQCenter); - void validate_eq_bandwidth(float flEQBandwidth); - void validate_all(const EAXDISTORTIONPROPERTIES& eax_all); - - void defer_edge(float flEdge); - void defer_gain(long lGain); - void defer_low_pass_cutoff(float flLowPassCutOff); - void defer_eq_center(float flEQCenter); - void defer_eq_bandwidth(float flEQBandwidth); - void defer_all(const EAXDISTORTIONPROPERTIES& eax_all); - - void defer_edge(const EaxEaxCall& eax_call); - void defer_gain(const EaxEaxCall& eax_call); - void defer_low_pass_cutoff(const EaxEaxCall& eax_call); - void defer_eq_center(const EaxEaxCall& eax_call); - void defer_eq_bandwidth(const EaxEaxCall& eax_call); - void defer_all(const EaxEaxCall& eax_call); - - void set(const EaxEaxCall& eax_call); + struct EdgeValidator { + void operator()(float flEdge) const + { + eax_validate_range( + "Edge", + flEdge, + EAXDISTORTION_MINEDGE, + EAXDISTORTION_MAXEDGE); + } + }; // EdgeValidator + + struct GainValidator { + void operator()(long lGain) const + { + eax_validate_range( + "Gain", + lGain, + EAXDISTORTION_MINGAIN, + EAXDISTORTION_MAXGAIN); + } + }; // GainValidator + + struct LowPassCutOffValidator { + void operator()(float flLowPassCutOff) const + { + eax_validate_range( + "Low-pass Cut-off", + flLowPassCutOff, + EAXDISTORTION_MINLOWPASSCUTOFF, + EAXDISTORTION_MAXLOWPASSCUTOFF); + } + }; // LowPassCutOffValidator + + struct EqCenterValidator { + void operator()(float flEQCenter) const + { + eax_validate_range( + "EQ Center", + flEQCenter, + EAXDISTORTION_MINEQCENTER, + EAXDISTORTION_MAXEQCENTER); + } + }; // EqCenterValidator + + struct EqBandwidthValidator { + void operator()(float flEQBandwidth) const + { + eax_validate_range( + "EQ Bandwidth", + flEQBandwidth, + EAXDISTORTION_MINEQBANDWIDTH, + EAXDISTORTION_MAXEQBANDWIDTH); + } + }; // EqBandwidthValidator + + struct AllValidator { + void operator()(const Props& all) const + { + EdgeValidator{}(all.flEdge); + GainValidator{}(all.lGain); + LowPassCutOffValidator{}(all.flLowPassCutOff); + EqCenterValidator{}(all.flEQCenter); + EqBandwidthValidator{}(all.flEQBandwidth); + } + }; // AllValidator + + void set_defaults(Props& props) override; + + void set_efx_edge() noexcept; + void set_efx_gain() noexcept; + void set_efx_lowpass_cutoff() noexcept; + void set_efx_eq_center() noexcept; + void set_efx_eq_bandwidth() noexcept; + void set_efx_defaults() override; + + void get(const EaxCall& call, const Props& props) override; + void set(const EaxCall& call, Props& props) override; + bool commit_props(const Props& props) override; }; // EaxDistortionEffect +EaxDistortionEffect::EaxDistortionEffect(const EaxCall& call) + : EaxEffect4{AL_EFFECT_DISTORTION, call} +{} -class EaxDistortionEffectException : - public EaxException -{ -public: - explicit EaxDistortionEffectException( - const char* message) - : - EaxException{"EAX_DISTORTION_EFFECT", message} - { - } -}; // EaxDistortionEffectException - - -EaxDistortionEffect::EaxDistortionEffect() - : EaxEffect{AL_EFFECT_DISTORTION} -{ - set_eax_defaults(); - set_efx_defaults(); -} - -void EaxDistortionEffect::dispatch(const EaxEaxCall& eax_call) -{ - eax_call.is_get() ? get(eax_call) : set(eax_call); -} - -void EaxDistortionEffect::set_eax_defaults() +void EaxDistortionEffect::set_defaults(Props& props) { - eax_.flEdge = EAXDISTORTION_DEFAULTEDGE; - eax_.lGain = EAXDISTORTION_DEFAULTGAIN; - eax_.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF; - eax_.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER; - eax_.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH; - - eax_d_ = eax_; + props.flEdge = EAXDISTORTION_DEFAULTEDGE; + props.lGain = EAXDISTORTION_DEFAULTGAIN; + props.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF; + props.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER; + props.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH; } -void EaxDistortionEffect::set_efx_edge() +void EaxDistortionEffect::set_efx_edge() noexcept { - const auto edge = clamp( - eax_.flEdge, + al_effect_props_.Distortion.Edge = clamp( + props_.flEdge, AL_DISTORTION_MIN_EDGE, AL_DISTORTION_MAX_EDGE); - - al_effect_props_.Distortion.Edge = edge; } -void EaxDistortionEffect::set_efx_gain() +void EaxDistortionEffect::set_efx_gain() noexcept { - const auto gain = clamp( - level_mb_to_gain(static_cast(eax_.lGain)), + al_effect_props_.Distortion.Gain = clamp( + level_mb_to_gain(static_cast(props_.lGain)), AL_DISTORTION_MIN_GAIN, AL_DISTORTION_MAX_GAIN); - - al_effect_props_.Distortion.Gain = gain; } -void EaxDistortionEffect::set_efx_lowpass_cutoff() +void EaxDistortionEffect::set_efx_lowpass_cutoff() noexcept { - const auto lowpass_cutoff = clamp( - eax_.flLowPassCutOff, + al_effect_props_.Distortion.LowpassCutoff = clamp( + props_.flLowPassCutOff, AL_DISTORTION_MIN_LOWPASS_CUTOFF, AL_DISTORTION_MAX_LOWPASS_CUTOFF); - - al_effect_props_.Distortion.LowpassCutoff = lowpass_cutoff; } -void EaxDistortionEffect::set_efx_eq_center() +void EaxDistortionEffect::set_efx_eq_center() noexcept { - const auto eq_center = clamp( - eax_.flEQCenter, + al_effect_props_.Distortion.EQCenter = clamp( + props_.flEQCenter, AL_DISTORTION_MIN_EQCENTER, AL_DISTORTION_MAX_EQCENTER); - - al_effect_props_.Distortion.EQCenter = eq_center; } -void EaxDistortionEffect::set_efx_eq_bandwidth() +void EaxDistortionEffect::set_efx_eq_bandwidth() noexcept { - const auto eq_bandwidth = clamp( - eax_.flEdge, + al_effect_props_.Distortion.EQBandwidth = clamp( + props_.flEdge, AL_DISTORTION_MIN_EQBANDWIDTH, AL_DISTORTION_MAX_EQBANDWIDTH); - - al_effect_props_.Distortion.EQBandwidth = eq_bandwidth; } void EaxDistortionEffect::set_efx_defaults() @@ -284,288 +278,78 @@ void EaxDistortionEffect::set_efx_defaults() set_efx_eq_bandwidth(); } -void EaxDistortionEffect::get(const EaxEaxCall& eax_call) +void EaxDistortionEffect::get(const EaxCall& call, const Props& props) { - switch(eax_call.get_property_id()) + switch(call.get_property_id()) { - case EAXDISTORTION_NONE: - break; - - case EAXDISTORTION_ALLPARAMETERS: - eax_call.set_value(eax_); - break; - - case EAXDISTORTION_EDGE: - eax_call.set_value(eax_.flEdge); - break; - - case EAXDISTORTION_GAIN: - eax_call.set_value(eax_.lGain); - break; - - case EAXDISTORTION_LOWPASSCUTOFF: - eax_call.set_value(eax_.flLowPassCutOff); - break; - - case EAXDISTORTION_EQCENTER: - eax_call.set_value(eax_.flEQCenter); - break; - - case EAXDISTORTION_EQBANDWIDTH: - eax_call.set_value(eax_.flEQBandwidth); - break; - - default: - throw EaxDistortionEffectException{"Unsupported property id."}; + case EAXDISTORTION_NONE: break; + case EAXDISTORTION_ALLPARAMETERS: call.set_value(props); break; + case EAXDISTORTION_EDGE: call.set_value(props.flEdge); break; + case EAXDISTORTION_GAIN: call.set_value(props.lGain); break; + case EAXDISTORTION_LOWPASSCUTOFF: call.set_value(props.flLowPassCutOff); break; + case EAXDISTORTION_EQCENTER: call.set_value(props.flEQCenter); break; + case EAXDISTORTION_EQBANDWIDTH: call.set_value(props.flEQBandwidth); break; + default: fail_unknown_property_id(); } } -void EaxDistortionEffect::validate_edge( - float flEdge) -{ - eax_validate_range( - "Edge", - flEdge, - EAXDISTORTION_MINEDGE, - EAXDISTORTION_MAXEDGE); -} - -void EaxDistortionEffect::validate_gain( - long lGain) -{ - eax_validate_range( - "Gain", - lGain, - EAXDISTORTION_MINGAIN, - EAXDISTORTION_MAXGAIN); -} - -void EaxDistortionEffect::validate_lowpass_cutoff( - float flLowPassCutOff) -{ - eax_validate_range( - "Low-pass Cut-off", - flLowPassCutOff, - EAXDISTORTION_MINLOWPASSCUTOFF, - EAXDISTORTION_MAXLOWPASSCUTOFF); -} - -void EaxDistortionEffect::validate_eq_center( - float flEQCenter) -{ - eax_validate_range( - "EQ Center", - flEQCenter, - EAXDISTORTION_MINEQCENTER, - EAXDISTORTION_MAXEQCENTER); -} - -void EaxDistortionEffect::validate_eq_bandwidth( - float flEQBandwidth) -{ - eax_validate_range( - "EQ Bandwidth", - flEQBandwidth, - EAXDISTORTION_MINEQBANDWIDTH, - EAXDISTORTION_MAXEQBANDWIDTH); -} - -void EaxDistortionEffect::validate_all( - const EAXDISTORTIONPROPERTIES& eax_all) -{ - validate_edge(eax_all.flEdge); - validate_gain(eax_all.lGain); - validate_lowpass_cutoff(eax_all.flLowPassCutOff); - validate_eq_center(eax_all.flEQCenter); - validate_eq_bandwidth(eax_all.flEQBandwidth); -} - -void EaxDistortionEffect::defer_edge( - float flEdge) -{ - eax_d_.flEdge = flEdge; - eax_dirty_flags_.flEdge = (eax_.flEdge != eax_d_.flEdge); -} - -void EaxDistortionEffect::defer_gain( - long lGain) +void EaxDistortionEffect::set(const EaxCall& call, Props& props) { - eax_d_.lGain = lGain; - eax_dirty_flags_.lGain = (eax_.lGain != eax_d_.lGain); -} - -void EaxDistortionEffect::defer_low_pass_cutoff( - float flLowPassCutOff) -{ - eax_d_.flLowPassCutOff = flLowPassCutOff; - eax_dirty_flags_.flLowPassCutOff = (eax_.flLowPassCutOff != eax_d_.flLowPassCutOff); -} - -void EaxDistortionEffect::defer_eq_center( - float flEQCenter) -{ - eax_d_.flEQCenter = flEQCenter; - eax_dirty_flags_.flEQCenter = (eax_.flEQCenter != eax_d_.flEQCenter); -} - -void EaxDistortionEffect::defer_eq_bandwidth( - float flEQBandwidth) -{ - eax_d_.flEQBandwidth = flEQBandwidth; - eax_dirty_flags_.flEQBandwidth = (eax_.flEQBandwidth != eax_d_.flEQBandwidth); -} - -void EaxDistortionEffect::defer_all( - const EAXDISTORTIONPROPERTIES& eax_all) -{ - defer_edge(eax_all.flEdge); - defer_gain(eax_all.lGain); - defer_low_pass_cutoff(eax_all.flLowPassCutOff); - defer_eq_center(eax_all.flEQCenter); - defer_eq_bandwidth(eax_all.flEQBandwidth); -} - -void EaxDistortionEffect::defer_edge( - const EaxEaxCall& eax_call) -{ - const auto& edge = - eax_call.get_value(); - - validate_edge(edge); - defer_edge(edge); -} - -void EaxDistortionEffect::defer_gain( - const EaxEaxCall& eax_call) -{ - const auto& gain = - eax_call.get_value(); - - validate_gain(gain); - defer_gain(gain); -} - -void EaxDistortionEffect::defer_low_pass_cutoff( - const EaxEaxCall& eax_call) -{ - const auto& lowpass_cutoff = - eax_call.get_value(); - - validate_lowpass_cutoff(lowpass_cutoff); - defer_low_pass_cutoff(lowpass_cutoff); -} - -void EaxDistortionEffect::defer_eq_center( - const EaxEaxCall& eax_call) -{ - const auto& eq_center = - eax_call.get_value(); - - validate_eq_center(eq_center); - defer_eq_center(eq_center); -} - -void EaxDistortionEffect::defer_eq_bandwidth( - const EaxEaxCall& eax_call) -{ - const auto& eq_bandwidth = - eax_call.get_value(); - - validate_eq_bandwidth(eq_bandwidth); - defer_eq_bandwidth(eq_bandwidth); -} - -void EaxDistortionEffect::defer_all( - const EaxEaxCall& eax_call) -{ - const auto& all = - eax_call.get_value(); - - validate_all(all); - defer_all(all); -} - -// [[nodiscard]] -bool EaxDistortionEffect::apply_deferred() -{ - if (eax_dirty_flags_ == EaxDistortionEffectDirtyFlags{}) + switch(call.get_property_id()) { - return false; + case EAXDISTORTION_NONE: break; + case EAXDISTORTION_ALLPARAMETERS: defer(call, props); break; + case EAXDISTORTION_EDGE: defer(call, props.flEdge); break; + case EAXDISTORTION_GAIN: defer(call, props.lGain); break; + case EAXDISTORTION_LOWPASSCUTOFF: defer(call, props.flLowPassCutOff); break; + case EAXDISTORTION_EQCENTER: defer(call, props.flEQCenter); break; + case EAXDISTORTION_EQBANDWIDTH: defer(call, props.flEQBandwidth); break; + default: fail_unknown_property_id(); } +} - eax_ = eax_d_; +bool EaxDistortionEffect::commit_props(const Props& props) +{ + auto is_dirty = false; - if (eax_dirty_flags_.flEdge) + if (props_.flEdge != props.flEdge) { + is_dirty = true; set_efx_edge(); } - if (eax_dirty_flags_.lGain) + if (props_.lGain != props.lGain) { + is_dirty = true; set_efx_gain(); } - if (eax_dirty_flags_.flLowPassCutOff) + if (props_.flLowPassCutOff != props.flLowPassCutOff) { + is_dirty = true; set_efx_lowpass_cutoff(); } - if (eax_dirty_flags_.flEQCenter) + if (props_.flEQCenter != props.flEQCenter) { + is_dirty = true; set_efx_eq_center(); } - if (eax_dirty_flags_.flEQBandwidth) + if (props_.flEQBandwidth != props.flEQBandwidth) { + is_dirty = true; set_efx_eq_bandwidth(); } - eax_dirty_flags_ = EaxDistortionEffectDirtyFlags{}; - - return true; -} - -void EaxDistortionEffect::set(const EaxEaxCall& eax_call) -{ - switch(eax_call.get_property_id()) - { - case EAXDISTORTION_NONE: - break; - - case EAXDISTORTION_ALLPARAMETERS: - defer_all(eax_call); - break; - - case EAXDISTORTION_EDGE: - defer_edge(eax_call); - break; - - case EAXDISTORTION_GAIN: - defer_gain(eax_call); - break; - - case EAXDISTORTION_LOWPASSCUTOFF: - defer_low_pass_cutoff(eax_call); - break; - - case EAXDISTORTION_EQCENTER: - defer_eq_center(eax_call); - break; - - case EAXDISTORTION_EQBANDWIDTH: - defer_eq_bandwidth(eax_call); - break; - - default: - throw EaxDistortionEffectException{"Unsupported property id."}; - } + return is_dirty; } } // namespace -EaxEffectUPtr eax_create_eax_distortion_effect() +EaxEffectUPtr eax_create_eax_distortion_effect(const EaxCall& call) { - return std::make_unique(); + return eax_create_eax4_effect(call); } #endif // ALSOFT_EAX diff --git a/al/effects/echo.cpp b/al/effects/echo.cpp index 56849b9d..f25c94bf 100644 --- a/al/effects/echo.cpp +++ b/al/effects/echo.cpp @@ -9,7 +9,6 @@ #ifdef ALSOFT_EAX #include "alnumeric.h" - #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -120,157 +119,151 @@ const EffectProps EchoEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { -using EaxEchoEffectDirtyFlagsValue = std::uint_least8_t; - -struct EaxEchoEffectDirtyFlags +class EaxEchoEffectException : public EaxException { - using EaxIsBitFieldStruct = bool; - - EaxEchoEffectDirtyFlagsValue flDelay : 1; - EaxEchoEffectDirtyFlagsValue flLRDelay : 1; - EaxEchoEffectDirtyFlagsValue flDamping : 1; - EaxEchoEffectDirtyFlagsValue flFeedback : 1; - EaxEchoEffectDirtyFlagsValue flSpread : 1; -}; // EaxEchoEffectDirtyFlags - +public: + explicit EaxEchoEffectException(const char* message) + : EaxException{"EAX_ECHO_EFFECT", message} + {} +}; // EaxEchoEffectException -class EaxEchoEffect final : - public EaxEffect +class EaxEchoEffect final : public EaxEffect4 { public: - EaxEchoEffect(); - - void dispatch(const EaxEaxCall& eax_call) override; - - // [[nodiscard]] - bool apply_deferred() override; + EaxEchoEffect(const EaxCall& call); private: - EAXECHOPROPERTIES eax_{}; - EAXECHOPROPERTIES eax_d_{}; - EaxEchoEffectDirtyFlags eax_dirty_flags_{}; - - void set_eax_defaults(); - - void set_efx_delay(); - void set_efx_lr_delay(); - void set_efx_damping(); - void set_efx_feedback(); - void set_efx_spread(); - void set_efx_defaults(); - - void get(const EaxEaxCall& eax_call); - - void validate_delay(float flDelay); - void validate_lr_delay(float flLRDelay); - void validate_damping(float flDamping); - void validate_feedback(float flFeedback); - void validate_spread(float flSpread); - void validate_all(const EAXECHOPROPERTIES& all); - - void defer_delay(float flDelay); - void defer_lr_delay(float flLRDelay); - void defer_damping(float flDamping); - void defer_feedback(float flFeedback); - void defer_spread(float flSpread); - void defer_all(const EAXECHOPROPERTIES& all); - - void defer_delay(const EaxEaxCall& eax_call); - void defer_lr_delay(const EaxEaxCall& eax_call); - void defer_damping(const EaxEaxCall& eax_call); - void defer_feedback(const EaxEaxCall& eax_call); - void defer_spread(const EaxEaxCall& eax_call); - void defer_all(const EaxEaxCall& eax_call); - - void set(const EaxEaxCall& eax_call); + struct DelayValidator { + void operator()(float flDelay) const + { + eax_validate_range( + "Delay", + flDelay, + EAXECHO_MINDELAY, + EAXECHO_MAXDELAY); + } + }; // DelayValidator + + struct LrDelayValidator { + void operator()(float flLRDelay) const + { + eax_validate_range( + "LR Delay", + flLRDelay, + EAXECHO_MINLRDELAY, + EAXECHO_MAXLRDELAY); + } + }; // LrDelayValidator + + struct DampingValidator { + void operator()(float flDamping) const + { + eax_validate_range( + "Damping", + flDamping, + EAXECHO_MINDAMPING, + EAXECHO_MAXDAMPING); + } + }; // DampingValidator + + struct FeedbackValidator { + void operator()(float flFeedback) const + { + eax_validate_range( + "Feedback", + flFeedback, + EAXECHO_MINFEEDBACK, + EAXECHO_MAXFEEDBACK); + } + }; // FeedbackValidator + + struct SpreadValidator { + void operator()(float flSpread) const + { + eax_validate_range( + "Spread", + flSpread, + EAXECHO_MINSPREAD, + EAXECHO_MAXSPREAD); + } + }; // SpreadValidator + + struct AllValidator { + void operator()(const Props& all) const + { + DelayValidator{}(all.flDelay); + LrDelayValidator{}(all.flLRDelay); + DampingValidator{}(all.flDamping); + FeedbackValidator{}(all.flFeedback); + SpreadValidator{}(all.flSpread); + } + }; // AllValidator + + void set_defaults(Props& props) override; + + void set_efx_delay() noexcept; + void set_efx_lr_delay() noexcept; + void set_efx_damping() noexcept; + void set_efx_feedback() noexcept; + void set_efx_spread() noexcept; + void set_efx_defaults() override; + + void get(const EaxCall& call, const Props& props) override; + void set(const EaxCall& call, Props& props) override; + bool commit_props(const Props& props) override; }; // EaxEchoEffect +EaxEchoEffect::EaxEchoEffect(const EaxCall& call) + : EaxEffect4{AL_EFFECT_ECHO, call} +{} -class EaxEchoEffectException : - public EaxException -{ -public: - explicit EaxEchoEffectException( - const char* message) - : - EaxException{"EAX_ECHO_EFFECT", message} - { - } -}; // EaxEchoEffectException - - -EaxEchoEffect::EaxEchoEffect() - : EaxEffect{AL_EFFECT_ECHO} -{ - set_eax_defaults(); - set_efx_defaults(); -} - -void EaxEchoEffect::dispatch( - const EaxEaxCall& eax_call) -{ - eax_call.is_get() ? get(eax_call) : set(eax_call); -} - -void EaxEchoEffect::set_eax_defaults() +void EaxEchoEffect::set_defaults(Props& props) { - eax_.flDelay = EAXECHO_DEFAULTDELAY; - eax_.flLRDelay = EAXECHO_DEFAULTLRDELAY; - eax_.flDamping = EAXECHO_DEFAULTDAMPING; - eax_.flFeedback = EAXECHO_DEFAULTFEEDBACK; - eax_.flSpread = EAXECHO_DEFAULTSPREAD; - - eax_d_ = eax_; + props.flDelay = EAXECHO_DEFAULTDELAY; + props.flLRDelay = EAXECHO_DEFAULTLRDELAY; + props.flDamping = EAXECHO_DEFAULTDAMPING; + props.flFeedback = EAXECHO_DEFAULTFEEDBACK; + props.flSpread = EAXECHO_DEFAULTSPREAD; } -void EaxEchoEffect::set_efx_delay() +void EaxEchoEffect::set_efx_delay() noexcept { - const auto delay = clamp( - eax_.flDelay, + al_effect_props_.Echo.Delay = clamp( + props_.flDelay, AL_ECHO_MIN_DELAY, AL_ECHO_MAX_DELAY); - - al_effect_props_.Echo.Delay = delay; } -void EaxEchoEffect::set_efx_lr_delay() +void EaxEchoEffect::set_efx_lr_delay() noexcept { - const auto lr_delay = clamp( - eax_.flLRDelay, + al_effect_props_.Echo.LRDelay = clamp( + props_.flLRDelay, AL_ECHO_MIN_LRDELAY, AL_ECHO_MAX_LRDELAY); - - al_effect_props_.Echo.LRDelay = lr_delay; } -void EaxEchoEffect::set_efx_damping() +void EaxEchoEffect::set_efx_damping() noexcept { - const auto damping = clamp( - eax_.flDamping, + al_effect_props_.Echo.Damping = clamp( + props_.flDamping, AL_ECHO_MIN_DAMPING, AL_ECHO_MAX_DAMPING); - - al_effect_props_.Echo.Damping = damping; } -void EaxEchoEffect::set_efx_feedback() +void EaxEchoEffect::set_efx_feedback() noexcept { - const auto feedback = clamp( - eax_.flFeedback, + al_effect_props_.Echo.Feedback = clamp( + props_.flFeedback, AL_ECHO_MIN_FEEDBACK, AL_ECHO_MAX_FEEDBACK); - - al_effect_props_.Echo.Feedback = feedback; } -void EaxEchoEffect::set_efx_spread() +void EaxEchoEffect::set_efx_spread() noexcept { - const auto spread = clamp( - eax_.flSpread, + al_effect_props_.Echo.Spread = clamp( + props_.flSpread, AL_ECHO_MIN_SPREAD, AL_ECHO_MAX_SPREAD); - - al_effect_props_.Echo.Spread = spread; } void EaxEchoEffect::set_efx_defaults() @@ -282,288 +275,78 @@ void EaxEchoEffect::set_efx_defaults() set_efx_spread(); } -void EaxEchoEffect::get(const EaxEaxCall& eax_call) +void EaxEchoEffect::get(const EaxCall& call, const Props& props) { - switch(eax_call.get_property_id()) + switch(call.get_property_id()) { - case EAXECHO_NONE: - break; - - case EAXECHO_ALLPARAMETERS: - eax_call.set_value(eax_); - break; - - case EAXECHO_DELAY: - eax_call.set_value(eax_.flDelay); - break; - - case EAXECHO_LRDELAY: - eax_call.set_value(eax_.flLRDelay); - break; - - case EAXECHO_DAMPING: - eax_call.set_value(eax_.flDamping); - break; - - case EAXECHO_FEEDBACK: - eax_call.set_value(eax_.flFeedback); - break; - - case EAXECHO_SPREAD: - eax_call.set_value(eax_.flSpread); - break; - - default: - throw EaxEchoEffectException{"Unsupported property id."}; + case EAXECHO_NONE: break; + case EAXECHO_ALLPARAMETERS: call.set_value(props); break; + case EAXECHO_DELAY: call.set_value(props.flDelay); break; + case EAXECHO_LRDELAY: call.set_value(props.flLRDelay); break; + case EAXECHO_DAMPING: call.set_value(props.flDamping); break; + case EAXECHO_FEEDBACK: call.set_value(props.flFeedback); break; + case EAXECHO_SPREAD: call.set_value(props.flSpread); break; + default: fail_unknown_property_id(); } } -void EaxEchoEffect::validate_delay( - float flDelay) -{ - eax_validate_range( - "Delay", - flDelay, - EAXECHO_MINDELAY, - EAXECHO_MAXDELAY); -} - -void EaxEchoEffect::validate_lr_delay( - float flLRDelay) -{ - eax_validate_range( - "LR Delay", - flLRDelay, - EAXECHO_MINLRDELAY, - EAXECHO_MAXLRDELAY); -} - -void EaxEchoEffect::validate_damping( - float flDamping) -{ - eax_validate_range( - "Damping", - flDamping, - EAXECHO_MINDAMPING, - EAXECHO_MAXDAMPING); -} - -void EaxEchoEffect::validate_feedback( - float flFeedback) -{ - eax_validate_range( - "Feedback", - flFeedback, - EAXECHO_MINFEEDBACK, - EAXECHO_MAXFEEDBACK); -} - -void EaxEchoEffect::validate_spread( - float flSpread) -{ - eax_validate_range( - "Spread", - flSpread, - EAXECHO_MINSPREAD, - EAXECHO_MAXSPREAD); -} - -void EaxEchoEffect::validate_all( - const EAXECHOPROPERTIES& all) -{ - validate_delay(all.flDelay); - validate_lr_delay(all.flLRDelay); - validate_damping(all.flDamping); - validate_feedback(all.flFeedback); - validate_spread(all.flSpread); -} - -void EaxEchoEffect::defer_delay( - float flDelay) -{ - eax_d_.flDelay = flDelay; - eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay); -} - -void EaxEchoEffect::defer_lr_delay( - float flLRDelay) +void EaxEchoEffect::set(const EaxCall& call, Props& props) { - eax_d_.flLRDelay = flLRDelay; - eax_dirty_flags_.flLRDelay = (eax_.flLRDelay != eax_d_.flLRDelay); -} - -void EaxEchoEffect::defer_damping( - float flDamping) -{ - eax_d_.flDamping = flDamping; - eax_dirty_flags_.flDamping = (eax_.flDamping != eax_d_.flDamping); -} - -void EaxEchoEffect::defer_feedback( - float flFeedback) -{ - eax_d_.flFeedback = flFeedback; - eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback); -} - -void EaxEchoEffect::defer_spread( - float flSpread) -{ - eax_d_.flSpread = flSpread; - eax_dirty_flags_.flSpread = (eax_.flSpread != eax_d_.flSpread); -} - -void EaxEchoEffect::defer_all( - const EAXECHOPROPERTIES& all) -{ - defer_delay(all.flDelay); - defer_lr_delay(all.flLRDelay); - defer_damping(all.flDamping); - defer_feedback(all.flFeedback); - defer_spread(all.flSpread); -} - -void EaxEchoEffect::defer_delay( - const EaxEaxCall& eax_call) -{ - const auto& delay = - eax_call.get_value(); - - validate_delay(delay); - defer_delay(delay); -} - -void EaxEchoEffect::defer_lr_delay( - const EaxEaxCall& eax_call) -{ - const auto& lr_delay = - eax_call.get_value(); - - validate_lr_delay(lr_delay); - defer_lr_delay(lr_delay); -} - -void EaxEchoEffect::defer_damping( - const EaxEaxCall& eax_call) -{ - const auto& damping = - eax_call.get_value(); - - validate_damping(damping); - defer_damping(damping); -} - -void EaxEchoEffect::defer_feedback( - const EaxEaxCall& eax_call) -{ - const auto& feedback = - eax_call.get_value(); - - validate_feedback(feedback); - defer_feedback(feedback); -} - -void EaxEchoEffect::defer_spread( - const EaxEaxCall& eax_call) -{ - const auto& spread = - eax_call.get_value(); - - validate_spread(spread); - defer_spread(spread); -} - -void EaxEchoEffect::defer_all( - const EaxEaxCall& eax_call) -{ - const auto& all = - eax_call.get_value(); - - validate_all(all); - defer_all(all); -} - -// [[nodiscard]] -bool EaxEchoEffect::apply_deferred() -{ - if (eax_dirty_flags_ == EaxEchoEffectDirtyFlags{}) + switch(call.get_property_id()) { - return false; + case EAXECHO_NONE: break; + case EAXECHO_ALLPARAMETERS: defer(call, props); break; + case EAXECHO_DELAY: defer(call, props.flDelay); break; + case EAXECHO_LRDELAY: defer(call, props.flLRDelay); break; + case EAXECHO_DAMPING: defer(call, props.flDamping); break; + case EAXECHO_FEEDBACK: defer(call, props.flFeedback); break; + case EAXECHO_SPREAD: defer(call, props.flSpread); break; + default: fail_unknown_property_id(); } +} - eax_ = eax_d_; +bool EaxEchoEffect::commit_props(const Props& props) +{ + auto is_dirty = false; - if (eax_dirty_flags_.flDelay) + if (props_.flDelay != props.flDelay) { + is_dirty = true; set_efx_delay(); } - if (eax_dirty_flags_.flLRDelay) + if (props_.flLRDelay != props.flLRDelay) { + is_dirty = true; set_efx_lr_delay(); } - if (eax_dirty_flags_.flDamping) + if (props_.flDamping != props.flDamping) { + is_dirty = true; set_efx_damping(); } - if (eax_dirty_flags_.flFeedback) + if (props_.flFeedback != props.flFeedback) { + is_dirty = true; set_efx_feedback(); } - if (eax_dirty_flags_.flSpread) + if (props_.flSpread != props.flSpread) { + is_dirty = true; set_efx_spread(); } - eax_dirty_flags_ = EaxEchoEffectDirtyFlags{}; - - return true; -} - -void EaxEchoEffect::set(const EaxEaxCall& eax_call) -{ - switch(eax_call.get_property_id()) - { - case EAXECHO_NONE: - break; - - case EAXECHO_ALLPARAMETERS: - defer_all(eax_call); - break; - - case EAXECHO_DELAY: - defer_delay(eax_call); - break; - - case EAXECHO_LRDELAY: - defer_lr_delay(eax_call); - break; - - case EAXECHO_DAMPING: - defer_damping(eax_call); - break; - - case EAXECHO_FEEDBACK: - defer_feedback(eax_call); - break; - - case EAXECHO_SPREAD: - defer_spread(eax_call); - break; - - default: - throw EaxEchoEffectException{"Unsupported property id."}; - } + return is_dirty; } } // namespace -EaxEffectUPtr eax_create_eax_echo_effect() +EaxEffectUPtr eax_create_eax_echo_effect(const EaxCall& call) { - return std::make_unique(); + return eax_create_eax4_effect(call); } #endif // ALSOFT_EAX diff --git a/al/effects/effects.cpp b/al/effects/effects.cpp index faf322d2..e4e61231 100644 --- a/al/effects/effects.cpp +++ b/al/effects/effects.cpp @@ -10,7 +10,7 @@ #include "AL/efx.h" -EaxEffectUPtr eax_create_eax_effect(ALenum al_effect_type) +EaxEffectUPtr eax_create_eax_effect(ALenum al_effect_type, const EaxCall& call) { #define EAX_PREFIX "[EAX_MAKE_EAX_EFFECT] " @@ -20,40 +20,40 @@ EaxEffectUPtr eax_create_eax_effect(ALenum al_effect_type) return eax_create_eax_null_effect(); case AL_EFFECT_CHORUS: - return eax_create_eax_chorus_effect(); + return eax_create_eax_chorus_effect(call); case AL_EFFECT_DISTORTION: - return eax_create_eax_distortion_effect(); + return eax_create_eax_distortion_effect(call); case AL_EFFECT_ECHO: - return eax_create_eax_echo_effect(); + return eax_create_eax_echo_effect(call); case AL_EFFECT_FLANGER: - return eax_create_eax_flanger_effect(); + return eax_create_eax_flanger_effect(call); case AL_EFFECT_FREQUENCY_SHIFTER: - return eax_create_eax_frequency_shifter_effect(); + return eax_create_eax_frequency_shifter_effect(call); case AL_EFFECT_VOCAL_MORPHER: - return eax_create_eax_vocal_morpher_effect(); + return eax_create_eax_vocal_morpher_effect(call); case AL_EFFECT_PITCH_SHIFTER: - return eax_create_eax_pitch_shifter_effect(); + return eax_create_eax_pitch_shifter_effect(call); case AL_EFFECT_RING_MODULATOR: - return eax_create_eax_ring_modulator_effect(); + return eax_create_eax_ring_modulator_effect(call); case AL_EFFECT_AUTOWAH: - return eax_create_eax_auto_wah_effect(); + return eax_create_eax_auto_wah_effect(call); case AL_EFFECT_COMPRESSOR: - return eax_create_eax_compressor_effect(); + return eax_create_eax_compressor_effect(call); case AL_EFFECT_EQUALIZER: - return eax_create_eax_equalizer_effect(); + return eax_create_eax_equalizer_effect(call); case AL_EFFECT_EAXREVERB: - return eax_create_eax_reverb_effect(); + return eax_create_eax_reverb_effect(call); default: assert(false && "Unsupported AL effect type."); diff --git a/al/effects/effects.h b/al/effects/effects.h index f8b97eeb..164c0d19 100644 --- a/al/effects/effects.h +++ b/al/effects/effects.h @@ -6,6 +6,7 @@ #include "core/except.h" #ifdef ALSOFT_EAX +#include "al/eax/call.h" #include "al/eax/effect.h" #endif // ALSOFT_EAX @@ -86,7 +87,7 @@ extern const EffectVtable ConvolutionEffectVtable; #ifdef ALSOFT_EAX -EaxEffectUPtr eax_create_eax_effect(ALenum al_effect_type); +EaxEffectUPtr eax_create_eax_effect(ALenum al_effect_type, const EaxCall& call); #endif // ALSOFT_EAX #endif /* AL_EFFECTS_EFFECTS_H */ diff --git a/al/effects/equalizer.cpp b/al/effects/equalizer.cpp index ccc00f67..80dd1c4b 100644 --- a/al/effects/equalizer.cpp +++ b/al/effects/equalizer.cpp @@ -9,7 +9,6 @@ #ifdef ALSOFT_EAX #include "alnumeric.h" - #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -178,236 +177,261 @@ const EffectProps EqualizerEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { -using EaxEqualizerEffectDirtyFlagsValue = std::uint_least16_t; - -struct EaxEqualizerEffectDirtyFlags -{ - using EaxIsBitFieldStruct = bool; - - EaxEqualizerEffectDirtyFlagsValue lLowGain : 1; - EaxEqualizerEffectDirtyFlagsValue flLowCutOff : 1; - EaxEqualizerEffectDirtyFlagsValue lMid1Gain : 1; - EaxEqualizerEffectDirtyFlagsValue flMid1Center : 1; - EaxEqualizerEffectDirtyFlagsValue flMid1Width : 1; - EaxEqualizerEffectDirtyFlagsValue lMid2Gain : 1; - EaxEqualizerEffectDirtyFlagsValue flMid2Center : 1; - EaxEqualizerEffectDirtyFlagsValue flMid2Width : 1; - EaxEqualizerEffectDirtyFlagsValue lHighGain : 1; - EaxEqualizerEffectDirtyFlagsValue flHighCutOff : 1; -}; // EaxEqualizerEffectDirtyFlags - - -class EaxEqualizerEffect final : - public EaxEffect +class EaxEqualizerEffectException : public EaxException { public: - EaxEqualizerEffect(); - - void dispatch(const EaxEaxCall& eax_call) override; - - // [[nodiscard]] - bool apply_deferred() override; - -private: - EAXEQUALIZERPROPERTIES eax_{}; - EAXEQUALIZERPROPERTIES eax_d_{}; - EaxEqualizerEffectDirtyFlags eax_dirty_flags_{}; - - void set_eax_defaults(); - - void set_efx_low_gain(); - void set_efx_low_cutoff(); - void set_efx_mid1_gain(); - void set_efx_mid1_center(); - void set_efx_mid1_width(); - void set_efx_mid2_gain(); - void set_efx_mid2_center(); - void set_efx_mid2_width(); - void set_efx_high_gain(); - void set_efx_high_cutoff(); - void set_efx_defaults(); - - void get(const EaxEaxCall& eax_call); - - void validate_low_gain(long lLowGain); - void validate_low_cutoff(float flLowCutOff); - void validate_mid1_gain(long lMid1Gain); - void validate_mid1_center(float flMid1Center); - void validate_mid1_width(float flMid1Width); - void validate_mid2_gain(long lMid2Gain); - void validate_mid2_center(float flMid2Center); - void validate_mid2_width(float flMid2Width); - void validate_high_gain(long lHighGain); - void validate_high_cutoff(float flHighCutOff); - void validate_all(const EAXEQUALIZERPROPERTIES& all); - - void defer_low_gain(long lLowGain); - void defer_low_cutoff(float flLowCutOff); - void defer_mid1_gain(long lMid1Gain); - void defer_mid1_center(float flMid1Center); - void defer_mid1_width(float flMid1Width); - void defer_mid2_gain(long lMid2Gain); - void defer_mid2_center(float flMid2Center); - void defer_mid2_width(float flMid2Width); - void defer_high_gain(long lHighGain); - void defer_high_cutoff(float flHighCutOff); - void defer_all(const EAXEQUALIZERPROPERTIES& all); - - void defer_low_gain(const EaxEaxCall& eax_call); - void defer_low_cutoff(const EaxEaxCall& eax_call); - void defer_mid1_gain(const EaxEaxCall& eax_call); - void defer_mid1_center(const EaxEaxCall& eax_call); - void defer_mid1_width(const EaxEaxCall& eax_call); - void defer_mid2_gain(const EaxEaxCall& eax_call); - void defer_mid2_center(const EaxEaxCall& eax_call); - void defer_mid2_width(const EaxEaxCall& eax_call); - void defer_high_gain(const EaxEaxCall& eax_call); - void defer_high_cutoff(const EaxEaxCall& eax_call); - void defer_all(const EaxEaxCall& eax_call); - - void set(const EaxEaxCall& eax_call); -}; // EaxEqualizerEffect - + explicit EaxEqualizerEffectException(const char* message) + : EaxException{"EAX_EQUALIZER_EFFECT", message} + {} +}; // EaxEqualizerEffectException -class EaxEqualizerEffectException : - public EaxException +class EaxEqualizerEffect final : public EaxEffect4 { public: - explicit EaxEqualizerEffectException( - const char* message) - : - EaxException{"EAX_EQUALIZER_EFFECT", message} - { - } -}; // EaxEqualizerEffectException - + EaxEqualizerEffect(const EaxCall& call); -EaxEqualizerEffect::EaxEqualizerEffect() - : EaxEffect{AL_EFFECT_EQUALIZER} -{ - set_eax_defaults(); - set_efx_defaults(); -} +private: + struct LowGainValidator { + void operator()(long lLowGain) const + { + eax_validate_range( + "Low Gain", + lLowGain, + EAXEQUALIZER_MINLOWGAIN, + EAXEQUALIZER_MAXLOWGAIN); + } + }; // LowGainValidator + + struct LowCutOffValidator { + void operator()(float flLowCutOff) const + { + eax_validate_range( + "Low Cutoff", + flLowCutOff, + EAXEQUALIZER_MINLOWCUTOFF, + EAXEQUALIZER_MAXLOWCUTOFF); + } + }; // LowCutOffValidator + + struct Mid1GainValidator { + void operator()(long lMid1Gain) const + { + eax_validate_range( + "Mid1 Gain", + lMid1Gain, + EAXEQUALIZER_MINMID1GAIN, + EAXEQUALIZER_MAXMID1GAIN); + } + }; // Mid1GainValidator + + struct Mid1CenterValidator { + void operator()(float flMid1Center) const + { + eax_validate_range( + "Mid1 Center", + flMid1Center, + EAXEQUALIZER_MINMID1CENTER, + EAXEQUALIZER_MAXMID1CENTER); + } + }; // Mid1CenterValidator + + struct Mid1WidthValidator { + void operator()(float flMid1Width) const + { + eax_validate_range( + "Mid1 Width", + flMid1Width, + EAXEQUALIZER_MINMID1WIDTH, + EAXEQUALIZER_MAXMID1WIDTH); + } + }; // Mid1WidthValidator + + struct Mid2GainValidator { + void operator()(long lMid2Gain) const + { + eax_validate_range( + "Mid2 Gain", + lMid2Gain, + EAXEQUALIZER_MINMID2GAIN, + EAXEQUALIZER_MAXMID2GAIN); + } + }; // Mid2GainValidator + + struct Mid2CenterValidator { + void operator()(float flMid2Center) const + { + eax_validate_range( + "Mid2 Center", + flMid2Center, + EAXEQUALIZER_MINMID2CENTER, + EAXEQUALIZER_MAXMID2CENTER); + } + }; // Mid2CenterValidator + + struct Mid2WidthValidator { + void operator()(float flMid2Width) const + { + eax_validate_range( + "Mid2 Width", + flMid2Width, + EAXEQUALIZER_MINMID2WIDTH, + EAXEQUALIZER_MAXMID2WIDTH); + } + }; // Mid2WidthValidator + + struct HighGainValidator { + void operator()(long lHighGain) const + { + eax_validate_range( + "High Gain", + lHighGain, + EAXEQUALIZER_MINHIGHGAIN, + EAXEQUALIZER_MAXHIGHGAIN); + } + }; // HighGainValidator + + struct HighCutOffValidator { + void operator()(float flHighCutOff) const + { + eax_validate_range( + "High Cutoff", + flHighCutOff, + EAXEQUALIZER_MINHIGHCUTOFF, + EAXEQUALIZER_MAXHIGHCUTOFF); + } + }; // HighCutOffValidator + + struct AllValidator { + void operator()(const Props& all) const + { + LowGainValidator{}(all.lLowGain); + LowCutOffValidator{}(all.flLowCutOff); + Mid1GainValidator{}(all.lMid1Gain); + Mid1CenterValidator{}(all.flMid1Center); + Mid1WidthValidator{}(all.flMid1Width); + Mid2GainValidator{}(all.lMid2Gain); + Mid2CenterValidator{}(all.flMid2Center); + Mid2WidthValidator{}(all.flMid2Width); + HighGainValidator{}(all.lHighGain); + HighCutOffValidator{}(all.flHighCutOff); + } + }; // AllValidator + + void set_defaults(Props& props) override; + + void set_efx_low_gain() noexcept; + void set_efx_low_cutoff() noexcept; + void set_efx_mid1_gain() noexcept; + void set_efx_mid1_center() noexcept; + void set_efx_mid1_width() noexcept; + void set_efx_mid2_gain() noexcept; + void set_efx_mid2_center() noexcept; + void set_efx_mid2_width() noexcept; + void set_efx_high_gain() noexcept; + void set_efx_high_cutoff() noexcept; + void set_efx_defaults() override; + + void get(const EaxCall& call, const Props& props) override; + void set(const EaxCall& call, Props& props) override; + bool commit_props(const Props& props) override; +}; // EaxEqualizerEffect -void EaxEqualizerEffect::dispatch(const EaxEaxCall& eax_call) -{ - eax_call.is_get() ? get(eax_call) : set(eax_call); -} +EaxEqualizerEffect::EaxEqualizerEffect(const EaxCall& call) + : EaxEffect4{AL_EFFECT_EQUALIZER, call} +{} -void EaxEqualizerEffect::set_eax_defaults() +void EaxEqualizerEffect::set_defaults(Props& props) { - eax_.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN; - eax_.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF; - eax_.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN; - eax_.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER; - eax_.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH; - eax_.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN; - eax_.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER; - eax_.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH; - eax_.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN; - eax_.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF; - - eax_d_ = eax_; + props.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN; + props.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF; + props.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN; + props.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER; + props.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH; + props.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN; + props.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER; + props.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH; + props.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN; + props.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF; } -void EaxEqualizerEffect::set_efx_low_gain() +void EaxEqualizerEffect::set_efx_low_gain() noexcept { - const auto low_gain = clamp( - level_mb_to_gain(static_cast(eax_.lLowGain)), + al_effect_props_.Equalizer.LowGain = clamp( + level_mb_to_gain(static_cast(props_.lLowGain)), AL_EQUALIZER_MIN_LOW_GAIN, AL_EQUALIZER_MAX_LOW_GAIN); - - al_effect_props_.Equalizer.LowGain = low_gain; } -void EaxEqualizerEffect::set_efx_low_cutoff() +void EaxEqualizerEffect::set_efx_low_cutoff() noexcept { - const auto low_cutoff = clamp( - eax_.flLowCutOff, + al_effect_props_.Equalizer.LowCutoff = clamp( + props_.flLowCutOff, AL_EQUALIZER_MIN_LOW_CUTOFF, AL_EQUALIZER_MAX_LOW_CUTOFF); - - al_effect_props_.Equalizer.LowCutoff = low_cutoff; } -void EaxEqualizerEffect::set_efx_mid1_gain() +void EaxEqualizerEffect::set_efx_mid1_gain() noexcept { - const auto mid1_gain = clamp( - level_mb_to_gain(static_cast(eax_.lMid1Gain)), + al_effect_props_.Equalizer.Mid1Gain = clamp( + level_mb_to_gain(static_cast(props_.lMid1Gain)), AL_EQUALIZER_MIN_MID1_GAIN, AL_EQUALIZER_MAX_MID1_GAIN); - - al_effect_props_.Equalizer.Mid1Gain = mid1_gain; } -void EaxEqualizerEffect::set_efx_mid1_center() +void EaxEqualizerEffect::set_efx_mid1_center() noexcept { - const auto mid1_center = clamp( - eax_.flMid1Center, + al_effect_props_.Equalizer.Mid1Center = clamp( + props_.flMid1Center, AL_EQUALIZER_MIN_MID1_CENTER, AL_EQUALIZER_MAX_MID1_CENTER); - - al_effect_props_.Equalizer.Mid1Center = mid1_center; } -void EaxEqualizerEffect::set_efx_mid1_width() +void EaxEqualizerEffect::set_efx_mid1_width() noexcept { - const auto mid1_width = clamp( - eax_.flMid1Width, + al_effect_props_.Equalizer.Mid1Width = clamp( + props_.flMid1Width, AL_EQUALIZER_MIN_MID1_WIDTH, AL_EQUALIZER_MAX_MID1_WIDTH); - - al_effect_props_.Equalizer.Mid1Width = mid1_width; } -void EaxEqualizerEffect::set_efx_mid2_gain() +void EaxEqualizerEffect::set_efx_mid2_gain() noexcept { - const auto mid2_gain = clamp( - level_mb_to_gain(static_cast(eax_.lMid2Gain)), + al_effect_props_.Equalizer.Mid2Gain = clamp( + level_mb_to_gain(static_cast(props_.lMid2Gain)), AL_EQUALIZER_MIN_MID2_GAIN, AL_EQUALIZER_MAX_MID2_GAIN); - - al_effect_props_.Equalizer.Mid2Gain = mid2_gain; } -void EaxEqualizerEffect::set_efx_mid2_center() +void EaxEqualizerEffect::set_efx_mid2_center() noexcept { - const auto mid2_center = clamp( - eax_.flMid2Center, + al_effect_props_.Equalizer.Mid2Center = clamp( + props_.flMid2Center, AL_EQUALIZER_MIN_MID2_CENTER, AL_EQUALIZER_MAX_MID2_CENTER); - - al_effect_props_.Equalizer.Mid2Center = mid2_center; } -void EaxEqualizerEffect::set_efx_mid2_width() +void EaxEqualizerEffect::set_efx_mid2_width() noexcept { - const auto mid2_width = clamp( - eax_.flMid2Width, + al_effect_props_.Equalizer.Mid2Width = clamp( + props_.flMid2Width, AL_EQUALIZER_MIN_MID2_WIDTH, AL_EQUALIZER_MAX_MID2_WIDTH); - - al_effect_props_.Equalizer.Mid2Width = mid2_width; } -void EaxEqualizerEffect::set_efx_high_gain() +void EaxEqualizerEffect::set_efx_high_gain() noexcept { - const auto high_gain = clamp( - level_mb_to_gain(static_cast(eax_.lHighGain)), + al_effect_props_.Equalizer.HighGain = clamp( + level_mb_to_gain(static_cast(props_.lHighGain)), AL_EQUALIZER_MIN_HIGH_GAIN, AL_EQUALIZER_MAX_HIGH_GAIN); - - al_effect_props_.Equalizer.HighGain = high_gain; } -void EaxEqualizerEffect::set_efx_high_cutoff() +void EaxEqualizerEffect::set_efx_high_cutoff() noexcept { - const auto high_cutoff = clamp( - eax_.flHighCutOff, + al_effect_props_.Equalizer.HighCutoff = clamp( + props_.flHighCutOff, AL_EQUALIZER_MIN_HIGH_CUTOFF, AL_EQUALIZER_MAX_HIGH_CUTOFF); - - al_effect_props_.Equalizer.HighCutoff = high_cutoff; } void EaxEqualizerEffect::set_efx_defaults() @@ -424,498 +448,118 @@ void EaxEqualizerEffect::set_efx_defaults() set_efx_high_cutoff(); } -void EaxEqualizerEffect::get(const EaxEaxCall& eax_call) +void EaxEqualizerEffect::get(const EaxCall& call, const Props& props) { - switch(eax_call.get_property_id()) + switch(call.get_property_id()) { - case EAXEQUALIZER_NONE: - break; - - case EAXEQUALIZER_ALLPARAMETERS: - eax_call.set_value(eax_); - break; - - case EAXEQUALIZER_LOWGAIN: - eax_call.set_value(eax_.lLowGain); - break; - - case EAXEQUALIZER_LOWCUTOFF: - eax_call.set_value(eax_.flLowCutOff); - break; - - case EAXEQUALIZER_MID1GAIN: - eax_call.set_value(eax_.lMid1Gain); - break; - - case EAXEQUALIZER_MID1CENTER: - eax_call.set_value(eax_.flMid1Center); - break; - - case EAXEQUALIZER_MID1WIDTH: - eax_call.set_value(eax_.flMid1Width); - break; - - case EAXEQUALIZER_MID2GAIN: - eax_call.set_value(eax_.lMid2Gain); - break; - - case EAXEQUALIZER_MID2CENTER: - eax_call.set_value(eax_.flMid2Center); - break; - - case EAXEQUALIZER_MID2WIDTH: - eax_call.set_value(eax_.flMid2Width); - break; - - case EAXEQUALIZER_HIGHGAIN: - eax_call.set_value(eax_.lHighGain); - break; - - case EAXEQUALIZER_HIGHCUTOFF: - eax_call.set_value(eax_.flHighCutOff); - break; - - default: - throw EaxEqualizerEffectException{"Unsupported property id."}; + case EAXEQUALIZER_NONE: break; + case EAXEQUALIZER_ALLPARAMETERS: call.set_value(props); break; + case EAXEQUALIZER_LOWGAIN: call.set_value(props.lLowGain); break; + case EAXEQUALIZER_LOWCUTOFF: call.set_value(props.flLowCutOff); break; + case EAXEQUALIZER_MID1GAIN: call.set_value(props.lMid1Gain); break; + case EAXEQUALIZER_MID1CENTER: call.set_value(props.flMid1Center); break; + case EAXEQUALIZER_MID1WIDTH: call.set_value(props.flMid1Width); break; + case EAXEQUALIZER_MID2GAIN: call.set_value(props.lMid2Gain); break; + case EAXEQUALIZER_MID2CENTER: call.set_value(props.flMid2Center); break; + case EAXEQUALIZER_MID2WIDTH: call.set_value(props.flMid2Width); break; + case EAXEQUALIZER_HIGHGAIN: call.set_value(props.lHighGain); break; + case EAXEQUALIZER_HIGHCUTOFF: call.set_value(props.flHighCutOff); break; + default: fail_unknown_property_id(); } } -void EaxEqualizerEffect::validate_low_gain( - long lLowGain) -{ - eax_validate_range( - "Low Gain", - lLowGain, - EAXEQUALIZER_MINLOWGAIN, - EAXEQUALIZER_MAXLOWGAIN); -} - -void EaxEqualizerEffect::validate_low_cutoff( - float flLowCutOff) -{ - eax_validate_range( - "Low Cutoff", - flLowCutOff, - EAXEQUALIZER_MINLOWCUTOFF, - EAXEQUALIZER_MAXLOWCUTOFF); -} - -void EaxEqualizerEffect::validate_mid1_gain( - long lMid1Gain) -{ - eax_validate_range( - "Mid1 Gain", - lMid1Gain, - EAXEQUALIZER_MINMID1GAIN, - EAXEQUALIZER_MAXMID1GAIN); -} - -void EaxEqualizerEffect::validate_mid1_center( - float flMid1Center) -{ - eax_validate_range( - "Mid1 Center", - flMid1Center, - EAXEQUALIZER_MINMID1CENTER, - EAXEQUALIZER_MAXMID1CENTER); -} - -void EaxEqualizerEffect::validate_mid1_width( - float flMid1Width) -{ - eax_validate_range( - "Mid1 Width", - flMid1Width, - EAXEQUALIZER_MINMID1WIDTH, - EAXEQUALIZER_MAXMID1WIDTH); -} - -void EaxEqualizerEffect::validate_mid2_gain( - long lMid2Gain) -{ - eax_validate_range( - "Mid2 Gain", - lMid2Gain, - EAXEQUALIZER_MINMID2GAIN, - EAXEQUALIZER_MAXMID2GAIN); -} - -void EaxEqualizerEffect::validate_mid2_center( - float flMid2Center) -{ - eax_validate_range( - "Mid2 Center", - flMid2Center, - EAXEQUALIZER_MINMID2CENTER, - EAXEQUALIZER_MAXMID2CENTER); -} - -void EaxEqualizerEffect::validate_mid2_width( - float flMid2Width) -{ - eax_validate_range( - "Mid2 Width", - flMid2Width, - EAXEQUALIZER_MINMID2WIDTH, - EAXEQUALIZER_MAXMID2WIDTH); -} - -void EaxEqualizerEffect::validate_high_gain( - long lHighGain) -{ - eax_validate_range( - "High Gain", - lHighGain, - EAXEQUALIZER_MINHIGHGAIN, - EAXEQUALIZER_MAXHIGHGAIN); -} - -void EaxEqualizerEffect::validate_high_cutoff( - float flHighCutOff) -{ - eax_validate_range( - "High Cutoff", - flHighCutOff, - EAXEQUALIZER_MINHIGHCUTOFF, - EAXEQUALIZER_MAXHIGHCUTOFF); -} - -void EaxEqualizerEffect::validate_all( - const EAXEQUALIZERPROPERTIES& all) -{ - validate_low_gain(all.lLowGain); - validate_low_cutoff(all.flLowCutOff); - validate_mid1_gain(all.lMid1Gain); - validate_mid1_center(all.flMid1Center); - validate_mid1_width(all.flMid1Width); - validate_mid2_gain(all.lMid2Gain); - validate_mid2_center(all.flMid2Center); - validate_mid2_width(all.flMid2Width); - validate_high_gain(all.lHighGain); - validate_high_cutoff(all.flHighCutOff); -} - -void EaxEqualizerEffect::defer_low_gain( - long lLowGain) -{ - eax_d_.lLowGain = lLowGain; - eax_dirty_flags_.lLowGain = (eax_.lLowGain != eax_d_.lLowGain); -} - -void EaxEqualizerEffect::defer_low_cutoff( - float flLowCutOff) -{ - eax_d_.flLowCutOff = flLowCutOff; - eax_dirty_flags_.flLowCutOff = (eax_.flLowCutOff != eax_d_.flLowCutOff); -} - -void EaxEqualizerEffect::defer_mid1_gain( - long lMid1Gain) -{ - eax_d_.lMid1Gain = lMid1Gain; - eax_dirty_flags_.lMid1Gain = (eax_.lMid1Gain != eax_d_.lMid1Gain); -} - -void EaxEqualizerEffect::defer_mid1_center( - float flMid1Center) -{ - eax_d_.flMid1Center = flMid1Center; - eax_dirty_flags_.flMid1Center = (eax_.flMid1Center != eax_d_.flMid1Center); -} - -void EaxEqualizerEffect::defer_mid1_width( - float flMid1Width) -{ - eax_d_.flMid1Width = flMid1Width; - eax_dirty_flags_.flMid1Width = (eax_.flMid1Width != eax_d_.flMid1Width); -} - -void EaxEqualizerEffect::defer_mid2_gain( - long lMid2Gain) -{ - eax_d_.lMid2Gain = lMid2Gain; - eax_dirty_flags_.lMid2Gain = (eax_.lMid2Gain != eax_d_.lMid2Gain); -} - -void EaxEqualizerEffect::defer_mid2_center( - float flMid2Center) -{ - eax_d_.flMid2Center = flMid2Center; - eax_dirty_flags_.flMid2Center = (eax_.flMid2Center != eax_d_.flMid2Center); -} - -void EaxEqualizerEffect::defer_mid2_width( - float flMid2Width) -{ - eax_d_.flMid2Width = flMid2Width; - eax_dirty_flags_.flMid2Width = (eax_.flMid2Width != eax_d_.flMid2Width); -} - -void EaxEqualizerEffect::defer_high_gain( - long lHighGain) -{ - eax_d_.lHighGain = lHighGain; - eax_dirty_flags_.lHighGain = (eax_.lHighGain != eax_d_.lHighGain); -} - -void EaxEqualizerEffect::defer_high_cutoff( - float flHighCutOff) -{ - eax_d_.flHighCutOff = flHighCutOff; - eax_dirty_flags_.flHighCutOff = (eax_.flHighCutOff != eax_d_.flHighCutOff); -} - -void EaxEqualizerEffect::defer_all( - const EAXEQUALIZERPROPERTIES& all) -{ - defer_low_gain(all.lLowGain); - defer_low_cutoff(all.flLowCutOff); - defer_mid1_gain(all.lMid1Gain); - defer_mid1_center(all.flMid1Center); - defer_mid1_width(all.flMid1Width); - defer_mid2_gain(all.lMid2Gain); - defer_mid2_center(all.flMid2Center); - defer_mid2_width(all.flMid2Width); - defer_high_gain(all.lHighGain); - defer_high_cutoff(all.flHighCutOff); -} - -void EaxEqualizerEffect::defer_low_gain( - const EaxEaxCall& eax_call) -{ - const auto& low_gain = - eax_call.get_value(); - - validate_low_gain(low_gain); - defer_low_gain(low_gain); -} - -void EaxEqualizerEffect::defer_low_cutoff( - const EaxEaxCall& eax_call) -{ - const auto& low_cutoff = - eax_call.get_value(); - - validate_low_cutoff(low_cutoff); - defer_low_cutoff(low_cutoff); -} - -void EaxEqualizerEffect::defer_mid1_gain( - const EaxEaxCall& eax_call) -{ - const auto& mid1_gain = - eax_call.get_value(); - - validate_mid1_gain(mid1_gain); - defer_mid1_gain(mid1_gain); -} - -void EaxEqualizerEffect::defer_mid1_center( - const EaxEaxCall& eax_call) -{ - const auto& mid1_center = - eax_call.get_value(); - - validate_mid1_center(mid1_center); - defer_mid1_center(mid1_center); -} - -void EaxEqualizerEffect::defer_mid1_width( - const EaxEaxCall& eax_call) -{ - const auto& mid1_width = - eax_call.get_value(); - - validate_mid1_width(mid1_width); - defer_mid1_width(mid1_width); -} - -void EaxEqualizerEffect::defer_mid2_gain( - const EaxEaxCall& eax_call) -{ - const auto& mid2_gain = - eax_call.get_value(); - - validate_mid2_gain(mid2_gain); - defer_mid2_gain(mid2_gain); -} - -void EaxEqualizerEffect::defer_mid2_center( - const EaxEaxCall& eax_call) -{ - const auto& mid2_center = - eax_call.get_value(); - - validate_mid2_center(mid2_center); - defer_mid2_center(mid2_center); -} - -void EaxEqualizerEffect::defer_mid2_width( - const EaxEaxCall& eax_call) -{ - const auto& mid2_width = - eax_call.get_value(); - - validate_mid2_width(mid2_width); - defer_mid2_width(mid2_width); -} - -void EaxEqualizerEffect::defer_high_gain( - const EaxEaxCall& eax_call) +void EaxEqualizerEffect::set(const EaxCall& call, Props& props) { - const auto& high_gain = - eax_call.get_value(); - - validate_high_gain(high_gain); - defer_high_gain(high_gain); -} - -void EaxEqualizerEffect::defer_high_cutoff( - const EaxEaxCall& eax_call) -{ - const auto& high_cutoff = - eax_call.get_value(); - - validate_high_cutoff(high_cutoff); - defer_high_cutoff(high_cutoff); -} - -void EaxEqualizerEffect::defer_all( - const EaxEaxCall& eax_call) -{ - const auto& all = - eax_call.get_value(); - - validate_all(all); - defer_all(all); -} - -// [[nodiscard]] -bool EaxEqualizerEffect::apply_deferred() -{ - if (eax_dirty_flags_ == EaxEqualizerEffectDirtyFlags{}) + switch(call.get_property_id()) { - return false; + case EAXEQUALIZER_NONE: break; + case EAXEQUALIZER_ALLPARAMETERS: defer(call, props); break; + case EAXEQUALIZER_LOWGAIN: defer(call, props.lLowGain); break; + case EAXEQUALIZER_LOWCUTOFF: defer(call, props.flLowCutOff); break; + case EAXEQUALIZER_MID1GAIN: defer(call, props.lMid1Gain); break; + case EAXEQUALIZER_MID1CENTER: defer(call, props.flMid1Center); break; + case EAXEQUALIZER_MID1WIDTH: defer(call, props.flMid1Width); break; + case EAXEQUALIZER_MID2GAIN: defer(call, props.lMid2Gain); break; + case EAXEQUALIZER_MID2CENTER: defer(call, props.flMid2Center); break; + case EAXEQUALIZER_MID2WIDTH: defer(call, props.flMid2Width); break; + case EAXEQUALIZER_HIGHGAIN: defer(call, props.lHighGain); break; + case EAXEQUALIZER_HIGHCUTOFF: defer(call, props.flHighCutOff); break; + default: fail_unknown_property_id(); } +} - eax_ = eax_d_; +bool EaxEqualizerEffect::commit_props(const Props& props) +{ + auto is_dirty = false; - if (eax_dirty_flags_.lLowGain) + if (props_.lLowGain != props.lLowGain) { + is_dirty = true; set_efx_low_gain(); } - if (eax_dirty_flags_.flLowCutOff) + if (props_.flLowCutOff != props.flLowCutOff) { + is_dirty = true; set_efx_low_cutoff(); } - if (eax_dirty_flags_.lMid1Gain) + if (props_.lMid1Gain != props.lMid1Gain) { + is_dirty = true; set_efx_mid1_gain(); } - if (eax_dirty_flags_.flMid1Center) + if (props_.flMid1Center != props.flMid1Center) { + is_dirty = true; set_efx_mid1_center(); } - if (eax_dirty_flags_.flMid1Width) + if (props_.flMid1Width != props.flMid1Width) { + is_dirty = true; set_efx_mid1_width(); } - if (eax_dirty_flags_.lMid2Gain) + if (props_.lMid2Gain != props.lMid2Gain) { + is_dirty = true; set_efx_mid2_gain(); } - if (eax_dirty_flags_.flMid2Center) + if (props_.flMid2Center != props.flMid2Center) { + is_dirty = true; set_efx_mid2_center(); } - if (eax_dirty_flags_.flMid2Width) + if (props_.flMid2Width != props.flMid2Width) { + is_dirty = true; set_efx_mid2_width(); } - if (eax_dirty_flags_.lHighGain) + if (props_.lHighGain != props.lHighGain) { + is_dirty = true; set_efx_high_gain(); } - if (eax_dirty_flags_.flHighCutOff) + if (props_.flHighCutOff != props.flHighCutOff) { + is_dirty = true; set_efx_high_cutoff(); } - eax_dirty_flags_ = EaxEqualizerEffectDirtyFlags{}; - - return true; -} - -void EaxEqualizerEffect::set(const EaxEaxCall& eax_call) -{ - switch(eax_call.get_property_id()) - { - case EAXEQUALIZER_NONE: - break; - - case EAXEQUALIZER_ALLPARAMETERS: - defer_all(eax_call); - break; - - case EAXEQUALIZER_LOWGAIN: - defer_low_gain(eax_call); - break; - - case EAXEQUALIZER_LOWCUTOFF: - defer_low_cutoff(eax_call); - break; - - case EAXEQUALIZER_MID1GAIN: - defer_mid1_gain(eax_call); - break; - - case EAXEQUALIZER_MID1CENTER: - defer_mid1_center(eax_call); - break; - - case EAXEQUALIZER_MID1WIDTH: - defer_mid1_width(eax_call); - break; - - case EAXEQUALIZER_MID2GAIN: - defer_mid2_gain(eax_call); - break; - - case EAXEQUALIZER_MID2CENTER: - defer_mid2_center(eax_call); - break; - - case EAXEQUALIZER_MID2WIDTH: - defer_mid2_width(eax_call); - break; - - case EAXEQUALIZER_HIGHGAIN: - defer_high_gain(eax_call); - break; - - case EAXEQUALIZER_HIGHCUTOFF: - defer_high_cutoff(eax_call); - break; - - default: - throw EaxEqualizerEffectException{"Unsupported property id."}; - } + return is_dirty; } } // namespace -EaxEffectUPtr eax_create_eax_equalizer_effect() +EaxEffectUPtr eax_create_eax_equalizer_effect(const EaxCall& call) { - return std::make_unique(); + return eax_create_eax4_effect(call); } #endif // ALSOFT_EAX diff --git a/al/effects/fshifter.cpp b/al/effects/fshifter.cpp index 1ca97ecb..2b1710ad 100644 --- a/al/effects/fshifter.cpp +++ b/al/effects/fshifter.cpp @@ -12,9 +12,7 @@ #ifdef ALSOFT_EAX #include - #include "alnumeric.h" - #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -141,113 +139,99 @@ const EffectProps FshifterEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { -using EaxFrequencyShifterEffectDirtyFlagsValue = std::uint_least8_t; - -struct EaxFrequencyShifterEffectDirtyFlags -{ - using EaxIsBitFieldStruct = bool; - - EaxFrequencyShifterEffectDirtyFlagsValue flFrequency : 1; - EaxFrequencyShifterEffectDirtyFlagsValue ulLeftDirection : 1; - EaxFrequencyShifterEffectDirtyFlagsValue ulRightDirection : 1; -}; // EaxFrequencyShifterEffectDirtyFlags - - -class EaxFrequencyShifterEffect final : - public EaxEffect +class EaxFrequencyShifterEffectException : public EaxException { public: - EaxFrequencyShifterEffect(); - - void dispatch(const EaxEaxCall& eax_call) override; + explicit EaxFrequencyShifterEffectException(const char* message) + : EaxException{"EAX_FREQUENCY_SHIFTER_EFFECT", message} + {} +}; // EaxFrequencyShifterEffectException - // [[nodiscard]] - bool apply_deferred() override; +class EaxFrequencyShifterEffect final : public EaxEffect4 { +public: + EaxFrequencyShifterEffect(const EaxCall& call); private: - EAXFREQUENCYSHIFTERPROPERTIES eax_{}; - EAXFREQUENCYSHIFTERPROPERTIES eax_d_{}; - EaxFrequencyShifterEffectDirtyFlags eax_dirty_flags_{}; - - void set_eax_defaults(); - - void set_efx_frequency(); + struct FrequencyValidator { + void operator()(float flFrequency) const + { + eax_validate_range( + "Frequency", + flFrequency, + EAXFREQUENCYSHIFTER_MINFREQUENCY, + EAXFREQUENCYSHIFTER_MAXFREQUENCY); + } + }; // FrequencyValidator + + struct LeftDirectionValidator { + void operator()(unsigned long ulLeftDirection) const + { + eax_validate_range( + "Left Direction", + ulLeftDirection, + EAXFREQUENCYSHIFTER_MINLEFTDIRECTION, + EAXFREQUENCYSHIFTER_MAXLEFTDIRECTION); + } + }; // LeftDirectionValidator + + struct RightDirectionValidator { + void operator()(unsigned long ulRightDirection) const + { + eax_validate_range( + "Right Direction", + ulRightDirection, + EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION, + EAXFREQUENCYSHIFTER_MAXRIGHTDIRECTION); + } + }; // RightDirectionValidator + + struct AllValidator { + void operator()(const Props& all) const + { + FrequencyValidator{}(all.flFrequency); + LeftDirectionValidator{}(all.ulLeftDirection); + RightDirectionValidator{}(all.ulRightDirection); + } + }; // AllValidator + + void set_defaults(Props& props) override; + + void set_efx_frequency() noexcept; void set_efx_left_direction(); void set_efx_right_direction(); - void set_efx_defaults(); + void set_efx_defaults() override; - void get(const EaxEaxCall& eax_call); - - void validate_frequency(float flFrequency); - void validate_left_direction(unsigned long ulLeftDirection); - void validate_right_direction(unsigned long ulRightDirection); - void validate_all(const EAXFREQUENCYSHIFTERPROPERTIES& all); - - void defer_frequency(float flFrequency); - void defer_left_direction(unsigned long ulLeftDirection); - void defer_right_direction(unsigned long ulRightDirection); - void defer_all(const EAXFREQUENCYSHIFTERPROPERTIES& all); - - void defer_frequency(const EaxEaxCall& eax_call); - void defer_left_direction(const EaxEaxCall& eax_call); - void defer_right_direction(const EaxEaxCall& eax_call); - void defer_all(const EaxEaxCall& eax_call); - - void set(const EaxEaxCall& eax_call); + void get(const EaxCall& call, const Props& props) override; + void set(const EaxCall& call, Props& props) override; + bool commit_props(const Props& props) override; }; // EaxFrequencyShifterEffect -class EaxFrequencyShifterEffectException : - public EaxException -{ -public: - explicit EaxFrequencyShifterEffectException( - const char* message) - : - EaxException{"EAX_FREQUENCY_SHIFTER_EFFECT", message} - { - } -}; // EaxFrequencyShifterEffectException +EaxFrequencyShifterEffect::EaxFrequencyShifterEffect(const EaxCall& call) + : EaxEffect4{AL_EFFECT_FREQUENCY_SHIFTER, call} +{} - -EaxFrequencyShifterEffect::EaxFrequencyShifterEffect() - : EaxEffect{AL_EFFECT_FREQUENCY_SHIFTER} +void EaxFrequencyShifterEffect::set_defaults(Props& props) { - set_eax_defaults(); - set_efx_defaults(); + props.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY; + props.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION; + props.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION; } -void EaxFrequencyShifterEffect::dispatch(const EaxEaxCall& eax_call) +void EaxFrequencyShifterEffect::set_efx_frequency() noexcept { - eax_call.is_get() ? get(eax_call) : set(eax_call); -} - -void EaxFrequencyShifterEffect::set_eax_defaults() -{ - eax_.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY; - eax_.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION; - eax_.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION; - - eax_d_ = eax_; -} - -void EaxFrequencyShifterEffect::set_efx_frequency() -{ - const auto frequency = clamp( - eax_.flFrequency, + al_effect_props_.Fshifter.Frequency = clamp( + props_.flFrequency, AL_FREQUENCY_SHIFTER_MIN_FREQUENCY, AL_FREQUENCY_SHIFTER_MAX_FREQUENCY); - - al_effect_props_.Fshifter.Frequency = frequency; } void EaxFrequencyShifterEffect::set_efx_left_direction() { const auto left_direction = clamp( - static_cast(eax_.ulLeftDirection), + static_cast(props_.ulLeftDirection), AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION, AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION); - const auto efx_left_direction = DirectionFromEmum(left_direction); assert(efx_left_direction.has_value()); al_effect_props_.Fshifter.LeftDirection = *efx_left_direction; @@ -256,10 +240,9 @@ void EaxFrequencyShifterEffect::set_efx_left_direction() void EaxFrequencyShifterEffect::set_efx_right_direction() { const auto right_direction = clamp( - static_cast(eax_.ulRightDirection), + static_cast(props_.ulRightDirection), AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION, AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION); - const auto efx_right_direction = DirectionFromEmum(right_direction); assert(efx_right_direction.has_value()); al_effect_props_.Fshifter.RightDirection = *efx_right_direction; @@ -272,208 +255,62 @@ void EaxFrequencyShifterEffect::set_efx_defaults() set_efx_right_direction(); } -void EaxFrequencyShifterEffect::get(const EaxEaxCall& eax_call) +void EaxFrequencyShifterEffect::get(const EaxCall& call, const Props& props) { - switch(eax_call.get_property_id()) + switch(call.get_property_id()) { - case EAXFREQUENCYSHIFTER_NONE: - break; - - case EAXFREQUENCYSHIFTER_ALLPARAMETERS: - eax_call.set_value(eax_); - break; - - case EAXFREQUENCYSHIFTER_FREQUENCY: - eax_call.set_value(eax_.flFrequency); - break; - - case EAXFREQUENCYSHIFTER_LEFTDIRECTION: - eax_call.set_value(eax_.ulLeftDirection); - break; - - case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: - eax_call.set_value(eax_.ulRightDirection); - break; - - default: - throw EaxFrequencyShifterEffectException{"Unsupported property id."}; + case EAXFREQUENCYSHIFTER_NONE: break; + case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value(props); break; + case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value(props.flFrequency); break; + case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value(props.ulLeftDirection); break; + case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value(props.ulRightDirection); break; + default: fail_unknown_property_id(); } } -void EaxFrequencyShifterEffect::validate_frequency( - float flFrequency) -{ - eax_validate_range( - "Frequency", - flFrequency, - EAXFREQUENCYSHIFTER_MINFREQUENCY, - EAXFREQUENCYSHIFTER_MAXFREQUENCY); -} - -void EaxFrequencyShifterEffect::validate_left_direction( - unsigned long ulLeftDirection) -{ - eax_validate_range( - "Left Direction", - ulLeftDirection, - EAXFREQUENCYSHIFTER_MINLEFTDIRECTION, - EAXFREQUENCYSHIFTER_MAXLEFTDIRECTION); -} - -void EaxFrequencyShifterEffect::validate_right_direction( - unsigned long ulRightDirection) -{ - eax_validate_range( - "Right Direction", - ulRightDirection, - EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION, - EAXFREQUENCYSHIFTER_MAXRIGHTDIRECTION); -} - -void EaxFrequencyShifterEffect::validate_all( - const EAXFREQUENCYSHIFTERPROPERTIES& all) -{ - validate_frequency(all.flFrequency); - validate_left_direction(all.ulLeftDirection); - validate_right_direction(all.ulRightDirection); -} - -void EaxFrequencyShifterEffect::defer_frequency( - float flFrequency) -{ - eax_d_.flFrequency = flFrequency; - eax_dirty_flags_.flFrequency = (eax_.flFrequency != eax_d_.flFrequency); -} - -void EaxFrequencyShifterEffect::defer_left_direction( - unsigned long ulLeftDirection) -{ - eax_d_.ulLeftDirection = ulLeftDirection; - eax_dirty_flags_.ulLeftDirection = (eax_.ulLeftDirection != eax_d_.ulLeftDirection); -} - -void EaxFrequencyShifterEffect::defer_right_direction( - unsigned long ulRightDirection) +void EaxFrequencyShifterEffect::set(const EaxCall& call, Props& props) { - eax_d_.ulRightDirection = ulRightDirection; - eax_dirty_flags_.ulRightDirection = (eax_.ulRightDirection != eax_d_.ulRightDirection); -} - -void EaxFrequencyShifterEffect::defer_all( - const EAXFREQUENCYSHIFTERPROPERTIES& all) -{ - defer_frequency(all.flFrequency); - defer_left_direction(all.ulLeftDirection); - defer_right_direction(all.ulRightDirection); -} - -void EaxFrequencyShifterEffect::defer_frequency( - const EaxEaxCall& eax_call) -{ - const auto& frequency = - eax_call.get_value< - EaxFrequencyShifterEffectException, const decltype(EAXFREQUENCYSHIFTERPROPERTIES::flFrequency)>(); - - validate_frequency(frequency); - defer_frequency(frequency); -} - -void EaxFrequencyShifterEffect::defer_left_direction( - const EaxEaxCall& eax_call) -{ - const auto& left_direction = - eax_call.get_value< - EaxFrequencyShifterEffectException, const decltype(EAXFREQUENCYSHIFTERPROPERTIES::ulLeftDirection)>(); - - validate_left_direction(left_direction); - defer_left_direction(left_direction); -} - -void EaxFrequencyShifterEffect::defer_right_direction( - const EaxEaxCall& eax_call) -{ - const auto& right_direction = - eax_call.get_value< - EaxFrequencyShifterEffectException, const decltype(EAXFREQUENCYSHIFTERPROPERTIES::ulRightDirection)>(); - - validate_right_direction(right_direction); - defer_right_direction(right_direction); -} - -void EaxFrequencyShifterEffect::defer_all( - const EaxEaxCall& eax_call) -{ - const auto& all = - eax_call.get_value< - EaxFrequencyShifterEffectException, const EAXFREQUENCYSHIFTERPROPERTIES>(); - - validate_all(all); - defer_all(all); -} - -// [[nodiscard]] -bool EaxFrequencyShifterEffect::apply_deferred() -{ - if (eax_dirty_flags_ == EaxFrequencyShifterEffectDirtyFlags{}) + switch(call.get_property_id()) { - return false; + case EAXFREQUENCYSHIFTER_NONE: break; + case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer(call, props); break; + case EAXFREQUENCYSHIFTER_FREQUENCY: defer(call, props.flFrequency); break; + case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer(call, props.ulLeftDirection); break; + case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer(call, props.ulRightDirection); break; + default: fail_unknown_property_id(); } +} - eax_ = eax_d_; +bool EaxFrequencyShifterEffect::commit_props(const Props& props) +{ + auto is_dirty = false; - if (eax_dirty_flags_.flFrequency) + if (props_.flFrequency != props.flFrequency) { + is_dirty = true; set_efx_frequency(); } - if (eax_dirty_flags_.ulLeftDirection) + if (props_.ulLeftDirection != props.ulLeftDirection) { + is_dirty = true; set_efx_left_direction(); } - if (eax_dirty_flags_.ulRightDirection) + if (props_.ulRightDirection != props.ulRightDirection) { + is_dirty = true; set_efx_right_direction(); } - eax_dirty_flags_ = EaxFrequencyShifterEffectDirtyFlags{}; - - return true; -} - -void EaxFrequencyShifterEffect::set(const EaxEaxCall& eax_call) -{ - switch(eax_call.get_property_id()) - { - case EAXFREQUENCYSHIFTER_NONE: - break; - - case EAXFREQUENCYSHIFTER_ALLPARAMETERS: - defer_all(eax_call); - break; - - case EAXFREQUENCYSHIFTER_FREQUENCY: - defer_frequency(eax_call); - break; - - case EAXFREQUENCYSHIFTER_LEFTDIRECTION: - defer_left_direction(eax_call); - break; - - case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: - defer_right_direction(eax_call); - break; - - default: - throw EaxFrequencyShifterEffectException{"Unsupported property id."}; - } + return is_dirty; } } // namespace -EaxEffectUPtr eax_create_eax_frequency_shifter_effect() +EaxEffectUPtr eax_create_eax_frequency_shifter_effect(const EaxCall& call) { - return std::make_unique(); + return eax_create_eax4_effect(call); } #endif // ALSOFT_EAX diff --git a/al/effects/modulator.cpp b/al/effects/modulator.cpp index 082597cd..774fb767 100644 --- a/al/effects/modulator.cpp +++ b/al/effects/modulator.cpp @@ -12,9 +12,7 @@ #ifdef ALSOFT_EAX #include - #include "alnumeric.h" - #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -147,123 +145,107 @@ const EffectProps ModulatorEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { -using EaxRingModulatorEffectDirtyFlagsValue = std::uint_least8_t; - -struct EaxRingModulatorEffectDirtyFlags +class EaxRingModulatorEffectException : public EaxException { - using EaxIsBitFieldStruct = bool; - - EaxRingModulatorEffectDirtyFlagsValue flFrequency : 1; - EaxRingModulatorEffectDirtyFlagsValue flHighPassCutOff : 1; - EaxRingModulatorEffectDirtyFlagsValue ulWaveform : 1; -}; // EaxPitchShifterEffectDirtyFlags - +public: + explicit EaxRingModulatorEffectException(const char* message) + : EaxException{"EAX_RING_MODULATOR_EFFECT", message} + {} +}; // EaxRingModulatorEffectException -class EaxRingModulatorEffect final : - public EaxEffect +class EaxRingModulatorEffect final : public EaxEffect4 { public: - EaxRingModulatorEffect(); - - void dispatch(const EaxEaxCall& eax_call) override; - - // [[nodiscard]] - bool apply_deferred() override; + EaxRingModulatorEffect(const EaxCall& call); private: - EAXRINGMODULATORPROPERTIES eax_{}; - EAXRINGMODULATORPROPERTIES eax_d_{}; - EaxRingModulatorEffectDirtyFlags eax_dirty_flags_{}; - - void set_eax_defaults(); - - void set_efx_frequency(); - void set_efx_high_pass_cutoff(); + struct FrequencyValidator { + void operator()(float flFrequency) const + { + eax_validate_range( + "Frequency", + flFrequency, + EAXRINGMODULATOR_MINFREQUENCY, + EAXRINGMODULATOR_MAXFREQUENCY); + } + }; // FrequencyValidator + + struct HighPassCutOffValidator { + void operator()(float flHighPassCutOff) const + { + eax_validate_range( + "High-Pass Cutoff", + flHighPassCutOff, + EAXRINGMODULATOR_MINHIGHPASSCUTOFF, + EAXRINGMODULATOR_MAXHIGHPASSCUTOFF); + } + }; // HighPassCutOffValidator + + struct WaveformValidator { + void operator()(unsigned long ulWaveform) const + { + eax_validate_range( + "Waveform", + ulWaveform, + EAXRINGMODULATOR_MINWAVEFORM, + EAXRINGMODULATOR_MAXWAVEFORM); + } + }; // WaveformValidator + + struct AllValidator { + void operator()(const Props& all) const + { + FrequencyValidator{}(all.flFrequency); + HighPassCutOffValidator{}(all.flHighPassCutOff); + WaveformValidator{}(all.ulWaveform); + } + }; // AllValidator + + void set_defaults(Props& props) override; + + void set_efx_frequency() noexcept; + void set_efx_high_pass_cutoff() noexcept; void set_efx_waveform(); - void set_efx_defaults(); - - void get(const EaxEaxCall& eax_call); + void set_efx_defaults() override; - void validate_frequency(float flFrequency); - void validate_high_pass_cutoff(float flHighPassCutOff); - void validate_waveform(unsigned long ulWaveform); - void validate_all(const EAXRINGMODULATORPROPERTIES& all); - - void defer_frequency(float flFrequency); - void defer_high_pass_cutoff(float flHighPassCutOff); - void defer_waveform(unsigned long ulWaveform); - void defer_all(const EAXRINGMODULATORPROPERTIES& all); - - void defer_frequency(const EaxEaxCall& eax_call); - void defer_high_pass_cutoff(const EaxEaxCall& eax_call); - void defer_waveform(const EaxEaxCall& eax_call); - void defer_all(const EaxEaxCall& eax_call); - - void set(const EaxEaxCall& eax_call); + void get(const EaxCall& call, const Props& props) override; + void set(const EaxCall& call, Props& props) override; + bool commit_props(const Props& props) override; }; // EaxRingModulatorEffect +EaxRingModulatorEffect::EaxRingModulatorEffect(const EaxCall& call) + : EaxEffect4{AL_EFFECT_RING_MODULATOR, call} +{} -class EaxRingModulatorEffectException : - public EaxException +void EaxRingModulatorEffect::set_defaults(Props& props) { -public: - explicit EaxRingModulatorEffectException( - const char* message) - : - EaxException{"EAX_RING_MODULATOR_EFFECT", message} - { - } -}; // EaxRingModulatorEffectException - - -EaxRingModulatorEffect::EaxRingModulatorEffect() - : EaxEffect{AL_EFFECT_RING_MODULATOR} -{ - set_eax_defaults(); - set_efx_defaults(); + props.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY; + props.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF; + props.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM; } -void EaxRingModulatorEffect::dispatch(const EaxEaxCall& eax_call) +void EaxRingModulatorEffect::set_efx_frequency() noexcept { - eax_call.is_get() ? get(eax_call) : set(eax_call); -} - -void EaxRingModulatorEffect::set_eax_defaults() -{ - eax_.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY; - eax_.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF; - eax_.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM; - - eax_d_ = eax_; -} - -void EaxRingModulatorEffect::set_efx_frequency() -{ - const auto frequency = clamp( - eax_.flFrequency, + al_effect_props_.Modulator.Frequency = clamp( + props_.flFrequency, AL_RING_MODULATOR_MIN_FREQUENCY, AL_RING_MODULATOR_MAX_FREQUENCY); - - al_effect_props_.Modulator.Frequency = frequency; } -void EaxRingModulatorEffect::set_efx_high_pass_cutoff() +void EaxRingModulatorEffect::set_efx_high_pass_cutoff() noexcept { - const auto high_pass_cutoff = clamp( - eax_.flHighPassCutOff, + al_effect_props_.Modulator.HighPassCutoff = clamp( + props_.flHighPassCutOff, AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF, AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF); - - al_effect_props_.Modulator.HighPassCutoff = high_pass_cutoff; } void EaxRingModulatorEffect::set_efx_waveform() { const auto waveform = clamp( - static_cast(eax_.ulWaveform), + static_cast(props_.ulWaveform), AL_RING_MODULATOR_MIN_WAVEFORM, AL_RING_MODULATOR_MAX_WAVEFORM); - const auto efx_waveform = WaveformFromEmum(waveform); assert(efx_waveform.has_value()); al_effect_props_.Modulator.Waveform = *efx_waveform; @@ -276,207 +258,62 @@ void EaxRingModulatorEffect::set_efx_defaults() set_efx_waveform(); } -void EaxRingModulatorEffect::get(const EaxEaxCall& eax_call) +void EaxRingModulatorEffect::get(const EaxCall& call, const Props& props) { - switch(eax_call.get_property_id()) + switch(call.get_property_id()) { - case EAXRINGMODULATOR_NONE: - break; - - case EAXRINGMODULATOR_ALLPARAMETERS: - eax_call.set_value(eax_); - break; - - case EAXRINGMODULATOR_FREQUENCY: - eax_call.set_value(eax_.flFrequency); - break; - - case EAXRINGMODULATOR_HIGHPASSCUTOFF: - eax_call.set_value(eax_.flHighPassCutOff); - break; - - case EAXRINGMODULATOR_WAVEFORM: - eax_call.set_value(eax_.ulWaveform); - break; - - default: - throw EaxRingModulatorEffectException{"Unsupported property id."}; + case EAXRINGMODULATOR_NONE: break; + case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value(props); break; + case EAXRINGMODULATOR_FREQUENCY: call.set_value(props.flFrequency); break; + case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value(props.flHighPassCutOff); break; + case EAXRINGMODULATOR_WAVEFORM: call.set_value(props.ulWaveform); break; + default: fail_unknown_property_id(); } } -void EaxRingModulatorEffect::validate_frequency( - float flFrequency) -{ - eax_validate_range( - "Frequency", - flFrequency, - EAXRINGMODULATOR_MINFREQUENCY, - EAXRINGMODULATOR_MAXFREQUENCY); -} - -void EaxRingModulatorEffect::validate_high_pass_cutoff( - float flHighPassCutOff) -{ - eax_validate_range( - "High-Pass Cutoff", - flHighPassCutOff, - EAXRINGMODULATOR_MINHIGHPASSCUTOFF, - EAXRINGMODULATOR_MAXHIGHPASSCUTOFF); -} - -void EaxRingModulatorEffect::validate_waveform( - unsigned long ulWaveform) -{ - eax_validate_range( - "Waveform", - ulWaveform, - EAXRINGMODULATOR_MINWAVEFORM, - EAXRINGMODULATOR_MAXWAVEFORM); -} - -void EaxRingModulatorEffect::validate_all( - const EAXRINGMODULATORPROPERTIES& all) -{ - validate_frequency(all.flFrequency); - validate_high_pass_cutoff(all.flHighPassCutOff); - validate_waveform(all.ulWaveform); -} - -void EaxRingModulatorEffect::defer_frequency( - float flFrequency) -{ - eax_d_.flFrequency = flFrequency; - eax_dirty_flags_.flFrequency = (eax_.flFrequency != eax_d_.flFrequency); -} - -void EaxRingModulatorEffect::defer_high_pass_cutoff( - float flHighPassCutOff) -{ - eax_d_.flHighPassCutOff = flHighPassCutOff; - eax_dirty_flags_.flHighPassCutOff = (eax_.flHighPassCutOff != eax_d_.flHighPassCutOff); -} - -void EaxRingModulatorEffect::defer_waveform( - unsigned long ulWaveform) +void EaxRingModulatorEffect::set(const EaxCall& call, Props& props) { - eax_d_.ulWaveform = ulWaveform; - eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform); -} - -void EaxRingModulatorEffect::defer_all( - const EAXRINGMODULATORPROPERTIES& all) -{ - defer_frequency(all.flFrequency); - defer_high_pass_cutoff(all.flHighPassCutOff); - defer_waveform(all.ulWaveform); -} - -void EaxRingModulatorEffect::defer_frequency( - const EaxEaxCall& eax_call) -{ - const auto& frequency = - eax_call.get_value< - EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::flFrequency)>(); - - validate_frequency(frequency); - defer_frequency(frequency); -} - -void EaxRingModulatorEffect::defer_high_pass_cutoff( - const EaxEaxCall& eax_call) -{ - const auto& high_pass_cutoff = - eax_call.get_value< - EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::flHighPassCutOff)>(); - - validate_high_pass_cutoff(high_pass_cutoff); - defer_high_pass_cutoff(high_pass_cutoff); -} - -void EaxRingModulatorEffect::defer_waveform( - const EaxEaxCall& eax_call) -{ - const auto& waveform = - eax_call.get_value< - EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::ulWaveform)>(); - - validate_waveform(waveform); - defer_waveform(waveform); -} - -void EaxRingModulatorEffect::defer_all( - const EaxEaxCall& eax_call) -{ - const auto& all = - eax_call.get_value(); - - validate_all(all); - defer_all(all); -} - -// [[nodiscard]] -bool EaxRingModulatorEffect::apply_deferred() -{ - if (eax_dirty_flags_ == EaxRingModulatorEffectDirtyFlags{}) + switch (call.get_property_id()) { - return false; + case EAXRINGMODULATOR_NONE: break; + case EAXRINGMODULATOR_ALLPARAMETERS: defer(call, props); break; + case EAXRINGMODULATOR_FREQUENCY: defer(call, props.flFrequency); break; + case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer(call, props.flHighPassCutOff); break; + case EAXRINGMODULATOR_WAVEFORM: defer(call, props.ulWaveform); break; + default: fail_unknown_property_id(); } +} - eax_ = eax_d_; +bool EaxRingModulatorEffect::commit_props(const Props& props) +{ + auto is_dirty = false; - if (eax_dirty_flags_.flFrequency) + if (props_.flFrequency != props.flFrequency) { + is_dirty = true; set_efx_frequency(); } - if (eax_dirty_flags_.flHighPassCutOff) + if (props_.flHighPassCutOff != props.flHighPassCutOff) { + is_dirty = true; set_efx_high_pass_cutoff(); } - if (eax_dirty_flags_.ulWaveform) + if (props_.ulWaveform != props.ulWaveform) { + is_dirty = true; set_efx_waveform(); } - eax_dirty_flags_ = EaxRingModulatorEffectDirtyFlags{}; - - return true; -} - -void EaxRingModulatorEffect::set(const EaxEaxCall& eax_call) -{ - switch (eax_call.get_property_id()) - { - case EAXRINGMODULATOR_NONE: - break; - - case EAXRINGMODULATOR_ALLPARAMETERS: - defer_all(eax_call); - break; - - case EAXRINGMODULATOR_FREQUENCY: - defer_frequency(eax_call); - break; - - case EAXRINGMODULATOR_HIGHPASSCUTOFF: - defer_high_pass_cutoff(eax_call); - break; - - case EAXRINGMODULATOR_WAVEFORM: - defer_waveform(eax_call); - break; - - default: - throw EaxRingModulatorEffectException{"Unsupported property id."}; - } + return is_dirty; } } // namespace -EaxEffectUPtr eax_create_eax_ring_modulator_effect() +EaxEffectUPtr eax_create_eax_ring_modulator_effect(const EaxCall& call) { - return std::make_unique(); + return eax_create_eax4_effect(call); } #endif // ALSOFT_EAX diff --git a/al/effects/null.cpp b/al/effects/null.cpp index 0ec387d4..2243dfe1 100644 --- a/al/effects/null.cpp +++ b/al/effects/null.cpp @@ -100,44 +100,34 @@ const EffectProps NullEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { -class EaxNullEffect final : - public EaxEffect -{ +class EaxNullEffect final : public EaxEffect { public: - EaxNullEffect(); - - void dispatch(const EaxEaxCall& eax_call) override; + EaxNullEffect() noexcept; - // [[nodiscard]] - bool apply_deferred() override; + void dispatch(const EaxCall& call) override; + /*[[nodiscard]]*/ bool commit() override; }; // EaxNullEffect -class EaxNullEffectException : - public EaxException +class EaxNullEffectException : public EaxException { public: - explicit EaxNullEffectException( - const char* message) - : - EaxException{"EAX_NULL_EFFECT", message} - { - } + explicit EaxNullEffectException(const char* message) + : EaxException{"EAX_NULL_EFFECT", message} + {} }; // EaxNullEffectException - -EaxNullEffect::EaxNullEffect() +EaxNullEffect::EaxNullEffect() noexcept : EaxEffect{AL_EFFECT_NULL} -{ -} +{} -void EaxNullEffect::dispatch(const EaxEaxCall& eax_call) +void EaxNullEffect::dispatch(const EaxCall& call) { - if(eax_call.get_property_id() != 0) + if(call.get_property_id() != 0) throw EaxNullEffectException{"Unsupported property id."}; } -bool EaxNullEffect::apply_deferred() +bool EaxNullEffect::commit() { return false; } diff --git a/al/effects/pshifter.cpp b/al/effects/pshifter.cpp index cb81d831..51dbdd8f 100644 --- a/al/effects/pshifter.cpp +++ b/al/effects/pshifter.cpp @@ -9,7 +9,6 @@ #ifdef ALSOFT_EAX #include "alnumeric.h" - #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -93,108 +92,84 @@ const EffectProps PshifterEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { -using EaxPitchShifterEffectDirtyFlagsValue = std::uint_least8_t; - -struct EaxPitchShifterEffectDirtyFlags -{ - using EaxIsBitFieldStruct = bool; - - EaxPitchShifterEffectDirtyFlagsValue lCoarseTune : 1; - EaxPitchShifterEffectDirtyFlagsValue lFineTune : 1; -}; // EaxPitchShifterEffectDirtyFlags - - -class EaxPitchShifterEffect final : - public EaxEffect +class EaxPitchShifterEffectException : public EaxException { public: - EaxPitchShifterEffect(); - - void dispatch(const EaxEaxCall& eax_call) override; + explicit EaxPitchShifterEffectException(const char* message) + : EaxException{"EAX_PITCH_SHIFTER_EFFECT", message} + {} +}; // EaxPitchShifterEffectException - // [[nodiscard]] - bool apply_deferred() override; +class EaxPitchShifterEffect final : public EaxEffect4 { +public: + EaxPitchShifterEffect(const EaxCall& call); private: - EAXPITCHSHIFTERPROPERTIES eax_{}; - EAXPITCHSHIFTERPROPERTIES eax_d_{}; - EaxPitchShifterEffectDirtyFlags eax_dirty_flags_{}; - - void set_eax_defaults(); - - void set_efx_coarse_tune(); - void set_efx_fine_tune(); - void set_efx_defaults(); - - void get(const EaxEaxCall& eax_call); - - void validate_coarse_tune(long lCoarseTune); - void validate_fine_tune(long lFineTune); - void validate_all(const EAXPITCHSHIFTERPROPERTIES& all); - - void defer_coarse_tune(long lCoarseTune); - void defer_fine_tune(long lFineTune); - void defer_all(const EAXPITCHSHIFTERPROPERTIES& all); - - void defer_coarse_tune(const EaxEaxCall& eax_call); - void defer_fine_tune(const EaxEaxCall& eax_call); - void defer_all(const EaxEaxCall& eax_call); - - void set(const EaxEaxCall& eax_call); + struct CoarseTuneValidator { + void operator()(long lCoarseTune) const + { + eax_validate_range( + "Coarse Tune", + lCoarseTune, + EAXPITCHSHIFTER_MINCOARSETUNE, + EAXPITCHSHIFTER_MAXCOARSETUNE); + } + }; // CoarseTuneValidator + + struct FineTuneValidator { + void operator()(long lFineTune) const + { + eax_validate_range( + "Fine Tune", + lFineTune, + EAXPITCHSHIFTER_MINFINETUNE, + EAXPITCHSHIFTER_MAXFINETUNE); + } + }; // FineTuneValidator + + struct AllValidator { + void operator()(const Props& all) const + { + CoarseTuneValidator{}(all.lCoarseTune); + FineTuneValidator{}(all.lFineTune); + } + }; // AllValidator + + void set_defaults(Props& props) override; + + void set_efx_coarse_tune() noexcept; + void set_efx_fine_tune() noexcept; + void set_efx_defaults() override; + + void get(const EaxCall& call, const Props& props) override; + void set(const EaxCall& call, Props& props) override; + bool commit_props(const Props& old_i) override; }; // EaxPitchShifterEffect +EaxPitchShifterEffect::EaxPitchShifterEffect(const EaxCall& call) + : EaxEffect4{AL_EFFECT_PITCH_SHIFTER, call} +{} -class EaxPitchShifterEffectException : - public EaxException +void EaxPitchShifterEffect::set_defaults(Props& props) { -public: - explicit EaxPitchShifterEffectException( - const char* message) - : - EaxException{"EAX_PITCH_SHIFTER_EFFECT", message} - { - } -}; // EaxPitchShifterEffectException - - -EaxPitchShifterEffect::EaxPitchShifterEffect() - : EaxEffect{AL_EFFECT_PITCH_SHIFTER} -{ - set_eax_defaults(); - set_efx_defaults(); -} - -void EaxPitchShifterEffect::dispatch(const EaxEaxCall& eax_call) -{ - eax_call.is_get() ? get(eax_call) : set(eax_call); -} - -void EaxPitchShifterEffect::set_eax_defaults() -{ - eax_.lCoarseTune = EAXPITCHSHIFTER_DEFAULTCOARSETUNE; - eax_.lFineTune = EAXPITCHSHIFTER_DEFAULTFINETUNE; - - eax_d_ = eax_; + props.lCoarseTune = EAXPITCHSHIFTER_DEFAULTCOARSETUNE; + props.lFineTune = EAXPITCHSHIFTER_DEFAULTFINETUNE; } -void EaxPitchShifterEffect::set_efx_coarse_tune() +void EaxPitchShifterEffect::set_efx_coarse_tune() noexcept { - const auto coarse_tune = clamp( - static_cast(eax_.lCoarseTune), + al_effect_props_.Pshifter.CoarseTune = clamp( + static_cast(props_.lCoarseTune), AL_PITCH_SHIFTER_MIN_COARSE_TUNE, AL_PITCH_SHIFTER_MAX_COARSE_TUNE); - - al_effect_props_.Pshifter.CoarseTune = coarse_tune; } -void EaxPitchShifterEffect::set_efx_fine_tune() +void EaxPitchShifterEffect::set_efx_fine_tune() noexcept { - const auto fine_tune = clamp( - static_cast(eax_.lFineTune), + al_effect_props_.Pshifter.FineTune = clamp( + static_cast(props_.lFineTune), AL_PITCH_SHIFTER_MIN_FINE_TUNE, AL_PITCH_SHIFTER_MAX_FINE_TUNE); - - al_effect_props_.Pshifter.FineTune = fine_tune; } void EaxPitchShifterEffect::set_efx_defaults() @@ -203,162 +178,54 @@ void EaxPitchShifterEffect::set_efx_defaults() set_efx_fine_tune(); } -void EaxPitchShifterEffect::get(const EaxEaxCall& eax_call) +void EaxPitchShifterEffect::get(const EaxCall& call, const Props& props) { - switch(eax_call.get_property_id()) + switch(call.get_property_id()) { - case EAXPITCHSHIFTER_NONE: - break; - - case EAXPITCHSHIFTER_ALLPARAMETERS: - eax_call.set_value(eax_); - break; - - case EAXPITCHSHIFTER_COARSETUNE: - eax_call.set_value(eax_.lCoarseTune); - break; - - case EAXPITCHSHIFTER_FINETUNE: - eax_call.set_value(eax_.lFineTune); - break; - - default: - throw EaxPitchShifterEffectException{"Unsupported property id."}; + case EAXPITCHSHIFTER_NONE: break; + case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value(props); break; + case EAXPITCHSHIFTER_COARSETUNE: call.set_value(props.lCoarseTune); break; + case EAXPITCHSHIFTER_FINETUNE: call.set_value(props.lFineTune); break; + default: fail_unknown_property_id(); } } -void EaxPitchShifterEffect::validate_coarse_tune( - long lCoarseTune) -{ - eax_validate_range( - "Coarse Tune", - lCoarseTune, - EAXPITCHSHIFTER_MINCOARSETUNE, - EAXPITCHSHIFTER_MAXCOARSETUNE); -} - -void EaxPitchShifterEffect::validate_fine_tune( - long lFineTune) +void EaxPitchShifterEffect::set(const EaxCall& call, Props& props) { - eax_validate_range( - "Fine Tune", - lFineTune, - EAXPITCHSHIFTER_MINFINETUNE, - EAXPITCHSHIFTER_MAXFINETUNE); -} - -void EaxPitchShifterEffect::validate_all( - const EAXPITCHSHIFTERPROPERTIES& all) -{ - validate_coarse_tune(all.lCoarseTune); - validate_fine_tune(all.lFineTune); -} - -void EaxPitchShifterEffect::defer_coarse_tune( - long lCoarseTune) -{ - eax_d_.lCoarseTune = lCoarseTune; - eax_dirty_flags_.lCoarseTune = (eax_.lCoarseTune != eax_d_.lCoarseTune); -} - -void EaxPitchShifterEffect::defer_fine_tune( - long lFineTune) -{ - eax_d_.lFineTune = lFineTune; - eax_dirty_flags_.lFineTune = (eax_.lFineTune != eax_d_.lFineTune); -} - -void EaxPitchShifterEffect::defer_all( - const EAXPITCHSHIFTERPROPERTIES& all) -{ - defer_coarse_tune(all.lCoarseTune); - defer_fine_tune(all.lFineTune); -} - -void EaxPitchShifterEffect::defer_coarse_tune( - const EaxEaxCall& eax_call) -{ - const auto& coarse_tune = - eax_call.get_value(); - - validate_coarse_tune(coarse_tune); - defer_coarse_tune(coarse_tune); -} - -void EaxPitchShifterEffect::defer_fine_tune( - const EaxEaxCall& eax_call) -{ - const auto& fine_tune = - eax_call.get_value(); - - validate_fine_tune(fine_tune); - defer_fine_tune(fine_tune); -} - -void EaxPitchShifterEffect::defer_all( - const EaxEaxCall& eax_call) -{ - const auto& all = - eax_call.get_value(); - - validate_all(all); - defer_all(all); -} - -// [[nodiscard]] -bool EaxPitchShifterEffect::apply_deferred() -{ - if (eax_dirty_flags_ == EaxPitchShifterEffectDirtyFlags{}) + switch(call.get_property_id()) { - return false; + case EAXPITCHSHIFTER_NONE: break; + case EAXPITCHSHIFTER_ALLPARAMETERS: defer(call, props); break; + case EAXPITCHSHIFTER_COARSETUNE: defer(call, props.lCoarseTune); break; + case EAXPITCHSHIFTER_FINETUNE: defer(call, props.lFineTune); break; + default: fail_unknown_property_id(); } +} - eax_ = eax_d_; +bool EaxPitchShifterEffect::commit_props(const Props& props) +{ + auto is_dirty = false; - if (eax_dirty_flags_.lCoarseTune) + if (props_.lCoarseTune != props.lCoarseTune) { + is_dirty = true; set_efx_coarse_tune(); } - if (eax_dirty_flags_.lFineTune) + if (props_.lFineTune != props.lFineTune) { + is_dirty = true; set_efx_fine_tune(); } - eax_dirty_flags_ = EaxPitchShifterEffectDirtyFlags{}; - - return true; -} - -void EaxPitchShifterEffect::set(const EaxEaxCall& eax_call) -{ - switch(eax_call.get_property_id()) - { - case EAXPITCHSHIFTER_NONE: - break; - - case EAXPITCHSHIFTER_ALLPARAMETERS: - defer_all(eax_call); - break; - - case EAXPITCHSHIFTER_COARSETUNE: - defer_coarse_tune(eax_call); - break; - - case EAXPITCHSHIFTER_FINETUNE: - defer_fine_tune(eax_call); - break; - - default: - throw EaxPitchShifterEffectException{"Unsupported property id."}; - } + return is_dirty; } } // namespace -EaxEffectUPtr eax_create_eax_pitch_shifter_effect() +EaxEffectUPtr eax_create_eax_pitch_shifter_effect(const EaxCall& call) { - return std::make_unique(); + return eax_create_eax4_effect(call); } #endif // ALSOFT_EAX diff --git a/al/effects/reverb.cpp b/al/effects/reverb.cpp index 4184eda0..da4c3fb4 100644 --- a/al/effects/reverb.cpp +++ b/al/effects/reverb.cpp @@ -10,12 +10,9 @@ #include "effects.h" #ifdef ALSOFT_EAX -#include - -#include "AL/efx-presets.h" - +#include #include "alnumeric.h" - +#include "AL/efx-presets.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -569,475 +566,884 @@ const EffectProps StdReverbEffectProps{genDefaultStdProps()}; #ifdef ALSOFT_EAX namespace { -extern const EFXEAXREVERBPROPERTIES eax_efx_reverb_presets[]; - -using EaxReverbEffectDirtyFlagsValue = std::uint_least32_t; - -struct EaxReverbEffectDirtyFlags -{ - using EaxIsBitFieldStruct = bool; - - EaxReverbEffectDirtyFlagsValue ulEnvironment : 1; - EaxReverbEffectDirtyFlagsValue flEnvironmentSize : 1; - EaxReverbEffectDirtyFlagsValue flEnvironmentDiffusion : 1; - EaxReverbEffectDirtyFlagsValue lRoom : 1; - EaxReverbEffectDirtyFlagsValue lRoomHF : 1; - EaxReverbEffectDirtyFlagsValue lRoomLF : 1; - EaxReverbEffectDirtyFlagsValue flDecayTime : 1; - EaxReverbEffectDirtyFlagsValue flDecayHFRatio : 1; - EaxReverbEffectDirtyFlagsValue flDecayLFRatio : 1; - EaxReverbEffectDirtyFlagsValue lReflections : 1; - EaxReverbEffectDirtyFlagsValue flReflectionsDelay : 1; - EaxReverbEffectDirtyFlagsValue vReflectionsPan : 1; - EaxReverbEffectDirtyFlagsValue lReverb : 1; - EaxReverbEffectDirtyFlagsValue flReverbDelay : 1; - EaxReverbEffectDirtyFlagsValue vReverbPan : 1; - EaxReverbEffectDirtyFlagsValue flEchoTime : 1; - EaxReverbEffectDirtyFlagsValue flEchoDepth : 1; - EaxReverbEffectDirtyFlagsValue flModulationTime : 1; - EaxReverbEffectDirtyFlagsValue flModulationDepth : 1; - EaxReverbEffectDirtyFlagsValue flAirAbsorptionHF : 1; - EaxReverbEffectDirtyFlagsValue flHFReference : 1; - EaxReverbEffectDirtyFlagsValue flLFReference : 1; - EaxReverbEffectDirtyFlagsValue flRoomRolloffFactor : 1; - EaxReverbEffectDirtyFlagsValue ulFlags : 1; -}; // EaxReverbEffectDirtyFlags - -struct Eax1ReverbEffectDirtyFlags +class EaxReverbEffectException : public EaxException { - using EaxIsBitFieldStruct = bool; - - EaxReverbEffectDirtyFlagsValue ulEnvironment : 1; - EaxReverbEffectDirtyFlagsValue flVolume : 1; - EaxReverbEffectDirtyFlagsValue flDecayTime : 1; - EaxReverbEffectDirtyFlagsValue flDamping : 1; -}; // Eax1ReverbEffectDirtyFlags +public: + explicit EaxReverbEffectException(const char* message) + : EaxException{"EAX_REVERB_EFFECT", message} + {} +}; // EaxReverbEffectException -class EaxReverbEffect final : - public EaxEffect +class EaxReverbEffect final : public EaxEffect { public: - EaxReverbEffect(); - - void dispatch(const EaxEaxCall& eax_call) override; + EaxReverbEffect(const EaxCall& call) noexcept; - // [[nodiscard]] - bool apply_deferred() override; + void dispatch(const EaxCall& call) override; + /*[[nodiscard]]*/ bool commit() override; private: - EAX_REVERBPROPERTIES eax1_{}; - EAX_REVERBPROPERTIES eax1_d_{}; - Eax1ReverbEffectDirtyFlags eax1_dirty_flags_{}; - EAXREVERBPROPERTIES eax_{}; - EAXREVERBPROPERTIES eax_d_{}; - EaxReverbEffectDirtyFlags eax_dirty_flags_{}; - - [[noreturn]] static void eax_fail(const char* message); - - void set_eax_defaults(); - - void set_efx_density_from_environment_size(); - void set_efx_diffusion(); - void set_efx_gain(); - void set_efx_gain_hf(); - void set_efx_gain_lf(); - void set_efx_decay_time(); - void set_efx_decay_hf_ratio(); - void set_efx_decay_lf_ratio(); - void set_efx_reflections_gain(); - void set_efx_reflections_delay(); - void set_efx_reflections_pan(); - void set_efx_late_reverb_gain(); - void set_efx_late_reverb_delay(); - void set_efx_late_reverb_pan(); - void set_efx_echo_time(); - void set_efx_echo_depth(); - void set_efx_modulation_time(); - void set_efx_modulation_depth(); - void set_efx_air_absorption_gain_hf(); - void set_efx_hf_reference(); - void set_efx_lf_reference(); - void set_efx_room_rolloff_factor(); - void set_efx_flags(); - void set_efx_defaults(); - - void v1_get(const EaxEaxCall& eax_call) const; - - void get_all(const EaxEaxCall& eax_call) const; - - void get(const EaxEaxCall& eax_call) const; - - static void v1_validate_environment(unsigned long environment); - static void v1_validate_volume(float volume); - static void v1_validate_decay_time(float decay_time); - static void v1_validate_damping(float damping); - static void v1_validate_all(const EAX_REVERBPROPERTIES& all); - - void v1_defer_environment(unsigned long environment); - void v1_defer_volume(float volume); - void v1_defer_decay_time(float decay_time); - void v1_defer_damping(float damping); - void v1_defer_all(const EAX_REVERBPROPERTIES& all); - - void v1_defer_environment(const EaxEaxCall& eax_call); - void v1_defer_volume(const EaxEaxCall& eax_call); - void v1_defer_decay_time(const EaxEaxCall& eax_call); - void v1_defer_damping(const EaxEaxCall& eax_call); - void v1_defer_all(const EaxEaxCall& eax_call); - void v1_defer(const EaxEaxCall& eax_call); - - void v1_set_efx(); - - static void validate_environment(unsigned long ulEnvironment, int version, bool is_standalone); - static void validate_environment_size(float flEnvironmentSize); - static void validate_environment_diffusion(float flEnvironmentDiffusion); - static void validate_room(long lRoom); - static void validate_room_hf(long lRoomHF); - static void validate_room_lf(long lRoomLF); - static void validate_decay_time(float flDecayTime); - static void validate_decay_hf_ratio(float flDecayHFRatio); - static void validate_decay_lf_ratio(float flDecayLFRatio); - static void validate_reflections(long lReflections); - static void validate_reflections_delay(float flReflectionsDelay); - static void validate_reflections_pan(const EAXVECTOR& vReflectionsPan); - static void validate_reverb(long lReverb); - static void validate_reverb_delay(float flReverbDelay); - static void validate_reverb_pan(const EAXVECTOR& vReverbPan); - static void validate_echo_time(float flEchoTime); - static void validate_echo_depth(float flEchoDepth); - static void validate_modulation_time(float flModulationTime); - static void validate_modulation_depth(float flModulationDepth); - static void validate_air_absorbtion_hf(float air_absorbtion_hf); - static void validate_hf_reference(float flHFReference); - static void validate_lf_reference(float flLFReference); - static void validate_room_rolloff_factor(float flRoomRolloffFactor); - static void validate_flags(unsigned long ulFlags); - static void validate_all(const EAX20LISTENERPROPERTIES& all, int version); - static void validate_all(const EAXREVERBPROPERTIES& all, int version); - - void defer_environment(unsigned long ulEnvironment); - void defer_environment_size(float flEnvironmentSize); - void defer_environment_diffusion(float flEnvironmentDiffusion); - void defer_room(long lRoom); - void defer_room_hf(long lRoomHF); - void defer_room_lf(long lRoomLF); - void defer_decay_time(float flDecayTime); - void defer_decay_hf_ratio(float flDecayHFRatio); - void defer_decay_lf_ratio(float flDecayLFRatio); - void defer_reflections(long lReflections); - void defer_reflections_delay(float flReflectionsDelay); - void defer_reflections_pan(const EAXVECTOR& vReflectionsPan); - void defer_reverb(long lReverb); - void defer_reverb_delay(float flReverbDelay); - void defer_reverb_pan(const EAXVECTOR& vReverbPan); - void defer_echo_time(float flEchoTime); - void defer_echo_depth(float flEchoDepth); - void defer_modulation_time(float flModulationTime); - void defer_modulation_depth(float flModulationDepth); - void defer_air_absorbtion_hf(float flAirAbsorptionHF); - void defer_hf_reference(float flHFReference); - void defer_lf_reference(float flLFReference); - void defer_room_rolloff_factor(float flRoomRolloffFactor); - void defer_flags(unsigned long ulFlags); - void defer_all(const EAX20LISTENERPROPERTIES& all); - void defer_all(const EAXREVERBPROPERTIES& all); - - void defer_environment(const EaxEaxCall& eax_call); - void defer_environment_size(const EaxEaxCall& eax_call); - void defer_environment_diffusion(const EaxEaxCall& eax_call); - void defer_room(const EaxEaxCall& eax_call); - void defer_room_hf(const EaxEaxCall& eax_call); - void defer_room_lf(const EaxEaxCall& eax_call); - void defer_decay_time(const EaxEaxCall& eax_call); - void defer_decay_hf_ratio(const EaxEaxCall& eax_call); - void defer_decay_lf_ratio(const EaxEaxCall& eax_call); - void defer_reflections(const EaxEaxCall& eax_call); - void defer_reflections_delay(const EaxEaxCall& eax_call); - void defer_reflections_pan(const EaxEaxCall& eax_call); - void defer_reverb(const EaxEaxCall& eax_call); - void defer_reverb_delay(const EaxEaxCall& eax_call); - void defer_reverb_pan(const EaxEaxCall& eax_call); - void defer_echo_time(const EaxEaxCall& eax_call); - void defer_echo_depth(const EaxEaxCall& eax_call); - void defer_modulation_time(const EaxEaxCall& eax_call); - void defer_modulation_depth(const EaxEaxCall& eax_call); - void defer_air_absorbtion_hf(const EaxEaxCall& eax_call); - void defer_hf_reference(const EaxEaxCall& eax_call); - void defer_lf_reference(const EaxEaxCall& eax_call); - void defer_room_rolloff_factor(const EaxEaxCall& eax_call); - void defer_flags(const EaxEaxCall& eax_call); - void defer_all(const EaxEaxCall& eax_call); - - void set(const EaxEaxCall& eax_call); -}; // EaxReverbEffect + static constexpr auto initial_room2 = -10'000L; + using Exception = EaxReverbEffectException; -class EaxReverbEffectException : - public EaxException -{ -public: - explicit EaxReverbEffectException( - const char* message) - : - EaxException{"EAX_REVERB_EFFECT", message} + using Props1 = EAX_REVERBPROPERTIES; + using Props2 = EAX20LISTENERPROPERTIES; + using Props3 = EAXREVERBPROPERTIES; + + struct State1 + { + Props1 i; // Immediate. + Props1 d; // Deferred. + }; // State1 + + struct State2 + { + Props2 i; // Immediate. + Props2 d; // Deferred. + }; // State2 + + struct State3 + { + Props3 i; // Immediate. + Props3 d; // Deferred. + }; // State3 + + struct EnvironmentValidator1 { + void operator()(unsigned long ulEnvironment) const + { + eax_validate_range( + "Environment", + ulEnvironment, + EAXREVERB_MINENVIRONMENT, + EAX1REVERB_MAXENVIRONMENT); + } + }; // EnvironmentValidator1 + + struct VolumeValidator { + void operator()(float volume) const + { + eax_validate_range( + "Volume", + volume, + EAX1REVERB_MINVOLUME, + EAX1REVERB_MAXVOLUME); + } + }; // VolumeValidator + + struct DecayTimeValidator { + void operator()(float flDecayTime) const + { + eax_validate_range( + "Decay Time", + flDecayTime, + EAXREVERB_MINDECAYTIME, + EAXREVERB_MAXDECAYTIME); + } + }; // DecayTimeValidator + + struct DampingValidator { + void operator()(float damping) const + { + eax_validate_range( + "Damping", + damping, + EAX1REVERB_MINDAMPING, + EAX1REVERB_MAXDAMPING); + } + }; // DampingValidator + + struct AllValidator1 { + void operator()(const Props1& all) const + { + EnvironmentValidator1{}(all.environment); + VolumeValidator{}(all.fVolume); + DecayTimeValidator{}(all.fDecayTime_sec); + DampingValidator{}(all.fDamping); + } + }; // AllValidator1 + + struct RoomValidator { + void operator()(long lRoom) const + { + eax_validate_range( + "Room", + lRoom, + EAXREVERB_MINROOM, + EAXREVERB_MAXROOM); + } + }; // RoomValidator + + struct RoomHFValidator { + void operator()(long lRoomHF) const + { + eax_validate_range( + "Room HF", + lRoomHF, + EAXREVERB_MINROOMHF, + EAXREVERB_MAXROOMHF); + } + }; // RoomHFValidator + + struct RoomRolloffFactorValidator { + void operator()(float flRoomRolloffFactor) const + { + eax_validate_range( + "Room Rolloff Factor", + flRoomRolloffFactor, + EAXREVERB_MINROOMROLLOFFFACTOR, + EAXREVERB_MAXROOMROLLOFFFACTOR); + } + }; // RoomRolloffFactorValidator + + struct DecayHFRatioValidator { + void operator()(float flDecayHFRatio) const + { + eax_validate_range( + "Decay HF Ratio", + flDecayHFRatio, + EAXREVERB_MINDECAYHFRATIO, + EAXREVERB_MAXDECAYHFRATIO); + } + }; // DecayHFRatioValidator + + struct ReflectionsValidator { + void operator()(long lReflections) const + { + eax_validate_range( + "Reflections", + lReflections, + EAXREVERB_MINREFLECTIONS, + EAXREVERB_MAXREFLECTIONS); + } + }; // ReflectionsValidator + + struct ReflectionsDelayValidator { + void operator()(float flReflectionsDelay) const + { + eax_validate_range( + "Reflections Delay", + flReflectionsDelay, + EAXREVERB_MINREFLECTIONSDELAY, + EAXREVERB_MAXREFLECTIONSDELAY); + } + }; // ReflectionsDelayValidator + + struct ReverbValidator { + void operator()(long lReverb) const + { + eax_validate_range( + "Reverb", + lReverb, + EAXREVERB_MINREVERB, + EAXREVERB_MAXREVERB); + } + }; // ReverbValidator + + struct ReverbDelayValidator { + void operator()(float flReverbDelay) const + { + eax_validate_range( + "Reverb Delay", + flReverbDelay, + EAXREVERB_MINREVERBDELAY, + EAXREVERB_MAXREVERBDELAY); + } + }; // ReverbDelayValidator + + struct EnvironmentSizeValidator { + void operator()(float flEnvironmentSize) const + { + eax_validate_range( + "Environment Size", + flEnvironmentSize, + EAXREVERB_MINENVIRONMENTSIZE, + EAXREVERB_MAXENVIRONMENTSIZE); + } + }; // EnvironmentSizeValidator + + struct EnvironmentDiffusionValidator { + void operator()(float flEnvironmentDiffusion) const + { + eax_validate_range( + "Environment Diffusion", + flEnvironmentDiffusion, + EAXREVERB_MINENVIRONMENTDIFFUSION, + EAXREVERB_MAXENVIRONMENTDIFFUSION); + } + }; // EnvironmentDiffusionValidator + + struct AirAbsorptionHFValidator { + void operator()(float flAirAbsorptionHF) const + { + eax_validate_range( + "Air Absorbtion HF", + flAirAbsorptionHF, + EAXREVERB_MINAIRABSORPTIONHF, + EAXREVERB_MAXAIRABSORPTIONHF); + } + }; // AirAbsorptionHFValidator + + struct FlagsValidator2 { + void operator()(unsigned long ulFlags) const + { + eax_validate_range( + "Flags", + ulFlags, + 0UL, + ~EAX2LISTENERFLAGS_RESERVED); + } + }; // FlagsValidator2 + + struct AllValidator2 { + void operator()(const Props2& all) const + { + RoomValidator{}(all.lRoom); + RoomHFValidator{}(all.lRoomHF); + RoomRolloffFactorValidator{}(all.flRoomRolloffFactor); + DecayTimeValidator{}(all.flDecayTime); + DecayHFRatioValidator{}(all.flDecayHFRatio); + ReflectionsValidator{}(all.lReflections); + ReflectionsDelayValidator{}(all.flReflectionsDelay); + ReverbValidator{}(all.lReverb); + ReverbDelayValidator{}(all.flReverbDelay); + EnvironmentValidator1{}(all.dwEnvironment); + EnvironmentSizeValidator{}(all.flEnvironmentSize); + EnvironmentDiffusionValidator{}(all.flEnvironmentDiffusion); + AirAbsorptionHFValidator{}(all.flAirAbsorptionHF); + FlagsValidator2{}(all.dwFlags); + } + }; // AllValidator2 + + struct EnvironmentValidator3 { + void operator()(unsigned long ulEnvironment) const + { + eax_validate_range( + "Environment", + ulEnvironment, + EAXREVERB_MINENVIRONMENT, + EAX30REVERB_MAXENVIRONMENT); + } + }; // EnvironmentValidator1 + + struct RoomLFValidator { + void operator()(long lRoomLF) const + { + eax_validate_range( + "Room LF", + lRoomLF, + EAXREVERB_MINROOMLF, + EAXREVERB_MAXROOMLF); + } + }; // RoomLFValidator + + struct DecayLFRatioValidator { + void operator()(float flDecayLFRatio) const + { + eax_validate_range( + "Decay LF Ratio", + flDecayLFRatio, + EAXREVERB_MINDECAYLFRATIO, + EAXREVERB_MAXDECAYLFRATIO); + } + }; // DecayLFRatioValidator + + struct VectorValidator { + void operator()(const EAXVECTOR&) const + {} + }; // VectorValidator + + struct EchoTimeValidator { + void operator()(float flEchoTime) const + { + eax_validate_range( + "Echo Time", + flEchoTime, + EAXREVERB_MINECHOTIME, + EAXREVERB_MAXECHOTIME); + } + }; // EchoTimeValidator + + struct EchoDepthValidator { + void operator()(float flEchoDepth) const + { + eax_validate_range( + "Echo Depth", + flEchoDepth, + EAXREVERB_MINECHODEPTH, + EAXREVERB_MAXECHODEPTH); + } + }; // EchoDepthValidator + + struct ModulationTimeValidator { + void operator()(float flModulationTime) const + { + eax_validate_range( + "Modulation Time", + flModulationTime, + EAXREVERB_MINMODULATIONTIME, + EAXREVERB_MAXMODULATIONTIME); + } + }; // ModulationTimeValidator + + struct ModulationDepthValidator { + void operator()(float flModulationDepth) const + { + eax_validate_range( + "Modulation Depth", + flModulationDepth, + EAXREVERB_MINMODULATIONDEPTH, + EAXREVERB_MAXMODULATIONDEPTH); + } + }; // ModulationDepthValidator + + struct HFReferenceValidator { + void operator()(float flHFReference) const + { + eax_validate_range( + "HF Reference", + flHFReference, + EAXREVERB_MINHFREFERENCE, + EAXREVERB_MAXHFREFERENCE); + } + }; // HFReferenceValidator + + struct LFReferenceValidator { + void operator()(float flLFReference) const + { + eax_validate_range( + "LF Reference", + flLFReference, + EAXREVERB_MINLFREFERENCE, + EAXREVERB_MAXLFREFERENCE); + } + }; // LFReferenceValidator + + struct FlagsValidator3 { + void operator()(unsigned long ulFlags) const + { + eax_validate_range( + "Flags", + ulFlags, + 0UL, + ~EAXREVERBFLAGS_RESERVED); + } + }; // FlagsValidator3 + + struct AllValidator3 { + void operator()(const Props3& all) const + { + EnvironmentValidator3{}(all.ulEnvironment); + EnvironmentSizeValidator{}(all.flEnvironmentSize); + EnvironmentDiffusionValidator{}(all.flEnvironmentDiffusion); + RoomValidator{}(all.lRoom); + RoomHFValidator{}(all.lRoomHF); + RoomLFValidator{}(all.lRoomLF); + DecayTimeValidator{}(all.flDecayTime); + DecayHFRatioValidator{}(all.flDecayHFRatio); + DecayLFRatioValidator{}(all.flDecayLFRatio); + ReflectionsValidator{}(all.lReflections); + ReflectionsDelayValidator{}(all.flReflectionsDelay); + VectorValidator{}(all.vReflectionsPan); + ReverbValidator{}(all.lReverb); + ReverbDelayValidator{}(all.flReverbDelay); + VectorValidator{}(all.vReverbPan); + EchoTimeValidator{}(all.flEchoTime); + EchoDepthValidator{}(all.flEchoDepth); + ModulationTimeValidator{}(all.flModulationTime); + ModulationDepthValidator{}(all.flModulationDepth); + AirAbsorptionHFValidator{}(all.flAirAbsorptionHF); + HFReferenceValidator{}(all.flHFReference); + LFReferenceValidator{}(all.flLFReference); + RoomRolloffFactorValidator{}(all.flRoomRolloffFactor); + FlagsValidator3{}(all.ulFlags); + } + }; // AllValidator3 + + struct EnvironmentDeferrer2 { + void operator()(Props2& props, unsigned long dwEnvironment) const + { + props = EAX2REVERB_PRESETS[dwEnvironment]; + } + }; // EnvironmentDeferrer2 + + struct EnvironmentSizeDeferrer2 { + void operator()(Props2& props, float flEnvironmentSize) const + { + if (props.flEnvironmentSize == flEnvironmentSize) + { + return; + } + + const auto scale = flEnvironmentSize / props.flEnvironmentSize; + props.flEnvironmentSize = flEnvironmentSize; + + if ((props.dwFlags & EAX2LISTENERFLAGS_DECAYTIMESCALE) != 0) + { + props.flDecayTime = clamp( + props.flDecayTime * scale, + EAXREVERB_MINDECAYTIME, + EAXREVERB_MAXDECAYTIME); + } + + if ((props.dwFlags & EAX2LISTENERFLAGS_REFLECTIONSSCALE) != 0 && + (props.dwFlags & EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE) != 0) + { + props.lReflections = clamp( + props.lReflections - static_cast(gain_to_level_mb(scale)), + EAXREVERB_MINREFLECTIONS, + EAXREVERB_MAXREFLECTIONS); + } + + if ((props.dwFlags & EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE) != 0) + { + props.flReflectionsDelay = clamp( + props.flReflectionsDelay * scale, + EAXREVERB_MINREFLECTIONSDELAY, + EAXREVERB_MAXREFLECTIONSDELAY); + } + + if ((props.dwFlags & EAX2LISTENERFLAGS_REVERBSCALE) != 0) + { + const auto log_scalar = ((props.dwFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0) ? 2'000.0F : 3'000.0F; + + props.lReverb = clamp( + props.lReverb - static_cast(std::log10(scale) * log_scalar), + EAXREVERB_MINREVERB, + EAXREVERB_MAXREVERB); + } + + if ((props.dwFlags & EAX2LISTENERFLAGS_REVERBDELAYSCALE) != 0) + { + props.flReverbDelay = clamp( + props.flReverbDelay * scale, + EAXREVERB_MINREVERBDELAY, + EAXREVERB_MAXREVERBDELAY); + } + } + }; // EnvironmentSizeDeferrer2 + + struct EnvironmentDeferrer3 { + void operator()(Props3& props, unsigned long ulEnvironment) const + { + if (ulEnvironment == EAX_ENVIRONMENT_UNDEFINED) + { + props.ulEnvironment = EAX_ENVIRONMENT_UNDEFINED; + return; + } + + props = EAXREVERB_PRESETS[ulEnvironment]; + } + }; // EnvironmentDeferrer3 + + struct EnvironmentSizeDeferrer3 { + void operator()(Props3& props, float flEnvironmentSize) const + { + if (props.flEnvironmentSize == flEnvironmentSize) + { + return; + } + + const auto scale = flEnvironmentSize / props.flEnvironmentSize; + props.ulEnvironment = EAX_ENVIRONMENT_UNDEFINED; + props.flEnvironmentSize = flEnvironmentSize; + + if ((props.ulFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0) + { + props.flDecayTime = clamp( + props.flDecayTime * scale, + EAXREVERB_MINDECAYTIME, + EAXREVERB_MAXDECAYTIME); + } + + if ((props.ulFlags & EAXREVERBFLAGS_REFLECTIONSSCALE) != 0 && + (props.ulFlags & EAXREVERBFLAGS_REFLECTIONSDELAYSCALE) != 0) + { + props.lReflections = clamp( + props.lReflections - static_cast(gain_to_level_mb(scale)), + EAXREVERB_MINREFLECTIONS, + EAXREVERB_MAXREFLECTIONS); + } + + if ((props.ulFlags & EAXREVERBFLAGS_REFLECTIONSDELAYSCALE) != 0) + { + props.flReflectionsDelay = clamp( + props.flReflectionsDelay * scale, + EAXREVERB_MINREFLECTIONSDELAY, + EAXREVERB_MAXREFLECTIONSDELAY); + } + + if ((props.ulFlags & EAXREVERBFLAGS_REVERBSCALE) != 0) + { + const auto log_scalar = ((props.ulFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0) ? 2'000.0F : 3'000.0F; + props.lReverb = clamp( + props.lReverb - static_cast(std::log10(scale) * log_scalar), + EAXREVERB_MINREVERB, + EAXREVERB_MAXREVERB); + } + + if ((props.ulFlags & EAXREVERBFLAGS_REVERBDELAYSCALE) != 0) + { + props.flReverbDelay = clamp( + props.flReverbDelay * scale, + EAXREVERB_MINREVERBDELAY, + EAXREVERB_MAXREVERBDELAY); + } + + if ((props.ulFlags & EAXREVERBFLAGS_ECHOTIMESCALE) != 0) + { + props.flEchoTime = clamp( + props.flEchoTime * scale, + EAXREVERB_MINECHOTIME, + EAXREVERB_MAXECHOTIME); + } + + if ((props.ulFlags & EAXREVERBFLAGS_MODULATIONTIMESCALE) != 0) + { + props.flModulationTime = clamp( + props.flModulationTime * scale, + EAXREVERB_MINMODULATIONTIME, + EAXREVERB_MAXMODULATIONTIME); + } + } + }; // EnvironmentSizeDeferrer3 + + int version_; + Props3 props_{}; + State1 state1_{}; + State2 state2_{}; + State3 state3_{}; + State3 state4_{}; + State3 state5_{}; + + [[noreturn]] static void fail(const char* message); + [[noreturn]] static void fail_unknown_property_id(); + [[noreturn]] static void fail_unknown_version(); + + static void set_defaults(State1& state) noexcept; + static void set_defaults(State2& state) noexcept; + static void set_defaults(State3& state) noexcept; + void set_defaults() noexcept; + + void set_current_defaults(); + + void set_efx_density_from_environment_size() noexcept; + void set_efx_diffusion() noexcept; + void set_efx_gain() noexcept; + void set_efx_gain_hf() noexcept; + void set_efx_gain_lf() noexcept; + void set_efx_decay_time() noexcept; + void set_efx_decay_hf_ratio() noexcept; + void set_efx_decay_lf_ratio() noexcept; + void set_efx_reflections_gain() noexcept; + void set_efx_reflections_delay() noexcept; + void set_efx_reflections_pan() noexcept; + void set_efx_late_reverb_gain() noexcept; + void set_efx_late_reverb_delay() noexcept; + void set_efx_late_reverb_pan() noexcept; + void set_efx_echo_time() noexcept; + void set_efx_echo_depth() noexcept; + void set_efx_modulation_time() noexcept; + void set_efx_modulation_depth() noexcept; + void set_efx_air_absorption_gain_hf() noexcept; + void set_efx_hf_reference() noexcept; + void set_efx_lf_reference() noexcept; + void set_efx_room_rolloff_factor() noexcept; + void set_efx_flags() noexcept; + void set_efx_defaults() noexcept; + + static void get1(const EaxCall& call, const Props1& props); + static void get2(const EaxCall& call, const Props2& props); + static void get3(const EaxCall& call, const Props3& props); + void get(const EaxCall& call); + + template + static void defer(const EaxCall& call, TProperty& property) { + const auto& value = call.get_value(); + TValidator{}(value); + property = value; + } + + template + static void defer(const EaxCall& call, TProperties& properties, TProperty&) + { + const auto& value = call.get_value(); + TValidator{}(value); + TDeferrer{}(properties, value); } -}; // EaxReverbEffectException + template + static void defer3(const EaxCall& call, Props3& properties, TProperty& property) + { + const auto& value = call.get_value(); + TValidator{}(value); + if (value == property) + return; + property = value; + properties.ulEnvironment = EAX_ENVIRONMENT_UNDEFINED; + } + + static void set1(const EaxCall& call, Props1& props); + static void set2(const EaxCall& call, Props2& props); + static void set3(const EaxCall& call, Props3& props); + void set(const EaxCall& call); + + static void translate(const Props1& src, Props3& dst) noexcept; + static void translate(const Props2& src, Props3& dst) noexcept; +}; // EaxReverbEffect -EaxReverbEffect::EaxReverbEffect() - : EaxEffect{AL_EFFECT_EAXREVERB} +EaxReverbEffect::EaxReverbEffect(const EaxCall& call) noexcept + : EaxEffect{AL_EFFECT_EAXREVERB}, version_{call.get_version()} { - set_eax_defaults(); + set_defaults(); + set_current_defaults(); set_efx_defaults(); } -void EaxReverbEffect::dispatch(const EaxEaxCall& eax_call) +void EaxReverbEffect::dispatch(const EaxCall& call) +{ + call.is_get() ? get(call) : set(call); +} + +[[noreturn]] void EaxReverbEffect::fail(const char* message) +{ + throw Exception{message}; +} + +[[noreturn]] void EaxReverbEffect::fail_unknown_property_id() +{ + fail(EaxEffectErrorMessages::unknown_property_id()); +} + +[[noreturn]] void EaxReverbEffect::fail_unknown_version() +{ + fail(EaxEffectErrorMessages::unknown_version()); +} + +void EaxReverbEffect::set_defaults(State1& state) noexcept +{ + state.i = EAX1REVERB_PRESETS[EAX_ENVIRONMENT_GENERIC]; + state.d = state.i; +} + +void EaxReverbEffect::set_defaults(State2& state) noexcept { - eax_call.is_get() ? get(eax_call) : set(eax_call); + state.i = EAX2REVERB_PRESETS[EAX2_ENVIRONMENT_GENERIC]; + state.i.lRoom = initial_room2; + state.d = state.i; } -[[noreturn]] void EaxReverbEffect::eax_fail(const char* message) +void EaxReverbEffect::set_defaults(State3& state) noexcept { - throw EaxReverbEffectException{message}; + state.i = EAXREVERB_PRESETS[EAX_ENVIRONMENT_GENERIC]; + state.d = state.i; } -void EaxReverbEffect::set_eax_defaults() +void EaxReverbEffect::set_defaults() noexcept { - eax1_ = EAX1REVERB_PRESETS[EAX_ENVIRONMENT_GENERIC]; - eax1_d_ = eax1_; - eax_ = EAXREVERB_PRESETS[EAX_ENVIRONMENT_GENERIC]; - /* HACK: EAX2 has a default room volume of -10,000dB (silence), although - * newer versions use -1,000dB. What should be happening is properties for - * each EAX version is tracked separately, with the last version used for - * the properties to apply (presumably v2 or nothing being the default). - */ - eax_.lRoom = EAXREVERB_MINROOM; - eax_d_ = eax_; + set_defaults(state1_); + set_defaults(state2_); + set_defaults(state3_); + state4_ = state3_; + state5_ = state3_; } -void EaxReverbEffect::set_efx_density_from_environment_size() +void EaxReverbEffect::set_current_defaults() { - const auto eax_environment_size = eax_.flEnvironmentSize; + switch (version_) + { + case 1: translate(state1_.i, props_); break; + case 2: translate(state2_.i, props_); break; + case 3: props_ = state3_.i; break; + case 4: props_ = state4_.i; break; + case 5: props_ = state5_.i; break; + default: fail_unknown_version(); + } +} - const auto efx_density = clamp( - (eax_environment_size * eax_environment_size * eax_environment_size) / 16.0F, +void EaxReverbEffect::set_efx_density_from_environment_size() noexcept +{ + const auto size = props_.flEnvironmentSize; + const auto density = (size * size * size) / 16.0F; + al_effect_props_.Reverb.Density = clamp( + density, AL_EAXREVERB_MIN_DENSITY, AL_EAXREVERB_MAX_DENSITY); - - al_effect_props_.Reverb.Density = efx_density; } -void EaxReverbEffect::set_efx_diffusion() +void EaxReverbEffect::set_efx_diffusion() noexcept { - const auto efx_diffusion = clamp( - eax_.flEnvironmentDiffusion, + al_effect_props_.Reverb.Diffusion = clamp( + props_.flEnvironmentDiffusion, AL_EAXREVERB_MIN_DIFFUSION, AL_EAXREVERB_MAX_DIFFUSION); - - al_effect_props_.Reverb.Diffusion = efx_diffusion; } -void EaxReverbEffect::set_efx_gain() +void EaxReverbEffect::set_efx_gain() noexcept { - const auto efx_gain = clamp( - level_mb_to_gain(static_cast(eax_.lRoom)), + al_effect_props_.Reverb.Gain = clamp( + level_mb_to_gain(static_cast(props_.lRoom)), AL_EAXREVERB_MIN_GAIN, AL_EAXREVERB_MAX_GAIN); - - al_effect_props_.Reverb.Gain = efx_gain; } -void EaxReverbEffect::set_efx_gain_hf() +void EaxReverbEffect::set_efx_gain_hf() noexcept { - const auto efx_gain_hf = clamp( - level_mb_to_gain(static_cast(eax_.lRoomHF)), + al_effect_props_.Reverb.GainHF = clamp( + level_mb_to_gain(static_cast(props_.lRoomHF)), AL_EAXREVERB_MIN_GAINHF, AL_EAXREVERB_MAX_GAINHF); - - al_effect_props_.Reverb.GainHF = efx_gain_hf; } -void EaxReverbEffect::set_efx_gain_lf() +void EaxReverbEffect::set_efx_gain_lf() noexcept { - const auto efx_gain_lf = clamp( - level_mb_to_gain(static_cast(eax_.lRoomLF)), + al_effect_props_.Reverb.GainLF = clamp( + level_mb_to_gain(static_cast(props_.lRoomLF)), AL_EAXREVERB_MIN_GAINLF, AL_EAXREVERB_MAX_GAINLF); - - al_effect_props_.Reverb.GainLF = efx_gain_lf; } -void EaxReverbEffect::set_efx_decay_time() +void EaxReverbEffect::set_efx_decay_time() noexcept { - const auto efx_decay_time = clamp( - eax_.flDecayTime, + al_effect_props_.Reverb.DecayTime = clamp( + props_.flDecayTime, AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME); - - al_effect_props_.Reverb.DecayTime = efx_decay_time; } -void EaxReverbEffect::set_efx_decay_hf_ratio() +void EaxReverbEffect::set_efx_decay_hf_ratio() noexcept { - const auto efx_decay_hf_ratio = clamp( - eax_.flDecayHFRatio, + al_effect_props_.Reverb.DecayHFRatio = clamp( + props_.flDecayHFRatio, AL_EAXREVERB_MIN_DECAY_HFRATIO, AL_EAXREVERB_MAX_DECAY_HFRATIO); - - al_effect_props_.Reverb.DecayHFRatio = efx_decay_hf_ratio; } -void EaxReverbEffect::set_efx_decay_lf_ratio() +void EaxReverbEffect::set_efx_decay_lf_ratio() noexcept { - const auto efx_decay_lf_ratio = clamp( - eax_.flDecayLFRatio, + al_effect_props_.Reverb.DecayLFRatio = clamp( + props_.flDecayLFRatio, AL_EAXREVERB_MIN_DECAY_LFRATIO, AL_EAXREVERB_MAX_DECAY_LFRATIO); - - al_effect_props_.Reverb.DecayLFRatio = efx_decay_lf_ratio; } -void EaxReverbEffect::set_efx_reflections_gain() +void EaxReverbEffect::set_efx_reflections_gain() noexcept { - const auto efx_reflections_gain = clamp( - level_mb_to_gain(static_cast(eax_.lReflections)), + al_effect_props_.Reverb.ReflectionsGain = clamp( + level_mb_to_gain(static_cast(props_.lReflections)), AL_EAXREVERB_MIN_REFLECTIONS_GAIN, AL_EAXREVERB_MAX_REFLECTIONS_GAIN); - - al_effect_props_.Reverb.ReflectionsGain = efx_reflections_gain; } -void EaxReverbEffect::set_efx_reflections_delay() +void EaxReverbEffect::set_efx_reflections_delay() noexcept { - const auto efx_reflections_delay = clamp( - eax_.flReflectionsDelay, + al_effect_props_.Reverb.ReflectionsDelay = clamp( + props_.flReflectionsDelay, AL_EAXREVERB_MIN_REFLECTIONS_DELAY, AL_EAXREVERB_MAX_REFLECTIONS_DELAY); - - al_effect_props_.Reverb.ReflectionsDelay = efx_reflections_delay; } -void EaxReverbEffect::set_efx_reflections_pan() +void EaxReverbEffect::set_efx_reflections_pan() noexcept { - al_effect_props_.Reverb.ReflectionsPan[0] = eax_.vReflectionsPan.x; - al_effect_props_.Reverb.ReflectionsPan[1] = eax_.vReflectionsPan.y; - al_effect_props_.Reverb.ReflectionsPan[2] = eax_.vReflectionsPan.z; + al_effect_props_.Reverb.ReflectionsPan[0] = props_.vReflectionsPan.x; + al_effect_props_.Reverb.ReflectionsPan[1] = props_.vReflectionsPan.y; + al_effect_props_.Reverb.ReflectionsPan[2] = props_.vReflectionsPan.z; } -void EaxReverbEffect::set_efx_late_reverb_gain() +void EaxReverbEffect::set_efx_late_reverb_gain() noexcept { - const auto efx_late_reverb_gain = clamp( - level_mb_to_gain(static_cast(eax_.lReverb)), + al_effect_props_.Reverb.LateReverbGain = clamp( + level_mb_to_gain(static_cast(props_.lReverb)), AL_EAXREVERB_MIN_LATE_REVERB_GAIN, AL_EAXREVERB_MAX_LATE_REVERB_GAIN); - - al_effect_props_.Reverb.LateReverbGain = efx_late_reverb_gain; } -void EaxReverbEffect::set_efx_late_reverb_delay() +void EaxReverbEffect::set_efx_late_reverb_delay() noexcept { - const auto efx_late_reverb_delay = clamp( - eax_.flReverbDelay, + al_effect_props_.Reverb.LateReverbDelay = clamp( + props_.flReverbDelay, AL_EAXREVERB_MIN_LATE_REVERB_DELAY, AL_EAXREVERB_MAX_LATE_REVERB_DELAY); - - al_effect_props_.Reverb.LateReverbDelay = efx_late_reverb_delay; } -void EaxReverbEffect::set_efx_late_reverb_pan() +void EaxReverbEffect::set_efx_late_reverb_pan() noexcept { - al_effect_props_.Reverb.LateReverbPan[0] = eax_.vReverbPan.x; - al_effect_props_.Reverb.LateReverbPan[1] = eax_.vReverbPan.y; - al_effect_props_.Reverb.LateReverbPan[2] = eax_.vReverbPan.z; + al_effect_props_.Reverb.LateReverbPan[0] = props_.vReverbPan.x; + al_effect_props_.Reverb.LateReverbPan[1] = props_.vReverbPan.y; + al_effect_props_.Reverb.LateReverbPan[2] = props_.vReverbPan.z; } -void EaxReverbEffect::set_efx_echo_time() +void EaxReverbEffect::set_efx_echo_time() noexcept { - const auto efx_echo_time = clamp( - eax_.flEchoTime, + al_effect_props_.Reverb.EchoTime = clamp( + props_.flEchoTime, AL_EAXREVERB_MIN_ECHO_TIME, AL_EAXREVERB_MAX_ECHO_TIME); - - al_effect_props_.Reverb.EchoTime = efx_echo_time; } -void EaxReverbEffect::set_efx_echo_depth() +void EaxReverbEffect::set_efx_echo_depth() noexcept { - const auto efx_echo_depth = clamp( - eax_.flEchoDepth, + al_effect_props_.Reverb.EchoDepth = clamp( + props_.flEchoDepth, AL_EAXREVERB_MIN_ECHO_DEPTH, AL_EAXREVERB_MAX_ECHO_DEPTH); - - al_effect_props_.Reverb.EchoDepth = efx_echo_depth; } -void EaxReverbEffect::set_efx_modulation_time() +void EaxReverbEffect::set_efx_modulation_time() noexcept { - const auto efx_modulation_time = clamp( - eax_.flModulationTime, + al_effect_props_.Reverb.ModulationTime = clamp( + props_.flModulationTime, AL_EAXREVERB_MIN_MODULATION_TIME, AL_EAXREVERB_MAX_MODULATION_TIME); - - al_effect_props_.Reverb.ModulationTime = efx_modulation_time; } -void EaxReverbEffect::set_efx_modulation_depth() +void EaxReverbEffect::set_efx_modulation_depth() noexcept { - const auto efx_modulation_depth = clamp( - eax_.flModulationDepth, + al_effect_props_.Reverb.ModulationDepth = clamp( + props_.flModulationDepth, AL_EAXREVERB_MIN_MODULATION_DEPTH, AL_EAXREVERB_MAX_MODULATION_DEPTH); - - al_effect_props_.Reverb.ModulationDepth = efx_modulation_depth; } -void EaxReverbEffect::set_efx_air_absorption_gain_hf() +void EaxReverbEffect::set_efx_air_absorption_gain_hf() noexcept { - const auto efx_air_absorption_hf = clamp( - level_mb_to_gain(eax_.flAirAbsorptionHF), + al_effect_props_.Reverb.AirAbsorptionGainHF = clamp( + level_mb_to_gain(props_.flAirAbsorptionHF), AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF, AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF); - - al_effect_props_.Reverb.AirAbsorptionGainHF = efx_air_absorption_hf; } -void EaxReverbEffect::set_efx_hf_reference() +void EaxReverbEffect::set_efx_hf_reference() noexcept { - const auto efx_hf_reference = clamp( - eax_.flHFReference, + al_effect_props_.Reverb.HFReference = clamp( + props_.flHFReference, AL_EAXREVERB_MIN_HFREFERENCE, AL_EAXREVERB_MAX_HFREFERENCE); - - al_effect_props_.Reverb.HFReference = efx_hf_reference; } -void EaxReverbEffect::set_efx_lf_reference() +void EaxReverbEffect::set_efx_lf_reference() noexcept { - const auto efx_lf_reference = clamp( - eax_.flLFReference, + al_effect_props_.Reverb.LFReference = clamp( + props_.flLFReference, AL_EAXREVERB_MIN_LFREFERENCE, AL_EAXREVERB_MAX_LFREFERENCE); - - al_effect_props_.Reverb.LFReference = efx_lf_reference; } -void EaxReverbEffect::set_efx_room_rolloff_factor() +void EaxReverbEffect::set_efx_room_rolloff_factor() noexcept { - const auto efx_room_rolloff_factor = clamp( - eax_.flRoomRolloffFactor, + al_effect_props_.Reverb.RoomRolloffFactor = clamp( + props_.flRoomRolloffFactor, AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR, AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR); - - al_effect_props_.Reverb.RoomRolloffFactor = efx_room_rolloff_factor; } -void EaxReverbEffect::set_efx_flags() +void EaxReverbEffect::set_efx_flags() noexcept { - al_effect_props_.Reverb.DecayHFLimit = ((eax_.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0); + al_effect_props_.Reverb.DecayHFLimit = ((props_.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0); } -void EaxReverbEffect::set_efx_defaults() +void EaxReverbEffect::set_efx_defaults() noexcept { set_efx_density_from_environment_size(); set_efx_diffusion(); @@ -1064,1482 +1470,536 @@ void EaxReverbEffect::set_efx_defaults() set_efx_flags(); } -void EaxReverbEffect::v1_get(const EaxEaxCall& eax_call) const +void EaxReverbEffect::get1(const EaxCall& call, const Props1& props) { - switch(eax_call.get_property_id()) + switch(call.get_property_id()) { - case DSPROPERTY_EAX_ALL: - eax_call.set_value(eax1_); - break; - - case DSPROPERTY_EAX_ENVIRONMENT: - eax_call.set_value(eax1_.environment); - break; - - case DSPROPERTY_EAX_VOLUME: - eax_call.set_value(eax1_.fVolume); - break; - - case DSPROPERTY_EAX_DECAYTIME: - eax_call.set_value(eax1_.fDecayTime_sec); - break; - - case DSPROPERTY_EAX_DAMPING: - eax_call.set_value(eax1_.fDamping); - break; - - default: - eax_fail("Unsupported property id."); + case DSPROPERTY_EAX_ALL: call.set_value(props); break; + case DSPROPERTY_EAX_ENVIRONMENT: call.set_value(props.environment); break; + case DSPROPERTY_EAX_VOLUME: call.set_value(props.fVolume); break; + case DSPROPERTY_EAX_DECAYTIME: call.set_value(props.fDecayTime_sec); break; + case DSPROPERTY_EAX_DAMPING: call.set_value(props.fDamping); break; + default: fail_unknown_property_id(); } } -void EaxReverbEffect::get_all( - const EaxEaxCall& eax_call) const +void EaxReverbEffect::get2(const EaxCall& call, const Props2& props) { - if (eax_call.get_version() == 2) + switch(call.get_property_id()) { - auto& eax_reverb = eax_call.get_value(); - eax_reverb.lRoom = eax_.lRoom; - eax_reverb.lRoomHF = eax_.lRoomHF; - eax_reverb.flRoomRolloffFactor = eax_.flRoomRolloffFactor; - eax_reverb.flDecayTime = eax_.flDecayTime; - eax_reverb.flDecayHFRatio = eax_.flDecayHFRatio; - eax_reverb.lReflections = eax_.lReflections; - eax_reverb.flReflectionsDelay = eax_.flReflectionsDelay; - eax_reverb.lReverb = eax_.lReverb; - eax_reverb.flReverbDelay = eax_.flReverbDelay; - eax_reverb.dwEnvironment = eax_.ulEnvironment; - eax_reverb.flEnvironmentSize = eax_.flEnvironmentSize; - eax_reverb.flEnvironmentDiffusion = eax_.flEnvironmentDiffusion; - eax_reverb.flAirAbsorptionHF = eax_.flAirAbsorptionHF; - eax_reverb.dwFlags = eax_.ulFlags; + case DSPROPERTY_EAX20LISTENER_NONE: break; + case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS: call.set_value(props); break; + case DSPROPERTY_EAX20LISTENER_ROOM: call.set_value(props.lRoom); break; + case DSPROPERTY_EAX20LISTENER_ROOMHF: call.set_value(props.lRoomHF); break; + case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR: call.set_value(props.flRoomRolloffFactor); break; + case DSPROPERTY_EAX20LISTENER_DECAYTIME: call.set_value(props.flDecayTime); break; + case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO: call.set_value(props.flDecayHFRatio); break; + case DSPROPERTY_EAX20LISTENER_REFLECTIONS: call.set_value(props.lReflections); break; + case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY: call.set_value(props.flReverbDelay); break; + case DSPROPERTY_EAX20LISTENER_REVERB: call.set_value(props.lReverb); break; + case DSPROPERTY_EAX20LISTENER_REVERBDELAY: call.set_value(props.flReverbDelay); break; + case DSPROPERTY_EAX20LISTENER_ENVIRONMENT: call.set_value(props.dwEnvironment); break; + case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE: call.set_value(props.flEnvironmentSize); break; + case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION: call.set_value(props.flEnvironmentDiffusion); break; + case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF: call.set_value(props.flAirAbsorptionHF); break; + case DSPROPERTY_EAX20LISTENER_FLAGS: call.set_value(props.dwFlags); break; + default: fail_unknown_property_id(); } - else +} + +void EaxReverbEffect::get3(const EaxCall& call, const Props3& props) +{ + switch(call.get_property_id()) { - eax_call.set_value(eax_); + case EAXREVERB_NONE: break; + case EAXREVERB_ALLPARAMETERS: call.set_value(props); break; + case EAXREVERB_ENVIRONMENT: call.set_value(props.ulEnvironment); break; + case EAXREVERB_ENVIRONMENTSIZE: call.set_value(props.flEnvironmentSize); break; + case EAXREVERB_ENVIRONMENTDIFFUSION: call.set_value(props.flEnvironmentDiffusion); break; + case EAXREVERB_ROOM: call.set_value(props.lRoom); break; + case EAXREVERB_ROOMHF: call.set_value(props.lRoomHF); break; + case EAXREVERB_ROOMLF: call.set_value(props.lRoomLF); break; + case EAXREVERB_DECAYTIME: call.set_value(props.flDecayTime); break; + case EAXREVERB_DECAYHFRATIO: call.set_value(props.flDecayHFRatio); break; + case EAXREVERB_DECAYLFRATIO: call.set_value(props.flDecayLFRatio); break; + case EAXREVERB_REFLECTIONS: call.set_value(props.lReflections); break; + case EAXREVERB_REFLECTIONSDELAY: call.set_value(props.flReflectionsDelay); break; + case EAXREVERB_REFLECTIONSPAN: call.set_value(props.vReflectionsPan); break; + case EAXREVERB_REVERB: call.set_value(props.lReverb); break; + case EAXREVERB_REVERBDELAY: call.set_value(props.flReverbDelay); break; + case EAXREVERB_REVERBPAN: call.set_value(props.vReverbPan); break; + case EAXREVERB_ECHOTIME: call.set_value(props.flEchoTime); break; + case EAXREVERB_ECHODEPTH: call.set_value(props.flEchoDepth); break; + case EAXREVERB_MODULATIONTIME: call.set_value(props.flModulationTime); break; + case EAXREVERB_MODULATIONDEPTH: call.set_value(props.flModulationDepth); break; + case EAXREVERB_AIRABSORPTIONHF: call.set_value(props.flAirAbsorptionHF); break; + case EAXREVERB_HFREFERENCE: call.set_value(props.flHFReference); break; + case EAXREVERB_LFREFERENCE: call.set_value(props.flLFReference); break; + case EAXREVERB_ROOMROLLOFFFACTOR: call.set_value(props.flRoomRolloffFactor); break; + case EAXREVERB_FLAGS: call.set_value(props.ulFlags); break; + default: fail_unknown_property_id(); } } -void EaxReverbEffect::get(const EaxEaxCall& eax_call) const +void EaxReverbEffect::get(const EaxCall& call) { - if(eax_call.get_version() == 1) - v1_get(eax_call); - else switch(eax_call.get_property_id()) - { - case EAXREVERB_NONE: - break; + const auto version = call.get_version(); - case EAXREVERB_ALLPARAMETERS: - get_all(eax_call); - break; + switch (version) + { + case 1: get1(call, state1_.i); break; + case 2: get2(call, state2_.i); break; + case 3: get3(call, state3_.i); break; + case 4: get3(call, state4_.i); break; + case 5: get3(call, state5_.i); break; + default: fail_unknown_version(); + } - case EAXREVERB_ENVIRONMENT: - eax_call.set_value(eax_.ulEnvironment); - break; + version_ = version; +} - case EAXREVERB_ENVIRONMENTSIZE: - eax_call.set_value(eax_.flEnvironmentSize); - break; +/*[[nodiscard]]*/ bool EaxReverbEffect::commit() +{ + if ((version_ == 1 && state1_.i == state1_.d) || + (version_ == 2 && state2_.i == state2_.d)) + { + return false; + } - case EAXREVERB_ENVIRONMENTDIFFUSION: - eax_call.set_value(eax_.flEnvironmentDiffusion); - break; + const auto props = props_; - case EAXREVERB_ROOM: - eax_call.set_value(eax_.lRoom); + switch (version_) + { + case 1: + state1_.i = state1_.d; + translate(state1_.d, props_); break; - case EAXREVERB_ROOMHF: - eax_call.set_value(eax_.lRoomHF); + case 2: + state2_.i = state2_.d; + translate(state2_.d, props_); break; - case EAXREVERB_ROOMLF: - eax_call.set_value(eax_.lRoomLF); + case 3: + state3_.i = state3_.d; + props_ = state3_.d; break; - case EAXREVERB_DECAYTIME: - eax_call.set_value(eax_.flDecayTime); + case 4: + state4_.i = state4_.d; + props_ = state4_.d; break; - case EAXREVERB_DECAYHFRATIO: - eax_call.set_value(eax_.flDecayHFRatio); + case 5: + state5_.i = state5_.d; + props_ = state5_.d; break; - case EAXREVERB_DECAYLFRATIO: - eax_call.set_value(eax_.flDecayLFRatio); - break; + default: + fail_unknown_version(); + } - case EAXREVERB_REFLECTIONS: - eax_call.set_value(eax_.lReflections); - break; + auto is_dirty = false; - case EAXREVERB_REFLECTIONSDELAY: - eax_call.set_value(eax_.flReflectionsDelay); - break; + if (props_.flEnvironmentSize != props.flEnvironmentSize) + { + is_dirty = true; + set_efx_density_from_environment_size(); + } - case EAXREVERB_REFLECTIONSPAN: - eax_call.set_value(eax_.vReflectionsPan); - break; + if (props_.flEnvironmentDiffusion != props.flEnvironmentDiffusion) + { + is_dirty = true; + set_efx_diffusion(); + } - case EAXREVERB_REVERB: - eax_call.set_value(eax_.lReverb); - break; + if (props_.lRoom != props.lRoom) + { + is_dirty = true; + set_efx_gain(); + } - case EAXREVERB_REVERBDELAY: - eax_call.set_value(eax_.flReverbDelay); - break; + if (props_.lRoomHF != props.lRoomHF) + { + is_dirty = true; + set_efx_gain_hf(); + } - case EAXREVERB_REVERBPAN: - eax_call.set_value(eax_.vReverbPan); - break; + if (props_.lRoomLF != props.lRoomLF) + { + is_dirty = true; + set_efx_gain_lf(); + } - case EAXREVERB_ECHOTIME: - eax_call.set_value(eax_.flEchoTime); - break; + if (props_.flDecayTime != props.flDecayTime) + { + is_dirty = true; + set_efx_decay_time(); + } - case EAXREVERB_ECHODEPTH: - eax_call.set_value(eax_.flEchoDepth); - break; + if (props_.flDecayHFRatio != props.flDecayHFRatio) + { + is_dirty = true; + set_efx_decay_hf_ratio(); + } - case EAXREVERB_MODULATIONTIME: - eax_call.set_value(eax_.flModulationTime); - break; + if (props_.flDecayLFRatio != props.flDecayLFRatio) + { + is_dirty = true; + set_efx_decay_lf_ratio(); + } - case EAXREVERB_MODULATIONDEPTH: - eax_call.set_value(eax_.flModulationDepth); - break; - - case EAXREVERB_AIRABSORPTIONHF: - eax_call.set_value(eax_.flAirAbsorptionHF); - break; - - case EAXREVERB_HFREFERENCE: - eax_call.set_value(eax_.flHFReference); - break; - - case EAXREVERB_LFREFERENCE: - eax_call.set_value(eax_.flLFReference); - break; - - case EAXREVERB_ROOMROLLOFFFACTOR: - eax_call.set_value(eax_.flRoomRolloffFactor); - break; - - case EAXREVERB_FLAGS: - eax_call.set_value(eax_.ulFlags); - break; - - default: - eax_fail("Unsupported property id."); - } -} - -void EaxReverbEffect::v1_validate_environment(unsigned long environment) -{ - validate_environment(environment, 1, true); -} - -void EaxReverbEffect::v1_validate_volume(float volume) -{ - eax_validate_range("Volume", volume, EAX1REVERB_MINVOLUME, EAX1REVERB_MAXVOLUME); -} - -void EaxReverbEffect::v1_validate_decay_time(float decay_time) -{ - validate_decay_time(decay_time); -} - -void EaxReverbEffect::v1_validate_damping(float damping) -{ - eax_validate_range("Damping", damping, EAX1REVERB_MINDAMPING, EAX1REVERB_MAXDAMPING); -} - -void EaxReverbEffect::v1_validate_all(const EAX_REVERBPROPERTIES& all) -{ - v1_validate_environment(all.environment); - v1_validate_volume(all.fVolume); - v1_validate_decay_time(all.fDecayTime_sec); - v1_validate_damping(all.fDamping); -} - -void EaxReverbEffect::validate_environment( - unsigned long ulEnvironment, - int version, - bool is_standalone) -{ - eax_validate_range( - "Environment", - ulEnvironment, - EAXREVERB_MINENVIRONMENT, - (version <= 2 || is_standalone) ? EAX1REVERB_MAXENVIRONMENT : EAX30REVERB_MAXENVIRONMENT); -} - -void EaxReverbEffect::validate_environment_size( - float flEnvironmentSize) -{ - eax_validate_range( - "Environment Size", - flEnvironmentSize, - EAXREVERB_MINENVIRONMENTSIZE, - EAXREVERB_MAXENVIRONMENTSIZE); -} - -void EaxReverbEffect::validate_environment_diffusion( - float flEnvironmentDiffusion) -{ - eax_validate_range( - "Environment Diffusion", - flEnvironmentDiffusion, - EAXREVERB_MINENVIRONMENTDIFFUSION, - EAXREVERB_MAXENVIRONMENTDIFFUSION); -} - -void EaxReverbEffect::validate_room( - long lRoom) -{ - eax_validate_range( - "Room", - lRoom, - EAXREVERB_MINROOM, - EAXREVERB_MAXROOM); -} - -void EaxReverbEffect::validate_room_hf( - long lRoomHF) -{ - eax_validate_range( - "Room HF", - lRoomHF, - EAXREVERB_MINROOMHF, - EAXREVERB_MAXROOMHF); -} - -void EaxReverbEffect::validate_room_lf( - long lRoomLF) -{ - eax_validate_range( - "Room LF", - lRoomLF, - EAXREVERB_MINROOMLF, - EAXREVERB_MAXROOMLF); -} - -void EaxReverbEffect::validate_decay_time( - float flDecayTime) -{ - eax_validate_range( - "Decay Time", - flDecayTime, - EAXREVERB_MINDECAYTIME, - EAXREVERB_MAXDECAYTIME); -} - -void EaxReverbEffect::validate_decay_hf_ratio( - float flDecayHFRatio) -{ - eax_validate_range( - "Decay HF Ratio", - flDecayHFRatio, - EAXREVERB_MINDECAYHFRATIO, - EAXREVERB_MAXDECAYHFRATIO); -} - -void EaxReverbEffect::validate_decay_lf_ratio( - float flDecayLFRatio) -{ - eax_validate_range( - "Decay LF Ratio", - flDecayLFRatio, - EAXREVERB_MINDECAYLFRATIO, - EAXREVERB_MAXDECAYLFRATIO); -} - -void EaxReverbEffect::validate_reflections( - long lReflections) -{ - eax_validate_range( - "Reflections", - lReflections, - EAXREVERB_MINREFLECTIONS, - EAXREVERB_MAXREFLECTIONS); -} - -void EaxReverbEffect::validate_reflections_delay( - float flReflectionsDelay) -{ - eax_validate_range( - "Reflections Delay", - flReflectionsDelay, - EAXREVERB_MINREFLECTIONSDELAY, - EAXREVERB_MAXREFLECTIONSDELAY); -} - -void EaxReverbEffect::validate_reflections_pan( - const EAXVECTOR& vReflectionsPan) -{ - std::ignore = vReflectionsPan; -} - -void EaxReverbEffect::validate_reverb( - long lReverb) -{ - eax_validate_range( - "Reverb", - lReverb, - EAXREVERB_MINREVERB, - EAXREVERB_MAXREVERB); -} - -void EaxReverbEffect::validate_reverb_delay( - float flReverbDelay) -{ - eax_validate_range( - "Reverb Delay", - flReverbDelay, - EAXREVERB_MINREVERBDELAY, - EAXREVERB_MAXREVERBDELAY); -} - -void EaxReverbEffect::validate_reverb_pan( - const EAXVECTOR& vReverbPan) -{ - std::ignore = vReverbPan; -} - -void EaxReverbEffect::validate_echo_time( - float flEchoTime) -{ - eax_validate_range( - "Echo Time", - flEchoTime, - EAXREVERB_MINECHOTIME, - EAXREVERB_MAXECHOTIME); -} - -void EaxReverbEffect::validate_echo_depth( - float flEchoDepth) -{ - eax_validate_range( - "Echo Depth", - flEchoDepth, - EAXREVERB_MINECHODEPTH, - EAXREVERB_MAXECHODEPTH); -} - -void EaxReverbEffect::validate_modulation_time( - float flModulationTime) -{ - eax_validate_range( - "Modulation Time", - flModulationTime, - EAXREVERB_MINMODULATIONTIME, - EAXREVERB_MAXMODULATIONTIME); -} - -void EaxReverbEffect::validate_modulation_depth( - float flModulationDepth) -{ - eax_validate_range( - "Modulation Depth", - flModulationDepth, - EAXREVERB_MINMODULATIONDEPTH, - EAXREVERB_MAXMODULATIONDEPTH); -} - -void EaxReverbEffect::validate_air_absorbtion_hf( - float air_absorbtion_hf) -{ - eax_validate_range( - "Air Absorbtion HF", - air_absorbtion_hf, - EAXREVERB_MINAIRABSORPTIONHF, - EAXREVERB_MAXAIRABSORPTIONHF); -} - -void EaxReverbEffect::validate_hf_reference( - float flHFReference) -{ - eax_validate_range( - "HF Reference", - flHFReference, - EAXREVERB_MINHFREFERENCE, - EAXREVERB_MAXHFREFERENCE); -} - -void EaxReverbEffect::validate_lf_reference( - float flLFReference) -{ - eax_validate_range( - "LF Reference", - flLFReference, - EAXREVERB_MINLFREFERENCE, - EAXREVERB_MAXLFREFERENCE); -} - -void EaxReverbEffect::validate_room_rolloff_factor( - float flRoomRolloffFactor) -{ - eax_validate_range( - "Room Rolloff Factor", - flRoomRolloffFactor, - EAXREVERB_MINROOMROLLOFFFACTOR, - EAXREVERB_MAXROOMROLLOFFFACTOR); -} - -void EaxReverbEffect::validate_flags( - unsigned long ulFlags) -{ - eax_validate_range( - "Flags", - ulFlags, - 0UL, - ~EAXREVERBFLAGS_RESERVED); -} - -void EaxReverbEffect::validate_all( - const EAX20LISTENERPROPERTIES& listener, - int version) -{ - validate_room(listener.lRoom); - validate_room_hf(listener.lRoomHF); - validate_room_rolloff_factor(listener.flRoomRolloffFactor); - validate_decay_time(listener.flDecayTime); - validate_decay_hf_ratio(listener.flDecayHFRatio); - validate_reflections(listener.lReflections); - validate_reflections_delay(listener.flReflectionsDelay); - validate_reverb(listener.lReverb); - validate_reverb_delay(listener.flReverbDelay); - validate_environment(listener.dwEnvironment, version, false); - validate_environment_size(listener.flEnvironmentSize); - validate_environment_diffusion(listener.flEnvironmentDiffusion); - validate_air_absorbtion_hf(listener.flAirAbsorptionHF); - validate_flags(listener.dwFlags); -} - -void EaxReverbEffect::validate_all( - const EAXREVERBPROPERTIES& lReverb, - int version) -{ - validate_environment(lReverb.ulEnvironment, version, false); - validate_environment_size(lReverb.flEnvironmentSize); - validate_environment_diffusion(lReverb.flEnvironmentDiffusion); - validate_room(lReverb.lRoom); - validate_room_hf(lReverb.lRoomHF); - validate_room_lf(lReverb.lRoomLF); - validate_decay_time(lReverb.flDecayTime); - validate_decay_hf_ratio(lReverb.flDecayHFRatio); - validate_decay_lf_ratio(lReverb.flDecayLFRatio); - validate_reflections(lReverb.lReflections); - validate_reflections_delay(lReverb.flReflectionsDelay); - validate_reverb(lReverb.lReverb); - validate_reverb_delay(lReverb.flReverbDelay); - validate_echo_time(lReverb.flEchoTime); - validate_echo_depth(lReverb.flEchoDepth); - validate_modulation_time(lReverb.flModulationTime); - validate_modulation_depth(lReverb.flModulationDepth); - validate_air_absorbtion_hf(lReverb.flAirAbsorptionHF); - validate_hf_reference(lReverb.flHFReference); - validate_lf_reference(lReverb.flLFReference); - validate_room_rolloff_factor(lReverb.flRoomRolloffFactor); - validate_flags(lReverb.ulFlags); -} - -void EaxReverbEffect::v1_defer_environment(unsigned long environment) -{ - eax1_d_ = EAX1REVERB_PRESETS[environment]; - eax1_dirty_flags_.ulEnvironment = true; -} - -void EaxReverbEffect::v1_defer_volume(float volume) -{ - eax1_d_.fVolume = volume; - eax1_dirty_flags_.flVolume = (eax1_.fVolume != eax1_d_.fVolume); -} - -void EaxReverbEffect::v1_defer_decay_time(float decay_time) -{ - eax1_d_.fDecayTime_sec = decay_time; - eax1_dirty_flags_.flDecayTime = (eax1_.fDecayTime_sec != eax1_d_.fDecayTime_sec); -} - -void EaxReverbEffect::v1_defer_damping(float damping) -{ - eax1_d_.fDamping = damping; - eax1_dirty_flags_.flDamping = (eax1_.fDamping != eax1_d_.fDamping); -} - -void EaxReverbEffect::v1_defer_all(const EAX_REVERBPROPERTIES& lReverb) -{ - v1_defer_environment(lReverb.environment); - v1_defer_volume(lReverb.fVolume); - v1_defer_decay_time(lReverb.fDecayTime_sec); - v1_defer_damping(lReverb.fDamping); -} - - -void EaxReverbEffect::v1_set_efx() -{ - auto efx_props = eax_efx_reverb_presets[eax1_.environment]; - efx_props.flGain = eax1_.fVolume; - efx_props.flDecayTime = eax1_.fDecayTime_sec; - efx_props.flDecayHFRatio = clamp(eax1_.fDamping, AL_EAXREVERB_MIN_DECAY_HFRATIO, AL_EAXREVERB_MAX_DECAY_HFRATIO); - - al_effect_props_.Reverb.Density = efx_props.flDensity; - al_effect_props_.Reverb.Diffusion = efx_props.flDiffusion; - al_effect_props_.Reverb.Gain = efx_props.flGain; - al_effect_props_.Reverb.GainHF = efx_props.flGainHF; - al_effect_props_.Reverb.GainLF = efx_props.flGainLF; - al_effect_props_.Reverb.DecayTime = efx_props.flDecayTime; - al_effect_props_.Reverb.DecayHFRatio = efx_props.flDecayHFRatio; - al_effect_props_.Reverb.DecayLFRatio = efx_props.flDecayLFRatio; - al_effect_props_.Reverb.ReflectionsGain = efx_props.flReflectionsGain; - al_effect_props_.Reverb.ReflectionsDelay = efx_props.flReflectionsDelay; - al_effect_props_.Reverb.ReflectionsPan[0] = efx_props.flReflectionsPan[0]; - al_effect_props_.Reverb.ReflectionsPan[1] = efx_props.flReflectionsPan[1]; - al_effect_props_.Reverb.ReflectionsPan[2] = efx_props.flReflectionsPan[2]; - al_effect_props_.Reverb.LateReverbGain = efx_props.flLateReverbGain; - al_effect_props_.Reverb.LateReverbDelay = efx_props.flLateReverbDelay; - al_effect_props_.Reverb.LateReverbPan[0] = efx_props.flLateReverbPan[0]; - al_effect_props_.Reverb.LateReverbPan[1] = efx_props.flLateReverbPan[1]; - al_effect_props_.Reverb.LateReverbPan[2] = efx_props.flLateReverbPan[2]; - al_effect_props_.Reverb.EchoTime = efx_props.flEchoTime; - al_effect_props_.Reverb.EchoDepth = efx_props.flEchoDepth; - al_effect_props_.Reverb.ModulationTime = efx_props.flModulationTime; - al_effect_props_.Reverb.ModulationDepth = efx_props.flModulationDepth; - al_effect_props_.Reverb.HFReference = efx_props.flHFReference; - al_effect_props_.Reverb.LFReference = efx_props.flLFReference; - al_effect_props_.Reverb.RoomRolloffFactor = efx_props.flRoomRolloffFactor; - al_effect_props_.Reverb.AirAbsorptionGainHF = efx_props.flAirAbsorptionGainHF; - al_effect_props_.Reverb.DecayHFLimit = false; -} - -void EaxReverbEffect::defer_environment( - unsigned long ulEnvironment) -{ - eax_d_.ulEnvironment = ulEnvironment; - eax_dirty_flags_.ulEnvironment = (eax_.ulEnvironment != eax_d_.ulEnvironment); -} - -void EaxReverbEffect::defer_environment_size( - float flEnvironmentSize) -{ - eax_d_.flEnvironmentSize = flEnvironmentSize; - eax_dirty_flags_.flEnvironmentSize = (eax_.flEnvironmentSize != eax_d_.flEnvironmentSize); -} - -void EaxReverbEffect::defer_environment_diffusion( - float flEnvironmentDiffusion) -{ - eax_d_.flEnvironmentDiffusion = flEnvironmentDiffusion; - eax_dirty_flags_.flEnvironmentDiffusion = (eax_.flEnvironmentDiffusion != eax_d_.flEnvironmentDiffusion); -} - -void EaxReverbEffect::defer_room( - long lRoom) -{ - eax_d_.lRoom = lRoom; - eax_dirty_flags_.lRoom = (eax_.lRoom != eax_d_.lRoom); -} - -void EaxReverbEffect::defer_room_hf( - long lRoomHF) -{ - eax_d_.lRoomHF = lRoomHF; - eax_dirty_flags_.lRoomHF = (eax_.lRoomHF != eax_d_.lRoomHF); -} - -void EaxReverbEffect::defer_room_lf( - long lRoomLF) -{ - eax_d_.lRoomLF = lRoomLF; - eax_dirty_flags_.lRoomLF = (eax_.lRoomLF != eax_d_.lRoomLF); -} - -void EaxReverbEffect::defer_decay_time( - float flDecayTime) -{ - eax_d_.flDecayTime = flDecayTime; - eax_dirty_flags_.flDecayTime = (eax_.flDecayTime != eax_d_.flDecayTime); -} - -void EaxReverbEffect::defer_decay_hf_ratio( - float flDecayHFRatio) -{ - eax_d_.flDecayHFRatio = flDecayHFRatio; - eax_dirty_flags_.flDecayHFRatio = (eax_.flDecayHFRatio != eax_d_.flDecayHFRatio); -} - -void EaxReverbEffect::defer_decay_lf_ratio( - float flDecayLFRatio) -{ - eax_d_.flDecayLFRatio = flDecayLFRatio; - eax_dirty_flags_.flDecayLFRatio = (eax_.flDecayLFRatio != eax_d_.flDecayLFRatio); -} - -void EaxReverbEffect::defer_reflections( - long lReflections) -{ - eax_d_.lReflections = lReflections; - eax_dirty_flags_.lReflections = (eax_.lReflections != eax_d_.lReflections); -} - -void EaxReverbEffect::defer_reflections_delay( - float flReflectionsDelay) -{ - eax_d_.flReflectionsDelay = flReflectionsDelay; - eax_dirty_flags_.flReflectionsDelay = (eax_.flReflectionsDelay != eax_d_.flReflectionsDelay); -} - -void EaxReverbEffect::defer_reflections_pan( - const EAXVECTOR& vReflectionsPan) -{ - eax_d_.vReflectionsPan = vReflectionsPan; - eax_dirty_flags_.vReflectionsPan = (eax_.vReflectionsPan != eax_d_.vReflectionsPan); -} - -void EaxReverbEffect::defer_reverb( - long lReverb) -{ - eax_d_.lReverb = lReverb; - eax_dirty_flags_.lReverb = (eax_.lReverb != eax_d_.lReverb); -} - -void EaxReverbEffect::defer_reverb_delay( - float flReverbDelay) -{ - eax_d_.flReverbDelay = flReverbDelay; - eax_dirty_flags_.flReverbDelay = (eax_.flReverbDelay != eax_d_.flReverbDelay); -} - -void EaxReverbEffect::defer_reverb_pan( - const EAXVECTOR& vReverbPan) -{ - eax_d_.vReverbPan = vReverbPan; - eax_dirty_flags_.vReverbPan = (eax_.vReverbPan != eax_d_.vReverbPan); -} - -void EaxReverbEffect::defer_echo_time( - float flEchoTime) -{ - eax_d_.flEchoTime = flEchoTime; - eax_dirty_flags_.flEchoTime = (eax_.flEchoTime != eax_d_.flEchoTime); -} - -void EaxReverbEffect::defer_echo_depth( - float flEchoDepth) -{ - eax_d_.flEchoDepth = flEchoDepth; - eax_dirty_flags_.flEchoDepth = (eax_.flEchoDepth != eax_d_.flEchoDepth); -} - -void EaxReverbEffect::defer_modulation_time( - float flModulationTime) -{ - eax_d_.flModulationTime = flModulationTime; - eax_dirty_flags_.flModulationTime = (eax_.flModulationTime != eax_d_.flModulationTime); -} - -void EaxReverbEffect::defer_modulation_depth( - float flModulationDepth) -{ - eax_d_.flModulationDepth = flModulationDepth; - eax_dirty_flags_.flModulationDepth = (eax_.flModulationDepth != eax_d_.flModulationDepth); -} - -void EaxReverbEffect::defer_air_absorbtion_hf( - float flAirAbsorptionHF) -{ - eax_d_.flAirAbsorptionHF = flAirAbsorptionHF; - eax_dirty_flags_.flAirAbsorptionHF = (eax_.flAirAbsorptionHF != eax_d_.flAirAbsorptionHF); -} - -void EaxReverbEffect::defer_hf_reference( - float flHFReference) -{ - eax_d_.flHFReference = flHFReference; - eax_dirty_flags_.flHFReference = (eax_.flHFReference != eax_d_.flHFReference); -} - -void EaxReverbEffect::defer_lf_reference( - float flLFReference) -{ - eax_d_.flLFReference = flLFReference; - eax_dirty_flags_.flLFReference = (eax_.flLFReference != eax_d_.flLFReference); -} - -void EaxReverbEffect::defer_room_rolloff_factor( - float flRoomRolloffFactor) -{ - eax_d_.flRoomRolloffFactor = flRoomRolloffFactor; - eax_dirty_flags_.flRoomRolloffFactor = (eax_.flRoomRolloffFactor != eax_d_.flRoomRolloffFactor); -} - -void EaxReverbEffect::defer_flags( - unsigned long ulFlags) -{ - eax_d_.ulFlags = ulFlags; - eax_dirty_flags_.ulFlags = (eax_.ulFlags != eax_d_.ulFlags); -} - -void EaxReverbEffect::defer_all( - const EAX20LISTENERPROPERTIES& listener) -{ - defer_room(listener.lRoom); - defer_room_hf(listener.lRoomHF); - defer_room_rolloff_factor(listener.flRoomRolloffFactor); - defer_decay_time(listener.flDecayTime); - defer_decay_hf_ratio(listener.flDecayHFRatio); - defer_reflections(listener.lReflections); - defer_reflections_delay(listener.flReflectionsDelay); - defer_reverb(listener.lReverb); - defer_reverb_delay(listener.flReverbDelay); - defer_environment(listener.dwEnvironment); - defer_environment_size(listener.flEnvironmentSize); - defer_environment_diffusion(listener.flEnvironmentDiffusion); - defer_air_absorbtion_hf(listener.flAirAbsorptionHF); - defer_flags(listener.dwFlags); -} - -void EaxReverbEffect::defer_all( - const EAXREVERBPROPERTIES& lReverb) -{ - defer_environment(lReverb.ulEnvironment); - defer_environment_size(lReverb.flEnvironmentSize); - defer_environment_diffusion(lReverb.flEnvironmentDiffusion); - defer_room(lReverb.lRoom); - defer_room_hf(lReverb.lRoomHF); - defer_room_lf(lReverb.lRoomLF); - defer_decay_time(lReverb.flDecayTime); - defer_decay_hf_ratio(lReverb.flDecayHFRatio); - defer_decay_lf_ratio(lReverb.flDecayLFRatio); - defer_reflections(lReverb.lReflections); - defer_reflections_delay(lReverb.flReflectionsDelay); - defer_reflections_pan(lReverb.vReflectionsPan); - defer_reverb(lReverb.lReverb); - defer_reverb_delay(lReverb.flReverbDelay); - defer_reverb_pan(lReverb.vReverbPan); - defer_echo_time(lReverb.flEchoTime); - defer_echo_depth(lReverb.flEchoDepth); - defer_modulation_time(lReverb.flModulationTime); - defer_modulation_depth(lReverb.flModulationDepth); - defer_air_absorbtion_hf(lReverb.flAirAbsorptionHF); - defer_hf_reference(lReverb.flHFReference); - defer_lf_reference(lReverb.flLFReference); - defer_room_rolloff_factor(lReverb.flRoomRolloffFactor); - defer_flags(lReverb.ulFlags); -} - - -void EaxReverbEffect::v1_defer_environment(const EaxEaxCall& eax_call) -{ - const auto& environment = eax_call.get_value(); - - validate_environment(environment, 1, true); - - const auto& reverb_preset = EAX1REVERB_PRESETS[environment]; - v1_defer_all(reverb_preset); -} - -void EaxReverbEffect::v1_defer_volume(const EaxEaxCall& eax_call) -{ - const auto& volume = eax_call.get_value(); - - v1_validate_volume(volume); - v1_defer_volume(volume); -} - -void EaxReverbEffect::v1_defer_decay_time(const EaxEaxCall& eax_call) -{ - const auto& decay_time = eax_call.get_value(); - - v1_validate_decay_time(decay_time); - v1_defer_decay_time(decay_time); -} - -void EaxReverbEffect::v1_defer_damping(const EaxEaxCall& eax_call) -{ - const auto& damping = eax_call.get_value(); - - v1_validate_damping(damping); - v1_defer_damping(damping); -} - -void EaxReverbEffect::v1_defer_all(const EaxEaxCall& eax_call) -{ - const auto& reverb_all = eax_call.get_value(); - - v1_validate_all(reverb_all); - v1_defer_all(reverb_all); -} - - -void EaxReverbEffect::defer_environment( - const EaxEaxCall& eax_call) -{ - const auto& ulEnvironment = - eax_call.get_value(); - - validate_environment(ulEnvironment, eax_call.get_version(), true); - - if (eax_d_.ulEnvironment == ulEnvironment) - { - return; - } - - const auto& reverb_preset = EAXREVERB_PRESETS[ulEnvironment]; - - defer_all(reverb_preset); -} - -void EaxReverbEffect::defer_environment_size( - const EaxEaxCall& eax_call) -{ - const auto& flEnvironmentSize = - eax_call.get_value(); - - validate_environment_size(flEnvironmentSize); - - if (eax_d_.flEnvironmentSize == flEnvironmentSize) - { - return; - } - - const auto scale = flEnvironmentSize / eax_d_.flEnvironmentSize; - - defer_environment_size(flEnvironmentSize); - - if ((eax_d_.ulFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0) + if (props_.lReflections != props.lReflections) { - const auto flDecayTime = clamp( - scale * eax_d_.flDecayTime, - EAXREVERB_MINDECAYTIME, - EAXREVERB_MAXDECAYTIME); - - defer_decay_time(flDecayTime); + is_dirty = true; + set_efx_reflections_gain(); } - if ((eax_d_.ulFlags & EAXREVERBFLAGS_REFLECTIONSSCALE) != 0) + if (props_.flReflectionsDelay != props.flReflectionsDelay) { - if ((eax_d_.ulFlags & EAXREVERBFLAGS_REFLECTIONSDELAYSCALE) != 0) - { - const auto lReflections = clamp( - eax_d_.lReflections - static_cast(gain_to_level_mb(scale)), - EAXREVERB_MINREFLECTIONS, - EAXREVERB_MAXREFLECTIONS); - - defer_reflections(lReflections); - } + is_dirty = true; + set_efx_reflections_delay(); } - if ((eax_d_.ulFlags & EAXREVERBFLAGS_REFLECTIONSDELAYSCALE) != 0) + if (props_.vReflectionsPan != props.vReflectionsPan) { - const auto flReflectionsDelay = clamp( - eax_d_.flReflectionsDelay * scale, - EAXREVERB_MINREFLECTIONSDELAY, - EAXREVERB_MAXREFLECTIONSDELAY); - - defer_reflections_delay(flReflectionsDelay); + is_dirty = true; + set_efx_reflections_pan(); } - if ((eax_d_.ulFlags & EAXREVERBFLAGS_REVERBSCALE) != 0) + if (props_.lReverb != props.lReverb) { - const auto log_scalar = ((eax_d_.ulFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0) ? 2'000.0F : 3'000.0F; - - const auto lReverb = clamp( - eax_d_.lReverb - static_cast(std::log10(scale) * log_scalar), - EAXREVERB_MINREVERB, - EAXREVERB_MAXREVERB); - - defer_reverb(lReverb); + is_dirty = true; + set_efx_late_reverb_gain(); } - if ((eax_d_.ulFlags & EAXREVERBFLAGS_REVERBDELAYSCALE) != 0) + if (props_.flReverbDelay != props.flReverbDelay) { - const auto flReverbDelay = clamp( - scale * eax_d_.flReverbDelay, - EAXREVERB_MINREVERBDELAY, - EAXREVERB_MAXREVERBDELAY); - - defer_reverb_delay(flReverbDelay); + is_dirty = true; + set_efx_late_reverb_delay(); } - if ((eax_d_.ulFlags & EAXREVERBFLAGS_ECHOTIMESCALE) != 0) + if (props_.vReverbPan != props.vReverbPan) { - const auto flEchoTime = clamp( - eax_d_.flEchoTime * scale, - EAXREVERB_MINECHOTIME, - EAXREVERB_MAXECHOTIME); - - defer_echo_time(flEchoTime); + is_dirty = true; + set_efx_late_reverb_pan(); } - if ((eax_d_.ulFlags & EAXREVERBFLAGS_MODULATIONTIMESCALE) != 0) + if (props_.flEchoTime != props.flEchoTime) { - const auto flModulationTime = clamp( - scale * eax_d_.flModulationTime, - EAXREVERB_MINMODULATIONTIME, - EAXREVERB_MAXMODULATIONTIME); - - defer_modulation_time(flModulationTime); + is_dirty = true; + set_efx_echo_time(); } -} - -void EaxReverbEffect::defer_environment_diffusion( - const EaxEaxCall& eax_call) -{ - const auto& flEnvironmentDiffusion = - eax_call.get_value(); - - validate_environment_diffusion(flEnvironmentDiffusion); - defer_environment_diffusion(flEnvironmentDiffusion); -} - -void EaxReverbEffect::defer_room( - const EaxEaxCall& eax_call) -{ - const auto& lRoom = - eax_call.get_value(); - - validate_room(lRoom); - defer_room(lRoom); -} - -void EaxReverbEffect::defer_room_hf( - const EaxEaxCall& eax_call) -{ - const auto& lRoomHF = - eax_call.get_value(); - - validate_room_hf(lRoomHF); - defer_room_hf(lRoomHF); -} - -void EaxReverbEffect::defer_room_lf( - const EaxEaxCall& eax_call) -{ - const auto& lRoomLF = - eax_call.get_value(); - - validate_room_lf(lRoomLF); - defer_room_lf(lRoomLF); -} - -void EaxReverbEffect::defer_decay_time( - const EaxEaxCall& eax_call) -{ - const auto& flDecayTime = - eax_call.get_value(); - - validate_decay_time(flDecayTime); - defer_decay_time(flDecayTime); -} - -void EaxReverbEffect::defer_decay_hf_ratio( - const EaxEaxCall& eax_call) -{ - const auto& flDecayHFRatio = - eax_call.get_value(); - - validate_decay_hf_ratio(flDecayHFRatio); - defer_decay_hf_ratio(flDecayHFRatio); -} - -void EaxReverbEffect::defer_decay_lf_ratio( - const EaxEaxCall& eax_call) -{ - const auto& flDecayLFRatio = - eax_call.get_value(); - - validate_decay_lf_ratio(flDecayLFRatio); - defer_decay_lf_ratio(flDecayLFRatio); -} - -void EaxReverbEffect::defer_reflections( - const EaxEaxCall& eax_call) -{ - const auto& lReflections = - eax_call.get_value(); - - validate_reflections(lReflections); - defer_reflections(lReflections); -} - -void EaxReverbEffect::defer_reflections_delay( - const EaxEaxCall& eax_call) -{ - const auto& flReflectionsDelay = - eax_call.get_value(); - - validate_reflections_delay(flReflectionsDelay); - defer_reflections_delay(flReflectionsDelay); -} - -void EaxReverbEffect::defer_reflections_pan( - const EaxEaxCall& eax_call) -{ - const auto& vReflectionsPan = - eax_call.get_value(); - - validate_reflections_pan(vReflectionsPan); - defer_reflections_pan(vReflectionsPan); -} - -void EaxReverbEffect::defer_reverb( - const EaxEaxCall& eax_call) -{ - const auto& lReverb = - eax_call.get_value(); - - validate_reverb(lReverb); - defer_reverb(lReverb); -} - -void EaxReverbEffect::defer_reverb_delay( - const EaxEaxCall& eax_call) -{ - const auto& flReverbDelay = - eax_call.get_value(); - - validate_reverb_delay(flReverbDelay); - defer_reverb_delay(flReverbDelay); -} - -void EaxReverbEffect::defer_reverb_pan( - const EaxEaxCall& eax_call) -{ - const auto& vReverbPan = - eax_call.get_value(); - - validate_reverb_pan(vReverbPan); - defer_reverb_pan(vReverbPan); -} - -void EaxReverbEffect::defer_echo_time( - const EaxEaxCall& eax_call) -{ - const auto& flEchoTime = - eax_call.get_value(); - - validate_echo_time(flEchoTime); - defer_echo_time(flEchoTime); -} - -void EaxReverbEffect::defer_echo_depth( - const EaxEaxCall& eax_call) -{ - const auto& flEchoDepth = - eax_call.get_value(); - - validate_echo_depth(flEchoDepth); - defer_echo_depth(flEchoDepth); -} - -void EaxReverbEffect::defer_modulation_time( - const EaxEaxCall& eax_call) -{ - const auto& flModulationTime = - eax_call.get_value(); - - validate_modulation_time(flModulationTime); - defer_modulation_time(flModulationTime); -} - -void EaxReverbEffect::defer_modulation_depth( - const EaxEaxCall& eax_call) -{ - const auto& flModulationDepth = - eax_call.get_value(); - - validate_modulation_depth(flModulationDepth); - defer_modulation_depth(flModulationDepth); -} - -void EaxReverbEffect::defer_air_absorbtion_hf( - const EaxEaxCall& eax_call) -{ - const auto& air_absorbtion_hf = - eax_call.get_value(); - - validate_air_absorbtion_hf(air_absorbtion_hf); - defer_air_absorbtion_hf(air_absorbtion_hf); -} - -void EaxReverbEffect::defer_hf_reference( - const EaxEaxCall& eax_call) -{ - const auto& flHFReference = - eax_call.get_value(); - - validate_hf_reference(flHFReference); - defer_hf_reference(flHFReference); -} - -void EaxReverbEffect::defer_lf_reference( - const EaxEaxCall& eax_call) -{ - const auto& flLFReference = - eax_call.get_value(); - - validate_lf_reference(flLFReference); - defer_lf_reference(flLFReference); -} - -void EaxReverbEffect::defer_room_rolloff_factor( - const EaxEaxCall& eax_call) -{ - const auto& flRoomRolloffFactor = - eax_call.get_value(); - - validate_room_rolloff_factor(flRoomRolloffFactor); - defer_room_rolloff_factor(flRoomRolloffFactor); -} - -void EaxReverbEffect::defer_flags( - const EaxEaxCall& eax_call) -{ - const auto& ulFlags = - eax_call.get_value(); - - validate_flags(ulFlags); - defer_flags(ulFlags); -} -void EaxReverbEffect::defer_all( - const EaxEaxCall& eax_call) -{ - const auto eax_version = eax_call.get_version(); - - if (eax_version == 2) - { - const auto& listener = - eax_call.get_value(); - - validate_all(listener, eax_version); - defer_all(listener); - } - else + if (props_.flEchoDepth != props.flEchoDepth) { - const auto& reverb_all = - eax_call.get_value(); - - validate_all(reverb_all, eax_version); - defer_all(reverb_all); + is_dirty = true; + set_efx_echo_depth(); } -} - -void EaxReverbEffect::v1_defer(const EaxEaxCall& eax_call) -{ - switch (eax_call.get_property_id()) + if (props_.flModulationTime != props.flModulationTime) { - case DSPROPERTY_EAX_ALL: return v1_defer_all(eax_call); - case DSPROPERTY_EAX_ENVIRONMENT: return v1_defer_environment(eax_call); - case DSPROPERTY_EAX_VOLUME: return v1_defer_volume(eax_call); - case DSPROPERTY_EAX_DECAYTIME: return v1_defer_decay_time(eax_call); - case DSPROPERTY_EAX_DAMPING: return v1_defer_damping(eax_call); - default: eax_fail("Unsupported property id."); + is_dirty = true; + set_efx_modulation_time(); } -} -// [[nodiscard]] -bool EaxReverbEffect::apply_deferred() -{ - bool ret{false}; - - if(unlikely(eax1_dirty_flags_ != Eax1ReverbEffectDirtyFlags{})) + if (props_.flModulationDepth != props.flModulationDepth) { - eax1_ = eax1_d_; - - v1_set_efx(); - - eax1_dirty_flags_ = Eax1ReverbEffectDirtyFlags{}; - - ret = true; + is_dirty = true; + set_efx_modulation_depth(); } - if(eax_dirty_flags_ == EaxReverbEffectDirtyFlags{}) - return ret; - - eax_ = eax_d_; - - if (eax_dirty_flags_.ulEnvironment) + if (props_.flAirAbsorptionHF != props.flAirAbsorptionHF) { + is_dirty = true; + set_efx_air_absorption_gain_hf(); } - if (eax_dirty_flags_.flEnvironmentSize) + if (props_.flHFReference != props.flHFReference) { - set_efx_density_from_environment_size(); + is_dirty = true; + set_efx_hf_reference(); } - if (eax_dirty_flags_.flEnvironmentDiffusion) + if (props_.flLFReference != props.flLFReference) { - set_efx_diffusion(); + is_dirty = true; + set_efx_lf_reference(); } - if (eax_dirty_flags_.lRoom) + if (props_.flRoomRolloffFactor != props.flRoomRolloffFactor) { - set_efx_gain(); + is_dirty = true; + set_efx_room_rolloff_factor(); } - if (eax_dirty_flags_.lRoomHF) + if (props_.ulFlags != props.ulFlags) { - set_efx_gain_hf(); + is_dirty = true; + set_efx_flags(); } - if (eax_dirty_flags_.lRoomLF) - { - set_efx_gain_lf(); - } + return is_dirty; +} - if (eax_dirty_flags_.flDecayTime) +void EaxReverbEffect::set1(const EaxCall& call, Props1& props) +{ + switch (call.get_property_id()) { - set_efx_decay_time(); + case DSPROPERTY_EAX_ALL: defer(call, props); break; + case DSPROPERTY_EAX_ENVIRONMENT: defer(call, props.environment); break; + case DSPROPERTY_EAX_VOLUME: defer(call, props.fVolume); break; + case DSPROPERTY_EAX_DECAYTIME: defer(call, props.fDecayTime_sec); break; + case DSPROPERTY_EAX_DAMPING: defer(call, props.fDamping); break; + default: fail_unknown_property_id(); } +} - if (eax_dirty_flags_.flDecayHFRatio) +void EaxReverbEffect::set2(const EaxCall& call, Props2& props) +{ + switch (call.get_property_id()) { - set_efx_decay_hf_ratio(); - } + case DSPROPERTY_EAX20LISTENER_NONE: + break; - if (eax_dirty_flags_.flDecayLFRatio) - { - set_efx_decay_lf_ratio(); - } + case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS: + defer(call, props); + break; - if (eax_dirty_flags_.lReflections) - { - set_efx_reflections_gain(); - } + case DSPROPERTY_EAX20LISTENER_ROOM: + defer(call, props.lRoom); + break; - if (eax_dirty_flags_.flReflectionsDelay) - { - set_efx_reflections_delay(); - } + case DSPROPERTY_EAX20LISTENER_ROOMHF: + defer(call, props.lRoomHF); + break; - if (eax_dirty_flags_.vReflectionsPan) - { - set_efx_reflections_pan(); - } + case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR: + defer(call, props.flRoomRolloffFactor); + break; - if (eax_dirty_flags_.lReverb) - { - set_efx_late_reverb_gain(); - } + case DSPROPERTY_EAX20LISTENER_DECAYTIME: + defer(call, props.flDecayTime); + break; - if (eax_dirty_flags_.flReverbDelay) - { - set_efx_late_reverb_delay(); - } + case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO: + defer(call, props.flDecayHFRatio); + break; - if (eax_dirty_flags_.vReverbPan) - { - set_efx_late_reverb_pan(); - } + case DSPROPERTY_EAX20LISTENER_REFLECTIONS: + defer(call, props.lReflections); + break; - if (eax_dirty_flags_.flEchoTime) - { - set_efx_echo_time(); - } + case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY: + defer(call, props.flReverbDelay); + break; - if (eax_dirty_flags_.flEchoDepth) - { - set_efx_echo_depth(); - } + case DSPROPERTY_EAX20LISTENER_REVERB: + defer(call, props.lReverb); + break; - if (eax_dirty_flags_.flModulationTime) - { - set_efx_modulation_time(); - } + case DSPROPERTY_EAX20LISTENER_REVERBDELAY: + defer(call, props.flReverbDelay); + break; - if (eax_dirty_flags_.flModulationDepth) - { - set_efx_modulation_depth(); - } + case DSPROPERTY_EAX20LISTENER_ENVIRONMENT: + defer(call, props, props.dwEnvironment); + break; - if (eax_dirty_flags_.flAirAbsorptionHF) - { - set_efx_air_absorption_gain_hf(); - } + case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE: + defer(call, props, props.flEnvironmentSize); + break; - if (eax_dirty_flags_.flHFReference) - { - set_efx_hf_reference(); - } + case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION: + defer(call, props.flEnvironmentDiffusion); + break; - if (eax_dirty_flags_.flLFReference) - { - set_efx_lf_reference(); - } + case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF: + defer(call, props.flAirAbsorptionHF); + break; - if (eax_dirty_flags_.flRoomRolloffFactor) - { - set_efx_room_rolloff_factor(); - } + case DSPROPERTY_EAX20LISTENER_FLAGS: + defer(call, props.dwFlags); + break; - if (eax_dirty_flags_.ulFlags) - { - set_efx_flags(); + default: + fail_unknown_property_id(); } - - eax_dirty_flags_ = EaxReverbEffectDirtyFlags{}; - - return true; } -void EaxReverbEffect::set(const EaxEaxCall& eax_call) +void EaxReverbEffect::set3(const EaxCall& call, Props3& props) { - if(eax_call.get_version() == 1) - v1_defer(eax_call); - else switch(eax_call.get_property_id()) + switch(call.get_property_id()) { case EAXREVERB_NONE: break; case EAXREVERB_ALLPARAMETERS: - defer_all(eax_call); + defer(call, props); break; case EAXREVERB_ENVIRONMENT: - defer_environment(eax_call); + defer(call, props, props.ulEnvironment); break; case EAXREVERB_ENVIRONMENTSIZE: - defer_environment_size(eax_call); + defer(call, props, props.flEnvironmentSize); break; case EAXREVERB_ENVIRONMENTDIFFUSION: - defer_environment_diffusion(eax_call); + defer3(call, props, props.flEnvironmentDiffusion); break; case EAXREVERB_ROOM: - defer_room(eax_call); + defer3(call, props, props.lRoom); break; case EAXREVERB_ROOMHF: - defer_room_hf(eax_call); + defer3(call, props, props.lRoomHF); break; case EAXREVERB_ROOMLF: - defer_room_lf(eax_call); + defer3(call, props, props.lRoomLF); break; case EAXREVERB_DECAYTIME: - defer_decay_time(eax_call); + defer3(call, props, props.flDecayTime); break; case EAXREVERB_DECAYHFRATIO: - defer_decay_hf_ratio(eax_call); + defer3(call, props, props.flDecayHFRatio); break; case EAXREVERB_DECAYLFRATIO: - defer_decay_lf_ratio(eax_call); + defer3(call, props, props.flDecayLFRatio); break; case EAXREVERB_REFLECTIONS: - defer_reflections(eax_call); + defer3(call, props, props.lReflections); break; case EAXREVERB_REFLECTIONSDELAY: - defer_reflections_delay(eax_call); + defer3(call, props, props.flReflectionsDelay); break; case EAXREVERB_REFLECTIONSPAN: - defer_reflections_pan(eax_call); + defer3(call, props, props.vReflectionsPan); break; case EAXREVERB_REVERB: - defer_reverb(eax_call); + defer3(call, props, props.lReverb); break; case EAXREVERB_REVERBDELAY: - defer_reverb_delay(eax_call); + defer3(call, props, props.flReverbDelay); break; case EAXREVERB_REVERBPAN: - defer_reverb_pan(eax_call); + defer3(call, props, props.vReverbPan); break; case EAXREVERB_ECHOTIME: - defer_echo_time(eax_call); + defer3(call, props, props.flEchoTime); break; case EAXREVERB_ECHODEPTH: - defer_echo_depth(eax_call); + defer3(call, props, props.flEchoDepth); break; case EAXREVERB_MODULATIONTIME: - defer_modulation_time(eax_call); + defer3(call, props, props.flModulationTime); break; case EAXREVERB_MODULATIONDEPTH: - defer_modulation_depth(eax_call); + defer3(call, props, props.flModulationDepth); break; case EAXREVERB_AIRABSORPTIONHF: - defer_air_absorbtion_hf(eax_call); + defer3(call, props, props.flAirAbsorptionHF); break; case EAXREVERB_HFREFERENCE: - defer_hf_reference(eax_call); + defer3(call, props, props.flHFReference); break; case EAXREVERB_LFREFERENCE: - defer_lf_reference(eax_call); + defer3(call, props, props.flLFReference); break; case EAXREVERB_ROOMROLLOFFFACTOR: - defer_room_rolloff_factor(eax_call); + defer3(call, props, props.flRoomRolloffFactor); break; case EAXREVERB_FLAGS: - defer_flags(eax_call); + defer3(call, props, props.ulFlags); break; default: - eax_fail("Unsupported property id."); + fail_unknown_property_id(); } } -const EFXEAXREVERBPROPERTIES eax_efx_reverb_presets[EAX1_ENVIRONMENT_COUNT] = +void EaxReverbEffect::set(const EaxCall& call) { - EFX_REVERB_PRESET_GENERIC, - EFX_REVERB_PRESET_PADDEDCELL, - EFX_REVERB_PRESET_ROOM, - EFX_REVERB_PRESET_BATHROOM, - EFX_REVERB_PRESET_LIVINGROOM, - EFX_REVERB_PRESET_STONEROOM, - EFX_REVERB_PRESET_AUDITORIUM, - EFX_REVERB_PRESET_CONCERTHALL, - EFX_REVERB_PRESET_CAVE, - EFX_REVERB_PRESET_ARENA, - EFX_REVERB_PRESET_HANGAR, - EFX_REVERB_PRESET_CARPETEDHALLWAY, - EFX_REVERB_PRESET_HALLWAY, - EFX_REVERB_PRESET_STONECORRIDOR, - EFX_REVERB_PRESET_ALLEY, - EFX_REVERB_PRESET_FOREST, - EFX_REVERB_PRESET_CITY, - EFX_REVERB_PRESET_MOUNTAINS, - EFX_REVERB_PRESET_QUARRY, - EFX_REVERB_PRESET_PLAIN, - EFX_REVERB_PRESET_PARKINGLOT, - EFX_REVERB_PRESET_SEWERPIPE, - EFX_REVERB_PRESET_UNDERWATER, - EFX_REVERB_PRESET_DRUGGED, - EFX_REVERB_PRESET_DIZZY, - EFX_REVERB_PRESET_PSYCHOTIC, -}; // EFXEAXREVERBPROPERTIES + const auto version = call.get_version(); + + switch (version) + { + case 1: set1(call, state1_.d); break; + case 2: set2(call, state2_.d); break; + case 3: set3(call, state3_.d); break; + case 4: set3(call, state4_.d); break; + case 5: set3(call, state5_.d); break; + default: fail_unknown_version(); + } + + version_ = version; +} + +void EaxReverbEffect::translate(const Props1& src, Props3& dst) noexcept +{ + assert(src.environment <= EAX1REVERB_MAXENVIRONMENT); + dst = EAXREVERB_PRESETS[src.environment]; + dst.flDecayTime = src.fDecayTime_sec; + dst.flDecayHFRatio = src.fDamping; + dst.lReverb = mini(static_cast(gain_to_level_mb(src.fVolume)), 0); +} + +void EaxReverbEffect::translate(const Props2& src, Props3& dst) noexcept +{ + assert(src.dwEnvironment <= EAX1REVERB_MAXENVIRONMENT); + const auto& env = EAXREVERB_PRESETS[src.dwEnvironment]; + dst.ulEnvironment = src.dwEnvironment; + dst.flEnvironmentSize = src.flEnvironmentSize; + dst.flEnvironmentDiffusion = src.flEnvironmentDiffusion; + dst.lRoom = src.lRoom; + dst.lRoomHF = src.lRoomHF; + dst.lRoomLF = env.lRoomLF; + dst.flDecayTime = src.flDecayTime; + dst.flDecayHFRatio = src.flDecayHFRatio; + dst.flDecayLFRatio = env.flDecayLFRatio; + dst.lReflections = src.lReflections; + dst.flReflectionsDelay = src.flReflectionsDelay; + dst.vReflectionsPan = env.vReflectionsPan; + dst.lReverb = src.lReverb; + dst.flReverbDelay = src.flReverbDelay; + dst.vReverbPan = env.vReverbPan; + dst.flEchoTime = env.flEchoTime; + dst.flEchoDepth = env.flEchoDepth; + dst.flModulationTime = env.flModulationTime; + dst.flModulationDepth = env.flModulationDepth; + dst.flAirAbsorptionHF = src.flAirAbsorptionHF; + dst.flHFReference = env.flHFReference; + dst.flLFReference = env.flLFReference; + dst.flRoomRolloffFactor = src.flRoomRolloffFactor; + dst.ulFlags = src.dwFlags; +} } // namespace -EaxEffectUPtr eax_create_eax_reverb_effect() +EaxEffectUPtr eax_create_eax_reverb_effect(const EaxCall& call) { - return std::make_unique(); + return std::make_unique(call); } #endif // ALSOFT_EAX diff --git a/al/effects/vmorpher.cpp b/al/effects/vmorpher.cpp index da513015..95f98db5 100644 --- a/al/effects/vmorpher.cpp +++ b/al/effects/vmorpher.cpp @@ -12,9 +12,7 @@ #ifdef ALSOFT_EAX #include - #include "alnumeric.h" - #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -260,178 +258,181 @@ const EffectProps VmorpherEffectProps{genDefaultProps()}; #ifdef ALSOFT_EAX namespace { -using EaxVocalMorpherEffectDirtyFlagsValue = std::uint_least8_t; - -struct EaxVocalMorpherEffectDirtyFlags -{ - using EaxIsBitFieldStruct = bool; - - EaxVocalMorpherEffectDirtyFlagsValue ulPhonemeA : 1; - EaxVocalMorpherEffectDirtyFlagsValue lPhonemeACoarseTuning : 1; - EaxVocalMorpherEffectDirtyFlagsValue ulPhonemeB : 1; - EaxVocalMorpherEffectDirtyFlagsValue lPhonemeBCoarseTuning : 1; - EaxVocalMorpherEffectDirtyFlagsValue ulWaveform : 1; - EaxVocalMorpherEffectDirtyFlagsValue flRate : 1; -}; // EaxPitchShifterEffectDirtyFlags - - -class EaxVocalMorpherEffect final : - public EaxEffect -{ +class EaxVocalMorpherEffectException : public EaxException { public: - EaxVocalMorpherEffect(); - - void dispatch(const EaxEaxCall& eax_call) override; + explicit EaxVocalMorpherEffectException(const char* message) + : EaxException{"EAX_VOCAL_MORPHER_EFFECT", message} + {} +}; // EaxVocalMorpherEffectException - // [[nodiscard]] - bool apply_deferred() override; +class EaxVocalMorpherEffect final : public EaxEffect4 { +public: + EaxVocalMorpherEffect(const EaxCall& call); private: - EAXVOCALMORPHERPROPERTIES eax_{}; - EAXVOCALMORPHERPROPERTIES eax_d_{}; - EaxVocalMorpherEffectDirtyFlags eax_dirty_flags_{}; - - void set_eax_defaults(); + struct PhonemeAValidator { + void operator()(unsigned long ulPhonemeA) const + { + eax_validate_range( + "Phoneme A", + ulPhonemeA, + EAXVOCALMORPHER_MINPHONEMEA, + EAXVOCALMORPHER_MAXPHONEMEA); + } + }; // PhonemeAValidator + + struct PhonemeACoarseTuningValidator { + void operator()(long lPhonemeACoarseTuning) const + { + eax_validate_range( + "Phoneme A Coarse Tuning", + lPhonemeACoarseTuning, + EAXVOCALMORPHER_MINPHONEMEACOARSETUNING, + EAXVOCALMORPHER_MAXPHONEMEACOARSETUNING); + } + }; // PhonemeACoarseTuningValidator + + struct PhonemeBValidator { + void operator()(unsigned long ulPhonemeB) const + { + eax_validate_range( + "Phoneme B", + ulPhonemeB, + EAXVOCALMORPHER_MINPHONEMEB, + EAXVOCALMORPHER_MAXPHONEMEB); + } + }; // PhonemeBValidator + + struct PhonemeBCoarseTuningValidator { + void operator()(long lPhonemeBCoarseTuning) const + { + eax_validate_range( + "Phoneme B Coarse Tuning", + lPhonemeBCoarseTuning, + EAXVOCALMORPHER_MINPHONEMEBCOARSETUNING, + EAXVOCALMORPHER_MAXPHONEMEBCOARSETUNING); + } + }; // PhonemeBCoarseTuningValidator + + struct WaveformValidator { + void operator()(unsigned long ulWaveform) const + { + eax_validate_range( + "Waveform", + ulWaveform, + EAXVOCALMORPHER_MINWAVEFORM, + EAXVOCALMORPHER_MAXWAVEFORM); + } + }; // WaveformValidator + + struct RateValidator { + void operator()(float flRate) const + { + eax_validate_range( + "Rate", + flRate, + EAXVOCALMORPHER_MINRATE, + EAXVOCALMORPHER_MAXRATE); + } + }; // RateValidator + + struct AllValidator { + void operator()(const Props& all) const + { + PhonemeAValidator{}(all.ulPhonemeA); + PhonemeACoarseTuningValidator{}(all.lPhonemeACoarseTuning); + PhonemeBValidator{}(all.ulPhonemeB); + PhonemeBCoarseTuningValidator{}(all.lPhonemeBCoarseTuning); + WaveformValidator{}(all.ulWaveform); + RateValidator{}(all.flRate); + } + }; // AllValidator + + void set_defaults(Props& props) override; void set_efx_phoneme_a(); - void set_efx_phoneme_a_coarse_tuning(); + void set_efx_phoneme_a_coarse_tuning() noexcept; void set_efx_phoneme_b(); - void set_efx_phoneme_b_coarse_tuning(); + void set_efx_phoneme_b_coarse_tuning() noexcept; void set_efx_waveform(); - void set_efx_rate(); - void set_efx_defaults(); - - void get(const EaxEaxCall& eax_call); - - void validate_phoneme_a(unsigned long ulPhonemeA); - void validate_phoneme_a_coarse_tuning(long lPhonemeACoarseTuning); - void validate_phoneme_b(unsigned long ulPhonemeB); - void validate_phoneme_b_coarse_tuning(long lPhonemeBCoarseTuning); - void validate_waveform(unsigned long ulWaveform); - void validate_rate(float flRate); - void validate_all(const EAXVOCALMORPHERPROPERTIES& all); - - void defer_phoneme_a(unsigned long ulPhonemeA); - void defer_phoneme_a_coarse_tuning(long lPhonemeACoarseTuning); - void defer_phoneme_b(unsigned long ulPhonemeB); - void defer_phoneme_b_coarse_tuning(long lPhonemeBCoarseTuning); - void defer_waveform(unsigned long ulWaveform); - void defer_rate(float flRate); - void defer_all(const EAXVOCALMORPHERPROPERTIES& all); - - void defer_phoneme_a(const EaxEaxCall& eax_call); - void defer_phoneme_a_coarse_tuning(const EaxEaxCall& eax_call); - void defer_phoneme_b(const EaxEaxCall& eax_call); - void defer_phoneme_b_coarse_tuning(const EaxEaxCall& eax_call); - void defer_waveform(const EaxEaxCall& eax_call); - void defer_rate(const EaxEaxCall& eax_call); - void defer_all(const EaxEaxCall& eax_call); - - void set(const EaxEaxCall& eax_call); -}; // EaxVocalMorpherEffect + void set_efx_rate() noexcept; + void set_efx_defaults() override; + void get(const EaxCall& call, const Props& props) override; + void set(const EaxCall& call, Props& props) override; + bool commit_props(const Props& props) override; +}; // EaxVocalMorpherEffect -class EaxVocalMorpherEffectException : - public EaxException -{ -public: - explicit EaxVocalMorpherEffectException( - const char* message) - : - EaxException{"EAX_VOCAL_MORPHER_EFFECT", message} - { - } -}; // EaxVocalMorpherEffectException - +EaxVocalMorpherEffect::EaxVocalMorpherEffect(const EaxCall& call) + : EaxEffect4{AL_EFFECT_VOCAL_MORPHER, call} +{} -EaxVocalMorpherEffect::EaxVocalMorpherEffect() - : EaxEffect{AL_EFFECT_VOCAL_MORPHER} +void EaxVocalMorpherEffect::set_defaults(Props& props) { - set_eax_defaults(); - set_efx_defaults(); -} - -void EaxVocalMorpherEffect::dispatch(const EaxEaxCall& eax_call) -{ - eax_call.is_get() ? get(eax_call) : set(eax_call); -} - -void EaxVocalMorpherEffect::set_eax_defaults() -{ - eax_.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA; - eax_.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING; - eax_.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB; - eax_.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING; - eax_.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM; - eax_.flRate = EAXVOCALMORPHER_DEFAULTRATE; - - eax_d_ = eax_; + props.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA; + props.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING; + props.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB; + props.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING; + props.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM; + props.flRate = EAXVOCALMORPHER_DEFAULTRATE; } void EaxVocalMorpherEffect::set_efx_phoneme_a() { const auto phoneme_a = clamp( - static_cast(eax_.ulPhonemeA), + static_cast(props_.ulPhonemeA), AL_VOCAL_MORPHER_MIN_PHONEMEA, AL_VOCAL_MORPHER_MAX_PHONEMEA); - const auto efx_phoneme_a = PhenomeFromEnum(phoneme_a); assert(efx_phoneme_a.has_value()); al_effect_props_.Vmorpher.PhonemeA = *efx_phoneme_a; } -void EaxVocalMorpherEffect::set_efx_phoneme_a_coarse_tuning() +void EaxVocalMorpherEffect::set_efx_phoneme_a_coarse_tuning() noexcept { const auto phoneme_a_coarse_tuning = clamp( - static_cast(eax_.lPhonemeACoarseTuning), + static_cast(props_.lPhonemeACoarseTuning), AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING, AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING); - al_effect_props_.Vmorpher.PhonemeACoarseTuning = phoneme_a_coarse_tuning; } void EaxVocalMorpherEffect::set_efx_phoneme_b() { const auto phoneme_b = clamp( - static_cast(eax_.ulPhonemeB), + static_cast(props_.ulPhonemeB), AL_VOCAL_MORPHER_MIN_PHONEMEB, AL_VOCAL_MORPHER_MAX_PHONEMEB); - const auto efx_phoneme_b = PhenomeFromEnum(phoneme_b); assert(efx_phoneme_b.has_value()); al_effect_props_.Vmorpher.PhonemeB = *efx_phoneme_b; } -void EaxVocalMorpherEffect::set_efx_phoneme_b_coarse_tuning() +void EaxVocalMorpherEffect::set_efx_phoneme_b_coarse_tuning() noexcept { - const auto phoneme_b_coarse_tuning = clamp( - static_cast(eax_.lPhonemeBCoarseTuning), + al_effect_props_.Vmorpher.PhonemeBCoarseTuning = clamp( + static_cast(props_.lPhonemeBCoarseTuning), AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING, AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING); - - al_effect_props_.Vmorpher.PhonemeBCoarseTuning = phoneme_b_coarse_tuning; } void EaxVocalMorpherEffect::set_efx_waveform() { const auto waveform = clamp( - static_cast(eax_.ulWaveform), + static_cast(props_.ulWaveform), AL_VOCAL_MORPHER_MIN_WAVEFORM, AL_VOCAL_MORPHER_MAX_WAVEFORM); - const auto wfx_waveform = WaveformFromEmum(waveform); assert(wfx_waveform.has_value()); al_effect_props_.Vmorpher.Waveform = *wfx_waveform; } -void EaxVocalMorpherEffect::set_efx_rate() +void EaxVocalMorpherEffect::set_efx_rate() noexcept { - const auto rate = clamp( - eax_.flRate, + al_effect_props_.Vmorpher.Rate = clamp( + props_.flRate, AL_VOCAL_MORPHER_MIN_RATE, AL_VOCAL_MORPHER_MAX_RATE); - - al_effect_props_.Vmorpher.Rate = rate; } void EaxVocalMorpherEffect::set_efx_defaults() @@ -444,343 +445,134 @@ void EaxVocalMorpherEffect::set_efx_defaults() set_efx_rate(); } -void EaxVocalMorpherEffect::get(const EaxEaxCall& eax_call) +void EaxVocalMorpherEffect::get(const EaxCall& call, const Props& props) { - switch(eax_call.get_property_id()) + switch(call.get_property_id()) { case EAXVOCALMORPHER_NONE: break; case EAXVOCALMORPHER_ALLPARAMETERS: - eax_call.set_value(eax_); + call.set_value(props); break; case EAXVOCALMORPHER_PHONEMEA: - eax_call.set_value(eax_.ulPhonemeA); + call.set_value(props.ulPhonemeA); break; case EAXVOCALMORPHER_PHONEMEACOARSETUNING: - eax_call.set_value(eax_.lPhonemeACoarseTuning); + call.set_value(props.lPhonemeACoarseTuning); break; case EAXVOCALMORPHER_PHONEMEB: - eax_call.set_value(eax_.ulPhonemeB); + call.set_value(props.ulPhonemeB); break; case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: - eax_call.set_value(eax_.lPhonemeBCoarseTuning); + call.set_value(props.lPhonemeBCoarseTuning); break; case EAXVOCALMORPHER_WAVEFORM: - eax_call.set_value(eax_.ulWaveform); + call.set_value(props.ulWaveform); break; case EAXVOCALMORPHER_RATE: - eax_call.set_value(eax_.flRate); + call.set_value(props.flRate); break; default: - throw EaxVocalMorpherEffectException{"Unsupported property id."}; + fail_unknown_property_id(); } } -void EaxVocalMorpherEffect::validate_phoneme_a( - unsigned long ulPhonemeA) +void EaxVocalMorpherEffect::set(const EaxCall& call, Props& props) { - eax_validate_range( - "Phoneme A", - ulPhonemeA, - EAXVOCALMORPHER_MINPHONEMEA, - EAXVOCALMORPHER_MAXPHONEMEA); -} - -void EaxVocalMorpherEffect::validate_phoneme_a_coarse_tuning( - long lPhonemeACoarseTuning) -{ - eax_validate_range( - "Phoneme A Coarse Tuning", - lPhonemeACoarseTuning, - EAXVOCALMORPHER_MINPHONEMEACOARSETUNING, - EAXVOCALMORPHER_MAXPHONEMEACOARSETUNING); -} - -void EaxVocalMorpherEffect::validate_phoneme_b( - unsigned long ulPhonemeB) -{ - eax_validate_range( - "Phoneme B", - ulPhonemeB, - EAXVOCALMORPHER_MINPHONEMEB, - EAXVOCALMORPHER_MAXPHONEMEB); -} - -void EaxVocalMorpherEffect::validate_phoneme_b_coarse_tuning( - long lPhonemeBCoarseTuning) -{ - eax_validate_range( - "Phoneme B Coarse Tuning", - lPhonemeBCoarseTuning, - EAXVOCALMORPHER_MINPHONEMEBCOARSETUNING, - EAXVOCALMORPHER_MAXPHONEMEBCOARSETUNING); -} - -void EaxVocalMorpherEffect::validate_waveform( - unsigned long ulWaveform) -{ - eax_validate_range( - "Waveform", - ulWaveform, - EAXVOCALMORPHER_MINWAVEFORM, - EAXVOCALMORPHER_MAXWAVEFORM); -} - -void EaxVocalMorpherEffect::validate_rate( - float flRate) -{ - eax_validate_range( - "Rate", - flRate, - EAXVOCALMORPHER_MINRATE, - EAXVOCALMORPHER_MAXRATE); -} - -void EaxVocalMorpherEffect::validate_all( - const EAXVOCALMORPHERPROPERTIES& all) -{ - validate_phoneme_a(all.ulPhonemeA); - validate_phoneme_a_coarse_tuning(all.lPhonemeACoarseTuning); - validate_phoneme_b(all.ulPhonemeB); - validate_phoneme_b_coarse_tuning(all.lPhonemeBCoarseTuning); - validate_waveform(all.ulWaveform); - validate_rate(all.flRate); -} - -void EaxVocalMorpherEffect::defer_phoneme_a( - unsigned long ulPhonemeA) -{ - eax_d_.ulPhonemeA = ulPhonemeA; - eax_dirty_flags_.ulPhonemeA = (eax_.ulPhonemeA != eax_d_.ulPhonemeA); -} - -void EaxVocalMorpherEffect::defer_phoneme_a_coarse_tuning( - long lPhonemeACoarseTuning) -{ - eax_d_.lPhonemeACoarseTuning = lPhonemeACoarseTuning; - eax_dirty_flags_.lPhonemeACoarseTuning = (eax_.lPhonemeACoarseTuning != eax_d_.lPhonemeACoarseTuning); -} - -void EaxVocalMorpherEffect::defer_phoneme_b( - unsigned long ulPhonemeB) -{ - eax_d_.ulPhonemeB = ulPhonemeB; - eax_dirty_flags_.ulPhonemeB = (eax_.ulPhonemeB != eax_d_.ulPhonemeB); -} - -void EaxVocalMorpherEffect::defer_phoneme_b_coarse_tuning( - long lPhonemeBCoarseTuning) -{ - eax_d_.lPhonemeBCoarseTuning = lPhonemeBCoarseTuning; - eax_dirty_flags_.lPhonemeBCoarseTuning = (eax_.lPhonemeBCoarseTuning != eax_d_.lPhonemeBCoarseTuning); -} - -void EaxVocalMorpherEffect::defer_waveform( - unsigned long ulWaveform) -{ - eax_d_.ulWaveform = ulWaveform; - eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform); -} - -void EaxVocalMorpherEffect::defer_rate( - float flRate) -{ - eax_d_.flRate = flRate; - eax_dirty_flags_.flRate = (eax_.flRate != eax_d_.flRate); -} - -void EaxVocalMorpherEffect::defer_all( - const EAXVOCALMORPHERPROPERTIES& all) -{ - defer_phoneme_a(all.ulPhonemeA); - defer_phoneme_a_coarse_tuning(all.lPhonemeACoarseTuning); - defer_phoneme_b(all.ulPhonemeB); - defer_phoneme_b_coarse_tuning(all.lPhonemeBCoarseTuning); - defer_waveform(all.ulWaveform); - defer_rate(all.flRate); -} - -void EaxVocalMorpherEffect::defer_phoneme_a( - const EaxEaxCall& eax_call) -{ - const auto& phoneme_a = eax_call.get_value(); - - validate_phoneme_a(phoneme_a); - defer_phoneme_a(phoneme_a); -} - -void EaxVocalMorpherEffect::defer_phoneme_a_coarse_tuning( - const EaxEaxCall& eax_call) -{ - const auto& phoneme_a_coarse_tuning = eax_call.get_value< - EaxVocalMorpherEffectException, - const decltype(EAXVOCALMORPHERPROPERTIES::lPhonemeACoarseTuning) - >(); - - validate_phoneme_a_coarse_tuning(phoneme_a_coarse_tuning); - defer_phoneme_a_coarse_tuning(phoneme_a_coarse_tuning); -} - -void EaxVocalMorpherEffect::defer_phoneme_b( - const EaxEaxCall& eax_call) -{ - const auto& phoneme_b = eax_call.get_value< - EaxVocalMorpherEffectException, - const decltype(EAXVOCALMORPHERPROPERTIES::ulPhonemeB) - >(); - - validate_phoneme_b(phoneme_b); - defer_phoneme_b(phoneme_b); -} + switch(call.get_property_id()) + { + case EAXVOCALMORPHER_NONE: + break; -void EaxVocalMorpherEffect::defer_phoneme_b_coarse_tuning( - const EaxEaxCall& eax_call) -{ - const auto& phoneme_b_coarse_tuning = eax_call.get_value< - EaxVocalMorpherEffectException, - const decltype(EAXVOCALMORPHERPROPERTIES::lPhonemeBCoarseTuning) - >(); + case EAXVOCALMORPHER_ALLPARAMETERS: + defer(call, props); + break; - validate_phoneme_b_coarse_tuning(phoneme_b_coarse_tuning); - defer_phoneme_b_coarse_tuning(phoneme_b_coarse_tuning); -} + case EAXVOCALMORPHER_PHONEMEA: + defer(call, props.ulPhonemeA); + break; -void EaxVocalMorpherEffect::defer_waveform( - const EaxEaxCall& eax_call) -{ - const auto& waveform = eax_call.get_value< - EaxVocalMorpherEffectException, - const decltype(EAXVOCALMORPHERPROPERTIES::ulWaveform) - >(); + case EAXVOCALMORPHER_PHONEMEACOARSETUNING: + defer(call, props.lPhonemeACoarseTuning); + break; - validate_waveform(waveform); - defer_waveform(waveform); -} + case EAXVOCALMORPHER_PHONEMEB: + defer(call, props.ulPhonemeB); + break; -void EaxVocalMorpherEffect::defer_rate( - const EaxEaxCall& eax_call) -{ - const auto& rate = eax_call.get_value< - EaxVocalMorpherEffectException, - const decltype(EAXVOCALMORPHERPROPERTIES::flRate) - >(); + case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: + defer(call, props.lPhonemeBCoarseTuning); + break; - validate_rate(rate); - defer_rate(rate); -} + case EAXVOCALMORPHER_WAVEFORM: + defer(call, props.ulWaveform); + break; -void EaxVocalMorpherEffect::defer_all( - const EaxEaxCall& eax_call) -{ - const auto& all = eax_call.get_value< - EaxVocalMorpherEffectException, - const EAXVOCALMORPHERPROPERTIES - >(); + case EAXVOCALMORPHER_RATE: + defer(call, props.flRate); + break; - validate_all(all); - defer_all(all); + default: + fail_unknown_property_id(); + } } -// [[nodiscard]] -bool EaxVocalMorpherEffect::apply_deferred() +bool EaxVocalMorpherEffect::commit_props(const Props& props) { - if (eax_dirty_flags_ == EaxVocalMorpherEffectDirtyFlags{}) - { - return false; - } + auto is_dirty = false; - eax_ = eax_d_; - - if (eax_dirty_flags_.ulPhonemeA) + if (props_.ulPhonemeA != props.ulPhonemeA) { + is_dirty = true; set_efx_phoneme_a(); } - if (eax_dirty_flags_.lPhonemeACoarseTuning) + if (props_.lPhonemeACoarseTuning != props.lPhonemeACoarseTuning) { + is_dirty = true; set_efx_phoneme_a_coarse_tuning(); } - if (eax_dirty_flags_.ulPhonemeB) + if (props_.ulPhonemeB != props.ulPhonemeB) { + is_dirty = true; set_efx_phoneme_b(); } - if (eax_dirty_flags_.lPhonemeBCoarseTuning) + if (props_.lPhonemeBCoarseTuning != props.lPhonemeBCoarseTuning) { + is_dirty = true; set_efx_phoneme_b_coarse_tuning(); } - if (eax_dirty_flags_.ulWaveform) + if (props_.ulWaveform != props.ulWaveform) { + is_dirty = true; set_efx_waveform(); } - if (eax_dirty_flags_.flRate) + if (props_.flRate != props.flRate) { + is_dirty = true; set_efx_rate(); } - eax_dirty_flags_ = EaxVocalMorpherEffectDirtyFlags{}; - - return true; -} - -void EaxVocalMorpherEffect::set(const EaxEaxCall& eax_call) -{ - switch(eax_call.get_property_id()) - { - case EAXVOCALMORPHER_NONE: - break; - - case EAXVOCALMORPHER_ALLPARAMETERS: - defer_all(eax_call); - break; - - case EAXVOCALMORPHER_PHONEMEA: - defer_phoneme_a(eax_call); - break; - - case EAXVOCALMORPHER_PHONEMEACOARSETUNING: - defer_phoneme_a_coarse_tuning(eax_call); - break; - - case EAXVOCALMORPHER_PHONEMEB: - defer_phoneme_b(eax_call); - break; - - case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: - defer_phoneme_b_coarse_tuning(eax_call); - break; - - case EAXVOCALMORPHER_WAVEFORM: - defer_waveform(eax_call); - break; - - case EAXVOCALMORPHER_RATE: - defer_rate(eax_call); - break; - - default: - throw EaxVocalMorpherEffectException{"Unsupported property id."}; - } + return is_dirty; } } // namespace - -EaxEffectUPtr eax_create_eax_vocal_morpher_effect() +EaxEffectUPtr eax_create_eax_vocal_morpher_effect(const EaxCall& call) { - return std::make_unique(); + return eax_create_eax4_effect(call); } #endif // ALSOFT_EAX diff --git a/al/source.cpp b/al/source.cpp index 604d4566..f2c7e2f8 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -4068,10 +4068,10 @@ void ALsource::eax_update_primary_fx_slot_id() } void ALsource::eax_defer_active_fx_slots( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto active_fx_slots_span = - eax_call.get_values(); + call.get_values(); const auto fx_slot_count = active_fx_slots_span.size(); @@ -4436,10 +4436,10 @@ void ALsource::eax_defer_send_all( } void ALsource::eax_defer_send( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto eax_all_span = - eax_call.get_values(); + call.get_values(); const auto count = eax_all_span.size(); @@ -4463,10 +4463,10 @@ void ALsource::eax_defer_send( } void ALsource::eax_defer_send_exclusion_all( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto eax_all_span = - eax_call.get_values(); + call.get_values(); const auto count = eax_all_span.size(); @@ -4490,10 +4490,10 @@ void ALsource::eax_defer_send_exclusion_all( } void ALsource::eax_defer_send_occlusion_all( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto eax_all_span = - eax_call.get_values(); + call.get_values(); const auto count = eax_all_span.size(); @@ -4517,10 +4517,10 @@ void ALsource::eax_defer_send_occlusion_all( } void ALsource::eax_defer_send_all( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto eax_all_span = - eax_call.get_values(); + call.get_values(); const auto count = eax_all_span.size(); @@ -5074,6 +5074,29 @@ void ALsource::eax_defer_source_speaker_level_all( } } +ALuint ALsource::eax2_translate_property_id(const EaxCall& call) +{ + switch (call.get_property_id()) + { + case DSPROPERTY_EAX20BUFFER_NONE: return EAXSOURCE_NONE; + case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS: return EAXSOURCE_ALLPARAMETERS; + case DSPROPERTY_EAX20BUFFER_DIRECT: return EAXSOURCE_DIRECT; + case DSPROPERTY_EAX20BUFFER_DIRECTHF: return EAXSOURCE_DIRECTHF; + case DSPROPERTY_EAX20BUFFER_ROOM: return EAXSOURCE_ROOM; + case DSPROPERTY_EAX20BUFFER_ROOMHF: return EAXSOURCE_ROOMHF; + case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR: return EAXSOURCE_ROOMROLLOFFFACTOR; + case DSPROPERTY_EAX20BUFFER_OBSTRUCTION: return EAXSOURCE_OBSTRUCTION; + case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO: return EAXSOURCE_OBSTRUCTIONLFRATIO; + case DSPROPERTY_EAX20BUFFER_OCCLUSION: return EAXSOURCE_OCCLUSION; + case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO: return EAXSOURCE_OCCLUSIONLFRATIO; + case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO: return EAXSOURCE_OCCLUSIONROOMRATIO; + case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF: return EAXSOURCE_OUTSIDEVOLUMEHF; + case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR: return EAXSOURCE_AIRABSORPTIONFACTOR; + case DSPROPERTY_EAX20BUFFER_FLAGS: return EAXSOURCE_FLAGS; + default: eax_fail("Unknown property id."); + } +} + void ALsource::eax1_set_efx() { const auto primary_fx_slot_index = eax_al_context_->eax_get_primary_fx_slot_index(); @@ -5088,9 +5111,9 @@ void ALsource::eax1_set_efx() mPropsDirty = true; } -void ALsource::eax1_set_reverb_mix(const EaxEaxCall& eax_call) +void ALsource::eax1_set_reverb_mix(const EaxCall& call) { - const auto reverb_mix = eax_call.get_value(); + const auto reverb_mix = call.get_value(); eax1_validate_reverb_mix(reverb_mix); if (eax1_.fMix == reverb_mix) @@ -5101,253 +5124,253 @@ void ALsource::eax1_set_reverb_mix(const EaxEaxCall& eax_call) } void ALsource::eax_defer_source_direct( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto direct = - eax_call.get_value(); + call.get_value(); eax_validate_source_direct(direct); eax_defer_source_direct(direct); } void ALsource::eax_defer_source_direct_hf( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto direct_hf = - eax_call.get_value(); + call.get_value(); eax_validate_source_direct_hf(direct_hf); eax_defer_source_direct_hf(direct_hf); } void ALsource::eax_defer_source_room( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto room = - eax_call.get_value(); + call.get_value(); eax_validate_source_room(room); eax_defer_source_room(room); } void ALsource::eax_defer_source_room_hf( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto room_hf = - eax_call.get_value(); + call.get_value(); eax_validate_source_room_hf(room_hf); eax_defer_source_room_hf(room_hf); } void ALsource::eax_defer_source_obstruction( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto obstruction = - eax_call.get_value(); + call.get_value(); eax_validate_source_obstruction(obstruction); eax_defer_source_obstruction(obstruction); } void ALsource::eax_defer_source_obstruction_lf_ratio( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto obstruction_lf_ratio = - eax_call.get_value(); + call.get_value(); eax_validate_source_obstruction_lf_ratio(obstruction_lf_ratio); eax_defer_source_obstruction_lf_ratio(obstruction_lf_ratio); } void ALsource::eax_defer_source_occlusion( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto occlusion = - eax_call.get_value(); + call.get_value(); eax_validate_source_occlusion(occlusion); eax_defer_source_occlusion(occlusion); } void ALsource::eax_defer_source_occlusion_lf_ratio( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto occlusion_lf_ratio = - eax_call.get_value(); + call.get_value(); eax_validate_source_occlusion_lf_ratio(occlusion_lf_ratio); eax_defer_source_occlusion_lf_ratio(occlusion_lf_ratio); } void ALsource::eax_defer_source_occlusion_room_ratio( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto occlusion_room_ratio = - eax_call.get_value(); + call.get_value(); eax_validate_source_occlusion_room_ratio(occlusion_room_ratio); eax_defer_source_occlusion_room_ratio(occlusion_room_ratio); } void ALsource::eax_defer_source_occlusion_direct_ratio( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto occlusion_direct_ratio = - eax_call.get_value(); + call.get_value(); eax_validate_source_occlusion_direct_ratio(occlusion_direct_ratio); eax_defer_source_occlusion_direct_ratio(occlusion_direct_ratio); } void ALsource::eax_defer_source_exclusion( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto exclusion = - eax_call.get_value(); + call.get_value(); eax_validate_source_exclusion(exclusion); eax_defer_source_exclusion(exclusion); } void ALsource::eax_defer_source_exclusion_lf_ratio( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto exclusion_lf_ratio = - eax_call.get_value(); + call.get_value(); eax_validate_source_exclusion_lf_ratio(exclusion_lf_ratio); eax_defer_source_exclusion_lf_ratio(exclusion_lf_ratio); } void ALsource::eax_defer_source_outside_volume_hf( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto outside_volume_hf = - eax_call.get_value(); + call.get_value(); eax_validate_source_outside_volume_hf(outside_volume_hf); eax_defer_source_outside_volume_hf(outside_volume_hf); } void ALsource::eax_defer_source_doppler_factor( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto doppler_factor = - eax_call.get_value(); + call.get_value(); eax_validate_source_doppler_factor(doppler_factor); eax_defer_source_doppler_factor(doppler_factor); } void ALsource::eax_defer_source_rolloff_factor( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto rolloff_factor = - eax_call.get_value(); + call.get_value(); eax_validate_source_rolloff_factor(rolloff_factor); eax_defer_source_rolloff_factor(rolloff_factor); } void ALsource::eax_defer_source_room_rolloff_factor( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto room_rolloff_factor = - eax_call.get_value(); + call.get_value(); eax_validate_source_room_rolloff_factor(room_rolloff_factor); eax_defer_source_room_rolloff_factor(room_rolloff_factor); } void ALsource::eax_defer_source_air_absorption_factor( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto air_absorption_factor = - eax_call.get_value(); + call.get_value(); eax_validate_source_air_absorption_factor(air_absorption_factor); eax_defer_source_air_absorption_factor(air_absorption_factor); } void ALsource::eax_defer_source_flags( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto flags = - eax_call.get_value(); + call.get_value(); - eax_validate_source_flags(flags, eax_call.get_version()); + eax_validate_source_flags(flags, call.get_version()); eax_defer_source_flags(flags); } void ALsource::eax_defer_source_macro_fx_factor( - const EaxEaxCall& eax_call) + const EaxCall& call) { const auto macro_fx_factor = - eax_call.get_value(); + call.get_value(); eax_validate_source_macro_fx_factor(macro_fx_factor); eax_defer_source_macro_fx_factor(macro_fx_factor); } void ALsource::eax_defer_source_2d_all( - const EaxEaxCall& eax_call) + const EaxCall& call) { - const auto all = eax_call.get_value(); + const auto all = call.get_value(); - eax_validate_source_2d_all(all, eax_call.get_version()); + eax_validate_source_2d_all(all, call.get_version()); eax_defer_source_2d_all(all); } void ALsource::eax_defer_source_obstruction_all( - const EaxEaxCall& eax_call) + const EaxCall& call) { - const auto all = eax_call.get_value(); + const auto all = call.get_value(); eax_validate_source_obstruction_all(all); eax_defer_source_obstruction_all(all); } void ALsource::eax_defer_source_exclusion_all( - const EaxEaxCall& eax_call) + const EaxCall& call) { - const auto all = eax_call.get_value(); + const auto all = call.get_value(); eax_validate_source_exclusion_all(all); eax_defer_source_exclusion_all(all); } void ALsource::eax_defer_source_occlusion_all( - const EaxEaxCall& eax_call) + const EaxCall& call) { - const auto all = eax_call.get_value(); + const auto all = call.get_value(); eax_validate_source_occlusion_all(all); eax_defer_source_occlusion_all(all); } void ALsource::eax_defer_source_all( - const EaxEaxCall& eax_call) + const EaxCall& call) { - const auto eax_version = eax_call.get_version(); + const auto eax_version = call.get_version(); if (eax_version == 2) { - const auto all = eax_call.get_value(); + const auto all = call.get_value(); eax_validate_source_all(all, eax_version); eax_defer_source_all(all); } else if (eax_version < 5) { - const auto all = eax_call.get_value(); + const auto all = call.get_value(); eax_validate_source_all(all, eax_version); eax_defer_source_all(all); } else { - const auto all = eax_call.get_value(); + const auto all = call.get_value(); eax_validate_source_all(all, eax_version); eax_defer_source_all(all); @@ -5355,9 +5378,9 @@ void ALsource::eax_defer_source_all( } void ALsource::eax_defer_source_speaker_level_all( - const EaxEaxCall& eax_call) + const EaxCall& call) { - const auto speaker_level_properties = eax_call.get_value(); + const auto speaker_level_properties = call.get_value(); eax_validate_source_speaker_level_all(speaker_level_properties); eax_defer_source_speaker_level_all(speaker_level_properties); @@ -5433,13 +5456,13 @@ void ALsource::eax_set_speaker_levels() // TODO } -void ALsource::eax1_set(const EaxEaxCall& eax_call) +void ALsource::eax1_set(const EaxCall& call) { - switch (eax_call.get_property_id()) + switch (call.get_property_id()) { case DSPROPERTY_EAXBUFFER_ALL: case DSPROPERTY_EAXBUFFER_REVERBMIX: - eax1_set_reverb_mix(eax_call); + eax1_set_reverb_mix(call); break; default: @@ -5534,137 +5557,141 @@ void ALsource::eax_apply_deferred() } void ALsource::eax_set( - const EaxEaxCall& eax_call) + const EaxCall& call) { - if (eax_call.get_version() == 1) + const auto version = call.get_version(); + + if (version == 1) { - eax1_set(eax_call); + eax1_set(call); return; } - switch (eax_call.get_property_id()) + const auto property_id = (version == 2 ? eax2_translate_property_id(call) : call.get_property_id()); + + switch (property_id) { case EAXSOURCE_NONE: break; case EAXSOURCE_ALLPARAMETERS: - eax_defer_source_all(eax_call); + eax_defer_source_all(call); break; case EAXSOURCE_OBSTRUCTIONPARAMETERS: - eax_defer_source_obstruction_all(eax_call); + eax_defer_source_obstruction_all(call); break; case EAXSOURCE_OCCLUSIONPARAMETERS: - eax_defer_source_occlusion_all(eax_call); + eax_defer_source_occlusion_all(call); break; case EAXSOURCE_EXCLUSIONPARAMETERS: - eax_defer_source_exclusion_all(eax_call); + eax_defer_source_exclusion_all(call); break; case EAXSOURCE_DIRECT: - eax_defer_source_direct(eax_call); + eax_defer_source_direct(call); break; case EAXSOURCE_DIRECTHF: - eax_defer_source_direct_hf(eax_call); + eax_defer_source_direct_hf(call); break; case EAXSOURCE_ROOM: - eax_defer_source_room(eax_call); + eax_defer_source_room(call); break; case EAXSOURCE_ROOMHF: - eax_defer_source_room_hf(eax_call); + eax_defer_source_room_hf(call); break; case EAXSOURCE_OBSTRUCTION: - eax_defer_source_obstruction(eax_call); + eax_defer_source_obstruction(call); break; case EAXSOURCE_OBSTRUCTIONLFRATIO: - eax_defer_source_obstruction_lf_ratio(eax_call); + eax_defer_source_obstruction_lf_ratio(call); break; case EAXSOURCE_OCCLUSION: - eax_defer_source_occlusion(eax_call); + eax_defer_source_occlusion(call); break; case EAXSOURCE_OCCLUSIONLFRATIO: - eax_defer_source_occlusion_lf_ratio(eax_call); + eax_defer_source_occlusion_lf_ratio(call); break; case EAXSOURCE_OCCLUSIONROOMRATIO: - eax_defer_source_occlusion_room_ratio(eax_call); + eax_defer_source_occlusion_room_ratio(call); break; case EAXSOURCE_OCCLUSIONDIRECTRATIO: - eax_defer_source_occlusion_direct_ratio(eax_call); + eax_defer_source_occlusion_direct_ratio(call); break; case EAXSOURCE_EXCLUSION: - eax_defer_source_exclusion(eax_call); + eax_defer_source_exclusion(call); break; case EAXSOURCE_EXCLUSIONLFRATIO: - eax_defer_source_exclusion_lf_ratio(eax_call); + eax_defer_source_exclusion_lf_ratio(call); break; case EAXSOURCE_OUTSIDEVOLUMEHF: - eax_defer_source_outside_volume_hf(eax_call); + eax_defer_source_outside_volume_hf(call); break; case EAXSOURCE_DOPPLERFACTOR: - eax_defer_source_doppler_factor(eax_call); + eax_defer_source_doppler_factor(call); break; case EAXSOURCE_ROLLOFFFACTOR: - eax_defer_source_rolloff_factor(eax_call); + eax_defer_source_rolloff_factor(call); break; case EAXSOURCE_ROOMROLLOFFFACTOR: - eax_defer_source_room_rolloff_factor(eax_call); + eax_defer_source_room_rolloff_factor(call); break; case EAXSOURCE_AIRABSORPTIONFACTOR: - eax_defer_source_air_absorption_factor(eax_call); + eax_defer_source_air_absorption_factor(call); break; case EAXSOURCE_FLAGS: - eax_defer_source_flags(eax_call); + eax_defer_source_flags(call); break; case EAXSOURCE_SENDPARAMETERS: - eax_defer_send(eax_call); + eax_defer_send(call); break; case EAXSOURCE_ALLSENDPARAMETERS: - eax_defer_send_all(eax_call); + eax_defer_send_all(call); break; case EAXSOURCE_OCCLUSIONSENDPARAMETERS: - eax_defer_send_occlusion_all(eax_call); + eax_defer_send_occlusion_all(call); break; case EAXSOURCE_EXCLUSIONSENDPARAMETERS: - eax_defer_send_exclusion_all(eax_call); + eax_defer_send_exclusion_all(call); break; case EAXSOURCE_ACTIVEFXSLOTID: - eax_defer_active_fx_slots(eax_call); + eax_defer_active_fx_slots(call); break; case EAXSOURCE_MACROFXFACTOR: - eax_defer_source_macro_fx_factor(eax_call); + eax_defer_source_macro_fx_factor(call); break; case EAXSOURCE_SPEAKERLEVELS: - eax_defer_source_speaker_level_all(eax_call); + eax_defer_source_speaker_level_all(call); break; case EAXSOURCE_ALL2DPARAMETERS: - eax_defer_source_2d_all(eax_call); + eax_defer_source_2d_all(call); break; default: @@ -5754,13 +5781,13 @@ void ALsource::eax_copy_send( dst_send.flExclusionLFRatio = src_send.flExclusionLFRatio; } -void ALsource::eax1_get(const EaxEaxCall& eax_call) +void ALsource::eax1_get(const EaxCall& call) { - switch (eax_call.get_property_id()) + switch (call.get_property_id()) { case DSPROPERTY_EAXBUFFER_ALL: case DSPROPERTY_EAXBUFFER_REVERBMIX: - eax_call.set_value(eax1_); + call.set_value(eax1_); break; default: @@ -5769,7 +5796,7 @@ void ALsource::eax1_get(const EaxEaxCall& eax_call) } void ALsource::eax_api_get_source_all_v2( - const EaxEaxCall& eax_call) + const EaxCall& call) { auto eax_2_all = EAX20BUFFERPROPERTIES{}; eax_2_all.lDirect = eax_.source.lDirect; @@ -5786,37 +5813,37 @@ void ALsource::eax_api_get_source_all_v2( eax_2_all.flAirAbsorptionFactor = eax_.source.flAirAbsorptionFactor; eax_2_all.dwFlags = eax_.source.ulFlags; - eax_call.set_value(eax_2_all); + call.set_value(eax_2_all); } void ALsource::eax_api_get_source_all_v3( - const EaxEaxCall& eax_call) + const EaxCall& call) { - eax_call.set_value(static_cast(eax_.source)); + call.set_value(static_cast(eax_.source)); } void ALsource::eax_api_get_source_all_v5( - const EaxEaxCall& eax_call) + const EaxCall& call) { - eax_call.set_value(eax_.source); + call.set_value(eax_.source); } void ALsource::eax_api_get_source_all( - const EaxEaxCall& eax_call) + const EaxCall& call) { - switch (eax_call.get_version()) + switch (call.get_version()) { case 2: - eax_api_get_source_all_v2(eax_call); + eax_api_get_source_all_v2(call); break; case 3: case 4: - eax_api_get_source_all_v3(eax_call); + eax_api_get_source_all_v3(call); break; case 5: - eax_api_get_source_all_v5(eax_call); + eax_api_get_source_all_v5(call); break; default: @@ -5825,17 +5852,17 @@ void ALsource::eax_api_get_source_all( } void ALsource::eax_api_get_source_all_obstruction( - const EaxEaxCall& eax_call) + const EaxCall& call) { auto eax_obstruction_all = EAXOBSTRUCTIONPROPERTIES{}; eax_obstruction_all.lObstruction = eax_.source.lObstruction; eax_obstruction_all.flObstructionLFRatio = eax_.source.flObstructionLFRatio; - eax_call.set_value(eax_obstruction_all); + call.set_value(eax_obstruction_all); } void ALsource::eax_api_get_source_all_occlusion( - const EaxEaxCall& eax_call) + const EaxCall& call) { auto eax_occlusion_all = EAXOCCLUSIONPROPERTIES{}; eax_occlusion_all.lOcclusion = eax_.source.lOcclusion; @@ -5843,35 +5870,35 @@ void ALsource::eax_api_get_source_all_occlusion( eax_occlusion_all.flOcclusionRoomRatio = eax_.source.flOcclusionRoomRatio; eax_occlusion_all.flOcclusionDirectRatio = eax_.source.flOcclusionDirectRatio; - eax_call.set_value(eax_occlusion_all); + call.set_value(eax_occlusion_all); } void ALsource::eax_api_get_source_all_exclusion( - const EaxEaxCall& eax_call) + const EaxCall& call) { auto eax_exclusion_all = EAXEXCLUSIONPROPERTIES{}; eax_exclusion_all.lExclusion = eax_.source.lExclusion; eax_exclusion_all.flExclusionLFRatio = eax_.source.flExclusionLFRatio; - eax_call.set_value(eax_exclusion_all); + call.set_value(eax_exclusion_all); } void ALsource::eax_api_get_source_active_fx_slot_id( - const EaxEaxCall& eax_call) + const EaxCall& call) { - switch (eax_call.get_version()) + switch (call.get_version()) { case 4: { const auto& active_fx_slots = reinterpret_cast(eax_.active_fx_slots); - eax_call.set_value(active_fx_slots); + call.set_value(active_fx_slots); } break; case 5: { const auto& active_fx_slots = reinterpret_cast(eax_.active_fx_slots); - eax_call.set_value(active_fx_slots); + call.set_value(active_fx_slots); } break; @@ -5881,7 +5908,7 @@ void ALsource::eax_api_get_source_active_fx_slot_id( } void ALsource::eax_api_get_source_all_2d( - const EaxEaxCall& eax_call) + const EaxCall& call) { auto eax_2d_all = EAXSOURCE2DPROPERTIES{}; eax_2d_all.lDirect = eax_.source.lDirect; @@ -5890,13 +5917,13 @@ void ALsource::eax_api_get_source_all_2d( eax_2d_all.lRoomHF = eax_.source.lRoomHF; eax_2d_all.ulFlags = eax_.source.ulFlags; - eax_call.set_value(eax_2d_all); + call.set_value(eax_2d_all); } void ALsource::eax_api_get_source_speaker_level_all( - const EaxEaxCall& eax_call) + const EaxCall& call) { - auto& all = eax_call.get_value(); + auto& all = call.get_value(); eax_validate_source_speaker_id(all.lSpeakerID); const auto speaker_index = static_cast(all.lSpeakerID - 1); @@ -5904,137 +5931,141 @@ void ALsource::eax_api_get_source_speaker_level_all( } void ALsource::eax_get( - const EaxEaxCall& eax_call) + const EaxCall& call) { - if (eax_call.get_version() == 1) + const auto version = call.get_version(); + + if (version == 1) { - eax1_get(eax_call); + eax1_get(call); return; } - switch (eax_call.get_property_id()) + const auto property_id = (version == 2 ? eax2_translate_property_id(call) : call.get_property_id()); + + switch (property_id) { case EAXSOURCE_NONE: break; case EAXSOURCE_ALLPARAMETERS: - eax_api_get_source_all(eax_call); + eax_api_get_source_all(call); break; case EAXSOURCE_OBSTRUCTIONPARAMETERS: - eax_api_get_source_all_obstruction(eax_call); + eax_api_get_source_all_obstruction(call); break; case EAXSOURCE_OCCLUSIONPARAMETERS: - eax_api_get_source_all_occlusion(eax_call); + eax_api_get_source_all_occlusion(call); break; case EAXSOURCE_EXCLUSIONPARAMETERS: - eax_api_get_source_all_exclusion(eax_call); + eax_api_get_source_all_exclusion(call); break; case EAXSOURCE_DIRECT: - eax_call.set_value(eax_.source.lDirect); + call.set_value(eax_.source.lDirect); break; case EAXSOURCE_DIRECTHF: - eax_call.set_value(eax_.source.lDirectHF); + call.set_value(eax_.source.lDirectHF); break; case EAXSOURCE_ROOM: - eax_call.set_value(eax_.source.lRoom); + call.set_value(eax_.source.lRoom); break; case EAXSOURCE_ROOMHF: - eax_call.set_value(eax_.source.lRoomHF); + call.set_value(eax_.source.lRoomHF); break; case EAXSOURCE_OBSTRUCTION: - eax_call.set_value(eax_.source.lObstruction); + call.set_value(eax_.source.lObstruction); break; case EAXSOURCE_OBSTRUCTIONLFRATIO: - eax_call.set_value(eax_.source.flObstructionLFRatio); + call.set_value(eax_.source.flObstructionLFRatio); break; case EAXSOURCE_OCCLUSION: - eax_call.set_value(eax_.source.lOcclusion); + call.set_value(eax_.source.lOcclusion); break; case EAXSOURCE_OCCLUSIONLFRATIO: - eax_call.set_value(eax_.source.flOcclusionLFRatio); + call.set_value(eax_.source.flOcclusionLFRatio); break; case EAXSOURCE_OCCLUSIONROOMRATIO: - eax_call.set_value(eax_.source.flOcclusionRoomRatio); + call.set_value(eax_.source.flOcclusionRoomRatio); break; case EAXSOURCE_OCCLUSIONDIRECTRATIO: - eax_call.set_value(eax_.source.flOcclusionDirectRatio); + call.set_value(eax_.source.flOcclusionDirectRatio); break; case EAXSOURCE_EXCLUSION: - eax_call.set_value(eax_.source.lExclusion); + call.set_value(eax_.source.lExclusion); break; case EAXSOURCE_EXCLUSIONLFRATIO: - eax_call.set_value(eax_.source.flExclusionLFRatio); + call.set_value(eax_.source.flExclusionLFRatio); break; case EAXSOURCE_OUTSIDEVOLUMEHF: - eax_call.set_value(eax_.source.lOutsideVolumeHF); + call.set_value(eax_.source.lOutsideVolumeHF); break; case EAXSOURCE_DOPPLERFACTOR: - eax_call.set_value(eax_.source.flDopplerFactor); + call.set_value(eax_.source.flDopplerFactor); break; case EAXSOURCE_ROLLOFFFACTOR: - eax_call.set_value(eax_.source.flRolloffFactor); + call.set_value(eax_.source.flRolloffFactor); break; case EAXSOURCE_ROOMROLLOFFFACTOR: - eax_call.set_value(eax_.source.flRoomRolloffFactor); + call.set_value(eax_.source.flRoomRolloffFactor); break; case EAXSOURCE_AIRABSORPTIONFACTOR: - eax_call.set_value(eax_.source.flAirAbsorptionFactor); + call.set_value(eax_.source.flAirAbsorptionFactor); break; case EAXSOURCE_FLAGS: - eax_call.set_value(eax_.source.ulFlags); + call.set_value(eax_.source.ulFlags); break; case EAXSOURCE_SENDPARAMETERS: - eax_api_get_send_properties(eax_call); + eax_api_get_send_properties(call); break; case EAXSOURCE_ALLSENDPARAMETERS: - eax_api_get_send_properties(eax_call); + eax_api_get_send_properties(call); break; case EAXSOURCE_OCCLUSIONSENDPARAMETERS: - eax_api_get_send_properties(eax_call); + eax_api_get_send_properties(call); break; case EAXSOURCE_EXCLUSIONSENDPARAMETERS: - eax_api_get_send_properties(eax_call); + eax_api_get_send_properties(call); break; case EAXSOURCE_ACTIVEFXSLOTID: - eax_api_get_source_active_fx_slot_id(eax_call); + eax_api_get_source_active_fx_slot_id(call); break; case EAXSOURCE_MACROFXFACTOR: - eax_call.set_value(eax_.source.flMacroFXFactor); + call.set_value(eax_.source.flMacroFXFactor); break; case EAXSOURCE_SPEAKERLEVELS: - eax_api_get_source_speaker_level_all(eax_call); + eax_api_get_source_speaker_level_all(call); break; case EAXSOURCE_ALL2DPARAMETERS: - eax_api_get_source_all_2d(eax_call); + eax_api_get_source_all_2d(call); break; default: diff --git a/al/source.h b/al/source.h index 6fc1c1d4..2d93e177 100644 --- a/al/source.h +++ b/al/source.h @@ -22,7 +22,7 @@ #include "vector.h" #ifdef ALSOFT_EAX -#include "eax/eax_call.h" +#include "eax/call.h" #include "eax/fx_slot_index.h" #include "eax/utils.h" #endif // ALSOFT_EAX @@ -216,8 +216,8 @@ public: void eax_initialize(ALCcontext *context) noexcept; - void eax_dispatch(const EaxEaxCall& eax_call) - { eax_call.is_get() ? eax_get(eax_call) : eax_set(eax_call); } + void eax_dispatch(const EaxCall& call) + { call.is_get() ? eax_get(call) : eax_set(call); } void eax_update_filters(); @@ -308,7 +308,7 @@ private: void eax_defer_active_fx_slots( - const EaxEaxCall& eax_call); + const EaxCall& call); static const char* eax_get_exclusion_name() noexcept; @@ -421,16 +421,16 @@ private: void eax_defer_send( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_send_exclusion_all( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_send_occlusion_all( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_send_all( - const EaxEaxCall& eax_call); + const EaxCall& call); static void eax_validate_source_direct( @@ -609,79 +609,79 @@ private: void eax_defer_source_direct( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_direct_hf( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_room( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_room_hf( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_obstruction( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_obstruction_lf_ratio( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_occlusion( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_occlusion_lf_ratio( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_occlusion_room_ratio( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_occlusion_direct_ratio( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_exclusion( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_exclusion_lf_ratio( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_outside_volume_hf( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_doppler_factor( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_rolloff_factor( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_room_rolloff_factor( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_air_absorption_factor( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_flags( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_macro_fx_factor( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_2d_all( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_obstruction_all( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_exclusion_all( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_occlusion_all( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_all( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_defer_source_speaker_level_all( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_set_outside_volume_hf(); @@ -708,15 +708,16 @@ private: void eax_set_speaker_levels(); + static ALuint eax2_translate_property_id(const EaxCall& call); void eax1_set_efx(); - void eax1_set_reverb_mix(const EaxEaxCall& eax_call); - void eax1_set(const EaxEaxCall& eax_call); + void eax1_set_reverb_mix(const EaxCall& call); + void eax1_set(const EaxCall& call); void eax_apply_deferred(); void eax_set( - const EaxEaxCall& eax_call); + const EaxCall& call); static const GUID& eax_get_send_fx_slot_guid( @@ -744,10 +745,10 @@ private: typename TSrcSend > void eax_api_get_send_properties( - const EaxEaxCall& eax_call) const + const EaxCall& call) const { - const auto eax_version = eax_call.get_version(); - const auto dst_sends = eax_call.get_values(); + const auto eax_version = call.get_version(); + const auto dst_sends = call.get_values(); const auto send_count = dst_sends.size(); for (auto fx_slot_index = EaxFxSlotIndexValue{}; fx_slot_index < send_count; ++fx_slot_index) @@ -762,40 +763,40 @@ private: } - void eax1_get(const EaxEaxCall& eax_call); + void eax1_get(const EaxCall& call); void eax_api_get_source_all_v2( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_api_get_source_all_v3( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_api_get_source_all_v5( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_api_get_source_all( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_api_get_source_all_obstruction( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_api_get_source_all_occlusion( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_api_get_source_all_exclusion( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_api_get_source_active_fx_slot_id( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_api_get_source_all_2d( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_api_get_source_speaker_level_all( - const EaxEaxCall& eax_call); + const EaxCall& call); void eax_get( - const EaxEaxCall& eax_call); + const EaxCall& call); // `alSource3i(source, AL_AUXILIARY_SEND_FILTER, ...)` diff --git a/alc/context.cpp b/alc/context.cpp index 5fe03e78..33c888e7 100644 --- a/alc/context.cpp +++ b/alc/context.cpp @@ -330,32 +330,29 @@ ALenum ALCcontext::eax_eax_set( ALvoid* property_value, ALuint property_value_size) { - eax_initialize(); - - const auto eax_call = create_eax_call( - false, + const auto call = create_eax_call( + EaxCallType::set, property_set_id, property_id, property_source_id, property_value, - property_value_size - ); - - eax_unlock_legacy_fx_slots(eax_call); + property_value_size); + eax_initialize(call); + eax_unlock_legacy_fx_slots(call); - switch (eax_call.get_property_set_id()) + switch (call.get_property_set_id()) { - case EaxEaxCallPropertySetId::context: - eax_set(eax_call); + case EaxCallPropertySetId::context: + eax_set(call); break; - case EaxEaxCallPropertySetId::fx_slot: - case EaxEaxCallPropertySetId::fx_slot_effect: - eax_dispatch_fx_slot(eax_call); + case EaxCallPropertySetId::fx_slot: + case EaxCallPropertySetId::fx_slot_effect: + eax_dispatch_fx_slot(call); break; - case EaxEaxCallPropertySetId::source: - eax_dispatch_source(eax_call); + case EaxCallPropertySetId::source: + eax_dispatch_source(call); break; default: @@ -376,32 +373,29 @@ ALenum ALCcontext::eax_eax_get( ALvoid* property_value, ALuint property_value_size) { - eax_initialize(); - - const auto eax_call = create_eax_call( - true, + const auto call = create_eax_call( + EaxCallType::get, property_set_id, property_id, property_source_id, property_value, - property_value_size - ); + property_value_size); + eax_initialize(call); + eax_unlock_legacy_fx_slots(call); - eax_unlock_legacy_fx_slots(eax_call); - - switch (eax_call.get_property_set_id()) + switch (call.get_property_set_id()) { - case EaxEaxCallPropertySetId::context: - eax_get(eax_call); + case EaxCallPropertySetId::context: + eax_get(call); break; - case EaxEaxCallPropertySetId::fx_slot: - case EaxEaxCallPropertySetId::fx_slot_effect: - eax_dispatch_fx_slot(eax_call); + case EaxCallPropertySetId::fx_slot: + case EaxCallPropertySetId::fx_slot_effect: + eax_dispatch_fx_slot(call); break; - case EaxEaxCallPropertySetId::source: - eax_dispatch_source(eax_call); + case EaxCallPropertySetId::source: + eax_dispatch_source(call); break; default: @@ -478,7 +472,7 @@ void ALCcontext::eax_initialize_extensions() mExtensionList = eax_extension_list_.c_str(); } -void ALCcontext::eax_initialize() +void ALCcontext::eax_initialize(const EaxCall& call) { if (eax_is_initialized_) { @@ -501,7 +495,7 @@ void ALCcontext::eax_initialize() eax_set_defaults(); eax_set_air_absorbtion_hf(); eax_update_speaker_configuration(); - eax_initialize_fx_slots(); + eax_initialize_fx_slots(call); eax_initialize_sources(); eax_is_initialized_ = true; @@ -607,34 +601,32 @@ void ALCcontext::eax_set_defaults() noexcept eax_d_ = eax_; } -void ALCcontext::eax_unlock_legacy_fx_slots(const EaxEaxCall& eax_call) noexcept +void ALCcontext::eax_unlock_legacy_fx_slots(const EaxCall& call) noexcept { - if (eax_call.get_version() != 5 || eax_are_legacy_fx_slots_unlocked_) + if (call.get_version() != 5 || eax_are_legacy_fx_slots_unlocked_) return; eax_are_legacy_fx_slots_unlocked_ = true; eax_fx_slots_.unlock_legacy(); } -void ALCcontext::eax_dispatch_fx_slot( - const EaxEaxCall& eax_call) +void ALCcontext::eax_dispatch_fx_slot(const EaxCall& call) { - const auto fx_slot_index = eax_call.get_fx_slot_index(); + const auto fx_slot_index = call.get_fx_slot_index(); if(!fx_slot_index.has_value()) eax_fail("Invalid fx slot index."); auto& fx_slot = eax_get_fx_slot(*fx_slot_index); - if(fx_slot.eax_dispatch(eax_call)) + if(fx_slot.eax_dispatch(call)) { std::lock_guard source_lock{mSourceLock}; eax_update_filters(); } } -void ALCcontext::eax_dispatch_source( - const EaxEaxCall& eax_call) +void ALCcontext::eax_dispatch_source(const EaxCall& call) { - const auto source_id = eax_call.get_property_al_name(); + const auto source_id = call.get_property_al_name(); std::lock_guard source_lock{mSourceLock}; @@ -645,70 +637,61 @@ void ALCcontext::eax_dispatch_source( eax_fail("Source not found."); } - source->eax_dispatch(eax_call); + source->eax_dispatch(call); } -void ALCcontext::eax_get_primary_fx_slot_id( - const EaxEaxCall& eax_call) +void ALCcontext::eax_get_primary_fx_slot_id(const EaxCall& call) { - eax_call.set_value(eax_.context.guidPrimaryFXSlotID); + call.set_value(eax_.context.guidPrimaryFXSlotID); } -void ALCcontext::eax_get_distance_factor( - const EaxEaxCall& eax_call) +void ALCcontext::eax_get_distance_factor(const EaxCall& call) { - eax_call.set_value(eax_.context.flDistanceFactor); + call.set_value(eax_.context.flDistanceFactor); } -void ALCcontext::eax_get_air_absorption_hf( - const EaxEaxCall& eax_call) +void ALCcontext::eax_get_air_absorption_hf(const EaxCall& call) { - eax_call.set_value(eax_.context.flAirAbsorptionHF); + call.set_value(eax_.context.flAirAbsorptionHF); } -void ALCcontext::eax_get_hf_reference( - const EaxEaxCall& eax_call) +void ALCcontext::eax_get_hf_reference(const EaxCall& call) { - eax_call.set_value(eax_.context.flHFReference); + call.set_value(eax_.context.flHFReference); } -void ALCcontext::eax_get_last_error( - const EaxEaxCall& eax_call) +void ALCcontext::eax_get_last_error(const EaxCall& call) { const auto eax_last_error = eax_last_error_; eax_last_error_ = EAX_OK; - eax_call.set_value(eax_last_error); + call.set_value(eax_last_error); } -void ALCcontext::eax_get_speaker_config( - const EaxEaxCall& eax_call) +void ALCcontext::eax_get_speaker_config(const EaxCall& call) { - eax_call.set_value(eax_speaker_config_); + call.set_value(eax_speaker_config_); } -void ALCcontext::eax_get_session( - const EaxEaxCall& eax_call) +void ALCcontext::eax_get_session(const EaxCall& call) { - eax_call.set_value(eax_session_); + call.set_value(eax_session_); } -void ALCcontext::eax_get_macro_fx_factor( - const EaxEaxCall& eax_call) +void ALCcontext::eax_get_macro_fx_factor(const EaxCall& call) { - eax_call.set_value(eax_.context.flMacroFXFactor); + call.set_value(eax_.context.flMacroFXFactor); } -void ALCcontext::eax_get_context_all( - const EaxEaxCall& eax_call) +void ALCcontext::eax_get_context_all(const EaxCall& call) { - switch (eax_call.get_version()) + switch (call.get_version()) { case 4: - eax_call.set_value(static_cast(eax_.context)); + call.set_value(static_cast(eax_.context)); break; case 5: - eax_call.set_value(static_cast(eax_.context)); + call.set_value(static_cast(eax_.context)); break; default: @@ -716,48 +699,47 @@ void ALCcontext::eax_get_context_all( } } -void ALCcontext::eax_get( - const EaxEaxCall& eax_call) +void ALCcontext::eax_get(const EaxCall& call) { - switch (eax_call.get_property_id()) + switch (call.get_property_id()) { case EAXCONTEXT_NONE: break; case EAXCONTEXT_ALLPARAMETERS: - eax_get_context_all(eax_call); + eax_get_context_all(call); break; case EAXCONTEXT_PRIMARYFXSLOTID: - eax_get_primary_fx_slot_id(eax_call); + eax_get_primary_fx_slot_id(call); break; case EAXCONTEXT_DISTANCEFACTOR: - eax_get_distance_factor(eax_call); + eax_get_distance_factor(call); break; case EAXCONTEXT_AIRABSORPTIONHF: - eax_get_air_absorption_hf(eax_call); + eax_get_air_absorption_hf(call); break; case EAXCONTEXT_HFREFERENCE: - eax_get_hf_reference(eax_call); + eax_get_hf_reference(call); break; case EAXCONTEXT_LASTERROR: - eax_get_last_error(eax_call); + eax_get_last_error(call); break; case EAXCONTEXT_SPEAKERCONFIG: - eax_get_speaker_config(eax_call); + eax_get_speaker_config(call); break; case EAXCONTEXT_EAXSESSION: - eax_get_session(eax_call); + eax_get_session(call); break; case EAXCONTEXT_MACROFXFACTOR: - eax_get_macro_fx_factor(eax_call); + eax_get_macro_fx_factor(call); break; default: @@ -801,9 +783,9 @@ void ALCcontext::eax_set_context() eax_set_hf_reference(); } -void ALCcontext::eax_initialize_fx_slots() +void ALCcontext::eax_initialize_fx_slots(const EaxCall& call) { - eax_fx_slots_.initialize(*this); + eax_fx_slots_.initialize(call, *this); eax_previous_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID; eax_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID; } @@ -1007,15 +989,14 @@ void ALCcontext::eax_defer_context_all( eax_defer_macro_fx_factor(context_all.flMacroFXFactor); } -void ALCcontext::eax_defer_context_all( - const EaxEaxCall& eax_call) +void ALCcontext::eax_defer_context_all(const EaxCall& call) { - switch(eax_call.get_version()) + switch(call.get_version()) { case 4: { const auto& context_all = - eax_call.get_value(); + call.get_value(); eax_validate_context_all(context_all); eax_defer_context_all(context_all); @@ -1025,7 +1006,7 @@ void ALCcontext::eax_defer_context_all( case 5: { const auto& context_all = - eax_call.get_value(); + call.get_value(); eax_validate_context_all(context_all); eax_defer_context_all(context_all); @@ -1037,93 +1018,86 @@ void ALCcontext::eax_defer_context_all( } } -void ALCcontext::eax_defer_primary_fx_slot_id( - const EaxEaxCall& eax_call) +void ALCcontext::eax_defer_primary_fx_slot_id(const EaxCall& call) { const auto& primary_fx_slot_id = - eax_call.get_value(); + call.get_value(); eax_validate_primary_fx_slot_id(primary_fx_slot_id); eax_defer_primary_fx_slot_id(primary_fx_slot_id); } -void ALCcontext::eax_defer_distance_factor( - const EaxEaxCall& eax_call) +void ALCcontext::eax_defer_distance_factor(const EaxCall& call) { const auto& distance_factor = - eax_call.get_value(); + call.get_value(); eax_validate_distance_factor(distance_factor); eax_defer_distance_factor(distance_factor); } -void ALCcontext::eax_defer_air_absorption_hf( - const EaxEaxCall& eax_call) +void ALCcontext::eax_defer_air_absorption_hf(const EaxCall& call) { const auto& air_absorption_hf = - eax_call.get_value(); + call.get_value(); eax_validate_air_absorption_hf(air_absorption_hf); eax_defer_air_absorption_hf(air_absorption_hf); } -void ALCcontext::eax_defer_hf_reference( - const EaxEaxCall& eax_call) +void ALCcontext::eax_defer_hf_reference(const EaxCall& call) { const auto& hf_reference = - eax_call.get_value(); + call.get_value(); eax_validate_hf_reference(hf_reference); eax_defer_hf_reference(hf_reference); } -void ALCcontext::eax_set_session( - const EaxEaxCall& eax_call) +void ALCcontext::eax_set_session(const EaxCall& call) { const auto& eax_session = - eax_call.get_value(); + call.get_value(); eax_validate_session(eax_session); eax_session_ = eax_session; } -void ALCcontext::eax_defer_macro_fx_factor( - const EaxEaxCall& eax_call) +void ALCcontext::eax_defer_macro_fx_factor(const EaxCall& call) { const auto& macro_fx_factor = - eax_call.get_value(); + call.get_value(); eax_validate_macro_fx_factor(macro_fx_factor); eax_defer_macro_fx_factor(macro_fx_factor); } -void ALCcontext::eax_set( - const EaxEaxCall& eax_call) +void ALCcontext::eax_set(const EaxCall& call) { - switch (eax_call.get_property_id()) + switch (call.get_property_id()) { case EAXCONTEXT_NONE: break; case EAXCONTEXT_ALLPARAMETERS: - eax_defer_context_all(eax_call); + eax_defer_context_all(call); break; case EAXCONTEXT_PRIMARYFXSLOTID: - eax_defer_primary_fx_slot_id(eax_call); + eax_defer_primary_fx_slot_id(call); break; case EAXCONTEXT_DISTANCEFACTOR: - eax_defer_distance_factor(eax_call); + eax_defer_distance_factor(call); break; case EAXCONTEXT_AIRABSORPTIONHF: - eax_defer_air_absorption_hf(eax_call); + eax_defer_air_absorption_hf(call); break; case EAXCONTEXT_HFREFERENCE: - eax_defer_hf_reference(eax_call); + eax_defer_hf_reference(call); break; case EAXCONTEXT_LASTERROR: @@ -1133,11 +1107,11 @@ void ALCcontext::eax_set( eax_fail("Speaker configuration is read-only."); case EAXCONTEXT_EAXSESSION: - eax_set_session(eax_call); + eax_set_session(call); break; case EAXCONTEXT_MACROFXFACTOR: - eax_defer_macro_fx_factor(eax_call); + eax_defer_macro_fx_factor(call); break; default: diff --git a/alc/context.h b/alc/context.h index b3f1ea09..a34e3e4b 100644 --- a/alc/context.h +++ b/alc/context.h @@ -20,7 +20,7 @@ #include "vector.h" #ifdef ALSOFT_EAX -#include "al/eax/eax_call.h" +#include "al/eax/call.h" #include "al/eax/fx_slot_index.h" #include "al/eax/fx_slots.h" #include "al/eax/utils.h" @@ -297,7 +297,7 @@ private: void eax_initialize_extensions(); - void eax_initialize(); + void eax_initialize(const EaxCall& call); bool eax_has_no_default_effect_slot() const noexcept; @@ -326,45 +326,33 @@ private: void eax_initialize_sources(); - void eax_unlock_legacy_fx_slots(const EaxEaxCall& eax_call) noexcept; + void eax_unlock_legacy_fx_slots(const EaxCall& call) noexcept; - void eax_dispatch_fx_slot( - const EaxEaxCall& eax_call); + void eax_dispatch_fx_slot(const EaxCall& call); - void eax_dispatch_source( - const EaxEaxCall& eax_call); + void eax_dispatch_source(const EaxCall& call); - void eax_get_primary_fx_slot_id( - const EaxEaxCall& eax_call); + void eax_get_primary_fx_slot_id(const EaxCall& call); - void eax_get_distance_factor( - const EaxEaxCall& eax_call); + void eax_get_distance_factor(const EaxCall& call); - void eax_get_air_absorption_hf( - const EaxEaxCall& eax_call); + void eax_get_air_absorption_hf(const EaxCall& call); - void eax_get_hf_reference( - const EaxEaxCall& eax_call); + void eax_get_hf_reference(const EaxCall& call); - void eax_get_last_error( - const EaxEaxCall& eax_call); + void eax_get_last_error(const EaxCall& call); - void eax_get_speaker_config( - const EaxEaxCall& eax_call); + void eax_get_speaker_config(const EaxCall& call); - void eax_get_session( - const EaxEaxCall& eax_call); + void eax_get_session(const EaxCall& call); - void eax_get_macro_fx_factor( - const EaxEaxCall& eax_call); + void eax_get_macro_fx_factor(const EaxCall& call); - void eax_get_context_all( - const EaxEaxCall& eax_call); + void eax_get_context_all(const EaxCall& call); - void eax_get( - const EaxEaxCall& eax_call); + void eax_get(const EaxCall& call); void eax_set_primary_fx_slot_id(); @@ -379,7 +367,7 @@ private: void eax_set_context(); - void eax_initialize_fx_slots(); + void eax_initialize_fx_slots(const EaxCall& call); void eax_update_sources(); @@ -441,29 +429,21 @@ private: const EAX50CONTEXTPROPERTIES& context_all); - void eax_defer_context_all( - const EaxEaxCall& eax_call); + void eax_defer_context_all(const EaxCall& call); - void eax_defer_primary_fx_slot_id( - const EaxEaxCall& eax_call); + void eax_defer_primary_fx_slot_id(const EaxCall& call); - void eax_defer_distance_factor( - const EaxEaxCall& eax_call); + void eax_defer_distance_factor(const EaxCall& call); - void eax_defer_air_absorption_hf( - const EaxEaxCall& eax_call); + void eax_defer_air_absorption_hf(const EaxCall& call); - void eax_defer_hf_reference( - const EaxEaxCall& eax_call); + void eax_defer_hf_reference(const EaxCall& call); - void eax_set_session( - const EaxEaxCall& eax_call); + void eax_set_session(const EaxCall& call); - void eax_defer_macro_fx_factor( - const EaxEaxCall& eax_call); + void eax_defer_macro_fx_factor(const EaxCall& call); - void eax_set( - const EaxEaxCall& eax_call); + void eax_set(const EaxCall& call); void eax_apply_deferred(); #endif // ALSOFT_EAX -- cgit v1.2.3 From f3092e286e8dc8ca7a2c6f2b89cb413f806944d2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 28 May 2022 12:20:30 -0700 Subject: Search for Oboe config modules first --- CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a3ce192..4442fdb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1143,8 +1143,11 @@ if(ANDROID) set(OBOE_TARGET oboe) else() - find_package(Oboe) - if(OBOE_FOUND) + find_package(oboe CONFIG) + if(NOT TARGET oboe::oboe) + find_package(Oboe) + endif() + if(TARGET oboe::oboe) set(OBOE_TARGET "oboe::oboe") endif() endif() -- cgit v1.2.3 From 00dced1c968a249eeef2b112fdb40febf2bc2c7a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 15 Jun 2022 02:29:38 -0700 Subject: Set policies to exclude /W3 and /GR on MSVC We set /W4, and /GR is the default. --- CMakeLists.txt | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 4442fdb5..c3363c54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,8 +30,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS") endif() endif() -project(OpenAL) - if(COMMAND CMAKE_POLICY) cmake_policy(SET CMP0003 NEW) cmake_policy(SET CMP0005 NEW) @@ -47,8 +45,16 @@ if(COMMAND CMAKE_POLICY) if(POLICY CMP0075) cmake_policy(SET CMP0075 NEW) endif(POLICY CMP0075) + if(POLICY CMP0092) + cmake_policy(SET CMP0092 NEW) + endif(POLICY CMP0092) + if(POLICY CMP0117) + cmake_policy(SET CMP0117 NEW) + endif(POLICY CMP0117) endif(COMMAND CMAKE_POLICY) +project(OpenAL) + if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." @@ -225,13 +231,6 @@ if(MSVC) set(C_FLAGS ${C_FLAGS} $<$:/permissive->) endif() set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4127 /wd4268 /wd4324 /wd5030) - # Remove /W3, which is added by default, since we set /W4. Some build - # generators with MSVC complain about both /W3 and /W4 being specified. - foreach(flag_var CMAKE_C_FLAGS CMAKE_CXX_FLAGS) - if(${flag_var} MATCHES "/W3") - string(REGEX REPLACE "/W3" "" ${flag_var} "${${flag_var}}") - endif() - endforeach() if(NOT DXSDK_DIR) string(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}") -- cgit v1.2.3 From 05f5faf2655f4a51c69bfaacd4f67a740429f0dc Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 20 Jun 2022 18:16:18 -0700 Subject: Release 1.22.1 --- CMakeLists.txt | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index c3363c54..bc87d81c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -162,7 +162,7 @@ endif() set(LIB_MAJOR_VERSION "1") set(LIB_MINOR_VERSION "22") -set(LIB_REVISION "0") +set(LIB_REVISION "1") set(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}") set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0) diff --git a/appveyor.yml b/appveyor.yml index c468d4e9..70bb08de 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 1.22.0.{build} +version: 1.22.1.{build} environment: APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 -- cgit v1.2.3 From dc83d99c95a42c960150ddeee06c124134b52208 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 27 Jun 2022 01:33:53 -0700 Subject: Release 1.22.2 --- CMakeLists.txt | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index bc87d81c..1984ac97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -162,7 +162,7 @@ endif() set(LIB_MAJOR_VERSION "1") set(LIB_MINOR_VERSION "22") -set(LIB_REVISION "1") +set(LIB_REVISION "2") set(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}") set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0) diff --git a/appveyor.yml b/appveyor.yml index 70bb08de..b75fbd79 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 1.22.1.{build} +version: 1.22.2.{build} environment: APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 -- cgit v1.2.3 From fa51c89549590319cb545a8c81419e2e1ddc5db3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 8 Jul 2022 17:28:48 -0700 Subject: Fix building on some MinGW systems Some systems don't like having a space in the export declaration, with windres interpreting the extra attributes as input files. GCC seems to accept the attributes being "packed" together, which avoids the problem with windres. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 1984ac97..0d1690a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -472,7 +472,7 @@ if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2) # assumes the stack is suitably aligned. Older Linux code or other # OSs don't guarantee this on 32-bit, so externally-callable # functions need to ensure an aligned stack. - set(EXPORT_DECL "${EXPORT_DECL} __attribute__((force_align_arg_pointer))") + set(EXPORT_DECL "${EXPORT_DECL}__attribute__((force_align_arg_pointer))") endif() endif() endif() -- cgit v1.2.3 From 6815926cc52e66bb2d27c1f0cfddb385c9e279e4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 20 Aug 2022 20:59:15 -0700 Subject: Update alffplay to support newer ffmpeg versions --- CMakeLists.txt | 20 ++-- cmake/FindFFmpeg.cmake | 18 +--- examples/alffplay.cpp | 253 +++++++++++++++++++++++++++---------------------- 3 files changed, 150 insertions(+), 141 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d1690a8..c712a16a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1681,24 +1681,24 @@ if(ALSOFT_EXAMPLES) set(FFVER_OK FALSE) if(FFMPEG_FOUND) set(FFVER_OK TRUE) - if(AVFORMAT_VERSION VERSION_LESS "57.56.101") - message(STATUS "libavformat is too old! (${AVFORMAT_VERSION}, wanted 57.56.101)") + if(AVFORMAT_VERSION VERSION_LESS "59.27.100") + message(STATUS "libavformat is too old! (${AVFORMAT_VERSION}, wanted 59.27.100)") set(FFVER_OK FALSE) endif() - if(AVCODEC_VERSION VERSION_LESS "57.64.101") - message(STATUS "libavcodec is too old! (${AVCODEC_VERSION}, wanted 57.64.101)") + if(AVCODEC_VERSION VERSION_LESS "59.37.100") + message(STATUS "libavcodec is too old! (${AVCODEC_VERSION}, wanted 59.37.100)") set(FFVER_OK FALSE) endif() - if(AVUTIL_VERSION VERSION_LESS "55.34.101") - message(STATUS "libavutil is too old! (${AVUTIL_VERSION}, wanted 55.34.101)") + if(AVUTIL_VERSION VERSION_LESS "57.28.100") + message(STATUS "libavutil is too old! (${AVUTIL_VERSION}, wanted 57.28.100)") set(FFVER_OK FALSE) endif() - if(SWSCALE_VERSION VERSION_LESS "4.2.100") - message(STATUS "libswscale is too old! (${SWSCALE_VERSION}, wanted 4.2.100)") + if(SWSCALE_VERSION VERSION_LESS "6.7.100") + message(STATUS "libswscale is too old! (${SWSCALE_VERSION}, wanted 6.7.100)") set(FFVER_OK FALSE) endif() - if(SWRESAMPLE_VERSION VERSION_LESS "2.3.100") - message(STATUS "libswresample is too old! (${SWRESAMPLE_VERSION}, wanted 2.3.100)") + if(SWRESAMPLE_VERSION VERSION_LESS "4.7.100") + message(STATUS "libswresample is too old! (${SWRESAMPLE_VERSION}, wanted 4.7.100)") set(FFVER_OK FALSE) endif() endif() diff --git a/cmake/FindFFmpeg.cmake b/cmake/FindFFmpeg.cmake index 60ca68fd..26ed4d2f 100644 --- a/cmake/FindFFmpeg.cmake +++ b/cmake/FindFFmpeg.cmake @@ -80,22 +80,8 @@ macro(find_component _component _pkgconfig _library _header) ${PC_LIB${_component}_LIBRARY_DIRS} ) - STRING(REGEX REPLACE "/.*" "/version.h" _ver_header ${_header}) - if(EXISTS "${${_component}_INCLUDE_DIRS}/${_ver_header}") - file(STRINGS "${${_component}_INCLUDE_DIRS}/${_ver_header}" version_str REGEX "^#define[\t ]+LIB${_component}_VERSION_M.*") - - string(REGEX REPLACE "^.*LIB${_component}_VERSION_MAJOR[\t ]+([0-9]*).*$" "\\1" version_maj "${version_str}") - string(REGEX REPLACE "^.*LIB${_component}_VERSION_MINOR[\t ]+([0-9]*).*$" "\\1" version_min "${version_str}") - string(REGEX REPLACE "^.*LIB${_component}_VERSION_MICRO[\t ]+([0-9]*).*$" "\\1" version_mic "${version_str}") - unset(version_str) - - set(${_component}_VERSION "${version_maj}.${version_min}.${version_mic}" CACHE STRING "The ${_component} version number.") - unset(version_maj) - unset(version_min) - unset(version_mic) - endif(EXISTS "${${_component}_INCLUDE_DIRS}/${_ver_header}") - set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number.") - set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS.") + set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number." FORCE) + set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS." FORCE) set_component_found(${_component}) diff --git a/examples/alffplay.cpp b/examples/alffplay.cpp index 177d508a..ec40f89a 100644 --- a/examples/alffplay.cpp +++ b/examples/alffplay.cpp @@ -167,6 +167,12 @@ struct SwsContextDeleter { using SwsContextPtr = std::unique_ptr; +struct ChannelLayout : public AVChannelLayout { + ChannelLayout() : AVChannelLayout{} { } + ~ChannelLayout() { av_channel_layout_uninit(this); } +}; + + template class DataQueue { std::mutex mPacketMutex, mFrameMutex; @@ -674,8 +680,8 @@ int AudioState::decodeFrame() if(mDecodedFrame->nb_samples > mSamplesMax) { av_freep(&mSamples); - av_samples_alloc(&mSamples, nullptr, mCodecCtx->channels, mDecodedFrame->nb_samples, - mDstSampleFmt, 0); + av_samples_alloc(&mSamples, nullptr, mCodecCtx->ch_layout.nb_channels, + mDecodedFrame->nb_samples, mDstSampleFmt, 0); mSamplesMax = mDecodedFrame->nb_samples; } /* Return the amount of sample frames converted */ @@ -929,10 +935,6 @@ int AudioState::handler() }; EventControlManager event_controller{sleep_time}; - const bool has_bfmt_ex{alIsExtensionPresent("AL_SOFT_bformat_ex") != AL_FALSE}; - ALenum ambi_layout{AL_FUMA_SOFT}; - ALenum ambi_scale{AL_FUMA_SOFT}; - std::unique_ptr samples; ALsizei buffer_len{0}; @@ -950,40 +952,38 @@ int AudioState::handler() { mDstSampleFmt = AV_SAMPLE_FMT_FLT; mFrameSize = 4; - if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + if(mCodecCtx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE) { - if(mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1) - { - mDstChanLayout = mCodecCtx->channel_layout; - mFrameSize *= 8; - mFormat = alGetEnumValue("AL_FORMAT_71CHN32"); - } - if(mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1 - || mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) { - mDstChanLayout = mCodecCtx->channel_layout; - mFrameSize *= 6; - mFormat = alGetEnumValue("AL_FORMAT_51CHN32"); + if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_7POINT1) + { + mDstChanLayout = mCodecCtx->ch_layout.u.mask; + mFrameSize *= 8; + mFormat = alGetEnumValue("AL_FORMAT_71CHN32"); + } + if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1 + || mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1_BACK) + { + mDstChanLayout = mCodecCtx->ch_layout.u.mask; + mFrameSize *= 6; + mFormat = alGetEnumValue("AL_FORMAT_51CHN32"); + } + if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_QUAD) + { + mDstChanLayout = mCodecCtx->ch_layout.u.mask; + mFrameSize *= 4; + mFormat = alGetEnumValue("AL_FORMAT_QUAD32"); + } } - if(mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD) + if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_MONO) { - mDstChanLayout = mCodecCtx->channel_layout; - mFrameSize *= 4; - mFormat = alGetEnumValue("AL_FORMAT_QUAD32"); + mDstChanLayout = mCodecCtx->ch_layout.u.mask; + mFrameSize *= 1; + mFormat = AL_FORMAT_MONO_FLOAT32; } } - if(mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO) - { - mDstChanLayout = mCodecCtx->channel_layout; - mFrameSize *= 1; - mFormat = AL_FORMAT_MONO_FLOAT32; - } - /* Assume 3D B-Format (ambisonics) if the channel layout is blank and - * there's 4 or more channels. FFmpeg/libavcodec otherwise seems to - * have no way to specify if the source is actually B-Format (let alone - * if it's 2D or 3D). - */ - if(mCodecCtx->channel_layout == 0 && mCodecCtx->channels >= 4 + else if(mCodecCtx->ch_layout.order == AV_CHANNEL_ORDER_AMBISONIC && alIsExtensionPresent("AL_EXT_BFORMAT")) { /* Calculate what should be the ambisonic order from the number of @@ -991,9 +991,10 @@ int AudioState::handler() * an optional non-diegetic stereo stream with the B-Format stream, * which we can ignore, so check for that too. */ - auto order = static_cast(std::sqrt(mCodecCtx->channels)) - 1; + auto order = static_cast(std::sqrt(mCodecCtx->ch_layout.nb_channels)) - 1; int channels{(order+1) * (order+1)}; - if(channels == mCodecCtx->channels || channels+2 == mCodecCtx->channels) + if(channels == mCodecCtx->ch_layout.nb_channels + || channels+2 == mCodecCtx->ch_layout.nb_channels) { /* OpenAL only supports first-order with AL_EXT_BFORMAT, which * is 4 channels for 3D buffers. @@ -1013,40 +1014,44 @@ int AudioState::handler() { mDstSampleFmt = AV_SAMPLE_FMT_U8; mFrameSize = 1; - if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + if(mCodecCtx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE) { - if(mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1) + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) { - mDstChanLayout = mCodecCtx->channel_layout; - mFrameSize *= 8; - mFormat = alGetEnumValue("AL_FORMAT_71CHN8"); - } - if(mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1 - || mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) - { - mDstChanLayout = mCodecCtx->channel_layout; - mFrameSize *= 6; - mFormat = alGetEnumValue("AL_FORMAT_51CHN8"); + if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_7POINT1) + { + mDstChanLayout = mCodecCtx->ch_layout.u.mask; + mFrameSize *= 8; + mFormat = alGetEnumValue("AL_FORMAT_71CHN8"); + } + if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1 + || mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1_BACK) + { + mDstChanLayout = mCodecCtx->ch_layout.u.mask; + mFrameSize *= 6; + mFormat = alGetEnumValue("AL_FORMAT_51CHN8"); + } + if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_QUAD) + { + mDstChanLayout = mCodecCtx->ch_layout.u.mask; + mFrameSize *= 4; + mFormat = alGetEnumValue("AL_FORMAT_QUAD8"); + } } - if(mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD) + if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_MONO) { - mDstChanLayout = mCodecCtx->channel_layout; - mFrameSize *= 4; - mFormat = alGetEnumValue("AL_FORMAT_QUAD8"); + mDstChanLayout = mCodecCtx->ch_layout.u.mask; + mFrameSize *= 1; + mFormat = AL_FORMAT_MONO8; } } - if(mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO) - { - mDstChanLayout = mCodecCtx->channel_layout; - mFrameSize *= 1; - mFormat = AL_FORMAT_MONO8; - } - if(mCodecCtx->channel_layout == 0 && mCodecCtx->channels >= 4 + else if(mCodecCtx->ch_layout.order == AV_CHANNEL_ORDER_AMBISONIC && alIsExtensionPresent("AL_EXT_BFORMAT")) { - auto order = static_cast(std::sqrt(mCodecCtx->channels)) - 1; + auto order = static_cast(std::sqrt(mCodecCtx->ch_layout.nb_channels)) - 1; int channels{(order+1) * (order+1)}; - if(channels == mCodecCtx->channels || channels+2 == mCodecCtx->channels) + if(channels == mCodecCtx->ch_layout.nb_channels + || channels+2 == mCodecCtx->ch_layout.nb_channels) { mFrameSize *= 4; mFormat = alGetEnumValue("AL_FORMAT_BFORMAT3D_8"); @@ -1063,40 +1068,44 @@ int AudioState::handler() { mDstSampleFmt = AV_SAMPLE_FMT_S16; mFrameSize = 2; - if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + if(mCodecCtx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE) { - if(mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1) - { - mDstChanLayout = mCodecCtx->channel_layout; - mFrameSize *= 8; - mFormat = alGetEnumValue("AL_FORMAT_71CHN16"); - } - if(mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1 - || mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) { - mDstChanLayout = mCodecCtx->channel_layout; - mFrameSize *= 6; - mFormat = alGetEnumValue("AL_FORMAT_51CHN16"); + if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_7POINT1) + { + mDstChanLayout = mCodecCtx->ch_layout.u.mask; + mFrameSize *= 8; + mFormat = alGetEnumValue("AL_FORMAT_71CHN16"); + } + if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1 + || mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1_BACK) + { + mDstChanLayout = mCodecCtx->ch_layout.u.mask; + mFrameSize *= 6; + mFormat = alGetEnumValue("AL_FORMAT_51CHN16"); + } + if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_QUAD) + { + mDstChanLayout = mCodecCtx->ch_layout.u.mask; + mFrameSize *= 4; + mFormat = alGetEnumValue("AL_FORMAT_QUAD16"); + } } - if(mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD) + if(mCodecCtx->ch_layout.u.mask == AV_CH_LAYOUT_MONO) { - mDstChanLayout = mCodecCtx->channel_layout; - mFrameSize *= 4; - mFormat = alGetEnumValue("AL_FORMAT_QUAD16"); + mDstChanLayout = mCodecCtx->ch_layout.u.mask; + mFrameSize *= 1; + mFormat = AL_FORMAT_MONO16; } } - if(mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO) - { - mDstChanLayout = mCodecCtx->channel_layout; - mFrameSize *= 1; - mFormat = AL_FORMAT_MONO16; - } - if(mCodecCtx->channel_layout == 0 && mCodecCtx->channels >= 4 + else if(mCodecCtx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE && alIsExtensionPresent("AL_EXT_BFORMAT")) { - auto order = static_cast(std::sqrt(mCodecCtx->channels)) - 1; + auto order = static_cast(std::sqrt(mCodecCtx->ch_layout.nb_channels)) - 1; int channels{(order+1) * (order+1)}; - if(channels == mCodecCtx->channels || channels+2 == mCodecCtx->channels) + if(channels == mCodecCtx->ch_layout.nb_channels + || channels+2 == mCodecCtx->ch_layout.nb_channels) { mFrameSize *= 4; mFormat = alGetEnumValue("AL_FORMAT_BFORMAT3D_16"); @@ -1122,32 +1131,33 @@ int AudioState::handler() return 0; } + /* Note that ffmpeg assumes AmbiX (ACN layout, SN3D normalization). */ + const bool has_bfmt_ex{alIsExtensionPresent("AL_SOFT_bformat_ex") != AL_FALSE}; + const ALenum ambi_layout{AL_ACN_SOFT}; + const ALenum ambi_scale{AL_SN3D_SOFT}; + if(!mDstChanLayout) { /* OpenAL only supports first-order ambisonics with AL_EXT_BFORMAT, so * we have to drop any extra channels. */ - mSwresCtx.reset(swr_alloc_set_opts(nullptr, - (1_i64<<4)-1, mDstSampleFmt, mCodecCtx->sample_rate, - (1_i64<channels)-1, mCodecCtx->sample_fmt, mCodecCtx->sample_rate, - 0, nullptr)); - - /* Note that ffmpeg/libavcodec has no method to check the ambisonic - * channel order and normalization, so we can only assume AmbiX as the - * defacto-standard. This is not true for .amb files, which use FuMa. - */ - std::vector mtx(64*64, 0.0); - ambi_layout = AL_ACN_SOFT; - ambi_scale = AL_SN3D_SOFT; - if(has_bfmt_ex) + ChannelLayout layout{}; + av_channel_layout_from_string(&layout, "ambisonic 1"); + + SwrContext *ps{}; + int err{swr_alloc_set_opts2(&ps, &layout, mDstSampleFmt, mCodecCtx->sample_rate, + &mCodecCtx->ch_layout, mCodecCtx->sample_fmt, mCodecCtx->sample_rate, 0, nullptr)}; + mSwresCtx.reset(ps); + if(err != 0) { - /* An identity matrix that doesn't remix any channels. */ - std::cout<< "Found AL_SOFT_bformat_ex" < mtx(64*64, 0.0); mtx[0 + 0*64] = std::sqrt(0.5); mtx[3 + 1*64] = 1.0; mtx[1 + 2*64] = 1.0; mtx[2 + 3*64] = 1.0; + swr_set_matrix(mSwresCtx.get(), mtx.data(), 64); } - swr_set_matrix(mSwresCtx.get(), mtx.data(), 64); } else - mSwresCtx.reset(swr_alloc_set_opts(nullptr, - static_cast(mDstChanLayout), mDstSampleFmt, mCodecCtx->sample_rate, - mCodecCtx->channel_layout ? static_cast(mCodecCtx->channel_layout) - : av_get_default_channel_layout(mCodecCtx->channels), - mCodecCtx->sample_fmt, mCodecCtx->sample_rate, - 0, nullptr)); - if(!mSwresCtx || swr_init(mSwresCtx.get()) != 0) - { - std::cerr<< "Failed to initialize audio converter" <sample_rate, + &mCodecCtx->ch_layout, mCodecCtx->sample_fmt, mCodecCtx->sample_rate, 0, nullptr)}; + mSwresCtx.reset(ps); + if(err != 0) + { + char errstr[AV_ERROR_MAX_STRING_SIZE]{}; + std::cerr<< "Failed to allocate SwrContext: " + < Date: Sun, 21 Aug 2022 17:47:56 -0700 Subject: Don't include the full path for frameworks to link --- CMakeLists.txt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index c712a16a..8ed55f4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1086,19 +1086,22 @@ if(COREAUDIO_FRAMEWORK AND AUDIOUNIT_INCLUDE_DIR) set(HAVE_COREAUDIO 1) set(ALC_OBJS ${ALC_OBJS} alc/backends/coreaudio.cpp alc/backends/coreaudio.h) set(BACKENDS "${BACKENDS} CoreAudio,") + + set(EXTRA_LIBS -Wl,-framework,CoreAudio ${EXTRA_LIBS}) if(CMAKE_SYSTEM_NAME STREQUAL "iOS") find_library(COREFOUNDATION_FRAMEWORK NAMES CoreFoundation) - set(EXTRA_LIBS ${COREAUDIO_FRAMEWORK} ${COREFOUNDATION_FRAMEWORK} ${EXTRA_LIBS}) + if(COREFOUNDATION_FRAMEWORK) + set(EXTRA_LIBS -Wl,-framework,CoreFoundation ${EXTRA_LIBS}) + endif() else() - set(EXTRA_LIBS ${COREAUDIO_FRAMEWORK} /System/Library/Frameworks/AudioUnit.framework - /System/Library/Frameworks/ApplicationServices.framework ${EXTRA_LIBS}) + set(EXTRA_LIBS -Wl,-framework,AudioUnit,-framework,ApplicationServices ${EXTRA_LIBS}) endif() # Some versions of OSX may need the AudioToolbox framework. Add it if # it's found. find_library(AUDIOTOOLBOX_LIBRARY NAMES AudioToolbox) if(AUDIOTOOLBOX_LIBRARY) - set(EXTRA_LIBS ${AUDIOTOOLBOX_LIBRARY} ${EXTRA_LIBS}) + set(EXTRA_LIBS -Wl,-framework,AudioToolbox ${EXTRA_LIBS}) endif() set(INC_PATHS ${INC_PATHS} ${AUDIOUNIT_INCLUDE_DIR}) -- cgit v1.2.3 From 6e94db24015ca9aa7a76e7c3340143e4c3a66806 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 28 Aug 2022 03:28:55 -0700 Subject: Fix OSX framework versions --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ed55f4a..0b5051c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1367,7 +1367,7 @@ else() # Sets framework name to soft_oal to avoid ambiguity with the system OpenAL.framework set(LIBNAME "soft_oal") if(GIT_FOUND) - EXECUTE_PROCESS(COMMAND ${GIT_EXECUTABLE} rev-list --count head + EXECUTE_PROCESS(COMMAND ${GIT_EXECUTABLE} rev-list --count HEAD TIMEOUT 5 OUTPUT_VARIABLE BUNDLE_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) @@ -1388,7 +1388,7 @@ else() FRAMEWORK_VERSION C MACOSX_FRAMEWORK_NAME "${IMPL_TARGET}" MACOSX_FRAMEWORK_IDENTIFIER "org.openal-soft.openal" - MACOSX_FRAMEWORK_SHORT_VERSION_STRING ${LIB_VERSION} + MACOSX_FRAMEWORK_SHORT_VERSION_STRING "${LIB_VERSION}" MACOSX_FRAMEWORK_BUNDLE_VERSION ${BUNDLE_VERSION} XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "" XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO" -- cgit v1.2.3 From 057a350c1e15906d12559acb56115fbef2d9a3bf Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 28 Aug 2022 04:36:15 -0700 Subject: More updates for making an OSX framework --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b5051c4..aea0f293 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1355,7 +1355,8 @@ else() # !important: for osx framework public header works, the headers must be in # the project - set(TARGET_PUBLIC_HEADERS include/AL/al.h include/AL/alc.h include/AL/alext.h include/AL/efx.h) + set(TARGET_PUBLIC_HEADERS include/AL/al.h include/AL/alc.h include/AL/alext.h include/AL/efx.h + include/AL/efx-presets.h) add_library(${IMPL_TARGET} SHARED ${OPENAL_OBJS} ${ALC_OBJS} ${CORE_OBJS} ${RC_CONFIG} ${TARGET_PUBLIC_HEADERS}) if(WIN32) @@ -1389,7 +1390,7 @@ else() MACOSX_FRAMEWORK_NAME "${IMPL_TARGET}" MACOSX_FRAMEWORK_IDENTIFIER "org.openal-soft.openal" MACOSX_FRAMEWORK_SHORT_VERSION_STRING "${LIB_VERSION}" - MACOSX_FRAMEWORK_BUNDLE_VERSION ${BUNDLE_VERSION} + MACOSX_FRAMEWORK_BUNDLE_VERSION "${BUNDLE_VERSION}" XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "" XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO" XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO" -- cgit v1.2.3 From ee3142d4d605eac7e7c5f6d4df87ec5e3b345d29 Mon Sep 17 00:00:00 2001 From: Paulo Coutinho Date: Mon, 5 Sep 2022 02:09:14 -0300 Subject: tvos support (#762) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index aea0f293..629ff201 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1088,7 +1088,7 @@ if(COREAUDIO_FRAMEWORK AND AUDIOUNIT_INCLUDE_DIR) set(BACKENDS "${BACKENDS} CoreAudio,") set(EXTRA_LIBS -Wl,-framework,CoreAudio ${EXTRA_LIBS}) - if(CMAKE_SYSTEM_NAME STREQUAL "iOS") + if(CMAKE_SYSTEM_NAME MATCHES "^(iOS|tvOS)$") find_library(COREFOUNDATION_FRAMEWORK NAMES CoreFoundation) if(COREFOUNDATION_FRAMEWORK) set(EXTRA_LIBS -Wl,-framework,CoreFoundation ${EXTRA_LIBS}) -- cgit v1.2.3 From 49be7c53cc8d7b8ff15d9fad8349f85b3c96d804 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 16 Nov 2022 17:57:48 -0800 Subject: Avoid unnecessary duplicate CMake variables This should make FindPulseAudio.cmake more compatible with PulseAudio's own PulseAudioConfig.cmake. --- CMakeLists.txt | 4 ++-- cmake/FindPulseAudio.cmake | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 629ff201..b1b3d86f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1051,8 +1051,8 @@ if(PULSEAUDIO_FOUND) set(HAVE_PULSEAUDIO 1) set(BACKENDS "${BACKENDS} PulseAudio${IS_LINKED},") set(ALC_OBJS ${ALC_OBJS} alc/backends/pulseaudio.cpp alc/backends/pulseaudio.h) - add_backend_libs(${PULSEAUDIO_LIBRARIES}) - set(INC_PATHS ${INC_PATHS} ${PULSEAUDIO_INCLUDE_DIRS}) + add_backend_libs(${PULSEAUDIO_LIBRARY}) + set(INC_PATHS ${INC_PATHS} ${PULSEAUDIO_INCLUDE_DIR}) endif() endif() if(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO) diff --git a/cmake/FindPulseAudio.cmake b/cmake/FindPulseAudio.cmake index 1f6f843a..fdcbc20f 100644 --- a/cmake/FindPulseAudio.cmake +++ b/cmake/FindPulseAudio.cmake @@ -2,8 +2,6 @@ # # PULSEAUDIO_FOUND - True if PULSEAUDIO_INCLUDE_DIR & # PULSEAUDIO_LIBRARY are found -# PULSEAUDIO_LIBRARIES - Set when PULSEAUDIO_LIBRARY is found -# PULSEAUDIO_INCLUDE_DIRS - Set when PULSEAUDIO_INCLUDE_DIR is found # # PULSEAUDIO_INCLUDE_DIR - where to find pulse/pulseaudio.h, etc. # PULSEAUDIO_LIBRARY - the pulse library @@ -34,10 +32,3 @@ find_package_handle_standard_args(PulseAudio REQUIRED_VARS PULSEAUDIO_LIBRARY PULSEAUDIO_INCLUDE_DIR VERSION_VAR PULSEAUDIO_VERSION_STRING ) - -if(PULSEAUDIO_FOUND) - set(PULSEAUDIO_LIBRARIES ${PULSEAUDIO_LIBRARY}) - set(PULSEAUDIO_INCLUDE_DIRS ${PULSEAUDIO_INCLUDE_DIR}) -endif() - -mark_as_advanced(PULSEAUDIO_INCLUDE_DIR PULSEAUDIO_LIBRARY) -- cgit v1.2.3 From 8ee842a948367bde4f323561acc899450126eaf7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 16 Nov 2022 18:58:24 -0800 Subject: Use SDL2's sdl2-config.cmake Instead of our own custom FindSDL2.cmake --- CMakeLists.txt | 19 ++--- cmake/FindSDL2.cmake | 191 --------------------------------------------------- 2 files changed, 11 insertions(+), 199 deletions(-) delete mode 100644 cmake/FindSDL2.cmake (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index b1b3d86f..1bc557b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1171,7 +1171,7 @@ endif() # Check for SDL2 backend option(ALSOFT_REQUIRE_SDL2 "Require SDL2 backend" OFF) -find_package(SDL2) +find_package(SDL2 QUIET) if(SDL2_FOUND) # Off by default, since it adds a runtime dependency option(ALSOFT_BACKEND_SDL2 "Enable SDL2 backend" OFF) @@ -1179,9 +1179,11 @@ if(SDL2_FOUND) set(HAVE_SDL2 1) set(ALC_OBJS ${ALC_OBJS} alc/backends/sdl2.cpp alc/backends/sdl2.h) set(BACKENDS "${BACKENDS} SDL2,") - set(EXTRA_LIBS ${SDL2_LIBRARY} ${EXTRA_LIBS}) + set(EXTRA_LIBS ${EXTRA_LIBS} SDL2::SDL2) set(INC_PATHS ${INC_PATHS} ${SDL2_INCLUDE_DIR}) endif() +else() + message(STATUS "Could NOT find SDL2") endif() if(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND) message(FATAL_ERROR "Failed to enabled required SDL2 backend") @@ -1249,7 +1251,6 @@ if(ALSOFT_UTILS) endif() if(ALSOFT_UTILS OR ALSOFT_EXAMPLES) find_package(SndFile) - find_package(SDL2) if(SDL2_FOUND) find_package(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE) endif() @@ -1274,9 +1275,12 @@ if(LIBTYPE STREQUAL "STATIC") set(PKG_CONFIG_CFLAGS -DAL_LIBTYPE_STATIC) foreach(FLAG ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) # If this is already a linker flag, or is a full path+file, add it - # as-is. Otherwise, it's a name intended to be dressed as -lname. + # as-is. If it's an SDL2 target, add the link flag for it. Otherwise, + # it's a name intended to be dressed as -lname. if(FLAG MATCHES "^-.*" OR EXISTS "${FLAG}") set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} ${FLAG}") + elseif(FLAG MATCHES "^SDL2::SDL2") + set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -lSDL2") else() set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -l${FLAG}") endif() @@ -1674,7 +1678,7 @@ if(ALSOFT_EXAMPLES) add_executable(alloopback examples/alloopback.c) target_include_directories(alloopback PRIVATE ${SDL2_INCLUDE_DIR}) target_link_libraries(alloopback - PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ex-common ${MATH_LIB}) + PRIVATE ${LINKER_FLAGS} SDL2::SDL2 SDL2::SDL2main ex-common ${MATH_LIB}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alloopback) @@ -1708,10 +1712,9 @@ if(ALSOFT_EXAMPLES) endif() if(FFVER_OK) add_executable(alffplay examples/alffplay.cpp) - target_include_directories(alffplay - PRIVATE ${SDL2_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIRS}) + target_include_directories(alffplay PRIVATE ${FFMPEG_INCLUDE_DIRS}) target_link_libraries(alffplay - PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} ex-common) + PRIVATE ${LINKER_FLAGS} SDL2::SDL2 SDL2::SDL2main ${FFMPEG_LIBRARIES} ex-common) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alffplay) diff --git a/cmake/FindSDL2.cmake b/cmake/FindSDL2.cmake deleted file mode 100644 index e808d006..00000000 --- a/cmake/FindSDL2.cmake +++ /dev/null @@ -1,191 +0,0 @@ -# Locate SDL2 library -# This module defines -# SDL2_LIBRARY, the name of the library to link against -# SDL2_FOUND, if false, do not try to link to SDL2 -# SDL2_INCLUDE_DIR, where to find SDL.h -# -# This module responds to the the flag: -# SDL2_BUILDING_LIBRARY -# If this is defined, then no SDL2_main will be linked in because -# only applications need main(). -# Otherwise, it is assumed you are building an application and this -# module will attempt to locate and set the the proper link flags -# as part of the returned SDL2_LIBRARY variable. -# -# Don't forget to include SDL2main.h and SDL2main.m your project for the -# OS X framework based version. (Other versions link to -lSDL2main which -# this module will try to find on your behalf.) Also for OS X, this -# module will automatically add the -framework Cocoa on your behalf. -# -# -# Additional Note: If you see an empty SDL2_CORE_LIBRARY in your configuration -# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library -# (SDL2.dll, libsdl2.so, SDL2.framework, etc). -# Set SDL2_CORE_LIBRARY to point to your SDL2 library, and configure again. -# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value -# as appropriate. These values are used to generate the final SDL2_LIBRARY -# variable, but when these values are unset, SDL2_LIBRARY does not get created. -# -# -# $SDL2DIR is an environment variable that would -# correspond to the ./configure --prefix=$SDL2DIR -# used in building SDL2. -# l.e.galup 9-20-02 -# -# Modified by Eric Wing. -# Added code to assist with automated building by using environmental variables -# and providing a more controlled/consistent search behavior. -# Added new modifications to recognize OS X frameworks and -# additional Unix paths (FreeBSD, etc). -# Also corrected the header search path to follow "proper" SDL2 guidelines. -# Added a search for SDL2main which is needed by some platforms. -# Added a search for threads which is needed by some platforms. -# Added needed compile switches for MinGW. -# -# On OSX, this will prefer the Framework version (if found) over others. -# People will have to manually change the cache values of -# SDL2_LIBRARY to override this selection or set the CMake environment -# CMAKE_INCLUDE_PATH to modify the search paths. -# -# Note that the header path has changed from SDL2/SDL.h to just SDL.h -# This needed to change because "proper" SDL2 convention -# is #include "SDL.h", not . This is done for portability -# reasons because not all systems place things in SDL2/ (see FreeBSD). -# -# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake -# module with the minor edit of changing "SDL" to "SDL2" where necessary. This -# was not created for redistribution, and exists temporarily pending official -# SDL2 CMake modules. - -#============================================================================= -# Copyright 2003-2009 Kitware, Inc. -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - - -FIND_PATH(SDL2_INCLUDE_DIR SDL.h - HINTS - $ENV{SDL2DIR} - PATH_SUFFIXES include/SDL2 include - PATHS - ~/Library/Frameworks - /Library/Frameworks - /usr/local/include/SDL2 - /usr/include/SDL2 - /sw # Fink - /opt/local # DarwinPorts - /opt/csw # Blastwave - /opt -) -#MESSAGE("SDL2_INCLUDE_DIR is ${SDL2_INCLUDE_DIR}") - -FIND_LIBRARY(SDL2_CORE_LIBRARY - NAMES SDL2 - HINTS - $ENV{SDL2DIR} - PATH_SUFFIXES lib64 lib - PATHS - /sw - /opt/local - /opt/csw - /opt -) -#MESSAGE("SDL2_CORE_LIBRARY is ${SDL2_CORE_LIBRARY}") - -IF(NOT SDL2_BUILDING_LIBRARY) - IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") - # Non-OS X framework versions expect you to also dynamically link to - # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms - # seem to provide SDL2main for compatibility even though they don't - # necessarily need it. - FIND_LIBRARY(SDL2MAIN_LIBRARY - NAMES SDL2main - HINTS - $ENV{SDL2DIR} - PATH_SUFFIXES lib64 lib - PATHS - /sw - /opt/local - /opt/csw - /opt - ) - ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") -ENDIF(NOT SDL2_BUILDING_LIBRARY) - -# SDL2 may require threads on your system. -# The Apple build may not need an explicit flag because one of the -# frameworks may already provide it. -# But for non-OSX systems, I will use the CMake Threads package. -IF(NOT APPLE) - FIND_PACKAGE(Threads) -ENDIF(NOT APPLE) - -# MinGW needs an additional library, mwindows -# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows -# (Actually on second look, I think it only needs one of the m* libraries.) -IF(MINGW) - SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") -ENDIF(MINGW) - -SET(SDL2_FOUND "NO") -IF(SDL2_CORE_LIBRARY) - SET(SDL2_LIBRARY_TEMP ${SDL2_CORE_LIBRARY}) - - # For SDL2main - IF(NOT SDL2_BUILDING_LIBRARY) - IF(SDL2MAIN_LIBRARY) - SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) - ENDIF(SDL2MAIN_LIBRARY) - ENDIF(NOT SDL2_BUILDING_LIBRARY) - - # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. - # CMake doesn't display the -framework Cocoa string in the UI even - # though it actually is there if I modify a pre-used variable. - # I think it has something to do with the CACHE STRING. - # So I use a temporary variable until the end so I can set the - # "real" variable in one-shot. - IF(APPLE) - SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") - ENDIF(APPLE) - - # For threads, as mentioned Apple doesn't need this. - # In fact, there seems to be a problem if I used the Threads package - # and try using this line, so I'm just skipping it entirely for OS X. - IF(NOT APPLE) - SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) - ENDIF(NOT APPLE) - - # For MinGW library - IF(MINGW) - SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) - ENDIF(MINGW) - - IF(WIN32) - SET(SDL2_LIBRARY_TEMP winmm imm32 version msimg32 ${SDL2_LIBRARY_TEMP}) - ENDIF(WIN32) - - # Set the final string here so the GUI reflects the final state. - SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP}) - - SET(SDL2_FOUND "YES") -ENDIF(SDL2_CORE_LIBRARY) - -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 - REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) - -IF(SDL2_STATIC) - if (UNIX AND NOT APPLE) - EXECUTE_PROCESS(COMMAND sdl2-config --static-libs OUTPUT_VARIABLE SDL2_LINK_FLAGS) - STRING(REGEX REPLACE "(\r?\n)+$" "" SDL2_LINK_FLAGS "${SDL2_LINK_FLAGS}") - SET(SDL2_LIBRARY ${SDL2_LINK_FLAGS}) - ENDIF() -ENDIF(SDL2_STATIC) -- cgit v1.2.3 From 0239556444a6174d6fa81fc5993a14cdb8d6a4f1 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 16 Nov 2022 20:31:21 -0800 Subject: Remove unnecessary use of SDL2_INCLUDE_DIR --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bc557b1..e0a2e6d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1180,7 +1180,6 @@ if(SDL2_FOUND) set(ALC_OBJS ${ALC_OBJS} alc/backends/sdl2.cpp alc/backends/sdl2.h) set(BACKENDS "${BACKENDS} SDL2,") set(EXTRA_LIBS ${EXTRA_LIBS} SDL2::SDL2) - set(INC_PATHS ${INC_PATHS} ${SDL2_INCLUDE_DIR}) endif() else() message(STATUS "Could NOT find SDL2") @@ -1676,7 +1675,6 @@ if(ALSOFT_EXAMPLES) if(SDL2_FOUND) add_executable(alloopback examples/alloopback.c) - target_include_directories(alloopback PRIVATE ${SDL2_INCLUDE_DIR}) target_link_libraries(alloopback PRIVATE ${LINKER_FLAGS} SDL2::SDL2 SDL2::SDL2main ex-common ${MATH_LIB}) -- cgit v1.2.3 From 3b838bc781ca2a314fa51e08389037ebce34a9cc Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 23 Nov 2022 06:41:49 -0800 Subject: Avoid overriding main with SDL --- CMakeLists.txt | 4 ++-- examples/alffplay.cpp | 3 +++ examples/alloopback.c | 7 +++++++ 3 files changed, 12 insertions(+), 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index e0a2e6d3..30820f91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1676,7 +1676,7 @@ if(ALSOFT_EXAMPLES) if(SDL2_FOUND) add_executable(alloopback examples/alloopback.c) target_link_libraries(alloopback - PRIVATE ${LINKER_FLAGS} SDL2::SDL2 SDL2::SDL2main ex-common ${MATH_LIB}) + PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ex-common ${MATH_LIB}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alloopback) @@ -1712,7 +1712,7 @@ if(ALSOFT_EXAMPLES) add_executable(alffplay examples/alffplay.cpp) target_include_directories(alffplay PRIVATE ${FFMPEG_INCLUDE_DIRS}) target_link_libraries(alffplay - PRIVATE ${LINKER_FLAGS} SDL2::SDL2 SDL2::SDL2main ${FFMPEG_LIBRARIES} ex-common) + PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} ex-common) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alffplay) diff --git a/examples/alffplay.cpp b/examples/alffplay.cpp index ef7aa89d..ae40a51a 100644 --- a/examples/alffplay.cpp +++ b/examples/alffplay.cpp @@ -57,6 +57,7 @@ constexpr auto AVErrorEOF = AVERROR_EOF; struct SwsContext; } +#define SDL_MAIN_HANDLED #include "SDL.h" #ifdef __GNUC__ _Pragma("GCC diagnostic pop") @@ -1899,6 +1900,8 @@ std::ostream &operator<<(std::ostream &os, const PrettyTime &rhs) int main(int argc, char *argv[]) { + SDL_SetMainReady(); + std::unique_ptr movState; if(argc < 2) diff --git a/examples/alloopback.c b/examples/alloopback.c index 7513458b..56cd420f 100644 --- a/examples/alloopback.c +++ b/examples/alloopback.c @@ -30,6 +30,7 @@ #include #include +#define SDL_MAIN_HANDLED #include "SDL.h" #include "SDL_audio.h" #include "SDL_error.h" @@ -141,6 +142,8 @@ int main(int argc, char *argv[]) (void)argc; (void)argv; + SDL_SetMainReady(); + /* Print out error if extension is missing. */ if(!alcIsExtensionPresent(NULL, "ALC_SOFT_loopback")) { @@ -197,6 +200,10 @@ int main(int argc, char *argv[]) attrs[3] = ALC_UNSIGNED_SHORT_SOFT; else if(obtained.format == AUDIO_S16SYS) attrs[3] = ALC_SHORT_SOFT; + else if(obtained.format == AUDIO_S32SYS) + attrs[3] = ALC_INT_SOFT; + else if(obtained.format == AUDIO_F32SYS) + attrs[3] = ALC_FLOAT_SOFT; else { fprintf(stderr, "Unhandled SDL format: 0x%04x\n", obtained.format); -- cgit v1.2.3 From 25a6814cf36bee82b24cb1b5f40769e66c327db4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 5 Dec 2022 22:29:26 -0800 Subject: Don't warn for attributes being in a later standard C++ specifies that unknown attributes should be ignored and aren't an error. Clang issues a warning when encountering a known attribute from newer standard version than the target version. GCC recognizes attributes from newer standard versions it's aware of, allowing improved compilation capabilities. Hopefully Clang (and MSVC) will take advantage since it recognizes them, even if they weren't standardized for the target version, but there's no need to warn about known attributes in either case. --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 30820f91..e20770bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -258,6 +258,11 @@ else() -Wpedantic $<$:-Wold-style-cast -Wnon-virtual-dtor -Woverloaded-virtual>) + check_cxx_compiler_flag(-Wno-c++20-attribute-extensions HAVE_WNO_CXX20_ATTR_EXT) + if(HAVE_WNO_CXX20_ATTR_EXT) + set(C_FLAGS ${C_FLAGS} $<$:-Wno-c++20-attribute-extensions>) + endif() + if(ALSOFT_WERROR) set(C_FLAGS ${C_FLAGS} -Werror) endif() -- cgit v1.2.3 From 3f8a3af3637606dae61ba112cb5bd9d2d897e5cb Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 5 Jan 2023 17:41:30 -0800 Subject: Disable MSVC warning C5051 "attribute 'attribute-name' requires at least 'standard-level'; ignored" This hides the fact that it's not optimizing [[likely]] and [[unlikely]], even though it could (GCC does, Clang gives a similar warning to MSVC, but is ambiguous about whether it's still doing the optimization). There's nothing I know to do to make MSVC do those optimizations it otherwise knows to do, so just hide the warnings instead of pessimizing the code. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index e20770bc..c721b641 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -230,7 +230,7 @@ if(MSVC) if(HAVE_PERMISSIVE_SWITCH) set(C_FLAGS ${C_FLAGS} $<$:/permissive->) endif() - set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4127 /wd4268 /wd4324 /wd5030) + set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4127 /wd4268 /wd4324 /wd5030 /wd5051) if(NOT DXSDK_DIR) string(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}") -- cgit v1.2.3 From 97f2c28ddd79474e163f62d666fc8ce555f31e0f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 12 Jan 2023 01:23:28 -0800 Subject: Use cmake's visibility attributes --- CMakeLists.txt | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index c721b641..97192131 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS") endif() endif() +set(CMAKE_C_VISIBILITY_PRESET hidden) +set(CMAKE_CXX_VISIBILITY_PRESET hidden) + if(COMMAND CMAKE_POLICY) cmake_policy(SET CMP0003 NEW) cmake_policy(SET CMP0005 NEW) @@ -42,6 +45,9 @@ if(COMMAND CMAKE_POLICY) if(POLICY CMP0054) cmake_policy(SET CMP0054 NEW) endif(POLICY CMP0054) + if(POLICY CMP0063) + cmake_policy(SET CMP0063 NEW) + endif(POLICY CMP0063) if(POLICY CMP0075) cmake_policy(SET CMP0075 NEW) endif(POLICY CMP0075) @@ -351,13 +357,6 @@ else() endif() endif() - if(HAVE_GCC_PROTECTED_VISIBILITY OR HAVE_GCC_DEFAULT_VISIBILITY) - check_c_compiler_flag(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN_SWITCH) - if(HAVE_VISIBILITY_HIDDEN_SWITCH) - set(C_FLAGS ${C_FLAGS} -fvisibility=hidden) - endif() - endif() - set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}") endif() @@ -1137,17 +1136,7 @@ set(OBOE_TARGET ) if(ANDROID) set(OBOE_SOURCE "" CACHE STRING "Source directory for Oboe.") if(OBOE_SOURCE) - # Force Oboe to build with hidden symbols. Don't want to be exporting - # them from OpenAL. - set(OLD_CXX_FLAGS ${CMAKE_CXX_FLAGS}) - check_cxx_compiler_flag(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN_SWITCH) - if(HAVE_VISIBILITY_HIDDEN_SWITCH) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") - endif() add_subdirectory(${OBOE_SOURCE} ./oboe) - set(CMAKE_CXX_FLAGS ${OLD_CXX_FLAGS}) - unset(OLD_CXX_FLAGS) - set(OBOE_TARGET oboe) else() find_package(oboe CONFIG) -- cgit v1.2.3 From dd2f72ce90bcc0ffcfe2874778e7b2b2ef31ebe3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 13 Jan 2023 12:21:37 -0800 Subject: Try to handle cmake custom targets better --- CMakeLists.txt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 97192131..d5b1e79d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,9 @@ if(COMMAND CMAKE_POLICY) if(POLICY CMP0054) cmake_policy(SET CMP0054 NEW) endif(POLICY CMP0054) + if(POLICY CMP0058) + cmake_policy(SET CMP0058 NEW) + endif(POLICY CMP0058) if(POLICY CMP0063) cmake_policy(SET CMP0063 NEW) endif(POLICY CMP0063) @@ -1197,13 +1200,19 @@ set(BACKENDS "${BACKENDS} Null") find_package(Git) if(ALSOFT_UPDATE_BUILD_VERSION AND GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.git") # Get the current working branch and its latest abbreviated commit hash - add_custom_target(build_version - ${CMAKE_COMMAND} -D GIT_EXECUTABLE=${GIT_EXECUTABLE} -D LIB_VERSION=${LIB_VERSION} + add_custom_command(OUTPUT "${OpenAL_BINARY_DIR}/version_witness.txt" + BYPRODUCTS "${OpenAL_BINARY_DIR}/version.h" + COMMAND ${CMAKE_COMMAND} -D GIT_EXECUTABLE=${GIT_EXECUTABLE} -D LIB_VERSION=${LIB_VERSION} -D LIB_VERSION_NUM=${LIB_VERSION_NUM} -D SRC=${OpenAL_SOURCE_DIR}/version.h.in -D DST=${OpenAL_BINARY_DIR}/version.h -P ${OpenAL_SOURCE_DIR}/version.cmake + COMMAND ${CMAKE_COMMAND} -E touch "${OpenAL_BINARY_DIR}/version_witness.txt" WORKING_DIRECTORY "${OpenAL_SOURCE_DIR}" + MAIN_DEPENDENCY "${OpenAL_SOURCE_DIR}/version.h.in" + DEPENDS "${OpenAL_SOURCE_DIR}/.git/index" "${OpenAL_SOURCE_DIR}/version.cmake" VERBATIM ) + + add_custom_target(build_version DEPENDS "${OpenAL_BINARY_DIR}/version_witness.txt") else() set(GIT_BRANCH "UNKNOWN") set(GIT_COMMIT_HASH "unknown") @@ -1439,6 +1448,11 @@ if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC" add_custom_command(TARGET OpenAL POST_BUILD COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def COMMAND "${CMAKE_DLLTOOL}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll + # Technically OpenAL32.def was created by the build, but cmake + # doesn't recognize it due to -Wl,--output-def,OpenAL32.def being + # manually specified. But declaring the file here allows it to be + # properly cleaned, e.g. during make clean. + BYPRODUCTS OpenAL32.def OpenAL32.lib COMMENT "Stripping ordinals from OpenAL32.def and generating OpenAL32.lib..." VERBATIM ) -- cgit v1.2.3 From 5ef7bed3a4c064fe54850f419cb248cc2a406f55 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 13 Jan 2023 16:58:29 -0800 Subject: Don't include the array definition with bin2h --- CMakeLists.txt | 6 +++--- cmake/bin2h.script.cmake | 5 ++--- core/hrtf.cpp | 4 +++- 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index d5b1e79d..f9a2c859 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1226,11 +1226,11 @@ option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library foot if(ALSOFT_EMBED_HRTF_DATA) macro(make_hrtf_header FILENAME VARNAME) set(infile "${OpenAL_SOURCE_DIR}/hrtf/${FILENAME}") - set(outfile "${OpenAL_BINARY_DIR}/${VARNAME}.h") + set(outfile "${OpenAL_BINARY_DIR}/${VARNAME}.txt") add_custom_command(OUTPUT "${outfile}" COMMAND ${CMAKE_COMMAND} -D "INPUT_FILE=${infile}" -D "OUTPUT_FILE=${outfile}" - -D "VARIABLE_NAME=${VARNAME}" -P "${CMAKE_MODULE_PATH}/bin2h.script.cmake" + -P "${CMAKE_MODULE_PATH}/bin2h.script.cmake" WORKING_DIRECTORY "${OpenAL_SOURCE_DIR}" DEPENDS "${infile}" "${CMAKE_MODULE_PATH}/bin2h.script.cmake" VERBATIM @@ -1238,7 +1238,7 @@ if(ALSOFT_EMBED_HRTF_DATA) set(ALC_OBJS ${ALC_OBJS} "${outfile}") endmacro() - make_hrtf_header("Default HRTF.mhr" "hrtf_default") + make_hrtf_header("Default HRTF.mhr" "default_hrtf") endif() diff --git a/cmake/bin2h.script.cmake b/cmake/bin2h.script.cmake index 1438fde2..7e74a7a1 100644 --- a/cmake/bin2h.script.cmake +++ b/cmake/bin2h.script.cmake @@ -8,6 +8,5 @@ file(READ "${INPUT_FILE}" indata HEX) # per line. string(REGEX REPLACE "(..)" " 0x\\1,\n" output "${indata}") -# Write the list of hex chars to the output file in a const byte array -file(WRITE "${OUTPUT_FILE}" - "const unsigned char ${VARIABLE_NAME}[] = {\n${output}};\n") +# Write the list of hex chars to the output file +file(WRITE "${OUTPUT_FILE}" "${output}") diff --git a/core/hrtf.cpp b/core/hrtf.cpp index 48b9a3d7..28179189 100644 --- a/core/hrtf.cpp +++ b/core/hrtf.cpp @@ -1206,7 +1206,9 @@ al::span GetResource(int /*name*/) #else -#include "hrtf_default.h" +constexpr unsigned char hrtf_default[]{ +#include "default_hrtf.txt" +}; al::span GetResource(int name) { -- cgit v1.2.3 From 4021cccc3e735d707be83fd431473f57614a13d7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 31 Jan 2023 03:44:18 -0800 Subject: Try -Wno-c++20-extensions if -Wno-c++20-attribute-extensions fails --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index f9a2c859..1b181cac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -270,6 +270,11 @@ else() check_cxx_compiler_flag(-Wno-c++20-attribute-extensions HAVE_WNO_CXX20_ATTR_EXT) if(HAVE_WNO_CXX20_ATTR_EXT) set(C_FLAGS ${C_FLAGS} $<$:-Wno-c++20-attribute-extensions>) + else() + check_cxx_compiler_flag(-Wno-c++20-extensions HAVE_WNO_CXX20_EXT) + if(HAVE_WNO_CXX20_EXT) + set(C_FLAGS ${C_FLAGS} $<$:-Wno-c++20-extensions>) + endif() endif() if(ALSOFT_WERROR) -- cgit v1.2.3 From 70c14cd560db819f180073052f4c2bfae5cf3c31 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 3 Feb 2023 06:48:18 -0800 Subject: Release 1.23.0 --- CMakeLists.txt | 4 ++-- ChangeLog | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ appveyor.yml | 2 +- 3 files changed, 55 insertions(+), 3 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b181cac..379544fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,8 +170,8 @@ if(NOT LIBTYPE) endif() set(LIB_MAJOR_VERSION "1") -set(LIB_MINOR_VERSION "22") -set(LIB_REVISION "2") +set(LIB_MINOR_VERSION "23") +set(LIB_REVISION "0") set(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}") set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0) diff --git a/ChangeLog b/ChangeLog index 51f409ff..da6698f8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,55 @@ +openal-soft-1.23.0: + + Fixed CoreAudio capture support. + + Fixed handling per-version EAX properties. + + Fixed interpolating changes to the Super Stereo width source property. + + Fixed detection of the update and buffer size from PipeWire. + + Fixed resuming playback devices with OpenSL. + + Fixed support for certain OpenAL implementations with the router. + + Improved reverb environment transitions. + + Improved performance of convolution reverb. + + Improved quality and performance of the pitch shifter effect slightly. + + Improved sub-sample precision for resampled sources. + + Improved blending spatialized multi-channel sources that use the source + radius property. + + Improved mixing 2D ambisonic sources for higher-order 3D ambisonic mixing. + + Improved quadraphonic and 7.1 surround sound output slightly. + + Added config options for UHJ encoding/decoding quality. Including Super + Stereo processing. + + Added a config option for specifying the speaker distance. + + Added a compatibility config option for specifying the NFC distance + scaling. + + Added a config option for mixing on PipeWire's non-real-time thread. + + Added support for virtual source nodes with PipeWire capture. + + Added the ability for the WASAPI backend to use different playback rates. + + Added support for SOFA files that define per-response delays in makemhr. + + Changed the default fallback playback sample rate to 48khz. This doesn't + affect most backends, which can detect a default rate from the system. + + Changed the default resampler to cubic. + + Changed the default HRTF size from 32 to 64 points. + openal-soft-1.22.2: Fixed PipeWire version check. diff --git a/appveyor.yml b/appveyor.yml index b75fbd79..d7adb17b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 1.22.2.{build} +version: 1.23.0.{build} environment: APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 -- cgit v1.2.3 From da845ddd9c35a1e1fcff03ea342636ae4bb8018b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 6 Feb 2023 17:46:32 -0800 Subject: Use an interpolated FIR filter for cubic resampling Similar to how the bsinc filters work, but optimized for 4-point filtering. At least the SSE version is notably faster than calculating the coefficients in real time. --- CMakeLists.txt | 3 +++ alc/alu.cpp | 11 +++++++++ core/cubic_defs.h | 13 +++++++++++ core/cubic_tables.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++++++ core/cubic_tables.h | 16 +++++++++++++ core/mixer/defs.h | 9 ++++++++ core/mixer/mixer_c.cpp | 35 ++++++++++++++++++++-------- core/mixer/mixer_neon.cpp | 52 +++++++++++++++++++++++++++++++++++------ core/mixer/mixer_sse.cpp | 51 +++++++++++++++++++++++++++++++++++----- 9 files changed, 227 insertions(+), 22 deletions(-) create mode 100644 core/cubic_defs.h create mode 100644 core/cubic_tables.cpp create mode 100644 core/cubic_tables.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 379544fb..c37fe0b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -654,6 +654,9 @@ set(CORE_OBJS core/converter.h core/cpu_caps.cpp core/cpu_caps.h + core/cubic_defs.h + core/cubic_tables.cpp + core/cubic_tables.h core/devformat.cpp core/devformat.h core/device.cpp diff --git a/alc/alu.cpp b/alc/alu.cpp index 6e243412..45f518ee 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -55,6 +55,7 @@ #include "core/buffer_storage.h" #include "core/context.h" #include "core/cpu_caps.h" +#include "core/cubic_tables.h" #include "core/devformat.h" #include "core/device.h" #include "core/effects/base.h" @@ -211,6 +212,14 @@ inline ResamplerFunc SelectResampler(Resampler resampler, uint increment) #endif return Resample_; case Resampler::Cubic: +#ifdef HAVE_NEON + if((CPUCapFlags&CPU_CAP_NEON)) + return Resample_; +#endif +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + return Resample_; +#endif return Resample_; case Resampler::BSinc12: case Resampler::BSinc24: @@ -262,7 +271,9 @@ ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState { case Resampler::Point: case Resampler::Linear: + break; case Resampler::Cubic: + state->cubic.filter = gCubicSpline.Tab; break; case Resampler::FastBSinc12: case Resampler::BSinc12: diff --git a/core/cubic_defs.h b/core/cubic_defs.h new file mode 100644 index 00000000..33751c97 --- /dev/null +++ b/core/cubic_defs.h @@ -0,0 +1,13 @@ +#ifndef CORE_CUBIC_DEFS_H +#define CORE_CUBIC_DEFS_H + +/* The number of distinct phase intervals within the cubic filter tables. */ +constexpr unsigned int CubicPhaseBits{5}; +constexpr unsigned int CubicPhaseCount{1 << CubicPhaseBits}; + +struct CubicCoefficients { + float mCoeffs[4]; + float mDeltas[4]; +}; + +#endif /* CORE_CUBIC_DEFS_H */ diff --git a/core/cubic_tables.cpp b/core/cubic_tables.cpp new file mode 100644 index 00000000..bdcc0bae --- /dev/null +++ b/core/cubic_tables.cpp @@ -0,0 +1,59 @@ + +#include "cubic_tables.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "alnumbers.h" +#include "core/mixer/defs.h" + + +namespace { + +using uint = unsigned int; + +struct SplineFilterArray { + alignas(16) CubicCoefficients mTable[CubicPhaseCount]{}; + + constexpr SplineFilterArray() + { + /* Fill in the main coefficients. */ + for(size_t pi{0};pi < CubicPhaseCount;++pi) + { + const double mu{pi / double{CubicPhaseCount}}; + const double mu2{mu*mu}, mu3{mu2*mu}; + mTable[pi].mCoeffs[0] = static_cast(-0.5*mu3 + mu2 + -0.5*mu); + mTable[pi].mCoeffs[1] = static_cast( 1.5*mu3 + -2.5*mu2 + 1.0); + mTable[pi].mCoeffs[2] = static_cast(-1.5*mu3 + 2.0*mu2 + 0.5*mu); + mTable[pi].mCoeffs[3] = static_cast( 0.5*mu3 + -0.5*mu2); + } + + /* Fill in the coefficient deltas. */ + for(size_t pi{0};pi < CubicPhaseCount-1;++pi) + { + mTable[pi].mDeltas[0] = mTable[pi+1].mCoeffs[0] - mTable[pi].mCoeffs[0]; + mTable[pi].mDeltas[1] = mTable[pi+1].mCoeffs[1] - mTable[pi].mCoeffs[1]; + mTable[pi].mDeltas[2] = mTable[pi+1].mCoeffs[2] - mTable[pi].mCoeffs[2]; + mTable[pi].mDeltas[3] = mTable[pi+1].mCoeffs[3] - mTable[pi].mCoeffs[3]; + } + + const size_t pi{CubicPhaseCount - 1}; + mTable[pi].mDeltas[0] = -mTable[pi].mCoeffs[0]; + mTable[pi].mDeltas[1] = -mTable[pi].mCoeffs[1]; + mTable[pi].mDeltas[2] = 1.0f - mTable[pi].mCoeffs[2]; + mTable[pi].mDeltas[3] = -mTable[pi].mCoeffs[3]; + } + + constexpr const CubicCoefficients *getTable() const noexcept { return mTable; } +}; + +constexpr SplineFilterArray SplineFilter{}; + +} // namespace + +const CubicTable gCubicSpline{SplineFilter.getTable()}; diff --git a/core/cubic_tables.h b/core/cubic_tables.h new file mode 100644 index 00000000..297aa89e --- /dev/null +++ b/core/cubic_tables.h @@ -0,0 +1,16 @@ +#ifndef CORE_CUBIC_TABLES_H +#define CORE_CUBIC_TABLES_H + +#include "cubic_defs.h" + + +struct CubicTable { + const CubicCoefficients *Tab; +}; + +/* A Catmull-Rom spline. The spline passes through the center two samples, + * ensuring no discontinuity while moving through a series of samples. + */ +extern const CubicTable gCubicSpline; + +#endif /* CORE_CUBIC_TABLES_H */ diff --git a/core/mixer/defs.h b/core/mixer/defs.h index 80d9fc7f..74a474fe 100644 --- a/core/mixer/defs.h +++ b/core/mixer/defs.h @@ -8,6 +8,7 @@ #include "core/bufferline.h" #include "core/resampler_limits.h" +struct CubicCoefficients; struct HrtfChannelState; struct HrtfFilter; struct MixHrtfFilter; @@ -51,7 +52,15 @@ struct BsincState { const float *filter; }; +struct CubicState { + /* Filter coefficients, and coefficient deltas. Starting at phase index 0, + * each subsequent phase index follows contiguously. + */ + const CubicCoefficients *filter; +}; + union InterpState { + CubicState cubic; BsincState bsinc; }; diff --git a/core/mixer/mixer_c.cpp b/core/mixer/mixer_c.cpp index 9ac2a9c4..24b9f0b7 100644 --- a/core/mixer/mixer_c.cpp +++ b/core/mixer/mixer_c.cpp @@ -5,7 +5,8 @@ #include #include "alnumeric.h" -#include "core/bsinc_tables.h" +#include "core/bsinc_defs.h" +#include "core/cubic_defs.h" #include "defs.h" #include "hrtfbase.h" @@ -20,23 +21,39 @@ struct FastBSincTag; namespace { -constexpr uint FracPhaseBitDiff{MixerFracBits - BSincPhaseBits}; -constexpr uint FracPhaseDiffOne{1 << FracPhaseBitDiff}; +constexpr uint BsincPhaseBitDiff{MixerFracBits - BSincPhaseBits}; +constexpr uint BsincPhaseDiffOne{1 << BsincPhaseBitDiff}; + +constexpr uint CubicPhaseBitDiff{MixerFracBits - CubicPhaseBits}; +constexpr uint CubicPhaseDiffOne{1 << CubicPhaseBitDiff}; +constexpr uint CubicPhaseDiffMask{CubicPhaseDiffOne - 1u}; inline float do_point(const InterpState&, const float *RESTRICT vals, const uint) { return vals[0]; } inline float do_lerp(const InterpState&, const float *RESTRICT vals, const uint frac) { return lerpf(vals[0], vals[1], static_cast(frac)*(1.0f/MixerFracOne)); } -inline float do_cubic(const InterpState&, const float *RESTRICT vals, const uint frac) -{ return cubic(vals[0], vals[1], vals[2], vals[3], static_cast(frac)*(1.0f/MixerFracOne)); } +inline float do_cubic(const InterpState &istate, const float *RESTRICT vals, const uint frac) +{ + /* Calculate the phase index and factor. */ + const uint pi{frac >> CubicPhaseBitDiff}; + const float pf{static_cast(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; + + const CubicCoefficients *RESTRICT filter = al::assume_aligned<16>(istate.cubic.filter + pi); + + // Apply the phase interpolated filter. + return (filter->mCoeffs[0] + pf*filter->mDeltas[0]) * vals[0] + + (filter->mCoeffs[1] + pf*filter->mDeltas[1]) * vals[1] + + (filter->mCoeffs[2] + pf*filter->mDeltas[2]) * vals[2] + + (filter->mCoeffs[3] + pf*filter->mDeltas[3]) * vals[3]; +} inline float do_bsinc(const InterpState &istate, const float *RESTRICT vals, const uint frac) { const size_t m{istate.bsinc.m}; ASSUME(m > 0); // Calculate the phase index and factor. - const uint pi{frac >> FracPhaseBitDiff}; - const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; + const uint pi{frac >> BsincPhaseBitDiff}; + const float pf{static_cast(frac & (BsincPhaseDiffOne-1)) * (1.0f/BsincPhaseDiffOne)}; const float *RESTRICT fil{istate.bsinc.filter + m*pi*2}; const float *RESTRICT phd{fil + m}; @@ -55,8 +72,8 @@ inline float do_fastbsinc(const InterpState &istate, const float *RESTRICT vals, ASSUME(m > 0); // Calculate the phase index and factor. - const uint pi{frac >> FracPhaseBitDiff}; - const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; + const uint pi{frac >> BsincPhaseBitDiff}; + const float pf{static_cast(frac & (BsincPhaseDiffOne-1)) * (1.0f/BsincPhaseDiffOne)}; const float *RESTRICT fil{istate.bsinc.filter + m*pi*2}; const float *RESTRICT phd{fil + m}; diff --git a/core/mixer/mixer_neon.cpp b/core/mixer/mixer_neon.cpp index 5a91da60..e636ac74 100644 --- a/core/mixer/mixer_neon.cpp +++ b/core/mixer/mixer_neon.cpp @@ -7,11 +7,13 @@ #include "alnumeric.h" #include "core/bsinc_defs.h" +#include "core/cubic_defs.h" #include "defs.h" #include "hrtfbase.h" struct NEONTag; struct LerpTag; +struct CubicTag; struct BSincTag; struct FastBSincTag; @@ -22,6 +24,14 @@ struct FastBSincTag; namespace { +constexpr uint BSincPhaseBitDiff{MixerFracBits - BSincPhaseBits}; +constexpr uint BSincPhaseDiffOne{1 << BSincPhaseBitDiff}; +constexpr uint BSincPhaseDiffMask{BSincPhaseDiffOne - 1u}; + +constexpr uint CubicPhaseBitDiff{MixerFracBits - CubicPhaseBits}; +constexpr uint CubicPhaseDiffOne{1 << CubicPhaseBitDiff}; +constexpr uint CubicPhaseDiffMask{CubicPhaseDiffOne - 1u}; + inline float32x4_t set_f4(float l0, float l1, float l2, float l3) { float32x4_t ret{vmovq_n_f32(l0)}; @@ -31,9 +41,6 @@ inline float32x4_t set_f4(float l0, float l1, float l2, float l3) return ret; } -constexpr uint FracPhaseBitDiff{MixerFracBits - BSincPhaseBits}; -constexpr uint FracPhaseDiffOne{1 << FracPhaseBitDiff}; - inline void ApplyCoeffs(float2 *RESTRICT Values, const size_t IrSize, const ConstHrirSpan Coeffs, const float left, const float right) { @@ -183,6 +190,37 @@ float *Resample_(const InterpState*, float *RESTRICT src, uint return dst.data(); } +template<> +float *Resample_(const InterpState *state, float *RESTRICT src, uint frac, + uint increment, const al::span dst) +{ + const CubicCoefficients *RESTRICT filter = al::assume_aligned<16>(state->cubic.filter); + + src -= 1; + for(float &out_sample : dst) + { + const uint pi{frac >> CubicPhaseBitDiff}; + const float pf{static_cast(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; + const float32x4_t pf4{vdupq_n_f32(pf)}; + + /* Apply the phase interpolated filter. */ + + /* f = fil + pf*phd */ + const float32x4_t f4 = vmlaq_f32(vld1q_f32(filter[pi].mCoeffs), pf4, + vld1q_f32(filter[pi].mDeltas)); + /* r = f*src */ + float32x4_t r4{vmulq_f32(f4, vld1q_f32(src))}; + + r4 = vaddq_f32(r4, vrev64q_f32(r4)); + out_sample = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + + frac += increment; + src += frac>>MixerFracBits; + frac &= MixerFracMask; + } + return dst.data(); +} + template<> float *Resample_(const InterpState *state, float *RESTRICT src, uint frac, uint increment, const al::span dst) @@ -196,8 +234,8 @@ float *Resample_(const InterpState *state, float *RESTRICT src for(float &out_sample : dst) { // Calculate the phase index and factor. - const uint pi{frac >> FracPhaseBitDiff}; - const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; + const uint pi{frac >> BSincPhaseBitDiff}; + const float pf{static_cast(frac&BSincPhaseDiffMask) * (1.0f/BSincPhaseDiffOne)}; // Apply the scale and phase interpolated filter. float32x4_t r4{vdupq_n_f32(0.0f)}; @@ -242,8 +280,8 @@ float *Resample_(const InterpState *state, float *RESTRICT for(float &out_sample : dst) { // Calculate the phase index and factor. - const uint pi{frac >> FracPhaseBitDiff}; - const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; + const uint pi{frac >> BSincPhaseBitDiff}; + const float pf{static_cast(frac&BSincPhaseDiffMask) * (1.0f/BSincPhaseDiffOne)}; // Apply the phase interpolated filter. float32x4_t r4{vdupq_n_f32(0.0f)}; diff --git a/core/mixer/mixer_sse.cpp b/core/mixer/mixer_sse.cpp index 1b0d1386..4a31a0f1 100644 --- a/core/mixer/mixer_sse.cpp +++ b/core/mixer/mixer_sse.cpp @@ -7,10 +7,12 @@ #include "alnumeric.h" #include "core/bsinc_defs.h" +#include "core/cubic_defs.h" #include "defs.h" #include "hrtfbase.h" struct SSETag; +struct CubicTag; struct BSincTag; struct FastBSincTag; @@ -21,8 +23,13 @@ struct FastBSincTag; namespace { -constexpr uint FracPhaseBitDiff{MixerFracBits - BSincPhaseBits}; -constexpr uint FracPhaseDiffOne{1 << FracPhaseBitDiff}; +constexpr uint BSincPhaseBitDiff{MixerFracBits - BSincPhaseBits}; +constexpr uint BSincPhaseDiffOne{1 << BSincPhaseBitDiff}; +constexpr uint BSincPhaseDiffMask{BSincPhaseDiffOne - 1u}; + +constexpr uint CubicPhaseBitDiff{MixerFracBits - CubicPhaseBits}; +constexpr uint CubicPhaseDiffOne{1 << CubicPhaseBitDiff}; +constexpr uint CubicPhaseDiffMask{CubicPhaseDiffOne - 1u}; #define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z)) @@ -146,6 +153,38 @@ force_inline void MixLine(const al::span InSamples, float *RESTRICT } // namespace +template<> +float *Resample_(const InterpState *state, float *RESTRICT src, uint frac, + uint increment, const al::span dst) +{ + const CubicCoefficients *RESTRICT filter = al::assume_aligned<16>(state->cubic.filter); + + src -= 1; + for(float &out_sample : dst) + { + const uint pi{frac >> CubicPhaseBitDiff}; + const float pf{static_cast(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; + const __m128 pf4{_mm_set1_ps(pf)}; + + /* Apply the phase interpolated filter. */ + + /* f = fil + pf*phd */ + const __m128 f4 = MLA4(_mm_load_ps(filter[pi].mCoeffs), pf4, + _mm_load_ps(filter[pi].mDeltas)); + /* r = f*src */ + __m128 r4{_mm_mul_ps(f4, _mm_loadu_ps(src))}; + + r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + out_sample = _mm_cvtss_f32(r4); + + frac += increment; + src += frac>>MixerFracBits; + frac &= MixerFracMask; + } + return dst.data(); +} + template<> float *Resample_(const InterpState *state, float *RESTRICT src, uint frac, uint increment, const al::span dst) @@ -159,8 +198,8 @@ float *Resample_(const InterpState *state, float *RESTRICT src, for(float &out_sample : dst) { // Calculate the phase index and factor. - const uint pi{frac >> FracPhaseBitDiff}; - const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; + const uint pi{frac >> BSincPhaseBitDiff}; + const float pf{static_cast(frac&BSincPhaseDiffMask) * (1.0f/BSincPhaseDiffOne)}; // Apply the scale and phase interpolated filter. __m128 r4{_mm_setzero_ps()}; @@ -206,8 +245,8 @@ float *Resample_(const InterpState *state, float *RESTRICT for(float &out_sample : dst) { // Calculate the phase index and factor. - const uint pi{frac >> FracPhaseBitDiff}; - const float pf{static_cast(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)}; + const uint pi{frac >> BSincPhaseBitDiff}; + const float pf{static_cast(frac&BSincPhaseDiffMask) * (1.0f/BSincPhaseDiffOne)}; // Apply the phase interpolated filter. __m128 r4{_mm_setzero_ps()}; -- cgit v1.2.3 From 8695aa49a5f5d62782217d2380b037d18e7a7460 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 8 Feb 2023 23:06:00 -0800 Subject: Check for the existence of the file that's depended on --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index c37fe0b6..92cc138a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1206,7 +1206,7 @@ set(BACKENDS "${BACKENDS} Null") find_package(Git) -if(ALSOFT_UPDATE_BUILD_VERSION AND GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.git") +if(ALSOFT_UPDATE_BUILD_VERSION AND GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.git/index") # Get the current working branch and its latest abbreviated commit hash add_custom_command(OUTPUT "${OpenAL_BINARY_DIR}/version_witness.txt" BYPRODUCTS "${OpenAL_BINARY_DIR}/version.h" -- cgit v1.2.3 From 3ce537d474bbde645017ecbcc3255c9a2d9432fe Mon Sep 17 00:00:00 2001 From: Aoife Fey <93552084+aoife-fey@users.noreply.github.com> Date: Thu, 9 Feb 2023 14:19:43 -0700 Subject: Allow finding index file when built as a submodule (#819) --- CMakeLists.txt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 92cc138a..74eeb9fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1206,7 +1206,18 @@ set(BACKENDS "${BACKENDS} Null") find_package(Git) -if(ALSOFT_UPDATE_BUILD_VERSION AND GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.git/index") +if(ALSOFT_UPDATE_BUILD_VERSION AND GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.git") + set(GIT_DIR "${OpenAL_SOURCE_DIR}/.git") + + # Check if this is a submodule, if it is then find the .git directory + if(NOT IS_DIRECTORY "${OpenAL_SOURCE_DIR}/.git") + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE ${submodule}) + string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) + endif() + # Get the current working branch and its latest abbreviated commit hash add_custom_command(OUTPUT "${OpenAL_BINARY_DIR}/version_witness.txt" BYPRODUCTS "${OpenAL_BINARY_DIR}/version.h" @@ -1216,7 +1227,7 @@ if(ALSOFT_UPDATE_BUILD_VERSION AND GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.g COMMAND ${CMAKE_COMMAND} -E touch "${OpenAL_BINARY_DIR}/version_witness.txt" WORKING_DIRECTORY "${OpenAL_SOURCE_DIR}" MAIN_DEPENDENCY "${OpenAL_SOURCE_DIR}/version.h.in" - DEPENDS "${OpenAL_SOURCE_DIR}/.git/index" "${OpenAL_SOURCE_DIR}/version.cmake" + DEPENDS "${GIT_DIR}/index" "${OpenAL_SOURCE_DIR}/version.cmake" VERBATIM ) -- cgit v1.2.3 From d0c28c652f10856f3f1eadbbe8f362be6224355d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 14 Feb 2023 00:00:46 -0800 Subject: Support IMA4 ADPCM as a mixing voice format --- CMakeLists.txt | 1 + al/buffer.cpp | 14 ++-- al/buffer.h | 1 - al/source.cpp | 27 ++++--- alc/effects/convolution.cpp | 4 + common/alnumeric.h | 21 ++++-- common/alspan.h | 8 +- common/altraits.h | 14 ++++ core/buffer_storage.cpp | 1 + core/buffer_storage.h | 8 ++ core/voice.cpp | 175 ++++++++++++++++++++++++++++++++++++-------- core/voice.h | 6 +- 12 files changed, 215 insertions(+), 65 deletions(-) create mode 100644 common/altraits.h (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 74eeb9fd..9ab92b7b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -612,6 +612,7 @@ set(COMMON_OBJS common/alspan.h common/alstring.cpp common/alstring.h + common/altraits.h common/atomic.h common/comptr.h common/dynload.cpp diff --git a/al/buffer.cpp b/al/buffer.cpp index ff416fda..bc007219 100644 --- a/al/buffer.cpp +++ b/al/buffer.cpp @@ -571,7 +571,7 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, /* Can only preserve data with the same format and alignment. */ if(ALBuf->mChannels != *DstChannels || ALBuf->OriginalType != SrcType) [[unlikely]] return context->setError(AL_INVALID_VALUE, "Preserving data of mismatched format"); - if(ALBuf->OriginalAlign != align) [[unlikely]] + if(ALBuf->mBlockAlign != align) [[unlikely]] return context->setError(AL_INVALID_VALUE, "Preserving data of mismatched alignment"); if(ALBuf->mAmbiOrder != ambiorder) [[unlikely]] return context->setError(AL_INVALID_VALUE, "Preserving data of mismatched order"); @@ -641,7 +641,7 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, if(SrcData != nullptr && !ALBuf->mData.empty()) Convert_int16_ima4(reinterpret_cast(ALBuf->mData.data()), SrcData, NumChannels, frames, align); - ALBuf->OriginalAlign = align; + ALBuf->mBlockAlign = align; } else if(SrcType == UserFmtMSADPCM) { @@ -649,14 +649,14 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, if(SrcData != nullptr && !ALBuf->mData.empty()) Convert_int16_msadpcm(reinterpret_cast(ALBuf->mData.data()), SrcData, NumChannels, frames, align); - ALBuf->OriginalAlign = align; + ALBuf->mBlockAlign = align; } else { assert(DstType.has_value()); if(SrcData != nullptr && !ALBuf->mData.empty()) std::copy_n(SrcData, frames*FrameSize, ALBuf->mData.begin()); - ALBuf->OriginalAlign = 1; + ALBuf->mBlockAlign = 1; } ALBuf->OriginalSize = size; ALBuf->OriginalType = SrcType; @@ -722,7 +722,7 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALBuf->OriginalType = SrcType; ALBuf->OriginalSize = 0; - ALBuf->OriginalAlign = 1; + ALBuf->mBlockAlign = 1; ALBuf->Access = 0; ALBuf->mSampleRate = static_cast(freq); @@ -1105,10 +1105,10 @@ START_API_FUNC else if(al::to_underlying(usrfmt->channels) != al::to_underlying(albuf->mChannels) || usrfmt->type != albuf->OriginalType) [[unlikely]] context->setError(AL_INVALID_ENUM, "Unpacking data with mismatched format"); - else if(align != albuf->OriginalAlign) [[unlikely]] + else if(align != albuf->mBlockAlign) [[unlikely]] context->setError(AL_INVALID_VALUE, "Unpacking data with alignment %u does not match original alignment %u", align, - albuf->OriginalAlign); + albuf->mBlockAlign); else if(albuf->isBFormat() && albuf->UnpackAmbiOrder != albuf->mAmbiOrder) [[unlikely]] context->setError(AL_INVALID_VALUE, "Unpacking data with mismatched ambisonic order"); else if(albuf->MappedAccess != 0) [[unlikely]] diff --git a/al/buffer.h b/al/buffer.h index 7ded83bd..322b918f 100644 --- a/al/buffer.h +++ b/al/buffer.h @@ -51,7 +51,6 @@ struct ALbuffer : public BufferStorage { UserFmtType OriginalType{UserFmtShort}; ALuint OriginalSize{0}; - ALuint OriginalAlign{0}; ALuint UnpackAlign{0}; ALuint PackAlign{0}; diff --git a/al/source.cpp b/al/source.cpp index 7dc5df37..7db175ef 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -334,8 +334,8 @@ double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) case AL_BYTE_OFFSET: if(BufferFmt->OriginalType == UserFmtIMA4) { - ALuint FrameBlockSize{BufferFmt->OriginalAlign}; - ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4}; + ALuint FrameBlockSize{BufferFmt->mBlockAlign}; + ALuint align{(BufferFmt->mBlockAlign-1)/2 + 4}; ALuint BlockSize{align * BufferFmt->channelsFromFmt()}; /* Round down to nearest ADPCM block */ @@ -343,7 +343,7 @@ double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) } else if(BufferFmt->OriginalType == UserFmtMSADPCM) { - ALuint FrameBlockSize{BufferFmt->OriginalAlign}; + ALuint FrameBlockSize{BufferFmt->mBlockAlign}; ALuint align{(FrameBlockSize-2)/2 + 7}; ALuint BlockSize{align * BufferFmt->channelsFromFmt()}; @@ -390,8 +390,8 @@ double GetSourceLength(const ALsource *source, ALenum name) case AL_BYTE_LENGTH_SOFT: if(BufferFmt->OriginalType == UserFmtIMA4) { - ALuint FrameBlockSize{BufferFmt->OriginalAlign}; - ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4}; + ALuint FrameBlockSize{BufferFmt->mBlockAlign}; + ALuint align{(BufferFmt->mBlockAlign-1)/2 + 4}; ALuint BlockSize{align * BufferFmt->channelsFromFmt()}; /* Round down to nearest ADPCM block */ @@ -399,7 +399,7 @@ double GetSourceLength(const ALsource *source, ALenum name) } else if(BufferFmt->OriginalType == UserFmtMSADPCM) { - ALuint FrameBlockSize{BufferFmt->OriginalAlign}; + ALuint FrameBlockSize{BufferFmt->mBlockAlign}; ALuint align{(FrameBlockSize-2)/2 + 7}; ALuint BlockSize{align * BufferFmt->channelsFromFmt()}; @@ -474,15 +474,15 @@ al::optional GetSampleOffset(al::deque &BufferList, /* Determine the ByteOffset (and ensure it is block aligned) */ if(BufferFmt->OriginalType == UserFmtIMA4) { - const ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4}; + const ALuint align{(BufferFmt->mBlockAlign-1)/2 + 4}; Offset = std::floor(Offset / align / BufferFmt->channelsFromFmt()); - Offset *= BufferFmt->OriginalAlign; + Offset *= BufferFmt->mBlockAlign; } else if(BufferFmt->OriginalType == UserFmtMSADPCM) { - const ALuint align{(BufferFmt->OriginalAlign-2)/2 + 7}; + const ALuint align{(BufferFmt->mBlockAlign-2)/2 + 7}; Offset = std::floor(Offset / align / BufferFmt->channelsFromFmt()); - Offset *= BufferFmt->OriginalAlign; + Offset *= BufferFmt->mBlockAlign; } else Offset = std::floor(Offset / BufferFmt->channelsFromFmt()); @@ -530,14 +530,15 @@ void InitVoice(Voice *voice, ALsource *source, ALbufferQueueItem *BufferList, AL FmtSuperStereo : buffer->mChannels; voice->mFmtType = buffer->mType; voice->mFrameStep = buffer->channelsFromFmt(); - voice->mFrameSize = buffer->frameSizeFromFmt(); + voice->mBytesPerBlock = buffer->blockSizeFromFmt(); + voice->mSamplesPerBlock = buffer->mBlockAlign; voice->mAmbiLayout = IsUHJ(voice->mFmtChannels) ? AmbiLayout::FuMa : buffer->mAmbiLayout; voice->mAmbiScaling = IsUHJ(voice->mFmtChannels) ? AmbiScaling::UHJ : buffer->mAmbiScaling; voice->mAmbiOrder = (voice->mFmtChannels == FmtSuperStereo) ? 1 : buffer->mAmbiOrder; if(buffer->mCallback) voice->mFlags.set(VoiceIsCallback); else if(source->SourceType == AL_STATIC) voice->mFlags.set(VoiceIsStatic); - voice->mNumCallbackSamples = 0; + voice->mNumCallbackBlocks = 0; voice->prepare(device); @@ -1536,6 +1537,7 @@ try { newlist.emplace_back(); newlist.back().mCallback = buffer->mCallback; newlist.back().mUserData = buffer->mUserData; + newlist.back().mBlockAlign = buffer->mBlockAlign; newlist.back().mSampleLen = buffer->mSampleLen; newlist.back().mLoopStart = buffer->mLoopStart; newlist.back().mLoopEnd = buffer->mLoopEnd; @@ -3604,6 +3606,7 @@ START_API_FUNC BufferList = &item; } if(!buffer) continue; + BufferList->mBlockAlign = buffer->mBlockAlign; BufferList->mSampleLen = buffer->mSampleLen; BufferList->mLoopEnd = buffer->mSampleLen; BufferList->mSamples = buffer->mData.data(); diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index e88fb0d0..1c5c3691 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -84,6 +84,10 @@ void LoadSamples(float *RESTRICT dst, const al::byte *src, const size_t srcstep, HANDLE_FMT(FmtDouble); HANDLE_FMT(FmtMulaw); HANDLE_FMT(FmtAlaw); + /* FIXME: Handle ADPCM decoding here. */ + case FmtIMA4: + std::fill_n(dst, samples, 0.0f); + break; } #undef HANDLE_FMT } diff --git a/common/alnumeric.h b/common/alnumeric.h index 13e61645..a426763f 100644 --- a/common/alnumeric.h +++ b/common/alnumeric.h @@ -12,6 +12,7 @@ #include #endif +#include "altraits.h" #include "opthelpers.h" @@ -97,12 +98,20 @@ inline uint32_t NextPowerOf2(uint32_t value) noexcept return value+1; } -/** Round up a value to the next multiple. */ -inline size_t RoundUp(size_t value, size_t r) noexcept -{ - value += r-1; - return value - (value%r); -} +/** + * If the value is not already a multiple of r, round down to the next + * multiple. + */ +template +constexpr T RoundDown(T value, al::type_identity_t r) noexcept +{ return value - (value%r); } + +/** + * If the value is not already a multiple of r, round up to the next multiple. + */ +template +constexpr T RoundUp(T value, al::type_identity_t r) noexcept +{ return RoundDown(value + r-1, r); } /** diff --git a/common/alspan.h b/common/alspan.h index 519f22e4..1d6cdfe5 100644 --- a/common/alspan.h +++ b/common/alspan.h @@ -8,6 +8,7 @@ #include #include "almalloc.h" +#include "altraits.h" namespace al { @@ -37,13 +38,6 @@ constexpr const T* data(std::initializer_list list) noexcept { return list.begin(); } -template -struct type_identity { using type = T; }; - -template -using type_identity_t = typename type_identity::type; - - constexpr size_t dynamic_extent{static_cast(-1)}; template diff --git a/common/altraits.h b/common/altraits.h new file mode 100644 index 00000000..7ce0422e --- /dev/null +++ b/common/altraits.h @@ -0,0 +1,14 @@ +#ifndef COMMON_ALTRAITS_H +#define COMMON_ALTRAITS_H + +namespace al { + +template +struct type_identity { using type = T; }; + +template +using type_identity_t = typename type_identity::type; + +} // namespace al + +#endif /* COMMON_ALTRAITS_H */ diff --git a/core/buffer_storage.cpp b/core/buffer_storage.cpp index 1c80e7ef..1e826bff 100644 --- a/core/buffer_storage.cpp +++ b/core/buffer_storage.cpp @@ -16,6 +16,7 @@ uint BytesFromFmt(FmtType type) noexcept case FmtDouble: return sizeof(double); case FmtMulaw: return sizeof(uint8_t); case FmtAlaw: return sizeof(uint8_t); + case FmtIMA4: break; } return 0; } diff --git a/core/buffer_storage.h b/core/buffer_storage.h index ec934681..a4d1b289 100644 --- a/core/buffer_storage.h +++ b/core/buffer_storage.h @@ -18,6 +18,7 @@ enum FmtType : unsigned char { FmtDouble, FmtMulaw, FmtAlaw, + FmtIMA4, }; enum FmtChannels : unsigned char { FmtMono, @@ -83,6 +84,7 @@ struct BufferStorage { FmtChannels mChannels{FmtMono}; FmtType mType{FmtShort}; uint mSampleLen{0u}; + uint mBlockAlign{0u}; AmbiLayout mAmbiLayout{AmbiLayout::FuMa}; AmbiScaling mAmbiScaling{AmbiScaling::FuMa}; @@ -93,6 +95,12 @@ struct BufferStorage { { return ChannelsFromFmt(mChannels, mAmbiOrder); } inline uint frameSizeFromFmt() const noexcept { return channelsFromFmt() * bytesFromFmt(); } + inline uint blockSizeFromFmt() const noexcept + { + if(mType == FmtIMA4) return ((mBlockAlign-1)/2 + 4) * channelsFromFmt(); + return frameSizeFromFmt(); + }; + inline bool isBFormat() const noexcept { return IsBFormat(mChannels); } }; diff --git a/core/voice.cpp b/core/voice.cpp index 4ca62a02..f6954cbe 100644 --- a/core/voice.cpp +++ b/core/voice.cpp @@ -178,6 +178,32 @@ void Voice::InitMixer(al::optional resampler) namespace { +/* IMA ADPCM Stepsize table */ +constexpr int IMAStep_size[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, + 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, + 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, + 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, + 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, + 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, + 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,10442, + 11487,12635,13899,15289,16818,18500,20350,22358,24633,27086,29794, + 32767 +}; + +/* IMA4 ADPCM Codeword decode table */ +constexpr int IMA4Codeword[16] = { + 1, 3, 5, 7, 9, 11, 13, 15, + -1,-3,-5,-7,-9,-11,-13,-15, +}; + +/* IMA4 ADPCM Step index adjust decode table */ +constexpr int IMA4Index_adjust[16] = { + -1,-1,-1,-1, 2, 4, 6, 8, + -1,-1,-1,-1, 2, 4, 6, 8 +}; + + void SendSourceStoppedEvent(ContextBase *context, uint id) { RingBuffer *ring{context->mAsyncEvents.get()}; @@ -221,8 +247,9 @@ const float *DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, float *ds template -inline void LoadSamples(float *dstSamples, const al::byte *src, const size_t srcChan, - const size_t srcOffset, const size_t srcStep, const size_t samples) noexcept +inline void LoadSamples(float *RESTRICT dstSamples, const al::byte *src, const size_t srcChan, + const size_t srcOffset, const size_t srcStep, const size_t /*samplesPerBlock*/, + const size_t samples) noexcept { constexpr size_t sampleSize{sizeof(typename al::FmtTypeTraits::Type)}; auto s = src + (srcOffset*srcStep + srcChan)*sampleSize; @@ -230,12 +257,91 @@ inline void LoadSamples(float *dstSamples, const al::byte *src, const size_t src al::LoadSampleArray(dstSamples, s, srcStep, samples); } +template<> +inline void LoadSamples(float *RESTRICT dstSamples, const al::byte *src, + const size_t srcChan, const size_t srcOffset, const size_t srcStep, + const size_t samplesPerBlock, const size_t samples) noexcept +{ + const size_t blockBytes{((samplesPerBlock-1)/2 + 4)*srcStep}; + + /* Skip to the ADPCM block containing the srcOffset sample. */ + src += srcOffset/samplesPerBlock*blockBytes; + /* Calculate how many samples need to be skipped in the block. */ + size_t skip{srcOffset % samplesPerBlock}; + + /* NOTE: This could probably be optimized better. */ + size_t wrote{0}; + do { + /* Each IMA4 block starts with a signed 16-bit sample, and a signed + * 16-bit table index. The table index needs to be clamped. + */ + int sample{src[srcChan*4] | (src[srcChan*4 + 1] << 8)}; + int index{src[srcChan*4 + 2] | (src[srcChan*4 + 3] << 8)}; + + sample = (sample^0x8000) - 32768; + index = clampi((index^0x8000) - 32768, 0, al::size(IMAStep_size)-1); + + if(!skip) [[likely]] + { + dstSamples[++wrote] = static_cast(sample) / 32768.0f; + if(wrote == samples) return; + } + else + --skip; + + int tempsamples[8]{}; + const al::byte *nibbleData{src + (srcStep+srcChan)*4}; + for(size_t i{1};i < samplesPerBlock;i+=8) + { + /* The rest of the block is arranged as a series of nibbles, with 4 + * bytes per channel interleaved. So we can decode a series of 8 + * samples at once from these next 4 bytes. + */ + uint code{uint{nibbleData[0]} | (uint{nibbleData[1]} << 8) + | (uint{nibbleData[2]} << 16) | (uint{nibbleData[3]} << 24)}; + for(size_t j{0};j < 8;++j) + { + const uint nibble{code & 0xf}; + code >>= 4; + + sample += IMA4Codeword[nibble] * IMAStep_size[index] / 8; + sample = clampi(sample, -32768, 32767); + tempsamples[j] = sample; + + index += IMA4Index_adjust[nibble]; + index = clampi(index, 0, al::size(IMAStep_size)-1); + } + nibbleData += 4*srcStep; + + /* If we're skipping these 8 samples, go on to the next set. They + * still need to be decoded to update the predictor state for the + * next set. + */ + if(skip >= 8) + { + skip -= 8; + continue; + } + + const size_t todo{minz(8-skip, samples-wrote)}; + for(size_t j{0};j < todo;++j) + dstSamples[++wrote] = static_cast(tempsamples[j+skip]) / 32768.0f; + if(wrote == samples) + return; + skip = 0; + } + + src += blockBytes; + } while(1); +} + void LoadSamples(float *dstSamples, const al::byte *src, const size_t srcChan, - const size_t srcOffset, const FmtType srcType, const size_t srcStep, const size_t samples) - noexcept + const size_t srcOffset, const FmtType srcType, const size_t srcStep, + const size_t samplesPerBlock, const size_t samples) noexcept { #define HANDLE_FMT(T) case T: \ - LoadSamples(dstSamples, src, srcChan, srcOffset, srcStep, samples); \ + LoadSamples(dstSamples, src, srcChan, srcOffset, srcStep, \ + samplesPerBlock, samples); \ break switch(srcType) @@ -246,6 +352,7 @@ void LoadSamples(float *dstSamples, const al::byte *src, const size_t srcChan, HANDLE_FMT(FmtDouble); HANDLE_FMT(FmtMulaw); HANDLE_FMT(FmtAlaw); + HANDLE_FMT(FmtIMA4); } #undef HANDLE_FMT } @@ -263,7 +370,7 @@ void LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, const size_t buffer_remaining{buffer->mSampleLen - dataPosInt}; const size_t remaining{minz(samplesToLoad-samplesLoaded, buffer_remaining)}; LoadSamples(voiceSamples+samplesLoaded, buffer->mSamples, srcChannel, dataPosInt, - sampleType, srcStep, remaining); + sampleType, srcStep, buffer->mBlockAlign, remaining); samplesLoaded += remaining; } @@ -285,7 +392,7 @@ void LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, /* Load what's left of this loop iteration */ const size_t remaining{minz(samplesToLoad-samplesLoaded, loopEnd-dataPosInt)}; LoadSamples(voiceSamples+samplesLoaded, buffer->mSamples, srcChannel, intPos, sampleType, - srcStep, remaining); + srcStep, buffer->mBlockAlign, remaining); samplesLoaded += remaining; /* Load repeats of the loop to fill the buffer. */ @@ -293,7 +400,7 @@ void LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, while(const size_t toFill{minz(samplesToLoad - samplesLoaded, loopSize)}) { LoadSamples(voiceSamples+samplesLoaded, buffer->mSamples, srcChannel, loopStart, - sampleType, srcStep, toFill); + sampleType, srcStep, buffer->mBlockAlign, toFill); samplesLoaded += toFill; } } @@ -308,7 +415,7 @@ void LoadBufferCallback(VoiceBufferItem *buffer, const size_t dataPosInt, { const size_t remaining{minz(samplesToLoad-samplesLoaded, numCallbackSamples-dataPosInt)}; LoadSamples(voiceSamples+samplesLoaded, buffer->mSamples, srcChannel, dataPosInt, - sampleType, srcStep, remaining); + sampleType, srcStep, buffer->mBlockAlign, remaining); samplesLoaded += remaining; } @@ -337,7 +444,7 @@ void LoadBufferQueue(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, const size_t remaining{minz(samplesToLoad-samplesLoaded, buffer->mSampleLen-dataPosInt)}; LoadSamples(voiceSamples+samplesLoaded, buffer->mSamples, srcChannel, dataPosInt, - sampleType, srcStep, remaining); + sampleType, srcStep, buffer->mBlockAlign, remaining); samplesLoaded += remaining; if(samplesLoaded == samplesToLoad) @@ -527,7 +634,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi const uint samplesToLoad{samplesToMix + mDecoderPadding}; /* Get a span of pointers to hold the floating point, deinterlaced, - * resampled buffer data. + * resampled buffer data to be mixed. */ std::array SamplePointers; const al::span MixingSamples{SamplePointers.data(), mChans.size()}; @@ -544,6 +651,12 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi const al::span dst) { std::copy_n(src, dst.size(), dst.begin()); }} : mResampler}; + /* For callback buffers, this is the sample offset for the start of the + * buffer data. This is needed with compressed formats to track how many + * samples into a block we're starting from. + */ + const uint callbackBase{RoundDown(static_cast(maxi(DataPosInt, 0)), mSamplesPerBlock)}; + /* UHJ2 and SuperStereo only have 2 buffer channels, but 3 mixing channels * (3rd channel is generated from decoding). */ @@ -557,7 +670,6 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi const auto prevSamples = al::as_span(mPrevSamples[chan]); const auto resampleBuffer = std::copy(prevSamples.cbegin(), prevSamples.cend(), Device->mResampleData.begin()) - MaxResamplerEdge; - const uint callbackBase{static_cast(maxi(DataPosInt, 0))}; int intPos{DataPosInt}; uint fracPos{DataPosFrac}; @@ -567,7 +679,8 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi for(uint samplesLoaded{0};samplesLoaded < samplesToLoad;) { /* Calculate the number of dst samples that can be loaded this - * iteration, given the available resampler buffer size. + * iteration, given the available resampler buffer size, and the + * number of src samples that are needed to load it. */ auto calc_buffer_sizes = [fracPos,increment](uint dstBufferSize) { @@ -599,7 +712,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi { /* Some resamplers require the destination being 16-byte * aligned, so limit to a multiple of 4 samples to maintain - * alignment. + * alignment if we need to do another iteration after this. */ dstBufferSize = static_cast(dataSize64) & ~3u; } @@ -654,11 +767,12 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi else if(mFlags.test(VoiceIsCallback)) { const size_t bufferOffset{uintPos - callbackBase}; - const size_t getTotal{bufferOffset + srcBufferSize - srcSampleDelay}; - if(!mFlags.test(VoiceCallbackStopped) && getTotal > mNumCallbackSamples) + const size_t needSamples{bufferOffset + srcBufferSize - srcSampleDelay}; + const size_t needBlocks{(needSamples + mSamplesPerBlock-1) / mSamplesPerBlock}; + if(!mFlags.test(VoiceCallbackStopped) && needBlocks > mNumCallbackBlocks) { - const size_t byteOffset{mNumCallbackSamples*mFrameSize}; - const size_t needBytes{getTotal*mFrameSize - byteOffset}; + const size_t byteOffset{mNumCallbackBlocks*mBytesPerBlock}; + const size_t needBytes{(needBlocks-mNumCallbackBlocks)*mBytesPerBlock}; const int gotBytes{BufferListItem->mCallback(BufferListItem->mUserData, &BufferListItem->mSamples[byteOffset], static_cast(needBytes))}; @@ -667,14 +781,14 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi else if(static_cast(gotBytes) < needBytes) { mFlags.set(VoiceCallbackStopped); - mNumCallbackSamples += static_cast(gotBytes) / mFrameSize; + mNumCallbackBlocks += static_cast(gotBytes) / mBytesPerBlock; } else - mNumCallbackSamples = static_cast(getTotal); + mNumCallbackBlocks = static_cast(needBlocks); } - LoadBufferCallback(BufferListItem, bufferOffset, mNumCallbackSamples, - mFmtType, chan, mFrameStep, srcSampleDelay, srcBufferSize, - al::to_address(resampleBuffer)); + const size_t numSamples{uint{mNumCallbackBlocks} * mSamplesPerBlock}; + LoadBufferCallback(BufferListItem, bufferOffset, numSamples, mFmtType, chan, + mFrameStep, srcSampleDelay, srcBufferSize, al::to_address(resampleBuffer)); } else LoadBufferQueue(BufferListItem, BufferLoopItem, uintPos, mFmtType, chan, @@ -815,13 +929,12 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi return; } - /* Update positions */ + /* Update voice positions and buffers as needed. */ DataPosFrac += increment*samplesToMix; const uint SrcSamplesDone{DataPosFrac>>MixerFracBits}; DataPosInt += SrcSamplesDone; DataPosFrac &= MixerFracMask; - /* Update voice positions and buffers as needed. */ uint buffers_done{0u}; if(BufferListItem && DataPosInt >= 0) [[likely]] { @@ -850,18 +963,20 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi else if(mFlags.test(VoiceIsCallback)) { /* Handle callback buffer source */ - if(SrcSamplesDone < mNumCallbackSamples) + const uint samplesDone{static_cast(DataPosInt) - callbackBase}; + const uint blocksDone{samplesDone / mSamplesPerBlock}; + if(blocksDone < mNumCallbackBlocks) { - const size_t byteOffset{SrcSamplesDone*mFrameSize}; - const size_t byteEnd{mNumCallbackSamples*mFrameSize}; + const size_t byteOffset{blocksDone*mBytesPerBlock}; + const size_t byteEnd{mNumCallbackBlocks*mBytesPerBlock}; al::byte *data{BufferListItem->mSamples}; std::copy(data+byteOffset, data+byteEnd, data); - mNumCallbackSamples -= SrcSamplesDone; + mNumCallbackBlocks -= blocksDone; } else { BufferListItem = nullptr; - mNumCallbackSamples = 0; + mNumCallbackBlocks = 0; } } else diff --git a/core/voice.h b/core/voice.h index cf7345a1..f197e463 100644 --- a/core/voice.h +++ b/core/voice.h @@ -100,6 +100,7 @@ struct VoiceBufferItem { CallbackType mCallback{nullptr}; void *mUserData{nullptr}; + uint mBlockAlign{0u}; uint mSampleLen{0u}; uint mLoopStart{0u}; uint mLoopEnd{0u}; @@ -219,7 +220,8 @@ struct Voice { FmtType mFmtType; uint mFrequency; uint mFrameStep; /**< In steps of the sample type size. */ - uint mFrameSize; /**< In bytes. */ + uint mBytesPerBlock; /**< Or for PCM formats, BytesPerFrame. */ + uint mSamplesPerBlock; /**< Always 1 for PCM formats. */ AmbiLayout mAmbiLayout; AmbiScaling mAmbiScaling; uint mAmbiOrder; @@ -235,7 +237,7 @@ struct Voice { InterpState mResampleState; std::bitset mFlags{}; - uint mNumCallbackSamples{0}; + uint mNumCallbackBlocks{0}; struct TargetData { int FilterType; -- cgit v1.2.3 From 097fac398470d15e9761a6441df5d47fa9a1ddfb Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 25 Feb 2023 16:38:59 -0800 Subject: Set the C/C++ standard version properties manually Setting it globally interferes with Oboe trying to use C++17 (when its built as a sub-project), which we don't require yet. --- CMakeLists.txt | 44 +++++++++++++++++++++++++++----------- utils/alsoft-config/CMakeLists.txt | 3 ++- 2 files changed, 34 insertions(+), 13 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ab92b7b..3ca29401 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,13 @@ if(NOT CMAKE_DEBUG_POSTFIX) FORCE) endif() +set(DEFAULT_TARGET_PROPS + # Require C++14. + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED TRUE + # Prefer C11, but support C99 and earlier when possible. + C_STANDARD 11) + set(CMAKE_MODULE_PATH "${OpenAL_SOURCE_DIR}/cmake") include(CheckFunctionExists) @@ -178,13 +185,6 @@ set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0) set(EXPORT_DECL "") -# Require C++14 -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED TRUE) - -# Prefer C11, but support C99 and C90 too. -set(CMAKE_C_STANDARD 11) - if(NOT WIN32) # Check if _POSIX_C_SOURCE and _XOPEN_SOURCE needs to be set for POSIX functions check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_DEFAULT) @@ -1323,7 +1323,7 @@ add_library(common STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS}) target_include_directories(common PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include) target_compile_definitions(common PRIVATE ${CPP_DEFS}) target_compile_options(common PRIVATE ${C_FLAGS}) -set_target_properties(common PROPERTIES POSITION_INDEPENDENT_CODE TRUE) +set_target_properties(common PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE) unset(HAS_ROUTER) @@ -1367,8 +1367,8 @@ else() ${OpenAL_SOURCE_DIR}/common ${OpenAL_BINARY_DIR} ) - set_target_properties(OpenAL PROPERTIES PREFIX "") - set_target_properties(OpenAL PROPERTIES OUTPUT_NAME ${LIBNAME}) + set_target_properties(OpenAL PROPERTIES ${DEFAULT_TARGET_PROPS} PREFIX "" + OUTPUT_NAME ${LIBNAME}) if(TARGET build_version) add_dependencies(OpenAL build_version) endif() @@ -1394,7 +1394,7 @@ else() # Sets framework name to soft_oal to avoid ambiguity with the system OpenAL.framework set(LIBNAME "soft_oal") if(GIT_FOUND) - EXECUTE_PROCESS(COMMAND ${GIT_EXECUTABLE} rev-list --count HEAD + execute_process(COMMAND ${GIT_EXECUTABLE} rev-list --count HEAD TIMEOUT 5 OUTPUT_VARIABLE BUNDLE_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) @@ -1439,7 +1439,8 @@ target_include_directories(${IMPL_TARGET} ${OpenAL_SOURCE_DIR}/common ) -set_target_properties(${IMPL_TARGET} PROPERTIES OUTPUT_NAME ${LIBNAME} +set_target_properties(${IMPL_TARGET} PROPERTIES ${DEFAULT_TARGET_PROPS} + OUTPUT_NAME ${LIBNAME} VERSION ${LIB_VERSION} SOVERSION ${LIB_MAJOR_VERSION} ) @@ -1570,6 +1571,7 @@ if(ALSOFT_UTILS) target_include_directories(openal-info PRIVATE ${OpenAL_SOURCE_DIR}/common) target_compile_options(openal-info PRIVATE ${C_FLAGS}) target_link_libraries(openal-info PRIVATE ${LINKER_FLAGS} OpenAL ${UNICODE_FLAG}) + set_target_properties(openal-info PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} openal-info) endif() @@ -1582,6 +1584,7 @@ if(ALSOFT_UTILS) target_compile_options(uhjdecoder PRIVATE ${C_FLAGS}) target_link_libraries(uhjdecoder PUBLIC common PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG}) + set_target_properties(uhjdecoder PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(uhjencoder utils/uhjencoder.cpp) target_compile_definitions(uhjencoder PRIVATE ${CPP_DEFS}) @@ -1590,6 +1593,7 @@ if(ALSOFT_UTILS) target_compile_options(uhjencoder PRIVATE ${C_FLAGS}) target_link_libraries(uhjencoder PUBLIC common PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG}) + set_target_properties(uhjencoder PROPERTIES ${DEFAULT_TARGET_PROPS}) endif() if(MYSOFA_FOUND) @@ -1601,6 +1605,7 @@ if(ALSOFT_UTILS) target_include_directories(sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common) target_compile_options(sofa-support PRIVATE ${C_FLAGS}) target_link_libraries(sofa-support PUBLIC common MySOFA::MySOFA PRIVATE ${LINKER_FLAGS}) + set_target_properties(sofa-support PROPERTIES ${DEFAULT_TARGET_PROPS}) set(MAKEMHR_SRCS utils/makemhr/loaddef.cpp @@ -1618,6 +1623,7 @@ if(ALSOFT_UTILS) PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/utils) target_compile_options(makemhr PRIVATE ${C_FLAGS}) target_link_libraries(makemhr PRIVATE ${LINKER_FLAGS} sofa-support ${UNICODE_FLAG}) + set_target_properties(makemhr PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} makemhr) endif() @@ -1628,6 +1634,7 @@ if(ALSOFT_UTILS) target_include_directories(sofa-info PRIVATE ${OpenAL_SOURCE_DIR}/utils) target_compile_options(sofa-info PRIVATE ${C_FLAGS}) target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} sofa-support ${UNICODE_FLAG}) + set_target_properties(sofa-info PROPERTIES ${DEFAULT_TARGET_PROPS}) endif() message(STATUS "Building utility programs") @@ -1646,13 +1653,16 @@ target_compile_definitions(ex-common PUBLIC ${CPP_DEFS}) target_include_directories(ex-common PUBLIC ${OpenAL_SOURCE_DIR}/common) target_compile_options(ex-common PUBLIC ${C_FLAGS}) target_link_libraries(ex-common PUBLIC OpenAL PRIVATE ${RT_LIB}) +set_target_properties(ex-common PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_EXAMPLES) add_executable(altonegen examples/altonegen.c) target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} ex-common ${UNICODE_FLAG}) + set_target_properties(altonegen PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(alrecord examples/alrecord.c) target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} ex-common ${UNICODE_FLAG}) + set_target_properties(alrecord PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} altonegen alrecord) @@ -1664,34 +1674,42 @@ if(ALSOFT_EXAMPLES) add_executable(alplay examples/alplay.c) target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${UNICODE_FLAG}) + set_target_properties(alplay PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(alstream examples/alstream.c) target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${UNICODE_FLAG}) + set_target_properties(alstream PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(alreverb examples/alreverb.c) target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${UNICODE_FLAG}) + set_target_properties(alreverb PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(almultireverb examples/almultireverb.c) target_link_libraries(almultireverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB} ${UNICODE_FLAG}) + set_target_properties(almultireverb PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(allatency examples/allatency.c) target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${UNICODE_FLAG}) + set_target_properties(allatency PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(alhrtf examples/alhrtf.c) target_link_libraries(alhrtf PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB} ${UNICODE_FLAG}) + set_target_properties(alhrtf PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(alstreamcb examples/alstreamcb.cpp) target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${UNICODE_FLAG}) + set_target_properties(alstreamcb PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(alconvolve examples/alconvolve.c) target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} common SndFile::SndFile ex-common ${UNICODE_FLAG}) + set_target_properties(alconvolve PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alplay alstream alreverb almultireverb allatency @@ -1705,6 +1723,7 @@ if(ALSOFT_EXAMPLES) add_executable(alloopback examples/alloopback.c) target_link_libraries(alloopback PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ex-common ${MATH_LIB}) + set_target_properties(alloopback PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alloopback) @@ -1741,6 +1760,7 @@ if(ALSOFT_EXAMPLES) target_include_directories(alffplay PRIVATE ${FFMPEG_INCLUDE_DIRS}) target_link_libraries(alffplay PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} ex-common) + set_target_properties(alffplay PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alffplay) diff --git a/utils/alsoft-config/CMakeLists.txt b/utils/alsoft-config/CMakeLists.txt index 65a70638..c6a46075 100644 --- a/utils/alsoft-config/CMakeLists.txt +++ b/utils/alsoft-config/CMakeLists.txt @@ -15,7 +15,8 @@ if(Qt5Widgets_FOUND) target_link_libraries(alsoft-config Qt5::Widgets) target_include_directories(alsoft-config PRIVATE "${alsoft-config_BINARY_DIR}" "${OpenAL_BINARY_DIR}") - set_target_properties(alsoft-config PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${OpenAL_BINARY_DIR}) + set_target_properties(alsoft-config PROPERTIES ${DEFAULT_TARGET_PROPS} + RUNTIME_OUTPUT_DIRECTORY ${OpenAL_BINARY_DIR}) if(TARGET build_version) add_dependencies(alsoft-config build_version) endif() -- cgit v1.2.3 From 3e4a2a151860c04fb0ebb971343a6f05e0c3c08a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 30 Mar 2023 02:20:16 -0700 Subject: Don't check for backend packages that aren't wanted --- CMakeLists.txt | 394 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 192 insertions(+), 202 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ca29401..03a24828 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,6 +98,7 @@ include(CMakePackageConfigHelpers) include(GNUInstallDirs) find_package(PkgConfig) +find_package(SDL2 QUIET) option(ALSOFT_DLOPEN "Check for the dlopen API for loading optional libs" ON) @@ -396,64 +397,54 @@ set(HAVE_SSE4_1 0) set(HAVE_NEON 0) # Check for SSE support +option(ALSOFT_CPUEXT_SSE "Enable SSE support" ON) option(ALSOFT_REQUIRE_SSE "Require SSE support" OFF) -if(HAVE_XMMINTRIN_H) - option(ALSOFT_CPUEXT_SSE "Enable SSE support" ON) - if(ALSOFT_CPUEXT_SSE) - set(HAVE_SSE 1) - endif() +if(ALSOFT_CPUEXT_SSE AND HAVE_XMMINTRIN_H) + set(HAVE_SSE 1) endif() if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE) message(FATAL_ERROR "Failed to enabled required SSE CPU extensions") endif() +option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON) option(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF) -if(HAVE_EMMINTRIN_H) - option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON) - if(HAVE_SSE AND ALSOFT_CPUEXT_SSE2) - set(HAVE_SSE2 1) - endif() +if(ALSOFT_CPUEXT_SSE2 AND HAVE_SSE AND HAVE_EMMINTRIN_H) + set(HAVE_SSE2 1) endif() if(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2) message(FATAL_ERROR "Failed to enable required SSE2 CPU extensions") endif() +option(ALSOFT_CPUEXT_SSE3 "Enable SSE3 support" ON) option(ALSOFT_REQUIRE_SSE3 "Require SSE3 support" OFF) -if(HAVE_PMMINTRIN_H) - option(ALSOFT_CPUEXT_SSE3 "Enable SSE3 support" ON) - if(HAVE_SSE2 AND ALSOFT_CPUEXT_SSE3) - set(HAVE_SSE3 1) - endif() +if(ALSOFT_CPUEXT_SSE3 AND HAVE_SSE2 AND HAVE_PMMINTRIN_H) + set(HAVE_SSE3 1) endif() if(ALSOFT_REQUIRE_SSE3 AND NOT HAVE_SSE3) message(FATAL_ERROR "Failed to enable required SSE3 CPU extensions") endif() +option(ALSOFT_CPUEXT_SSE4_1 "Enable SSE4.1 support" ON) option(ALSOFT_REQUIRE_SSE4_1 "Require SSE4.1 support" OFF) -if(HAVE_SMMINTRIN_H) - option(ALSOFT_CPUEXT_SSE4_1 "Enable SSE4.1 support" ON) - if(HAVE_SSE3 AND ALSOFT_CPUEXT_SSE4_1) - set(HAVE_SSE4_1 1) - endif() +if(ALSOFT_CPUEXT_SSE4_1 AND HAVE_SSE3 AND HAVE_SMMINTRIN_H) + set(HAVE_SSE4_1 1) endif() if(ALSOFT_REQUIRE_SSE4_1 AND NOT HAVE_SSE4_1) message(FATAL_ERROR "Failed to enable required SSE4.1 CPU extensions") endif() # Check for ARM Neon support +option(ALSOFT_CPUEXT_NEON "Enable ARM NEON support" ON) option(ALSOFT_REQUIRE_NEON "Require ARM NEON support" OFF) -if(HAVE_ARM_NEON_H) - option(ALSOFT_CPUEXT_NEON "Enable ARM NEON support" ON) - if(ALSOFT_CPUEXT_NEON) - check_c_source_compiles("#include - int main() - { - int32x4_t ret4 = vdupq_n_s32(0); - return vgetq_lane_s32(ret4, 0); - }" HAVE_NEON_INTRINSICS) - if(HAVE_NEON_INTRINSICS) - set(HAVE_NEON 1) - endif() +if(ALSOFT_CPUEXT_NEON AND HAVE_ARM_NEON_H) + check_c_source_compiles("#include + int main() + { + int32x4_t ret4 = vdupq_n_s32(0); + return vgetq_lane_s32(ret4, 0); + }" HAVE_NEON_INTRINSICS) + if(HAVE_NEON_INTRINSICS) + set(HAVE_NEON 1) endif() endif() if(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON) @@ -698,14 +689,14 @@ set(CORE_OBJS set(HAVE_RTKIT 0) if(NOT WIN32) + option(ALSOFT_RTKIT "Enable RTKit support" ON) option(ALSOFT_REQUIRE_RTKIT "Require RTKit/D-Bus support" FALSE) - find_package(DBus1 QUIET) - if(NOT DBus1_FOUND AND PkgConfig_FOUND) - pkg_check_modules(DBUS dbus-1) - endif() - if(DBus1_FOUND OR DBUS_FOUND) - option(ALSOFT_RTKIT "Enable RTKit support" ON) - if(ALSOFT_RTKIT) + if(ALSOFT_RTKIT) + find_package(DBus1 QUIET) + if(NOT DBus1_FOUND AND PkgConfig_FOUND) + pkg_check_modules(DBUS dbus-1) + endif() + if(DBus1_FOUND OR DBUS_FOUND) set(HAVE_RTKIT 1) set(CORE_OBJS ${CORE_OBJS} core/dbus_wrap.cpp core/dbus_wrap.h core/rtkit.cpp core/rtkit.h) @@ -894,101 +885,116 @@ set(ALC_OBJS ${ALC_OBJS} alc/backends/null.h ) -# Check ALSA backend -option(ALSOFT_REQUIRE_ALSA "Require ALSA backend" OFF) -find_package(ALSA) -if(ALSA_FOUND) - option(ALSOFT_BACKEND_ALSA "Enable ALSA backend" ON) - if(ALSOFT_BACKEND_ALSA) - set(HAVE_ALSA 1) - set(BACKENDS "${BACKENDS} ALSA${IS_LINKED},") - set(ALC_OBJS ${ALC_OBJS} alc/backends/alsa.cpp alc/backends/alsa.h) - add_backend_libs(${ALSA_LIBRARIES}) - set(INC_PATHS ${INC_PATHS} ${ALSA_INCLUDE_DIRS}) +# Check PipeWire backend +option(ALSOFT_BACKEND_PIPEWIRE "Enable PipeWire backend" ON) +option(ALSOFT_REQUIRE_PIPEWIRE "Require PipeWire backend" OFF) +if(ALSOFT_BACKEND_PIPEWIRE AND PkgConfig_FOUND) + pkg_check_modules(PIPEWIRE libpipewire-0.3>=0.3.23) + if(PIPEWIRE_FOUND) + set(HAVE_PIPEWIRE 1) + set(BACKENDS "${BACKENDS} PipeWire${IS_LINKED},") + set(ALC_OBJS ${ALC_OBJS} alc/backends/pipewire.cpp alc/backends/pipewire.h) + add_backend_libs(${PIPEWIRE_LIBRARIES}) + set(INC_PATHS ${INC_PATHS} ${PIPEWIRE_INCLUDE_DIRS}) endif() endif() -if(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA) - message(FATAL_ERROR "Failed to enabled required ALSA backend") +if(ALSOFT_REQUIRE_PIPEWIRE AND NOT HAVE_PIPEWIRE) + message(FATAL_ERROR "Failed to enabled required PipeWire backend") endif() -# Check OSS backend -option(ALSOFT_REQUIRE_OSS "Require OSS backend" OFF) -find_package(OSS) -if(OSS_FOUND) - option(ALSOFT_BACKEND_OSS "Enable OSS backend" ON) - if(ALSOFT_BACKEND_OSS) - set(HAVE_OSS 1) - set(BACKENDS "${BACKENDS} OSS,") - set(ALC_OBJS ${ALC_OBJS} alc/backends/oss.cpp alc/backends/oss.h) - if(OSS_LIBRARIES) - set(EXTRA_LIBS ${OSS_LIBRARIES} ${EXTRA_LIBS}) - endif() - set(INC_PATHS ${INC_PATHS} ${OSS_INCLUDE_DIRS}) +# Check PulseAudio backend +option(ALSOFT_BACKEND_PULSEAUDIO "Enable PulseAudio backend" ON) +option(ALSOFT_REQUIRE_PULSEAUDIO "Require PulseAudio backend" OFF) +if(ALSOFT_BACKEND_PULSEAUDIO) + find_package(PulseAudio) + if(PULSEAUDIO_FOUND) + set(HAVE_PULSEAUDIO 1) + set(BACKENDS "${BACKENDS} PulseAudio${IS_LINKED},") + set(ALC_OBJS ${ALC_OBJS} alc/backends/pulseaudio.cpp alc/backends/pulseaudio.h) + add_backend_libs(${PULSEAUDIO_LIBRARY}) + set(INC_PATHS ${INC_PATHS} ${PULSEAUDIO_INCLUDE_DIR}) endif() endif() -if(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS) - message(FATAL_ERROR "Failed to enabled required OSS backend") +if(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO) + message(FATAL_ERROR "Failed to enabled required PulseAudio backend") endif() -# Check PipeWire backend -option(ALSOFT_REQUIRE_PIPEWIRE "Require PipeWire backend" OFF) -if(PkgConfig_FOUND) - pkg_check_modules(PIPEWIRE libpipewire-0.3>=0.3.23) - if(PIPEWIRE_FOUND) - option(ALSOFT_BACKEND_PIPEWIRE "Enable PipeWire backend" ON) - if(ALSOFT_BACKEND_PIPEWIRE) - set(HAVE_PIPEWIRE 1) - set(BACKENDS "${BACKENDS} PipeWire${IS_LINKED},") - set(ALC_OBJS ${ALC_OBJS} alc/backends/pipewire.cpp alc/backends/pipewire.h) - add_backend_libs(${PIPEWIRE_LIBRARIES}) - set(INC_PATHS ${INC_PATHS} ${PIPEWIRE_INCLUDE_DIRS}) +if(NOT WIN32) + # Check ALSA backend + option(ALSOFT_BACKEND_ALSA "Enable ALSA backend" ON) + option(ALSOFT_REQUIRE_ALSA "Require ALSA backend" OFF) + if(ALSOFT_BACKEND_ALSA) + find_package(ALSA) + if(ALSA_FOUND) + set(HAVE_ALSA 1) + set(BACKENDS "${BACKENDS} ALSA${IS_LINKED},") + set(ALC_OBJS ${ALC_OBJS} alc/backends/alsa.cpp alc/backends/alsa.h) + add_backend_libs(${ALSA_LIBRARIES}) + set(INC_PATHS ${INC_PATHS} ${ALSA_INCLUDE_DIRS}) endif() endif() -endif() -if(ALSOFT_REQUIRE_PIPEWIRE AND NOT HAVE_PIPEWIRE) - message(FATAL_ERROR "Failed to enabled required PipeWire backend") -endif() -# Check Solaris backend -option(ALSOFT_REQUIRE_SOLARIS "Require Solaris backend" OFF) -find_package(AudioIO) -if(AUDIOIO_FOUND) + # Check OSS backend + option(ALSOFT_BACKEND_OSS "Enable OSS backend" ON) + option(ALSOFT_REQUIRE_OSS "Require OSS backend" OFF) + if(ALSOFT_BACKEND_OSS) + find_package(OSS) + if(OSS_FOUND) + set(HAVE_OSS 1) + set(BACKENDS "${BACKENDS} OSS,") + set(ALC_OBJS ${ALC_OBJS} alc/backends/oss.cpp alc/backends/oss.h) + if(OSS_LIBRARIES) + set(EXTRA_LIBS ${OSS_LIBRARIES} ${EXTRA_LIBS}) + endif() + set(INC_PATHS ${INC_PATHS} ${OSS_INCLUDE_DIRS}) + endif() + endif() + + # Check Solaris backend option(ALSOFT_BACKEND_SOLARIS "Enable Solaris backend" ON) + option(ALSOFT_REQUIRE_SOLARIS "Require Solaris backend" OFF) if(ALSOFT_BACKEND_SOLARIS) - set(HAVE_SOLARIS 1) - set(BACKENDS "${BACKENDS} Solaris,") - set(ALC_OBJS ${ALC_OBJS} alc/backends/solaris.cpp alc/backends/solaris.h) - set(INC_PATHS ${INC_PATHS} ${AUDIOIO_INCLUDE_DIRS}) + find_package(AudioIO) + if(AUDIOIO_FOUND) + set(HAVE_SOLARIS 1) + set(BACKENDS "${BACKENDS} Solaris,") + set(ALC_OBJS ${ALC_OBJS} alc/backends/solaris.cpp alc/backends/solaris.h) + set(INC_PATHS ${INC_PATHS} ${AUDIOIO_INCLUDE_DIRS}) + endif() endif() -endif() -if(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS) - message(FATAL_ERROR "Failed to enabled required Solaris backend") -endif() -# Check SndIO backend -option(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF) -find_package(SoundIO) -if(SOUNDIO_FOUND) + # Check SndIO backend option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON) + option(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF) if(ALSOFT_BACKEND_SNDIO) - set(HAVE_SNDIO 1) - set(BACKENDS "${BACKENDS} SndIO (linked),") - set(ALC_OBJS ${ALC_OBJS} alc/backends/sndio.cpp alc/backends/sndio.h) - set(EXTRA_LIBS ${SOUNDIO_LIBRARIES} ${EXTRA_LIBS}) - set(INC_PATHS ${INC_PATHS} ${SOUNDIO_INCLUDE_DIRS}) + find_package(SoundIO) + if(SOUNDIO_FOUND) + set(HAVE_SNDIO 1) + set(BACKENDS "${BACKENDS} SndIO (linked),") + set(ALC_OBJS ${ALC_OBJS} alc/backends/sndio.cpp alc/backends/sndio.h) + set(EXTRA_LIBS ${SOUNDIO_LIBRARIES} ${EXTRA_LIBS}) + set(INC_PATHS ${INC_PATHS} ${SOUNDIO_INCLUDE_DIRS}) + endif() endif() endif() +if(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA) + message(FATAL_ERROR "Failed to enabled required ALSA backend") +endif() +if(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS) + message(FATAL_ERROR "Failed to enabled required OSS backend") +endif() +if(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS) + message(FATAL_ERROR "Failed to enabled required Solaris backend") +endif() if(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO) message(FATAL_ERROR "Failed to enabled required SndIO backend") endif() # Check Windows-only backends -option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF) -option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF) -option(ALSOFT_REQUIRE_WASAPI "Require WASAPI backend" OFF) if(WIN32) # Check MMSystem backend option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON) + option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF) if(ALSOFT_BACKEND_WINMM) set(HAVE_WINMM 1) set(BACKENDS "${BACKENDS} WinMM,") @@ -1001,15 +1007,16 @@ if(WIN32) endif() # Check DSound backend - check_include_file(dsound.h HAVE_DSOUND_H) - if(DXSDK_DIR) - find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h" - PATHS "${DXSDK_DIR}" PATH_SUFFIXES include - DOC "The DirectSound include directory") - endif() - if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR) - option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON) - if(ALSOFT_BACKEND_DSOUND) + option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON) + option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF) + if(ALSOFT_BACKEND_DSOUND) + check_include_file(dsound.h HAVE_DSOUND_H) + if(DXSDK_DIR) + find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h" + PATHS "${DXSDK_DIR}" PATH_SUFFIXES include + DOC "The DirectSound include directory") + endif() + if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR) set(HAVE_DSOUND 1) set(BACKENDS "${BACKENDS} DirectSound,") set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h) @@ -1021,10 +1028,11 @@ if(WIN32) endif() # Check for WASAPI backend - check_include_file(mmdeviceapi.h HAVE_MMDEVICEAPI_H) - if(HAVE_MMDEVICEAPI_H) - option(ALSOFT_BACKEND_WASAPI "Enable WASAPI backend" ON) - if(ALSOFT_BACKEND_WASAPI) + option(ALSOFT_BACKEND_WASAPI "Enable WASAPI backend" ON) + option(ALSOFT_REQUIRE_WASAPI "Require WASAPI backend" OFF) + if(ALSOFT_BACKEND_WASAPI) + check_include_file(mmdeviceapi.h HAVE_MMDEVICEAPI_H) + if(HAVE_MMDEVICEAPI_H) set(HAVE_WASAPI 1) set(BACKENDS "${BACKENDS} WASAPI,") set(ALC_OBJS ${ALC_OBJS} alc/backends/wasapi.cpp alc/backends/wasapi.h) @@ -1041,46 +1049,12 @@ if(ALSOFT_REQUIRE_WASAPI AND NOT HAVE_WASAPI) message(FATAL_ERROR "Failed to enabled required WASAPI backend") endif() -# Check PortAudio backend -option(ALSOFT_REQUIRE_PORTAUDIO "Require PortAudio backend" OFF) -find_package(PortAudio) -if(PORTAUDIO_FOUND) - option(ALSOFT_BACKEND_PORTAUDIO "Enable PortAudio backend" ON) - if(ALSOFT_BACKEND_PORTAUDIO) - set(HAVE_PORTAUDIO 1) - set(BACKENDS "${BACKENDS} PortAudio${IS_LINKED},") - set(ALC_OBJS ${ALC_OBJS} alc/backends/portaudio.cpp alc/backends/portaudio.h) - add_backend_libs(${PORTAUDIO_LIBRARIES}) - set(INC_PATHS ${INC_PATHS} ${PORTAUDIO_INCLUDE_DIRS}) - endif() -endif() -if(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO) - message(FATAL_ERROR "Failed to enabled required PortAudio backend") -endif() - -# Check PulseAudio backend -option(ALSOFT_REQUIRE_PULSEAUDIO "Require PulseAudio backend" OFF) -find_package(PulseAudio) -if(PULSEAUDIO_FOUND) - option(ALSOFT_BACKEND_PULSEAUDIO "Enable PulseAudio backend" ON) - if(ALSOFT_BACKEND_PULSEAUDIO) - set(HAVE_PULSEAUDIO 1) - set(BACKENDS "${BACKENDS} PulseAudio${IS_LINKED},") - set(ALC_OBJS ${ALC_OBJS} alc/backends/pulseaudio.cpp alc/backends/pulseaudio.h) - add_backend_libs(${PULSEAUDIO_LIBRARY}) - set(INC_PATHS ${INC_PATHS} ${PULSEAUDIO_INCLUDE_DIR}) - endif() -endif() -if(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO) - message(FATAL_ERROR "Failed to enabled required PulseAudio backend") -endif() - # Check JACK backend +option(ALSOFT_BACKEND_JACK "Enable JACK backend" ON) option(ALSOFT_REQUIRE_JACK "Require JACK backend" OFF) -find_package(JACK) -if(JACK_FOUND) - option(ALSOFT_BACKEND_JACK "Enable JACK backend" ON) - if(ALSOFT_BACKEND_JACK) +if(ALSOFT_BACKEND_JACK) + find_package(JACK) + if(JACK_FOUND) set(HAVE_JACK 1) set(BACKENDS "${BACKENDS} JACK${IS_LINKED},") set(ALC_OBJS ${ALC_OBJS} alc/backends/jack.cpp alc/backends/jack.h) @@ -1093,12 +1067,12 @@ if(ALSOFT_REQUIRE_JACK AND NOT HAVE_JACK) endif() # Check CoreAudio backend +option(ALSOFT_BACKEND_COREAUDIO "Enable CoreAudio backend" ON) option(ALSOFT_REQUIRE_COREAUDIO "Require CoreAudio backend" OFF) -find_library(COREAUDIO_FRAMEWORK NAMES CoreAudio) -find_path(AUDIOUNIT_INCLUDE_DIR NAMES AudioUnit/AudioUnit.h) -if(COREAUDIO_FRAMEWORK AND AUDIOUNIT_INCLUDE_DIR) - option(ALSOFT_BACKEND_COREAUDIO "Enable CoreAudio backend" ON) - if(ALSOFT_BACKEND_COREAUDIO) +if(ALSOFT_BACKEND_COREAUDIO) + find_library(COREAUDIO_FRAMEWORK NAMES CoreAudio) + find_path(AUDIOUNIT_INCLUDE_DIR NAMES AudioUnit/AudioUnit.h) + if(COREAUDIO_FRAMEWORK AND AUDIOUNIT_INCLUDE_DIR) set(HAVE_COREAUDIO 1) set(ALC_OBJS ${ALC_OBJS} alc/backends/coreaudio.cpp alc/backends/coreaudio.h) set(BACKENDS "${BACKENDS} CoreAudio,") @@ -1127,12 +1101,44 @@ if(ALSOFT_REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO) message(FATAL_ERROR "Failed to enabled required CoreAudio backend") endif() +# Check for Oboe (Android) backend +option(ALSOFT_BACKEND_OBOE "Enable Oboe backend" ON) +option(ALSOFT_REQUIRE_OBOE "Require Oboe backend" OFF) +if(ALSOFT_BACKEND_OBOE) + set(OBOE_TARGET ) + if(ANDROID) + set(OBOE_SOURCE "" CACHE STRING "Source directory for Oboe.") + if(OBOE_SOURCE) + add_subdirectory(${OBOE_SOURCE} ./oboe) + set(OBOE_TARGET oboe) + else() + find_package(oboe CONFIG) + if(NOT TARGET oboe::oboe) + find_package(Oboe) + endif() + if(TARGET oboe::oboe) + set(OBOE_TARGET "oboe::oboe") + endif() + endif() + endif() + + if(OBOE_TARGET) + set(HAVE_OBOE 1) + set(ALC_OBJS ${ALC_OBJS} alc/backends/oboe.cpp alc/backends/oboe.h) + set(BACKENDS "${BACKENDS} Oboe,") + set(EXTRA_LIBS ${OBOE_TARGET} ${EXTRA_LIBS}) + endif() +endif() +if(ALSOFT_REQUIRE_OBOE AND NOT HAVE_OBOE) + message(FATAL_ERROR "Failed to enabled required Oboe backend") +endif() + # Check for OpenSL (Android) backend +option(ALSOFT_BACKEND_OPENSL "Enable OpenSL backend" ON) option(ALSOFT_REQUIRE_OPENSL "Require OpenSL backend" OFF) -find_package(OpenSL) -if(OPENSL_FOUND) - option(ALSOFT_BACKEND_OPENSL "Enable OpenSL backend" ON) - if(ALSOFT_BACKEND_OPENSL) +if(ALSOFT_BACKEND_OPENSL) + find_package(OpenSL) + if(OPENSL_FOUND) set(HAVE_OPENSL 1) set(ALC_OBJS ${ALC_OBJS} alc/backends/opensl.cpp alc/backends/opensl.h) set(BACKENDS "${BACKENDS} OpenSL,") @@ -1143,52 +1149,36 @@ if(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL) message(FATAL_ERROR "Failed to enabled required OpenSL backend") endif() -# Check for Oboe (Android) backend -set(OBOE_TARGET ) -if(ANDROID) - set(OBOE_SOURCE "" CACHE STRING "Source directory for Oboe.") - if(OBOE_SOURCE) - add_subdirectory(${OBOE_SOURCE} ./oboe) - set(OBOE_TARGET oboe) - else() - find_package(oboe CONFIG) - if(NOT TARGET oboe::oboe) - find_package(Oboe) - endif() - if(TARGET oboe::oboe) - set(OBOE_TARGET "oboe::oboe") - endif() - endif() -endif() - -option(ALSOFT_REQUIRE_OBOE "Require Oboe backend" OFF) -if(OBOE_TARGET) - option(ALSOFT_BACKEND_OBOE "Enable Oboe backend" ON) - if(ALSOFT_BACKEND_OBOE) - set(HAVE_OBOE 1) - set(ALC_OBJS ${ALC_OBJS} alc/backends/oboe.cpp alc/backends/oboe.h) - set(BACKENDS "${BACKENDS} Oboe,") - set(EXTRA_LIBS ${OBOE_TARGET} ${EXTRA_LIBS}) +# Check PortAudio backend +option(ALSOFT_BACKEND_PORTAUDIO "Enable PortAudio backend" ON) +option(ALSOFT_REQUIRE_PORTAUDIO "Require PortAudio backend" OFF) +if(ALSOFT_BACKEND_PORTAUDIO) + find_package(PortAudio) + if(PORTAUDIO_FOUND) + set(HAVE_PORTAUDIO 1) + set(BACKENDS "${BACKENDS} PortAudio${IS_LINKED},") + set(ALC_OBJS ${ALC_OBJS} alc/backends/portaudio.cpp alc/backends/portaudio.h) + add_backend_libs(${PORTAUDIO_LIBRARIES}) + set(INC_PATHS ${INC_PATHS} ${PORTAUDIO_INCLUDE_DIRS}) endif() endif() -if(ALSOFT_REQUIRE_OBOE AND NOT HAVE_OBOE) - message(FATAL_ERROR "Failed to enabled required Oboe backend") +if(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO) + message(FATAL_ERROR "Failed to enabled required PortAudio backend") endif() # Check for SDL2 backend +# Off by default, since it adds a runtime dependency +option(ALSOFT_BACKEND_SDL2 "Enable SDL2 backend" OFF) option(ALSOFT_REQUIRE_SDL2 "Require SDL2 backend" OFF) -find_package(SDL2 QUIET) -if(SDL2_FOUND) - # Off by default, since it adds a runtime dependency - option(ALSOFT_BACKEND_SDL2 "Enable SDL2 backend" OFF) - if(ALSOFT_BACKEND_SDL2) +if(ALSOFT_BACKEND_SDL2) + if(SDL2_FOUND) set(HAVE_SDL2 1) set(ALC_OBJS ${ALC_OBJS} alc/backends/sdl2.cpp alc/backends/sdl2.h) set(BACKENDS "${BACKENDS} SDL2,") set(EXTRA_LIBS ${EXTRA_LIBS} SDL2::SDL2) + else() + message(STATUS "Could NOT find SDL2") endif() -else() - message(STATUS "Could NOT find SDL2") endif() if(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND) message(FATAL_ERROR "Failed to enabled required SDL2 backend") -- cgit v1.2.3 From 31f33369135c80cfceec9765b73fa6556641cd2e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 10 Apr 2023 12:37:06 -0700 Subject: Add a version script for non-Windows/macOS systems To more aggressively control library exports. Despite the -fvisibility=hidden flag that should hide everything by default, GNU's libstdc++ forces default visibility for generated std namespace symbols (from template functions that don't inline, for example), adding some standard C++ symbols to the exported symbol list. This can cause ABI problems if an app links to OpenAL and uses one of those symbols, as a future internal change can cause the function to not be generated and make the symbol no longer available. There seems to be no way to prevent this, aside from this version script. This has the added advantage of ensuring future extension functions don't accidentally get exported due to the ALC_API or AL_API macros making them visible. --- CMakeLists.txt | 16 +++-- libopenal.version | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 libopenal.version (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 03a24828..1fd3f0fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -751,7 +751,7 @@ set(OPENAL_OBJS al/effects/dedicated.cpp al/effects/distortion.cpp al/effects/echo.cpp - al/effects/effects.cpp + al/effects/effects.cpp al/effects/effects.h al/effects/equalizer.cpp al/effects/fshifter.cpp @@ -801,7 +801,7 @@ set(ALC_OBJS alc/inprogext.h alc/panning.cpp) -if (ALSOFT_EAX) +if(ALSOFT_EAX) set(OPENAL_OBJS ${OPENAL_OBJS} al/eax/api.cpp @@ -820,8 +820,8 @@ if (ALSOFT_EAX) al/eax/utils.cpp al/eax/utils.h al/eax/x_ram.h -) -endif () + ) +endif() # Include SIMD mixers set(CPU_EXTS "Default") @@ -1373,6 +1373,7 @@ else() # the project set(TARGET_PUBLIC_HEADERS include/AL/al.h include/AL/alc.h include/AL/alext.h include/AL/efx.h include/AL/efx-presets.h) + add_library(${IMPL_TARGET} SHARED ${OPENAL_OBJS} ${ALC_OBJS} ${CORE_OBJS} ${RC_CONFIG} ${TARGET_PUBLIC_HEADERS}) if(WIN32) @@ -1380,6 +1381,13 @@ else() endif() target_link_libraries(${IMPL_TARGET} PRIVATE common ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) + if(NOT WIN32 AND NOT APPLE) + # FIXME: This doesn't put a dependency on the version script. Changing + # the version script will not cause a relink as it should. + set_property(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY + LINK_FLAGS " -Wl,--version-script=${OpenAL_SOURCE_DIR}/libopenal.version") + endif() + if(APPLE AND ALSOFT_OSX_FRAMEWORK) # Sets framework name to soft_oal to avoid ambiguity with the system OpenAL.framework set(LIBNAME "soft_oal") diff --git a/libopenal.version b/libopenal.version new file mode 100644 index 00000000..dd998e14 --- /dev/null +++ b/libopenal.version @@ -0,0 +1,192 @@ +{ +global: +alBuffer3f; +alBuffer3i; +alBufferData; +alBufferf; +alBufferfv; +alBufferi; +alBufferiv; +alcCaptureCloseDevice; +alcCaptureOpenDevice; +alcCaptureSamples; +alcCaptureStart; +alcCaptureStop; +alcCloseDevice; +alcCreateContext; +alcDestroyContext; +alcGetContextsDevice; +alcGetCurrentContext; +alcGetEnumValue; +alcGetError; +alcGetIntegerv; +alcGetProcAddress; +alcGetString; +alcIsExtensionPresent; +alcMakeContextCurrent; +alcOpenDevice; +alcProcessContext; +alcSuspendContext; +alDeleteBuffers; +alDeleteSources; +alDisable; +alDistanceModel; +alDopplerFactor; +alEnable; +alGenBuffers; +alGenSources; +alGetBoolean; +alGetBooleanv; +alGetBuffer3f; +alGetBuffer3i; +alGetBufferf; +alGetBufferfv; +alGetBufferi; +alGetBufferiv; +alGetDouble; +alGetDoublev; +alGetEnumValue; +alGetError; +alGetFloat; +alGetFloatv; +alGetInteger; +alGetIntegerv; +alGetListener3f; +alGetListener3i; +alGetListenerf; +alGetListenerfv; +alGetListeneri; +alGetListeneriv; +alGetProcAddress; +alGetSource3f; +alGetSource3i; +alGetSourcef; +alGetSourcefv; +alGetSourcei; +alGetSourceiv; +alGetString; +alIsBuffer; +alIsEnabled; +alIsExtensionPresent; +alIsSource; +alListener3f; +alListener3i; +alListenerf; +alListenerfv; +alListeneri; +alListeneriv; +alSource3f; +alSource3i; +alSourcef; +alSourcefv; +alSourcei; +alSourceiv; +alSourcePause; +alSourcePausev; +alSourcePlay; +alSourcePlayv; +alSourceQueueBuffers; +alSourceRewind; +alSourceRewindv; +alSourceStop; +alSourceStopv; +alSourceUnqueueBuffers; +alSpeedOfSound; + +# Deprecated in AL 1.1, kept for compatibility. +alDopplerVelocity; + +# EFX, effectively standard at this point. +alAuxiliaryEffectSlotf; +alAuxiliaryEffectSlotfv; +alAuxiliaryEffectSloti; +alAuxiliaryEffectSlotiv; +alDeleteAuxiliaryEffectSlots; +alDeleteEffects; +alDeleteFilters; +alEffectf; +alEffectfv; +alEffecti; +alEffectiv; +alFilterf; +alFilterfv; +alFilteri; +alFilteriv; +alGenAuxiliaryEffectSlots; +alGenEffects; +alGenFilters; +alGetAuxiliaryEffectSlotf; +alGetAuxiliaryEffectSlotfv; +alGetAuxiliaryEffectSloti; +alGetAuxiliaryEffectSlotiv; +alGetEffectf; +alGetEffectfv; +alGetEffecti; +alGetEffectiv; +alGetFilterf; +alGetFilterfv; +alGetFilteri; +alGetFilteriv; +alIsAuxiliaryEffectSlot; +alIsEffect; +alIsFilter; + +# Non-standard +alsoft_get_version; + +# These extension functions shouldn't be exported here, but they were exported +# by mistake in previous releases, so need to stay for compatibility with apps +# that may have directly linked to them. Remove them if it can be done without +# breaking anything. +alAuxiliaryEffectSlotPlaySOFT; +alAuxiliaryEffectSlotPlayvSOFT; +alAuxiliaryEffectSlotStopSOFT; +alAuxiliaryEffectSlotStopvSOFT; +alBufferCallbackSOFT; +alBufferSamplesSOFT; +alBufferStorageSOFT; +alBufferSubDataSOFT; +alBufferSubSamplesSOFT; +alcDevicePauseSOFT; +alcDeviceResumeSOFT; +alcGetInteger64vSOFT; +alcGetStringiSOFT; +alcGetThreadContext; +alcIsRenderFormatSupportedSOFT; +alcLoopbackOpenDeviceSOFT; +alcRenderSamplesSOFT; +alcResetDeviceSOFT; +alcSetThreadContext; +alDeferUpdatesSOFT; +alEventCallbackSOFT; +alEventControlSOFT; +alFlushMappedBufferSOFT; +alGetBuffer3PtrSOFT; +alGetBufferPtrSOFT; +alGetBufferPtrvSOFT; +alGetBufferSamplesSOFT; +alGetInteger64SOFT; +alGetInteger64vSOFT; +alGetPointerSOFT; +alGetPointervSOFT; +alGetSource3dSOFT; +alGetSource3i64SOFT; +alGetSourcedSOFT; +alGetSourcedvSOFT; +alGetSourcei64SOFT; +alGetSourcei64vSOFT; +alGetStringiSOFT; +alIsBufferFormatSupportedSOFT; +alMapBufferSOFT; +alProcessUpdatesSOFT; +alSource3dSOFT; +alSource3i64SOFT; +alSourcedSOFT; +alSourcedvSOFT; +alSourcei64SOFT; +alSourcei64vSOFT; +alSourceQueueBufferLayersSOFT; +alUnmapBufferSOFT; + +local: *; +}; -- cgit v1.2.3 From d3875f333fb6abe2f39d82caca329414871ae53b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 11 Apr 2023 12:46:23 -0700 Subject: Release 1.23.1 --- CMakeLists.txt | 2 +- ChangeLog | 31 +++++++++++++++++++++++++++++++ appveyor.yml | 2 +- 3 files changed, 33 insertions(+), 2 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fd3f0fd..55644b04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -179,7 +179,7 @@ endif() set(LIB_MAJOR_VERSION "1") set(LIB_MINOR_VERSION "23") -set(LIB_REVISION "0") +set(LIB_REVISION "1") set(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}") set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0) diff --git a/ChangeLog b/ChangeLog index da6698f8..e4236f85 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,34 @@ +openal-soft-1.23.1: + + Implemented the AL_SOFT_UHJ_ex extension. + + Implemented the AL_SOFT_buffer_length_query extension. + + Implemented the AL_SOFT_source_start_delay extension. + + Implemented the AL_EXT_STATIC_BUFFER extension. + + Fixed compiling with certain older versions of GCC. + + Fixed compiling as a submodule. + + Fixed compiling with newer versions of Oboe. + + Improved EAX effect version switching. + + Improved the quality of the reverb modulator. + + Improved performance of the cubic resampler. + + Added a compatibility option to restore AL_SOFT_buffer_sub_data. The option + disables AL_EXT_SOURCE_RADIUS due to incompatibility. + + Reduced CPU usage when EAX is initialized and FXSlot0 or FXSlot1 are not + used. + + Reduced memory usage for ADPCM buffer formats. They're no longer converted + to 16-bit samples on load. + openal-soft-1.23.0: Fixed CoreAudio capture support. diff --git a/appveyor.yml b/appveyor.yml index d7adb17b..aa155af4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 1.23.0.{build} +version: 1.23.1.{build} environment: APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 -- cgit v1.2.3