| @@ -444,8 +444,6 @@ struct GraphRenderSequence | |||||
| struct Context | struct Context | ||||
| { | { | ||||
| FloatType* const* audioBuffers; | |||||
| MidiBuffer* midiBuffers; | |||||
| AudioPlayHead* audioPlayHead; | AudioPlayHead* audioPlayHead; | ||||
| int numSamples; | int numSamples; | ||||
| }; | }; | ||||
| @@ -484,10 +482,10 @@ struct GraphRenderSequence | |||||
| currentMidiOutputBuffer.clear(); | currentMidiOutputBuffer.clear(); | ||||
| { | { | ||||
| const Context context { renderingBuffer.getArrayOfWritePointers(), midiBuffers.begin(), audioPlayHead, numSamples }; | |||||
| const Context context { audioPlayHead, numSamples }; | |||||
| for (const auto& op : renderOps) | for (const auto& op : renderOps) | ||||
| op (context); | |||||
| op->process (context); | |||||
| } | } | ||||
| for (int i = 0; i < buffer.getNumChannels(); ++i) | for (int i = 0; i < buffer.getNumChannels(); ++i) | ||||
| @@ -502,39 +500,189 @@ struct GraphRenderSequence | |||||
| void addClearChannelOp (int index) | void addClearChannelOp (int index) | ||||
| { | { | ||||
| renderOps.push_back ([=] (const Context& c) { FloatVectorOperations::clear (c.audioBuffers[index], c.numSamples); }); | |||||
| struct ClearOp : public RenderOp | |||||
| { | |||||
| explicit ClearOp (int indexIn) : index (indexIn) {} | |||||
| void prepare (FloatType* const* renderBuffer, MidiBuffer*) override | |||||
| { | |||||
| channelBuffer = renderBuffer[index]; | |||||
| } | |||||
| void process (const Context& c) override | |||||
| { | |||||
| FloatVectorOperations::clear (channelBuffer, c.numSamples); | |||||
| } | |||||
| FloatType* channelBuffer = nullptr; | |||||
| int index = 0; | |||||
| }; | |||||
| renderOps.push_back (std::make_unique<ClearOp> (index)); | |||||
| } | } | ||||
| void addCopyChannelOp (int srcIndex, int dstIndex) | void addCopyChannelOp (int srcIndex, int dstIndex) | ||||
| { | { | ||||
| renderOps.push_back ([=] (const Context& c) { FloatVectorOperations::copy (c.audioBuffers[dstIndex], c.audioBuffers[srcIndex], c.numSamples); }); | |||||
| struct CopyOp : public RenderOp | |||||
| { | |||||
| explicit CopyOp (int fromIn, int toIn) : from (fromIn), to (toIn) {} | |||||
| void prepare (FloatType* const* renderBuffer, MidiBuffer*) override | |||||
| { | |||||
| fromBuffer = renderBuffer[from]; | |||||
| toBuffer = renderBuffer[to]; | |||||
| } | |||||
| void process (const Context& c) override | |||||
| { | |||||
| FloatVectorOperations::copy (toBuffer, fromBuffer, c.numSamples); | |||||
| } | |||||
| FloatType* fromBuffer = nullptr; | |||||
| FloatType* toBuffer = nullptr; | |||||
| int from = 0, to = 0; | |||||
| }; | |||||
| renderOps.push_back (std::make_unique<CopyOp> (srcIndex, dstIndex)); | |||||
| } | } | ||||
| void addAddChannelOp (int srcIndex, int dstIndex) | void addAddChannelOp (int srcIndex, int dstIndex) | ||||
| { | { | ||||
| renderOps.push_back ([=] (const Context& c) { FloatVectorOperations::add (c.audioBuffers[dstIndex], c.audioBuffers[srcIndex], c.numSamples); }); | |||||
| struct AddOp : public RenderOp | |||||
| { | |||||
| explicit AddOp (int fromIn, int toIn) : from (fromIn), to (toIn) {} | |||||
| void prepare (FloatType* const* renderBuffer, MidiBuffer*) override | |||||
| { | |||||
| fromBuffer = renderBuffer[from]; | |||||
| toBuffer = renderBuffer[to]; | |||||
| } | |||||
| void process (const Context& c) override | |||||
| { | |||||
| FloatVectorOperations::add (toBuffer, fromBuffer, c.numSamples); | |||||
| } | |||||
| FloatType* fromBuffer = nullptr; | |||||
| FloatType* toBuffer = nullptr; | |||||
| int from = 0, to = 0; | |||||
| }; | |||||
| renderOps.push_back (std::make_unique<AddOp> (srcIndex, dstIndex)); | |||||
| } | } | ||||
| JUCE_END_IGNORE_WARNINGS_MSVC | JUCE_END_IGNORE_WARNINGS_MSVC | ||||
| void addClearMidiBufferOp (int index) | void addClearMidiBufferOp (int index) | ||||
| { | { | ||||
| renderOps.push_back ([=] (const Context& c) { c.midiBuffers[index].clear(); }); | |||||
| struct ClearOp : public RenderOp | |||||
| { | |||||
| explicit ClearOp (int indexIn) : index (indexIn) {} | |||||
| void prepare (FloatType* const*, MidiBuffer* buffers) override | |||||
| { | |||||
| channelBuffer = buffers + index; | |||||
| } | |||||
| void process (const Context&) override | |||||
| { | |||||
| channelBuffer->clear(); | |||||
| } | |||||
| MidiBuffer* channelBuffer = nullptr; | |||||
| int index = 0; | |||||
| }; | |||||
| renderOps.push_back (std::make_unique<ClearOp> (index)); | |||||
| } | } | ||||
| void addCopyMidiBufferOp (int srcIndex, int dstIndex) | void addCopyMidiBufferOp (int srcIndex, int dstIndex) | ||||
| { | { | ||||
| renderOps.push_back ([=] (const Context& c) { c.midiBuffers[dstIndex] = c.midiBuffers[srcIndex]; }); | |||||
| struct CopyOp : public RenderOp | |||||
| { | |||||
| explicit CopyOp (int fromIn, int toIn) : from (fromIn), to (toIn) {} | |||||
| void prepare (FloatType* const*, MidiBuffer* buffers) override | |||||
| { | |||||
| fromBuffer = buffers + from; | |||||
| toBuffer = buffers + to; | |||||
| } | |||||
| void process (const Context&) override | |||||
| { | |||||
| *toBuffer = *fromBuffer; | |||||
| } | |||||
| MidiBuffer* fromBuffer = nullptr; | |||||
| MidiBuffer* toBuffer = nullptr; | |||||
| int from = 0, to = 0; | |||||
| }; | |||||
| renderOps.push_back (std::make_unique<CopyOp> (srcIndex, dstIndex)); | |||||
| } | } | ||||
| void addAddMidiBufferOp (int srcIndex, int dstIndex) | void addAddMidiBufferOp (int srcIndex, int dstIndex) | ||||
| { | { | ||||
| renderOps.push_back ([=] (const Context& c) { c.midiBuffers[dstIndex].addEvents (c.midiBuffers[srcIndex], 0, c.numSamples, 0); }); | |||||
| struct AddOp : public RenderOp | |||||
| { | |||||
| explicit AddOp (int fromIn, int toIn) : from (fromIn), to (toIn) {} | |||||
| void prepare (FloatType* const*, MidiBuffer* buffers) override | |||||
| { | |||||
| fromBuffer = buffers + from; | |||||
| toBuffer = buffers + to; | |||||
| } | |||||
| void process (const Context& c) override | |||||
| { | |||||
| toBuffer->addEvents (*fromBuffer, 0, c.numSamples, 0); | |||||
| } | |||||
| MidiBuffer* fromBuffer = nullptr; | |||||
| MidiBuffer* toBuffer = nullptr; | |||||
| int from = 0, to = 0; | |||||
| }; | |||||
| renderOps.push_back (std::make_unique<AddOp> (srcIndex, dstIndex)); | |||||
| } | } | ||||
| void addDelayChannelOp (int chan, int delaySize) | void addDelayChannelOp (int chan, int delaySize) | ||||
| { | { | ||||
| renderOps.push_back (DelayChannelOp { chan, delaySize }); | |||||
| struct DelayChannelOp : public RenderOp | |||||
| { | |||||
| DelayChannelOp (int chan, int delaySize) | |||||
| : buffer ((size_t) (delaySize + 1), (FloatType) 0), | |||||
| channel (chan), | |||||
| writeIndex (delaySize) | |||||
| { | |||||
| } | |||||
| void prepare (FloatType* const* renderBuffer, MidiBuffer*) override | |||||
| { | |||||
| channelBuffer = renderBuffer[channel]; | |||||
| } | |||||
| void process (const Context& c) override | |||||
| { | |||||
| auto* data = channelBuffer; | |||||
| for (int i = c.numSamples; --i >= 0;) | |||||
| { | |||||
| buffer[(size_t) writeIndex] = *data; | |||||
| *data++ = buffer[(size_t) readIndex]; | |||||
| if (++readIndex >= (int) buffer.size()) readIndex = 0; | |||||
| if (++writeIndex >= (int) buffer.size()) writeIndex = 0; | |||||
| } | |||||
| } | |||||
| std::vector<FloatType> buffer; | |||||
| FloatType* channelBuffer = nullptr; | |||||
| const int channel; | |||||
| int readIndex = 0, writeIndex; | |||||
| }; | |||||
| renderOps.push_back (std::make_unique<DelayChannelOp> (chan, delaySize)); | |||||
| } | } | ||||
| void addProcessOp (const Node::Ptr& node, | void addProcessOp (const Node::Ptr& node, | ||||
| @@ -542,7 +690,7 @@ struct GraphRenderSequence | |||||
| int totalNumChans, | int totalNumChans, | ||||
| int midiBuffer) | int midiBuffer) | ||||
| { | { | ||||
| renderOps.push_back (ProcessOp { node, audioChannelsUsed, totalNumChans, midiBuffer }); | |||||
| renderOps.push_back (std::make_unique<ProcessOp> (node, audioChannelsUsed, totalNumChans, midiBuffer)); | |||||
| } | } | ||||
| void prepareBuffers (int blockSize) | void prepareBuffers (int blockSize) | ||||
| @@ -565,16 +713,9 @@ struct GraphRenderSequence | |||||
| for (auto&& m : midiBuffers) | for (auto&& m : midiBuffers) | ||||
| m.ensureSize (defaultMIDIBufferSize); | m.ensureSize (defaultMIDIBufferSize); | ||||
| } | |||||
| void releaseBuffers() | |||||
| { | |||||
| renderingBuffer.setSize (1, 1); | |||||
| currentAudioOutputBuffer.setSize (1, 1); | |||||
| currentAudioInputBuffer = nullptr; | |||||
| currentMidiInputBuffer = nullptr; | |||||
| currentMidiOutputBuffer.clear(); | |||||
| midiBuffers.clear(); | |||||
| for (const auto& op : renderOps) | |||||
| op->prepare (renderingBuffer.getArrayOfWritePointers(), midiBuffers.data()); | |||||
| } | } | ||||
| int numBuffersNeeded = 0, numMidiBuffersNeeded = 0; | int numBuffersNeeded = 0, numMidiBuffersNeeded = 0; | ||||
| @@ -590,59 +731,40 @@ struct GraphRenderSequence | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| std::vector<std::function<void (const Context&)>> renderOps; | |||||
| //============================================================================== | |||||
| struct DelayChannelOp | |||||
| struct RenderOp | |||||
| { | { | ||||
| DelayChannelOp (int chan, int delaySize) | |||||
| : buffer ((size_t) (delaySize + 1), (FloatType) 0), | |||||
| channel (chan), | |||||
| writeIndex (delaySize) | |||||
| { | |||||
| } | |||||
| void operator() (const Context& c) | |||||
| { | |||||
| auto* data = c.audioBuffers[channel]; | |||||
| for (int i = c.numSamples; --i >= 0;) | |||||
| { | |||||
| buffer[(size_t) writeIndex] = *data; | |||||
| *data++ = buffer[(size_t) readIndex]; | |||||
| if (++readIndex >= (int) buffer.size()) readIndex = 0; | |||||
| if (++writeIndex >= (int) buffer.size()) writeIndex = 0; | |||||
| } | |||||
| } | |||||
| std::vector<FloatType> buffer; | |||||
| const int channel; | |||||
| int readIndex = 0, writeIndex; | |||||
| virtual ~RenderOp() = default; | |||||
| virtual void prepare (FloatType* const*, MidiBuffer*) = 0; | |||||
| virtual void process (const Context&) = 0; | |||||
| }; | }; | ||||
| //============================================================================== | |||||
| struct ProcessOp | |||||
| struct ProcessOp : public RenderOp | |||||
| { | { | ||||
| ProcessOp (const Node::Ptr& n, | ProcessOp (const Node::Ptr& n, | ||||
| const Array<int>& audioChannelsUsed, | const Array<int>& audioChannelsUsed, | ||||
| int totalNumChans, int midiBuffer) | |||||
| int totalNumChans, | |||||
| int midiBufferIndex) | |||||
| : node (n), | : node (n), | ||||
| processor (*n->getProcessor()), | processor (*n->getProcessor()), | ||||
| audioChannelsToUse (audioChannelsUsed), | audioChannelsToUse (audioChannelsUsed), | ||||
| audioChannels ((size_t) jmax (1, totalNumChans), nullptr), | audioChannels ((size_t) jmax (1, totalNumChans), nullptr), | ||||
| midiBufferToUse (midiBuffer) | |||||
| midiBufferToUse (midiBufferIndex) | |||||
| { | { | ||||
| while (audioChannelsToUse.size() < (int) audioChannels.size()) | while (audioChannelsToUse.size() < (int) audioChannels.size()) | ||||
| audioChannelsToUse.add (0); | audioChannelsToUse.add (0); | ||||
| } | } | ||||
| void operator() (const Context& c) | |||||
| void prepare (FloatType* const* renderBuffer, MidiBuffer* buffers) override | |||||
| { | { | ||||
| processor.setPlayHead (c.audioPlayHead); | |||||
| for (size_t i = 0; i < audioChannels.size(); ++i) | for (size_t i = 0; i < audioChannels.size(); ++i) | ||||
| audioChannels[i] = c.audioBuffers[audioChannelsToUse.getUnchecked ((int) i)]; | |||||
| audioChannels[i] = renderBuffer[audioChannelsToUse.getUnchecked ((int) i)]; | |||||
| midiBuffer = buffers + midiBufferToUse; | |||||
| } | |||||
| void process (const Context& c) override | |||||
| { | |||||
| processor.setPlayHead (c.audioPlayHead); | |||||
| auto numAudioChannels = [this] | auto numAudioChannels = [this] | ||||
| { | { | ||||
| @@ -660,7 +782,7 @@ private: | |||||
| if (processor.isSuspended()) | if (processor.isSuspended()) | ||||
| buffer.clear(); | buffer.clear(); | ||||
| else | else | ||||
| callProcess (buffer, c.midiBuffers[midiBufferToUse]); | |||||
| callProcess (buffer, *midiBuffer); | |||||
| } | } | ||||
| void callProcess (AudioBuffer<float>& buffer, MidiBuffer& midi) | void callProcess (AudioBuffer<float>& buffer, MidiBuffer& midi) | ||||
| @@ -702,12 +824,15 @@ private: | |||||
| const Node::Ptr node; | const Node::Ptr node; | ||||
| AudioProcessor& processor; | AudioProcessor& processor; | ||||
| MidiBuffer* midiBuffer = nullptr; | |||||
| Array<int> audioChannelsToUse; | Array<int> audioChannelsToUse; | ||||
| std::vector<FloatType*> audioChannels; | std::vector<FloatType*> audioChannels; | ||||
| AudioBuffer<float> tempBufferFloat, tempBufferDouble; | AudioBuffer<float> tempBufferFloat, tempBufferDouble; | ||||
| const int midiBufferToUse; | const int midiBufferToUse; | ||||
| }; | }; | ||||
| std::vector<std::unique_ptr<RenderOp>> renderOps; | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||