🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

241 lines
8.6 KiB

  1. #include "config.h"
  2. #include "uhjfilter.h"
  3. #include <algorithm>
  4. #include <iterator>
  5. #include "alcomplex.h"
  6. #include "alnumeric.h"
  7. #include "opthelpers.h"
  8. #include "phase_shifter.h"
  9. namespace {
  10. const PhaseShifterT<UhjFilterBase::sFilterDelay*2> PShift{};
  11. } // namespace
  12. /* Encoding UHJ from B-Format is done as:
  13. *
  14. * S = 0.9396926*W + 0.1855740*X
  15. * D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y
  16. *
  17. * Left = (S + D)/2.0
  18. * Right = (S - D)/2.0
  19. * T = j(-0.1432*W + 0.6512*X) - 0.7071068*Y
  20. * Q = 0.9772*Z
  21. *
  22. * where j is a wide-band +90 degree phase shift. 3-channel UHJ excludes Q,
  23. * while 2-channel excludes Q and T.
  24. *
  25. * The phase shift is done using a linear FIR filter derived from an FFT'd
  26. * impulse with the desired shift.
  27. */
  28. void UhjEncoder::encode(float *LeftOut, float *RightOut,
  29. const al::span<const float*const,3> InSamples, const size_t SamplesToDo)
  30. {
  31. ASSUME(SamplesToDo > 0);
  32. float *RESTRICT left{al::assume_aligned<16>(LeftOut)};
  33. float *RESTRICT right{al::assume_aligned<16>(RightOut)};
  34. const float *RESTRICT winput{al::assume_aligned<16>(InSamples[0])};
  35. const float *RESTRICT xinput{al::assume_aligned<16>(InSamples[1])};
  36. const float *RESTRICT yinput{al::assume_aligned<16>(InSamples[2])};
  37. /* Combine the previously delayed S/D signal with the input. Include any
  38. * existing direct signal with it.
  39. */
  40. /* S = 0.9396926*W + 0.1855740*X */
  41. auto miditer = mS.begin() + sFilterDelay;
  42. std::transform(winput, winput+SamplesToDo, xinput, miditer,
  43. [](const float w, const float x) noexcept -> float
  44. { return 0.9396926f*w + 0.1855740f*x; });
  45. for(size_t i{0};i < SamplesToDo;++i,++miditer)
  46. *miditer += left[i] + right[i];
  47. /* D = 0.6554516*Y */
  48. auto sideiter = mD.begin() + sFilterDelay;
  49. std::transform(yinput, yinput+SamplesToDo, sideiter,
  50. [](const float y) noexcept -> float { return 0.6554516f*y; });
  51. for(size_t i{0};i < SamplesToDo;++i,++sideiter)
  52. *sideiter += left[i] - right[i];
  53. /* D += j(-0.3420201*W + 0.5098604*X) */
  54. auto tmpiter = std::copy(mWXHistory.cbegin(), mWXHistory.cend(), mTemp.begin());
  55. std::transform(winput, winput+SamplesToDo, xinput, tmpiter,
  56. [](const float w, const float x) noexcept -> float
  57. { return -0.3420201f*w + 0.5098604f*x; });
  58. std::copy_n(mTemp.cbegin()+SamplesToDo, mWXHistory.size(), mWXHistory.begin());
  59. PShift.processAccum({mD.data(), SamplesToDo}, mTemp.data());
  60. /* Left = (S + D)/2.0 */
  61. for(size_t i{0};i < SamplesToDo;i++)
  62. left[i] = (mS[i] + mD[i]) * 0.5f;
  63. /* Right = (S - D)/2.0 */
  64. for(size_t i{0};i < SamplesToDo;i++)
  65. right[i] = (mS[i] - mD[i]) * 0.5f;
  66. /* Copy the future samples to the front for next time. */
  67. std::copy(mS.cbegin()+SamplesToDo, mS.cbegin()+SamplesToDo+sFilterDelay, mS.begin());
  68. std::copy(mD.cbegin()+SamplesToDo, mD.cbegin()+SamplesToDo+sFilterDelay, mD.begin());
  69. }
  70. /* Decoding UHJ is done as:
  71. *
  72. * S = Left + Right
  73. * D = Left - Right
  74. *
  75. * W = 0.981532*S + 0.197484*j(0.828331*D + 0.767820*T)
  76. * X = 0.418496*S - j(0.828331*D + 0.767820*T)
  77. * Y = 0.795968*D - 0.676392*T + j(0.186633*S)
  78. * Z = 1.023332*Q
  79. *
  80. * where j is a +90 degree phase shift. 3-channel UHJ excludes Q, while 2-
  81. * channel excludes Q and T.
  82. */
  83. void UhjDecoder::decode(const al::span<float*> samples, const size_t samplesToDo,
  84. const size_t forwardSamples)
  85. {
  86. ASSUME(samplesToDo > 0);
  87. {
  88. const float *RESTRICT left{al::assume_aligned<16>(samples[0])};
  89. const float *RESTRICT right{al::assume_aligned<16>(samples[1])};
  90. const float *RESTRICT t{al::assume_aligned<16>(samples[2])};
  91. /* S = Left + Right */
  92. for(size_t i{0};i < samplesToDo+sFilterDelay;++i)
  93. mS[i] = left[i] + right[i];
  94. /* D = Left - Right */
  95. for(size_t i{0};i < samplesToDo+sFilterDelay;++i)
  96. mD[i] = left[i] - right[i];
  97. /* T */
  98. for(size_t i{0};i < samplesToDo+sFilterDelay;++i)
  99. mT[i] = t[i];
  100. }
  101. float *RESTRICT woutput{al::assume_aligned<16>(samples[0])};
  102. float *RESTRICT xoutput{al::assume_aligned<16>(samples[1])};
  103. float *RESTRICT youtput{al::assume_aligned<16>(samples[2])};
  104. /* Precompute j(0.828331*D + 0.767820*T) and store in xoutput. */
  105. auto tmpiter = std::copy(mDTHistory.cbegin(), mDTHistory.cend(), mTemp.begin());
  106. std::transform(mD.cbegin(), mD.cbegin()+samplesToDo+sFilterDelay, mT.cbegin(), tmpiter,
  107. [](const float d, const float t) noexcept { return 0.828331f*d + 0.767820f*t; });
  108. std::copy_n(mTemp.cbegin()+forwardSamples, mDTHistory.size(), mDTHistory.begin());
  109. PShift.process({xoutput, samplesToDo}, mTemp.data());
  110. /* W = 0.981532*S + 0.197484*j(0.828331*D + 0.767820*T) */
  111. for(size_t i{0};i < samplesToDo;++i)
  112. woutput[i] = 0.981532f*mS[i] + 0.197484f*xoutput[i];
  113. /* X = 0.418496*S - j(0.828331*D + 0.767820*T) */
  114. for(size_t i{0};i < samplesToDo;++i)
  115. xoutput[i] = 0.418496f*mS[i] - xoutput[i];
  116. /* Precompute j*S and store in youtput. */
  117. tmpiter = std::copy(mSHistory.cbegin(), mSHistory.cend(), mTemp.begin());
  118. std::copy_n(mS.cbegin(), samplesToDo+sFilterDelay, tmpiter);
  119. std::copy_n(mTemp.cbegin()+forwardSamples, mSHistory.size(), mSHistory.begin());
  120. PShift.process({youtput, samplesToDo}, mTemp.data());
  121. /* Y = 0.795968*D - 0.676392*T + j(0.186633*S) */
  122. for(size_t i{0};i < samplesToDo;++i)
  123. youtput[i] = 0.795968f*mD[i] - 0.676392f*mT[i] + 0.186633f*youtput[i];
  124. if(samples.size() > 3)
  125. {
  126. float *RESTRICT zoutput{al::assume_aligned<16>(samples[3])};
  127. /* Z = 1.023332*Q */
  128. for(size_t i{0};i < samplesToDo;++i)
  129. zoutput[i] = 1.023332f*zoutput[i];
  130. }
  131. }
  132. /* Super Stereo processing is done as:
  133. *
  134. * S = Left + Right
  135. * D = Left - Right
  136. *
  137. * W = 0.6098637*S - 0.6896511*j*w*D
  138. * X = 0.8624776*S + 0.7626955*j*w*D
  139. * Y = 1.6822415*w*D - 0.2156194*j*S
  140. *
  141. * where j is a +90 degree phase shift. w is a variable control for the
  142. * resulting stereo width, with the range 0 <= w <= 0.7.
  143. */
  144. void UhjStereoDecoder::decode(const al::span<float*> samples, const size_t samplesToDo,
  145. const size_t forwardSamples)
  146. {
  147. ASSUME(samplesToDo > 0);
  148. {
  149. const float *RESTRICT left{al::assume_aligned<16>(samples[0])};
  150. const float *RESTRICT right{al::assume_aligned<16>(samples[1])};
  151. for(size_t i{0};i < samplesToDo+sFilterDelay;++i)
  152. mS[i] = left[i] + right[i];
  153. /* Pre-apply the width factor to the difference signal D. Smoothly
  154. * interpolate when it changes.
  155. */
  156. const float wtarget{mWidthControl};
  157. const float wcurrent{unlikely(mCurrentWidth < 0.0f) ? wtarget : mCurrentWidth};
  158. if(likely(wtarget == wcurrent) || unlikely(forwardSamples == 0))
  159. {
  160. for(size_t i{0};i < samplesToDo+sFilterDelay;++i)
  161. mD[i] = (left[i] - right[i]) * wcurrent;
  162. }
  163. else
  164. {
  165. const float wstep{(wtarget - wcurrent) / static_cast<float>(forwardSamples)};
  166. float fi{0.0f};
  167. size_t i{0};
  168. for(;i < forwardSamples;++i)
  169. {
  170. mD[i] = (left[i] - right[i]) * (wcurrent + wstep*fi);
  171. fi += 1.0f;
  172. }
  173. for(;i < samplesToDo+sFilterDelay;++i)
  174. mD[i] = (left[i] - right[i]) * wtarget;
  175. mCurrentWidth = wtarget;
  176. }
  177. }
  178. float *RESTRICT woutput{al::assume_aligned<16>(samples[0])};
  179. float *RESTRICT xoutput{al::assume_aligned<16>(samples[1])};
  180. float *RESTRICT youtput{al::assume_aligned<16>(samples[2])};
  181. /* Precompute j*D and store in xoutput. */
  182. auto tmpiter = std::copy(mDTHistory.cbegin(), mDTHistory.cend(), mTemp.begin());
  183. std::copy_n(mD.cbegin(), samplesToDo+sFilterDelay, tmpiter);
  184. std::copy_n(mTemp.cbegin()+forwardSamples, mDTHistory.size(), mDTHistory.begin());
  185. PShift.process({xoutput, samplesToDo}, mTemp.data());
  186. /* W = 0.6098637*S - 0.6896511*j*w*D */
  187. for(size_t i{0};i < samplesToDo;++i)
  188. woutput[i] = 0.6098637f*mS[i] - 0.6896511f*xoutput[i];
  189. /* X = 0.8624776*S + 0.7626955*j*w*D */
  190. for(size_t i{0};i < samplesToDo;++i)
  191. xoutput[i] = 0.8624776f*mS[i] + 0.7626955f*xoutput[i];
  192. /* Precompute j*S and store in youtput. */
  193. tmpiter = std::copy(mSHistory.cbegin(), mSHistory.cend(), mTemp.begin());
  194. std::copy_n(mS.cbegin(), samplesToDo+sFilterDelay, tmpiter);
  195. std::copy_n(mTemp.cbegin()+forwardSamples, mSHistory.size(), mSHistory.begin());
  196. PShift.process({youtput, samplesToDo}, mTemp.data());
  197. /* Y = 1.6822415*w*D - 0.2156194*j*S */
  198. for(size_t i{0};i < samplesToDo;++i)
  199. youtput[i] = 1.6822415f*mD[i] - 0.2156194f*youtput[i];
  200. }