diff --git a/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp b/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp index 849971cb78..d865710958 100644 --- a/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp +++ b/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp @@ -169,9 +169,9 @@ struct fxProgramSet #ifdef JUCE_LITTLE_ENDIAN - static long swap (const long x) throw() { return (long) swapByteOrder ((uint32) x); } + static long vst_swap (const long x) throw() { return (long) swapByteOrder ((uint32) x); } - static float swapFloat (const float x) throw() + static float vst_swapFloat (const float x) throw() { union { uint32 asInt; float asFloat; } n; n.asFloat = x; @@ -179,8 +179,8 @@ struct fxProgramSet return n.asFloat; } #else - #define swap(x) (x) - #define swapFloat(x) (x) + #define vst_swap(x) (x) + #define vst_swapFloat(x) (x) #endif //============================================================================== @@ -1206,7 +1206,7 @@ void VSTPluginInstance::handleMidiFromPlugin (const VstEvents* const events) } //============================================================================== -static Array activeWindows; +static Array activeVSTWindows; //============================================================================== class VSTPluginWindow : public AudioProcessorEditor, @@ -1236,7 +1236,7 @@ public: movementWatcher = new CompMovementWatcher (this); - activeWindows.add (this); + activeVSTWindows.add (this); setSize (1, 1); setOpaque (true); @@ -1249,7 +1249,7 @@ public: closePluginWindow(); - activeWindows.removeValue (this); + activeVSTWindows.removeValue (this); plugin.editorBeingDeleted (this); } @@ -1474,8 +1474,8 @@ public: void broughtToFront() { - activeWindows.removeValue (this); - activeWindows.add (this); + activeVSTWindows.removeValue (this); + activeVSTWindows.add (this); #if JUCE_MAC dispatch (effEditTop, 0, 0, 0, 0); @@ -1753,9 +1753,9 @@ private: #if JUCE_WIN32 static LRESULT CALLBACK vstHookWndProc (HWND hW, UINT message, WPARAM wParam, LPARAM lParam) { - for (int i = activeWindows.size(); --i >= 0;) + for (int i = activeVSTWindows.size(); --i >= 0;) { - const VSTPluginWindow* const w = (const VSTPluginWindow*) activeWindows.getUnchecked (i); + const VSTPluginWindow* const w = (const VSTPluginWindow*) activeVSTWindows.getUnchecked (i); if (w->pluginHWND == hW) { @@ -1965,12 +1965,12 @@ void VSTPluginInstance::handleAsyncUpdate() //============================================================================== bool VSTPluginInstance::restoreProgramSettings (const fxProgram* const prog) { - if (swap (prog->chunkMagic) == 'CcnK' && swap (prog->fxMagic) == 'FxCk') + if (vst_swap (prog->chunkMagic) == 'CcnK' && vst_swap (prog->fxMagic) == 'FxCk') { changeProgramName (getCurrentProgram(), prog->prgName); - for (int i = 0; i < swap (prog->numParams); ++i) - setParameter (i, swapFloat (prog->params[i])); + for (int i = 0; i < vst_swap (prog->numParams); ++i) + setParameter (i, vst_swapFloat (prog->params[i])); return true; } @@ -1986,20 +1986,20 @@ bool VSTPluginInstance::loadFromFXBFile (const void* const data, const fxSet* const set = (const fxSet*) data; - if ((swap (set->chunkMagic) != 'CcnK' && swap (set->chunkMagic) != 'KncC') - || swap (set->version) > fxbVersionNum) + if ((vst_swap (set->chunkMagic) != 'CcnK' && vst_swap (set->chunkMagic) != 'KncC') + || vst_swap (set->version) > fxbVersionNum) return false; - if (swap (set->fxMagic) == 'FxBk') + if (vst_swap (set->fxMagic) == 'FxBk') { // bank of programs - if (swap (set->numPrograms) >= 0) + if (vst_swap (set->numPrograms) >= 0) { const int oldProg = getCurrentProgram(); - const int numParams = swap (((const fxProgram*) (set->programs))->numParams); + const int numParams = vst_swap (((const fxProgram*) (set->programs))->numParams); const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); - for (int i = 0; i < swap (set->numPrograms); ++i) + for (int i = 0; i < vst_swap (set->numPrograms); ++i) { if (i != oldProg) { @@ -2007,7 +2007,7 @@ bool VSTPluginInstance::loadFromFXBFile (const void* const data, if (((const char*) prog) - ((const char*) set) >= dataSize) return false; - if (swap (set->numPrograms) > 0) + if (vst_swap (set->numPrograms) > 0) setCurrentProgram (i); if (! restoreProgramSettings (prog)) @@ -2015,7 +2015,7 @@ bool VSTPluginInstance::loadFromFXBFile (const void* const data, } } - if (swap (set->numPrograms) > 0) + if (vst_swap (set->numPrograms) > 0) setCurrentProgram (oldProg); const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + oldProg * progLen); @@ -2026,38 +2026,38 @@ bool VSTPluginInstance::loadFromFXBFile (const void* const data, return false; } } - else if (swap (set->fxMagic) == 'FxCk') + else if (vst_swap (set->fxMagic) == 'FxCk') { // single program const fxProgram* const prog = (const fxProgram*) data; - if (swap (prog->chunkMagic) != 'CcnK') + if (vst_swap (prog->chunkMagic) != 'CcnK') return false; changeProgramName (getCurrentProgram(), prog->prgName); - for (int i = 0; i < swap (prog->numParams); ++i) - setParameter (i, swapFloat (prog->params[i])); + for (int i = 0; i < vst_swap (prog->numParams); ++i) + setParameter (i, vst_swapFloat (prog->params[i])); } - else if (swap (set->fxMagic) == 'FBCh' || swap (set->fxMagic) == 'hCBF') + else if (vst_swap (set->fxMagic) == 'FBCh' || vst_swap (set->fxMagic) == 'hCBF') { // non-preset chunk const fxChunkSet* const cset = (const fxChunkSet*) data; - if (swap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize) + if (vst_swap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize) return false; - setChunkData (cset->chunk, swap (cset->chunkSize), false); + setChunkData (cset->chunk, vst_swap (cset->chunkSize), false); } - else if (swap (set->fxMagic) == 'FPCh' || swap (set->fxMagic) == 'hCPF') + else if (vst_swap (set->fxMagic) == 'FPCh' || vst_swap (set->fxMagic) == 'hCPF') { // preset chunk const fxProgramSet* const cset = (const fxProgramSet*) data; - if (swap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize) + if (vst_swap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize) return false; - setChunkData (cset->chunk, swap (cset->chunkSize), true); + setChunkData (cset->chunk, vst_swap (cset->chunkSize), true); changeProgramName (getCurrentProgram(), cset->name); } @@ -2074,18 +2074,18 @@ void VSTPluginInstance::setParamsInProgramBlock (fxProgram* const prog) throw() { const int numParams = getNumParameters(); - prog->chunkMagic = swap ('CcnK'); + prog->chunkMagic = vst_swap ('CcnK'); prog->byteSize = 0; - prog->fxMagic = swap ('FxCk'); - prog->version = swap (fxbVersionNum); - prog->fxID = swap (getUID()); - prog->fxVersion = swap (getVersionNumber()); - prog->numParams = swap (numParams); + prog->fxMagic = vst_swap ('FxCk'); + prog->version = vst_swap (fxbVersionNum); + prog->fxID = vst_swap (getUID()); + prog->fxVersion = vst_swap (getVersionNumber()); + prog->numParams = vst_swap (numParams); getCurrentProgramName().copyToBuffer (prog->prgName, sizeof (prog->prgName) - 1); for (int i = 0; i < numParams; ++i) - prog->params[i] = swapFloat (getParameter (i)); + prog->params[i] = vst_swapFloat (getParameter (i)); } bool VSTPluginInstance::saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB) @@ -2104,14 +2104,14 @@ bool VSTPluginInstance::saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSiz dest.setSize (totalLen, true); fxChunkSet* const set = (fxChunkSet*) dest.getData(); - set->chunkMagic = swap ('CcnK'); + set->chunkMagic = vst_swap ('CcnK'); set->byteSize = 0; - set->fxMagic = swap ('FBCh'); - set->version = swap (fxbVersionNum); - set->fxID = swap (getUID()); - set->fxVersion = swap (getVersionNumber()); - set->numPrograms = swap (numPrograms); - set->chunkSize = swap (chunk.getSize()); + set->fxMagic = vst_swap ('FBCh'); + set->version = vst_swap (fxbVersionNum); + set->fxID = vst_swap (getUID()); + set->fxVersion = vst_swap (getVersionNumber()); + set->numPrograms = vst_swap (numPrograms); + set->chunkSize = vst_swap (chunk.getSize()); chunk.copyTo (set->chunk, 0, chunk.getSize()); } @@ -2124,14 +2124,14 @@ bool VSTPluginInstance::saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSiz dest.setSize (totalLen, true); fxProgramSet* const set = (fxProgramSet*) dest.getData(); - set->chunkMagic = swap ('CcnK'); + set->chunkMagic = vst_swap ('CcnK'); set->byteSize = 0; - set->fxMagic = swap ('FPCh'); - set->version = swap (fxbVersionNum); - set->fxID = swap (getUID()); - set->fxVersion = swap (getVersionNumber()); - set->numPrograms = swap (numPrograms); - set->chunkSize = swap (chunk.getSize()); + set->fxMagic = vst_swap ('FPCh'); + set->version = vst_swap (fxbVersionNum); + set->fxID = vst_swap (getUID()); + set->fxVersion = vst_swap (getVersionNumber()); + set->numPrograms = vst_swap (numPrograms); + set->chunkSize = vst_swap (chunk.getSize()); getCurrentProgramName().copyToBuffer (set->name, sizeof (set->name) - 1); chunk.copyTo (set->chunk, 0, chunk.getSize()); @@ -2146,13 +2146,13 @@ bool VSTPluginInstance::saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSiz dest.setSize (len, true); fxSet* const set = (fxSet*) dest.getData(); - set->chunkMagic = swap ('CcnK'); + set->chunkMagic = vst_swap ('CcnK'); set->byteSize = 0; - set->fxMagic = swap ('FxBk'); - set->version = swap (fxbVersionNum); - set->fxID = swap (getUID()); - set->fxVersion = swap (getVersionNumber()); - set->numPrograms = swap (numPrograms); + set->fxMagic = vst_swap ('FxBk'); + set->version = vst_swap (fxbVersionNum); + set->fxID = vst_swap (getUID()); + set->fxVersion = vst_swap (getVersionNumber()); + set->numPrograms = vst_swap (numPrograms); const int oldProgram = getCurrentProgram(); MemoryBlock oldSettings; diff --git a/src/juce_appframework/audio/processors/juce_AudioProcessorGraph.cpp b/src/juce_appframework/audio/processors/juce_AudioProcessorGraph.cpp index 393c571974..a0033a178c 100644 --- a/src/juce_appframework/audio/processors/juce_AudioProcessorGraph.cpp +++ b/src/juce_appframework/audio/processors/juce_AudioProcessorGraph.cpp @@ -1,1342 +1,1366 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - JUCE is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -#include "../../../juce_core/basics/juce_StandardHeader.h" - -BEGIN_JUCE_NAMESPACE - -#include "juce_AudioProcessorGraph.h" -#include "../../events/juce_MessageManager.h" - - -const int AudioProcessorGraph::midiChannelIndex = 0x1000; - -//============================================================================== -AudioProcessorGraph::Node::Node (const uint32 id_, - AudioProcessor* const processor_) throw() - : id (id_), - processor (processor_), - isPrepared (false) -{ - jassert (processor_ != 0); -} - -AudioProcessorGraph::Node::~Node() -{ - delete processor; -} - -void AudioProcessorGraph::Node::prepare (const double sampleRate, const int blockSize, - AudioProcessorGraph* const graph) -{ - if (! isPrepared) - { - isPrepared = true; - - AudioProcessorGraph::AudioGraphIOProcessor* const ioProc - = dynamic_cast (processor); - - if (ioProc != 0) - ioProc->setParentGraph (graph); - - processor->setPlayConfigDetails (processor->getNumInputChannels(), - processor->getNumOutputChannels(), - sampleRate, blockSize); - - processor->prepareToPlay (sampleRate, blockSize); - } -} - -void AudioProcessorGraph::Node::unprepare() -{ - if (isPrepared) - { - isPrepared = false; - processor->releaseResources(); - } -} - -//============================================================================== -AudioProcessorGraph::AudioProcessorGraph() - : lastNodeId (0), - renderingBuffers (1, 1) -{ -} - -AudioProcessorGraph::~AudioProcessorGraph() -{ - clearRenderingSequence(); - clear(); -} - -const String AudioProcessorGraph::getName() const -{ - return "Audio Graph"; -} - -//============================================================================== -void AudioProcessorGraph::clear() -{ - nodes.clear(); - connections.clear(); - triggerAsyncUpdate(); -} - -AudioProcessorGraph::Node* AudioProcessorGraph::getNodeForId (const uint32 nodeId) const throw() -{ - for (int i = nodes.size(); --i >= 0;) - if (nodes.getUnchecked(i)->id == nodeId) - return nodes.getUnchecked(i); - - return 0; -} - -AudioProcessorGraph::Node* AudioProcessorGraph::addNode (AudioProcessor* const newProcessor, - uint32 nodeId) -{ - if (newProcessor == 0) - { - jassertfalse - return 0; - } - - if (nodeId == 0) - { - nodeId = ++lastNodeId; - } - else - { - // you can't add a node with an id that already exists in the graph.. - jassert (getNodeForId (nodeId) == 0); - removeNode (nodeId); - } - - lastNodeId = nodeId; - - Node* const n = new Node (nodeId, newProcessor); - nodes.add (n); - triggerAsyncUpdate(); - - AudioProcessorGraph::AudioGraphIOProcessor* const ioProc - = dynamic_cast (n->processor); - - if (ioProc != 0) - ioProc->setParentGraph (this); - - return n; -} - -bool AudioProcessorGraph::removeNode (const uint32 nodeId) -{ - disconnectNode (nodeId); - - for (int i = nodes.size(); --i >= 0;) - { - if (nodes.getUnchecked(i)->id == nodeId) - { - AudioProcessorGraph::AudioGraphIOProcessor* const ioProc - = dynamic_cast (nodes.getUnchecked(i)->processor); - - if (ioProc != 0) - ioProc->setParentGraph (0); - - nodes.remove (i); - triggerAsyncUpdate(); - - return true; - } - } - - return false; -} - -//============================================================================== -const AudioProcessorGraph::Connection* AudioProcessorGraph::getConnectionBetween (const uint32 sourceNodeId, - const int sourceChannelIndex, - const uint32 destNodeId, - const int destChannelIndex) const throw() -{ - for (int i = connections.size(); --i >= 0;) - { - const Connection* const c = connections.getUnchecked(i); - - if (c->sourceNodeId == sourceNodeId - && c->destNodeId == destNodeId - && c->sourceChannelIndex == sourceChannelIndex - && c->destChannelIndex == destChannelIndex) - { - return c; - } - } - - return 0; -} - -bool AudioProcessorGraph::isConnected (const uint32 possibleSourceNodeId, - const uint32 possibleDestNodeId) const throw() -{ - for (int i = connections.size(); --i >= 0;) - { - const Connection* const c = connections.getUnchecked(i); - - if (c->sourceNodeId == possibleSourceNodeId - && c->destNodeId == possibleDestNodeId) - { - return true; - } - } - - return false; -} - -bool AudioProcessorGraph::canConnect (const uint32 sourceNodeId, - const int sourceChannelIndex, - const uint32 destNodeId, - const int destChannelIndex) const throw() -{ - if (sourceChannelIndex < 0 - || destChannelIndex < 0 - || sourceNodeId == destNodeId - || (destChannelIndex == midiChannelIndex) != (sourceChannelIndex == midiChannelIndex)) - return false; - - const Node* const source = getNodeForId (sourceNodeId); - - if (source == 0 - || (sourceChannelIndex != midiChannelIndex && sourceChannelIndex >= source->processor->getNumOutputChannels()) - || (sourceChannelIndex == midiChannelIndex && ! source->processor->producesMidi())) - return false; - - const Node* const dest = getNodeForId (destNodeId); - - if (dest == 0 - || (destChannelIndex != midiChannelIndex && destChannelIndex >= dest->processor->getNumInputChannels()) - || (destChannelIndex == midiChannelIndex && ! dest->processor->acceptsMidi())) - return false; - - return getConnectionBetween (sourceNodeId, sourceChannelIndex, - destNodeId, destChannelIndex) == 0; -} - -bool AudioProcessorGraph::addConnection (const uint32 sourceNodeId, - const int sourceChannelIndex, - const uint32 destNodeId, - const int destChannelIndex) -{ - if (! canConnect (sourceNodeId, sourceChannelIndex, destNodeId, destChannelIndex)) - return false; - - Connection* const c = new Connection(); - c->sourceNodeId = sourceNodeId; - c->sourceChannelIndex = sourceChannelIndex; - c->destNodeId = destNodeId; - c->destChannelIndex = destChannelIndex; - - connections.add (c); - triggerAsyncUpdate(); - - return true; -} - -void AudioProcessorGraph::removeConnection (const int index) -{ - connections.remove (index); - triggerAsyncUpdate(); -} - -bool AudioProcessorGraph::removeConnection (const uint32 sourceNodeId, const int sourceChannelIndex, - const uint32 destNodeId, const int destChannelIndex) -{ - bool doneAnything = false; - - for (int i = connections.size(); --i >= 0;) - { - const Connection* const c = connections.getUnchecked(i); - - if (c->sourceNodeId == sourceNodeId - && c->destNodeId == destNodeId - && c->sourceChannelIndex == sourceChannelIndex - && c->destChannelIndex == destChannelIndex) - { - removeConnection (i); - doneAnything = true; - triggerAsyncUpdate(); - } - } - - return doneAnything; -} - -bool AudioProcessorGraph::disconnectNode (const uint32 nodeId) -{ - bool doneAnything = false; - - for (int i = connections.size(); --i >= 0;) - { - const Connection* const c = connections.getUnchecked(i); - - if (c->sourceNodeId == nodeId || c->destNodeId == nodeId) - { - removeConnection (i); - doneAnything = true; - triggerAsyncUpdate(); - } - } - - return doneAnything; -} - -bool AudioProcessorGraph::removeIllegalConnections() -{ - bool doneAnything = false; - - for (int i = connections.size(); --i >= 0;) - { - const Connection* const c = connections.getUnchecked(i); - - const Node* const source = getNodeForId (c->sourceNodeId); - const Node* const dest = getNodeForId (c->destNodeId); - - if (source == 0 || dest == 0 - || (c->sourceChannelIndex != midiChannelIndex - && (((unsigned int) c->sourceChannelIndex) >= (unsigned int) source->processor->getNumOutputChannels())) - || (c->sourceChannelIndex == midiChannelIndex - && ! source->processor->producesMidi()) - || (c->destChannelIndex != midiChannelIndex - && (((unsigned int) c->destChannelIndex) >= (unsigned int) dest->processor->getNumInputChannels())) - || (c->destChannelIndex == midiChannelIndex - && ! dest->processor->acceptsMidi())) - { - removeConnection (i); - doneAnything = true; - triggerAsyncUpdate(); - } - } - - return doneAnything; -} - -//============================================================================== -namespace GraphRenderingOps -{ - -//============================================================================== -class AudioGraphRenderingOp -{ -public: - AudioGraphRenderingOp() throw() {} - virtual ~AudioGraphRenderingOp() throw() {} - - virtual void perform (AudioSampleBuffer& sharedBufferChans, - const OwnedArray & sharedMidiBuffers, - const int numSamples) throw() = 0; - - juce_UseDebuggingNewOperator -}; - -//============================================================================== -class ClearChannelOp : public AudioGraphRenderingOp -{ -public: - ClearChannelOp (const int channelNum_) throw() - : channelNum (channelNum_) - {} - - ~ClearChannelOp() throw() {} - - void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray &, const int numSamples) throw() - { - sharedBufferChans.clear (channelNum, 0, numSamples); - } - -private: - const int channelNum; - - ClearChannelOp (const ClearChannelOp&); - const ClearChannelOp& operator= (const ClearChannelOp&); -}; - -//============================================================================== -class CopyChannelOp : public AudioGraphRenderingOp -{ -public: - CopyChannelOp (const int srcChannelNum_, const int dstChannelNum_) throw() - : srcChannelNum (srcChannelNum_), - dstChannelNum (dstChannelNum_) - {} - - ~CopyChannelOp() throw() {} - - void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray &, const int numSamples) throw() - { - sharedBufferChans.copyFrom (dstChannelNum, 0, sharedBufferChans, srcChannelNum, 0, numSamples); - } - -private: - const int srcChannelNum, dstChannelNum; - - CopyChannelOp (const CopyChannelOp&); - const CopyChannelOp& operator= (const CopyChannelOp&); -}; - -//============================================================================== -class AddChannelOp : public AudioGraphRenderingOp -{ -public: - AddChannelOp (const int srcChannelNum_, const int dstChannelNum_) throw() - : srcChannelNum (srcChannelNum_), - dstChannelNum (dstChannelNum_) - {} - - ~AddChannelOp() throw() {} - - void perform (AudioSampleBuffer& sharedBufferChans, - const OwnedArray & sharedMidiBuffers, - const int numSamples) throw() - { - if (dstChannelNum != AudioProcessorGraph::midiChannelIndex) - sharedBufferChans.addFrom (dstChannelNum, 0, sharedBufferChans, srcChannelNum, 0, numSamples); - else - sharedMidiBuffers.getUnchecked (dstChannelNum) - ->addEvents (*sharedMidiBuffers.getUnchecked (srcChannelNum), - 0, numSamples, 0); - } - -private: - const int srcChannelNum, dstChannelNum; - - AddChannelOp (const AddChannelOp&); - const AddChannelOp& operator= (const AddChannelOp&); -}; - -//============================================================================== -class ClearMidiBufferOp : public AudioGraphRenderingOp -{ -public: - ClearMidiBufferOp (const int bufferNum_) throw() - : bufferNum (bufferNum_) - {} - - ~ClearMidiBufferOp() throw() {} - - void perform (AudioSampleBuffer&, const OwnedArray & sharedMidiBuffers, const int) throw() - { - sharedMidiBuffers.getUnchecked (bufferNum)->clear(); - } - -private: - const int bufferNum; - - ClearMidiBufferOp (const ClearMidiBufferOp&); - const ClearMidiBufferOp& operator= (const ClearMidiBufferOp&); -}; - -//============================================================================== -class CopyMidiBufferOp : public AudioGraphRenderingOp -{ -public: - CopyMidiBufferOp (const int srcBufferNum_, const int dstBufferNum_) throw() - : srcBufferNum (srcBufferNum_), - dstBufferNum (dstBufferNum_) - {} - - ~CopyMidiBufferOp() throw() {} - - void perform (AudioSampleBuffer&, const OwnedArray & sharedMidiBuffers, const int) throw() - { - *sharedMidiBuffers.getUnchecked (dstBufferNum) = *sharedMidiBuffers.getUnchecked (srcBufferNum); - } - -private: - const int srcBufferNum, dstBufferNum; - - CopyMidiBufferOp (const CopyMidiBufferOp&); - const CopyMidiBufferOp& operator= (const CopyMidiBufferOp&); -}; - -//============================================================================== -class AddMidiBufferOp : public AudioGraphRenderingOp -{ -public: - AddMidiBufferOp (const int srcBufferNum_, const int dstBufferNum_) throw() - : srcBufferNum (srcBufferNum_), - dstBufferNum (dstBufferNum_) - {} - - ~AddMidiBufferOp() throw() {} - - void perform (AudioSampleBuffer&, const OwnedArray & sharedMidiBuffers, const int numSamples) throw() - { - sharedMidiBuffers.getUnchecked (dstBufferNum) - ->addEvents (*sharedMidiBuffers.getUnchecked (srcBufferNum), 0, numSamples, 0); - } - -private: - const int srcBufferNum, dstBufferNum; - - AddMidiBufferOp (const AddMidiBufferOp&); - const AddMidiBufferOp& operator= (const AddMidiBufferOp&); -}; - -//============================================================================== -class ProcessBufferOp : public AudioGraphRenderingOp -{ -public: - ProcessBufferOp (const AudioProcessorGraph::Node::Ptr& node_, - const Array & audioChannelsToUse_, - const int totalChans_, - const int midiBufferToUse_) throw() - : node (node_), - processor (node_->processor), - audioChannelsToUse (audioChannelsToUse_), - totalChans (totalChans_), - midiBufferToUse (midiBufferToUse_) - { - channels = (float**) juce_calloc (sizeof (float*) * totalChans_); - } - - ~ProcessBufferOp() throw() - { - juce_free (channels); - } - - void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray & sharedMidiBuffers, const int numSamples) throw() - { - for (int i = totalChans; --i >= 0;) - channels[i] = sharedBufferChans.getSampleData (audioChannelsToUse.getUnchecked (i), 0); - - AudioSampleBuffer buffer (channels, totalChans, numSamples); - - processor->processBlock (buffer, *sharedMidiBuffers.getUnchecked (midiBufferToUse)); - } - - const AudioProcessorGraph::Node::Ptr node; - AudioProcessor* const processor; - -private: - Array audioChannelsToUse; - float** channels; - int totalChans; - int midiBufferToUse; - - ProcessBufferOp (const ProcessBufferOp&); - const ProcessBufferOp& operator= (const ProcessBufferOp&); -}; - -//============================================================================== -/** Used to calculate the correct sequence of rendering ops needed, based on - the best re-use of shared buffers at each stage. -*/ -class RenderingOpSequenceCalculator -{ -public: - //============================================================================== - RenderingOpSequenceCalculator (AudioProcessorGraph& graph_, - const VoidArray& orderedNodes_, - VoidArray& renderingOps) - : graph (graph_), - orderedNodes (orderedNodes_) - { - nodeIds.add (-2); // first buffer is read-only zeros - channels.add (0); - - midiNodeIds.add (-2); - - for (int i = 0; i < orderedNodes.size(); ++i) - { - createRenderingOpsForNode ((AudioProcessorGraph::Node*) orderedNodes.getUnchecked(i), - renderingOps, i); - - markAnyUnusedBuffersAsFree (i); - } - } - - int getNumBuffersNeeded() const throw() { return nodeIds.size(); } - - int getNumMidiBuffersNeeded() const throw() { return midiNodeIds.size(); } - - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - AudioProcessorGraph& graph; - const VoidArray& orderedNodes; - Array nodeIds, channels, midiNodeIds; - - //============================================================================== - void createRenderingOpsForNode (AudioProcessorGraph::Node* const node, - VoidArray& renderingOps, - const int ourRenderingIndex) - { - const int numIns = node->processor->getNumInputChannels(); - const int numOuts = node->processor->getNumOutputChannels(); - const int totalChans = jmax (numIns, numOuts); - - Array audioChannelsToUse; - int midiBufferToUse = -1; - - for (int inputChan = 0; inputChan < numIns; ++inputChan) - { - // get a list of all the inputs to this node - Array sourceNodes, sourceOutputChans; - - for (int i = graph.getNumConnections(); --i >= 0;) - { - const AudioProcessorGraph::Connection* const c = graph.getConnection (i); - - if (c->destNodeId == node->id && c->destChannelIndex == inputChan) - { - sourceNodes.add (c->sourceNodeId); - sourceOutputChans.add (c->sourceChannelIndex); - } - } - - int bufIndex = -1; - - if (sourceNodes.size() == 0) - { - // unconnected input channel - - if (inputChan >= numOuts) - { - bufIndex = getReadOnlyEmptyBuffer(); - jassert (bufIndex >= 0); - } - else - { - bufIndex = getFreeBuffer (false); - renderingOps.add (new ClearChannelOp (bufIndex)); - } - } - else if (sourceNodes.size() == 1) - { - // channel with a straightforward single input.. - const int srcNode = sourceNodes.getUnchecked(0); - const int srcChan = sourceOutputChans.getUnchecked(0); - - bufIndex = getBufferContaining (srcNode, srcChan); - jassert (bufIndex >= 0); - - if (inputChan < numOuts - && isBufferNeededLater (ourRenderingIndex, - inputChan, - srcNode, srcChan)) - { - // can't mess up this channel because it's needed later by another node, so we - // need to use a copy of it.. - const int newFreeBuffer = getFreeBuffer (false); - - renderingOps.add (new CopyChannelOp (bufIndex, newFreeBuffer)); - - bufIndex = newFreeBuffer; - } - } - else - { - // channel with a mix of several inputs.. - - // try to find a re-usable channel from our inputs.. - int reusableInputIndex = -1; - - for (int i = 0; i < sourceNodes.size(); ++i) - { - const int sourceBufIndex = getBufferContaining (sourceNodes.getUnchecked(i), - sourceOutputChans.getUnchecked(i)); - - jassert (sourceBufIndex >= 0); - - if (! isBufferNeededLater (ourRenderingIndex, - inputChan, - sourceNodes.getUnchecked(i), - sourceOutputChans.getUnchecked(i))) - { - // we've found one of our input chans that can be re-used.. - reusableInputIndex = i; - bufIndex = sourceBufIndex; - break; - } - } - - if (reusableInputIndex < 0) - { - // can't re-use any of our input chans, so get a new one and copy everything into it.. - bufIndex = getFreeBuffer (false); - jassert (bufIndex != 0); - - const int srcIndex = getBufferContaining (sourceNodes.getUnchecked (0), - sourceOutputChans.getUnchecked (0)); - jassert (srcIndex >= 0); - - renderingOps.add (new CopyChannelOp (srcIndex, bufIndex)); - - reusableInputIndex = 0; - } - - for (int j = 0; j < sourceNodes.size(); ++j) - { - if (j != reusableInputIndex) - { - const int srcIndex = getBufferContaining (sourceNodes.getUnchecked(j), - sourceOutputChans.getUnchecked(j)); - jassert (srcIndex >= 0); - - renderingOps.add (new AddChannelOp (srcIndex, bufIndex)); - } - } - } - - jassert (bufIndex >= 0); - audioChannelsToUse.add (bufIndex); - - if (inputChan < numOuts) - markBufferAsContaining (bufIndex, node->id, inputChan); - } - - for (int outputChan = numIns; outputChan < numOuts; ++outputChan) - { - const int bufIndex = getFreeBuffer (false); - jassert (bufIndex != 0); - audioChannelsToUse.add (bufIndex); - - markBufferAsContaining (bufIndex, node->id, outputChan); - } - - // Now the same thing for midi.. - Array midiSourceNodes; - - for (int i = graph.getNumConnections(); --i >= 0;) - { - const AudioProcessorGraph::Connection* const c = graph.getConnection (i); - - if (c->destNodeId == node->id && c->destChannelIndex == AudioProcessorGraph::midiChannelIndex) - midiSourceNodes.add (c->sourceNodeId); - } - - if (midiSourceNodes.size() == 0) - { - // No midi inputs.. - midiBufferToUse = getFreeBuffer (true); // need to pick a buffer even if the processor doesn't use midi - - if (node->processor->acceptsMidi() || node->processor->producesMidi()) - renderingOps.add (new ClearMidiBufferOp (midiBufferToUse)); - } - else if (midiSourceNodes.size() == 1) - { - // One midi input.. - midiBufferToUse = getBufferContaining (midiSourceNodes.getUnchecked(0), - AudioProcessorGraph::midiChannelIndex); - - jassert (midiBufferToUse >= 0); - - if (midiBufferToUse >= 0) - { - if (isBufferNeededLater (ourRenderingIndex, - AudioProcessorGraph::midiChannelIndex, - midiSourceNodes.getUnchecked(0), - AudioProcessorGraph::midiChannelIndex)) - { - // can't mess up this channel because it's needed later by another node, so we - // need to use a copy of it.. - const int newFreeBuffer = getFreeBuffer (true); - renderingOps.add (new CopyMidiBufferOp (midiBufferToUse, newFreeBuffer)); - midiBufferToUse = newFreeBuffer; - } - } - } - else - { - // More than one midi input being mixed.. - int reusableInputIndex = -1; - - for (int i = 0; i < midiSourceNodes.size(); ++i) - { - const int sourceBufIndex = getBufferContaining (midiSourceNodes.getUnchecked(i), - AudioProcessorGraph::midiChannelIndex); - - jassert (sourceBufIndex >= 0); - - if (! isBufferNeededLater (ourRenderingIndex, - AudioProcessorGraph::midiChannelIndex, - midiSourceNodes.getUnchecked(i), - AudioProcessorGraph::midiChannelIndex)) - { - // we've found one of our input buffers that can be re-used.. - reusableInputIndex = i; - midiBufferToUse = sourceBufIndex; - break; - } - } - - if (reusableInputIndex < 0) - { - // can't re-use any of our input buffers, so get a new one and copy everything into it.. - midiBufferToUse = getFreeBuffer (true); - jassert (midiBufferToUse >= 0); - - const int srcIndex = getBufferContaining (midiSourceNodes.getUnchecked(0), - AudioProcessorGraph::midiChannelIndex); - jassert (srcIndex >= 0); - - renderingOps.add (new CopyMidiBufferOp (srcIndex, midiBufferToUse)); - - reusableInputIndex = 0; - } - - for (int j = 0; j < midiSourceNodes.size(); ++j) - { - if (j != reusableInputIndex) - { - const int srcIndex = getBufferContaining (midiSourceNodes.getUnchecked(j), - AudioProcessorGraph::midiChannelIndex); - jassert (srcIndex >= 0); - - renderingOps.add (new AddMidiBufferOp (srcIndex, midiBufferToUse)); - } - } - } - - if (node->processor->producesMidi()) - markBufferAsContaining (midiBufferToUse, node->id, - AudioProcessorGraph::midiChannelIndex); - - renderingOps.add (new ProcessBufferOp (node, audioChannelsToUse, - totalChans, midiBufferToUse)); - } - - //============================================================================== - int getFreeBuffer (const bool forMidi) - { - if (forMidi) - { - for (int i = 1; i < midiNodeIds.size(); ++i) - if (midiNodeIds.getUnchecked(i) < 0) - return i; - - midiNodeIds.add (-1); - return midiNodeIds.size() - 1; - } - else - { - for (int i = 1; i < nodeIds.size(); ++i) - if (nodeIds.getUnchecked(i) < 0) - return i; - - nodeIds.add (-1); - channels.add (0); - return nodeIds.size() - 1; - } - } - - int getReadOnlyEmptyBuffer() const throw() - { - return 0; - } - - int getBufferContaining (const int nodeId, const int outputChannel) const throw() - { - if (outputChannel == AudioProcessorGraph::midiChannelIndex) - { - for (int i = midiNodeIds.size(); --i >= 0;) - if (midiNodeIds.getUnchecked(i) == nodeId) - return i; - } - else - { - for (int i = nodeIds.size(); --i >= 0;) - if (nodeIds.getUnchecked(i) == nodeId - && channels.getUnchecked(i) == outputChannel) - return i; - } - - return -1; - } - - void markAnyUnusedBuffersAsFree (const int stepIndex) - { - int i; - for (i = 0; i < nodeIds.size(); ++i) - { - if (nodeIds.getUnchecked(i) >= 0 - && ! isBufferNeededLater (stepIndex, -1, - nodeIds.getUnchecked(i), - channels.getUnchecked(i))) - { - nodeIds.set (i, -1); - } - } - - for (i = 0; i < midiNodeIds.size(); ++i) - { - if (midiNodeIds.getUnchecked(i) >= 0 - && ! isBufferNeededLater (stepIndex, -1, - midiNodeIds.getUnchecked(i), - AudioProcessorGraph::midiChannelIndex)) - { - midiNodeIds.set (i, -1); - } - } - } - - bool isBufferNeededLater (int stepIndexToSearchFrom, - int inputChannelOfIndexToIgnore, - const int nodeId, - const int outputChanIndex) const throw() - { - while (stepIndexToSearchFrom < orderedNodes.size()) - { - const AudioProcessorGraph::Node* const node = (const AudioProcessorGraph::Node*) orderedNodes.getUnchecked (stepIndexToSearchFrom); - - if (outputChanIndex == AudioProcessorGraph::midiChannelIndex) - { - if (inputChannelOfIndexToIgnore != AudioProcessorGraph::midiChannelIndex - && graph.getConnectionBetween (nodeId, AudioProcessorGraph::midiChannelIndex, - node->id, AudioProcessorGraph::midiChannelIndex) != 0) - return true; - } - else - { - for (int i = 0; i < node->processor->getNumInputChannels(); ++i) - if (i != inputChannelOfIndexToIgnore - && graph.getConnectionBetween (nodeId, outputChanIndex, - node->id, i) != 0) - return true; - } - - inputChannelOfIndexToIgnore = -1; - ++stepIndexToSearchFrom; - } - - return false; - } - - void markBufferAsContaining (int bufferNum, int nodeId, int outputIndex) - { - if (outputIndex == AudioProcessorGraph::midiChannelIndex) - { - jassert (bufferNum > 0 && bufferNum < midiNodeIds.size()); - - midiNodeIds.set (bufferNum, nodeId); - } - else - { - jassert (bufferNum > 0 && bufferNum < nodeIds.size()); - - nodeIds.set (bufferNum, nodeId); - channels.set (bufferNum, outputIndex); - } - } - - RenderingOpSequenceCalculator (const RenderingOpSequenceCalculator&); - const RenderingOpSequenceCalculator& operator= (const RenderingOpSequenceCalculator&); -}; - -} - -//============================================================================== -void AudioProcessorGraph::clearRenderingSequence() -{ - const ScopedLock sl (renderLock); - - for (int i = renderingOps.size(); --i >= 0;) - { - GraphRenderingOps::AudioGraphRenderingOp* const r - = (GraphRenderingOps::AudioGraphRenderingOp*) renderingOps.getUnchecked(i); - - renderingOps.remove (i); - delete r; - } -} - -bool AudioProcessorGraph::isAnInputTo (const uint32 possibleInputId, - const uint32 possibleDestinationId, - const int recursionCheck) const throw() -{ - if (recursionCheck > 0) - { - for (int i = connections.size(); --i >= 0;) - { - const AudioProcessorGraph::Connection* const c = connections.getUnchecked (i); - - if (c->destNodeId == possibleDestinationId - && (c->sourceNodeId == possibleInputId - || isAnInputTo (possibleInputId, c->sourceNodeId, recursionCheck - 1))) - return true; - } - } - - return false; -} - -void AudioProcessorGraph::buildRenderingSequence() -{ - VoidArray newRenderingOps; - int numRenderingBuffersNeeded = 2; - int numMidiBuffersNeeded = 1; - - { - MessageManagerLock mml; - - VoidArray orderedNodes; - - int i; - for (i = 0; i < nodes.size(); ++i) - { - Node* const node = nodes.getUnchecked(i); - - node->prepare (getSampleRate(), getBlockSize(), this); - - int j = 0; - for (; j < orderedNodes.size(); ++j) - if (isAnInputTo (node->id, - ((Node*) orderedNodes.getUnchecked (j))->id, - nodes.size() + 1)) - break; - - orderedNodes.insert (j, node); - } - - GraphRenderingOps::RenderingOpSequenceCalculator calculator (*this, orderedNodes, newRenderingOps); - - numRenderingBuffersNeeded = calculator.getNumBuffersNeeded(); - numMidiBuffersNeeded = calculator.getNumMidiBuffersNeeded(); - } - - VoidArray oldRenderingOps (renderingOps); - - { - // swap over to the new set of rendering sequence.. - const ScopedLock sl (renderLock); - - renderingBuffers.setSize (numRenderingBuffersNeeded, getBlockSize()); - renderingBuffers.clear(); - - for (int i = midiBuffers.size(); --i >= 0;) - midiBuffers.getUnchecked(i)->clear(); - - while (midiBuffers.size() < numMidiBuffersNeeded) - midiBuffers.add (new MidiBuffer()); - - renderingOps = newRenderingOps; - } - - for (int i = oldRenderingOps.size(); --i >= 0;) - delete (GraphRenderingOps::AudioGraphRenderingOp*) oldRenderingOps.getUnchecked(i); -} - -void AudioProcessorGraph::handleAsyncUpdate() -{ - buildRenderingSequence(); -} - -//============================================================================== -void AudioProcessorGraph::prepareToPlay (double /*sampleRate*/, int /*estimatedSamplesPerBlock*/) -{ - currentAudioIOBuffer = 0; - currentMidiIOBuffer = 0; - - clearRenderingSequence(); - buildRenderingSequence(); -} - -void AudioProcessorGraph::releaseResources() -{ - for (int i = 0; i < nodes.size(); ++i) - nodes.getUnchecked(i)->unprepare(); - - renderingBuffers.setSize (1, 1); - midiBuffers.clear(); - - currentAudioIOBuffer = 0; - currentMidiIOBuffer = 0; -} - -void AudioProcessorGraph::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) -{ - const ScopedLock sl (renderLock); - - currentAudioIOBuffer = &buffer; - currentMidiIOBuffer = &midiMessages; - - const int numSamples = buffer.getNumSamples(); - - for (int i = 0; i < renderingOps.size(); ++i) - { - GraphRenderingOps::AudioGraphRenderingOp* const op - = (GraphRenderingOps::AudioGraphRenderingOp*) renderingOps.getUnchecked(i); - - op->perform (renderingBuffers, midiBuffers, numSamples); - } -} - -const String AudioProcessorGraph::getInputChannelName (const int channelIndex) const -{ - return "Input " + String (channelIndex + 1); -} - -const String AudioProcessorGraph::getOutputChannelName (const int channelIndex) const -{ - return "Output " + String (channelIndex + 1); -} - -bool AudioProcessorGraph::isInputChannelStereoPair (int /*index*/) const -{ - return true; -} - -bool AudioProcessorGraph::isOutputChannelStereoPair (int /*index*/) const -{ - return true; -} - -bool AudioProcessorGraph::acceptsMidi() const -{ - return true; -} - -bool AudioProcessorGraph::producesMidi() const -{ - return true; -} - -void AudioProcessorGraph::getStateInformation (JUCE_NAMESPACE::MemoryBlock& /*destData*/) -{ -} - -void AudioProcessorGraph::setStateInformation (const void* /*data*/, int /*sizeInBytes*/) -{ -} - - -//============================================================================== -AudioProcessorGraph::AudioGraphIOProcessor::AudioGraphIOProcessor (const IODeviceType type_) - : type (type_), - graph (0) -{ -} - -AudioProcessorGraph::AudioGraphIOProcessor::~AudioGraphIOProcessor() -{ -} - -const String AudioProcessorGraph::AudioGraphIOProcessor::getName() const -{ - switch (type) - { - case audioOutputNode: - return "Audio Output"; - case audioInputNode: - return "Audio Input"; - case midiOutputNode: - return "Midi Output"; - case midiInputNode: - return "Midi Input"; - default: - break; - } - - return String::empty; -} - -void AudioProcessorGraph::AudioGraphIOProcessor::fillInPluginDescription (PluginDescription& d) const -{ - d.name = getName(); - d.uid = d.name.hashCode(); - d.category = "I/O devices"; - d.pluginFormatName = "Internal"; - d.manufacturerName = "Raw Material Software"; - d.version = "1.0"; - d.isInstrument = false; - - d.numInputChannels = getNumInputChannels(); - if (type == audioOutputNode && graph != 0) - d.numInputChannels = graph->getNumInputChannels(); - - d.numOutputChannels = getNumOutputChannels(); - if (type == audioInputNode && graph != 0) - d.numOutputChannels = graph->getNumOutputChannels(); -} - -void AudioProcessorGraph::AudioGraphIOProcessor::prepareToPlay (double, int) -{ - jassert (graph != 0); -} - -void AudioProcessorGraph::AudioGraphIOProcessor::releaseResources() -{ -} - -void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioSampleBuffer& buffer, - MidiBuffer& midiMessages) -{ - jassert (graph != 0); - - switch (type) - { - case audioOutputNode: - { - for (int i = jmin (graph->currentAudioIOBuffer->getNumChannels(), - buffer.getNumChannels()); --i >= 0;) - { - graph->currentAudioIOBuffer->addFrom (i, 0, buffer, i, 0, buffer.getNumSamples()); - } - - break; - } - - case audioInputNode: - { - for (int i = jmin (graph->currentAudioIOBuffer->getNumChannels(), - buffer.getNumChannels()); --i >= 0;) - { - buffer.addFrom (i, 0, *graph->currentAudioIOBuffer, i, 0, buffer.getNumSamples()); - } - - break; - } - - case midiOutputNode: - graph->currentMidiIOBuffer->addEvents (midiMessages, 0, buffer.getNumSamples(), 0); - break; - - case midiInputNode: - midiMessages.addEvents (*graph->currentMidiIOBuffer, 0, buffer.getNumSamples(), 0); - break; - - default: - break; - } -} - -bool AudioProcessorGraph::AudioGraphIOProcessor::acceptsMidi() const -{ - return type == midiOutputNode; -} - -bool AudioProcessorGraph::AudioGraphIOProcessor::producesMidi() const -{ - return type == midiInputNode; -} - -const String AudioProcessorGraph::AudioGraphIOProcessor::getInputChannelName (const int channelIndex) const -{ - switch (type) - { - case audioOutputNode: - return "Output " + String (channelIndex + 1); - case midiOutputNode: - return "Midi Output"; - default: - break; - } - - return String::empty; -} - -const String AudioProcessorGraph::AudioGraphIOProcessor::getOutputChannelName (const int channelIndex) const -{ - switch (type) - { - case audioInputNode: - return "Input " + String (channelIndex + 1); - case midiInputNode: - return "Midi Input"; - default: - break; - } - - return String::empty; -} - -bool AudioProcessorGraph::AudioGraphIOProcessor::isInputChannelStereoPair (int /*index*/) const -{ - return type == audioInputNode || type == audioOutputNode; -} - -bool AudioProcessorGraph::AudioGraphIOProcessor::isOutputChannelStereoPair (int index) const -{ - return isInputChannelStereoPair (index); -} - -bool AudioProcessorGraph::AudioGraphIOProcessor::isInput() const throw() -{ - return type == audioInputNode || type == midiInputNode; -} - -bool AudioProcessorGraph::AudioGraphIOProcessor::isOutput() const throw() -{ - return type == audioOutputNode || type == midiOutputNode; -} - -AudioProcessorEditor* AudioProcessorGraph::AudioGraphIOProcessor::createEditor() -{ - return 0; -} - -int AudioProcessorGraph::AudioGraphIOProcessor::getNumParameters() { return 0; } -const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterName (int) { return String::empty; } - -float AudioProcessorGraph::AudioGraphIOProcessor::getParameter (int) { return 0.0f; } -const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterText (int) { return String::empty; } -void AudioProcessorGraph::AudioGraphIOProcessor::setParameter (int, float) { } - -int AudioProcessorGraph::AudioGraphIOProcessor::getNumPrograms() { return 0; } -int AudioProcessorGraph::AudioGraphIOProcessor::getCurrentProgram() { return 0; } -void AudioProcessorGraph::AudioGraphIOProcessor::setCurrentProgram (int) { } - -const String AudioProcessorGraph::AudioGraphIOProcessor::getProgramName (int) { return String::empty; } -void AudioProcessorGraph::AudioGraphIOProcessor::changeProgramName (int, const String&) { } - -void AudioProcessorGraph::AudioGraphIOProcessor::getStateInformation (JUCE_NAMESPACE::MemoryBlock&) -{ -} - -void AudioProcessorGraph::AudioGraphIOProcessor::setStateInformation (const void*, int) -{ -} - -void AudioProcessorGraph::AudioGraphIOProcessor::setParentGraph (AudioProcessorGraph* const newGraph) throw() -{ - graph = newGraph; - - if (graph != 0) - { - setPlayConfigDetails (type == audioOutputNode ? graph->getNumInputChannels() : 0, - type == audioInputNode ? graph->getNumOutputChannels() : 0, - getSampleRate(), - getBlockSize()); - - updateHostDisplay(); - } -} - - -END_JUCE_NAMESPACE +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_AudioProcessorGraph.h" +#include "../../events/juce_MessageManager.h" + + +const int AudioProcessorGraph::midiChannelIndex = 0x1000; + +//============================================================================== +AudioProcessorGraph::Node::Node (const uint32 id_, + AudioProcessor* const processor_) throw() + : id (id_), + processor (processor_), + isPrepared (false) +{ + jassert (processor_ != 0); +} + +AudioProcessorGraph::Node::~Node() +{ + delete processor; +} + +void AudioProcessorGraph::Node::prepare (const double sampleRate, const int blockSize, + AudioProcessorGraph* const graph) +{ + if (! isPrepared) + { + isPrepared = true; + + AudioProcessorGraph::AudioGraphIOProcessor* const ioProc + = dynamic_cast (processor); + + if (ioProc != 0) + ioProc->setParentGraph (graph); + + processor->setPlayConfigDetails (processor->getNumInputChannels(), + processor->getNumOutputChannels(), + sampleRate, blockSize); + + processor->prepareToPlay (sampleRate, blockSize); + } +} + +void AudioProcessorGraph::Node::unprepare() +{ + if (isPrepared) + { + isPrepared = false; + processor->releaseResources(); + } +} + +//============================================================================== +AudioProcessorGraph::AudioProcessorGraph() + : lastNodeId (0), + renderingBuffers (1, 1), + currentAudioOutputBuffer (1, 1) +{ +} + +AudioProcessorGraph::~AudioProcessorGraph() +{ + clearRenderingSequence(); + clear(); +} + +const String AudioProcessorGraph::getName() const +{ + return "Audio Graph"; +} + +//============================================================================== +void AudioProcessorGraph::clear() +{ + nodes.clear(); + connections.clear(); + triggerAsyncUpdate(); +} + +AudioProcessorGraph::Node* AudioProcessorGraph::getNodeForId (const uint32 nodeId) const throw() +{ + for (int i = nodes.size(); --i >= 0;) + if (nodes.getUnchecked(i)->id == nodeId) + return nodes.getUnchecked(i); + + return 0; +} + +AudioProcessorGraph::Node* AudioProcessorGraph::addNode (AudioProcessor* const newProcessor, + uint32 nodeId) +{ + if (newProcessor == 0) + { + jassertfalse + return 0; + } + + if (nodeId == 0) + { + nodeId = ++lastNodeId; + } + else + { + // you can't add a node with an id that already exists in the graph.. + jassert (getNodeForId (nodeId) == 0); + removeNode (nodeId); + } + + lastNodeId = nodeId; + + Node* const n = new Node (nodeId, newProcessor); + nodes.add (n); + triggerAsyncUpdate(); + + AudioProcessorGraph::AudioGraphIOProcessor* const ioProc + = dynamic_cast (n->processor); + + if (ioProc != 0) + ioProc->setParentGraph (this); + + return n; +} + +bool AudioProcessorGraph::removeNode (const uint32 nodeId) +{ + disconnectNode (nodeId); + + for (int i = nodes.size(); --i >= 0;) + { + if (nodes.getUnchecked(i)->id == nodeId) + { + AudioProcessorGraph::AudioGraphIOProcessor* const ioProc + = dynamic_cast (nodes.getUnchecked(i)->processor); + + if (ioProc != 0) + ioProc->setParentGraph (0); + + nodes.remove (i); + triggerAsyncUpdate(); + + return true; + } + } + + return false; +} + +//============================================================================== +const AudioProcessorGraph::Connection* AudioProcessorGraph::getConnectionBetween (const uint32 sourceNodeId, + const int sourceChannelIndex, + const uint32 destNodeId, + const int destChannelIndex) const throw() +{ + for (int i = connections.size(); --i >= 0;) + { + const Connection* const c = connections.getUnchecked(i); + + if (c->sourceNodeId == sourceNodeId + && c->destNodeId == destNodeId + && c->sourceChannelIndex == sourceChannelIndex + && c->destChannelIndex == destChannelIndex) + { + return c; + } + } + + return 0; +} + +bool AudioProcessorGraph::isConnected (const uint32 possibleSourceNodeId, + const uint32 possibleDestNodeId) const throw() +{ + for (int i = connections.size(); --i >= 0;) + { + const Connection* const c = connections.getUnchecked(i); + + if (c->sourceNodeId == possibleSourceNodeId + && c->destNodeId == possibleDestNodeId) + { + return true; + } + } + + return false; +} + +bool AudioProcessorGraph::canConnect (const uint32 sourceNodeId, + const int sourceChannelIndex, + const uint32 destNodeId, + const int destChannelIndex) const throw() +{ + if (sourceChannelIndex < 0 + || destChannelIndex < 0 + || sourceNodeId == destNodeId + || (destChannelIndex == midiChannelIndex) != (sourceChannelIndex == midiChannelIndex)) + return false; + + const Node* const source = getNodeForId (sourceNodeId); + + if (source == 0 + || (sourceChannelIndex != midiChannelIndex && sourceChannelIndex >= source->processor->getNumOutputChannels()) + || (sourceChannelIndex == midiChannelIndex && ! source->processor->producesMidi())) + return false; + + const Node* const dest = getNodeForId (destNodeId); + + if (dest == 0 + || (destChannelIndex != midiChannelIndex && destChannelIndex >= dest->processor->getNumInputChannels()) + || (destChannelIndex == midiChannelIndex && ! dest->processor->acceptsMidi())) + return false; + + return getConnectionBetween (sourceNodeId, sourceChannelIndex, + destNodeId, destChannelIndex) == 0; +} + +bool AudioProcessorGraph::addConnection (const uint32 sourceNodeId, + const int sourceChannelIndex, + const uint32 destNodeId, + const int destChannelIndex) +{ + if (! canConnect (sourceNodeId, sourceChannelIndex, destNodeId, destChannelIndex)) + return false; + + Connection* const c = new Connection(); + c->sourceNodeId = sourceNodeId; + c->sourceChannelIndex = sourceChannelIndex; + c->destNodeId = destNodeId; + c->destChannelIndex = destChannelIndex; + + connections.add (c); + triggerAsyncUpdate(); + + return true; +} + +void AudioProcessorGraph::removeConnection (const int index) +{ + connections.remove (index); + triggerAsyncUpdate(); +} + +bool AudioProcessorGraph::removeConnection (const uint32 sourceNodeId, const int sourceChannelIndex, + const uint32 destNodeId, const int destChannelIndex) +{ + bool doneAnything = false; + + for (int i = connections.size(); --i >= 0;) + { + const Connection* const c = connections.getUnchecked(i); + + if (c->sourceNodeId == sourceNodeId + && c->destNodeId == destNodeId + && c->sourceChannelIndex == sourceChannelIndex + && c->destChannelIndex == destChannelIndex) + { + removeConnection (i); + doneAnything = true; + triggerAsyncUpdate(); + } + } + + return doneAnything; +} + +bool AudioProcessorGraph::disconnectNode (const uint32 nodeId) +{ + bool doneAnything = false; + + for (int i = connections.size(); --i >= 0;) + { + const Connection* const c = connections.getUnchecked(i); + + if (c->sourceNodeId == nodeId || c->destNodeId == nodeId) + { + removeConnection (i); + doneAnything = true; + triggerAsyncUpdate(); + } + } + + return doneAnything; +} + +bool AudioProcessorGraph::removeIllegalConnections() +{ + bool doneAnything = false; + + for (int i = connections.size(); --i >= 0;) + { + const Connection* const c = connections.getUnchecked(i); + + const Node* const source = getNodeForId (c->sourceNodeId); + const Node* const dest = getNodeForId (c->destNodeId); + + if (source == 0 || dest == 0 + || (c->sourceChannelIndex != midiChannelIndex + && (((unsigned int) c->sourceChannelIndex) >= (unsigned int) source->processor->getNumOutputChannels())) + || (c->sourceChannelIndex == midiChannelIndex + && ! source->processor->producesMidi()) + || (c->destChannelIndex != midiChannelIndex + && (((unsigned int) c->destChannelIndex) >= (unsigned int) dest->processor->getNumInputChannels())) + || (c->destChannelIndex == midiChannelIndex + && ! dest->processor->acceptsMidi())) + { + removeConnection (i); + doneAnything = true; + triggerAsyncUpdate(); + } + } + + return doneAnything; +} + +//============================================================================== +namespace GraphRenderingOps +{ + +//============================================================================== +class AudioGraphRenderingOp +{ +public: + AudioGraphRenderingOp() throw() {} + virtual ~AudioGraphRenderingOp() throw() {} + + virtual void perform (AudioSampleBuffer& sharedBufferChans, + const OwnedArray & sharedMidiBuffers, + const int numSamples) throw() = 0; + + juce_UseDebuggingNewOperator +}; + +//============================================================================== +class ClearChannelOp : public AudioGraphRenderingOp +{ +public: + ClearChannelOp (const int channelNum_) throw() + : channelNum (channelNum_) + {} + + ~ClearChannelOp() throw() {} + + void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray &, const int numSamples) throw() + { + sharedBufferChans.clear (channelNum, 0, numSamples); + } + +private: + const int channelNum; + + ClearChannelOp (const ClearChannelOp&); + const ClearChannelOp& operator= (const ClearChannelOp&); +}; + +//============================================================================== +class CopyChannelOp : public AudioGraphRenderingOp +{ +public: + CopyChannelOp (const int srcChannelNum_, const int dstChannelNum_) throw() + : srcChannelNum (srcChannelNum_), + dstChannelNum (dstChannelNum_) + {} + + ~CopyChannelOp() throw() {} + + void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray &, const int numSamples) throw() + { + sharedBufferChans.copyFrom (dstChannelNum, 0, sharedBufferChans, srcChannelNum, 0, numSamples); + } + +private: + const int srcChannelNum, dstChannelNum; + + CopyChannelOp (const CopyChannelOp&); + const CopyChannelOp& operator= (const CopyChannelOp&); +}; + +//============================================================================== +class AddChannelOp : public AudioGraphRenderingOp +{ +public: + AddChannelOp (const int srcChannelNum_, const int dstChannelNum_) throw() + : srcChannelNum (srcChannelNum_), + dstChannelNum (dstChannelNum_) + {} + + ~AddChannelOp() throw() {} + + void perform (AudioSampleBuffer& sharedBufferChans, + const OwnedArray & sharedMidiBuffers, + const int numSamples) throw() + { + if (dstChannelNum != AudioProcessorGraph::midiChannelIndex) + sharedBufferChans.addFrom (dstChannelNum, 0, sharedBufferChans, srcChannelNum, 0, numSamples); + else + sharedMidiBuffers.getUnchecked (dstChannelNum) + ->addEvents (*sharedMidiBuffers.getUnchecked (srcChannelNum), + 0, numSamples, 0); + } + +private: + const int srcChannelNum, dstChannelNum; + + AddChannelOp (const AddChannelOp&); + const AddChannelOp& operator= (const AddChannelOp&); +}; + +//============================================================================== +class ClearMidiBufferOp : public AudioGraphRenderingOp +{ +public: + ClearMidiBufferOp (const int bufferNum_) throw() + : bufferNum (bufferNum_) + {} + + ~ClearMidiBufferOp() throw() {} + + void perform (AudioSampleBuffer&, const OwnedArray & sharedMidiBuffers, const int) throw() + { + sharedMidiBuffers.getUnchecked (bufferNum)->clear(); + } + +private: + const int bufferNum; + + ClearMidiBufferOp (const ClearMidiBufferOp&); + const ClearMidiBufferOp& operator= (const ClearMidiBufferOp&); +}; + +//============================================================================== +class CopyMidiBufferOp : public AudioGraphRenderingOp +{ +public: + CopyMidiBufferOp (const int srcBufferNum_, const int dstBufferNum_) throw() + : srcBufferNum (srcBufferNum_), + dstBufferNum (dstBufferNum_) + {} + + ~CopyMidiBufferOp() throw() {} + + void perform (AudioSampleBuffer&, const OwnedArray & sharedMidiBuffers, const int) throw() + { + *sharedMidiBuffers.getUnchecked (dstBufferNum) = *sharedMidiBuffers.getUnchecked (srcBufferNum); + } + +private: + const int srcBufferNum, dstBufferNum; + + CopyMidiBufferOp (const CopyMidiBufferOp&); + const CopyMidiBufferOp& operator= (const CopyMidiBufferOp&); +}; + +//============================================================================== +class AddMidiBufferOp : public AudioGraphRenderingOp +{ +public: + AddMidiBufferOp (const int srcBufferNum_, const int dstBufferNum_) throw() + : srcBufferNum (srcBufferNum_), + dstBufferNum (dstBufferNum_) + {} + + ~AddMidiBufferOp() throw() {} + + void perform (AudioSampleBuffer&, const OwnedArray & sharedMidiBuffers, const int numSamples) throw() + { + sharedMidiBuffers.getUnchecked (dstBufferNum) + ->addEvents (*sharedMidiBuffers.getUnchecked (srcBufferNum), 0, numSamples, 0); + } + +private: + const int srcBufferNum, dstBufferNum; + + AddMidiBufferOp (const AddMidiBufferOp&); + const AddMidiBufferOp& operator= (const AddMidiBufferOp&); +}; + +//============================================================================== +class ProcessBufferOp : public AudioGraphRenderingOp +{ +public: + ProcessBufferOp (const AudioProcessorGraph::Node::Ptr& node_, + const Array & audioChannelsToUse_, + const int totalChans_, + const int midiBufferToUse_) throw() + : node (node_), + processor (node_->processor), + audioChannelsToUse (audioChannelsToUse_), + totalChans (totalChans_), + midiBufferToUse (midiBufferToUse_) + { + channels = (float**) juce_calloc (sizeof (float*) * totalChans_); + } + + ~ProcessBufferOp() throw() + { + juce_free (channels); + } + + void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray & sharedMidiBuffers, const int numSamples) throw() + { + for (int i = totalChans; --i >= 0;) + channels[i] = sharedBufferChans.getSampleData (audioChannelsToUse.getUnchecked (i), 0); + + AudioSampleBuffer buffer (channels, totalChans, numSamples); + + processor->processBlock (buffer, *sharedMidiBuffers.getUnchecked (midiBufferToUse)); + } + + const AudioProcessorGraph::Node::Ptr node; + AudioProcessor* const processor; + +private: + Array audioChannelsToUse; + float** channels; + int totalChans; + int midiBufferToUse; + + ProcessBufferOp (const ProcessBufferOp&); + const ProcessBufferOp& operator= (const ProcessBufferOp&); +}; + +//============================================================================== +/** Used to calculate the correct sequence of rendering ops needed, based on + the best re-use of shared buffers at each stage. +*/ +class RenderingOpSequenceCalculator +{ +public: + //============================================================================== + RenderingOpSequenceCalculator (AudioProcessorGraph& graph_, + const VoidArray& orderedNodes_, + VoidArray& renderingOps) + : graph (graph_), + orderedNodes (orderedNodes_) + { + nodeIds.add (-2); // first buffer is read-only zeros + channels.add (0); + + midiNodeIds.add (-2); + + for (int i = 0; i < orderedNodes.size(); ++i) + { + createRenderingOpsForNode ((AudioProcessorGraph::Node*) orderedNodes.getUnchecked(i), + renderingOps, i); + + markAnyUnusedBuffersAsFree (i); + } + } + + int getNumBuffersNeeded() const throw() { return nodeIds.size(); } + + int getNumMidiBuffersNeeded() const throw() { return midiNodeIds.size(); } + + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + AudioProcessorGraph& graph; + const VoidArray& orderedNodes; + Array nodeIds, channels, midiNodeIds; + + //============================================================================== + void createRenderingOpsForNode (AudioProcessorGraph::Node* const node, + VoidArray& renderingOps, + const int ourRenderingIndex) + { + const int numIns = node->processor->getNumInputChannels(); + const int numOuts = node->processor->getNumOutputChannels(); + const int totalChans = jmax (numIns, numOuts); + + Array audioChannelsToUse; + int midiBufferToUse = -1; + + for (int inputChan = 0; inputChan < numIns; ++inputChan) + { + // get a list of all the inputs to this node + Array sourceNodes, sourceOutputChans; + + for (int i = graph.getNumConnections(); --i >= 0;) + { + const AudioProcessorGraph::Connection* const c = graph.getConnection (i); + + if (c->destNodeId == node->id && c->destChannelIndex == inputChan) + { + sourceNodes.add (c->sourceNodeId); + sourceOutputChans.add (c->sourceChannelIndex); + } + } + + int bufIndex = -1; + + if (sourceNodes.size() == 0) + { + // unconnected input channel + + if (inputChan >= numOuts) + { + bufIndex = getReadOnlyEmptyBuffer(); + jassert (bufIndex >= 0); + } + else + { + bufIndex = getFreeBuffer (false); + renderingOps.add (new ClearChannelOp (bufIndex)); + } + } + else if (sourceNodes.size() == 1) + { + // channel with a straightforward single input.. + const int srcNode = sourceNodes.getUnchecked(0); + const int srcChan = sourceOutputChans.getUnchecked(0); + + bufIndex = getBufferContaining (srcNode, srcChan); + + if (bufIndex < 0) + { + // if not found, this is probably a feedback loop + bufIndex = getReadOnlyEmptyBuffer(); + jassert (bufIndex >= 0); + } + + if (inputChan < numOuts + && isBufferNeededLater (ourRenderingIndex, + inputChan, + srcNode, srcChan)) + { + // can't mess up this channel because it's needed later by another node, so we + // need to use a copy of it.. + const int newFreeBuffer = getFreeBuffer (false); + + renderingOps.add (new CopyChannelOp (bufIndex, newFreeBuffer)); + + bufIndex = newFreeBuffer; + } + } + else + { + // channel with a mix of several inputs.. + + // try to find a re-usable channel from our inputs.. + int reusableInputIndex = -1; + + for (int i = 0; i < sourceNodes.size(); ++i) + { + const int sourceBufIndex = getBufferContaining (sourceNodes.getUnchecked(i), + sourceOutputChans.getUnchecked(i)); + + if (sourceBufIndex >= 0 + && ! isBufferNeededLater (ourRenderingIndex, + inputChan, + sourceNodes.getUnchecked(i), + sourceOutputChans.getUnchecked(i))) + { + // we've found one of our input chans that can be re-used.. + reusableInputIndex = i; + bufIndex = sourceBufIndex; + break; + } + } + + if (reusableInputIndex < 0) + { + // can't re-use any of our input chans, so get a new one and copy everything into it.. + bufIndex = getFreeBuffer (false); + jassert (bufIndex != 0); + + const int srcIndex = getBufferContaining (sourceNodes.getUnchecked (0), + sourceOutputChans.getUnchecked (0)); + if (srcIndex < 0) + { + // if not found, this is probably a feedback loop + renderingOps.add (new ClearChannelOp (bufIndex)); + } + else + { + renderingOps.add (new CopyChannelOp (srcIndex, bufIndex)); + } + + reusableInputIndex = 0; + } + + for (int j = 0; j < sourceNodes.size(); ++j) + { + if (j != reusableInputIndex) + { + const int srcIndex = getBufferContaining (sourceNodes.getUnchecked(j), + sourceOutputChans.getUnchecked(j)); + jassert (srcIndex >= 0); + + renderingOps.add (new AddChannelOp (srcIndex, bufIndex)); + } + } + } + + jassert (bufIndex >= 0); + audioChannelsToUse.add (bufIndex); + + if (inputChan < numOuts) + markBufferAsContaining (bufIndex, node->id, inputChan); + } + + for (int outputChan = numIns; outputChan < numOuts; ++outputChan) + { + const int bufIndex = getFreeBuffer (false); + jassert (bufIndex != 0); + audioChannelsToUse.add (bufIndex); + + markBufferAsContaining (bufIndex, node->id, outputChan); + } + + // Now the same thing for midi.. + Array midiSourceNodes; + + for (int i = graph.getNumConnections(); --i >= 0;) + { + const AudioProcessorGraph::Connection* const c = graph.getConnection (i); + + if (c->destNodeId == node->id && c->destChannelIndex == AudioProcessorGraph::midiChannelIndex) + midiSourceNodes.add (c->sourceNodeId); + } + + if (midiSourceNodes.size() == 0) + { + // No midi inputs.. + midiBufferToUse = getFreeBuffer (true); // need to pick a buffer even if the processor doesn't use midi + + if (node->processor->acceptsMidi() || node->processor->producesMidi()) + renderingOps.add (new ClearMidiBufferOp (midiBufferToUse)); + } + else if (midiSourceNodes.size() == 1) + { + // One midi input.. + midiBufferToUse = getBufferContaining (midiSourceNodes.getUnchecked(0), + AudioProcessorGraph::midiChannelIndex); + + if (midiBufferToUse >= 0) + { + if (isBufferNeededLater (ourRenderingIndex, + AudioProcessorGraph::midiChannelIndex, + midiSourceNodes.getUnchecked(0), + AudioProcessorGraph::midiChannelIndex)) + { + // can't mess up this channel because it's needed later by another node, so we + // need to use a copy of it.. + const int newFreeBuffer = getFreeBuffer (true); + renderingOps.add (new CopyMidiBufferOp (midiBufferToUse, newFreeBuffer)); + midiBufferToUse = newFreeBuffer; + } + } + else + { + // probably a feedback loop, so just use an empty one.. + midiBufferToUse = getFreeBuffer (true); // need to pick a buffer even if the processor doesn't use midi + } + } + else + { + // More than one midi input being mixed.. + int reusableInputIndex = -1; + + for (int i = 0; i < midiSourceNodes.size(); ++i) + { + const int sourceBufIndex = getBufferContaining (midiSourceNodes.getUnchecked(i), + AudioProcessorGraph::midiChannelIndex); + + if (sourceBufIndex >= 0 + && ! isBufferNeededLater (ourRenderingIndex, + AudioProcessorGraph::midiChannelIndex, + midiSourceNodes.getUnchecked(i), + AudioProcessorGraph::midiChannelIndex)) + { + // we've found one of our input buffers that can be re-used.. + reusableInputIndex = i; + midiBufferToUse = sourceBufIndex; + break; + } + } + + if (reusableInputIndex < 0) + { + // can't re-use any of our input buffers, so get a new one and copy everything into it.. + midiBufferToUse = getFreeBuffer (true); + jassert (midiBufferToUse >= 0); + + const int srcIndex = getBufferContaining (midiSourceNodes.getUnchecked(0), + AudioProcessorGraph::midiChannelIndex); + if (srcIndex >= 0) + renderingOps.add (new CopyMidiBufferOp (srcIndex, midiBufferToUse)); + else + renderingOps.add (new ClearMidiBufferOp (midiBufferToUse)); + + reusableInputIndex = 0; + } + + for (int j = 0; j < midiSourceNodes.size(); ++j) + { + if (j != reusableInputIndex) + { + const int srcIndex = getBufferContaining (midiSourceNodes.getUnchecked(j), + AudioProcessorGraph::midiChannelIndex); + if (srcIndex >= 0) + renderingOps.add (new AddMidiBufferOp (srcIndex, midiBufferToUse)); + } + } + } + + if (node->processor->producesMidi()) + markBufferAsContaining (midiBufferToUse, node->id, + AudioProcessorGraph::midiChannelIndex); + + renderingOps.add (new ProcessBufferOp (node, audioChannelsToUse, + totalChans, midiBufferToUse)); + } + + //============================================================================== + int getFreeBuffer (const bool forMidi) + { + if (forMidi) + { + for (int i = 1; i < midiNodeIds.size(); ++i) + if (midiNodeIds.getUnchecked(i) < 0) + return i; + + midiNodeIds.add (-1); + return midiNodeIds.size() - 1; + } + else + { + for (int i = 1; i < nodeIds.size(); ++i) + if (nodeIds.getUnchecked(i) < 0) + return i; + + nodeIds.add (-1); + channels.add (0); + return nodeIds.size() - 1; + } + } + + int getReadOnlyEmptyBuffer() const throw() + { + return 0; + } + + int getBufferContaining (const int nodeId, const int outputChannel) const throw() + { + if (outputChannel == AudioProcessorGraph::midiChannelIndex) + { + for (int i = midiNodeIds.size(); --i >= 0;) + if (midiNodeIds.getUnchecked(i) == nodeId) + return i; + } + else + { + for (int i = nodeIds.size(); --i >= 0;) + if (nodeIds.getUnchecked(i) == nodeId + && channels.getUnchecked(i) == outputChannel) + return i; + } + + return -1; + } + + void markAnyUnusedBuffersAsFree (const int stepIndex) + { + int i; + for (i = 0; i < nodeIds.size(); ++i) + { + if (nodeIds.getUnchecked(i) >= 0 + && ! isBufferNeededLater (stepIndex, -1, + nodeIds.getUnchecked(i), + channels.getUnchecked(i))) + { + nodeIds.set (i, -1); + } + } + + for (i = 0; i < midiNodeIds.size(); ++i) + { + if (midiNodeIds.getUnchecked(i) >= 0 + && ! isBufferNeededLater (stepIndex, -1, + midiNodeIds.getUnchecked(i), + AudioProcessorGraph::midiChannelIndex)) + { + midiNodeIds.set (i, -1); + } + } + } + + bool isBufferNeededLater (int stepIndexToSearchFrom, + int inputChannelOfIndexToIgnore, + const int nodeId, + const int outputChanIndex) const throw() + { + while (stepIndexToSearchFrom < orderedNodes.size()) + { + const AudioProcessorGraph::Node* const node = (const AudioProcessorGraph::Node*) orderedNodes.getUnchecked (stepIndexToSearchFrom); + + if (outputChanIndex == AudioProcessorGraph::midiChannelIndex) + { + if (inputChannelOfIndexToIgnore != AudioProcessorGraph::midiChannelIndex + && graph.getConnectionBetween (nodeId, AudioProcessorGraph::midiChannelIndex, + node->id, AudioProcessorGraph::midiChannelIndex) != 0) + return true; + } + else + { + for (int i = 0; i < node->processor->getNumInputChannels(); ++i) + if (i != inputChannelOfIndexToIgnore + && graph.getConnectionBetween (nodeId, outputChanIndex, + node->id, i) != 0) + return true; + } + + inputChannelOfIndexToIgnore = -1; + ++stepIndexToSearchFrom; + } + + return false; + } + + void markBufferAsContaining (int bufferNum, int nodeId, int outputIndex) + { + if (outputIndex == AudioProcessorGraph::midiChannelIndex) + { + jassert (bufferNum > 0 && bufferNum < midiNodeIds.size()); + + midiNodeIds.set (bufferNum, nodeId); + } + else + { + jassert (bufferNum > 0 && bufferNum < nodeIds.size()); + + nodeIds.set (bufferNum, nodeId); + channels.set (bufferNum, outputIndex); + } + } + + RenderingOpSequenceCalculator (const RenderingOpSequenceCalculator&); + const RenderingOpSequenceCalculator& operator= (const RenderingOpSequenceCalculator&); +}; + +} + +//============================================================================== +void AudioProcessorGraph::clearRenderingSequence() +{ + const ScopedLock sl (renderLock); + + for (int i = renderingOps.size(); --i >= 0;) + { + GraphRenderingOps::AudioGraphRenderingOp* const r + = (GraphRenderingOps::AudioGraphRenderingOp*) renderingOps.getUnchecked(i); + + renderingOps.remove (i); + delete r; + } +} + +bool AudioProcessorGraph::isAnInputTo (const uint32 possibleInputId, + const uint32 possibleDestinationId, + const int recursionCheck) const throw() +{ + if (recursionCheck > 0) + { + for (int i = connections.size(); --i >= 0;) + { + const AudioProcessorGraph::Connection* const c = connections.getUnchecked (i); + + if (c->destNodeId == possibleDestinationId + && (c->sourceNodeId == possibleInputId + || isAnInputTo (possibleInputId, c->sourceNodeId, recursionCheck - 1))) + return true; + } + } + + return false; +} + +void AudioProcessorGraph::buildRenderingSequence() +{ + VoidArray newRenderingOps; + int numRenderingBuffersNeeded = 2; + int numMidiBuffersNeeded = 1; + + { + MessageManagerLock mml; + + VoidArray orderedNodes; + + int i; + for (i = 0; i < nodes.size(); ++i) + { + Node* const node = nodes.getUnchecked(i); + + node->prepare (getSampleRate(), getBlockSize(), this); + + int j = 0; + for (; j < orderedNodes.size(); ++j) + if (isAnInputTo (node->id, + ((Node*) orderedNodes.getUnchecked (j))->id, + nodes.size() + 1)) + break; + + orderedNodes.insert (j, node); + } + + GraphRenderingOps::RenderingOpSequenceCalculator calculator (*this, orderedNodes, newRenderingOps); + + numRenderingBuffersNeeded = calculator.getNumBuffersNeeded(); + numMidiBuffersNeeded = calculator.getNumMidiBuffersNeeded(); + } + + VoidArray oldRenderingOps (renderingOps); + + { + // swap over to the new set of rendering sequence.. + const ScopedLock sl (renderLock); + + renderingBuffers.setSize (numRenderingBuffersNeeded, getBlockSize()); + renderingBuffers.clear(); + + for (int i = midiBuffers.size(); --i >= 0;) + midiBuffers.getUnchecked(i)->clear(); + + while (midiBuffers.size() < numMidiBuffersNeeded) + midiBuffers.add (new MidiBuffer()); + + renderingOps = newRenderingOps; + } + + for (int i = oldRenderingOps.size(); --i >= 0;) + delete (GraphRenderingOps::AudioGraphRenderingOp*) oldRenderingOps.getUnchecked(i); +} + +void AudioProcessorGraph::handleAsyncUpdate() +{ + buildRenderingSequence(); +} + +//============================================================================== +void AudioProcessorGraph::prepareToPlay (double /*sampleRate*/, int estimatedSamplesPerBlock) +{ + currentAudioInputBuffer = 0; + currentAudioOutputBuffer.setSize (getNumOutputChannels(), estimatedSamplesPerBlock); + currentMidiInputBuffer = 0; + currentMidiOutputBuffer.clear(); + + clearRenderingSequence(); + buildRenderingSequence(); +} + +void AudioProcessorGraph::releaseResources() +{ + for (int i = 0; i < nodes.size(); ++i) + nodes.getUnchecked(i)->unprepare(); + + renderingBuffers.setSize (1, 1); + midiBuffers.clear(); + + currentAudioInputBuffer = 0; + currentAudioOutputBuffer.setSize (1, 1); + currentMidiInputBuffer = 0; + currentMidiOutputBuffer.clear(); +} + +void AudioProcessorGraph::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) +{ + const int numSamples = buffer.getNumSamples(); + + const ScopedLock sl (renderLock); + + currentAudioInputBuffer = &buffer; + currentAudioOutputBuffer.setSize (buffer.getNumChannels(), numSamples); + currentAudioOutputBuffer.clear(); + currentMidiInputBuffer = &midiMessages; + currentMidiOutputBuffer.clear(); + + for (int i = 0; i < renderingOps.size(); ++i) + { + GraphRenderingOps::AudioGraphRenderingOp* const op + = (GraphRenderingOps::AudioGraphRenderingOp*) renderingOps.getUnchecked(i); + + op->perform (renderingBuffers, midiBuffers, numSamples); + } + + for (int i = 0; i < buffer.getNumChannels(); ++i) + buffer.copyFrom (i, 0, currentAudioOutputBuffer, i, 0, numSamples); +} + +const String AudioProcessorGraph::getInputChannelName (const int channelIndex) const +{ + return "Input " + String (channelIndex + 1); +} + +const String AudioProcessorGraph::getOutputChannelName (const int channelIndex) const +{ + return "Output " + String (channelIndex + 1); +} + +bool AudioProcessorGraph::isInputChannelStereoPair (int /*index*/) const +{ + return true; +} + +bool AudioProcessorGraph::isOutputChannelStereoPair (int /*index*/) const +{ + return true; +} + +bool AudioProcessorGraph::acceptsMidi() const +{ + return true; +} + +bool AudioProcessorGraph::producesMidi() const +{ + return true; +} + +void AudioProcessorGraph::getStateInformation (JUCE_NAMESPACE::MemoryBlock& /*destData*/) +{ +} + +void AudioProcessorGraph::setStateInformation (const void* /*data*/, int /*sizeInBytes*/) +{ +} + + +//============================================================================== +AudioProcessorGraph::AudioGraphIOProcessor::AudioGraphIOProcessor (const IODeviceType type_) + : type (type_), + graph (0) +{ +} + +AudioProcessorGraph::AudioGraphIOProcessor::~AudioGraphIOProcessor() +{ +} + +const String AudioProcessorGraph::AudioGraphIOProcessor::getName() const +{ + switch (type) + { + case audioOutputNode: + return "Audio Output"; + case audioInputNode: + return "Audio Input"; + case midiOutputNode: + return "Midi Output"; + case midiInputNode: + return "Midi Input"; + default: + break; + } + + return String::empty; +} + +void AudioProcessorGraph::AudioGraphIOProcessor::fillInPluginDescription (PluginDescription& d) const +{ + d.name = getName(); + d.uid = d.name.hashCode(); + d.category = "I/O devices"; + d.pluginFormatName = "Internal"; + d.manufacturerName = "Raw Material Software"; + d.version = "1.0"; + d.isInstrument = false; + + d.numInputChannels = getNumInputChannels(); + if (type == audioOutputNode && graph != 0) + d.numInputChannels = graph->getNumInputChannels(); + + d.numOutputChannels = getNumOutputChannels(); + if (type == audioInputNode && graph != 0) + d.numOutputChannels = graph->getNumOutputChannels(); +} + +void AudioProcessorGraph::AudioGraphIOProcessor::prepareToPlay (double, int) +{ + jassert (graph != 0); +} + +void AudioProcessorGraph::AudioGraphIOProcessor::releaseResources() +{ +} + +void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages) +{ + jassert (graph != 0); + + switch (type) + { + case audioOutputNode: + { + for (int i = jmin (graph->currentAudioOutputBuffer.getNumChannels(), + buffer.getNumChannels()); --i >= 0;) + { + graph->currentAudioOutputBuffer.addFrom (i, 0, buffer, i, 0, buffer.getNumSamples()); + } + + break; + } + + case audioInputNode: + { + for (int i = jmin (graph->currentAudioInputBuffer->getNumChannels(), + buffer.getNumChannels()); --i >= 0;) + { + buffer.addFrom (i, 0, *graph->currentAudioInputBuffer, i, 0, buffer.getNumSamples()); + } + + break; + } + + case midiOutputNode: + graph->currentMidiOutputBuffer.addEvents (midiMessages, 0, buffer.getNumSamples(), 0); + break; + + case midiInputNode: + midiMessages.addEvents (*graph->currentMidiInputBuffer, 0, buffer.getNumSamples(), 0); + break; + + default: + break; + } +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::acceptsMidi() const +{ + return type == midiOutputNode; +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::producesMidi() const +{ + return type == midiInputNode; +} + +const String AudioProcessorGraph::AudioGraphIOProcessor::getInputChannelName (const int channelIndex) const +{ + switch (type) + { + case audioOutputNode: + return "Output " + String (channelIndex + 1); + case midiOutputNode: + return "Midi Output"; + default: + break; + } + + return String::empty; +} + +const String AudioProcessorGraph::AudioGraphIOProcessor::getOutputChannelName (const int channelIndex) const +{ + switch (type) + { + case audioInputNode: + return "Input " + String (channelIndex + 1); + case midiInputNode: + return "Midi Input"; + default: + break; + } + + return String::empty; +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::isInputChannelStereoPair (int /*index*/) const +{ + return type == audioInputNode || type == audioOutputNode; +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::isOutputChannelStereoPair (int index) const +{ + return isInputChannelStereoPair (index); +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::isInput() const throw() +{ + return type == audioInputNode || type == midiInputNode; +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::isOutput() const throw() +{ + return type == audioOutputNode || type == midiOutputNode; +} + +AudioProcessorEditor* AudioProcessorGraph::AudioGraphIOProcessor::createEditor() +{ + return 0; +} + +int AudioProcessorGraph::AudioGraphIOProcessor::getNumParameters() { return 0; } +const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterName (int) { return String::empty; } + +float AudioProcessorGraph::AudioGraphIOProcessor::getParameter (int) { return 0.0f; } +const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterText (int) { return String::empty; } +void AudioProcessorGraph::AudioGraphIOProcessor::setParameter (int, float) { } + +int AudioProcessorGraph::AudioGraphIOProcessor::getNumPrograms() { return 0; } +int AudioProcessorGraph::AudioGraphIOProcessor::getCurrentProgram() { return 0; } +void AudioProcessorGraph::AudioGraphIOProcessor::setCurrentProgram (int) { } + +const String AudioProcessorGraph::AudioGraphIOProcessor::getProgramName (int) { return String::empty; } +void AudioProcessorGraph::AudioGraphIOProcessor::changeProgramName (int, const String&) { } + +void AudioProcessorGraph::AudioGraphIOProcessor::getStateInformation (JUCE_NAMESPACE::MemoryBlock&) +{ +} + +void AudioProcessorGraph::AudioGraphIOProcessor::setStateInformation (const void*, int) +{ +} + +void AudioProcessorGraph::AudioGraphIOProcessor::setParentGraph (AudioProcessorGraph* const newGraph) throw() +{ + graph = newGraph; + + if (graph != 0) + { + setPlayConfigDetails (type == audioOutputNode ? graph->getNumInputChannels() : 0, + type == audioInputNode ? graph->getNumOutputChannels() : 0, + getSampleRate(), + getBlockSize()); + + updateHostDisplay(); + } +} + + +END_JUCE_NAMESPACE diff --git a/src/juce_appframework/audio/processors/juce_AudioProcessorGraph.h b/src/juce_appframework/audio/processors/juce_AudioProcessorGraph.h index 71f01aad5d..88f4298ec4 100644 --- a/src/juce_appframework/audio/processors/juce_AudioProcessorGraph.h +++ b/src/juce_appframework/audio/processors/juce_AudioProcessorGraph.h @@ -1,440 +1,442 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - JUCE is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -#ifndef __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ -#define __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ - -#include "juce_AudioProcessor.h" -#include "../plugins/juce_AudioPluginFormatManager.h" -#include "../plugins/juce_KnownPluginList.h" -#include "../../../juce_core/containers/juce_ReferenceCountedArray.h" - - -//============================================================================== -/** - A type of AudioProcessor which plays back a graph of other AudioProcessors. - - Use one of these objects if you want to wire-up a set of AudioProcessors - and play back the result. - - Processors can be added to the graph as "nodes" using addNode(), and once - added, you can connect any of their input or output channels to other - nodes using addConnection(). - - To play back a graph through an audio device, you might want to use an - AudioProcessorPlayer object. -*/ -class JUCE_API AudioProcessorGraph : public AudioProcessor, - public AsyncUpdater -{ -public: - //============================================================================== - /** Creates an empty graph. - */ - AudioProcessorGraph(); - - /** Destructor. - - Any processor objects that have been added to the graph will also be deleted. - */ - ~AudioProcessorGraph(); - - //============================================================================== - /** Represents one of the nodes, or processors, in an AudioProcessorGraph. - - To create a node, call AudioProcessorGraph::addNode(). - */ - class Node : public ReferenceCountedObject - { - public: - /** Destructor. - */ - ~Node(); - - //============================================================================== - /** The ID number assigned to this node. - - This is assigned by the graph that owns it, and can't be changed. - */ - const uint32 id; - - /** The actual processor object that this node represents. - */ - AudioProcessor* const processor; - - /** A set of user-definable properties that are associated with this node. - - This can be used to attach values to the node for whatever purpose seems - useful. For example, you might store an x and y position if your application - is displaying the nodes on-screen. - */ - PropertySet properties; - - //============================================================================== - /** A convenient typedef for referring to a pointer to a node object. - */ - typedef ReferenceCountedObjectPtr Ptr; - - //============================================================================== - juce_UseDebuggingNewOperator - - private: - friend class AudioProcessorGraph; - - bool isPrepared; - - Node (const uint32 id, AudioProcessor* const processor) throw(); - - void prepare (const double sampleRate, const int blockSize, AudioProcessorGraph* const graph); - void unprepare(); - - Node (const Node&); - const Node& operator= (const Node&); - }; - - //============================================================================== - /** Represents a connection between two channels of two nodes in an AudioProcessorGraph. - - To create a connection, use AudioProcessorGraph::addConnection(). - */ - struct Connection - { - public: - //============================================================================== - /** The ID number of the node which is the input source for this connection. - @see AudioProcessorGraph::getNodeForId - */ - uint32 sourceNodeId; - - /** The index of the output channel of the source node from which this - connection takes its data. - - If this value is the special number AudioProcessorGraph::midiChannelIndex, then - it is referring to the source node's midi output. Otherwise, it is the zero-based - index of an audio output channel in the source node. - */ - int sourceChannelIndex; - - /** The ID number of the node which is the destination for this connection. - @see AudioProcessorGraph::getNodeForId - */ - uint32 destNodeId; - - /** The index of the input channel of the destination node to which this - connection delivers its data. - - If this value is the special number AudioProcessorGraph::midiChannelIndex, then - it is referring to the destination node's midi input. Otherwise, it is the zero-based - index of an audio input channel in the destination node. - */ - int destChannelIndex; - - //============================================================================== - juce_UseDebuggingNewOperator - - private: - }; - - //============================================================================== - /** Deletes all nodes and connections from this graph. - - Any processor objects in the graph will be deleted. - */ - void clear(); - - /** Returns the number of nodes in the graph. */ - int getNumNodes() const throw() { return nodes.size(); } - - /** Returns a pointer to one of the nodes in the graph. - - This will return 0 if the index is out of range. - @see getNodeForId - */ - Node* getNode (const int index) const throw() { return nodes [index]; } - - /** Searches the graph for a node with the given ID number and returns it. - - If no such node was found, this returns 0. - @see getNode - */ - Node* getNodeForId (const uint32 nodeId) const throw(); - - /** Adds a node to the graph. - - This creates a new node in the graph, for the specified processor. Once you have - added a processor to the graph, the graph owns it and will delete it later when - it is no longer needed. - - The optional nodeId parameter lets you specify an ID to use for the node, but - if the value is already in use, this new node will overwrite the old one. - - If this succeeds, it returns a pointer to the newly-created node. - */ - Node* addNode (AudioProcessor* const newProcessor, - uint32 nodeId = 0); - - /** Deletes a node within the graph which has the specified ID. - - This will also delete any connections that are attached to this node. - */ - bool removeNode (const uint32 nodeId); - - //============================================================================== - /** Returns the number of connections in the graph. */ - int getNumConnections() const throw() { return connections.size(); } - - /** Returns a pointer to one of the connections in the graph. */ - const Connection* getConnection (const int index) const throw() { return connections [index]; } - - /** Searches for a connection between some specified channels. - - If no such connection is found, this returns 0. - */ - const Connection* getConnectionBetween (const uint32 sourceNodeId, - const int sourceChannelIndex, - const uint32 destNodeId, - const int destChannelIndex) const throw(); - - /** Returns true if there is a connection between any of the channels of - two specified nodes. - */ - bool isConnected (const uint32 possibleSourceNodeId, - const uint32 possibleDestNodeId) const throw(); - - /** Returns true if it would be legal to connect the specified points. - */ - bool canConnect (const uint32 sourceNodeId, const int sourceChannelIndex, - const uint32 destNodeId, const int destChannelIndex) const throw(); - - /** Attempts to connect two specified channels of two nodes. - - If this isn't allowed (e.g. because you're trying to connect a midi channel - to an audio one or other such nonsense), then it'll return 0. - */ - bool addConnection (const uint32 sourceNodeId, const int sourceChannelIndex, - const uint32 destNodeId, const int destChannelIndex); - - /** Deletes the connection with the specified index. - - Returns true if a connection was actually deleted. - */ - void removeConnection (const int index); - - /** Deletes any connection between two specified points. - - Returns true if a connection was actually deleted. - */ - bool removeConnection (const uint32 sourceNodeId, const int sourceChannelIndex, - const uint32 destNodeId, const int destChannelIndex); - - /** Removes all connections from the specified node. - */ - bool disconnectNode (const uint32 nodeId); - - /** Performs a sanity checks of all the connections. - - This might be useful if some of the processors are doing things like changing - their channel counts, which could render some connections obsolete. - */ - bool removeIllegalConnections(); - - //============================================================================== - /** A special number that represents the midi channel of a node. - - This is used as a channel index value if you want to refer to the midi input - or output instead of an audio channel. - */ - static const int midiChannelIndex; - - - //============================================================================== - /** A special type of AudioProcessor that can live inside an AudioProcessorGraph - in order to use the audio that comes into and out of the graph itself. - - If you create an AudioGraphIOProcessor in "input" mode, it will act as a - node in the graph which delivers the audio that is coming into the parent - graph. This allows you to stream the data to other nodes and process the - incoming audio. - - Likewise, one of these in "output" mode can be sent data which it will add to - the sum of data being sent to the graph's output. - - @see AudioProcessorGraph - */ - class AudioGraphIOProcessor : public AudioPluginInstance - { - public: - /** Specifies the mode in which this processor will operate. - */ - enum IODeviceType - { - audioInputNode, /**< In this mode, the processor has output channels - representing all the audio input channels that are - coming into its parent audio graph. */ - audioOutputNode, /**< In this mode, the processor has input channels - representing all the audio output channels that are - going out of its parent audio graph. */ - midiInputNode, /**< In this mode, the processor has a midi output which - delivers the same midi data that is arriving at its - parent graph. */ - midiOutputNode /**< In this mode, the processor has a midi input and - any data sent to it will be passed out of the parent - graph. */ - }; - - //============================================================================== - /** Returns the mode of this processor. */ - IODeviceType getType() const throw() { return type; } - - /** Returns the parent graph to which this processor belongs, or 0 if it - hasn't yet been added to one. */ - AudioProcessorGraph* getParentGraph() const throw() { return graph; } - - /** True if this is an audio or midi input. */ - bool isInput() const throw(); - /** True if this is an audio or midi output. */ - bool isOutput() const throw(); - - //============================================================================== - AudioGraphIOProcessor (const IODeviceType type); - ~AudioGraphIOProcessor(); - - const String getName() const; - void fillInPluginDescription (PluginDescription& d) const; - - void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); - void releaseResources(); - void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); - - const String getInputChannelName (const int channelIndex) const; - const String getOutputChannelName (const int channelIndex) const; - bool isInputChannelStereoPair (int index) const; - bool isOutputChannelStereoPair (int index) const; - bool acceptsMidi() const; - bool producesMidi() const; - - AudioProcessorEditor* createEditor(); - - int getNumParameters(); - const String getParameterName (int); - float getParameter (int); - const String getParameterText (int); - void setParameter (int, float); - - int getNumPrograms(); - int getCurrentProgram(); - void setCurrentProgram (int); - const String getProgramName (int); - void changeProgramName (int, const String&); - - void getStateInformation (JUCE_NAMESPACE::MemoryBlock& destData); - void setStateInformation (const void* data, int sizeInBytes); - - /** @internal */ - void setParentGraph (AudioProcessorGraph* const graph) throw(); - - juce_UseDebuggingNewOperator - - private: - const IODeviceType type; - AudioProcessorGraph* graph; - - AudioGraphIOProcessor (const AudioGraphIOProcessor&); - const AudioGraphIOProcessor& operator= (const AudioGraphIOProcessor&); - }; - - //============================================================================== - // AudioProcessor methods: - - const String getName() const; - - void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); - void releaseResources(); - void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); - - const String getInputChannelName (const int channelIndex) const; - const String getOutputChannelName (const int channelIndex) const; - bool isInputChannelStereoPair (int index) const; - bool isOutputChannelStereoPair (int index) const; - - bool acceptsMidi() const; - bool producesMidi() const; - - AudioProcessorEditor* createEditor() { return 0; } - - int getNumParameters() { return 0; } - const String getParameterName (int) { return String::empty; } - float getParameter (int) { return 0; } - const String getParameterText (int) { return String::empty; } - void setParameter (int, float) { } - - int getNumPrograms() { return 0; } - int getCurrentProgram() { return 0; } - void setCurrentProgram (int) { } - const String getProgramName (int) { return String::empty; } - void changeProgramName (int, const String&) { } - - void getStateInformation (JUCE_NAMESPACE::MemoryBlock& destData); - void setStateInformation (const void* data, int sizeInBytes); - - /** @internal */ - void handleAsyncUpdate(); - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - ReferenceCountedArray nodes; - OwnedArray connections; - int lastNodeId; - AudioSampleBuffer renderingBuffers; - OwnedArray midiBuffers; - - CriticalSection renderLock; - VoidArray renderingOps; - - friend class AudioGraphIOProcessor; - AudioSampleBuffer* currentAudioIOBuffer; - MidiBuffer* currentMidiIOBuffer; - - void clearRenderingSequence(); - void buildRenderingSequence(); - - bool isAnInputTo (const uint32 possibleInputId, - const uint32 possibleDestinationId, - const int recursionCheck) const throw(); - - AudioProcessorGraph (const AudioProcessorGraph&); - const AudioProcessorGraph& operator= (const AudioProcessorGraph&); -}; - - -#endif // __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ +#define __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ + +#include "juce_AudioProcessor.h" +#include "../plugins/juce_AudioPluginFormatManager.h" +#include "../plugins/juce_KnownPluginList.h" +#include "../../../juce_core/containers/juce_ReferenceCountedArray.h" + + +//============================================================================== +/** + A type of AudioProcessor which plays back a graph of other AudioProcessors. + + Use one of these objects if you want to wire-up a set of AudioProcessors + and play back the result. + + Processors can be added to the graph as "nodes" using addNode(), and once + added, you can connect any of their input or output channels to other + nodes using addConnection(). + + To play back a graph through an audio device, you might want to use an + AudioProcessorPlayer object. +*/ +class JUCE_API AudioProcessorGraph : public AudioProcessor, + public AsyncUpdater +{ +public: + //============================================================================== + /** Creates an empty graph. + */ + AudioProcessorGraph(); + + /** Destructor. + + Any processor objects that have been added to the graph will also be deleted. + */ + ~AudioProcessorGraph(); + + //============================================================================== + /** Represents one of the nodes, or processors, in an AudioProcessorGraph. + + To create a node, call AudioProcessorGraph::addNode(). + */ + class Node : public ReferenceCountedObject + { + public: + /** Destructor. + */ + ~Node(); + + //============================================================================== + /** The ID number assigned to this node. + + This is assigned by the graph that owns it, and can't be changed. + */ + const uint32 id; + + /** The actual processor object that this node represents. + */ + AudioProcessor* const processor; + + /** A set of user-definable properties that are associated with this node. + + This can be used to attach values to the node for whatever purpose seems + useful. For example, you might store an x and y position if your application + is displaying the nodes on-screen. + */ + PropertySet properties; + + //============================================================================== + /** A convenient typedef for referring to a pointer to a node object. + */ + typedef ReferenceCountedObjectPtr Ptr; + + //============================================================================== + juce_UseDebuggingNewOperator + + private: + friend class AudioProcessorGraph; + + bool isPrepared; + + Node (const uint32 id, AudioProcessor* const processor) throw(); + + void prepare (const double sampleRate, const int blockSize, AudioProcessorGraph* const graph); + void unprepare(); + + Node (const Node&); + const Node& operator= (const Node&); + }; + + //============================================================================== + /** Represents a connection between two channels of two nodes in an AudioProcessorGraph. + + To create a connection, use AudioProcessorGraph::addConnection(). + */ + struct Connection + { + public: + //============================================================================== + /** The ID number of the node which is the input source for this connection. + @see AudioProcessorGraph::getNodeForId + */ + uint32 sourceNodeId; + + /** The index of the output channel of the source node from which this + connection takes its data. + + If this value is the special number AudioProcessorGraph::midiChannelIndex, then + it is referring to the source node's midi output. Otherwise, it is the zero-based + index of an audio output channel in the source node. + */ + int sourceChannelIndex; + + /** The ID number of the node which is the destination for this connection. + @see AudioProcessorGraph::getNodeForId + */ + uint32 destNodeId; + + /** The index of the input channel of the destination node to which this + connection delivers its data. + + If this value is the special number AudioProcessorGraph::midiChannelIndex, then + it is referring to the destination node's midi input. Otherwise, it is the zero-based + index of an audio input channel in the destination node. + */ + int destChannelIndex; + + //============================================================================== + juce_UseDebuggingNewOperator + + private: + }; + + //============================================================================== + /** Deletes all nodes and connections from this graph. + + Any processor objects in the graph will be deleted. + */ + void clear(); + + /** Returns the number of nodes in the graph. */ + int getNumNodes() const throw() { return nodes.size(); } + + /** Returns a pointer to one of the nodes in the graph. + + This will return 0 if the index is out of range. + @see getNodeForId + */ + Node* getNode (const int index) const throw() { return nodes [index]; } + + /** Searches the graph for a node with the given ID number and returns it. + + If no such node was found, this returns 0. + @see getNode + */ + Node* getNodeForId (const uint32 nodeId) const throw(); + + /** Adds a node to the graph. + + This creates a new node in the graph, for the specified processor. Once you have + added a processor to the graph, the graph owns it and will delete it later when + it is no longer needed. + + The optional nodeId parameter lets you specify an ID to use for the node, but + if the value is already in use, this new node will overwrite the old one. + + If this succeeds, it returns a pointer to the newly-created node. + */ + Node* addNode (AudioProcessor* const newProcessor, + uint32 nodeId = 0); + + /** Deletes a node within the graph which has the specified ID. + + This will also delete any connections that are attached to this node. + */ + bool removeNode (const uint32 nodeId); + + //============================================================================== + /** Returns the number of connections in the graph. */ + int getNumConnections() const throw() { return connections.size(); } + + /** Returns a pointer to one of the connections in the graph. */ + const Connection* getConnection (const int index) const throw() { return connections [index]; } + + /** Searches for a connection between some specified channels. + + If no such connection is found, this returns 0. + */ + const Connection* getConnectionBetween (const uint32 sourceNodeId, + const int sourceChannelIndex, + const uint32 destNodeId, + const int destChannelIndex) const throw(); + + /** Returns true if there is a connection between any of the channels of + two specified nodes. + */ + bool isConnected (const uint32 possibleSourceNodeId, + const uint32 possibleDestNodeId) const throw(); + + /** Returns true if it would be legal to connect the specified points. + */ + bool canConnect (const uint32 sourceNodeId, const int sourceChannelIndex, + const uint32 destNodeId, const int destChannelIndex) const throw(); + + /** Attempts to connect two specified channels of two nodes. + + If this isn't allowed (e.g. because you're trying to connect a midi channel + to an audio one or other such nonsense), then it'll return false. + */ + bool addConnection (const uint32 sourceNodeId, const int sourceChannelIndex, + const uint32 destNodeId, const int destChannelIndex); + + /** Deletes the connection with the specified index. + + Returns true if a connection was actually deleted. + */ + void removeConnection (const int index); + + /** Deletes any connection between two specified points. + + Returns true if a connection was actually deleted. + */ + bool removeConnection (const uint32 sourceNodeId, const int sourceChannelIndex, + const uint32 destNodeId, const int destChannelIndex); + + /** Removes all connections from the specified node. + */ + bool disconnectNode (const uint32 nodeId); + + /** Performs a sanity checks of all the connections. + + This might be useful if some of the processors are doing things like changing + their channel counts, which could render some connections obsolete. + */ + bool removeIllegalConnections(); + + //============================================================================== + /** A special number that represents the midi channel of a node. + + This is used as a channel index value if you want to refer to the midi input + or output instead of an audio channel. + */ + static const int midiChannelIndex; + + + //============================================================================== + /** A special type of AudioProcessor that can live inside an AudioProcessorGraph + in order to use the audio that comes into and out of the graph itself. + + If you create an AudioGraphIOProcessor in "input" mode, it will act as a + node in the graph which delivers the audio that is coming into the parent + graph. This allows you to stream the data to other nodes and process the + incoming audio. + + Likewise, one of these in "output" mode can be sent data which it will add to + the sum of data being sent to the graph's output. + + @see AudioProcessorGraph + */ + class AudioGraphIOProcessor : public AudioPluginInstance + { + public: + /** Specifies the mode in which this processor will operate. + */ + enum IODeviceType + { + audioInputNode, /**< In this mode, the processor has output channels + representing all the audio input channels that are + coming into its parent audio graph. */ + audioOutputNode, /**< In this mode, the processor has input channels + representing all the audio output channels that are + going out of its parent audio graph. */ + midiInputNode, /**< In this mode, the processor has a midi output which + delivers the same midi data that is arriving at its + parent graph. */ + midiOutputNode /**< In this mode, the processor has a midi input and + any data sent to it will be passed out of the parent + graph. */ + }; + + //============================================================================== + /** Returns the mode of this processor. */ + IODeviceType getType() const throw() { return type; } + + /** Returns the parent graph to which this processor belongs, or 0 if it + hasn't yet been added to one. */ + AudioProcessorGraph* getParentGraph() const throw() { return graph; } + + /** True if this is an audio or midi input. */ + bool isInput() const throw(); + /** True if this is an audio or midi output. */ + bool isOutput() const throw(); + + //============================================================================== + AudioGraphIOProcessor (const IODeviceType type); + ~AudioGraphIOProcessor(); + + const String getName() const; + void fillInPluginDescription (PluginDescription& d) const; + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); + + const String getInputChannelName (const int channelIndex) const; + const String getOutputChannelName (const int channelIndex) const; + bool isInputChannelStereoPair (int index) const; + bool isOutputChannelStereoPair (int index) const; + bool acceptsMidi() const; + bool producesMidi() const; + + AudioProcessorEditor* createEditor(); + + int getNumParameters(); + const String getParameterName (int); + float getParameter (int); + const String getParameterText (int); + void setParameter (int, float); + + int getNumPrograms(); + int getCurrentProgram(); + void setCurrentProgram (int); + const String getProgramName (int); + void changeProgramName (int, const String&); + + void getStateInformation (JUCE_NAMESPACE::MemoryBlock& destData); + void setStateInformation (const void* data, int sizeInBytes); + + /** @internal */ + void setParentGraph (AudioProcessorGraph* const graph) throw(); + + juce_UseDebuggingNewOperator + + private: + const IODeviceType type; + AudioProcessorGraph* graph; + + AudioGraphIOProcessor (const AudioGraphIOProcessor&); + const AudioGraphIOProcessor& operator= (const AudioGraphIOProcessor&); + }; + + //============================================================================== + // AudioProcessor methods: + + const String getName() const; + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); + + const String getInputChannelName (const int channelIndex) const; + const String getOutputChannelName (const int channelIndex) const; + bool isInputChannelStereoPair (int index) const; + bool isOutputChannelStereoPair (int index) const; + + bool acceptsMidi() const; + bool producesMidi() const; + + AudioProcessorEditor* createEditor() { return 0; } + + int getNumParameters() { return 0; } + const String getParameterName (int) { return String::empty; } + float getParameter (int) { return 0; } + const String getParameterText (int) { return String::empty; } + void setParameter (int, float) { } + + int getNumPrograms() { return 0; } + int getCurrentProgram() { return 0; } + void setCurrentProgram (int) { } + const String getProgramName (int) { return String::empty; } + void changeProgramName (int, const String&) { } + + void getStateInformation (JUCE_NAMESPACE::MemoryBlock& destData); + void setStateInformation (const void* data, int sizeInBytes); + + /** @internal */ + void handleAsyncUpdate(); + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + ReferenceCountedArray nodes; + OwnedArray connections; + int lastNodeId; + AudioSampleBuffer renderingBuffers; + OwnedArray midiBuffers; + + CriticalSection renderLock; + VoidArray renderingOps; + + friend class AudioGraphIOProcessor; + AudioSampleBuffer* currentAudioInputBuffer; + AudioSampleBuffer currentAudioOutputBuffer; + MidiBuffer* currentMidiInputBuffer; + MidiBuffer currentMidiOutputBuffer; + + void clearRenderingSequence(); + void buildRenderingSequence(); + + bool isAnInputTo (const uint32 possibleInputId, + const uint32 possibleDestinationId, + const int recursionCheck) const throw(); + + AudioProcessorGraph (const AudioProcessorGraph&); + const AudioProcessorGraph& operator= (const AudioProcessorGraph&); +}; + + +#endif // __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__