Audio plugin host https://kx.studio/carla
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.

juce_Convolution.cpp 48KB

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