1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2020 - 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 6 End-User License
11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12
13 End User License Agreement: www.juce.com/juce-6-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
26 namespace juce
27 {
28
29 template <typename Value>
30 struct ChannelInfo
31 {
32 ChannelInfo() = default;
ChannelInfojuce::ChannelInfo33 ChannelInfo (Value** dataIn, int numChannelsIn)
34 : data (dataIn), numChannels (numChannelsIn) {}
35
36 Value** data = nullptr;
37 int numChannels = 0;
38 };
39
40 /** Sets up `channels` so that it contains channel pointers suitable for passing to
41 an AudioProcessor's processBlock.
42
43 On return, `channels` will hold `max (processorIns, processorOuts)` entries.
44 The first `processorIns` entries will point to buffers holding input data.
45 Any entries after the first `processorIns` entries will point to zeroed buffers.
46
47 In the case that the system only provides a single input channel, but the processor
48 has been initialised with multiple input channels, the system input will be copied
49 to all processor inputs.
50
51 In the case that the system provides no input channels, but the processor has
52 been initialise with multiple input channels, the processor's input channels will
53 all be zeroed.
54
55 @param ins the system inputs.
56 @param outs the system outputs.
57 @param numSamples the number of samples in the system buffers.
58 @param processorIns the number of input channels requested by the processor.
59 @param processorOuts the number of output channels requested by the processor.
60 @param tempBuffer temporary storage for inputs that don't have a corresponding output.
61 @param channels holds pointers to each of the processor's audio channels.
62 */
initialiseIoBuffers(ChannelInfo<const float> ins,ChannelInfo<float> outs,const int numSamples,int processorIns,int processorOuts,AudioBuffer<float> & tempBuffer,std::vector<float * > & channels)63 static void initialiseIoBuffers (ChannelInfo<const float> ins,
64 ChannelInfo<float> outs,
65 const int numSamples,
66 int processorIns,
67 int processorOuts,
68 AudioBuffer<float>& tempBuffer,
69 std::vector<float*>& channels)
70 {
71 jassert ((int) channels.size() >= jmax (processorIns, processorOuts));
72
73 size_t totalNumChans = 0;
74 const auto numBytes = (size_t) numSamples * sizeof (float);
75
76 const auto prepareInputChannel = [&] (int index)
77 {
78 if (ins.numChannels == 0)
79 zeromem (channels[totalNumChans], numBytes);
80 else
81 memcpy (channels[totalNumChans], ins.data[index % ins.numChannels], numBytes);
82 };
83
84 if (processorIns > processorOuts)
85 {
86 // If there aren't enough output channels for the number of
87 // inputs, we need to use some temporary extra ones (can't
88 // use the input data in case it gets written to).
89 jassert (tempBuffer.getNumChannels() >= processorIns - processorOuts);
90 jassert (tempBuffer.getNumSamples() >= numSamples);
91
92 for (int i = 0; i < processorOuts; ++i)
93 {
94 channels[totalNumChans] = outs.data[i];
95 prepareInputChannel (i);
96 ++totalNumChans;
97 }
98
99 for (auto i = processorOuts; i < processorIns; ++i)
100 {
101 channels[totalNumChans] = tempBuffer.getWritePointer (i - outs.numChannels);
102 prepareInputChannel (i);
103 ++totalNumChans;
104 }
105 }
106 else
107 {
108 for (int i = 0; i < processorIns; ++i)
109 {
110 channels[totalNumChans] = outs.data[i];
111 prepareInputChannel (i);
112 ++totalNumChans;
113 }
114
115 for (auto i = processorIns; i < processorOuts; ++i)
116 {
117 channels[totalNumChans] = outs.data[i];
118 zeromem (channels[totalNumChans], (size_t) numSamples * sizeof (float));
119 ++totalNumChans;
120 }
121 }
122 }
123
124 //==============================================================================
AudioProcessorPlayer(bool doDoublePrecisionProcessing)125 AudioProcessorPlayer::AudioProcessorPlayer (bool doDoublePrecisionProcessing)
126 : isDoublePrecision (doDoublePrecisionProcessing)
127 {
128 }
129
~AudioProcessorPlayer()130 AudioProcessorPlayer::~AudioProcessorPlayer()
131 {
132 setProcessor (nullptr);
133 }
134
135 //==============================================================================
findMostSuitableLayout(const AudioProcessor & proc) const136 AudioProcessorPlayer::NumChannels AudioProcessorPlayer::findMostSuitableLayout (const AudioProcessor& proc) const
137 {
138 std::vector<NumChannels> layouts { deviceChannels };
139
140 if (deviceChannels.ins == 0 || deviceChannels.ins == 1)
141 {
142 layouts.emplace_back (defaultProcessorChannels.ins, deviceChannels.outs);
143 layouts.emplace_back (deviceChannels.outs, deviceChannels.outs);
144 }
145
146 const auto it = std::find_if (layouts.begin(), layouts.end(), [&] (const NumChannels& chans)
147 {
148 return proc.checkBusesLayoutSupported (chans.toLayout());
149 });
150
151 return it != std::end (layouts) ? *it : layouts[0];
152 }
153
resizeChannels()154 void AudioProcessorPlayer::resizeChannels()
155 {
156 const auto maxChannels = jmax (deviceChannels.ins,
157 deviceChannels.outs,
158 actualProcessorChannels.ins,
159 actualProcessorChannels.outs);
160 channels.resize ((size_t) maxChannels);
161 tempBuffer.setSize (maxChannels, blockSize);
162 }
163
setProcessor(AudioProcessor * const processorToPlay)164 void AudioProcessorPlayer::setProcessor (AudioProcessor* const processorToPlay)
165 {
166 if (processor != processorToPlay)
167 {
168 if (processorToPlay != nullptr && sampleRate > 0 && blockSize > 0)
169 {
170 defaultProcessorChannels = NumChannels { processorToPlay->getBusesLayout() };
171 actualProcessorChannels = findMostSuitableLayout (*processorToPlay);
172
173 processorToPlay->setPlayConfigDetails (actualProcessorChannels.ins,
174 actualProcessorChannels.outs,
175 sampleRate,
176 blockSize);
177
178 auto supportsDouble = processorToPlay->supportsDoublePrecisionProcessing() && isDoublePrecision;
179
180 processorToPlay->setProcessingPrecision (supportsDouble ? AudioProcessor::doublePrecision
181 : AudioProcessor::singlePrecision);
182 processorToPlay->prepareToPlay (sampleRate, blockSize);
183 }
184
185 AudioProcessor* oldOne = nullptr;
186
187 {
188 const ScopedLock sl (lock);
189
190 oldOne = isPrepared ? processor : nullptr;
191 processor = processorToPlay;
192 isPrepared = true;
193 resizeChannels();
194 }
195
196 if (oldOne != nullptr)
197 oldOne->releaseResources();
198 }
199 }
200
setDoublePrecisionProcessing(bool doublePrecision)201 void AudioProcessorPlayer::setDoublePrecisionProcessing (bool doublePrecision)
202 {
203 if (doublePrecision != isDoublePrecision)
204 {
205 const ScopedLock sl (lock);
206
207 if (processor != nullptr)
208 {
209 processor->releaseResources();
210
211 auto supportsDouble = processor->supportsDoublePrecisionProcessing() && doublePrecision;
212
213 processor->setProcessingPrecision (supportsDouble ? AudioProcessor::doublePrecision
214 : AudioProcessor::singlePrecision);
215 processor->prepareToPlay (sampleRate, blockSize);
216 }
217
218 isDoublePrecision = doublePrecision;
219 }
220 }
221
setMidiOutput(MidiOutput * midiOutputToUse)222 void AudioProcessorPlayer::setMidiOutput (MidiOutput* midiOutputToUse)
223 {
224 if (midiOutput != midiOutputToUse)
225 {
226 const ScopedLock sl (lock);
227 midiOutput = midiOutputToUse;
228 }
229 }
230
231 //==============================================================================
audioDeviceIOCallback(const float ** const inputChannelData,const int numInputChannels,float ** const outputChannelData,const int numOutputChannels,const int numSamples)232 void AudioProcessorPlayer::audioDeviceIOCallback (const float** const inputChannelData,
233 const int numInputChannels,
234 float** const outputChannelData,
235 const int numOutputChannels,
236 const int numSamples)
237 {
238 // These should have been prepared by audioDeviceAboutToStart()...
239 jassert (sampleRate > 0 && blockSize > 0);
240
241 // The processor should be prepared to deal with the same number of output channels
242 // as our output device.
243 jassert (processor == nullptr || numOutputChannels == actualProcessorChannels.outs);
244
245 incomingMidi.clear();
246 messageCollector.removeNextBlockOfMessages (incomingMidi, numSamples);
247
248 initialiseIoBuffers ({ inputChannelData, numInputChannels },
249 { outputChannelData, numOutputChannels },
250 numSamples,
251 actualProcessorChannels.ins,
252 actualProcessorChannels.outs,
253 tempBuffer,
254 channels);
255
256 const auto totalNumChannels = jmax (actualProcessorChannels.ins, actualProcessorChannels.outs);
257 AudioBuffer<float> buffer (channels.data(), (int) totalNumChannels, numSamples);
258
259 {
260 const ScopedLock sl (lock);
261
262 if (processor != nullptr)
263 {
264 const ScopedLock sl2 (processor->getCallbackLock());
265
266 if (! processor->isSuspended())
267 {
268 if (processor->isUsingDoublePrecision())
269 {
270 conversionBuffer.makeCopyOf (buffer, true);
271 processor->processBlock (conversionBuffer, incomingMidi);
272 buffer.makeCopyOf (conversionBuffer, true);
273 }
274 else
275 {
276 processor->processBlock (buffer, incomingMidi);
277 }
278
279 if (midiOutput != nullptr)
280 {
281 if (midiOutput->isBackgroundThreadRunning())
282 {
283 midiOutput->sendBlockOfMessages (incomingMidi,
284 Time::getMillisecondCounterHiRes(),
285 sampleRate);
286 }
287 else
288 {
289 midiOutput->sendBlockOfMessagesNow (incomingMidi);
290 }
291 }
292
293 return;
294 }
295 }
296 }
297
298 for (int i = 0; i < numOutputChannels; ++i)
299 FloatVectorOperations::clear (outputChannelData[i], numSamples);
300 }
301
audioDeviceAboutToStart(AudioIODevice * const device)302 void AudioProcessorPlayer::audioDeviceAboutToStart (AudioIODevice* const device)
303 {
304 auto newSampleRate = device->getCurrentSampleRate();
305 auto newBlockSize = device->getCurrentBufferSizeSamples();
306 auto numChansIn = device->getActiveInputChannels().countNumberOfSetBits();
307 auto numChansOut = device->getActiveOutputChannels().countNumberOfSetBits();
308
309 const ScopedLock sl (lock);
310
311 sampleRate = newSampleRate;
312 blockSize = newBlockSize;
313 deviceChannels = { numChansIn, numChansOut };
314
315 resizeChannels();
316
317 messageCollector.reset (sampleRate);
318
319 if (processor != nullptr)
320 {
321 if (isPrepared)
322 processor->releaseResources();
323
324 auto* oldProcessor = processor;
325 setProcessor (nullptr);
326 setProcessor (oldProcessor);
327 }
328 }
329
audioDeviceStopped()330 void AudioProcessorPlayer::audioDeviceStopped()
331 {
332 const ScopedLock sl (lock);
333
334 if (processor != nullptr && isPrepared)
335 processor->releaseResources();
336
337 sampleRate = 0.0;
338 blockSize = 0;
339 isPrepared = false;
340 tempBuffer.setSize (1, 1);
341 }
342
handleIncomingMidiMessage(MidiInput *,const MidiMessage & message)343 void AudioProcessorPlayer::handleIncomingMidiMessage (MidiInput*, const MidiMessage& message)
344 {
345 messageCollector.addMessageToQueue (message);
346 }
347
348 //==============================================================================
349 //==============================================================================
350 #if JUCE_UNIT_TESTS
351
352 struct AudioProcessorPlayerTests : public UnitTest
353 {
AudioProcessorPlayerTestsjuce::AudioProcessorPlayerTests354 AudioProcessorPlayerTests()
355 : UnitTest ("AudioProcessorPlayer", UnitTestCategories::audio) {}
356
runTestjuce::AudioProcessorPlayerTests357 void runTest() override
358 {
359 struct Layout
360 {
361 int numIns, numOuts;
362 };
363
364 const Layout processorLayouts[] { Layout { 0, 0 },
365 Layout { 1, 1 },
366 Layout { 4, 4 },
367 Layout { 4, 8 },
368 Layout { 8, 4 } };
369
370 beginTest ("Buffers are prepared correctly for a variety of channel layouts");
371 {
372 for (const auto& layout : processorLayouts)
373 {
374 for (const auto numSystemInputs : { 0, 1, layout.numIns })
375 {
376 const int numSamples = 256;
377 const auto systemIns = getTestBuffer (numSystemInputs, numSamples);
378 auto systemOuts = getTestBuffer (layout.numOuts, numSamples);
379 AudioBuffer<float> tempBuffer (jmax (layout.numIns, layout.numOuts), numSamples);
380 std::vector<float*> channels ((size_t) jmax (layout.numIns, layout.numOuts), nullptr);
381
382 initialiseIoBuffers ({ systemIns.getArrayOfReadPointers(), systemIns.getNumChannels() },
383 { systemOuts.getArrayOfWritePointers(), systemOuts.getNumChannels() },
384 numSamples,
385 layout.numIns,
386 layout.numOuts,
387 tempBuffer,
388 channels);
389
390 int channelIndex = 0;
391
392 for (const auto& channel : channels)
393 {
394 const auto value = [&]
395 {
396 // Any channels past the number of inputs should be silent.
397 if (layout.numIns <= channelIndex)
398 return 0.0f;
399
400 // If there's no input, all input channels should be silent.
401 if (numSystemInputs == 0) return 0.0f;
402
403 // If there's one input, all input channels should copy from that input.
404 if (numSystemInputs == 1) return 1.0f;
405
406 // Otherwise, each processor input should match the corresponding system input.
407 return (float) (channelIndex + 1);
408 }();
409
410 expect (FloatVectorOperations::findMinAndMax (channel, numSamples) == Range<float> (value, value));
411
412 channelIndex += 1;
413 }
414 }
415 }
416 }
417 }
418
getTestBufferjuce::AudioProcessorPlayerTests419 static AudioBuffer<float> getTestBuffer (int numChannels, int numSamples)
420 {
421 AudioBuffer<float> result (numChannels, numSamples);
422
423 for (int i = 0; i < result.getNumChannels(); ++i)
424 FloatVectorOperations::fill (result.getWritePointer (i), (float) i + 1, result.getNumSamples());
425
426 return result;
427 }
428 };
429
430 static AudioProcessorPlayerTests audioProcessorPlayerTests;
431
432 #endif
433
434 } // namespace juce
435