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