From 62e4870bde080a24a4217777d5dc1ddf65e96321 Mon Sep 17 00:00:00 2001 From: jules Date: Wed, 15 Aug 2007 15:44:49 +0000 Subject: [PATCH] --- .../src/host/GraphEditorPanel.cpp | 1881 +++++++++-------- .../src/host/GraphEditorPanel.h | 1 + .../src/host/InternalFilters.cpp | 6 +- 3 files changed, 984 insertions(+), 904 deletions(-) diff --git a/extras/audio plugin host/src/host/GraphEditorPanel.cpp b/extras/audio plugin host/src/host/GraphEditorPanel.cpp index 31a5e8dc28..96b8919f9a 100644 --- a/extras/audio plugin host/src/host/GraphEditorPanel.cpp +++ b/extras/audio plugin host/src/host/GraphEditorPanel.cpp @@ -1,901 +1,980 @@ -/* - ============================================================================== - - 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 "GraphEditorPanel.h" -#include "InternalFilters.h" -#include "MainHostWindow.h" - - -//============================================================================== -class PinComponent : public Component -{ -public: - PinComponent (FilterGraph& graph_, - const uint32 filterID_, const int index_, const bool isInput_) - : graph (graph_), - filterID (filterID_), - index (index_), - isInput (isInput_) - { - setSize (16, 16); - } - - ~PinComponent() - { - } - - void paint (Graphics& g) - { - const float w = (float) getWidth(); - const float h = (float) getHeight(); - - Path p; - p.addEllipse (w * 0.25f, h * 0.25f, w * 0.5f, h * 0.5f); - - p.addRectangle (w * 0.4f, isInput ? (0.5f * h) : 0.0f, w * 0.2f, h * 0.5f); - - g.setColour (index == FilterGraph::midiChannelNumber ? Colours::red : Colours::green); - g.fillPath (p); - } - - void mouseDown (const MouseEvent& e) - { - getGraphPanel()->beginConnectorDrag (isInput ? 0 : filterID, - index, - isInput ? filterID : 0, - index, - e); - } - - void mouseDrag (const MouseEvent& e) - { - getGraphPanel()->dragConnector (e); - } - - void mouseUp (const MouseEvent& e) - { - getGraphPanel()->endDraggingConnector (e); - } - - juce_UseDebuggingNewOperator - - const uint32 filterID; - const int index; - const bool isInput; - -private: - FilterGraph& graph; - - PinComponent (const PinComponent&); - const PinComponent& operator= (const PinComponent&); - - GraphEditorPanel* getGraphPanel() const throw() - { - return findParentComponentOfClass ((GraphEditorPanel*) 0); - } -}; - -//============================================================================== -class FilterComponent : public Component -{ -public: - FilterComponent (FilterGraph& graph_, - const uint32 filterID_) - : graph (graph_), - filterID (filterID_), - numInputs (0), - numOutputs (0), - pinSize (16), - originalX (0), - originalY (0), - font (13.0f, Font::bold), - numIns (0), - numOuts (0) - { - shadow.setShadowProperties (2.5f, 0.5f, -1, 0); - setComponentEffect (&shadow); - - setSize (150, 60); - } - - ~FilterComponent() - { - deleteAllChildren(); - } - - void mouseDown (const MouseEvent& e) - { - originalX = 0; - originalY = 0; - relativePositionToGlobal (originalX, originalY); - - toFront (true); - - if (e.mods.isPopupMenu()) - { - PopupMenu m; - m.addItem (1, "Delete this filter"); - m.addItem (2, "Disconnect all pins"); - m.addSeparator(); - m.addItem (3, "Show plugin UI"); - m.addItem (4, "Show all parameters"); - - const int r = m.show(); - - if (r == 1) - { - graph.removeFilter (filterID); - return; - } - else if (r == 2) - { - graph.disconnectFilter (filterID); - } - else if (r == 3 || r == 4) - { - const FilterInGraph::Ptr f (graph.getFilterForUID (filterID)); - - if (f != 0) - f->showUI (r == 4); - } - } - } - - void mouseDrag (const MouseEvent& e) - { - if (! e.mods.isPopupMenu()) - { - int x = originalX + e.getDistanceFromDragStartX(); - int y = originalY + e.getDistanceFromDragStartY(); - - if (getParentComponent() != 0) - getParentComponent()->globalPositionToRelative (x, y); - - const FilterInGraph::Ptr f (graph.getFilterForUID (filterID)); - - if (f != 0) - { - f->setPosition ((x + getWidth() / 2) / (double) getParentWidth(), - (y + getHeight() / 2) / (double) getParentHeight()); - - getGraphPanel()->updateComponents(); - } - } - } - - void mouseUp (const MouseEvent& e) - { - if (e.mouseWasClicked() && e.getNumberOfClicks() == 2) - { - const FilterInGraph::Ptr f (graph.getFilterForUID (filterID)); - - if (f != 0) - f->showUI (false); - } - else if (! e.mouseWasClicked()) - { - graph.setChangedFlag (true); - } - } - - bool hitTest (int x, int y) - { - for (int i = getNumChildComponents(); --i >= 0;) - if (getChildComponent(i)->getBounds().contains (x, y)) - return true; - - return x >= 3 && x < getWidth() - 6 && y >= pinSize && y < getHeight() - pinSize; - } - - void paint (Graphics& g) - { - g.setColour (Colours::lightgrey); - - const int x = 4; - const int y = pinSize; - const int w = getWidth() - x * 2; - const int h = getHeight() - pinSize * 2; - - g.fillRect (x, y, w, h); - - g.setColour (Colours::black); - g.setFont (font); - g.drawFittedText (getName(), - x + 4, y + 2, w - 8, h - 4, - Justification::centred, 2); - - g.setColour (Colours::grey); - g.drawRect (x, y, w, h); - } - - void resized() - { - for (int i = 0; i < getNumChildComponents(); ++i) - { - PinComponent* const pc = dynamic_cast (getChildComponent(i)); - - if (pc != 0) - { - const int total = pc->isInput ? numIns : numOuts; - const int index = pc->index == FilterGraph::midiChannelNumber ? (total - 1) : pc->index; - - pc->setBounds (proportionOfWidth ((1 + index) / (total + 1.0f)) - pinSize / 2, - pc->isInput ? 0 : (getHeight() - pinSize), - pinSize, pinSize); - } - } - } - - void getPinPos (const int index, const bool isInput, float& x, float& y) - { - for (int i = 0; i < getNumChildComponents(); ++i) - { - PinComponent* const pc = dynamic_cast (getChildComponent(i)); - - if (pc != 0 && pc->index == index && isInput == pc->isInput) - { - x = getX() + pc->getX() + pc->getWidth() * 0.5f; - y = getY() + pc->getY() + pc->getHeight() * 0.5f; - break; - } - } - } - - void update() - { - const FilterInGraph* const f = graph.getFilterForUID (filterID); - - if (f == 0) - { - delete this; - return; - } - - numIns = f->filter->getNumInputChannels(); - if (f->filter->acceptsMidi()) - ++numIns; - - numOuts = f->filter->getNumOutputChannels(); - if (f->filter->producesMidi()) - ++numOuts; - - int w = 100; - int h = 60; - - w = jmax (w, (jmax (numIns, numOuts) + 1) * 20); - - const int textWidth = font.getStringWidth (f->filter->getName()); - w = jmax (w, 16 + jmin (textWidth, 300)); - if (textWidth > 300) - h = 100; - - setSize (w, h); - - setCentreRelative ((float) f->getX(), (float) f->getY()); - - if (numIns != numInputs || numOuts != numOutputs) - { - numInputs = numIns; - numOutputs = numOuts; - - deleteAllChildren(); - - int i; - for (i = 0; i < f->filter->getNumInputChannels(); ++i) - addAndMakeVisible (new PinComponent (graph, filterID, i, true)); - - if (f->filter->acceptsMidi()) - addAndMakeVisible (new PinComponent (graph, filterID, FilterGraph::midiChannelNumber, true)); - - for (i = 0; i < f->filter->getNumOutputChannels(); ++i) - addAndMakeVisible (new PinComponent (graph, filterID, i, false)); - - if (f->filter->producesMidi()) - addAndMakeVisible (new PinComponent (graph, filterID, FilterGraph::midiChannelNumber, false)); - - setName (f->filter->getName()); - - resized(); - } - } - - FilterGraph& graph; - const uint32 filterID; - int numInputs, numOutputs; - -private: - int pinSize; - int originalX, originalY; - int numIns, numOuts; - DropShadowEffect shadow; - Font font; - - GraphEditorPanel* getGraphPanel() const throw() - { - return findParentComponentOfClass ((GraphEditorPanel*) 0); - } - - FilterComponent (const FilterComponent&); - const FilterComponent& operator= (const FilterComponent&); -}; - -//============================================================================== -class ConnectorComponent : public Component -{ -public: - ConnectorComponent (FilterGraph& graph_) - : graph (graph_), - sourceFilterID (0), - destFilterID (0), - sourceFilterChannel (0), - destFilterChannel (0), - lastInputX (0), - lastInputY (0), - lastOutputX (0), - lastOutputY (0) - { - setAlwaysOnTop (true); - } - - ~ConnectorComponent() - { - } - - void setInput (const uint32 sourceFilterID_, const int sourceFilterChannel_) - { - if (sourceFilterID != sourceFilterID_ || sourceFilterChannel != sourceFilterChannel_) - { - sourceFilterID = sourceFilterID_; - sourceFilterChannel = sourceFilterChannel_; - update(); - } - } - - void setOutput (const uint32 destFilterID_, const int destFilterChannel_) - { - if (destFilterID != destFilterID_ || destFilterChannel != destFilterChannel_) - { - destFilterID = destFilterID_; - destFilterChannel = destFilterChannel_; - update(); - } - } - - void dragStart (int x, int y) - { - lastInputX = (float) x; - lastInputY = (float) y; - resizeToFit(); - } - - void dragEnd (int x, int y) - { - lastOutputX = (float) x; - lastOutputY = (float) y; - resizeToFit(); - } - - void update() - { - float x1, y1, x2, y2; - getPoints (x1, y1, x2, y2); - - if (lastInputX != x1 - || lastInputY != y1 - || lastOutputX != x2 - || lastOutputY != y2) - { - resizeToFit(); - } - } - - void resizeToFit() - { - float x1, y1, x2, y2; - getPoints (x1, y1, x2, y2); - - const Rectangle newBounds ((int) jmin (x1, x2) - 4, - (int) jmin (y1, y2) - 4, - (int) abs (x1 - x2) + 8, - (int) abs (y1 - y2) + 8); - - if (newBounds != getBounds()) - setBounds (newBounds); - else - resized(); - - repaint(); - } - - void getPoints (float& x1, float& y1, float& x2, float& y2) const - { - x1 = lastInputX; - y1 = lastInputY; - x2 = lastOutputX; - y2 = lastOutputY; - - GraphEditorPanel* const hostPanel = getGraphPanel(); - - if (hostPanel != 0) - { - FilterComponent* srcFilterComp = hostPanel->getComponentForFilter (sourceFilterID); - - if (srcFilterComp != 0) - srcFilterComp->getPinPos (sourceFilterChannel, false, x1, y1); - - FilterComponent* dstFilterComp = hostPanel->getComponentForFilter (destFilterID); - - if (dstFilterComp != 0) - dstFilterComp->getPinPos (destFilterChannel, true, x2, y2); - } - } - - void paint (Graphics& g) - { - if (sourceFilterChannel == FilterGraph::midiChannelNumber - || destFilterChannel == FilterGraph::midiChannelNumber) - { - g.setColour (Colours::red); - } - else - { - g.setColour (Colours::green); - } - - g.fillPath (linePath); - } - - bool hitTest (int x, int y) - { - if (hitPath.contains ((float) x, (float) y)) - { - double distanceFromStart, distanceFromEnd; - getDistancesFromEnds (x, y, distanceFromStart, distanceFromEnd); - - // avoid clicking the connector when over a pin - return distanceFromStart > 7.0 && distanceFromEnd > 7.0; - } - - return false; - } - - void mouseDown (const MouseEvent&) - { - dragging = false; - } - - void mouseDrag (const MouseEvent& e) - { - if ((! dragging) && ! e.mouseWasClicked()) - { - dragging = true; - - graph.removeConnection (sourceFilterID, sourceFilterChannel, destFilterID, destFilterChannel); - - double distanceFromStart, distanceFromEnd; - getDistancesFromEnds (e.x, e.y, distanceFromStart, distanceFromEnd); - const bool isNearerSource = (distanceFromStart < distanceFromEnd); - - getGraphPanel()->beginConnectorDrag (isNearerSource ? 0 : sourceFilterID, - sourceFilterChannel, - isNearerSource ? destFilterID : 0, - destFilterChannel, - e); - } - else if (dragging) - { - getGraphPanel()->dragConnector (e); - } - } - - void mouseUp (const MouseEvent& e) - { - if (dragging) - getGraphPanel()->endDraggingConnector (e); - } - - void resized() - { - float x1, y1, x2, y2; - getPoints (x1, y1, x2, y2); - - lastInputX = x1; - lastInputY = y1; - lastOutputX = x2; - lastOutputY = y2; - - x1 -= getX(); - y1 -= getY(); - x2 -= getX(); - y2 -= getY(); - - linePath.clear(); - linePath.startNewSubPath (x1, y1); - linePath.cubicTo (x1, y1 + (y2 - y1) * 0.33f, - x2, y1 + (y2 - y1) * 0.66f, - x2, y2); - - PathStrokeType wideStroke (8.0f); - wideStroke.createStrokedPath (hitPath, linePath); - - PathStrokeType stroke (2.5f); - stroke.createStrokedPath (linePath, linePath); - - const float arrowW = 5.0f; - const float arrowL = 4.0f; - - Path arrow; - arrow.addTriangle (-arrowL, arrowW, - -arrowL, -arrowW, - arrowL, 0.0f); - - arrow.applyTransform (AffineTransform::identity - .rotated (float_Pi * 0.5f - (float) atan2 (x2 - x1, y2 - y1)) - .translated ((x1 + x2) * 0.5f, - (y1 + y2) * 0.5f)); - - linePath.addPath (arrow); - linePath.setUsingNonZeroWinding (true); - } - - juce_UseDebuggingNewOperator - - uint32 sourceFilterID, destFilterID; - int sourceFilterChannel, destFilterChannel; - -private: - FilterGraph& graph; - float lastInputX, lastInputY, lastOutputX, lastOutputY; - Path linePath, hitPath; - bool dragging; - - GraphEditorPanel* getGraphPanel() const throw() - { - return findParentComponentOfClass ((GraphEditorPanel*) 0); - } - - void getDistancesFromEnds (int x, int y, double& distanceFromStart, double& distanceFromEnd) const - { - float x1, y1, x2, y2; - getPoints (x1, y1, x2, y2); - - distanceFromStart = juce_hypot (x - (x1 - getX()), y - (y1 - getY())); - distanceFromEnd = juce_hypot (x - (x2 - getX()), y - (y2 - getY())); - } - - ConnectorComponent (const ConnectorComponent&); - const ConnectorComponent& operator= (const ConnectorComponent&); -}; - - -//============================================================================== -GraphEditorPanel::GraphEditorPanel (FilterGraph& graph_) - : graph (graph_), - draggingConnector (0) -{ - graph.addChangeListener (this); - setOpaque (true); -} - -GraphEditorPanel::~GraphEditorPanel() -{ - graph.removeChangeListener (this); - deleteAndZero (draggingConnector); - deleteAllChildren(); -} - -void GraphEditorPanel::paint (Graphics& g) -{ - g.fillAll (Colours::white); -} - -void GraphEditorPanel::mouseDown (const MouseEvent& e) -{ - if (e.mods.isPopupMenu()) - { - PopupMenu m; - - MainHostWindow* const mainWindow = findParentComponentOfClass ((MainHostWindow*) 0); - - if (mainWindow != 0) - { - mainWindow->addPluginsToMenu (m); - - const int r = m.show(); - - createNewPlugin (mainWindow->getChosenType (r), e.x, e.y); - } - } -} - -void GraphEditorPanel::createNewPlugin (const PluginDescription* desc, int x, int y) -{ - graph.addFilter (desc, x / (double) getWidth(), y / (double) getHeight()); -} - -FilterComponent* GraphEditorPanel::getComponentForFilter (const uint32 filterID) const -{ - for (int i = getNumChildComponents(); --i >= 0;) - { - FilterComponent* const fc = dynamic_cast (getChildComponent (i)); - - if (fc != 0 && fc->filterID == filterID) - return fc; - } - - return 0; -} - -ConnectorComponent* GraphEditorPanel::getComponentForConnection (const FilterConnection& conn) const -{ - for (int i = getNumChildComponents(); --i >= 0;) - { - ConnectorComponent* const c = dynamic_cast (getChildComponent (i)); - - if (c != 0 - && c->sourceFilterID == conn.sourceFilterID - && c->destFilterID == conn.destFilterID - && c->sourceFilterChannel == conn.sourceChannel - && c->destFilterChannel == conn.destChannel) - { - return c; - } - } - - return 0; -} - -PinComponent* GraphEditorPanel::findPinAt (const int x, const int y) const -{ - for (int i = getNumChildComponents(); --i >= 0;) - { - FilterComponent* const fc = dynamic_cast (getChildComponent (i)); - - if (fc != 0) - { - PinComponent* const pin - = dynamic_cast (fc->getComponentAt (x - fc->getX(), - y - fc->getY())); - - if (pin != 0) - return pin; - } - } - - return 0; -} - -void GraphEditorPanel::resized() -{ - updateComponents(); -} - -void GraphEditorPanel::changeListenerCallback (void*) -{ - updateComponents(); -} - -void GraphEditorPanel::updateComponents() -{ - int i; - for (i = getNumChildComponents(); --i >= 0;) - { - FilterComponent* const fc = dynamic_cast (getChildComponent (i)); - - if (fc != 0) - fc->update(); - } - - for (i = getNumChildComponents(); --i >= 0;) - { - ConnectorComponent* const cc = dynamic_cast (getChildComponent (i)); - - if (cc != 0 && cc != draggingConnector) - { - if (graph.getConnectionBetween (cc->sourceFilterID, cc->sourceFilterChannel, - cc->destFilterID, cc->destFilterChannel) == 0) - { - delete cc; - } - else - { - cc->update(); - } - } - } - - for (i = graph.getNumFilters(); --i >= 0;) - { - const FilterInGraph* const f = graph.getFilter (i); - - if (getComponentForFilter (f->uid) == 0) - { - FilterComponent* const comp = new FilterComponent (graph, f->uid); - addAndMakeVisible (comp); - comp->update(); - } - } - - for (i = graph.getNumConnections(); --i >= 0;) - { - FilterConnection* const c = graph.getConnection (i); - - if (getComponentForConnection (*c) == 0) - { - ConnectorComponent* const comp = new ConnectorComponent (graph); - addAndMakeVisible (comp); - - comp->setInput (c->sourceFilterID, c->sourceChannel); - comp->setOutput (c->destFilterID, c->destChannel); - } - } -} - -void GraphEditorPanel::beginConnectorDrag (const uint32 sourceFilterID, const int sourceFilterChannel, - const uint32 destFilterID, const int destFilterChannel, - const MouseEvent& e) -{ - delete draggingConnector; - draggingConnector = dynamic_cast (e.originalComponent); - - if (draggingConnector == 0) - draggingConnector = new ConnectorComponent (graph); - - draggingConnector->setInput (sourceFilterID, sourceFilterChannel); - draggingConnector->setOutput (destFilterID, destFilterChannel); - - addAndMakeVisible (draggingConnector); - draggingConnector->toFront (false); - - dragConnector (e); -} - -void GraphEditorPanel::dragConnector (const MouseEvent& e) -{ - const MouseEvent e2 (e.getEventRelativeTo (this)); - - if (draggingConnector != 0) - { - int x = e2.x; - int y = e2.y; - - PinComponent* const pin = findPinAt (x, y); - - if (pin != 0) - { - uint32 srcFilter = draggingConnector->sourceFilterID; - int srcChannel = draggingConnector->sourceFilterChannel; - uint32 dstFilter = draggingConnector->destFilterID; - int dstChannel = draggingConnector->destFilterChannel; - - if (srcFilter == 0 && ! pin->isInput) - { - srcFilter = pin->filterID; - srcChannel = pin->index; - } - else if (dstFilter == 0 && pin->isInput) - { - dstFilter = pin->filterID; - dstChannel = pin->index; - } - - if (graph.canConnect (srcFilter, srcChannel, dstFilter, dstChannel)) - { - x = pin->getParentComponent()->getX() + pin->getX() + pin->getWidth() / 2; - y = pin->getParentComponent()->getY() + pin->getY() + pin->getHeight() / 2; - } - } - - if (draggingConnector->sourceFilterID == 0) - draggingConnector->dragStart (x, y); - else - draggingConnector->dragEnd (x, y); - } -} - -void GraphEditorPanel::endDraggingConnector (const MouseEvent& e) -{ - if (draggingConnector == 0) - return; - - const MouseEvent e2 (e.getEventRelativeTo (this)); - - uint32 srcFilter = draggingConnector->sourceFilterID; - int srcChannel = draggingConnector->sourceFilterChannel; - uint32 dstFilter = draggingConnector->destFilterID; - int dstChannel = draggingConnector->destFilterChannel; - - deleteAndZero (draggingConnector); - - PinComponent* const pin = findPinAt (e2.x, e2.y); - - if (pin != 0) - { - if (srcFilter == 0) - { - if (pin->isInput) - return; - - srcFilter = pin->filterID; - srcChannel = pin->index; - } - else - { - if (! pin->isInput) - return; - - dstFilter = pin->filterID; - dstChannel = pin->index; - } - - graph.addConnection (srcFilter, srcChannel, dstFilter, dstChannel); - } -} - - -//============================================================================== -GraphDocumentComponent::GraphDocumentComponent (AudioDeviceManager* deviceManager_) - : deviceManager (deviceManager_) -{ - addAndMakeVisible (graphPanel = new GraphEditorPanel (graph)); - - graphPlayer = new FilterGraphPlayer (graph); - - addAndMakeVisible (keyboardComp = new MidiKeyboardComponent (graphPlayer->keyState, - MidiKeyboardComponent::horizontalKeyboard)); - - graphPlayer->setAudioDeviceManager (deviceManager); - - graphPanel->updateComponents(); -} - -GraphDocumentComponent::~GraphDocumentComponent() -{ - deleteAllChildren(); - - graphPlayer->setAudioDeviceManager (0); - deleteAndZero (graphPlayer); - - graph.clear(); -} - -void GraphDocumentComponent::resized() -{ - const int keysHeight = 60; - graphPanel->setBounds (0, 0, getWidth(), getHeight() - keysHeight); - keyboardComp->setBounds (0, getHeight() - keysHeight, getWidth(), keysHeight); -} - -void GraphDocumentComponent::createNewPlugin (const PluginDescription* desc, int x, int y) -{ - graphPanel->createNewPlugin (desc, x, y); -} +/* + ============================================================================== + + 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 "GraphEditorPanel.h" +#include "InternalFilters.h" +#include "MainHostWindow.h" + + +//============================================================================== +class PinComponent : public Component, + public SettableTooltipClient +{ +public: + PinComponent (FilterGraph& graph_, + const uint32 filterID_, const int index_, const bool isInput_) + : graph (graph_), + filterID (filterID_), + index (index_), + isInput (isInput_) + { + FilterInGraph* const f = graph.getFilterForUID (filterID_); + + if (f != 0) + { + String tip; + + if (isInput) + tip = f->filter->getInputChannelName (index_); + else + tip = f->filter->getOutputChannelName (index_); + + if (tip.isEmpty()) + { + if (index_ == FilterGraph::midiChannelNumber) + tip = isInput ? "Midi Input" : "Midi Output"; + else + tip = (isInput ? "Input " : "Output ") + String (index_ + 1); + } + + setTooltip (tip); + } + + setSize (16, 16); + } + + ~PinComponent() + { + } + + void paint (Graphics& g) + { + const float w = (float) getWidth(); + const float h = (float) getHeight(); + + Path p; + p.addEllipse (w * 0.25f, h * 0.25f, w * 0.5f, h * 0.5f); + + p.addRectangle (w * 0.4f, isInput ? (0.5f * h) : 0.0f, w * 0.2f, h * 0.5f); + + g.setColour (index == FilterGraph::midiChannelNumber ? Colours::red : Colours::green); + g.fillPath (p); + } + + void mouseDown (const MouseEvent& e) + { + getGraphPanel()->beginConnectorDrag (isInput ? 0 : filterID, + index, + isInput ? filterID : 0, + index, + e); + } + + void mouseDrag (const MouseEvent& e) + { + getGraphPanel()->dragConnector (e); + } + + void mouseUp (const MouseEvent& e) + { + getGraphPanel()->endDraggingConnector (e); + } + + juce_UseDebuggingNewOperator + + const uint32 filterID; + const int index; + const bool isInput; + +private: + FilterGraph& graph; + + PinComponent (const PinComponent&); + const PinComponent& operator= (const PinComponent&); + + GraphEditorPanel* getGraphPanel() const throw() + { + return findParentComponentOfClass ((GraphEditorPanel*) 0); + } +}; + +//============================================================================== +class FilterComponent : public Component +{ +public: + FilterComponent (FilterGraph& graph_, + const uint32 filterID_) + : graph (graph_), + filterID (filterID_), + numInputs (0), + numOutputs (0), + pinSize (16), + originalX (0), + originalY (0), + font (13.0f, Font::bold), + numIns (0), + numOuts (0) + { + shadow.setShadowProperties (2.5f, 0.5f, -1, 0); + setComponentEffect (&shadow); + + setSize (150, 60); + } + + ~FilterComponent() + { + deleteAllChildren(); + } + + void mouseDown (const MouseEvent& e) + { + originalX = 0; + originalY = 0; + relativePositionToGlobal (originalX, originalY); + + toFront (true); + + if (e.mods.isPopupMenu()) + { + PopupMenu m; + m.addItem (1, "Delete this filter"); + m.addItem (2, "Disconnect all pins"); + m.addSeparator(); + m.addItem (3, "Show plugin UI"); + m.addItem (4, "Show all parameters"); + + const int r = m.show(); + + if (r == 1) + { + graph.removeFilter (filterID); + return; + } + else if (r == 2) + { + graph.disconnectFilter (filterID); + } + else if (r == 3 || r == 4) + { + const FilterInGraph::Ptr f (graph.getFilterForUID (filterID)); + + if (f != 0) + f->showUI (r == 4); + } + } + } + + void mouseDrag (const MouseEvent& e) + { + if (! e.mods.isPopupMenu()) + { + int x = originalX + e.getDistanceFromDragStartX(); + int y = originalY + e.getDistanceFromDragStartY(); + + if (getParentComponent() != 0) + getParentComponent()->globalPositionToRelative (x, y); + + const FilterInGraph::Ptr f (graph.getFilterForUID (filterID)); + + if (f != 0) + { + f->setPosition ((x + getWidth() / 2) / (double) getParentWidth(), + (y + getHeight() / 2) / (double) getParentHeight()); + + getGraphPanel()->updateComponents(); + } + } + } + + void mouseUp (const MouseEvent& e) + { + if (e.mouseWasClicked() && e.getNumberOfClicks() == 2) + { + const FilterInGraph::Ptr f (graph.getFilterForUID (filterID)); + + if (f != 0) + f->showUI (false); + } + else if (! e.mouseWasClicked()) + { + graph.setChangedFlag (true); + } + } + + bool hitTest (int x, int y) + { + for (int i = getNumChildComponents(); --i >= 0;) + if (getChildComponent(i)->getBounds().contains (x, y)) + return true; + + return x >= 3 && x < getWidth() - 6 && y >= pinSize && y < getHeight() - pinSize; + } + + void paint (Graphics& g) + { + g.setColour (Colours::lightgrey); + + const int x = 4; + const int y = pinSize; + const int w = getWidth() - x * 2; + const int h = getHeight() - pinSize * 2; + + g.fillRect (x, y, w, h); + + g.setColour (Colours::black); + g.setFont (font); + g.drawFittedText (getName(), + x + 4, y + 2, w - 8, h - 4, + Justification::centred, 2); + + g.setColour (Colours::grey); + g.drawRect (x, y, w, h); + } + + void resized() + { + for (int i = 0; i < getNumChildComponents(); ++i) + { + PinComponent* const pc = dynamic_cast (getChildComponent(i)); + + if (pc != 0) + { + const int total = pc->isInput ? numIns : numOuts; + const int index = pc->index == FilterGraph::midiChannelNumber ? (total - 1) : pc->index; + + pc->setBounds (proportionOfWidth ((1 + index) / (total + 1.0f)) - pinSize / 2, + pc->isInput ? 0 : (getHeight() - pinSize), + pinSize, pinSize); + } + } + } + + void getPinPos (const int index, const bool isInput, float& x, float& y) + { + for (int i = 0; i < getNumChildComponents(); ++i) + { + PinComponent* const pc = dynamic_cast (getChildComponent(i)); + + if (pc != 0 && pc->index == index && isInput == pc->isInput) + { + x = getX() + pc->getX() + pc->getWidth() * 0.5f; + y = getY() + pc->getY() + pc->getHeight() * 0.5f; + break; + } + } + } + + void update() + { + const FilterInGraph* const f = graph.getFilterForUID (filterID); + + if (f == 0) + { + delete this; + return; + } + + numIns = f->filter->getNumInputChannels(); + if (f->filter->acceptsMidi()) + ++numIns; + + numOuts = f->filter->getNumOutputChannels(); + if (f->filter->producesMidi()) + ++numOuts; + + int w = 100; + int h = 60; + + w = jmax (w, (jmax (numIns, numOuts) + 1) * 20); + + const int textWidth = font.getStringWidth (f->filter->getName()); + w = jmax (w, 16 + jmin (textWidth, 300)); + if (textWidth > 300) + h = 100; + + setSize (w, h); + + setCentreRelative ((float) f->getX(), (float) f->getY()); + + if (numIns != numInputs || numOuts != numOutputs) + { + numInputs = numIns; + numOutputs = numOuts; + + deleteAllChildren(); + + int i; + for (i = 0; i < f->filter->getNumInputChannels(); ++i) + addAndMakeVisible (new PinComponent (graph, filterID, i, true)); + + if (f->filter->acceptsMidi()) + addAndMakeVisible (new PinComponent (graph, filterID, FilterGraph::midiChannelNumber, true)); + + for (i = 0; i < f->filter->getNumOutputChannels(); ++i) + addAndMakeVisible (new PinComponent (graph, filterID, i, false)); + + if (f->filter->producesMidi()) + addAndMakeVisible (new PinComponent (graph, filterID, FilterGraph::midiChannelNumber, false)); + + setName (f->filter->getName()); + + resized(); + } + } + + FilterGraph& graph; + const uint32 filterID; + int numInputs, numOutputs; + +private: + int pinSize; + int originalX, originalY; + int numIns, numOuts; + DropShadowEffect shadow; + Font font; + + GraphEditorPanel* getGraphPanel() const throw() + { + return findParentComponentOfClass ((GraphEditorPanel*) 0); + } + + FilterComponent (const FilterComponent&); + const FilterComponent& operator= (const FilterComponent&); +}; + +//============================================================================== +class ConnectorComponent : public Component, + public SettableTooltipClient +{ +public: + ConnectorComponent (FilterGraph& graph_) + : graph (graph_), + sourceFilterID (0), + destFilterID (0), + sourceFilterChannel (0), + destFilterChannel (0), + lastInputX (0), + lastInputY (0), + lastOutputX (0), + lastOutputY (0) + { + setAlwaysOnTop (true); + } + + ~ConnectorComponent() + { + } + + void setInput (const uint32 sourceFilterID_, const int sourceFilterChannel_) + { + if (sourceFilterID != sourceFilterID_ || sourceFilterChannel != sourceFilterChannel_) + { + sourceFilterID = sourceFilterID_; + sourceFilterChannel = sourceFilterChannel_; + update(); + } + } + + void setOutput (const uint32 destFilterID_, const int destFilterChannel_) + { + if (destFilterID != destFilterID_ || destFilterChannel != destFilterChannel_) + { + destFilterID = destFilterID_; + destFilterChannel = destFilterChannel_; + update(); + } + } + + void dragStart (int x, int y) + { + lastInputX = (float) x; + lastInputY = (float) y; + resizeToFit(); + } + + void dragEnd (int x, int y) + { + lastOutputX = (float) x; + lastOutputY = (float) y; + resizeToFit(); + } + + void update() + { + float x1, y1, x2, y2; + getPoints (x1, y1, x2, y2); + + if (lastInputX != x1 + || lastInputY != y1 + || lastOutputX != x2 + || lastOutputY != y2) + { + resizeToFit(); + } + } + + void resizeToFit() + { + float x1, y1, x2, y2; + getPoints (x1, y1, x2, y2); + + const Rectangle newBounds ((int) jmin (x1, x2) - 4, + (int) jmin (y1, y2) - 4, + (int) abs (x1 - x2) + 8, + (int) abs (y1 - y2) + 8); + + if (newBounds != getBounds()) + setBounds (newBounds); + else + resized(); + + repaint(); + } + + void getPoints (float& x1, float& y1, float& x2, float& y2) const + { + x1 = lastInputX; + y1 = lastInputY; + x2 = lastOutputX; + y2 = lastOutputY; + + GraphEditorPanel* const hostPanel = getGraphPanel(); + + if (hostPanel != 0) + { + FilterComponent* srcFilterComp = hostPanel->getComponentForFilter (sourceFilterID); + + if (srcFilterComp != 0) + srcFilterComp->getPinPos (sourceFilterChannel, false, x1, y1); + + FilterComponent* dstFilterComp = hostPanel->getComponentForFilter (destFilterID); + + if (dstFilterComp != 0) + dstFilterComp->getPinPos (destFilterChannel, true, x2, y2); + } + } + + void paint (Graphics& g) + { + if (sourceFilterChannel == FilterGraph::midiChannelNumber + || destFilterChannel == FilterGraph::midiChannelNumber) + { + g.setColour (Colours::red); + } + else + { + g.setColour (Colours::green); + } + + g.fillPath (linePath); + } + + bool hitTest (int x, int y) + { + if (hitPath.contains ((float) x, (float) y)) + { + double distanceFromStart, distanceFromEnd; + getDistancesFromEnds (x, y, distanceFromStart, distanceFromEnd); + + // avoid clicking the connector when over a pin + return distanceFromStart > 7.0 && distanceFromEnd > 7.0; + } + + return false; + } + + void mouseDown (const MouseEvent&) + { + dragging = false; + } + + void mouseDrag (const MouseEvent& e) + { + if ((! dragging) && ! e.mouseWasClicked()) + { + dragging = true; + + graph.removeConnection (sourceFilterID, sourceFilterChannel, destFilterID, destFilterChannel); + + double distanceFromStart, distanceFromEnd; + getDistancesFromEnds (e.x, e.y, distanceFromStart, distanceFromEnd); + const bool isNearerSource = (distanceFromStart < distanceFromEnd); + + getGraphPanel()->beginConnectorDrag (isNearerSource ? 0 : sourceFilterID, + sourceFilterChannel, + isNearerSource ? destFilterID : 0, + destFilterChannel, + e); + } + else if (dragging) + { + getGraphPanel()->dragConnector (e); + } + } + + void mouseUp (const MouseEvent& e) + { + if (dragging) + getGraphPanel()->endDraggingConnector (e); + } + + void resized() + { + float x1, y1, x2, y2; + getPoints (x1, y1, x2, y2); + + lastInputX = x1; + lastInputY = y1; + lastOutputX = x2; + lastOutputY = y2; + + x1 -= getX(); + y1 -= getY(); + x2 -= getX(); + y2 -= getY(); + + linePath.clear(); + linePath.startNewSubPath (x1, y1); + linePath.cubicTo (x1, y1 + (y2 - y1) * 0.33f, + x2, y1 + (y2 - y1) * 0.66f, + x2, y2); + + PathStrokeType wideStroke (8.0f); + wideStroke.createStrokedPath (hitPath, linePath); + + PathStrokeType stroke (2.5f); + stroke.createStrokedPath (linePath, linePath); + + const float arrowW = 5.0f; + const float arrowL = 4.0f; + + Path arrow; + arrow.addTriangle (-arrowL, arrowW, + -arrowL, -arrowW, + arrowL, 0.0f); + + arrow.applyTransform (AffineTransform::identity + .rotated (float_Pi * 0.5f - (float) atan2 (x2 - x1, y2 - y1)) + .translated ((x1 + x2) * 0.5f, + (y1 + y2) * 0.5f)); + + linePath.addPath (arrow); + linePath.setUsingNonZeroWinding (true); + } + + juce_UseDebuggingNewOperator + + uint32 sourceFilterID, destFilterID; + int sourceFilterChannel, destFilterChannel; + +private: + FilterGraph& graph; + float lastInputX, lastInputY, lastOutputX, lastOutputY; + Path linePath, hitPath; + bool dragging; + + GraphEditorPanel* getGraphPanel() const throw() + { + return findParentComponentOfClass ((GraphEditorPanel*) 0); + } + + void getDistancesFromEnds (int x, int y, double& distanceFromStart, double& distanceFromEnd) const + { + float x1, y1, x2, y2; + getPoints (x1, y1, x2, y2); + + distanceFromStart = juce_hypot (x - (x1 - getX()), y - (y1 - getY())); + distanceFromEnd = juce_hypot (x - (x2 - getX()), y - (y2 - getY())); + } + + ConnectorComponent (const ConnectorComponent&); + const ConnectorComponent& operator= (const ConnectorComponent&); +}; + + +//============================================================================== +GraphEditorPanel::GraphEditorPanel (FilterGraph& graph_) + : graph (graph_), + draggingConnector (0) +{ + graph.addChangeListener (this); + setOpaque (true); +} + +GraphEditorPanel::~GraphEditorPanel() +{ + graph.removeChangeListener (this); + deleteAndZero (draggingConnector); + deleteAllChildren(); +} + +void GraphEditorPanel::paint (Graphics& g) +{ + g.fillAll (Colours::white); +} + +void GraphEditorPanel::mouseDown (const MouseEvent& e) +{ + if (e.mods.isPopupMenu()) + { + PopupMenu m; + + MainHostWindow* const mainWindow = findParentComponentOfClass ((MainHostWindow*) 0); + + if (mainWindow != 0) + { + mainWindow->addPluginsToMenu (m); + + const int r = m.show(); + + createNewPlugin (mainWindow->getChosenType (r), e.x, e.y); + } + } +} + +void GraphEditorPanel::createNewPlugin (const PluginDescription* desc, int x, int y) +{ + graph.addFilter (desc, x / (double) getWidth(), y / (double) getHeight()); +} + +FilterComponent* GraphEditorPanel::getComponentForFilter (const uint32 filterID) const +{ + for (int i = getNumChildComponents(); --i >= 0;) + { + FilterComponent* const fc = dynamic_cast (getChildComponent (i)); + + if (fc != 0 && fc->filterID == filterID) + return fc; + } + + return 0; +} + +ConnectorComponent* GraphEditorPanel::getComponentForConnection (const FilterConnection& conn) const +{ + for (int i = getNumChildComponents(); --i >= 0;) + { + ConnectorComponent* const c = dynamic_cast (getChildComponent (i)); + + if (c != 0 + && c->sourceFilterID == conn.sourceFilterID + && c->destFilterID == conn.destFilterID + && c->sourceFilterChannel == conn.sourceChannel + && c->destFilterChannel == conn.destChannel) + { + return c; + } + } + + return 0; +} + +PinComponent* GraphEditorPanel::findPinAt (const int x, const int y) const +{ + for (int i = getNumChildComponents(); --i >= 0;) + { + FilterComponent* const fc = dynamic_cast (getChildComponent (i)); + + if (fc != 0) + { + PinComponent* const pin + = dynamic_cast (fc->getComponentAt (x - fc->getX(), + y - fc->getY())); + + if (pin != 0) + return pin; + } + } + + return 0; +} + +void GraphEditorPanel::resized() +{ + updateComponents(); +} + +void GraphEditorPanel::changeListenerCallback (void*) +{ + updateComponents(); +} + +void GraphEditorPanel::updateComponents() +{ + int i; + for (i = getNumChildComponents(); --i >= 0;) + { + FilterComponent* const fc = dynamic_cast (getChildComponent (i)); + + if (fc != 0) + fc->update(); + } + + for (i = getNumChildComponents(); --i >= 0;) + { + ConnectorComponent* const cc = dynamic_cast (getChildComponent (i)); + + if (cc != 0 && cc != draggingConnector) + { + if (graph.getConnectionBetween (cc->sourceFilterID, cc->sourceFilterChannel, + cc->destFilterID, cc->destFilterChannel) == 0) + { + delete cc; + } + else + { + cc->update(); + } + } + } + + for (i = graph.getNumFilters(); --i >= 0;) + { + const FilterInGraph* const f = graph.getFilter (i); + + if (getComponentForFilter (f->uid) == 0) + { + FilterComponent* const comp = new FilterComponent (graph, f->uid); + addAndMakeVisible (comp); + comp->update(); + } + } + + for (i = graph.getNumConnections(); --i >= 0;) + { + FilterConnection* const c = graph.getConnection (i); + + if (getComponentForConnection (*c) == 0) + { + ConnectorComponent* const comp = new ConnectorComponent (graph); + addAndMakeVisible (comp); + + comp->setInput (c->sourceFilterID, c->sourceChannel); + comp->setOutput (c->destFilterID, c->destChannel); + } + } +} + +void GraphEditorPanel::beginConnectorDrag (const uint32 sourceFilterID, const int sourceFilterChannel, + const uint32 destFilterID, const int destFilterChannel, + const MouseEvent& e) +{ + delete draggingConnector; + draggingConnector = dynamic_cast (e.originalComponent); + + if (draggingConnector == 0) + draggingConnector = new ConnectorComponent (graph); + + draggingConnector->setInput (sourceFilterID, sourceFilterChannel); + draggingConnector->setOutput (destFilterID, destFilterChannel); + + addAndMakeVisible (draggingConnector); + draggingConnector->toFront (false); + + dragConnector (e); +} + +void GraphEditorPanel::dragConnector (const MouseEvent& e) +{ + const MouseEvent e2 (e.getEventRelativeTo (this)); + + if (draggingConnector != 0) + { + draggingConnector->setTooltip (String::empty); + + int x = e2.x; + int y = e2.y; + + PinComponent* const pin = findPinAt (x, y); + + if (pin != 0) + { + uint32 srcFilter = draggingConnector->sourceFilterID; + int srcChannel = draggingConnector->sourceFilterChannel; + uint32 dstFilter = draggingConnector->destFilterID; + int dstChannel = draggingConnector->destFilterChannel; + + if (srcFilter == 0 && ! pin->isInput) + { + srcFilter = pin->filterID; + srcChannel = pin->index; + } + else if (dstFilter == 0 && pin->isInput) + { + dstFilter = pin->filterID; + dstChannel = pin->index; + } + + if (graph.canConnect (srcFilter, srcChannel, dstFilter, dstChannel)) + { + x = pin->getParentComponent()->getX() + pin->getX() + pin->getWidth() / 2; + y = pin->getParentComponent()->getY() + pin->getY() + pin->getHeight() / 2; + + draggingConnector->setTooltip (pin->getTooltip()); + } + } + + if (draggingConnector->sourceFilterID == 0) + draggingConnector->dragStart (x, y); + else + draggingConnector->dragEnd (x, y); + } +} + +void GraphEditorPanel::endDraggingConnector (const MouseEvent& e) +{ + if (draggingConnector == 0) + return; + + draggingConnector->setTooltip (String::empty); + + const MouseEvent e2 (e.getEventRelativeTo (this)); + + uint32 srcFilter = draggingConnector->sourceFilterID; + int srcChannel = draggingConnector->sourceFilterChannel; + uint32 dstFilter = draggingConnector->destFilterID; + int dstChannel = draggingConnector->destFilterChannel; + + deleteAndZero (draggingConnector); + + PinComponent* const pin = findPinAt (e2.x, e2.y); + + if (pin != 0) + { + if (srcFilter == 0) + { + if (pin->isInput) + return; + + srcFilter = pin->filterID; + srcChannel = pin->index; + } + else + { + if (! pin->isInput) + return; + + dstFilter = pin->filterID; + dstChannel = pin->index; + } + + graph.addConnection (srcFilter, srcChannel, dstFilter, dstChannel); + } +} + + +//============================================================================== +class StatusAndTooltipBar : public Component, + private Timer +{ +public: + StatusAndTooltipBar() + { + startTimer (100); + } + + ~StatusAndTooltipBar() + { + } + + void paint (Graphics& g) + { + g.setFont (getHeight() * 0.7f, Font::bold); + g.setColour (Colours::black); + g.drawFittedText (tip, 10, 0, getWidth() - 12, getHeight(), Justification::centredLeft, 1); + } + + void timerCallback() + { + Component* const underMouse = Component::getComponentUnderMouse(); + TooltipClient* const ttc = dynamic_cast (underMouse); + + String newTip; + + if (ttc != 0 && ! (underMouse->isMouseButtonDown() || underMouse->isCurrentlyBlockedByAnotherModalComponent())) + newTip = ttc->getTooltip(); + + if (newTip != tip) + { + tip = newTip; + repaint(); + } + } + + juce_UseDebuggingNewOperator + +private: + String tip; +}; + +//============================================================================== +GraphDocumentComponent::GraphDocumentComponent (AudioDeviceManager* deviceManager_) + : deviceManager (deviceManager_) +{ + addAndMakeVisible (graphPanel = new GraphEditorPanel (graph)); + + graphPlayer = new FilterGraphPlayer (graph); + + addAndMakeVisible (keyboardComp = new MidiKeyboardComponent (graphPlayer->keyState, + MidiKeyboardComponent::horizontalKeyboard)); + + addAndMakeVisible (statusBar = new StatusAndTooltipBar()); + + graphPlayer->setAudioDeviceManager (deviceManager); + + graphPanel->updateComponents(); +} + +GraphDocumentComponent::~GraphDocumentComponent() +{ + deleteAllChildren(); + + graphPlayer->setAudioDeviceManager (0); + deleteAndZero (graphPlayer); + + graph.clear(); +} + +void GraphDocumentComponent::resized() +{ + const int keysHeight = 60; + const int statusHeight = 20; + + graphPanel->setBounds (0, 0, getWidth(), getHeight() - keysHeight); + statusBar->setBounds (0, getHeight() - keysHeight - statusHeight, getWidth(), statusHeight); + keyboardComp->setBounds (0, getHeight() - keysHeight, getWidth(), keysHeight); +} + +void GraphDocumentComponent::createNewPlugin (const PluginDescription* desc, int x, int y) +{ + graphPanel->createNewPlugin (desc, x, y); +} diff --git a/extras/audio plugin host/src/host/GraphEditorPanel.h b/extras/audio plugin host/src/host/GraphEditorPanel.h index cd96b62a3d..14c85da1ae 100644 --- a/extras/audio plugin host/src/host/GraphEditorPanel.h +++ b/extras/audio plugin host/src/host/GraphEditorPanel.h @@ -114,6 +114,7 @@ private: GraphEditorPanel* graphPanel; Component* keyboardComp; + Component* statusBar; }; diff --git a/extras/audio plugin host/src/host/InternalFilters.cpp b/extras/audio plugin host/src/host/InternalFilters.cpp index 4aa17e582d..f8ccb3f8b7 100644 --- a/extras/audio plugin host/src/host/InternalFilters.cpp +++ b/extras/audio plugin host/src/host/InternalFilters.cpp @@ -157,7 +157,7 @@ public: if (dev != 0) return dev->getInputChannelNames() [channelIndex]; - return String (channelIndex + 1); + return "Input Channel " + String (channelIndex + 1); } bool JUCE_CALLTYPE isInputChannelStereoPair (int) const @@ -240,7 +240,7 @@ public: if (dev != 0) return dev->getOutputChannelNames() [channelIndex]; - return String (channelIndex + 1); + return "Output Channel " + String (channelIndex + 1); } const String JUCE_CALLTYPE getOutputChannelName (const int channelIndex) const @@ -315,7 +315,7 @@ public: if (dev != 0) return dev->getInputChannelNames() [channelIndex]; - return String (channelIndex + 1); + return "Midi Input"; } bool JUCE_CALLTYPE isInputChannelStereoPair (int) const