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.

1291 lines
48KB

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