pshifter.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /**
  2. * OpenAL cross platform audio library
  3. * Copyright (C) 2018 by Raul Herraiz.
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Library General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Library General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Library General Public
  15. * License along with this library; if not, write to the
  16. * Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18. * Or go to http://www.gnu.org/copyleft/lgpl.html
  19. */
  20. #include "config.h"
  21. #include <cmath>
  22. #include <cstdlib>
  23. #include <array>
  24. #include <complex>
  25. #include <algorithm>
  26. #include "alcmain.h"
  27. #include "alcomplex.h"
  28. #include "alcontext.h"
  29. #include "alnumeric.h"
  30. #include "alu.h"
  31. #include "effectslot.h"
  32. #include "math_defs.h"
  33. namespace {
  34. using complex_d = std::complex<double>;
  35. #define STFT_SIZE 1024
  36. #define STFT_HALF_SIZE (STFT_SIZE>>1)
  37. #define OVERSAMP (1<<2)
  38. #define STFT_STEP (STFT_SIZE / OVERSAMP)
  39. #define FIFO_LATENCY (STFT_STEP * (OVERSAMP-1))
  40. /* Define a Hann window, used to filter the STFT input and output. */
  41. std::array<double,STFT_SIZE> InitHannWindow()
  42. {
  43. std::array<double,STFT_SIZE> ret;
  44. /* Create lookup table of the Hann window for the desired size, i.e. STFT_SIZE */
  45. for(size_t i{0};i < STFT_SIZE>>1;i++)
  46. {
  47. constexpr double scale{al::MathDefs<double>::Pi() / double{STFT_SIZE}};
  48. const double val{std::sin(static_cast<double>(i+1) * scale)};
  49. ret[i] = ret[STFT_SIZE-1-i] = val * val;
  50. }
  51. return ret;
  52. }
  53. alignas(16) const std::array<double,STFT_SIZE> HannWindow = InitHannWindow();
  54. struct FrequencyBin {
  55. double Amplitude;
  56. double FreqBin;
  57. };
  58. struct PshifterState final : public EffectState {
  59. /* Effect parameters */
  60. size_t mCount;
  61. uint mPitchShiftI;
  62. double mPitchShift;
  63. /* Effects buffers */
  64. std::array<double,STFT_SIZE> mFIFO;
  65. std::array<double,STFT_HALF_SIZE+1> mLastPhase;
  66. std::array<double,STFT_HALF_SIZE+1> mSumPhase;
  67. std::array<double,STFT_SIZE> mOutputAccum;
  68. std::array<complex_d,STFT_SIZE> mFftBuffer;
  69. std::array<FrequencyBin,STFT_HALF_SIZE+1> mAnalysisBuffer;
  70. std::array<FrequencyBin,STFT_HALF_SIZE+1> mSynthesisBuffer;
  71. alignas(16) FloatBufferLine mBufferOut;
  72. /* Effect gains for each output channel */
  73. float mCurrentGains[MAX_OUTPUT_CHANNELS];
  74. float mTargetGains[MAX_OUTPUT_CHANNELS];
  75. void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
  76. void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
  77. const EffectTarget target) override;
  78. void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
  79. const al::span<FloatBufferLine> samplesOut) override;
  80. DEF_NEWDEL(PshifterState)
  81. };
  82. void PshifterState::deviceUpdate(const ALCdevice*, const Buffer&)
  83. {
  84. /* (Re-)initializing parameters and clear the buffers. */
  85. mCount = FIFO_LATENCY;
  86. mPitchShiftI = MixerFracOne;
  87. mPitchShift = 1.0;
  88. std::fill(mFIFO.begin(), mFIFO.end(), 0.0);
  89. std::fill(mLastPhase.begin(), mLastPhase.end(), 0.0);
  90. std::fill(mSumPhase.begin(), mSumPhase.end(), 0.0);
  91. std::fill(mOutputAccum.begin(), mOutputAccum.end(), 0.0);
  92. std::fill(mFftBuffer.begin(), mFftBuffer.end(), complex_d{});
  93. std::fill(mAnalysisBuffer.begin(), mAnalysisBuffer.end(), FrequencyBin{});
  94. std::fill(mSynthesisBuffer.begin(), mSynthesisBuffer.end(), FrequencyBin{});
  95. std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f);
  96. std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f);
  97. }
  98. void PshifterState::update(const ALCcontext*, const EffectSlot *slot,
  99. const EffectProps *props, const EffectTarget target)
  100. {
  101. const int tune{props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune};
  102. const float pitch{std::pow(2.0f, static_cast<float>(tune) / 1200.0f)};
  103. mPitchShiftI = fastf2u(pitch*MixerFracOne);
  104. mPitchShift = mPitchShiftI * double{1.0/MixerFracOne};
  105. const auto coeffs = CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}, 0.0f);
  106. mOutTarget = target.Main->Buffer;
  107. ComputePanGains(target.Main, coeffs.data(), slot->Gain, mTargetGains);
  108. }
  109. void PshifterState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
  110. {
  111. /* Pitch shifter engine based on the work of Stephan Bernsee.
  112. * http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/
  113. */
  114. /* Cycle offset per update expected of each frequency bin (bin 0 is none,
  115. * bin 1 is x1, bin 2 is x2, etc).
  116. */
  117. constexpr double expected_cycles{al::MathDefs<double>::Tau() / OVERSAMP};
  118. for(size_t base{0u};base < samplesToDo;)
  119. {
  120. const size_t todo{minz(STFT_SIZE-mCount, samplesToDo-base)};
  121. /* Retrieve the output samples from the FIFO and fill in the new input
  122. * samples.
  123. */
  124. auto fifo_iter = mFIFO.begin() + mCount;
  125. std::transform(fifo_iter, fifo_iter+todo, mBufferOut.begin()+base,
  126. [](double d) noexcept -> float { return static_cast<float>(d); });
  127. std::copy_n(samplesIn[0].begin()+base, todo, fifo_iter);
  128. mCount += todo;
  129. base += todo;
  130. /* Check whether FIFO buffer is filled with new samples. */
  131. if(mCount < STFT_SIZE) break;
  132. mCount = FIFO_LATENCY;
  133. /* Time-domain signal windowing, store in FftBuffer, and apply a
  134. * forward FFT to get the frequency-domain signal.
  135. */
  136. for(size_t k{0u};k < STFT_SIZE;k++)
  137. mFftBuffer[k] = mFIFO[k] * HannWindow[k];
  138. forward_fft(mFftBuffer);
  139. /* Analyze the obtained data. Since the real FFT is symmetric, only
  140. * STFT_HALF_SIZE+1 samples are needed.
  141. */
  142. for(size_t k{0u};k < STFT_HALF_SIZE+1;k++)
  143. {
  144. const double amplitude{std::abs(mFftBuffer[k])};
  145. const double phase{std::arg(mFftBuffer[k])};
  146. /* Compute phase difference and subtract expected phase difference */
  147. double tmp{(phase - mLastPhase[k]) - static_cast<double>(k)*expected_cycles};
  148. /* Map delta phase into +/- Pi interval */
  149. int qpd{double2int(tmp / al::MathDefs<double>::Pi())};
  150. tmp -= al::MathDefs<double>::Pi() * (qpd + (qpd%2));
  151. /* Get deviation from bin frequency from the +/- Pi interval */
  152. tmp /= expected_cycles;
  153. /* Compute the k-th partials' true frequency and store the
  154. * amplitude and frequency bin in the analysis buffer.
  155. */
  156. mAnalysisBuffer[k].Amplitude = amplitude;
  157. mAnalysisBuffer[k].FreqBin = static_cast<double>(k) + tmp;
  158. /* Store the actual phase[k] for the next frame. */
  159. mLastPhase[k] = phase;
  160. }
  161. /* Shift the frequency bins according to the pitch adjustment,
  162. * accumulating the amplitudes of overlapping frequency bins.
  163. */
  164. std::fill(mSynthesisBuffer.begin(), mSynthesisBuffer.end(), FrequencyBin{});
  165. const size_t bin_count{minz(STFT_HALF_SIZE+1,
  166. (((STFT_HALF_SIZE+1)<<MixerFracBits) - (MixerFracOne>>1) - 1)/mPitchShiftI + 1)};
  167. for(size_t k{0u};k < bin_count;k++)
  168. {
  169. const size_t j{(k*mPitchShiftI + (MixerFracOne>>1)) >> MixerFracBits};
  170. mSynthesisBuffer[j].Amplitude += mAnalysisBuffer[k].Amplitude;
  171. mSynthesisBuffer[j].FreqBin = mAnalysisBuffer[k].FreqBin * mPitchShift;
  172. }
  173. /* Reconstruct the frequency-domain signal from the adjusted frequency
  174. * bins.
  175. */
  176. for(size_t k{0u};k < STFT_HALF_SIZE+1;k++)
  177. {
  178. /* Calculate actual delta phase and accumulate it to get bin phase */
  179. mSumPhase[k] += mSynthesisBuffer[k].FreqBin * expected_cycles;
  180. mFftBuffer[k] = std::polar(mSynthesisBuffer[k].Amplitude, mSumPhase[k]);
  181. }
  182. for(size_t k{STFT_HALF_SIZE+1};k < STFT_SIZE;++k)
  183. mFftBuffer[k] = std::conj(mFftBuffer[STFT_SIZE-k]);
  184. /* Apply an inverse FFT to get the time-domain siganl, and accumulate
  185. * for the output with windowing.
  186. */
  187. inverse_fft(mFftBuffer);
  188. for(size_t k{0u};k < STFT_SIZE;k++)
  189. mOutputAccum[k] += HannWindow[k]*mFftBuffer[k].real() * (4.0/OVERSAMP/STFT_SIZE);
  190. /* Shift FIFO and accumulator. */
  191. fifo_iter = std::copy(mFIFO.begin()+STFT_STEP, mFIFO.end(), mFIFO.begin());
  192. std::copy_n(mOutputAccum.begin(), STFT_STEP, fifo_iter);
  193. auto accum_iter = std::copy(mOutputAccum.begin()+STFT_STEP, mOutputAccum.end(),
  194. mOutputAccum.begin());
  195. std::fill(accum_iter, mOutputAccum.end(), 0.0);
  196. }
  197. /* Now, mix the processed sound data to the output. */
  198. MixSamples({mBufferOut.data(), samplesToDo}, samplesOut, mCurrentGains, mTargetGains,
  199. maxz(samplesToDo, 512), 0);
  200. }
  201. struct PshifterStateFactory final : public EffectStateFactory {
  202. al::intrusive_ptr<EffectState> create() override
  203. { return al::intrusive_ptr<EffectState>{new PshifterState{}}; }
  204. };
  205. } // namespace
  206. EffectStateFactory *PshifterStateFactory_getFactory()
  207. {
  208. static PshifterStateFactory PshifterFactory{};
  209. return &PshifterFactory;
  210. }