| @@ -399,6 +399,8 @@ public: | |||
| error = String::empty; | |||
| sampleRate = sampleRate_; | |||
| bufferSize = bufferSize_; | |||
| currentInputChans.clear(); | |||
| currentOutputChans.clear(); | |||
| numChannelsRunning = jmax (inputChannels.getHighestBit(), | |||
| outputChannels.getHighestBit()) + 1; | |||
| @@ -414,7 +416,10 @@ public: | |||
| inputChannelData [i] = (float*) juce_calloc (sizeof (float) * bufferSize); | |||
| if (inputChannels[i]) | |||
| { | |||
| inputChannelDataForCallback [totalNumInputChannels++] = inputChannelData [i]; | |||
| currentInputChans.setBit (i); | |||
| } | |||
| } | |||
| } | |||
| @@ -425,7 +430,10 @@ public: | |||
| outputChannelData [i] = (float*) juce_calloc (sizeof (float) * bufferSize); | |||
| if (outputChannels[i]) | |||
| { | |||
| outputChannelDataForCallback [totalNumOutputChannels++] = outputChannelData [i]; | |||
| currentOutputChans.setBit (i); | |||
| } | |||
| } | |||
| } | |||
| @@ -603,6 +611,7 @@ public: | |||
| String error; | |||
| double sampleRate; | |||
| int bufferSize; | |||
| BitArray currentInputChans, currentOutputChans; | |||
| Array <int> sampleRates; | |||
| StringArray channelNamesOut, channelNamesIn; | |||
| @@ -777,6 +786,16 @@ public: | |||
| return internal->getBitDepth(); | |||
| } | |||
| const BitArray getActiveOutputChannels() const | |||
| { | |||
| return internal->activeOutputChans; | |||
| } | |||
| const BitArray getActiveInputChannels() const | |||
| { | |||
| return internal->activeInputChans; | |||
| } | |||
| int getOutputLatencyInSamples() | |||
| { | |||
| return 0; | |||
| @@ -311,8 +311,8 @@ public: | |||
| int sampleRate = roundDoubleToInt (sr); | |||
| currentSampleRate = sampleRate; | |||
| currentBlockSizeSamples = bufferSizeSamples; | |||
| currentChansOut = outputChannels; | |||
| currentChansIn = inputChannels; | |||
| currentChansOut.clear(); | |||
| currentChansIn.clear(); | |||
| updateSampleRates(); | |||
| @@ -432,6 +432,7 @@ public: | |||
| { | |||
| if (inputChannels[i]) | |||
| { | |||
| currentChansIn.setBit (i); | |||
| info->isInput = 1; | |||
| info->channelNum = i; | |||
| info->buffers[0] = info->buffers[1] = 0; | |||
| @@ -444,6 +445,7 @@ public: | |||
| { | |||
| if (outputChannels[i]) | |||
| { | |||
| currentChansOut.setBit (i); | |||
| info->isInput = 0; | |||
| info->channelNum = i; | |||
| info->buffers[0] = info->buffers[1] = 0; | |||
| @@ -732,6 +734,16 @@ public: | |||
| return currentSampleRate; | |||
| } | |||
| const BitArray getActiveOutputChannels() const | |||
| { | |||
| return currentChansOut; | |||
| } | |||
| const BitArray getActiveInputChannels() const | |||
| { | |||
| return currentChansIn; | |||
| } | |||
| int getCurrentBitDepth() | |||
| { | |||
| return currentBitDepth; | |||
| @@ -1131,6 +1131,16 @@ public: | |||
| return bits; | |||
| } | |||
| const BitArray getActiveOutputChannels() const | |||
| { | |||
| return enabledOutputs; | |||
| } | |||
| const BitArray getActiveInputChannels() const | |||
| { | |||
| return enabledInputs; | |||
| } | |||
| int getOutputLatencyInSamples() | |||
| { | |||
| return (int) (getCurrentBufferSizeSamples() * 1.5); | |||
| @@ -1205,6 +1215,7 @@ private: | |||
| int volatile totalSamplesOut; | |||
| int64 volatile lastBlockTime; | |||
| double sampleRate; | |||
| BitArray enabledInputs, enabledOutputs; | |||
| float** inputBuffers; | |||
| float** outputBuffers; | |||
| @@ -1629,6 +1640,8 @@ const String DSoundAudioIODevice::openDevice (const BitArray& inputChannels, | |||
| { | |||
| closeDevice(); | |||
| totalSamplesOut = 0; | |||
| enabledInputs.clear(); | |||
| enabledOutputs.clear(); | |||
| sampleRate = sampleRate_; | |||
| @@ -1649,16 +1662,28 @@ const String DSoundAudioIODevice::openDevice (const BitArray& inputChannels, | |||
| int i; | |||
| for (i = 0; i < numInputBuffers + 2; ++i) | |||
| { | |||
| inputBuffers[i] = (inputChannels[i] && i < numInputBuffers) | |||
| ? (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float)) | |||
| : 0; | |||
| if (inputChannels[i] && i < numInputBuffers) | |||
| { | |||
| inputBuffers[i] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float)); | |||
| enabledInputs.setBit (i); | |||
| } | |||
| else | |||
| { | |||
| inputBuffers[i] = 0; | |||
| } | |||
| } | |||
| for (i = 0; i < numOutputBuffers + 2; ++i) | |||
| { | |||
| outputBuffers[i] = (outputChannels[i] && i < numOutputBuffers) | |||
| ? (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float)) | |||
| : 0; | |||
| if (outputChannels[i] && i < numOutputBuffers) | |||
| { | |||
| outputBuffers[i] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float)); | |||
| enabledOutputs.setBit (i); | |||
| } | |||
| else | |||
| { | |||
| outputBuffers[i] = 0; | |||
| } | |||
| } | |||
| for (i = 0; i < numInputBuffers; ++i) | |||
| @@ -7,6 +7,7 @@ | |||
| Changelist for version 1.45 | |||
| - big new project in the "extras" folder - a basic audio plugin host! Currently it loads VSTs on PC/Mac, and lets you put them together in a filter graph, which it plays. Hosting functionality is very basic at the moment, but I'm laying down a good architecture to hopefully build out into cross-platform plugin loading. | |||
| - audio plugins: I've simplified the processBlock() call in AudioFilterBase. It now just takes a single buffer for all input and output channels, and the accumulate parameter has gone. This will mean tweaking your plugin code, but the result will probably make it much less complex and less messy. | |||
| - audio plugins: AudioFilterBase now requires a few extra methods to be implemented by your plugin: getInputChannelName, getOutputChannelName, isInputChannelStereoPair, isOutputChannelStereoPair. | |||
| - new class: FileSearchPathListComponent, for letting the user edit a FileSearchPath. | |||
| @@ -116,7 +116,9 @@ | |||
| 20286C2AFDCF999611CA2CEA /* Sources */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 84FFAE900C6C8A6F009F6E72 /* src */, | |||
| 84FFAE910C6C8A6F009F6E72 /* host */, | |||
| 84FFAE9B0C6C8A6F009F6E72 /* plugins */, | |||
| 84FFAE9A0C6C8A6F009F6E72 /* HostStartup.cpp */, | |||
| ); | |||
| name = Sources; | |||
| sourceTree = "<group>"; | |||
| @@ -145,17 +147,6 @@ | |||
| name = "External Frameworks and Libraries"; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 84FFAE900C6C8A6F009F6E72 /* src */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 84FFAE910C6C8A6F009F6E72 /* host */, | |||
| 84FFAE9A0C6C8A6F009F6E72 /* HostStartup.cpp */, | |||
| 84FFAE9B0C6C8A6F009F6E72 /* plugins */, | |||
| ); | |||
| name = src; | |||
| path = ../../src; | |||
| sourceTree = SOURCE_ROOT; | |||
| }; | |||
| 84FFAE910C6C8A6F009F6E72 /* host */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| @@ -175,6 +166,7 @@ | |||
| 84FFAE9B0C6C8A6F009F6E72 /* plugins */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 84FFAEA80C6C8A6F009F6E72 /* vst */, | |||
| 84FFAE9C0C6C8A6F009F6E72 /* juce_AudioPluginFormat.cpp */, | |||
| 84FFAE9D0C6C8A6F009F6E72 /* juce_AudioPluginFormat.h */, | |||
| 84FFAE9E0C6C8A6F009F6E72 /* juce_AudioPluginInstance.cpp */, | |||
| @@ -187,7 +179,6 @@ | |||
| 84FFAEA50C6C8A6F009F6E72 /* juce_PluginDirectoryScanner.h */, | |||
| 84FFAEA60C6C8A6F009F6E72 /* juce_PluginListComponent.cpp */, | |||
| 84FFAEA70C6C8A6F009F6E72 /* juce_PluginListComponent.h */, | |||
| 84FFAEA80C6C8A6F009F6E72 /* vst */, | |||
| ); | |||
| name = plugins; | |||
| path = ../../src/plugins; | |||
| @@ -300,7 +291,6 @@ | |||
| C0E91AC608A95435008D54AB /* Debug */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| ARCHS = ppc; | |||
| COPY_PHASE_STRIP = NO; | |||
| }; | |||
| name = Debug; | |||
| @@ -308,7 +298,6 @@ | |||
| C0E91AC708A95435008D54AB /* Release */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| ARCHS = ppc; | |||
| DEAD_CODE_STRIPPING = YES; | |||
| INSTALL_PATH = "$(HOME)/Applications"; | |||
| PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES; | |||
| @@ -1,110 +1,110 @@ | |||
| /* | |||
| ============================================================================== | |||
| 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.h" | |||
| #include "host/MainHostWindow.h" | |||
| #include "host/InternalFilters.h" | |||
| ApplicationCommandManager* commandManager = 0; | |||
| //============================================================================== | |||
| class PluginHostApp : public JUCEApplication | |||
| { | |||
| MainHostWindow* mainWindow; | |||
| public: | |||
| //============================================================================== | |||
| PluginHostApp() | |||
| : mainWindow (0) | |||
| { | |||
| } | |||
| ~PluginHostApp() | |||
| { | |||
| } | |||
| void initialise (const String& /*commandLine*/) | |||
| { | |||
| // initialise our settings file.. | |||
| ApplicationProperties::getInstance() | |||
| ->setStorageParameters (T("Juce Audio Plugin Host"), | |||
| T("settings"), String::empty, 1000, | |||
| PropertiesFile::storeAsXML); | |||
| commandManager = new ApplicationCommandManager(); | |||
| AudioPluginFormatManager::getInstance()->addDefaultFormats(); | |||
| AudioPluginFormatManager::getInstance()->addFormat (new InternalPluginFormat()); | |||
| mainWindow = new MainHostWindow(); | |||
| commandManager->registerAllCommandsForTarget (this); | |||
| commandManager->registerAllCommandsForTarget (mainWindow); | |||
| } | |||
| void shutdown() | |||
| { | |||
| deleteAndZero (mainWindow); | |||
| ApplicationProperties::getInstance()->closeFiles(); | |||
| deleteAndZero (commandManager); | |||
| } | |||
| const String getApplicationName() | |||
| { | |||
| return T("Juce Plug-In Host"); | |||
| } | |||
| const String getApplicationVersion() | |||
| { | |||
| return T("0.9"); | |||
| } | |||
| void systemRequestedQuit() | |||
| { | |||
| if (mainWindow->isValidComponent()) | |||
| mainWindow->tryToQuitApplication(); | |||
| else | |||
| JUCEApplication::quit(); | |||
| } | |||
| bool moreThanOneInstanceAllowed() | |||
| { | |||
| return true; | |||
| } | |||
| }; | |||
| // This kicks the whole thing off.. | |||
| START_JUCE_APPLICATION (PluginHostApp) | |||
| /* | |||
| ============================================================================== | |||
| 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.h" | |||
| #include "host/MainHostWindow.h" | |||
| #include "host/InternalFilters.h" | |||
| ApplicationCommandManager* commandManager = 0; | |||
| //============================================================================== | |||
| class PluginHostApp : public JUCEApplication | |||
| { | |||
| MainHostWindow* mainWindow; | |||
| public: | |||
| //============================================================================== | |||
| PluginHostApp() | |||
| : mainWindow (0) | |||
| { | |||
| } | |||
| ~PluginHostApp() | |||
| { | |||
| } | |||
| void initialise (const String& /*commandLine*/) | |||
| { | |||
| // initialise our settings file.. | |||
| ApplicationProperties::getInstance() | |||
| ->setStorageParameters (T("Juce Audio Plugin Host"), | |||
| T("settings"), String::empty, 1000, | |||
| PropertiesFile::storeAsXML); | |||
| commandManager = new ApplicationCommandManager(); | |||
| AudioPluginFormatManager::getInstance()->addDefaultFormats(); | |||
| AudioPluginFormatManager::getInstance()->addFormat (new InternalPluginFormat()); | |||
| mainWindow = new MainHostWindow(); | |||
| commandManager->registerAllCommandsForTarget (this); | |||
| commandManager->registerAllCommandsForTarget (mainWindow); | |||
| } | |||
| void shutdown() | |||
| { | |||
| deleteAndZero (mainWindow); | |||
| ApplicationProperties::getInstance()->closeFiles(); | |||
| deleteAndZero (commandManager); | |||
| } | |||
| const String getApplicationName() | |||
| { | |||
| return T("Juce Plug-In Host"); | |||
| } | |||
| const String getApplicationVersion() | |||
| { | |||
| return T("0.9"); | |||
| } | |||
| void systemRequestedQuit() | |||
| { | |||
| if (mainWindow->isValidComponent()) | |||
| mainWindow->tryToQuitApplication(); | |||
| else | |||
| JUCEApplication::quit(); | |||
| } | |||
| bool moreThanOneInstanceAllowed() | |||
| { | |||
| return true; | |||
| } | |||
| }; | |||
| // This kicks the whole thing off.. | |||
| START_JUCE_APPLICATION (PluginHostApp) | |||
| @@ -99,7 +99,6 @@ FilterInGraph::FilterInGraph (FilterGraph& owner_, AudioPluginInstance* const fi | |||
| filter (filter_), | |||
| uid (0), | |||
| processedAudio (1, 1), | |||
| inputAudio (1, 1), | |||
| activeUI (0) | |||
| { | |||
| lastX = 100 + Random::getSystemRandom().nextInt (400); | |||
| @@ -137,8 +136,7 @@ void FilterInGraph::showUI() | |||
| void FilterInGraph::prepareBuffers (int blockSize) | |||
| { | |||
| processedAudio.setSize (jmax (1, filter->getNumOutputChannels()), blockSize); | |||
| inputAudio.setSize (jmax (1, filter->getNumInputChannels()), blockSize); | |||
| processedAudio.setSize (jmax (1, filter->getNumInputChannels(), filter->getNumOutputChannels()), blockSize); | |||
| processedAudio.clear(); | |||
| processedMidi.clear(); | |||
| @@ -148,11 +146,9 @@ void FilterInGraph::renderBlock (int numSamples, | |||
| const ReferenceCountedArray <FilterInGraph>& filters, | |||
| const OwnedArray <FilterConnection>& connections) | |||
| { | |||
| processedAudio.setSize (jmax (1, filter->getNumOutputChannels()), numSamples); | |||
| inputAudio.setSize (jmax (1, filter->getNumInputChannels()), numSamples); | |||
| processedAudio.setSize (jmax (1, filter->getNumInputChannels(), filter->getNumOutputChannels()), numSamples); | |||
| // this isn't particularly efficient - could do with some optimising here | |||
| inputAudio.clear(); | |||
| processedAudio.clear(); | |||
| processedMidi.clear(); | |||
| @@ -174,8 +170,12 @@ void FilterInGraph::renderBlock (int numSamples, | |||
| } | |||
| else | |||
| { | |||
| inputAudio.addFrom (fc->destChannel, 0, input->processedAudio, | |||
| fc->sourceChannel, 0, numSamples); | |||
| if (fc->destChannel < filter->getNumInputChannels() | |||
| && fc->sourceChannel < input->filter->getNumOutputChannels()) | |||
| { | |||
| processedAudio.addFrom (fc->destChannel, 0, input->processedAudio, | |||
| fc->sourceChannel, 0, numSamples); | |||
| } | |||
| } | |||
| break; | |||
| @@ -184,7 +184,7 @@ void FilterInGraph::renderBlock (int numSamples, | |||
| } | |||
| } | |||
| filter->processBlock (inputAudio, processedAudio, true, processedMidi); | |||
| filter->processBlock (processedAudio, processedMidi); | |||
| } | |||
| XmlElement* FilterInGraph::createXml() const | |||
| @@ -481,7 +481,7 @@ bool FilterGraph::canConnect (uint32 sourceFilterUID, int sourceFilterChannel, | |||
| return true; | |||
| } | |||
| void FilterGraph::addConnection (uint32 sourceFilterUID, int sourceChannel, | |||
| bool FilterGraph::addConnection (uint32 sourceFilterUID, int sourceChannel, | |||
| uint32 destFilterUID, int destChannel) | |||
| { | |||
| if (canConnect (sourceFilterUID, sourceChannel, destFilterUID, destChannel)) | |||
| @@ -495,7 +495,11 @@ void FilterGraph::addConnection (uint32 sourceFilterUID, int sourceChannel, | |||
| connections.add (conn); | |||
| changed(); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| void FilterGraph::removeConnection (const int index) | |||
| @@ -507,6 +511,33 @@ void FilterGraph::removeConnection (const int index) | |||
| } | |||
| } | |||
| void FilterGraph::removeIllegalConnections() | |||
| { | |||
| for (int i = connections.size(); --i >= 0;) | |||
| { | |||
| const FilterConnection* const fc = connections.getUnchecked(i); | |||
| bool ok = true; | |||
| const FilterInGraph* const source = getFilterForUID (fc->sourceFilterID); | |||
| if (source == 0 | |||
| || (fc->sourceChannel != midiChannelNumber && (fc->sourceChannel < 0 || fc->sourceChannel >= source->filter->getNumOutputChannels())) | |||
| || (fc->sourceChannel == midiChannelNumber && ! source->filter->producesMidi())) | |||
| ok = false; | |||
| const FilterInGraph* const dest = getFilterForUID (fc->destFilterID); | |||
| if (dest == 0 | |||
| || (fc->destChannel != midiChannelNumber && (fc->destChannel < 0 || fc->destChannel >= dest->filter->getNumInputChannels())) | |||
| || (fc->destChannel == midiChannelNumber && ! dest->filter->acceptsMidi())) | |||
| ok = false; | |||
| if (! ok) | |||
| removeConnection (i); | |||
| } | |||
| } | |||
| void FilterGraph::removeConnection (uint32 sourceFilterUID, int sourceFilterChannel, | |||
| uint32 destFilterUID, int destFilterChannel) | |||
| { | |||
| @@ -575,11 +606,14 @@ void FilterGraph::restoreFromXml (const XmlElement& xml) | |||
| (uint32) e->getIntAttribute (T("dstFilter")), | |||
| e->getIntAttribute (T("dstChannel"))); | |||
| } | |||
| removeIllegalConnections(); | |||
| } | |||
| //============================================================================== | |||
| FilterGraphPlayer::FilterGraphPlayer() | |||
| : sampleRate (44100.0), | |||
| FilterGraphPlayer::FilterGraphPlayer (FilterGraph& graph_) | |||
| : graph (graph_), | |||
| sampleRate (44100.0), | |||
| blockSize (512), | |||
| deviceManager (0), | |||
| inputChannelData (0), | |||
| @@ -589,10 +623,12 @@ FilterGraphPlayer::FilterGraphPlayer() | |||
| { | |||
| setAudioDeviceManager (0); | |||
| keyState.addListener (&messageCollector); | |||
| graph.addChangeListener (this); | |||
| } | |||
| FilterGraphPlayer::~FilterGraphPlayer() | |||
| { | |||
| graph.removeChangeListener (this); | |||
| keyState.removeListener (&messageCollector); | |||
| } | |||
| @@ -624,53 +660,43 @@ int FilterGraphPlayer::compareElements (FilterInGraph* const first, FilterInGrap | |||
| return firstIsInputToSecond ? -1 : 1; | |||
| } | |||
| void FilterGraphPlayer::updateFrom (FilterGraph* graphToUse) | |||
| void FilterGraphPlayer::update() | |||
| { | |||
| ReferenceCountedArray <FilterInGraph> filtersBeingRemoved (filters); | |||
| if (graphToUse != 0) | |||
| { | |||
| ReferenceCountedArray <FilterInGraph> newFilters (graphToUse->filters); | |||
| int i; | |||
| for (i = newFilters.size(); --i >= 0;) | |||
| if (filters.contains (newFilters.getUnchecked(i))) | |||
| newFilters.remove (i); | |||
| for (i = filtersBeingRemoved.size(); --i >= 0;) | |||
| if (graphToUse->filters.contains (filtersBeingRemoved.getUnchecked(i))) | |||
| filtersBeingRemoved.remove (i); | |||
| ReferenceCountedArray <FilterInGraph> newFilters (graph.filters); | |||
| int i; | |||
| for (i = newFilters.size(); --i >= 0;) | |||
| if (filters.contains (newFilters.getUnchecked(i))) | |||
| newFilters.remove (i); | |||
| // prepare any new filters for use.. | |||
| for (i = 0; i < newFilters.size(); ++i) | |||
| newFilters.getUnchecked(i)->filter->prepareToPlay (sampleRate, blockSize); | |||
| for (i = filtersBeingRemoved.size(); --i >= 0;) | |||
| if (graph.filters.contains (filtersBeingRemoved.getUnchecked(i))) | |||
| filtersBeingRemoved.remove (i); | |||
| ReferenceCountedArray <FilterInGraph> sortedFilters (graphToUse->filters); | |||
| sortedFilters.sort (*this, true); | |||
| // prepare any new filters for use.. | |||
| for (i = 0; i < newFilters.size(); ++i) | |||
| newFilters.getUnchecked(i)->filter->prepareToPlay (sampleRate, blockSize); | |||
| for (i = sortedFilters.size(); --i >= 0;) | |||
| { | |||
| PlayerAwareFilter* const specialFilter = dynamic_cast <PlayerAwareFilter*> (sortedFilters.getUnchecked(i)->filter); | |||
| if (specialFilter != 0) | |||
| specialFilter->setPlayer (this); | |||
| } | |||
| ReferenceCountedArray <FilterInGraph> sortedFilters (graph.filters); | |||
| sortedFilters.sort (*this, true); | |||
| { | |||
| const ScopedLock sl (processLock); | |||
| filters = sortedFilters; | |||
| connections.clear(); | |||
| for (i = sortedFilters.size(); --i >= 0;) | |||
| { | |||
| PlayerAwareFilter* const specialFilter = dynamic_cast <PlayerAwareFilter*> (sortedFilters.getUnchecked(i)->filter); | |||
| for (int i = 0; i < graphToUse->connections.size(); ++i) | |||
| connections.add (new FilterConnection (*graphToUse->connections.getUnchecked(i))); | |||
| } | |||
| if (specialFilter != 0) | |||
| specialFilter->setPlayer (this); | |||
| } | |||
| else | |||
| { | |||
| const ScopedLock sl (processLock); | |||
| filters.clear(); | |||
| filters = sortedFilters; | |||
| connections.clear(); | |||
| for (int i = 0; i < graph.connections.size(); ++i) | |||
| connections.add (new FilterConnection (*graph.connections.getUnchecked(i))); | |||
| } | |||
| // release any old ones.. | |||
| @@ -685,6 +711,11 @@ void FilterGraphPlayer::updateFrom (FilterGraph* graphToUse) | |||
| } | |||
| } | |||
| void FilterGraphPlayer::changeListenerCallback (void*) | |||
| { | |||
| update(); | |||
| } | |||
| void FilterGraphPlayer::audioDeviceIOCallback (const float** inputChannelData_, | |||
| int totalNumInputChannels_, | |||
| float** outputChannelData_, | |||
| @@ -724,9 +755,11 @@ void FilterGraphPlayer::audioDeviceAboutToStart (double sampleRate_, int numSamp | |||
| for (int i = 0; i < filters.size(); ++i) | |||
| { | |||
| filters.getUnchecked(i)->prepareBuffers (blockSize); | |||
| filters.getUnchecked(i)->filter->prepareToPlay (sampleRate, blockSize); | |||
| filters.getUnchecked(i)->prepareBuffers (blockSize); | |||
| } | |||
| graph.sendChangeMessage (&graph); | |||
| } | |||
| void FilterGraphPlayer::audioDeviceStopped() | |||
| @@ -114,7 +114,7 @@ private: | |||
| int lastX, lastY; | |||
| MidiBuffer outputMidi; | |||
| AudioSampleBuffer inputAudio, processedAudio; | |||
| AudioSampleBuffer processedAudio; | |||
| MidiBuffer processedMidi; | |||
| void prepareBuffers (int blockSize); | |||
| @@ -149,6 +149,8 @@ public: | |||
| void removeFilter (const uint32 filterUID); | |||
| void disconnectFilter (const uint32 filterUID); | |||
| void removeIllegalConnections(); | |||
| //============================================================================== | |||
| int getNumConnections() const throw() { return connections.size(); } | |||
| FilterConnection* getConnection (const int index) const throw() { return connections [index]; } | |||
| @@ -161,7 +163,7 @@ public: | |||
| bool canConnect (uint32 sourceFilterUID, int sourceFilterChannel, | |||
| uint32 destFilterUID, int destFilterChannel) const throw(); | |||
| void addConnection (uint32 sourceFilterUID, int sourceFilterChannel, | |||
| bool addConnection (uint32 sourceFilterUID, int sourceFilterChannel, | |||
| uint32 destFilterUID, int destFilterChannel); | |||
| void removeConnection (const int index); | |||
| @@ -208,17 +210,16 @@ private: | |||
| An object the | |||
| */ | |||
| class FilterGraphPlayer : public AudioIODeviceCallback, | |||
| public MidiInputCallback | |||
| public MidiInputCallback, | |||
| public ChangeListener | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| FilterGraphPlayer(); | |||
| FilterGraphPlayer (FilterGraph& graph); | |||
| ~FilterGraphPlayer(); | |||
| //============================================================================== | |||
| void updateFrom (FilterGraph* graphToUse); | |||
| void setAudioDeviceManager (AudioDeviceManager* dm); | |||
| AudioDeviceManager* getAudioDeviceManager() const throw() { return deviceManager; } | |||
| @@ -233,6 +234,8 @@ public: | |||
| void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message); | |||
| void changeListenerCallback (void*); | |||
| //============================================================================== | |||
| static int compareElements (FilterInGraph* const first, FilterInGraph* const second) throw(); | |||
| @@ -253,6 +256,7 @@ public: | |||
| }; | |||
| private: | |||
| FilterGraph& graph; | |||
| CriticalSection processLock; | |||
| double sampleRate; | |||
| int blockSize; | |||
| @@ -261,6 +265,8 @@ private: | |||
| ReferenceCountedArray <FilterInGraph> filters; | |||
| OwnedArray <FilterConnection> connections; | |||
| void update(); | |||
| FilterGraphPlayer (const FilterGraphPlayer&); | |||
| const FilterGraphPlayer& operator= (const FilterGraphPlayer&); | |||
| }; | |||
| @@ -858,27 +858,22 @@ GraphDocumentComponent::GraphDocumentComponent (AudioDeviceManager* deviceManage | |||
| { | |||
| addAndMakeVisible (graphPanel = new GraphEditorPanel (graph)); | |||
| addAndMakeVisible (keyboardComp = new MidiKeyboardComponent (graphRenderer.keyState, | |||
| MidiKeyboardComponent::horizontalKeyboard)); | |||
| graphPlayer = new FilterGraphPlayer (graph); | |||
| graphRenderer.setAudioDeviceManager (deviceManager); | |||
| graphRenderer.updateFrom (&graph); | |||
| addAndMakeVisible (keyboardComp = new MidiKeyboardComponent (graphPlayer->keyState, | |||
| MidiKeyboardComponent::horizontalKeyboard)); | |||
| graph.addChangeListener (this); | |||
| deviceManager->addChangeListener (this); | |||
| graphPlayer->setAudioDeviceManager (deviceManager); | |||
| graphPanel->updateComponents(); | |||
| } | |||
| GraphDocumentComponent::~GraphDocumentComponent() | |||
| { | |||
| graph.removeChangeListener (this); | |||
| deviceManager->removeChangeListener (this); | |||
| deleteAllChildren(); | |||
| graphRenderer.updateFrom (0); | |||
| graphRenderer.setAudioDeviceManager (0); | |||
| graphPlayer->setAudioDeviceManager (0); | |||
| deleteAndZero (graphPlayer); | |||
| graph.clear(); | |||
| } | |||
| @@ -894,8 +889,3 @@ void GraphDocumentComponent::createNewPlugin (const PluginDescription* desc, int | |||
| { | |||
| graphPanel->createNewPlugin (desc, x, y); | |||
| } | |||
| void GraphDocumentComponent::changeListenerCallback (void*) | |||
| { | |||
| graphRenderer.updateFrom (&graph); | |||
| } | |||
| @@ -89,8 +89,7 @@ private: | |||
| It also manages the graph itself, and plays it. | |||
| */ | |||
| class GraphDocumentComponent : public Component, | |||
| public ChangeListener | |||
| class GraphDocumentComponent : public Component | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| @@ -105,14 +104,13 @@ public: | |||
| //============================================================================== | |||
| void resized(); | |||
| void changeListenerCallback (void*); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| AudioDeviceManager* deviceManager; | |||
| FilterGraphPlayer graphRenderer; | |||
| FilterGraphPlayer* graphPlayer; | |||
| GraphEditorPanel* graphPanel; | |||
| Component* keyboardComp; | |||
| @@ -49,10 +49,10 @@ public: | |||
| void setPlayer (FilterGraphPlayer* p) | |||
| { | |||
| player = p; | |||
| rendererChanged(); | |||
| playerChanged(); | |||
| } | |||
| virtual void rendererChanged() {} | |||
| virtual void playerChanged() {} | |||
| const String getManufacturer() const { return "Raw Material Software"; } | |||
| const String getFormatName() const { return "Internal"; } | |||
| @@ -110,42 +110,36 @@ public: | |||
| bool acceptsMidi() const { return false; } | |||
| bool producesMidi() const { return false; } | |||
| void rendererChanged() | |||
| void playerChanged() | |||
| { | |||
| AudioIODevice* const dev = getAudioDevice(); | |||
| if (dev != 0) | |||
| numOutputChannels = dev->getInputChannelNames().size(); | |||
| numOutputChannels = dev->getActiveInputChannels().countNumberOfSetBits(); | |||
| } | |||
| void JUCE_CALLTYPE prepareToPlay (double /*sampleRate*/, int /*estimatedSamplesPerBlock*/) | |||
| { | |||
| rendererChanged(); | |||
| playerChanged(); | |||
| } | |||
| void JUCE_CALLTYPE releaseResources() | |||
| { | |||
| } | |||
| void JUCE_CALLTYPE processBlock (const AudioSampleBuffer& /*input*/, | |||
| AudioSampleBuffer& output, | |||
| const bool accumulateOutput, | |||
| void JUCE_CALLTYPE processBlock (AudioSampleBuffer& buffer, | |||
| MidiBuffer&) | |||
| { | |||
| int n = 0; | |||
| for (int i = 0; i < player->totalNumInputChannels; ++i) | |||
| { | |||
| if (n >= output.getNumChannels()) | |||
| if (n >= getNumOutputChannels()) | |||
| break; | |||
| if (player->inputChannelData [i] != 0) | |||
| { | |||
| if (accumulateOutput) | |||
| output.addFrom (n, 0, player->inputChannelData [i], output.getNumSamples()); | |||
| else | |||
| output.copyFrom (n, 0, player->inputChannelData [i], output.getNumSamples()); | |||
| buffer.copyFrom (n, 0, player->inputChannelData [i], buffer.getNumSamples()); | |||
| ++n; | |||
| } | |||
| } | |||
| @@ -196,49 +190,42 @@ public: | |||
| bool acceptsMidi() const { return false; } | |||
| bool producesMidi() const { return false; } | |||
| void rendererChanged() | |||
| void playerChanged() | |||
| { | |||
| AudioIODevice* const dev = getAudioDevice(); | |||
| if (dev != 0) | |||
| numInputChannels = dev->getOutputChannelNames().size(); | |||
| numInputChannels = dev->getActiveOutputChannels().countNumberOfSetBits(); | |||
| } | |||
| void JUCE_CALLTYPE prepareToPlay (double /*sampleRate*/, int /*estimatedSamplesPerBlock*/) | |||
| { | |||
| rendererChanged(); | |||
| playerChanged(); | |||
| } | |||
| void JUCE_CALLTYPE releaseResources() | |||
| { | |||
| } | |||
| void JUCE_CALLTYPE processBlock (const AudioSampleBuffer& input, | |||
| AudioSampleBuffer&, | |||
| const bool accumulateOutput, | |||
| void JUCE_CALLTYPE processBlock (AudioSampleBuffer& buffer, | |||
| MidiBuffer&) | |||
| { | |||
| int n = 0; | |||
| for (int i = 0; i < player->totalNumOutputChannels; ++i) | |||
| { | |||
| if (n >= input.getNumChannels()) | |||
| break; | |||
| float* dst = player->outputChannelData [i]; | |||
| float* const dst = player->outputChannelData [i]; | |||
| if (dst != 0) | |||
| { | |||
| const float* src = input.getSampleData (n); | |||
| if (accumulateOutput) | |||
| if (n >= getNumInputChannels()) | |||
| { | |||
| for (int i = input.getNumSamples(); --i >= 0;) | |||
| *dst++ += *src++; | |||
| zeromem (dst, sizeof (float) * buffer.getNumSamples()); | |||
| } | |||
| else | |||
| { | |||
| memcpy (dst, src, sizeof (float) * input.getNumSamples()); | |||
| memcpy (dst, buffer.getSampleData (n), | |||
| sizeof (float) * buffer.getNumSamples()); | |||
| } | |||
| ++n; | |||
| @@ -290,7 +277,7 @@ public: | |||
| bool acceptsMidi() const { return false; } | |||
| bool producesMidi() const { return true; } | |||
| void rendererChanged() | |||
| void playerChanged() | |||
| { | |||
| if (player != 0) | |||
| { | |||
| @@ -302,22 +289,18 @@ public: | |||
| void JUCE_CALLTYPE prepareToPlay (double /*sampleRate*/, int /*estimatedSamplesPerBlock*/) | |||
| { | |||
| rendererChanged(); | |||
| playerChanged(); | |||
| } | |||
| void JUCE_CALLTYPE releaseResources() | |||
| { | |||
| } | |||
| void JUCE_CALLTYPE processBlock (const AudioSampleBuffer& input, | |||
| AudioSampleBuffer&, | |||
| const bool accumulateOutput, | |||
| void JUCE_CALLTYPE processBlock (AudioSampleBuffer& buffer, | |||
| MidiBuffer& midiBuffer) | |||
| { | |||
| if (! accumulateOutput) | |||
| midiBuffer.clear(); | |||
| midiBuffer.addEvents (player->incomingMidi, 0, input.getNumSamples(), 0); | |||
| midiBuffer.clear(); | |||
| midiBuffer.addEvents (player->incomingMidi, 0, buffer.getNumSamples(), 0); | |||
| } | |||
| const String JUCE_CALLTYPE getInputChannelName (const int channelIndex) const | |||
| @@ -434,9 +434,9 @@ bool MainHostWindow::perform (const InvocationInfo& info) | |||
| void MainHostWindow::showAudioSettings() | |||
| { | |||
| AudioDeviceSelectorComponent audioSettingsComp (deviceManager, | |||
| 0, 2, | |||
| 0, 2, | |||
| false); | |||
| 0, 256, | |||
| 0, 256, | |||
| true); | |||
| audioSettingsComp.setSize (500, 300); | |||
| @@ -449,11 +449,16 @@ void MainHostWindow::showAudioSettings() | |||
| XmlElement* const audioState = deviceManager.createStateXml(); | |||
| ApplicationProperties::getInstance()->getUserSettings() | |||
| ->setValue (T("audioSettings"), audioState); | |||
| ->setValue (T("audioDeviceState"), audioState); | |||
| delete audioState; | |||
| ApplicationProperties::getInstance()->getUserSettings()->saveIfNeeded(); | |||
| delete audioState; | |||
| GraphDocumentComponent* const graphEditor = getGraphEditor(); | |||
| if (graphEditor != 0) | |||
| graphEditor->graph.removeIllegalConnections(); | |||
| } | |||
| bool MainHostWindow::filesDropped (const StringArray& files, int x, int y) | |||
| @@ -135,12 +135,11 @@ bool KnownPluginList::scanAndAddFile (const File& possiblePluginFile, | |||
| return false; | |||
| } | |||
| OwnedArray <PluginDescription> found; | |||
| for (int i = 0; i < AudioPluginFormatManager::getInstance()->getNumFormats(); ++i) | |||
| { | |||
| AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (i); | |||
| OwnedArray <PluginDescription> found; | |||
| format->findAllTypesForFile (found, possiblePluginFile); | |||
| for (int i = 0; i < found.size(); ++i) | |||
| @@ -165,7 +164,7 @@ void KnownPluginList::scanAndAddDragAndDroppedFiles (const StringArray& files, | |||
| { | |||
| const File f (files [i]); | |||
| if (! scanAndAddFile (f, false, typesFound)) | |||
| if (! scanAndAddFile (f, true, typesFound)) | |||
| { | |||
| if (f.isDirectory()) | |||
| { | |||
| @@ -88,9 +88,7 @@ public: | |||
| void JUCE_CALLTYPE prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); | |||
| void JUCE_CALLTYPE releaseResources(); | |||
| void JUCE_CALLTYPE processBlock (const AudioSampleBuffer& input, | |||
| AudioSampleBuffer& output, | |||
| const bool accumulateOutput, | |||
| void JUCE_CALLTYPE processBlock (AudioSampleBuffer& buffer, | |||
| MidiBuffer& midiMessages); | |||
| AudioFilterEditor* JUCE_CALLTYPE createEditor(); | |||
| @@ -147,8 +145,7 @@ private: | |||
| void* midiEventsToSend; | |||
| int numAllocatedMidiEvents; | |||
| VstTimeInfo vstHostTime; | |||
| float** channelsIn; | |||
| float** channelsOut; | |||
| float** channels; | |||
| ReferenceCountedObjectPtr <ModuleHandle> module; | |||
| @@ -31,6 +31,7 @@ | |||
| 3E8BF120079CA60300021B09 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5AA9A300281AAB901C34293 /* CoreServices.framework */; }; | |||
| 3E8BF121079CA60300021B09 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5DA37E702821D04014500A0 /* Carbon.framework */; }; | |||
| 3E8BF122079CA60300021B09 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5D19ABE0317606901CA2136 /* AudioUnit.framework */; }; | |||
| 844C50290C71B6E300D0082E /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 844C50280C71B6E300D0082E /* IOKit.framework */; }; | |||
| 845FAE5F0A5C0A6A008C94D8 /* juce.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 845FAE5E0A5C0A6A008C94D8 /* juce.xcconfig */; }; | |||
| 845FAEE10A5C2696008C94D8 /* QuickTime.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 845FAEE00A5C2696008C94D8 /* QuickTime.framework */; }; | |||
| 84CFAEFB090964560053C22C /* AUCarbonViewDispatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5D19AAB03175F3201CA2136 /* AUCarbonViewDispatch.cpp */; }; | |||
| @@ -96,6 +97,7 @@ | |||
| 7967EF8B04D70E7C00C625F7 /* CAAudioChannelLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = CAAudioChannelLayout.cpp; sourceTree = "<group>"; }; | |||
| 7967EF8C04D70E7C00C625F7 /* CAAudioChannelLayout.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CAAudioChannelLayout.h; sourceTree = "<group>"; }; | |||
| 7967EF8D04D70E7C00C625F7 /* CAStreamBasicDescription.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = CAStreamBasicDescription.cpp; sourceTree = "<group>"; }; | |||
| 844C50280C71B6E300D0082E /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = IOKit.framework; sourceTree = "<group>"; }; | |||
| 845FAE5E0A5C0A6A008C94D8 /* juce.xcconfig */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.xcconfig; name = juce.xcconfig; path = ../../../../../build/macosx/juce.xcconfig; sourceTree = SOURCE_ROOT; }; | |||
| 845FAEE00A5C2696008C94D8 /* QuickTime.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = QuickTime.framework; sourceTree = "<group>"; }; | |||
| 84EB4009090A4A2C008FAC1B /* juce_AudioUnitWrapper.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = juce_AudioUnitWrapper.cpp; path = ../../../wrapper/formats/AudioUnit/juce_AudioUnitWrapper.cpp; sourceTree = SOURCE_ROOT; }; | |||
| @@ -170,6 +172,7 @@ | |||
| 84F0548F090687F600AEC8DB /* CoreAudio.framework in Frameworks */, | |||
| 84F87963093B1EDC00225D65 /* AGL.framework in Frameworks */, | |||
| 845FAEE10A5C2696008C94D8 /* QuickTime.framework in Frameworks */, | |||
| 844C50290C71B6E300D0082E /* IOKit.framework in Frameworks */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| @@ -271,6 +274,7 @@ | |||
| F5AA9A2F0281AAB901C34293 /* CoreFoundation.framework */, | |||
| F5AA9A300281AAB901C34293 /* CoreServices.framework */, | |||
| 84F87962093B1EDC00225D65 /* AGL.framework */, | |||
| 844C50280C71B6E300D0082E /* IOKit.framework */, | |||
| 845FAEE00A5C2696008C94D8 /* QuickTime.framework */, | |||
| ); | |||
| name = Frameworks; | |||
| @@ -103,6 +103,26 @@ const String DemoJuceFilter::getParameterText (int index) | |||
| return String::empty; | |||
| } | |||
| const String JUCE_CALLTYPE DemoJuceFilter::getInputChannelName (const int channelIndex) const | |||
| { | |||
| return String (channelIndex + 1); | |||
| } | |||
| const String JUCE_CALLTYPE DemoJuceFilter::getOutputChannelName (const int channelIndex) const | |||
| { | |||
| return String (channelIndex + 1); | |||
| } | |||
| bool JUCE_CALLTYPE DemoJuceFilter::isInputChannelStereoPair (int index) const | |||
| { | |||
| return false; | |||
| } | |||
| bool JUCE_CALLTYPE DemoJuceFilter::isOutputChannelStereoPair (int index) const | |||
| { | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| void DemoJuceFilter::prepareToPlay (double sampleRate, int samplesPerBlock) | |||
| { | |||
| @@ -116,67 +136,28 @@ void DemoJuceFilter::releaseResources() | |||
| // spare memory, etc. | |||
| } | |||
| void DemoJuceFilter::processBlock (const AudioSampleBuffer& input, | |||
| AudioSampleBuffer& output, | |||
| const bool accumulateOutput, | |||
| void DemoJuceFilter::processBlock (AudioSampleBuffer& buffer, | |||
| MidiBuffer& midiMessages) | |||
| { | |||
| if (accumulateOutput) | |||
| // for each of our input channels, we'll attenuate its level by the | |||
| // amount that our volume parameter is set to. | |||
| for (int channel = 0; channel < getNumInputChannels(); ++channel) | |||
| { | |||
| // if we're accumulating, we should add our results to the existing contents of the | |||
| // output buffer.. | |||
| if (input.getNumChannels() > 0) | |||
| { | |||
| for (int channel = 0; channel < output.getNumChannels(); ++channel) | |||
| { | |||
| // for each output channel, add the contents of the corresponding | |||
| // input channel (or if there are more outputs than inputs, just | |||
| // keep using the last input channel) | |||
| output.addFrom (channel, | |||
| 0, | |||
| input, | |||
| jmin (channel, input.getNumChannels() - 1), | |||
| 0, | |||
| input.getNumSamples(), | |||
| gain); | |||
| } | |||
| } | |||
| buffer.applyGain (channel, 0, buffer.getNumSamples(), gain); | |||
| } | |||
| else | |||
| { | |||
| // if we're not accumulating, the output buffer's contents are undefined | |||
| // (don't assume they're zero!) and we should overwrite it. | |||
| if (input.getNumChannels() > 0) | |||
| { | |||
| for (int channel = 0; channel < output.getNumChannels(); ++channel) | |||
| { | |||
| // for each output channel, copy the contents of the corresponding | |||
| // input channel (or if there are more outputs than inputs, just | |||
| // keep using the last input channel) | |||
| output.copyFrom (channel, | |||
| 0, | |||
| input, | |||
| jmin (channel, input.getNumChannels() - 1), | |||
| 0, | |||
| input.getNumSamples()); | |||
| } | |||
| output.applyGain (0, output.getNumSamples(), gain); | |||
| } | |||
| else | |||
| { | |||
| // when not accumulating, you always have to put something into | |||
| // the output buffer, even if in this case we have no inputs to copy. | |||
| output.clear(); | |||
| } | |||
| // in case we have more outputs than inputs, we'll clear any output | |||
| // channels that didn't contain input data, (because these aren't | |||
| // guaranteed to be empty - they may contain garbage). | |||
| for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) | |||
| { | |||
| buffer.clear (i, 0, buffer.getNumSamples()); | |||
| } | |||
| // if any midi messages come in, use them to update the keyboard state object. This | |||
| // object sends notification to the UI component about key up/down changes | |||
| keyboardState.processNextMidiBuffer (midiMessages, | |||
| 0, output.getNumSamples(), | |||
| 0, buffer.getNumSamples(), | |||
| true); | |||
| // have a go at getting the current time from the host, and if it's changed, tell | |||
| @@ -53,9 +53,7 @@ public: | |||
| void JUCE_CALLTYPE prepareToPlay (double sampleRate, int samplesPerBlock); | |||
| void JUCE_CALLTYPE releaseResources(); | |||
| void JUCE_CALLTYPE processBlock (const AudioSampleBuffer& input, | |||
| AudioSampleBuffer& output, | |||
| const bool accumulateOutput, | |||
| void JUCE_CALLTYPE processBlock (AudioSampleBuffer& buffer, | |||
| MidiBuffer& midiMessages); | |||
| //============================================================================== | |||
| @@ -70,6 +68,11 @@ public: | |||
| const String JUCE_CALLTYPE getParameterName (int index); | |||
| const String JUCE_CALLTYPE getParameterText (int index); | |||
| const String JUCE_CALLTYPE getInputChannelName (const int channelIndex) const; | |||
| const String JUCE_CALLTYPE getOutputChannelName (const int channelIndex) const; | |||
| bool JUCE_CALLTYPE isInputChannelStereoPair (int index) const; | |||
| bool JUCE_CALLTYPE isOutputChannelStereoPair (int index) const; | |||
| //============================================================================== | |||
| int JUCE_CALLTYPE getNumPrograms() { return 0; } | |||
| int JUCE_CALLTYPE getCurrentProgram() { return 0; } | |||
| @@ -120,7 +120,8 @@ class JucePlugInProcess : public CEffectProcessMIDI, | |||
| public: | |||
| //============================================================================== | |||
| JucePlugInProcess() | |||
| : prepared (false), | |||
| : channels (0), | |||
| prepared (false), | |||
| midiBufferNode (0), | |||
| midiTransport (0), | |||
| sampleRate (44100.0) | |||
| @@ -143,6 +144,7 @@ public: | |||
| juceFilter->releaseResources(); | |||
| delete juceFilter; | |||
| juce_free (channels); | |||
| } | |||
| //============================================================================== | |||
| @@ -580,6 +582,10 @@ protected: | |||
| sampleRate = gProcessGroup->GetSampleRate(); | |||
| jassert (sampleRate > 0); | |||
| juce_free (channels); | |||
| channels = (float**) juce_calloc (sizeof (float*) * jmax (juceFilter->getNumInputChannels(), | |||
| juceFilter->getNumOutputChannels())); | |||
| juceFilter->prepareToPlay (sampleRate, | |||
| mRTGlobals->mHWBufferSizeInSamples); | |||
| @@ -632,15 +638,37 @@ protected: | |||
| #endif | |||
| { | |||
| const AudioSampleBuffer input (inputs, juceFilter->numInputChannels, numSamples); | |||
| AudioSampleBuffer output (outputs, juceFilter->numOutputChannels, numSamples); | |||
| const ScopedLock sl (juceFilter->getCallbackLock()); | |||
| const int numIn = juceFilter->numInputChannels; | |||
| const int numOut = juceFilter->numOutputChannels; | |||
| const int totalChans = jmax (numIn, numOut); | |||
| if (juceFilter->suspended) | |||
| bypassBuffers (inputs, outputs, numSamples); | |||
| { | |||
| for (int i = 0; i < numOut; ++i) | |||
| zeromem (outputs [i], sizeof (float) * numSamples); | |||
| } | |||
| else | |||
| juceFilter->processBlock (input, output, false, midiEvents); | |||
| { | |||
| { | |||
| int i; | |||
| for (i = 0; i < numOut; ++i) | |||
| { | |||
| channels[i] = outputs [i]; | |||
| if (i < numIn && inputs != outputs) | |||
| memcpy (outputs [i], inputs[i], sizeof (float) * numSamples); | |||
| } | |||
| for (; i < numIn; ++i) | |||
| channels [i] = inputs [i]; | |||
| } | |||
| AudioSampleBuffer chans (channels, totalChans, numSamples); | |||
| juceFilter->processBlock (chans, midiEvents); | |||
| } | |||
| } | |||
| if (! midiEvents.isEmpty()) | |||
| @@ -826,6 +854,7 @@ private: | |||
| DirectMidiPacket midiBuffer [midiBufferSize]; | |||
| juce::MemoryBlock tempFilterData; | |||
| float** channels; | |||
| bool prepared; | |||
| double sampleRate; | |||
| @@ -86,9 +86,16 @@ void AudioFilterStreamer::audioDeviceIOCallback (const float** inputChannelData, | |||
| const ScopedLock sl (filter.getCallbackLock()); | |||
| if (filter.suspended) | |||
| { | |||
| output.clear(); | |||
| } | |||
| else | |||
| filter.processBlock (input, output, false, midiBuffer); | |||
| { | |||
| for (int i = jmin (output.getNumChannels(), input.getNumChannels()); --i >= 0;) | |||
| output.copyFrom (i, 0, input, i, 0, numSamples); | |||
| filter.processBlock (output, midiBuffer); | |||
| } | |||
| } | |||
| while (numOutsWanted < numActiveOutChans) | |||
| @@ -323,6 +323,7 @@ public: | |||
| chunkMemoryTime = 0; | |||
| isProcessing = false; | |||
| firstResize = true; | |||
| channels = 0; | |||
| #if JUCE_MAC || JUCE_LINUX | |||
| hostWindow = 0; | |||
| @@ -377,6 +378,8 @@ public: | |||
| jassert (activePlugins.contains (this)); | |||
| activePlugins.removeValue (this); | |||
| juce_free (channels); | |||
| #if JUCE_MAC || JUCE_LINUX | |||
| if (activePlugins.size() == 0) | |||
| { | |||
| @@ -528,9 +531,24 @@ public: | |||
| #endif | |||
| } | |||
| void process (float** inputs, float** outputs, | |||
| const VstInt32 numSamples, const bool accumulate) | |||
| void process (float** inputs, float** outputs, VstInt32 numSamples) | |||
| { | |||
| AudioSampleBuffer temp (filter->numInputChannels, numSamples); | |||
| int i; | |||
| for (i = filter->numInputChannels; --i >= 0;) | |||
| memcpy (temp.getSampleData (i), outputs[i], sizeof (float) * numSamples); | |||
| processReplacing (inputs, outputs, numSamples); | |||
| AudioSampleBuffer dest (outputs, filter->numOutputChannels, numSamples); | |||
| for (i = jmin (filter->numOutputChannels, filter->numInputChannels); --i >= 0;) | |||
| dest.addFrom (i, 0, temp, i, 0, numSamples); | |||
| } | |||
| void processReplacing (float** inputs, float** outputs, VstInt32 numSamples) | |||
| { | |||
| //process (inputs, outputs, numSamples, false); | |||
| // if this fails, the host hasn't called resume() before processing | |||
| jassert (isProcessing); | |||
| @@ -546,19 +564,36 @@ public: | |||
| jassert (activePlugins.contains (this)); | |||
| { | |||
| const AudioSampleBuffer input (inputs, filter->numInputChannels, numSamples); | |||
| AudioSampleBuffer output (outputs, filter->numOutputChannels, numSamples); | |||
| const ScopedLock sl (filter->getCallbackLock()); | |||
| const int numIn = filter->numInputChannels; | |||
| const int numOut = filter->numOutputChannels; | |||
| const int totalChans = jmax (numIn, numOut); | |||
| if (filter->suspended) | |||
| { | |||
| if (! accumulate) | |||
| output.clear(); | |||
| for (int i = 0; i < numOut; ++i) | |||
| zeromem (outputs [i], sizeof (float) * numSamples); | |||
| } | |||
| else | |||
| { | |||
| filter->processBlock (input, output, accumulate, midiEvents); | |||
| { | |||
| int i; | |||
| for (i = 0; i < numOut; ++i) | |||
| { | |||
| channels[i] = outputs [i]; | |||
| if (i < numIn && inputs != outputs) | |||
| memcpy (outputs [i], inputs[i], sizeof (float) * numSamples); | |||
| } | |||
| for (; i < numIn; ++i) | |||
| channels [i] = inputs [i]; | |||
| } | |||
| AudioSampleBuffer chans (channels, totalChans, numSamples); | |||
| filter->processBlock (chans, midiEvents); | |||
| } | |||
| } | |||
| @@ -610,20 +645,12 @@ public: | |||
| } | |||
| } | |||
| void process (float** inputs, float** outputs, VstInt32 numSamples) | |||
| { | |||
| process (inputs, outputs, numSamples, true); | |||
| } | |||
| void processReplacing (float** inputs, float** outputs, VstInt32 numSamples) | |||
| { | |||
| process (inputs, outputs, numSamples, false); | |||
| } | |||
| //============================================================================== | |||
| void resume() | |||
| { | |||
| isProcessing = true; | |||
| juce_free (channels); | |||
| channels = (float**) juce_calloc (sizeof (float*) * jmax (filter->getNumInputChannels(), filter->getNumOutputChannels())); | |||
| filter->sampleRate = getSampleRate(); | |||
| @@ -656,6 +683,8 @@ public: | |||
| midiEvents.clear(); | |||
| isProcessing = false; | |||
| juce_free (channels); | |||
| channels = 0; | |||
| } | |||
| bool JUCE_CALLTYPE getCurrentPositionInfo (AudioFilterBase::CurrentPositionInfo& info) | |||
| @@ -766,13 +795,13 @@ public: | |||
| void getParameterDisplay (VstInt32 index, char* text) | |||
| { | |||
| jassert (index >= 0 && index < filter->getNumParameters()); | |||
| filter->getParameterText (index).copyToBuffer (text, 64); | |||
| filter->getParameterText (index).copyToBuffer (text, 24); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. | |||
| } | |||
| void getParameterName (VstInt32 index, char* text) | |||
| { | |||
| jassert (index >= 0 && index < filter->getNumParameters()); | |||
| filter->getParameterName (index).copyToBuffer (text, 8); | |||
| filter->getParameterName (index).copyToBuffer (text, 16); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. | |||
| } | |||
| void JUCE_CALLTYPE informHostOfParameterChange (int index, float newValue) | |||
| @@ -1149,6 +1178,7 @@ private: | |||
| bool isProcessing; | |||
| bool firstResize; | |||
| int diffW, diffH; | |||
| float** channels; | |||
| void ensureOutgoingEventSize (int numEvents) | |||
| { | |||
| @@ -42,14 +42,17 @@ | |||
| //============================================================================== | |||
| /** | |||
| Base class for plugins written using JUCE. | |||
| Base class for audio filters or plugins written using JUCE. | |||
| This is intended to act as a base class of plugin that is general enough to | |||
| be wrapped as a VST, AU, RTAS, etc. | |||
| This is intended to act as a base class of audio filter that is general enough to | |||
| be wrapped as a VST, AU, RTAS, etc, or used internally. | |||
| Derive your filter class from this base class, and implement a global function | |||
| called initialiseFilterInfo() which creates and returns a new instance of | |||
| your subclass. | |||
| It is also used by the plugin hosting code as the wrapper around an instance | |||
| of a loaded plugin. | |||
| Derive your filter class from this base class, and if you're building a plugin, | |||
| you should implement a global function called initialiseFilterInfo() which creates | |||
| and returns a new instance of your subclass. | |||
| */ | |||
| class AudioFilterBase | |||
| { | |||
| @@ -67,7 +70,7 @@ public: | |||
| virtual ~AudioFilterBase(); | |||
| //============================================================================== | |||
| /** Called before playback starts, to let the plugin prepare itself. | |||
| /** Called before playback starts, to let the filter prepare itself. | |||
| The sample rate is the target sample rate, and will remain constant until | |||
| playback stops. | |||
| @@ -80,47 +83,50 @@ public: | |||
| virtual void JUCE_CALLTYPE prepareToPlay (double sampleRate, | |||
| int estimatedSamplesPerBlock) = 0; | |||
| /** Called after playback has stopped, to let the plugin free up any resources it | |||
| /** Called after playback has stopped, to let the filter free up any resources it | |||
| no longer needs. | |||
| */ | |||
| virtual void JUCE_CALLTYPE releaseResources() = 0; | |||
| /** Renders the next block. | |||
| The input and output buffers will have been prepared with the number of samples | |||
| and channels required, and mustn't be resized. Note that these may both point to | |||
| the same block of memory if accumulateOutput is true. There will always be the | |||
| same number of samples in the input and output buffers, but the number of channels | |||
| may not be the same. | |||
| If accumulateOutput is true, then the output buffer will contain a copy of the | |||
| input buffer (or may be physically the same memory - be careful!), and your filter's | |||
| output should be added to (or may replace) whatever samples are already in the | |||
| output buffer. | |||
| If accumulateOutput is false then the contents of the output buffer are undefined | |||
| and MUST ALL be overwritten with your plugin's output. | |||
| Note that the number of samples in these buffers is NOT guaranteed to be | |||
| the same for every callback, and may be more or less than the estimated value | |||
| given to prepareToPlay(). Your code must be able to cope with variable-sized | |||
| blocks, or you're going to get clicks and crashes! | |||
| Your plugin must also not make any assumptions about the number of channels | |||
| supplied in the input and output buffers - there could be any number of channels | |||
| here, up to the maximum values specified in your JucePluginCharacteristics.h file. | |||
| However, the number of channels will remain constant between the prepareToPlay() | |||
| and releaseResources() calls. | |||
| If the plugin has indicated that it needs midi input, then the midiMessages | |||
| array will be filled with midi messages for this block. Each message's timestamp | |||
| will indicate the message's time, as a number of samples from the start of the | |||
| block. | |||
| If the plugin has indicated that it produces midi output, then any messages remaining | |||
| in the midiMessages array will be sent to the host after the processBlock method | |||
| returns. This means that the plugin must be careful to clear any incoming messages | |||
| out of the array if it doesn't want them to be passed-on. | |||
| When this method is called, the buffer contains a number of channels which is | |||
| at least as great as the maximum number of input and output channels that | |||
| this filter is using. It will be filled with the filter's input data and | |||
| should be replaced with the filter's output. | |||
| So for example if your filter has 2 input channels and 4 output channels, then | |||
| the buffer will contain 4 channels, the first two being filled with the | |||
| input data. Your filter should read these, do its processing, and replace | |||
| the contents of all 4 channels with its output. | |||
| Or if your filter has 5 inputs and 2 outputs, the buffer will have 5 channels, | |||
| all filled with data, and your filter should overwrite the first 2 of these | |||
| with its output. But be VERY careful not to write anything to the last 3 | |||
| channels, as these might be mapped to memory that the host assumes is read-only! | |||
| Note that if you have more outputs than inputs, then only those channels that | |||
| correspond to an input channel are guaranteed to contain sensible data - e.g. | |||
| in the case of 2 inputs and 4 outputs, the first two channels contain the input, | |||
| but the last two channels may contain garbage, so you should be careful not to | |||
| let this pass through without being overwritten or cleared. | |||
| Also note that the buffer may have more channels than are strictly necessary, | |||
| but your should only read/write from the ones that your filter is supposed to | |||
| be using. | |||
| The number of samples in these buffers is NOT guaranteed to be the same for every | |||
| callback, and may be more or less than the estimated value given to prepareToPlay(). | |||
| Your code must be able to cope with variable-sized blocks, or you're going to get | |||
| clicks and crashes! | |||
| If the filter is receiving a midi input, then the midiMessages array will be filled | |||
| with the midi messages for this block. Each message's timestamp will indicate the | |||
| message's time, as a number of samples from the start of the block. | |||
| Any messages left in the midi buffer when this method has finished are assumed to | |||
| be the filter's midi output. This means that your filter should be careful to | |||
| clear any incoming messages from the array if it doesn't want them to be passed-on. | |||
| Be very careful about what you do in this callback - it's going to be called by | |||
| the audio thread, so any kind of interaction with the UI is absolutely | |||
| @@ -130,9 +136,7 @@ public: | |||
| processBlock() method to send out an asynchronous message. You could also use | |||
| the AsyncUpdater class in a similar way. | |||
| */ | |||
| virtual void JUCE_CALLTYPE processBlock (const AudioSampleBuffer& input, | |||
| AudioSampleBuffer& output, | |||
| const bool accumulateOutput, | |||
| virtual void JUCE_CALLTYPE processBlock (AudioSampleBuffer& buffer, | |||
| MidiBuffer& midiMessages) = 0; | |||
| //============================================================================== | |||
| @@ -230,7 +234,7 @@ public: | |||
| /** Returns the number of input channels that the host will be sending the filter. | |||
| In your JucePluginCharacteristics.h file, you specify the number of channels | |||
| that your plugin would prefer to get, and this method lets you know how | |||
| that your filter would prefer to get, and this method lets you know how | |||
| many the host is actually going to send. | |||
| Note that this method is only valid during or after the prepareToPlay() | |||
| @@ -241,7 +245,7 @@ public: | |||
| /** Returns the number of output channels that the host will be sending the filter. | |||
| In your JucePluginCharacteristics.h file, you specify the number of channels | |||
| that your plugin would prefer to get, and this method lets you know how | |||
| that your filter would prefer to get, and this method lets you know how | |||
| many the host is actually going to send. | |||
| Note that this method is only valid during or after the prepareToPlay() | |||
| @@ -310,24 +314,27 @@ public: | |||
| void JUCE_CALLTYPE suspendProcessing (const bool shouldBeSuspended); | |||
| //============================================================================== | |||
| /** Creates the plugin's UI. | |||
| /** Creates the filter's UI. | |||
| This can return 0 if you want a UI-less plugin. Otherwise, the component should | |||
| be created and set to the size you want it to be before returning it. | |||
| This can return 0 if you want a UI-less filter, in which case the host may create | |||
| a generic UI that lets the user twiddle the parameters directly. | |||
| If you do want to pass back a component, the component should be created and set to | |||
| the correct size before returning it. | |||
| Remember not to do anything silly like allowing your plugin to keep a pointer to | |||
| the component that gets created - this may be deleted later without any warning, so | |||
| that pointer could become a dangler. Use the getActiveEditor() method instead. | |||
| Remember not to do anything silly like allowing your filter to keep a pointer to | |||
| the component that gets created - it could be deleted later without any warning, which | |||
| would make your pointer into a dangler. Use the getActiveEditor() method instead. | |||
| The correct way to handle the connection between an editor component and its | |||
| plugin is to use something like a ChangeBroadcaster so that the editor can | |||
| filter is to use something like a ChangeBroadcaster so that the editor can | |||
| register itself as a listener, and be told when a change occurs. This lets them | |||
| safely unregister themselves when they are deleted. | |||
| Here are a few assumptions to bear in mind when writing an editor: | |||
| Here are a few things to bear in mind when writing an editor: | |||
| - Initially there won't be an editor, until the user opens one, or they might | |||
| not open one at all. Your plugin mustn't rely on it being there. | |||
| not open one at all. Your filter mustn't rely on it being there. | |||
| - An editor object may be deleted and a replacement one created again at any time. | |||
| - It's safe to assume that an editor will be deleted before its filter. | |||
| */ | |||
| @@ -356,24 +363,26 @@ public: | |||
| /** Returns the name of a particular parameter. */ | |||
| virtual const String JUCE_CALLTYPE getParameterName (int parameterIndex) = 0; | |||
| /** Called by the host to find out the value of one of the plugin's parameters. | |||
| /** Called by the host to find out the value of one of the filter's parameters. | |||
| The host will expect the value returned to be between 0 and 1.0. | |||
| This could be called quite frequently, so try to make your code efficient. | |||
| It's also likely to be called by non-UI threads, so the code in here should | |||
| be thread-aware. | |||
| */ | |||
| virtual float JUCE_CALLTYPE getParameter (int parameterIndex) = 0; | |||
| /** Returns the value of a parameter as a text string. */ | |||
| virtual const String JUCE_CALLTYPE getParameterText (int parameterIndex) = 0; | |||
| /** The host will call this method to change the value of one of the plugin's parameters. | |||
| /** The host will call this method to change the value of one of the filter's parameters. | |||
| The host may call this at any time, including during the audio processing | |||
| callback, so the plugin has to process this very fast and avoid blocking. | |||
| callback, so the filter has to process this very fast and avoid blocking. | |||
| If you want to set the value of a parameter internally, e.g. from your | |||
| plugin editor, then don't call this directly - instead, use the | |||
| editor component, then don't call this directly - instead, use the | |||
| setParameterNotifyingHost() method, which will also send a message to | |||
| the host telling it about the change. If the message isn't sent, the host | |||
| won't be able to automate your parameters properly. | |||
| @@ -383,7 +392,7 @@ public: | |||
| virtual void JUCE_CALLTYPE setParameter (int parameterIndex, | |||
| float newValue) = 0; | |||
| /** Your plugin can call this when it needs to change one of its parameters. | |||
| /** Your filter can call this when it needs to change one of its parameters. | |||
| This could happen when the editor or some other internal operation changes | |||
| a parameter. This method will call the setParameter() method to change the | |||
| @@ -399,7 +408,7 @@ public: | |||
| virtual bool isParameterAutomatable (int index) const; | |||
| //============================================================================== | |||
| /** Returns the number of preset programs the plugin supports. | |||
| /** Returns the number of preset programs the filter supports. | |||
| The value returned must be valid as soon as this object is created, and | |||
| must not change over its lifetime. | |||
| @@ -424,9 +433,9 @@ public: | |||
| virtual void JUCE_CALLTYPE changeProgramName (int index, const String& newName) = 0; | |||
| //============================================================================== | |||
| /** The host will call this method when it wants to save the plugin's internal state. | |||
| /** The host will call this method when it wants to save the filter's internal state. | |||
| This must copy any info about the plugin's state into the block of memory provided, | |||
| This must copy any info about the filter's state into the block of memory provided, | |||
| so that the host can store this and later restore it using setStateInformation(). | |||
| Note that there's also a getCurrentProgramStateInformation() method, which only | |||
| @@ -438,7 +447,7 @@ public: | |||
| */ | |||
| virtual void JUCE_CALLTYPE getStateInformation (JUCE_NAMESPACE::MemoryBlock& destData) = 0; | |||
| /** The host will call this method if it wants to save the state of just the plugin's | |||
| /** The host will call this method if it wants to save the state of just the filter's | |||
| current program. | |||
| Unlike getStateInformation, this should only return the current program's state. | |||
| @@ -451,7 +460,7 @@ public: | |||
| */ | |||
| virtual void JUCE_CALLTYPE getCurrentProgramStateInformation (JUCE_NAMESPACE::MemoryBlock& destData); | |||
| /** This must restore the plugin's state from a block of data previously created | |||
| /** This must restore the filter's state from a block of data previously created | |||
| using getStateInformation(). | |||
| Note that there's also a setCurrentProgramStateInformation() method, which tries | |||
| @@ -463,7 +472,7 @@ public: | |||
| */ | |||
| virtual void JUCE_CALLTYPE setStateInformation (const void* data, int sizeInBytes) = 0; | |||
| /** The host will call this method if it wants to restore the state of just the plugin's | |||
| /** The host will call this method if it wants to restore the state of just the filter's | |||
| current program. | |||
| Not all hosts support this, and if you don't implement it, the base class | |||
| @@ -504,7 +513,7 @@ protected: | |||
| //============================================================================== | |||
| /** Helper function that just converts an xml element into a binary blob. | |||
| Use this in your plugin's getStateInformation() method if you want to | |||
| Use this in your filter's getStateInformation() method if you want to | |||
| store its state as xml. | |||
| Then use getXmlFromBinary() to reverse this operation and retrieve the XML | |||
| @@ -524,7 +533,7 @@ protected: | |||
| /** @internal */ | |||
| double sampleRate; | |||
| /** @internal */ | |||
| int numInputChannels, numOutputChannels, blockSize; | |||
| int blockSize, numInputChannels, numOutputChannels; | |||
| private: | |||
| friend class JuceVSTWrapper; | |||
| @@ -544,7 +553,7 @@ private: | |||
| //============================================================================== | |||
| /** Somewhere in the code for an actual plugin, you need to implement this function | |||
| and make it create an instance of the plugin subclass that you're building. | |||
| and make it create an instance of the filter subclass that you're building. | |||
| */ | |||
| extern AudioFilterBase* JUCE_CALLTYPE createPluginFilter(); | |||
| @@ -42,7 +42,7 @@ class AudioFilterBase; | |||
| //============================================================================== | |||
| /** | |||
| Base class for the component that forms a plugin's GUI. | |||
| Base class for the component that forms a filter's GUI. | |||
| Derive your editor component from this class, and create an instance of it | |||
| by overriding the AudioFilterBase::createEditor() method. | |||
| @@ -148,12 +148,18 @@ public: | |||
| */ | |||
| const String& getTypeName() const throw() { return typeName; } | |||
| /** Returns the names of the available output channels on this device. */ | |||
| //============================================================================== | |||
| /** Returns the names of all the available output channels on this device. | |||
| To find out which of these are currently in use, call getActiveOutputChannels(). | |||
| */ | |||
| virtual const StringArray getOutputChannelNames() = 0; | |||
| /** Returns the names of the available input channels on this device. */ | |||
| /** Returns the names of all the available input channels on this device. | |||
| To find out which of these are currently in use, call getActiveInputChannels(). | |||
| */ | |||
| virtual const StringArray getInputChannelNames() = 0; | |||
| //============================================================================== | |||
| /** Returns the number of sample-rates this device supports. | |||
| To find out which rates are available on this device, use this method to | |||
| @@ -273,6 +279,18 @@ public: | |||
| */ | |||
| virtual int getCurrentBitDepth() = 0; | |||
| /** Returns a mask showing which of the available output channels are currently | |||
| enabled. | |||
| @see getOutputChannelNames | |||
| */ | |||
| virtual const BitArray getActiveOutputChannels() const = 0; | |||
| /** Returns a mask showing which of the available input channels are currently | |||
| enabled. | |||
| @see getInputChannelNames | |||
| */ | |||
| virtual const BitArray getActiveInputChannels() const = 0; | |||
| /** Returns the device's output latency. | |||
| This is the delay in samples between a callback getting a block of data, and | |||
| @@ -289,22 +289,7 @@ void TopLevelWindow::addToDesktop (int windowStyleFlags, void* nativeWindowToAtt | |||
| void TopLevelWindow::centreAroundComponent (Component* c, const int width, const int height) | |||
| { | |||
| if (c == 0) | |||
| { | |||
| if (c == 0) | |||
| c = TopLevelWindow::getActiveTopLevelWindow(); | |||
| if (c == 0) | |||
| { | |||
| c = Component::getCurrentlyFocusedComponent(); | |||
| if (c != 0) | |||
| c = c->getTopLevelComponent(); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| c = c->getTopLevelComponent(); | |||
| } | |||
| c = TopLevelWindow::getActiveTopLevelWindow(); | |||
| if (c == 0) | |||
| { | |||
| @@ -312,39 +297,21 @@ void TopLevelWindow::centreAroundComponent (Component* c, const int width, const | |||
| } | |||
| else | |||
| { | |||
| int cx = c->getWidth() / 2; | |||
| int cy = c->getHeight() / 2; | |||
| c->relativePositionToGlobal (cx, cy); | |||
| int x = cx - width / 2; | |||
| int y = cy - height / 2; | |||
| int x = (c->getWidth() - width) / 2; | |||
| int y = (c->getHeight() - height) / 2; | |||
| c->relativePositionToGlobal (x, y); | |||
| if (x <= cx | |||
| && y <= cy | |||
| && x + width >= cx + c->getWidth() | |||
| && y + height >= cy + c->getHeight()) | |||
| { | |||
| cx = 20; | |||
| cy = 20; | |||
| c->relativePositionToGlobal (cx, cy); | |||
| } | |||
| Rectangle parentArea (c->getParentMonitorArea()); | |||
| if (getParentComponent() != 0) | |||
| { | |||
| getParentComponent()->globalPositionToRelative (x, y); | |||
| setBounds (jlimit (0, jmax (0, getParentWidth() - width), x), | |||
| jlimit (0, jmax (0, getParentHeight() - height), y), | |||
| width, height); | |||
| parentArea.setBounds (0, 0, getParentWidth(), getParentHeight()); | |||
| } | |||
| else | |||
| { | |||
| const Rectangle screenArea (getParentMonitorArea()); | |||
| setBounds (jlimit (screenArea.getX(), jmax (screenArea.getX(), screenArea.getWidth() - width), x), | |||
| jlimit (screenArea.getY(), jmax (screenArea.getY(), screenArea.getHeight() - height), y), | |||
| width, height); | |||
| } | |||
| setBounds (parentArea.getX() + jlimit (0, jmax (0, parentArea.getWidth() - width), x), | |||
| parentArea.getY() + jlimit (0, jmax (0, parentArea.getHeight() - height), y), | |||
| width, height); | |||
| } | |||
| } | |||