diff options
author | Chris Robinson <[email protected]> | 2020-12-04 13:13:52 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2020-12-04 13:13:52 -0800 |
commit | c4132b80ede60ead27fae595623ac61674ed166a (patch) | |
tree | 989d90c1c73f7fde3b97e9860a45478cd59aa2b7 /alc | |
parent | 3a3094c88762dbeadda6418bbb6a060a85e29f3f (diff) |
Move a couple more things to core
Diffstat (limited to 'alc')
-rw-r--r-- | alc/alc.cpp | 4 | ||||
-rw-r--r-- | alc/alu.cpp | 4 | ||||
-rw-r--r-- | alc/ambidefs.h | 3 | ||||
-rw-r--r-- | alc/bs2b.cpp | 183 | ||||
-rw-r--r-- | alc/bs2b.h | 89 | ||||
-rw-r--r-- | alc/panning.cpp | 4 | ||||
-rw-r--r-- | alc/uhjfilter.cpp | 208 | ||||
-rw-r--r-- | alc/uhjfilter.h | 39 |
8 files changed, 8 insertions, 526 deletions
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 <array> -#include <cstdint> +#include <stddef.h> +#include <stdint.h> /* 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 <algorithm> -#include <cmath> -#include <iterator> - -#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<float>::Tau() * Fc_lo / static_cast<float>(bs2b->srate)); - bs2b->b1_lo = x; - bs2b->a0_lo = G_lo * (1.0f - x) * g; - - x = std::exp(-al::MathDefs<float>::Tau() * Fc_hi / static_cast<float>(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<size_t>(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<float,MAX_AMBI_CHANNELS> 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 <xmmintrin.h> -#endif - -#include <algorithm> -#include <iterator> - -#include "AL/al.h" - -#include "alcomplex.h" -#include "alnumeric.h" -#include "opthelpers.h" - - -namespace { - -using complex_d = std::complex<double>; - -std::array<float,Uhj2Encoder::sFilterSize> 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<complex_d[]>(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<std::array<float,Uhj2Encoder::sFilterSize>>(); - auto fftiter = fftBuffer.get() + half_size + (Uhj2Encoder::sFilterSize-1); - for(float &coeff : *ret) - { - coeff = static_cast<float>(fftiter->real() / double{fft_size}); - fftiter -= 2; - } - return *ret; -} -alignas(16) const auto PShiftCoeffs = GenerateFilter(); - - -void allpass_process(al::span<float> 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 <array> - -#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<float,sFilterSize> mMidDelay{}; - alignas(16) std::array<float,sFilterSize> mSideDelay{}; - - alignas(16) std::array<float,BufferLineSize+sFilterSize> mMid{}; - alignas(16) std::array<float,BufferLineSize+sFilterSize> mSide{}; - - /* History for the FIR filter. */ - alignas(16) std::array<float,sFilterSize*2 - 1> mSideHistory{}; - - alignas(16) std::array<float,BufferLineSize + sFilterSize*2> 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 */ |