LMMS
Loading...
Searching...
No Matches
juce_Convolution.cpp
Go to the documentation of this file.
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28namespace dsp
29{
30
31template <typename Element>
32class Queue
33{
34public:
35 explicit Queue (int size)
36 : fifo (size), storage (static_cast<size_t> (size)) {}
37
38 bool push (Element& element) noexcept
39 {
40 if (fifo.getFreeSpace() == 0)
41 return false;
42
43 const auto writer = fifo.write (1);
44
45 if (writer.blockSize1 != 0)
46 storage[static_cast<size_t> (writer.startIndex1)] = std::move (element);
47 else if (writer.blockSize2 != 0)
48 storage[static_cast<size_t> (writer.startIndex2)] = std::move (element);
49
50 return true;
51 }
52
53 template <typename Fn>
54 void pop (Fn&& fn) { popN (1, std::forward<Fn> (fn)); }
55
56 template <typename Fn>
57 void popAll (Fn&& fn) { popN (fifo.getNumReady(), std::forward<Fn> (fn)); }
58
59 bool hasPendingMessages() const noexcept { return fifo.getNumReady() > 0; }
60
61private:
62 template <typename Fn>
63 void popN (int n, Fn&& fn)
64 {
65 fifo.read (n).forEach ([&] (int index)
66 {
67 fn (storage[static_cast<size_t> (index)]);
68 });
69 }
70
72 std::vector<Element> storage;
73};
74
76{
77public:
78 explicit BackgroundMessageQueue (int entries)
79 : Thread ("Convolution background loader"), queue (entries)
80 {}
81
83
84 // Push functions here, and they'll be called later on a background thread.
85 // This function is wait-free.
86 // This function is only safe to call from a single thread at a time.
87 bool push (IncomingCommand& command) { return queue.push (command); }
88
89 void popAll()
90 {
91 const ScopedLock lock (popMutex);
92 queue.popAll ([] (IncomingCommand& command) { command(); command = nullptr; });
93 }
94
97
98private:
99 void run() override
100 {
101 while (! threadShouldExit())
102 {
103 const auto tryPop = [&]
104 {
105 const ScopedLock lock (popMutex);
106
107 if (! queue.hasPendingMessages())
108 return false;
109
110 queue.pop ([] (IncomingCommand& command) { command(); command = nullptr;});
111 return true;
112 };
113
114 if (! tryPop())
115 sleep (10);
116 }
117 }
118
121
123};
124
129
133
135 : pimpl (std::make_unique<Impl> (entries))
136{
137 pimpl->startThread();
138}
139
144
147
148//==============================================================================
150{
151 ConvolutionEngine (const float* samples,
152 size_t numSamples,
153 size_t maxBlockSize)
154 : blockSize ((size_t) nextPowerOfTwo ((int) maxBlockSize)),
155 fftSize (blockSize > 128 ? 2 * blockSize : 4 * blockSize),
156 fftObject (std::make_unique<FFT> (roundToInt (std::log2 (fftSize)))),
157 numSegments (numSamples / (fftSize - blockSize) + 1u),
159 bufferInput (1, static_cast<int> (fftSize)),
160 bufferOutput (1, static_cast<int> (fftSize * 2)),
161 bufferTempOutput (1, static_cast<int> (fftSize * 2)),
162 bufferOverlap (1, static_cast<int> (fftSize))
163 {
164 bufferOutput.clear();
165
166 auto updateSegmentsIfNecessary = [this] (size_t numSegmentsToUpdate,
167 std::vector<AudioBuffer<float>>& segments)
168 {
169 if (numSegmentsToUpdate == 0
170 || numSegmentsToUpdate != (size_t) segments.size()
171 || (size_t) segments[0].getNumSamples() != fftSize * 2)
172 {
173 segments.clear();
174
175 for (size_t i = 0; i < numSegmentsToUpdate; ++i)
176 segments.push_back ({ 1, static_cast<int> (fftSize * 2) });
177 }
178 };
179
180 updateSegmentsIfNecessary (numInputSegments, buffersInputSegments);
181 updateSegmentsIfNecessary (numSegments, buffersImpulseSegments);
182
183 auto FFTTempObject = std::make_unique<FFT> (roundToInt (std::log2 (fftSize)));
184 size_t currentPtr = 0;
185
186 for (auto& buf : buffersImpulseSegments)
187 {
188 buf.clear();
189
190 auto* impulseResponse = buf.getWritePointer (0);
191
192 if (&buf == &buffersImpulseSegments.front())
193 impulseResponse[0] = 1.0f;
194
195 FloatVectorOperations::copy (impulseResponse,
196 samples + currentPtr,
197 static_cast<int> (jmin (fftSize - blockSize, numSamples - currentPtr)));
198
199 FFTTempObject->performRealOnlyForwardTransform (impulseResponse);
200 prepareForConvolution (impulseResponse);
201
202 currentPtr += (fftSize - blockSize);
203 }
204
205 reset();
206 }
207
208 void reset()
209 {
210 bufferInput.clear();
211 bufferOverlap.clear();
212 bufferTempOutput.clear();
213 bufferOutput.clear();
214
215 for (auto& buf : buffersInputSegments)
216 buf.clear();
217
218 currentSegment = 0;
219 inputDataPos = 0;
220 }
221
222 void processSamples (const float* input, float* output, size_t numSamples)
223 {
224 // Overlap-add, zero latency convolution algorithm with uniform partitioning
225 size_t numSamplesProcessed = 0;
226
227 auto indexStep = numInputSegments / numSegments;
228
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
234 while (numSamplesProcessed < numSamples)
235 {
236 const bool inputDataWasEmpty = (inputDataPos == 0);
237 auto numSamplesToProcess = jmin (numSamples - numSamplesProcessed, blockSize - inputDataPos);
238
239 FloatVectorOperations::copy (inputData + inputDataPos, input + numSamplesProcessed, static_cast<int> (numSamplesToProcess));
240
241 auto* inputSegmentData = buffersInputSegments[currentSegment].getWritePointer (0);
242 FloatVectorOperations::copy (inputSegmentData, inputData, static_cast<int> (fftSize));
243
244 fftObject->performRealOnlyForwardTransform (inputSegmentData);
245 prepareForConvolution (inputSegmentData);
246
247 // Complex multiplication
248 if (inputDataWasEmpty)
249 {
250 FloatVectorOperations::fill (outputTempData, 0, static_cast<int> (fftSize + 1));
251
252 auto index = currentSegment;
253
254 for (size_t i = 1; i < numSegments; ++i)
255 {
256 index += indexStep;
257
258 if (index >= numInputSegments)
259 index -= numInputSegments;
260
262 buffersImpulseSegments[i].getWritePointer (0),
263 outputTempData);
264 }
265 }
266
267 FloatVectorOperations::copy (outputData, outputTempData, static_cast<int> (fftSize + 1));
268
269 convolutionProcessingAndAccumulate (inputSegmentData,
270 buffersImpulseSegments.front().getWritePointer (0),
271 outputData);
272
274 fftObject->performRealOnlyInverseTransform (outputData);
275
276 // Add overlap
277 FloatVectorOperations::add (&output[numSamplesProcessed], &outputData[inputDataPos], &overlapData[inputDataPos], (int) numSamplesToProcess);
278
279 // Input buffer full => Next block
280 inputDataPos += numSamplesToProcess;
281
282 if (inputDataPos == blockSize)
283 {
284 // Input buffer is empty again now
285 FloatVectorOperations::fill (inputData, 0.0f, static_cast<int> (fftSize));
286
287 inputDataPos = 0;
288
289 // Extra step for segSize > blockSize
290 FloatVectorOperations::add (&(outputData[blockSize]), &(overlapData[blockSize]), static_cast<int> (fftSize - 2 * blockSize));
291
292 // Save the overlap
293 FloatVectorOperations::copy (overlapData, &(outputData[blockSize]), static_cast<int> (fftSize - blockSize));
294
296 }
297
298 numSamplesProcessed += numSamplesToProcess;
299 }
300 }
301
302 void processSamplesWithAddedLatency (const float* input, float* output, size_t numSamples)
303 {
304 // Overlap-add, zero latency convolution algorithm with uniform partitioning
305 size_t numSamplesProcessed = 0;
306
307 auto indexStep = numInputSegments / numSegments;
308
309 auto* inputData = bufferInput.getWritePointer (0);
310 auto* outputTempData = bufferTempOutput.getWritePointer (0);
311 auto* outputData = bufferOutput.getWritePointer (0);
312 auto* overlapData = bufferOverlap.getWritePointer (0);
313
314 while (numSamplesProcessed < numSamples)
315 {
316 auto numSamplesToProcess = jmin (numSamples - numSamplesProcessed, blockSize - inputDataPos);
317
318 FloatVectorOperations::copy (inputData + inputDataPos, input + numSamplesProcessed, static_cast<int> (numSamplesToProcess));
319
320 FloatVectorOperations::copy (output + numSamplesProcessed, outputData + inputDataPos, static_cast<int> (numSamplesToProcess));
321
322 numSamplesProcessed += numSamplesToProcess;
323 inputDataPos += numSamplesToProcess;
324
325 // processing itself when needed (with latency)
326 if (inputDataPos == blockSize)
327 {
328 // Copy input data in input segment
329 auto* inputSegmentData = buffersInputSegments[currentSegment].getWritePointer (0);
330 FloatVectorOperations::copy (inputSegmentData, inputData, static_cast<int> (fftSize));
331
332 fftObject->performRealOnlyForwardTransform (inputSegmentData);
333 prepareForConvolution (inputSegmentData);
334
335 // Complex multiplication
336 FloatVectorOperations::fill (outputTempData, 0, static_cast<int> (fftSize + 1));
337
338 auto index = currentSegment;
339
340 for (size_t i = 1; i < numSegments; ++i)
341 {
342 index += indexStep;
343
344 if (index >= numInputSegments)
345 index -= numInputSegments;
346
348 buffersImpulseSegments[i].getWritePointer (0),
349 outputTempData);
350 }
351
352 FloatVectorOperations::copy (outputData, outputTempData, static_cast<int> (fftSize + 1));
353
354 convolutionProcessingAndAccumulate (inputSegmentData,
355 buffersImpulseSegments.front().getWritePointer (0),
356 outputData);
357
359 fftObject->performRealOnlyInverseTransform (outputData);
360
361 // Add overlap
362 FloatVectorOperations::add (outputData, overlapData, static_cast<int> (blockSize));
363
364 // Input buffer is empty again now
365 FloatVectorOperations::fill (inputData, 0.0f, static_cast<int> (fftSize));
366
367 // Extra step for segSize > blockSize
368 FloatVectorOperations::add (&(outputData[blockSize]), &(overlapData[blockSize]), static_cast<int> (fftSize - 2 * blockSize));
369
370 // Save the overlap
371 FloatVectorOperations::copy (overlapData, &(outputData[blockSize]), static_cast<int> (fftSize - blockSize));
372
374
375 inputDataPos = 0;
376 }
377 }
378 }
379
380 // After each FFT, this function is called to allow convolution to be performed with only 4 SIMD functions calls.
381 void prepareForConvolution (float *samples) noexcept
382 {
383 auto FFTSizeDiv2 = fftSize / 2;
384
385 for (size_t i = 0; i < FFTSizeDiv2; i++)
386 samples[i] = samples[i << 1];
387
388 samples[FFTSizeDiv2] = 0;
389
390 for (size_t i = 1; i < FFTSizeDiv2; i++)
391 samples[i + FFTSizeDiv2] = -samples[((fftSize - i) << 1) + 1];
392 }
393
394 // Does the convolution operation itself only on half of the frequency domain samples.
395 void convolutionProcessingAndAccumulate (const float *input, const float *impulse, float *output)
396 {
397 auto FFTSizeDiv2 = fftSize / 2;
398
399 FloatVectorOperations::addWithMultiply (output, input, impulse, static_cast<int> (FFTSizeDiv2));
400 FloatVectorOperations::subtractWithMultiply (output, &(input[FFTSizeDiv2]), &(impulse[FFTSizeDiv2]), static_cast<int> (FFTSizeDiv2));
401
402 FloatVectorOperations::addWithMultiply (&(output[FFTSizeDiv2]), input, &(impulse[FFTSizeDiv2]), static_cast<int> (FFTSizeDiv2));
403 FloatVectorOperations::addWithMultiply (&(output[FFTSizeDiv2]), &(input[FFTSizeDiv2]), impulse, static_cast<int> (FFTSizeDiv2));
404
405 output[fftSize] += input[fftSize] * impulse[fftSize];
406 }
407
408 // Undoes the re-organization of samples from the function prepareForConvolution.
409 // Then takes the conjugate of the frequency domain first half of samples to fill the
410 // second half, so that the inverse transform will return real samples in the time domain.
411 void updateSymmetricFrequencyDomainData (float* samples) noexcept
412 {
413 auto FFTSizeDiv2 = fftSize / 2;
414
415 for (size_t i = 1; i < FFTSizeDiv2; i++)
416 {
417 samples[(fftSize - i) << 1] = samples[i];
418 samples[((fftSize - i) << 1) + 1] = -samples[FFTSizeDiv2 + i];
419 }
420
421 samples[1] = 0.f;
422
423 for (size_t i = 1; i < FFTSizeDiv2; i++)
424 {
425 samples[i << 1] = samples[(fftSize - i) << 1];
426 samples[(i << 1) + 1] = -samples[((fftSize - i) << 1) + 1];
427 }
428 }
429
430 //==============================================================================
431 const size_t blockSize;
432 const size_t fftSize;
433 const std::unique_ptr<FFT> fftObject;
434 const size_t numSegments;
435 const size_t numInputSegments;
437
439 std::vector<AudioBuffer<float>> buffersInputSegments, buffersImpulseSegments;
440};
441
442//==============================================================================
444{
445public:
447 int maxBlockSize,
448 int maxBufferSize,
449 Convolution::NonUniform headSizeIn,
450 bool isZeroDelayIn)
451 : tailBuffer (1, maxBlockSize),
452 latency (isZeroDelayIn ? 0 : maxBufferSize),
453 irSize (buf.getNumSamples()),
454 blockSize (maxBlockSize),
455 isZeroDelay (isZeroDelayIn)
456 {
457 constexpr auto numChannels = 2;
458
459 const auto makeEngine = [&] (int channel, int offset, int length, uint32 thisBlockSize)
460 {
461 return std::make_unique<ConvolutionEngine> (buf.getReadPointer (jmin (buf.getNumChannels() - 1, channel), offset),
462 length,
463 static_cast<size_t> (thisBlockSize));
464 };
465
466 if (headSizeIn.headSizeInSamples == 0)
467 {
468 for (int i = 0; i < numChannels; ++i)
469 head.emplace_back (makeEngine (i, 0, buf.getNumSamples(), static_cast<uint32> (maxBufferSize)));
470 }
471 else
472 {
473 const auto size = jmin (buf.getNumSamples(), headSizeIn.headSizeInSamples);
474
475 for (int i = 0; i < numChannels; ++i)
476 head.emplace_back (makeEngine (i, 0, size, static_cast<uint32> (maxBufferSize)));
477
478 const auto tailBufferSize = static_cast<uint32> (headSizeIn.headSizeInSamples + (isZeroDelay ? 0 : maxBufferSize));
479
480 if (size != buf.getNumSamples())
481 for (int i = 0; i < numChannels; ++i)
482 tail.emplace_back (makeEngine (i, size, buf.getNumSamples() - size, tailBufferSize));
483 }
484 }
485
486 void reset()
487 {
488 for (const auto& e : head)
489 e->reset();
490
491 for (const auto& e : tail)
492 e->reset();
493 }
494
496 {
497 const auto numChannels = jmin (head.size(), input.getNumChannels(), output.getNumChannels());
498 const auto numSamples = jmin (input.getNumSamples(), output.getNumSamples());
499
500 const AudioBlock<float> fullTailBlock (tailBuffer);
501 const auto tailBlock = fullTailBlock.getSubBlock (0, (size_t) numSamples);
502
503 const auto isUniform = tail.empty();
504
505 for (size_t channel = 0; channel < numChannels; ++channel)
506 {
507 if (! isUniform)
508 tail[channel]->processSamplesWithAddedLatency (input.getChannelPointer (channel),
509 tailBlock.getChannelPointer (0),
510 numSamples);
511
512 if (isZeroDelay)
513 head[channel]->processSamples (input.getChannelPointer (channel),
514 output.getChannelPointer (channel),
515 numSamples);
516 else
517 head[channel]->processSamplesWithAddedLatency (input.getChannelPointer (channel),
518 output.getChannelPointer (channel),
519 numSamples);
520
521 if (! isUniform)
522 output.getSingleChannelBlock (channel) += tailBlock;
523 }
524
525 const auto numOutputChannels = output.getNumChannels();
526
527 for (auto i = numChannels; i < numOutputChannels; ++i)
529 }
530
531 int getIRSize() const noexcept { return irSize; }
534
535private:
536 std::vector<std::unique_ptr<ConvolutionEngine>> head, tail;
538
539 const int latency;
540 const int irSize;
541 const int blockSize;
542 const bool isZeroDelay;
543};
544
546{
547 const auto numChannels = jmin (buf.getNumChannels(), stereo == Convolution::Stereo::yes ? 2 : 1);
548 const auto numSamples = buf.getNumSamples();
549
550 AudioBuffer<float> result (numChannels, buf.getNumSamples());
551
552 for (auto channel = 0; channel != numChannels; ++channel)
553 result.copyFrom (channel, 0, buf.getReadPointer (channel), numSamples);
554
555 if (result.getNumSamples() == 0 || result.getNumChannels() == 0)
556 {
557 result.setSize (1, 1);
558 result.setSample (0, 0, 1.0f);
559 }
560
561 return result;
562}
563
565{
566 const auto thresholdTrim = Decibels::decibelsToGain (-80.0f);
567
568 const auto numChannels = buf.getNumChannels();
569 const auto numSamples = buf.getNumSamples();
570
571 std::ptrdiff_t offsetBegin = numSamples;
572 std::ptrdiff_t offsetEnd = numSamples;
573
574 for (auto channel = 0; channel < numChannels; ++channel)
575 {
576 const auto indexAboveThreshold = [&] (auto begin, auto end)
577 {
578 return std::distance (begin, std::find_if (begin, end, [&] (float sample)
579 {
580 return std::abs (sample) >= thresholdTrim;
581 }));
582 };
583
584 const auto channelBegin = buf.getReadPointer (channel);
585 const auto channelEnd = channelBegin + numSamples;
586 const auto itStart = indexAboveThreshold (channelBegin, channelEnd);
587 const auto itEnd = indexAboveThreshold (std::make_reverse_iterator (channelEnd),
588 std::make_reverse_iterator (channelBegin));
589
590 offsetBegin = jmin (offsetBegin, itStart);
591 offsetEnd = jmin (offsetEnd, itEnd);
592 }
593
594 if (offsetBegin == numSamples)
595 {
596 auto result = AudioBuffer<float> (numChannels, 1);
597 result.clear();
598 return result;
599 }
600
601 const auto newLength = jmax (1, numSamples - static_cast<int> (offsetBegin + offsetEnd));
602
603 AudioBuffer<float> result (numChannels, newLength);
604
605 for (auto channel = 0; channel < numChannels; ++channel)
606 {
607 result.copyFrom (channel,
608 0,
609 buf.getReadPointer (channel, static_cast<int> (offsetBegin)),
610 result.getNumSamples());
611 }
612
613 return result;
614}
615
616static float calculateNormalisationFactor (float sumSquaredMagnitude)
617{
618 if (sumSquaredMagnitude < 1e-8f)
619 return 1.0f;
620
621 return 0.125f / std::sqrt (sumSquaredMagnitude);
622}
623
625{
626 const auto numChannels = buf.getNumChannels();
627 const auto numSamples = buf.getNumSamples();
628 const auto channelPtrs = buf.getArrayOfWritePointers();
629
630 const auto maxSumSquaredMag = std::accumulate (channelPtrs, channelPtrs + numChannels, 0.0f, [numSamples] (auto max, auto* channel)
631 {
632 return jmax (max, std::accumulate (channel, channel + numSamples, 0.0f, [] (auto sum, auto samp)
633 {
634 return sum + (samp * samp);
635 }));
636 });
637
638 const auto normalisationFactor = calculateNormalisationFactor (maxSumSquaredMag);
639
640 std::for_each (channelPtrs, channelPtrs + numChannels, [normalisationFactor, numSamples] (auto* channel)
641 {
642 FloatVectorOperations::multiply (channel, normalisationFactor, numSamples);
643 });
644}
645
647 const double srcSampleRate,
648 const double destSampleRate)
649{
650 if (srcSampleRate == destSampleRate)
651 return buf;
652
653 const auto factorReading = srcSampleRate / destSampleRate;
654
655 AudioBuffer<float> original = buf;
656 MemoryAudioSource memorySource (original, false);
657 ResamplingAudioSource resamplingSource (&memorySource, false, buf.getNumChannels());
658
659 const auto finalSize = roundToInt (jmax (1.0, buf.getNumSamples() / factorReading));
660 resamplingSource.setResamplingRatio (factorReading);
661 resamplingSource.prepareToPlay (finalSize, srcSampleRate);
662
663 AudioBuffer<float> result (buf.getNumChannels(), finalSize);
664 resamplingSource.getNextAudioBlock ({ &result, 0, result.getNumSamples() });
665
666 return result;
667}
668
669//==============================================================================
670template <typename Element>
672{
673public:
674 void set (std::unique_ptr<Element> p)
675 {
676 const SpinLock::ScopedLockType lock (mutex);
677 ptr = std::move (p);
678 }
679
680 std::unique_ptr<MultichannelEngine> get()
681 {
683 return lock.isLocked() ? std::move (ptr) : nullptr;
684 }
685
686private:
687 std::unique_ptr<Element> ptr;
689};
690
692{
694
695 BufferWithSampleRate (AudioBuffer<float>&& bufferIn, double sampleRateIn)
696 : buffer (std::move (bufferIn)), sampleRate (sampleRateIn) {}
697
699 double sampleRate = 0.0;
700};
701
702static BufferWithSampleRate loadStreamToBuffer (std::unique_ptr<InputStream> stream, size_t maxLength)
703{
704 AudioFormatManager manager;
705 manager.registerBasicFormats();
706 std::unique_ptr<AudioFormatReader> formatReader (manager.createReaderFor (std::move (stream)));
707
708 if (formatReader == nullptr)
709 return {};
710
711 const auto fileLength = static_cast<size_t> (formatReader->lengthInSamples);
712 const auto lengthToLoad = maxLength == 0 ? fileLength : jmin (maxLength, fileLength);
713
714 BufferWithSampleRate result { { jlimit (1, 2, static_cast<int> (formatReader->numChannels)),
715 static_cast<int> (lengthToLoad) },
716 formatReader->sampleRate };
717
718 formatReader->read (result.buffer.getArrayOfWritePointers(),
719 result.buffer.getNumChannels(),
720 0,
721 result.buffer.getNumSamples());
722
723 return result;
724}
725
726// This class caches the data required to build a new convolution engine
727// (in particular, impulse response data and a ProcessSpec).
728// Calls to `setProcessSpec` and `setImpulseResponse` construct a
729// new engine, which can be retrieved by calling `getEngine`.
731{
732public:
734 Convolution::NonUniform requiredHeadSize)
735 : latency { (requiredLatency.latencyInSamples <= 0) ? 0 : jmax (64, nextPowerOfTwo (requiredLatency.latencyInSamples)) },
736 headSize { (requiredHeadSize.headSizeInSamples <= 0) ? 0 : jmax (64, nextPowerOfTwo (requiredHeadSize.headSizeInSamples)) },
737 shouldBeZeroLatency (requiredLatency.latencyInSamples == 0)
738 {}
739
740 // It is safe to call this method simultaneously with other public
741 // member functions.
742 void setProcessSpec (const ProcessSpec& spec)
743 {
744 const std::lock_guard<std::mutex> lock (mutex);
745 processSpec = spec;
746
747 engine.set (makeEngine());
748 }
749
750 // It is safe to call this method simultaneously with other public
751 // member functions.
753 Convolution::Stereo stereo,
755 Convolution::Normalise normalise)
756 {
757 const std::lock_guard<std::mutex> lock (mutex);
758 wantsNormalise = normalise;
759 originalSampleRate = buf.sampleRate;
760
761 impulseResponse = [&]
762 {
763 auto corrected = fixNumChannels (buf.buffer, stereo);
764 return trim == Convolution::Trim::yes ? trimImpulseResponse (corrected) : corrected;
765 }();
766
767 engine.set (makeEngine());
768 }
769
770 // Returns the most recently-created engine, or nullptr
771 // if there is no pending engine, or if the engine is currently
772 // being updated by one of the setter methods.
773 // It is safe to call this simultaneously with other public
774 // member functions.
775 std::unique_ptr<MultichannelEngine> getEngine() { return engine.get(); }
776
777private:
778 std::unique_ptr<MultichannelEngine> makeEngine()
779 {
781
783 normaliseImpulseResponse (resampled);
784 else
785 resampled.applyGain ((float) (originalSampleRate / processSpec.sampleRate));
786
787 const auto currentLatency = jmax (processSpec.maximumBlockSize, (uint32) latency.latencyInSamples);
788 const auto maxBufferSize = shouldBeZeroLatency ? static_cast<int> (processSpec.maximumBlockSize)
789 : nextPowerOfTwo (static_cast<int> (currentLatency));
790
791 return std::make_unique<MultichannelEngine> (resampled,
792 processSpec.maximumBlockSize,
793 maxBufferSize,
794 headSize,
796 }
797
799 {
801 result.setSample (0, 0, 1.0f);
802 return result;
803 }
804
805 ProcessSpec processSpec { 44100.0, 128, 2 };
807 double originalSampleRate = processSpec.sampleRate;
812
814
815 mutable std::mutex mutex;
816};
817
819 const void* sourceData,
820 size_t sourceDataSize,
821 Convolution::Stereo stereo,
823 size_t size,
824 Convolution::Normalise normalise)
825{
826 factory.setImpulseResponse (loadStreamToBuffer (std::make_unique<MemoryInputStream> (sourceData, sourceDataSize, false), size),
827 stereo, trim, normalise);
828}
829
831 const File& fileImpulseResponse,
832 Convolution::Stereo stereo,
834 size_t size,
835 Convolution::Normalise normalise)
836{
837 factory.setImpulseResponse (loadStreamToBuffer (std::make_unique<FileInputStream> (fileImpulseResponse), size),
838 stereo, trim, normalise);
839}
840
841// This class acts as a destination for convolution engines which are loaded on
842// a background thread.
843
844// Deriving from `enable_shared_from_this` allows us to capture a reference to
845// this object when adding commands to the background message queue.
846// That way, we can avoid dangling references in the background thread in the case
847// that a Convolution instance is deleted before the background message queue.
848class ConvolutionEngineQueue : public std::enable_shared_from_this<ConvolutionEngineQueue>
849{
850public:
852 Convolution::Latency latencyIn,
853 Convolution::NonUniform headSizeIn)
854 : messageQueue (queue), factory (latencyIn, headSizeIn) {}
855
857 double sr,
858 Convolution::Stereo stereo,
860 Convolution::Normalise normalise)
861 {
862 callLater ([b = std::move (buffer), sr, stereo, trim, normalise] (ConvolutionEngineFactory& f) mutable
863 {
864 f.setImpulseResponse ({ std::move (b), sr }, stereo, trim, normalise);
865 });
866 }
867
868 void loadImpulseResponse (const void* sourceData,
869 size_t sourceDataSize,
870 Convolution::Stereo stereo,
872 size_t size,
873 Convolution::Normalise normalise)
874 {
875 callLater ([sourceData, sourceDataSize, stereo, trim, size, normalise] (ConvolutionEngineFactory& f) mutable
876 {
877 setImpulseResponse (f, sourceData, sourceDataSize, stereo, trim, size, normalise);
878 });
879 }
880
881 void loadImpulseResponse (const File& fileImpulseResponse,
882 Convolution::Stereo stereo,
884 size_t size,
885 Convolution::Normalise normalise)
886 {
887 callLater ([fileImpulseResponse, stereo, trim, size, normalise] (ConvolutionEngineFactory& f) mutable
888 {
889 setImpulseResponse (f, fileImpulseResponse, stereo, trim, size, normalise);
890 });
891 }
892
893 void prepare (const ProcessSpec& spec)
894 {
895 factory.setProcessSpec (spec);
896 }
897
898 // Call this regularly to try to resend any pending message.
899 // This allows us to always apply the most recently requested
900 // state (eventually), even if the message queue fills up.
902 {
903 if (pendingCommand == nullptr)
904 return;
905
906 if (messageQueue.push (pendingCommand))
907 pendingCommand = nullptr;
908 }
909
910 std::unique_ptr<MultichannelEngine> getEngine() { return factory.getEngine(); }
911
912private:
913 template <typename Fn>
914 void callLater (Fn&& fn)
915 {
916 // If there was already a pending command (because the queue was full) we'll end up deleting it here.
917 // Not much we can do about that!
918 pendingCommand = [weak = weakFromThis(), callback = std::forward<Fn> (fn)]() mutable
919 {
920 if (auto t = weak.lock())
921 callback (t->factory);
922 };
923
925 }
926
927 std::weak_ptr<ConvolutionEngineQueue> weakFromThis() { return shared_from_this(); }
928
932};
933
935{
936public:
937 void reset()
938 {
939 smoother.setCurrentAndTargetValue (1.0f);
940 }
941
942 void prepare (const ProcessSpec& spec)
943 {
944 smoother.reset (spec.sampleRate, 0.05);
945 smootherBuffer.setSize (1, static_cast<int> (spec.maximumBlockSize));
946 mixBuffer.setSize (static_cast<int> (spec.numChannels), static_cast<int> (spec.maximumBlockSize));
947 reset();
948 }
949
950 template <typename ProcessCurrent, typename ProcessPrevious, typename NotifyDone>
952 AudioBlock<float>& output,
953 ProcessCurrent&& current,
954 ProcessPrevious&& previous,
955 NotifyDone&& notifyDone)
956 {
957 if (smoother.isSmoothing())
958 {
959 const auto numSamples = static_cast<int> (input.getNumSamples());
960
961 for (auto sample = 0; sample != numSamples; ++sample)
962 smootherBuffer.setSample (0, sample, smoother.getNextValue());
963
964 AudioBlock<float> mixBlock (mixBuffer);
965 mixBlock.clear();
966 previous (input, mixBlock);
967
968 for (size_t channel = 0; channel != output.getNumChannels(); ++channel)
969 {
970 FloatVectorOperations::multiply (mixBlock.getChannelPointer (channel),
971 smootherBuffer.getReadPointer (0),
972 numSamples);
973 }
974
975 FloatVectorOperations::multiply (smootherBuffer.getWritePointer (0), -1.0f, numSamples);
976 FloatVectorOperations::add (smootherBuffer.getWritePointer (0), 1.0f, numSamples);
977
978 current (input, output);
979
980 for (size_t channel = 0; channel != output.getNumChannels(); ++channel)
981 {
982 FloatVectorOperations::multiply (output.getChannelPointer (channel),
983 smootherBuffer.getReadPointer (0),
984 numSamples);
985 FloatVectorOperations::add (output.getChannelPointer (channel),
986 mixBlock.getChannelPointer (channel),
987 numSamples);
988 }
989
990 if (! smoother.isSmoothing())
991 notifyDone();
992 }
993 else
994 {
995 current (input, output);
996 }
997 }
998
1000 {
1001 smoother.setCurrentAndTargetValue (1.0f);
1002 smoother.setTargetValue (0.0f);
1003 }
1004
1005private:
1009};
1010
1012
1014{
1015public:
1016 Impl (Latency requiredLatency,
1017 NonUniform requiredHeadSize,
1018 OptionalQueue&& queue)
1019 : messageQueue (std::move (queue)),
1021 requiredLatency,
1022 requiredHeadSize))
1023 {}
1024
1025 void reset()
1026 {
1027 mixer.reset();
1028
1029 if (currentEngine != nullptr)
1030 currentEngine->reset();
1031
1033 }
1034
1035 void prepare (const ProcessSpec& spec)
1036 {
1037 messageQueue->pimpl->popAll();
1038 mixer.prepare (spec);
1039 engineQueue->prepare (spec);
1040
1041 if (auto newEngine = engineQueue->getEngine())
1042 currentEngine = std::move (newEngine);
1043
1044 previousEngine = nullptr;
1045 jassert (currentEngine != nullptr);
1046 }
1047
1049 {
1050 engineQueue->postPendingCommand();
1051
1052 if (previousEngine == nullptr)
1054
1055 mixer.processSamples (input,
1056 output,
1058 {
1059 currentEngine->processSamples (in, out);
1060 },
1062 {
1063 if (previousEngine != nullptr)
1064 previousEngine->processSamples (in, out);
1065 else
1066 out.copyFrom (in);
1067 },
1068 [this] { destroyPreviousEngine(); });
1069 }
1070
1071 int getCurrentIRSize() const { return currentEngine != nullptr ? currentEngine->getIRSize() : 0; }
1072
1073 int getLatency() const { return currentEngine != nullptr ? currentEngine->getLatency() : 0; }
1074
1076 double originalSampleRate,
1077 Stereo stereo,
1078 Trim trim,
1079 Normalise normalise)
1080 {
1081 engineQueue->loadImpulseResponse (std::move (buffer), originalSampleRate, stereo, trim, normalise);
1082 }
1083
1084 void loadImpulseResponse (const void* sourceData,
1085 size_t sourceDataSize,
1086 Stereo stereo,
1087 Trim trim,
1088 size_t size,
1089 Normalise normalise)
1090 {
1091 engineQueue->loadImpulseResponse (sourceData, sourceDataSize, stereo, trim, size, normalise);
1092 }
1093
1094 void loadImpulseResponse (const File& fileImpulseResponse,
1095 Stereo stereo,
1096 Trim trim,
1097 size_t size,
1098 Normalise normalise)
1099 {
1100 engineQueue->loadImpulseResponse (fileImpulseResponse, stereo, trim, size, normalise);
1101 }
1102
1103private:
1105 {
1106 // If the queue is full, we'll destroy this straight away
1107 BackgroundMessageQueue::IncomingCommand command = [p = std::move (previousEngine)]() mutable { p = nullptr; };
1108 messageQueue->pimpl->push (command);
1109 }
1110
1111 void installNewEngine (std::unique_ptr<MultichannelEngine> newEngine)
1112 {
1114 previousEngine = std::move (currentEngine);
1115 currentEngine = std::move (newEngine);
1116 mixer.beginTransition();
1117 }
1118
1120 {
1121 if (auto newEngine = engineQueue->getEngine())
1122 installNewEngine (std::move (newEngine));
1123 }
1124
1126 std::shared_ptr<ConvolutionEngineQueue> engineQueue;
1127 std::unique_ptr<MultichannelEngine> previousEngine, currentEngine;
1129};
1130
1131//==============================================================================
1133{
1134 for (auto& dry : volumeDry)
1135 dry.reset (spec.sampleRate, 0.05);
1136
1137 for (auto& wet : volumeWet)
1138 wet.reset (spec.sampleRate, 0.05);
1139
1140 sampleRate = spec.sampleRate;
1141
1143 jmin (spec.numChannels, 2u),
1144 spec.maximumBlockSize);
1145
1146}
1147
1148template <typename ProcessWet>
1150 AudioBlock<float>& output,
1151 bool isBypassed,
1152 ProcessWet&& processWet) noexcept
1153{
1154 const auto numChannels = jmin (input.getNumChannels(), volumeDry.size());
1155 const auto numSamples = jmin (input.getNumSamples(), output.getNumSamples());
1156
1157 auto dry = dryBlock.getSubsetChannelBlock (0, numChannels);
1158
1159 if (volumeDry[0].isSmoothing())
1160 {
1161 dry.copyFrom (input);
1162
1163 for (size_t channel = 0; channel < numChannels; ++channel)
1164 volumeDry[channel].applyGain (dry.getChannelPointer (channel), (int) numSamples);
1165
1166 processWet (input, output);
1167
1168 for (size_t channel = 0; channel < numChannels; ++channel)
1169 volumeWet[channel].applyGain (output.getChannelPointer (channel), (int) numSamples);
1170
1171 output += dry;
1172 }
1173 else
1174 {
1175 if (! currentIsBypassed)
1176 processWet (input, output);
1177
1179 {
1181
1182 for (size_t channel = 0; channel < numChannels; ++channel)
1183 {
1184 volumeDry[channel].setTargetValue (isBypassed ? 0.0f : 1.0f);
1185 volumeDry[channel].reset (sampleRate, 0.05);
1186 volumeDry[channel].setTargetValue (isBypassed ? 1.0f : 0.0f);
1187
1188 volumeWet[channel].setTargetValue (isBypassed ? 1.0f : 0.0f);
1189 volumeWet[channel].reset (sampleRate, 0.05);
1190 volumeWet[channel].setTargetValue (isBypassed ? 0.0f : 1.0f);
1191 }
1192 }
1193 }
1194}
1195
1197
1198//==============================================================================
1202
1206
1207Convolution::Convolution (const Latency& requiredLatency)
1208 : Convolution (requiredLatency,
1209 {},
1210 OptionalQueue { std::make_unique<ConvolutionMessageQueue>() })
1211{}
1212
1214 : Convolution ({},
1215 nonUniform,
1216 OptionalQueue { std::make_unique<ConvolutionMessageQueue>() })
1217{}
1218
1220 : Convolution (requiredLatency, {}, OptionalQueue { queue })
1221{}
1222
1224 : Convolution ({}, nonUniform, OptionalQueue { queue })
1225{}
1226
1227Convolution::Convolution (const Latency& latency,
1228 const NonUniform& nonUniform,
1229 OptionalQueue&& queue)
1230 : pimpl (std::make_unique<Impl> (latency, nonUniform, std::move (queue)))
1231{}
1232
1233Convolution::~Convolution() noexcept = default;
1234
1235void Convolution::loadImpulseResponse (const void* sourceData,
1236 size_t sourceDataSize,
1237 Stereo stereo,
1238 Trim trim,
1239 size_t size,
1240 Normalise normalise)
1241{
1242 pimpl->loadImpulseResponse (sourceData, sourceDataSize, stereo, trim, size, normalise);
1243}
1244
1245void Convolution::loadImpulseResponse (const File& fileImpulseResponse,
1246 Stereo stereo,
1247 Trim trim,
1248 size_t size,
1249 Normalise normalise)
1250{
1251 pimpl->loadImpulseResponse (fileImpulseResponse, stereo, trim, size, normalise);
1252}
1253
1255 double originalSampleRate,
1256 Stereo stereo,
1257 Trim trim,
1258 Normalise normalise)
1259{
1260 pimpl->loadImpulseResponse (std::move (buffer), originalSampleRate, stereo, trim, normalise);
1261}
1262
1264{
1265 mixer.prepare (spec);
1266 pimpl->prepare (spec);
1267 isActive = true;
1268}
1269
1271{
1272 mixer.reset();
1273 pimpl->reset();
1274}
1275
1277 AudioBlock<float>& output,
1278 bool isBypassed) noexcept
1279{
1280 if (! isActive)
1281 return;
1282
1283 jassert (input.getNumChannels() == output.getNumChannels());
1284 jassert (isPositiveAndBelow (input.getNumChannels(), static_cast<size_t> (3))); // only mono and stereo is supported
1285
1286 mixer.processSamples (input, output, isBypassed, [this] (const auto& in, auto& out)
1287 {
1288 pimpl->processSamples (in, out);
1289 });
1290}
1291
1292int Convolution::getCurrentIRSize() const { return pimpl->getCurrentIRSize(); }
1293
1294int Convolution::getLatency() const { return pimpl->getLatency(); }
1295
1296} // namespace dsp
1297} // namespace juce
Type jmin(const Type a, const Type b)
Definition MathsFunctions.h:60
Type jmax(const Type a, const Type b)
Definition MathsFunctions.h:48
#define noexcept
Definition DistrhoDefines.h:72
Definition juce_AbstractFifo.h:73
Definition juce_AudioSampleBuffer.h:34
int getNumChannels() const noexcept
Definition juce_AudioSampleBuffer.h:236
int getNumSamples() const noexcept
Definition juce_AudioSampleBuffer.h:242
const Type * getReadPointer(int channelNumber) const noexcept
Definition juce_AudioSampleBuffer.h:253
Type ** getArrayOfWritePointers() noexcept
Definition juce_AudioSampleBuffer.h:342
Definition juce_AudioFormatManager.h:42
AudioFormatReader * createReaderFor(const File &audioFile)
Definition juce_AudioFormatManager.cpp:120
void registerBasicFormats()
Definition juce_AudioFormatManager.cpp:54
Definition juce_CriticalSection.h:43
static Type decibelsToGain(Type decibels, Type minusInfinityDb=Type(defaultMinusInfinitydB))
Definition juce_Decibels.h:42
Definition juce_File.h:45
bool isLocked() const noexcept
Definition juce_ScopedLock.h:231
Definition juce_MemoryAudioSource.h:33
Definition juce_OptionalScopedPointer.h:38
Definition juce_ResamplingAudioSource.h:35
void getNextAudioBlock(const AudioSourceChannelInfo &) override
Definition juce_ResamplingAudioSource.cpp:80
void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override
Definition juce_ResamplingAudioSource.cpp:46
void setResamplingRatio(double samplesInPerOutputSample)
Definition juce_ResamplingAudioSource.cpp:38
Definition juce_SpinLock.h:42
GenericScopedTryLock< SpinLock > ScopedTryLockType
Definition juce_SpinLock.h:79
GenericScopedLock< SpinLock > ScopedLockType
Definition juce_SpinLock.h:73
void startThread()
Definition juce_Thread.cpp:122
Thread(const String &threadName, size_t threadStackSize=0)
Definition juce_Thread.cpp:26
bool threadShouldExit() const
Definition juce_Thread.cpp:177
bool stopThread(int timeOutMilliseconds)
Definition juce_Thread.cpp:208
static void JUCE_CALLTYPE sleep(int milliseconds)
Definition juce_posix_SharedCode.h:44
Definition juce_AudioBlock.h:70
AudioBlock getSubBlock(size_t newOffset, size_t newLength) const noexcept
Definition juce_AudioBlock.h:371
constexpr size_t getNumChannels() const noexcept
Definition juce_AudioBlock.h:235
SampleType * getChannelPointer(size_t channel) const noexcept
Definition juce_AudioBlock.h:241
AudioBlock & copyFrom(const AudioBlock< OtherSampleType > &src) noexcept
Definition juce_AudioBlock.h:314
AudioBlock getSingleChannelBlock(size_t channel) const noexcept
Definition juce_AudioBlock.h:249
constexpr size_t getNumSamples() const noexcept
Definition juce_AudioBlock.h:238
AudioBlock & clear() noexcept
Definition juce_AudioBlock.h:305
Definition juce_Convolution.cpp:76
Queue< IncomingCommand > queue
Definition juce_Convolution.cpp:120
FixedSizeFunction< 400, void()> IncomingCommand
Definition juce_Convolution.cpp:82
BackgroundMessageQueue(int entries)
Definition juce_Convolution.cpp:78
CriticalSection popMutex
Definition juce_Convolution.cpp:119
void run() override
Definition juce_Convolution.cpp:99
void popAll()
Definition juce_Convolution.cpp:89
bool push(IncomingCommand &command)
Definition juce_Convolution.cpp:87
Definition juce_Convolution.cpp:1014
void prepare(const ProcessSpec &spec)
Definition juce_Convolution.cpp:1035
Impl(Latency requiredLatency, NonUniform requiredHeadSize, OptionalQueue &&queue)
Definition juce_Convolution.cpp:1016
std::unique_ptr< MultichannelEngine > previousEngine
Definition juce_Convolution.cpp:1127
void reset()
Definition juce_Convolution.cpp:1025
void destroyPreviousEngine()
Definition juce_Convolution.cpp:1104
std::unique_ptr< MultichannelEngine > currentEngine
Definition juce_Convolution.cpp:1127
OptionalQueue messageQueue
Definition juce_Convolution.cpp:1125
std::shared_ptr< ConvolutionEngineQueue > engineQueue
Definition juce_Convolution.cpp:1126
void processSamples(const AudioBlock< const float > &input, AudioBlock< float > &output)
Definition juce_Convolution.cpp:1048
void loadImpulseResponse(AudioBuffer< float > &&buffer, double originalSampleRate, Stereo stereo, Trim trim, Normalise normalise)
Definition juce_Convolution.cpp:1075
int getLatency() const
Definition juce_Convolution.cpp:1073
int getCurrentIRSize() const
Definition juce_Convolution.cpp:1071
void installPendingEngine()
Definition juce_Convolution.cpp:1119
void installNewEngine(std::unique_ptr< MultichannelEngine > newEngine)
Definition juce_Convolution.cpp:1111
void loadImpulseResponse(const void *sourceData, size_t sourceDataSize, Stereo stereo, Trim trim, size_t size, Normalise normalise)
Definition juce_Convolution.cpp:1084
void loadImpulseResponse(const File &fileImpulseResponse, Stereo stereo, Trim trim, size_t size, Normalise normalise)
Definition juce_Convolution.cpp:1094
CrossoverMixer mixer
Definition juce_Convolution.cpp:1128
std::array< SmoothedValue< float >, 2 > volumeDry
Definition juce_Convolution.h:290
double sampleRate
Definition juce_Convolution.h:293
void reset()
Definition juce_Convolution.cpp:1196
HeapBlock< char > dryBlockStorage
Definition juce_Convolution.h:292
void processSamples(const AudioBlock< const float > &, AudioBlock< float > &, bool isBypassed, ProcessWet &&) noexcept
Definition juce_Convolution.cpp:1149
void prepare(const ProcessSpec &)
Definition juce_Convolution.cpp:1132
AudioBlock< float > dryBlock
Definition juce_Convolution.h:291
bool currentIsBypassed
Definition juce_Convolution.h:294
std::array< SmoothedValue< float >, 2 > volumeWet
Definition juce_Convolution.h:290
Definition juce_Convolution.cpp:731
std::unique_ptr< MultichannelEngine > makeEngine()
Definition juce_Convolution.cpp:778
ProcessSpec processSpec
Definition juce_Convolution.cpp:805
AudioBuffer< float > impulseResponse
Definition juce_Convolution.cpp:806
static AudioBuffer< float > makeImpulseBuffer()
Definition juce_Convolution.cpp:798
const Convolution::Latency latency
Definition juce_Convolution.cpp:809
std::mutex mutex
Definition juce_Convolution.cpp:815
void setProcessSpec(const ProcessSpec &spec)
Definition juce_Convolution.cpp:742
const Convolution::NonUniform headSize
Definition juce_Convolution.cpp:810
void setImpulseResponse(BufferWithSampleRate &&buf, Convolution::Stereo stereo, Convolution::Trim trim, Convolution::Normalise normalise)
Definition juce_Convolution.cpp:752
std::unique_ptr< MultichannelEngine > getEngine()
Definition juce_Convolution.cpp:775
const bool shouldBeZeroLatency
Definition juce_Convolution.cpp:811
Convolution::Normalise wantsNormalise
Definition juce_Convolution.cpp:808
ConvolutionEngineFactory(Convolution::Latency requiredLatency, Convolution::NonUniform requiredHeadSize)
Definition juce_Convolution.cpp:733
double originalSampleRate
Definition juce_Convolution.cpp:807
TryLockedPtr< MultichannelEngine > engine
Definition juce_Convolution.cpp:813
Definition juce_Convolution.cpp:849
void loadImpulseResponse(const File &fileImpulseResponse, Convolution::Stereo stereo, Convolution::Trim trim, size_t size, Convolution::Normalise normalise)
Definition juce_Convolution.cpp:881
std::weak_ptr< ConvolutionEngineQueue > weakFromThis()
Definition juce_Convolution.cpp:927
void postPendingCommand()
Definition juce_Convolution.cpp:901
void loadImpulseResponse(AudioBuffer< float > &&buffer, double sr, Convolution::Stereo stereo, Convolution::Trim trim, Convolution::Normalise normalise)
Definition juce_Convolution.cpp:856
ConvolutionEngineFactory factory
Definition juce_Convolution.cpp:930
void loadImpulseResponse(const void *sourceData, size_t sourceDataSize, Convolution::Stereo stereo, Convolution::Trim trim, size_t size, Convolution::Normalise normalise)
Definition juce_Convolution.cpp:868
void prepare(const ProcessSpec &spec)
Definition juce_Convolution.cpp:893
BackgroundMessageQueue & messageQueue
Definition juce_Convolution.cpp:929
BackgroundMessageQueue::IncomingCommand pendingCommand
Definition juce_Convolution.cpp:931
std::unique_ptr< MultichannelEngine > getEngine()
Definition juce_Convolution.cpp:910
void callLater(Fn &&fn)
Definition juce_Convolution.cpp:914
ConvolutionEngineQueue(BackgroundMessageQueue &queue, Convolution::Latency latencyIn, Convolution::NonUniform headSizeIn)
Definition juce_Convolution.cpp:851
Trim
Definition juce_Convolution.h:195
@ yes
Definition juce_Convolution.h:195
int getCurrentIRSize() const
Definition juce_Convolution.cpp:1292
Convolution()
Definition juce_Convolution.cpp:1199
int getLatency() const
Definition juce_Convolution.cpp:1294
void prepare(const ProcessSpec &)
Definition juce_Convolution.cpp:1263
std::unique_ptr< Impl > pimpl
Definition juce_Convolution.h:299
bool isActive
Definition juce_Convolution.h:303
void loadImpulseResponse(const void *sourceData, size_t sourceDataSize, Stereo isStereo, Trim requiresTrimming, size_t size, Normalise requiresNormalisation=Normalise::yes)
Definition juce_Convolution.cpp:1235
Mixer mixer
Definition juce_Convolution.h:302
Stereo
Definition juce_Convolution.h:194
@ yes
Definition juce_Convolution.h:194
void reset() noexcept
Definition juce_Convolution.cpp:1270
void processSamples(const AudioBlock< const float > &, AudioBlock< float > &, bool isBypassed) noexcept
Definition juce_Convolution.cpp:1276
Normalise
Definition juce_Convolution.h:196
@ no
Definition juce_Convolution.h:196
@ yes
Definition juce_Convolution.h:196
Definition juce_Convolution.h:40
~ConvolutionMessageQueue() noexcept
Definition juce_Convolution.cpp:140
std::unique_ptr< Impl > pimpl
Definition juce_Convolution.h:67
ConvolutionMessageQueue()
Definition juce_Convolution.cpp:130
Definition juce_Convolution.cpp:935
void processSamples(const AudioBlock< const float > &input, AudioBlock< float > &output, ProcessCurrent &&current, ProcessPrevious &&previous, NotifyDone &&notifyDone)
Definition juce_Convolution.cpp:951
AudioBuffer< float > smootherBuffer
Definition juce_Convolution.cpp:1007
LinearSmoothedValue< float > smoother
Definition juce_Convolution.cpp:1006
AudioBuffer< float > mixBuffer
Definition juce_Convolution.cpp:1008
void reset()
Definition juce_Convolution.cpp:937
void beginTransition()
Definition juce_Convolution.cpp:999
void prepare(const ProcessSpec &spec)
Definition juce_Convolution.cpp:942
Definition juce_FFT.h:45
Definition juce_FixedSizeFunction.h:87
const int blockSize
Definition juce_Convolution.cpp:541
std::vector< std::unique_ptr< ConvolutionEngine > > head
Definition juce_Convolution.cpp:536
AudioBuffer< float > tailBuffer
Definition juce_Convolution.cpp:537
void reset()
Definition juce_Convolution.cpp:486
const int irSize
Definition juce_Convolution.cpp:540
int getLatency() const noexcept
Definition juce_Convolution.cpp:532
const int latency
Definition juce_Convolution.cpp:539
int getBlockSize() const noexcept
Definition juce_Convolution.cpp:533
std::vector< std::unique_ptr< ConvolutionEngine > > tail
Definition juce_Convolution.cpp:536
void processSamples(const AudioBlock< const float > &input, AudioBlock< float > &output)
Definition juce_Convolution.cpp:495
const bool isZeroDelay
Definition juce_Convolution.cpp:542
MultichannelEngine(const AudioBuffer< float > &buf, int maxBlockSize, int maxBufferSize, Convolution::NonUniform headSizeIn, bool isZeroDelayIn)
Definition juce_Convolution.cpp:446
int getIRSize() const noexcept
Definition juce_Convolution.cpp:531
Definition juce_Convolution.cpp:33
void popAll(Fn &&fn)
Definition juce_Convolution.cpp:57
AbstractFifo fifo
Definition juce_Convolution.cpp:71
bool push(Element &element) noexcept
Definition juce_Convolution.cpp:38
void pop(Fn &&fn)
Definition juce_Convolution.cpp:54
bool hasPendingMessages() const noexcept
Definition juce_Convolution.cpp:59
void popN(int n, Fn &&fn)
Definition juce_Convolution.cpp:63
std::vector< Element > storage
Definition juce_Convolution.cpp:72
Queue(int size)
Definition juce_Convolution.cpp:35
Definition juce_Convolution.cpp:672
SpinLock mutex
Definition juce_Convolution.cpp:688
std::unique_ptr< MultichannelEngine > get()
Definition juce_Convolution.cpp:680
std::unique_ptr< Element > ptr
Definition juce_Convolution.cpp:687
void set(std::unique_ptr< Element > p)
Definition juce_Convolution.cpp:674
* e
Definition inflate.c:1404
struct huft * t
Definition inflate.c:943
register unsigned i
Definition inflate.c:1575
unsigned f
Definition inflate.c:1572
#define jassert(expression)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
float in
Definition lilv_test.c:1460
float out
Definition lilv_test.c:1461
Definition juce_AudioBlock.h:29
static AudioBuffer< float > fixNumChannels(const AudioBuffer< float > &buf, Convolution::Stereo stereo)
Definition juce_Convolution.cpp:545
OptionalScopedPointer< ConvolutionMessageQueue > OptionalQueue
Definition juce_Convolution.cpp:1011
static void setImpulseResponse(ConvolutionEngineFactory &factory, const void *sourceData, size_t sourceDataSize, Convolution::Stereo stereo, Convolution::Trim trim, size_t size, Convolution::Normalise normalise)
Definition juce_Convolution.cpp:818
static AudioBuffer< float > trimImpulseResponse(const AudioBuffer< float > &buf)
Definition juce_Convolution.cpp:564
static float calculateNormalisationFactor(float sumSquaredMagnitude)
Definition juce_Convolution.cpp:616
static void normaliseImpulseResponse(AudioBuffer< float > &buf)
Definition juce_Convolution.cpp:624
bool isBypassed(const ProcessorChain< Processors... > &chain) noexcept
Definition juce_ProcessorChain.h:160
static BufferWithSampleRate loadStreamToBuffer(std::unique_ptr< InputStream > stream, size_t maxLength)
Definition juce_Convolution.cpp:702
static AudioBuffer< float > resampleImpulseResponse(const AudioBuffer< float > &buf, const double srcSampleRate, const double destSampleRate)
Definition juce_Convolution.cpp:646
Definition carla_juce.cpp:31
CriticalSection::ScopedLockType ScopedLock
Definition juce_CriticalSection.h:186
constexpr Type jmin(Type a, Type b)
Definition juce_MathsFunctions.h:106
unsigned int uint32
Definition juce_MathsFunctions.h:45
constexpr Type jmax(Type a, Type b)
Definition juce_MathsFunctions.h:94
RangedDirectoryIterator end(const RangedDirectoryIterator &)
Definition juce_RangedDirectoryIterator.h:184
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Definition juce_MathsFunctions.h:262
int nextPowerOfTwo(int n) noexcept
Definition juce_MathsFunctions.h:533
SmoothedValue< FloatType, ValueSmoothingTypes::Linear > LinearSmoothedValue
Definition juce_SmoothedValue.h:406
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Definition juce_MathsFunctions.h:279
int roundToInt(const FloatType value) noexcept
Definition juce_MathsFunctions.h:465
RangedDirectoryIterator begin(const RangedDirectoryIterator &it)
Definition juce_RangedDirectoryIterator.h:179
Definition juce_Uuid.h:141
#define max(x, y)
Definition os.h:78
png_uint_32 length
Definition png.c:2247
Definition juce_Convolution.cpp:692
double sampleRate
Definition juce_Convolution.cpp:699
AudioBuffer< float > buffer
Definition juce_Convolution.cpp:698
BufferWithSampleRate(AudioBuffer< float > &&bufferIn, double sampleRateIn)
Definition juce_Convolution.cpp:695
Definition juce_Convolution.h:119
Definition juce_Convolution.h:134
int headSizeInSamples
Definition juce_Convolution.h:134
void updateSymmetricFrequencyDomainData(float *samples) noexcept
Definition juce_Convolution.cpp:411
std::vector< AudioBuffer< float > > buffersInputSegments
Definition juce_Convolution.cpp:439
AudioBuffer< float > bufferOutput
Definition juce_Convolution.cpp:438
ConvolutionEngine(const float *samples, size_t numSamples, size_t maxBlockSize)
Definition juce_Convolution.cpp:151
void prepareForConvolution(float *samples) noexcept
Definition juce_Convolution.cpp:381
const size_t numSegments
Definition juce_Convolution.cpp:434
AudioBuffer< float > bufferInput
Definition juce_Convolution.cpp:438
std::vector< AudioBuffer< float > > buffersImpulseSegments
Definition juce_Convolution.cpp:439
size_t inputDataPos
Definition juce_Convolution.cpp:436
size_t currentSegment
Definition juce_Convolution.cpp:436
void convolutionProcessingAndAccumulate(const float *input, const float *impulse, float *output)
Definition juce_Convolution.cpp:395
void reset()
Definition juce_Convolution.cpp:208
void processSamplesWithAddedLatency(const float *input, float *output, size_t numSamples)
Definition juce_Convolution.cpp:302
void processSamples(const float *input, float *output, size_t numSamples)
Definition juce_Convolution.cpp:222
AudioBuffer< float > bufferTempOutput
Definition juce_Convolution.cpp:438
const size_t blockSize
Definition juce_Convolution.cpp:431
AudioBuffer< float > bufferOverlap
Definition juce_Convolution.cpp:438
const size_t numInputSegments
Definition juce_Convolution.cpp:435
const std::unique_ptr< FFT > fftObject
Definition juce_Convolution.cpp:433
const size_t fftSize
Definition juce_Convolution.cpp:432
Definition juce_Convolution.cpp:126
BackgroundMessageQueue(int entries)
Definition juce_Convolution.cpp:78
Definition juce_ProcessContext.h:38
uint32 numChannels
Definition juce_ProcessContext.h:46
double sampleRate
Definition juce_ProcessContext.h:40
uint32 maximumBlockSize
Definition juce_ProcessContext.h:43
const char const char const char const char char * fn
Definition swell-functions.h:168
RECT const char void(* callback)(const char *droppath))) SWELL_API_DEFINE(BOOL
Definition swell-functions.h:1004
signed int sample
Definition tap_dynamics_m.c:41
int n
Definition crypt.c:458
uch * p
Definition crypt.c:594
b
Definition crypt.c:628
ulg size
Definition extract.c:2350
int result
Definition process.c:1455
typedef int(UZ_EXP MsgFn)()
#define void
Definition unzip.h:396
#define const
Definition zconf.h:137