Rendering the SH and EQ coefficients for pathing involves the following steps: 1. EQ is applied to the dry audio. 2. The EQ-filtered audio is scaled by each SH coefficient in turn and combined into an Ambisonics buffer.
| 101 | // 1. EQ is applied to the dry audio. |
| 102 | // 2. The EQ-filtered audio is scaled by each SH coefficient in turn and combined into an Ambisonics buffer. |
| 103 | AudioEffectState PathEffect::apply(const PathEffectParams& params, |
| 104 | const AudioBuffer& in, |
| 105 | AudioBuffer& out) |
| 106 | { |
| 107 | assert(in.numSamples() == out.numSamples()); |
| 108 | assert(in.numChannels() == 1); |
| 109 | |
| 110 | out.makeSilent(); |
| 111 | |
| 112 | if (mSpatialize) |
| 113 | { |
| 114 | // todo: assert? |
| 115 | |
| 116 | // apply eq to mono input |
| 117 | EQEffectParams eqParams{}; |
| 118 | eqParams.gains = params.eqCoeffs; |
| 119 | |
| 120 | float eqGains[Bands::kNumBands] = {0}; |
| 121 | if (params.normalizeEQ) |
| 122 | { |
| 123 | memcpy(eqGains, params.eqCoeffs, Bands::kNumBands * sizeof(float)); |
| 124 | |
| 125 | auto overallGain = 1.0f; |
| 126 | EQEffect::normalizeGains(eqGains, overallGain); |
| 127 | |
| 128 | eqParams.gains = eqGains; |
| 129 | } |
| 130 | |
| 131 | mEQEffect.apply(eqParams, in, mEQBuffer); |
| 132 | |
| 133 | for (auto i = 0; i < SphericalHarmonics::numCoeffsForOrder(params.order); ++i) |
| 134 | { |
| 135 | (*mAmbisonicsBuffer)[i][0] = params.shCoeffs[i]; |
| 136 | } |
| 137 | |
| 138 | // rotate the sh coeffs |
| 139 | AmbisonicsRotateEffectParams ambisonicsRotateParams{}; |
| 140 | ambisonicsRotateParams.orientation = params.listener; |
| 141 | ambisonicsRotateParams.order = params.order; |
| 142 | |
| 143 | mAmbisonicsRotateEffect->apply(ambisonicsRotateParams, *mAmbisonicsBuffer, *mAmbisonicsBuffer); |
| 144 | |
| 145 | if (params.binaural) |
| 146 | { |
| 147 | // blend hrtf |
| 148 | memset(mHRTF.flatData(), 0, mHRTF.totalSize() * sizeof(complex_t)); |
| 149 | |
| 150 | auto cosine = cosf((137.9f * Math::kDegreesToRadians) / (params.order + 1.51f)); |
| 151 | for (auto l = 0, i = 0; l <= params.order; ++l) |
| 152 | { |
| 153 | auto scalar = SphericalHarmonics::legendre(l, cosine); |
| 154 | for (auto m = -l; m <= l; ++m, ++i) |
| 155 | { |
| 156 | const complex_t* hrtfForChannel[2] = {nullptr, nullptr}; |
| 157 | params.hrtf->ambisonicsHRTF(i, hrtfForChannel); |
| 158 | |
| 159 | for (auto k = 0; k < IHRTFMap::kNumEars; ++k) |
| 160 | { |
nothing calls this directly
no test coverage detected