The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

1295 lines
48KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce::dsp
  19. {
  20. template <typename Element>
  21. class Queue
  22. {
  23. public:
  24. explicit Queue (int size)
  25. : fifo (size), storage (static_cast<size_t> (size)) {}
  26. bool push (Element& element) noexcept
  27. {
  28. if (fifo.getFreeSpace() == 0)
  29. return false;
  30. const auto writer = fifo.write (1);
  31. if (writer.blockSize1 != 0)
  32. storage[static_cast<size_t> (writer.startIndex1)] = std::move (element);
  33. else if (writer.blockSize2 != 0)
  34. storage[static_cast<size_t> (writer.startIndex2)] = std::move (element);
  35. return true;
  36. }
  37. template <typename Fn>
  38. void pop (Fn&& fn) { popN (1, std::forward<Fn> (fn)); }
  39. template <typename Fn>
  40. void popAll (Fn&& fn) { popN (fifo.getNumReady(), std::forward<Fn> (fn)); }
  41. bool hasPendingMessages() const noexcept { return fifo.getNumReady() > 0; }
  42. private:
  43. template <typename Fn>
  44. void popN (int n, Fn&& fn)
  45. {
  46. fifo.read (n).forEach ([&] (int index)
  47. {
  48. fn (storage[static_cast<size_t> (index)]);
  49. });
  50. }
  51. AbstractFifo fifo;
  52. std::vector<Element> storage;
  53. };
  54. class BackgroundMessageQueue : private Thread
  55. {
  56. public:
  57. explicit BackgroundMessageQueue (int entries)
  58. : Thread ("Convolution background loader"), queue (entries)
  59. {}
  60. using IncomingCommand = FixedSizeFunction<400, void()>;
  61. // Push functions here, and they'll be called later on a background thread.
  62. // This function is wait-free.
  63. // This function is only safe to call from a single thread at a time.
  64. bool push (IncomingCommand& command) { return queue.push (command); }
  65. void popAll()
  66. {
  67. const ScopedLock lock (popMutex);
  68. queue.popAll ([] (IncomingCommand& command) { command(); command = nullptr; });
  69. }
  70. using Thread::startThread;
  71. using Thread::stopThread;
  72. private:
  73. void run() override
  74. {
  75. while (! threadShouldExit())
  76. {
  77. const auto tryPop = [&]
  78. {
  79. const ScopedLock lock (popMutex);
  80. if (! queue.hasPendingMessages())
  81. return false;
  82. queue.pop ([] (IncomingCommand& command) { command(); command = nullptr;});
  83. return true;
  84. };
  85. if (! tryPop())
  86. sleep (10);
  87. }
  88. }
  89. CriticalSection popMutex;
  90. Queue<IncomingCommand> queue;
  91. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BackgroundMessageQueue)
  92. };
  93. struct ConvolutionMessageQueue::Impl : public BackgroundMessageQueue
  94. {
  95. using BackgroundMessageQueue::BackgroundMessageQueue;
  96. };
  97. ConvolutionMessageQueue::ConvolutionMessageQueue()
  98. : ConvolutionMessageQueue (1000)
  99. {}
  100. ConvolutionMessageQueue::ConvolutionMessageQueue (int entries)
  101. : pimpl (std::make_unique<Impl> (entries))
  102. {
  103. pimpl->startThread();
  104. }
  105. ConvolutionMessageQueue::~ConvolutionMessageQueue() noexcept
  106. {
  107. pimpl->stopThread (-1);
  108. }
  109. ConvolutionMessageQueue::ConvolutionMessageQueue (ConvolutionMessageQueue&&) noexcept = default;
  110. ConvolutionMessageQueue& ConvolutionMessageQueue::operator= (ConvolutionMessageQueue&&) noexcept = default;
  111. //==============================================================================
  112. struct ConvolutionEngine
  113. {
  114. ConvolutionEngine (const float* samples,
  115. size_t numSamples,
  116. size_t maxBlockSize)
  117. : blockSize ((size_t) nextPowerOfTwo ((int) maxBlockSize)),
  118. fftSize (blockSize > 128 ? 2 * blockSize : 4 * blockSize),
  119. fftObject (std::make_unique<FFT> (roundToInt (std::log2 (fftSize)))),
  120. numSegments (numSamples / (fftSize - blockSize) + 1u),
  121. numInputSegments ((blockSize > 128 ? numSegments : 3 * numSegments)),
  122. bufferInput (1, static_cast<int> (fftSize)),
  123. bufferOutput (1, static_cast<int> (fftSize * 2)),
  124. bufferTempOutput (1, static_cast<int> (fftSize * 2)),
  125. bufferOverlap (1, static_cast<int> (fftSize))
  126. {
  127. bufferOutput.clear();
  128. auto updateSegmentsIfNecessary = [this] (size_t numSegmentsToUpdate,
  129. std::vector<AudioBuffer<float>>& segments)
  130. {
  131. if (numSegmentsToUpdate == 0
  132. || numSegmentsToUpdate != (size_t) segments.size()
  133. || (size_t) segments[0].getNumSamples() != fftSize * 2)
  134. {
  135. segments.clear();
  136. for (size_t i = 0; i < numSegmentsToUpdate; ++i)
  137. segments.push_back ({ 1, static_cast<int> (fftSize * 2) });
  138. }
  139. };
  140. updateSegmentsIfNecessary (numInputSegments, buffersInputSegments);
  141. updateSegmentsIfNecessary (numSegments, buffersImpulseSegments);
  142. auto FFTTempObject = std::make_unique<FFT> (roundToInt (std::log2 (fftSize)));
  143. size_t currentPtr = 0;
  144. for (auto& buf : buffersImpulseSegments)
  145. {
  146. buf.clear();
  147. auto* impulseResponse = buf.getWritePointer (0);
  148. if (&buf == &buffersImpulseSegments.front())
  149. impulseResponse[0] = 1.0f;
  150. FloatVectorOperations::copy (impulseResponse,
  151. samples + currentPtr,
  152. static_cast<int> (jmin (fftSize - blockSize, numSamples - currentPtr)));
  153. FFTTempObject->performRealOnlyForwardTransform (impulseResponse);
  154. prepareForConvolution (impulseResponse);
  155. currentPtr += (fftSize - blockSize);
  156. }
  157. reset();
  158. }
  159. void reset()
  160. {
  161. bufferInput.clear();
  162. bufferOverlap.clear();
  163. bufferTempOutput.clear();
  164. bufferOutput.clear();
  165. for (auto& buf : buffersInputSegments)
  166. buf.clear();
  167. currentSegment = 0;
  168. inputDataPos = 0;
  169. }
  170. void processSamples (const float* input, float* output, size_t numSamples)
  171. {
  172. // Overlap-add, zero latency convolution algorithm with uniform partitioning
  173. size_t numSamplesProcessed = 0;
  174. auto indexStep = numInputSegments / numSegments;
  175. auto* inputData = bufferInput.getWritePointer (0);
  176. auto* outputTempData = bufferTempOutput.getWritePointer (0);
  177. auto* outputData = bufferOutput.getWritePointer (0);
  178. auto* overlapData = bufferOverlap.getWritePointer (0);
  179. while (numSamplesProcessed < numSamples)
  180. {
  181. const bool inputDataWasEmpty = (inputDataPos == 0);
  182. auto numSamplesToProcess = jmin (numSamples - numSamplesProcessed, blockSize - inputDataPos);
  183. FloatVectorOperations::copy (inputData + inputDataPos, input + numSamplesProcessed, static_cast<int> (numSamplesToProcess));
  184. auto* inputSegmentData = buffersInputSegments[currentSegment].getWritePointer (0);
  185. FloatVectorOperations::copy (inputSegmentData, inputData, static_cast<int> (fftSize));
  186. fftObject->performRealOnlyForwardTransform (inputSegmentData);
  187. prepareForConvolution (inputSegmentData);
  188. // Complex multiplication
  189. if (inputDataWasEmpty)
  190. {
  191. FloatVectorOperations::fill (outputTempData, 0, static_cast<int> (fftSize + 1));
  192. auto index = currentSegment;
  193. for (size_t i = 1; i < numSegments; ++i)
  194. {
  195. index += indexStep;
  196. if (index >= numInputSegments)
  197. index -= numInputSegments;
  198. convolutionProcessingAndAccumulate (buffersInputSegments[index].getWritePointer (0),
  199. buffersImpulseSegments[i].getWritePointer (0),
  200. outputTempData);
  201. }
  202. }
  203. FloatVectorOperations::copy (outputData, outputTempData, static_cast<int> (fftSize + 1));
  204. convolutionProcessingAndAccumulate (inputSegmentData,
  205. buffersImpulseSegments.front().getWritePointer (0),
  206. outputData);
  207. updateSymmetricFrequencyDomainData (outputData);
  208. fftObject->performRealOnlyInverseTransform (outputData);
  209. // Add overlap
  210. FloatVectorOperations::add (&output[numSamplesProcessed], &outputData[inputDataPos], &overlapData[inputDataPos], (int) numSamplesToProcess);
  211. // Input buffer full => Next block
  212. inputDataPos += numSamplesToProcess;
  213. if (inputDataPos == blockSize)
  214. {
  215. // Input buffer is empty again now
  216. FloatVectorOperations::fill (inputData, 0.0f, static_cast<int> (fftSize));
  217. inputDataPos = 0;
  218. // Extra step for segSize > blockSize
  219. FloatVectorOperations::add (&(outputData[blockSize]), &(overlapData[blockSize]), static_cast<int> (fftSize - 2 * blockSize));
  220. // Save the overlap
  221. FloatVectorOperations::copy (overlapData, &(outputData[blockSize]), static_cast<int> (fftSize - blockSize));
  222. currentSegment = (currentSegment > 0) ? (currentSegment - 1) : (numInputSegments - 1);
  223. }
  224. numSamplesProcessed += numSamplesToProcess;
  225. }
  226. }
  227. void processSamplesWithAddedLatency (const float* input, float* output, size_t numSamples)
  228. {
  229. // Overlap-add, zero latency convolution algorithm with uniform partitioning
  230. size_t numSamplesProcessed = 0;
  231. auto indexStep = numInputSegments / numSegments;
  232. auto* inputData = bufferInput.getWritePointer (0);
  233. auto* outputTempData = bufferTempOutput.getWritePointer (0);
  234. auto* outputData = bufferOutput.getWritePointer (0);
  235. auto* overlapData = bufferOverlap.getWritePointer (0);
  236. while (numSamplesProcessed < numSamples)
  237. {
  238. auto numSamplesToProcess = jmin (numSamples - numSamplesProcessed, blockSize - inputDataPos);
  239. FloatVectorOperations::copy (inputData + inputDataPos, input + numSamplesProcessed, static_cast<int> (numSamplesToProcess));
  240. FloatVectorOperations::copy (output + numSamplesProcessed, outputData + inputDataPos, static_cast<int> (numSamplesToProcess));
  241. numSamplesProcessed += numSamplesToProcess;
  242. inputDataPos += numSamplesToProcess;
  243. // processing itself when needed (with latency)
  244. if (inputDataPos == blockSize)
  245. {
  246. // Copy input data in input segment
  247. auto* inputSegmentData = buffersInputSegments[currentSegment].getWritePointer (0);
  248. FloatVectorOperations::copy (inputSegmentData, inputData, static_cast<int> (fftSize));
  249. fftObject->performRealOnlyForwardTransform (inputSegmentData);
  250. prepareForConvolution (inputSegmentData);
  251. // Complex multiplication
  252. FloatVectorOperations::fill (outputTempData, 0, static_cast<int> (fftSize + 1));
  253. auto index = currentSegment;
  254. for (size_t i = 1; i < numSegments; ++i)
  255. {
  256. index += indexStep;
  257. if (index >= numInputSegments)
  258. index -= numInputSegments;
  259. convolutionProcessingAndAccumulate (buffersInputSegments[index].getWritePointer (0),
  260. buffersImpulseSegments[i].getWritePointer (0),
  261. outputTempData);
  262. }
  263. FloatVectorOperations::copy (outputData, outputTempData, static_cast<int> (fftSize + 1));
  264. convolutionProcessingAndAccumulate (inputSegmentData,
  265. buffersImpulseSegments.front().getWritePointer (0),
  266. outputData);
  267. updateSymmetricFrequencyDomainData (outputData);
  268. fftObject->performRealOnlyInverseTransform (outputData);
  269. // Add overlap
  270. FloatVectorOperations::add (outputData, overlapData, static_cast<int> (blockSize));
  271. // Input buffer is empty again now
  272. FloatVectorOperations::fill (inputData, 0.0f, static_cast<int> (fftSize));
  273. // Extra step for segSize > blockSize
  274. FloatVectorOperations::add (&(outputData[blockSize]), &(overlapData[blockSize]), static_cast<int> (fftSize - 2 * blockSize));
  275. // Save the overlap
  276. FloatVectorOperations::copy (overlapData, &(outputData[blockSize]), static_cast<int> (fftSize - blockSize));
  277. currentSegment = (currentSegment > 0) ? (currentSegment - 1) : (numInputSegments - 1);
  278. inputDataPos = 0;
  279. }
  280. }
  281. }
  282. // After each FFT, this function is called to allow convolution to be performed with only 4 SIMD functions calls.
  283. void prepareForConvolution (float *samples) noexcept
  284. {
  285. auto FFTSizeDiv2 = fftSize / 2;
  286. for (size_t i = 0; i < FFTSizeDiv2; i++)
  287. samples[i] = samples[i << 1];
  288. samples[FFTSizeDiv2] = 0;
  289. for (size_t i = 1; i < FFTSizeDiv2; i++)
  290. samples[i + FFTSizeDiv2] = -samples[((fftSize - i) << 1) + 1];
  291. }
  292. // Does the convolution operation itself only on half of the frequency domain samples.
  293. void convolutionProcessingAndAccumulate (const float *input, const float *impulse, float *output)
  294. {
  295. auto FFTSizeDiv2 = fftSize / 2;
  296. FloatVectorOperations::addWithMultiply (output, input, impulse, static_cast<int> (FFTSizeDiv2));
  297. FloatVectorOperations::subtractWithMultiply (output, &(input[FFTSizeDiv2]), &(impulse[FFTSizeDiv2]), static_cast<int> (FFTSizeDiv2));
  298. FloatVectorOperations::addWithMultiply (&(output[FFTSizeDiv2]), input, &(impulse[FFTSizeDiv2]), static_cast<int> (FFTSizeDiv2));
  299. FloatVectorOperations::addWithMultiply (&(output[FFTSizeDiv2]), &(input[FFTSizeDiv2]), impulse, static_cast<int> (FFTSizeDiv2));
  300. output[fftSize] += input[fftSize] * impulse[fftSize];
  301. }
  302. // Undoes the re-organization of samples from the function prepareForConvolution.
  303. // Then takes the conjugate of the frequency domain first half of samples to fill the
  304. // second half, so that the inverse transform will return real samples in the time domain.
  305. void updateSymmetricFrequencyDomainData (float* samples) noexcept
  306. {
  307. auto FFTSizeDiv2 = fftSize / 2;
  308. for (size_t i = 1; i < FFTSizeDiv2; i++)
  309. {
  310. samples[(fftSize - i) << 1] = samples[i];
  311. samples[((fftSize - i) << 1) + 1] = -samples[FFTSizeDiv2 + i];
  312. }
  313. samples[1] = 0.f;
  314. for (size_t i = 1; i < FFTSizeDiv2; i++)
  315. {
  316. samples[i << 1] = samples[(fftSize - i) << 1];
  317. samples[(i << 1) + 1] = -samples[((fftSize - i) << 1) + 1];
  318. }
  319. }
  320. //==============================================================================
  321. const size_t blockSize;
  322. const size_t fftSize;
  323. const std::unique_ptr<FFT> fftObject;
  324. const size_t numSegments;
  325. const size_t numInputSegments;
  326. size_t currentSegment = 0, inputDataPos = 0;
  327. AudioBuffer<float> bufferInput, bufferOutput, bufferTempOutput, bufferOverlap;
  328. std::vector<AudioBuffer<float>> buffersInputSegments, buffersImpulseSegments;
  329. };
  330. //==============================================================================
  331. class MultichannelEngine
  332. {
  333. public:
  334. MultichannelEngine (const AudioBuffer<float>& buf,
  335. int maxBlockSize,
  336. int maxBufferSize,
  337. Convolution::NonUniform headSizeIn,
  338. bool isZeroDelayIn)
  339. : tailBuffer (1, maxBlockSize),
  340. latency (isZeroDelayIn ? 0 : maxBufferSize),
  341. irSize (buf.getNumSamples()),
  342. blockSize (maxBlockSize),
  343. isZeroDelay (isZeroDelayIn)
  344. {
  345. constexpr auto numChannels = 2;
  346. const auto makeEngine = [&] (int channel, int offset, int length, uint32 thisBlockSize)
  347. {
  348. return std::make_unique<ConvolutionEngine> (buf.getReadPointer (jmin (buf.getNumChannels() - 1, channel), offset),
  349. length,
  350. static_cast<size_t> (thisBlockSize));
  351. };
  352. if (headSizeIn.headSizeInSamples == 0)
  353. {
  354. for (int i = 0; i < numChannels; ++i)
  355. head.emplace_back (makeEngine (i, 0, buf.getNumSamples(), static_cast<uint32> (maxBufferSize)));
  356. }
  357. else
  358. {
  359. const auto size = jmin (buf.getNumSamples(), headSizeIn.headSizeInSamples);
  360. for (int i = 0; i < numChannels; ++i)
  361. head.emplace_back (makeEngine (i, 0, size, static_cast<uint32> (maxBufferSize)));
  362. const auto tailBufferSize = static_cast<uint32> (headSizeIn.headSizeInSamples + (isZeroDelay ? 0 : maxBufferSize));
  363. if (size != buf.getNumSamples())
  364. for (int i = 0; i < numChannels; ++i)
  365. tail.emplace_back (makeEngine (i, size, buf.getNumSamples() - size, tailBufferSize));
  366. }
  367. }
  368. void reset()
  369. {
  370. for (const auto& e : head)
  371. e->reset();
  372. for (const auto& e : tail)
  373. e->reset();
  374. }
  375. void processSamples (const AudioBlock<const float>& input, AudioBlock<float>& output)
  376. {
  377. const auto numChannels = jmin (head.size(), input.getNumChannels(), output.getNumChannels());
  378. const auto numSamples = jmin (input.getNumSamples(), output.getNumSamples());
  379. const AudioBlock<float> fullTailBlock (tailBuffer);
  380. const auto tailBlock = fullTailBlock.getSubBlock (0, (size_t) numSamples);
  381. const auto isUniform = tail.empty();
  382. for (size_t channel = 0; channel < numChannels; ++channel)
  383. {
  384. if (! isUniform)
  385. tail[channel]->processSamplesWithAddedLatency (input.getChannelPointer (channel),
  386. tailBlock.getChannelPointer (0),
  387. numSamples);
  388. if (isZeroDelay)
  389. head[channel]->processSamples (input.getChannelPointer (channel),
  390. output.getChannelPointer (channel),
  391. numSamples);
  392. else
  393. head[channel]->processSamplesWithAddedLatency (input.getChannelPointer (channel),
  394. output.getChannelPointer (channel),
  395. numSamples);
  396. if (! isUniform)
  397. output.getSingleChannelBlock (channel) += tailBlock;
  398. }
  399. const auto numOutputChannels = output.getNumChannels();
  400. for (auto i = numChannels; i < numOutputChannels; ++i)
  401. output.getSingleChannelBlock (i).copyFrom (output.getSingleChannelBlock (0));
  402. }
  403. int getIRSize() const noexcept { return irSize; }
  404. int getLatency() const noexcept { return latency; }
  405. int getBlockSize() const noexcept { return blockSize; }
  406. private:
  407. std::vector<std::unique_ptr<ConvolutionEngine>> head, tail;
  408. AudioBuffer<float> tailBuffer;
  409. const int latency;
  410. const int irSize;
  411. const int blockSize;
  412. const bool isZeroDelay;
  413. };
  414. static AudioBuffer<float> fixNumChannels (const AudioBuffer<float>& buf, Convolution::Stereo stereo)
  415. {
  416. const auto numChannels = jmin (buf.getNumChannels(), stereo == Convolution::Stereo::yes ? 2 : 1);
  417. const auto numSamples = buf.getNumSamples();
  418. AudioBuffer<float> result (numChannels, buf.getNumSamples());
  419. for (auto channel = 0; channel != numChannels; ++channel)
  420. result.copyFrom (channel, 0, buf.getReadPointer (channel), numSamples);
  421. if (result.getNumSamples() == 0 || result.getNumChannels() == 0)
  422. {
  423. result.setSize (1, 1);
  424. result.setSample (0, 0, 1.0f);
  425. }
  426. return result;
  427. }
  428. static AudioBuffer<float> trimImpulseResponse (const AudioBuffer<float>& buf)
  429. {
  430. const auto thresholdTrim = Decibels::decibelsToGain (-80.0f);
  431. const auto numChannels = buf.getNumChannels();
  432. const auto numSamples = buf.getNumSamples();
  433. std::ptrdiff_t offsetBegin = numSamples;
  434. std::ptrdiff_t offsetEnd = numSamples;
  435. for (auto channel = 0; channel < numChannels; ++channel)
  436. {
  437. const auto indexAboveThreshold = [&] (auto begin, auto end)
  438. {
  439. return std::distance (begin, std::find_if (begin, end, [&] (float sample)
  440. {
  441. return std::abs (sample) >= thresholdTrim;
  442. }));
  443. };
  444. const auto channelBegin = buf.getReadPointer (channel);
  445. const auto channelEnd = channelBegin + numSamples;
  446. const auto itStart = indexAboveThreshold (channelBegin, channelEnd);
  447. const auto itEnd = indexAboveThreshold (std::make_reverse_iterator (channelEnd),
  448. std::make_reverse_iterator (channelBegin));
  449. offsetBegin = jmin (offsetBegin, itStart);
  450. offsetEnd = jmin (offsetEnd, itEnd);
  451. }
  452. if (offsetBegin == numSamples)
  453. {
  454. auto result = AudioBuffer<float> (numChannels, 1);
  455. result.clear();
  456. return result;
  457. }
  458. const auto newLength = jmax (1, numSamples - static_cast<int> (offsetBegin + offsetEnd));
  459. AudioBuffer<float> result (numChannels, newLength);
  460. for (auto channel = 0; channel < numChannels; ++channel)
  461. {
  462. result.copyFrom (channel,
  463. 0,
  464. buf.getReadPointer (channel, static_cast<int> (offsetBegin)),
  465. result.getNumSamples());
  466. }
  467. return result;
  468. }
  469. static float calculateNormalisationFactor (float sumSquaredMagnitude)
  470. {
  471. if (sumSquaredMagnitude < 1e-8f)
  472. return 1.0f;
  473. return 0.125f / std::sqrt (sumSquaredMagnitude);
  474. }
  475. static void normaliseImpulseResponse (AudioBuffer<float>& buf)
  476. {
  477. const auto numChannels = buf.getNumChannels();
  478. const auto numSamples = buf.getNumSamples();
  479. const auto channelPtrs = buf.getArrayOfWritePointers();
  480. const auto maxSumSquaredMag = std::accumulate (channelPtrs, channelPtrs + numChannels, 0.0f, [numSamples] (auto max, auto* channel)
  481. {
  482. return jmax (max, std::accumulate (channel, channel + numSamples, 0.0f, [] (auto sum, auto samp)
  483. {
  484. return sum + (samp * samp);
  485. }));
  486. });
  487. const auto normalisationFactor = calculateNormalisationFactor (maxSumSquaredMag);
  488. std::for_each (channelPtrs, channelPtrs + numChannels, [normalisationFactor, numSamples] (auto* channel)
  489. {
  490. FloatVectorOperations::multiply (channel, normalisationFactor, numSamples);
  491. });
  492. }
  493. static AudioBuffer<float> resampleImpulseResponse (const AudioBuffer<float>& buf,
  494. const double srcSampleRate,
  495. const double destSampleRate)
  496. {
  497. if (approximatelyEqual (srcSampleRate, destSampleRate))
  498. return buf;
  499. const auto factorReading = srcSampleRate / destSampleRate;
  500. AudioBuffer<float> original = buf;
  501. MemoryAudioSource memorySource (original, false);
  502. ResamplingAudioSource resamplingSource (&memorySource, false, buf.getNumChannels());
  503. const auto finalSize = roundToInt (jmax (1.0, buf.getNumSamples() / factorReading));
  504. resamplingSource.setResamplingRatio (factorReading);
  505. resamplingSource.prepareToPlay (finalSize, srcSampleRate);
  506. AudioBuffer<float> result (buf.getNumChannels(), finalSize);
  507. resamplingSource.getNextAudioBlock ({ &result, 0, result.getNumSamples() });
  508. return result;
  509. }
  510. //==============================================================================
  511. template <typename Element>
  512. class TryLockedPtr
  513. {
  514. public:
  515. void set (std::unique_ptr<Element> p)
  516. {
  517. const SpinLock::ScopedLockType lock (mutex);
  518. ptr = std::move (p);
  519. }
  520. std::unique_ptr<MultichannelEngine> get()
  521. {
  522. const SpinLock::ScopedTryLockType lock (mutex);
  523. return lock.isLocked() ? std::move (ptr) : nullptr;
  524. }
  525. private:
  526. std::unique_ptr<Element> ptr;
  527. SpinLock mutex;
  528. };
  529. struct BufferWithSampleRate
  530. {
  531. BufferWithSampleRate() = default;
  532. BufferWithSampleRate (AudioBuffer<float>&& bufferIn, double sampleRateIn)
  533. : buffer (std::move (bufferIn)), sampleRate (sampleRateIn) {}
  534. AudioBuffer<float> buffer;
  535. double sampleRate = 0.0;
  536. };
  537. static BufferWithSampleRate loadStreamToBuffer (std::unique_ptr<InputStream> stream, size_t maxLength)
  538. {
  539. AudioFormatManager manager;
  540. manager.registerBasicFormats();
  541. std::unique_ptr<AudioFormatReader> formatReader (manager.createReaderFor (std::move (stream)));
  542. if (formatReader == nullptr)
  543. return {};
  544. const auto fileLength = static_cast<size_t> (formatReader->lengthInSamples);
  545. const auto lengthToLoad = maxLength == 0 ? fileLength : jmin (maxLength, fileLength);
  546. BufferWithSampleRate result { { jlimit (1, 2, static_cast<int> (formatReader->numChannels)),
  547. static_cast<int> (lengthToLoad) },
  548. formatReader->sampleRate };
  549. formatReader->read (result.buffer.getArrayOfWritePointers(),
  550. result.buffer.getNumChannels(),
  551. 0,
  552. result.buffer.getNumSamples());
  553. return result;
  554. }
  555. // This class caches the data required to build a new convolution engine
  556. // (in particular, impulse response data and a ProcessSpec).
  557. // Calls to `setProcessSpec` and `setImpulseResponse` construct a
  558. // new engine, which can be retrieved by calling `getEngine`.
  559. class ConvolutionEngineFactory
  560. {
  561. public:
  562. ConvolutionEngineFactory (Convolution::Latency requiredLatency,
  563. Convolution::NonUniform requiredHeadSize)
  564. : latency { (requiredLatency.latencyInSamples <= 0) ? 0 : jmax (64, nextPowerOfTwo (requiredLatency.latencyInSamples)) },
  565. headSize { (requiredHeadSize.headSizeInSamples <= 0) ? 0 : jmax (64, nextPowerOfTwo (requiredHeadSize.headSizeInSamples)) },
  566. shouldBeZeroLatency (requiredLatency.latencyInSamples == 0)
  567. {}
  568. // It is safe to call this method simultaneously with other public
  569. // member functions.
  570. void setProcessSpec (const ProcessSpec& spec)
  571. {
  572. const std::lock_guard<std::mutex> lock (mutex);
  573. processSpec = spec;
  574. engine.set (makeEngine());
  575. }
  576. // It is safe to call this method simultaneously with other public
  577. // member functions.
  578. void setImpulseResponse (BufferWithSampleRate&& buf,
  579. Convolution::Stereo stereo,
  580. Convolution::Trim trim,
  581. Convolution::Normalise normalise)
  582. {
  583. const std::lock_guard<std::mutex> lock (mutex);
  584. wantsNormalise = normalise;
  585. originalSampleRate = buf.sampleRate;
  586. impulseResponse = [&]
  587. {
  588. auto corrected = fixNumChannels (buf.buffer, stereo);
  589. return trim == Convolution::Trim::yes ? trimImpulseResponse (corrected) : corrected;
  590. }();
  591. engine.set (makeEngine());
  592. }
  593. // Returns the most recently-created engine, or nullptr
  594. // if there is no pending engine, or if the engine is currently
  595. // being updated by one of the setter methods.
  596. // It is safe to call this simultaneously with other public
  597. // member functions.
  598. std::unique_ptr<MultichannelEngine> getEngine() { return engine.get(); }
  599. private:
  600. std::unique_ptr<MultichannelEngine> makeEngine()
  601. {
  602. auto resampled = resampleImpulseResponse (impulseResponse, originalSampleRate, processSpec.sampleRate);
  603. if (wantsNormalise == Convolution::Normalise::yes)
  604. normaliseImpulseResponse (resampled);
  605. else
  606. resampled.applyGain ((float) (originalSampleRate / processSpec.sampleRate));
  607. const auto currentLatency = jmax (processSpec.maximumBlockSize, (uint32) latency.latencyInSamples);
  608. const auto maxBufferSize = shouldBeZeroLatency ? static_cast<int> (processSpec.maximumBlockSize)
  609. : nextPowerOfTwo (static_cast<int> (currentLatency));
  610. return std::make_unique<MultichannelEngine> (resampled,
  611. processSpec.maximumBlockSize,
  612. maxBufferSize,
  613. headSize,
  614. shouldBeZeroLatency);
  615. }
  616. static AudioBuffer<float> makeImpulseBuffer()
  617. {
  618. AudioBuffer<float> result (1, 1);
  619. result.setSample (0, 0, 1.0f);
  620. return result;
  621. }
  622. ProcessSpec processSpec { 44100.0, 128, 2 };
  623. AudioBuffer<float> impulseResponse = makeImpulseBuffer();
  624. double originalSampleRate = processSpec.sampleRate;
  625. Convolution::Normalise wantsNormalise = Convolution::Normalise::no;
  626. const Convolution::Latency latency;
  627. const Convolution::NonUniform headSize;
  628. const bool shouldBeZeroLatency;
  629. TryLockedPtr<MultichannelEngine> engine;
  630. mutable std::mutex mutex;
  631. };
  632. static void setImpulseResponse (ConvolutionEngineFactory& factory,
  633. const void* sourceData,
  634. size_t sourceDataSize,
  635. Convolution::Stereo stereo,
  636. Convolution::Trim trim,
  637. size_t size,
  638. Convolution::Normalise normalise)
  639. {
  640. factory.setImpulseResponse (loadStreamToBuffer (std::make_unique<MemoryInputStream> (sourceData, sourceDataSize, false), size),
  641. stereo, trim, normalise);
  642. }
  643. static void setImpulseResponse (ConvolutionEngineFactory& factory,
  644. const File& fileImpulseResponse,
  645. Convolution::Stereo stereo,
  646. Convolution::Trim trim,
  647. size_t size,
  648. Convolution::Normalise normalise)
  649. {
  650. factory.setImpulseResponse (loadStreamToBuffer (std::make_unique<FileInputStream> (fileImpulseResponse), size),
  651. stereo, trim, normalise);
  652. }
  653. // This class acts as a destination for convolution engines which are loaded on
  654. // a background thread.
  655. // Deriving from `enable_shared_from_this` allows us to capture a reference to
  656. // this object when adding commands to the background message queue.
  657. // That way, we can avoid dangling references in the background thread in the case
  658. // that a Convolution instance is deleted before the background message queue.
  659. class ConvolutionEngineQueue : public std::enable_shared_from_this<ConvolutionEngineQueue>
  660. {
  661. public:
  662. ConvolutionEngineQueue (BackgroundMessageQueue& queue,
  663. Convolution::Latency latencyIn,
  664. Convolution::NonUniform headSizeIn)
  665. : messageQueue (queue), factory (latencyIn, headSizeIn) {}
  666. void loadImpulseResponse (AudioBuffer<float>&& buffer,
  667. double sr,
  668. Convolution::Stereo stereo,
  669. Convolution::Trim trim,
  670. Convolution::Normalise normalise)
  671. {
  672. callLater ([b = std::move (buffer), sr, stereo, trim, normalise] (ConvolutionEngineFactory& f) mutable
  673. {
  674. f.setImpulseResponse ({ std::move (b), sr }, stereo, trim, normalise);
  675. });
  676. }
  677. void loadImpulseResponse (const void* sourceData,
  678. size_t sourceDataSize,
  679. Convolution::Stereo stereo,
  680. Convolution::Trim trim,
  681. size_t size,
  682. Convolution::Normalise normalise)
  683. {
  684. callLater ([sourceData, sourceDataSize, stereo, trim, size, normalise] (ConvolutionEngineFactory& f) mutable
  685. {
  686. setImpulseResponse (f, sourceData, sourceDataSize, stereo, trim, size, normalise);
  687. });
  688. }
  689. void loadImpulseResponse (const File& fileImpulseResponse,
  690. Convolution::Stereo stereo,
  691. Convolution::Trim trim,
  692. size_t size,
  693. Convolution::Normalise normalise)
  694. {
  695. callLater ([fileImpulseResponse, stereo, trim, size, normalise] (ConvolutionEngineFactory& f) mutable
  696. {
  697. setImpulseResponse (f, fileImpulseResponse, stereo, trim, size, normalise);
  698. });
  699. }
  700. void prepare (const ProcessSpec& spec)
  701. {
  702. factory.setProcessSpec (spec);
  703. }
  704. // Call this regularly to try to resend any pending message.
  705. // This allows us to always apply the most recently requested
  706. // state (eventually), even if the message queue fills up.
  707. void postPendingCommand()
  708. {
  709. if (pendingCommand == nullptr)
  710. return;
  711. if (messageQueue.push (pendingCommand))
  712. pendingCommand = nullptr;
  713. }
  714. std::unique_ptr<MultichannelEngine> getEngine() { return factory.getEngine(); }
  715. private:
  716. template <typename Fn>
  717. void callLater (Fn&& fn)
  718. {
  719. // If there was already a pending command (because the queue was full) we'll end up deleting it here.
  720. // Not much we can do about that!
  721. pendingCommand = [weak = weakFromThis(), callback = std::forward<Fn> (fn)]() mutable
  722. {
  723. if (auto t = weak.lock())
  724. callback (t->factory);
  725. };
  726. postPendingCommand();
  727. }
  728. std::weak_ptr<ConvolutionEngineQueue> weakFromThis() { return shared_from_this(); }
  729. BackgroundMessageQueue& messageQueue;
  730. ConvolutionEngineFactory factory;
  731. BackgroundMessageQueue::IncomingCommand pendingCommand;
  732. };
  733. class CrossoverMixer
  734. {
  735. public:
  736. void reset()
  737. {
  738. smoother.setCurrentAndTargetValue (1.0f);
  739. }
  740. void prepare (const ProcessSpec& spec)
  741. {
  742. smoother.reset (spec.sampleRate, 0.05);
  743. smootherBuffer.setSize (1, static_cast<int> (spec.maximumBlockSize));
  744. mixBuffer.setSize (static_cast<int> (spec.numChannels), static_cast<int> (spec.maximumBlockSize));
  745. reset();
  746. }
  747. template <typename ProcessCurrent, typename ProcessPrevious, typename NotifyDone>
  748. void processSamples (const AudioBlock<const float>& input,
  749. AudioBlock<float>& output,
  750. ProcessCurrent&& current,
  751. ProcessPrevious&& previous,
  752. NotifyDone&& notifyDone)
  753. {
  754. if (smoother.isSmoothing())
  755. {
  756. const auto numSamples = static_cast<int> (input.getNumSamples());
  757. for (auto sample = 0; sample != numSamples; ++sample)
  758. smootherBuffer.setSample (0, sample, smoother.getNextValue());
  759. AudioBlock<float> mixBlock (mixBuffer);
  760. mixBlock.clear();
  761. previous (input, mixBlock);
  762. for (size_t channel = 0; channel != output.getNumChannels(); ++channel)
  763. {
  764. FloatVectorOperations::multiply (mixBlock.getChannelPointer (channel),
  765. smootherBuffer.getReadPointer (0),
  766. numSamples);
  767. }
  768. FloatVectorOperations::multiply (smootherBuffer.getWritePointer (0), -1.0f, numSamples);
  769. FloatVectorOperations::add (smootherBuffer.getWritePointer (0), 1.0f, numSamples);
  770. current (input, output);
  771. for (size_t channel = 0; channel != output.getNumChannels(); ++channel)
  772. {
  773. FloatVectorOperations::multiply (output.getChannelPointer (channel),
  774. smootherBuffer.getReadPointer (0),
  775. numSamples);
  776. FloatVectorOperations::add (output.getChannelPointer (channel),
  777. mixBlock.getChannelPointer (channel),
  778. numSamples);
  779. }
  780. if (! smoother.isSmoothing())
  781. notifyDone();
  782. }
  783. else
  784. {
  785. current (input, output);
  786. }
  787. }
  788. void beginTransition()
  789. {
  790. smoother.setCurrentAndTargetValue (1.0f);
  791. smoother.setTargetValue (0.0f);
  792. }
  793. private:
  794. LinearSmoothedValue<float> smoother;
  795. AudioBuffer<float> smootherBuffer;
  796. AudioBuffer<float> mixBuffer;
  797. };
  798. using OptionalQueue = OptionalScopedPointer<ConvolutionMessageQueue>;
  799. class Convolution::Impl
  800. {
  801. public:
  802. Impl (Latency requiredLatency,
  803. NonUniform requiredHeadSize,
  804. OptionalQueue&& queue)
  805. : messageQueue (std::move (queue)),
  806. engineQueue (std::make_shared<ConvolutionEngineQueue> (*messageQueue->pimpl,
  807. requiredLatency,
  808. requiredHeadSize))
  809. {}
  810. void reset()
  811. {
  812. mixer.reset();
  813. if (currentEngine != nullptr)
  814. currentEngine->reset();
  815. destroyPreviousEngine();
  816. }
  817. void prepare (const ProcessSpec& spec)
  818. {
  819. messageQueue->pimpl->popAll();
  820. mixer.prepare (spec);
  821. engineQueue->prepare (spec);
  822. if (auto newEngine = engineQueue->getEngine())
  823. currentEngine = std::move (newEngine);
  824. previousEngine = nullptr;
  825. jassert (currentEngine != nullptr);
  826. }
  827. void processSamples (const AudioBlock<const float>& input, AudioBlock<float>& output)
  828. {
  829. engineQueue->postPendingCommand();
  830. if (previousEngine == nullptr)
  831. installPendingEngine();
  832. mixer.processSamples (input,
  833. output,
  834. [this] (const AudioBlock<const float>& in, AudioBlock<float>& out)
  835. {
  836. currentEngine->processSamples (in, out);
  837. },
  838. [this] (const AudioBlock<const float>& in, AudioBlock<float>& out)
  839. {
  840. if (previousEngine != nullptr)
  841. previousEngine->processSamples (in, out);
  842. else
  843. out.copyFrom (in);
  844. },
  845. [this] { destroyPreviousEngine(); });
  846. }
  847. int getCurrentIRSize() const { return currentEngine != nullptr ? currentEngine->getIRSize() : 0; }
  848. int getLatency() const { return currentEngine != nullptr ? currentEngine->getLatency() : 0; }
  849. void loadImpulseResponse (AudioBuffer<float>&& buffer,
  850. double originalSampleRate,
  851. Stereo stereo,
  852. Trim trim,
  853. Normalise normalise)
  854. {
  855. engineQueue->loadImpulseResponse (std::move (buffer), originalSampleRate, stereo, trim, normalise);
  856. }
  857. void loadImpulseResponse (const void* sourceData,
  858. size_t sourceDataSize,
  859. Stereo stereo,
  860. Trim trim,
  861. size_t size,
  862. Normalise normalise)
  863. {
  864. engineQueue->loadImpulseResponse (sourceData, sourceDataSize, stereo, trim, size, normalise);
  865. }
  866. void loadImpulseResponse (const File& fileImpulseResponse,
  867. Stereo stereo,
  868. Trim trim,
  869. size_t size,
  870. Normalise normalise)
  871. {
  872. engineQueue->loadImpulseResponse (fileImpulseResponse, stereo, trim, size, normalise);
  873. }
  874. private:
  875. void destroyPreviousEngine()
  876. {
  877. // If the queue is full, we'll destroy this straight away
  878. BackgroundMessageQueue::IncomingCommand command = [p = std::move (previousEngine)]() mutable { p = nullptr; };
  879. messageQueue->pimpl->push (command);
  880. }
  881. void installNewEngine (std::unique_ptr<MultichannelEngine> newEngine)
  882. {
  883. destroyPreviousEngine();
  884. previousEngine = std::move (currentEngine);
  885. currentEngine = std::move (newEngine);
  886. mixer.beginTransition();
  887. }
  888. void installPendingEngine()
  889. {
  890. if (auto newEngine = engineQueue->getEngine())
  891. installNewEngine (std::move (newEngine));
  892. }
  893. OptionalQueue messageQueue;
  894. std::shared_ptr<ConvolutionEngineQueue> engineQueue;
  895. std::unique_ptr<MultichannelEngine> previousEngine, currentEngine;
  896. CrossoverMixer mixer;
  897. };
  898. //==============================================================================
  899. void Convolution::Mixer::prepare (const ProcessSpec& spec)
  900. {
  901. for (auto& dry : volumeDry)
  902. dry.reset (spec.sampleRate, 0.05);
  903. for (auto& wet : volumeWet)
  904. wet.reset (spec.sampleRate, 0.05);
  905. sampleRate = spec.sampleRate;
  906. dryBlock = AudioBlock<float> (dryBlockStorage,
  907. jmin (spec.numChannels, 2u),
  908. spec.maximumBlockSize);
  909. }
  910. template <typename ProcessWet>
  911. void Convolution::Mixer::processSamples (const AudioBlock<const float>& input,
  912. AudioBlock<float>& output,
  913. bool isBypassed,
  914. ProcessWet&& processWet) noexcept
  915. {
  916. const auto numChannels = jmin (input.getNumChannels(), volumeDry.size());
  917. const auto numSamples = jmin (input.getNumSamples(), output.getNumSamples());
  918. auto dry = dryBlock.getSubsetChannelBlock (0, numChannels);
  919. if (volumeDry[0].isSmoothing())
  920. {
  921. dry.copyFrom (input);
  922. for (size_t channel = 0; channel < numChannels; ++channel)
  923. volumeDry[channel].applyGain (dry.getChannelPointer (channel), (int) numSamples);
  924. processWet (input, output);
  925. for (size_t channel = 0; channel < numChannels; ++channel)
  926. volumeWet[channel].applyGain (output.getChannelPointer (channel), (int) numSamples);
  927. output += dry;
  928. }
  929. else
  930. {
  931. if (! currentIsBypassed)
  932. processWet (input, output);
  933. if (isBypassed != currentIsBypassed)
  934. {
  935. currentIsBypassed = isBypassed;
  936. for (size_t channel = 0; channel < numChannels; ++channel)
  937. {
  938. volumeDry[channel].setTargetValue (isBypassed ? 0.0f : 1.0f);
  939. volumeDry[channel].reset (sampleRate, 0.05);
  940. volumeDry[channel].setTargetValue (isBypassed ? 1.0f : 0.0f);
  941. volumeWet[channel].setTargetValue (isBypassed ? 1.0f : 0.0f);
  942. volumeWet[channel].reset (sampleRate, 0.05);
  943. volumeWet[channel].setTargetValue (isBypassed ? 0.0f : 1.0f);
  944. }
  945. }
  946. }
  947. }
  948. void Convolution::Mixer::reset() { dryBlock.clear(); }
  949. //==============================================================================
  950. Convolution::Convolution()
  951. : Convolution (Latency { 0 })
  952. {}
  953. Convolution::Convolution (ConvolutionMessageQueue& queue)
  954. : Convolution (Latency { 0 }, queue)
  955. {}
  956. Convolution::Convolution (const Latency& requiredLatency)
  957. : Convolution (requiredLatency,
  958. {},
  959. OptionalQueue { std::make_unique<ConvolutionMessageQueue>() })
  960. {}
  961. Convolution::Convolution (const NonUniform& nonUniform)
  962. : Convolution ({},
  963. nonUniform,
  964. OptionalQueue { std::make_unique<ConvolutionMessageQueue>() })
  965. {}
  966. Convolution::Convolution (const Latency& requiredLatency, ConvolutionMessageQueue& queue)
  967. : Convolution (requiredLatency, {}, OptionalQueue { queue })
  968. {}
  969. Convolution::Convolution (const NonUniform& nonUniform, ConvolutionMessageQueue& queue)
  970. : Convolution ({}, nonUniform, OptionalQueue { queue })
  971. {}
  972. Convolution::Convolution (const Latency& latency,
  973. const NonUniform& nonUniform,
  974. OptionalQueue&& queue)
  975. : pimpl (std::make_unique<Impl> (latency, nonUniform, std::move (queue)))
  976. {}
  977. Convolution::~Convolution() noexcept = default;
  978. void Convolution::loadImpulseResponse (const void* sourceData,
  979. size_t sourceDataSize,
  980. Stereo stereo,
  981. Trim trim,
  982. size_t size,
  983. Normalise normalise)
  984. {
  985. pimpl->loadImpulseResponse (sourceData, sourceDataSize, stereo, trim, size, normalise);
  986. }
  987. void Convolution::loadImpulseResponse (const File& fileImpulseResponse,
  988. Stereo stereo,
  989. Trim trim,
  990. size_t size,
  991. Normalise normalise)
  992. {
  993. pimpl->loadImpulseResponse (fileImpulseResponse, stereo, trim, size, normalise);
  994. }
  995. void Convolution::loadImpulseResponse (AudioBuffer<float>&& buffer,
  996. double originalSampleRate,
  997. Stereo stereo,
  998. Trim trim,
  999. Normalise normalise)
  1000. {
  1001. pimpl->loadImpulseResponse (std::move (buffer), originalSampleRate, stereo, trim, normalise);
  1002. }
  1003. void Convolution::prepare (const ProcessSpec& spec)
  1004. {
  1005. mixer.prepare (spec);
  1006. pimpl->prepare (spec);
  1007. isActive = true;
  1008. }
  1009. void Convolution::reset() noexcept
  1010. {
  1011. mixer.reset();
  1012. pimpl->reset();
  1013. }
  1014. void Convolution::processSamples (const AudioBlock<const float>& input,
  1015. AudioBlock<float>& output,
  1016. bool isBypassed) noexcept
  1017. {
  1018. if (! isActive)
  1019. return;
  1020. jassert (input.getNumChannels() == output.getNumChannels());
  1021. jassert (isPositiveAndBelow (input.getNumChannels(), static_cast<size_t> (3))); // only mono and stereo is supported
  1022. mixer.processSamples (input, output, isBypassed, [this] (const auto& in, auto& out)
  1023. {
  1024. pimpl->processSamples (in, out);
  1025. });
  1026. }
  1027. int Convolution::getCurrentIRSize() const { return pimpl->getCurrentIRSize(); }
  1028. int Convolution::getLatency() const { return pimpl->getLatency(); }
  1029. } // namespace juce::dsp