Browse Source

Initial code for juce-patchbay plugin (test for later carla-patch)

tags/1.9.4
falkTX 10 years ago
parent
commit
dc8424d97b
18 changed files with 3958 additions and 10 deletions
  1. +2
    -0
      source/backend/plugin/JucePlugin.cpp
  2. +2
    -0
      source/backend/standalone/Makefile
  3. +9
    -1
      source/modules/Makefile
  4. +4
    -0
      source/modules/daz-plugins/Makefile
  5. +2
    -0
      source/modules/daz-plugins/_all.c
  6. +362
    -0
      source/modules/daz-plugins/juce-host/FilterGraph.cpp
  7. +114
    -0
      source/modules/daz-plugins/juce-host/FilterGraph.h
  8. +1125
    -0
      source/modules/daz-plugins/juce-host/GraphEditorPanel.cpp
  9. +141
    -0
      source/modules/daz-plugins/juce-host/GraphEditorPanel.h
  10. +80
    -0
      source/modules/daz-plugins/juce-host/InternalFilters.cpp
  11. +76
    -0
      source/modules/daz-plugins/juce-host/InternalFilters.h
  12. +439
    -0
      source/modules/daz-plugins/juce-host/MainHostWindow.cpp
  13. +99
    -0
      source/modules/daz-plugins/juce-host/MainHostWindow.h
  14. +901
    -0
      source/modules/daz-plugins/juce-host/juce_MidiKeyboardComponent.cpp
  15. +409
    -0
      source/modules/daz-plugins/juce-host/juce_MidiKeyboardComponent.h
  16. +181
    -0
      source/modules/daz-plugins/juce-patchbay.cpp
  17. +0
    -2
      source/modules/daz-plugins/vex-synth.cpp
  18. +12
    -7
      source/utils/JucePluginWindow.hpp

+ 2
- 0
source/backend/plugin/JucePlugin.cpp View File

@@ -25,6 +25,8 @@

#include "juce_audio_processors.h"

// TODO - use setPlayConfigDetails

using namespace juce;

CARLA_BACKEND_START_NAMESPACE


+ 2
- 0
source/backend/standalone/Makefile View File

@@ -33,6 +33,7 @@ STATIC_LIBS += ../../modules/juce_data_structures.a
STATIC_LIBS += ../../modules/juce_events.a
STATIC_LIBS += ../../modules/juce_graphics.a
STATIC_LIBS += ../../modules/juce_gui_basics.a
STATIC_LIBS += ../../modules/juce_gui_extra.a
LINK_FLAGS += $(JUCE_AUDIO_BASICS_LIBS)
LINK_FLAGS += $(JUCE_AUDIO_DEVICES_LIBS)
LINK_FLAGS += $(JUCE_CORE_LIBS)
@@ -40,6 +41,7 @@ LINK_FLAGS += $(JUCE_DATA_STRUCTURES_LIBS)
LINK_FLAGS += $(JUCE_EVENTS_LIBS)
LINK_FLAGS += $(JUCE_GRAPHICS_LIBS)
LINK_FLAGS += $(JUCE_GUI_BASICS_LIBS)
LINK_FLAGS += $(JUCE_GUI_EXTRA_LIBS)
endif

ifeq ($(HAVE_DGL),true)


+ 9
- 1
source/modules/Makefile View File

@@ -93,6 +93,14 @@ juce_gui_basics_%:

# --------------------------------------------------------------

juce_gui_extra:
$(MAKE) -C juce_gui_extra

juce_gui_extra_%:
$(MAKE) -C juce_gui_extra $*

# --------------------------------------------------------------

lilv:
$(MAKE) -C lilv

@@ -211,6 +219,6 @@ clean:

.PHONY: \
daz-plugins dgl jackbridge lilv rtaudio rtmidi rtmempool stk theme \
juce_audio_basics juce_audio_devices juce_audio_formats juce_audio_processors juce_core juce_data_structures juce_events juce_graphics juce_gui_basics
juce_audio_basics juce_audio_devices juce_audio_formats juce_audio_processors juce_core juce_data_structures juce_events juce_graphics juce_gui_basics juce_gui_extra

# --------------------------------------------------------------

+ 4
- 0
source/modules/daz-plugins/Makefile View File

@@ -100,6 +100,7 @@ OBJS += \

ifeq ($(HAVE_JUCE),true)
OBJS += \
juce-patchbay.cpp.o \
vex-fx.cpp.o \
vex-synth.cpp.o \
vex-src.cpp.o
@@ -257,6 +258,9 @@ distrho-bigmeterM.cpp.o: distrho-bigmeterM.cpp bigmeterM/*.cpp bigmeterM/*.h big
distrho-notes.cpp.o: distrho-notes.cpp notes/*.cpp notes/*.h notes/*.hpp distrho/DistrhoPluginCarla.cpp $(CXXDEPS)
$(CXX) $< $(BUILD_CXX_FLAGS) -I../dgl -Inotes -DDISTRHO_NAMESPACE=DISTRHO_Notes -c -o $@

juce-patchbay.cpp.o: juce-patchbay.cpp
$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

midi-file.cpp.o: midi-file.cpp midi-base.hpp $(CXXDEPS)
$(CXX) $< $(MF_CXX_FLAGS) -c -o $@



+ 2
- 0
source/modules/daz-plugins/_all.c View File

@@ -36,6 +36,7 @@ extern void carla_register_native_plugin_carla();
#endif

#ifdef HAVE_JUCE
extern void carla_register_native_plugin_jucePatchbay();
extern void carla_register_native_plugin_vex_fx();
extern void carla_register_native_plugin_vex_synth();
#endif
@@ -93,6 +94,7 @@ void carla_register_all_plugins()
#endif

#ifdef HAVE_JUCE
carla_register_native_plugin_jucePatchbay();
carla_register_native_plugin_vex_fx();
carla_register_native_plugin_vex_synth();
#endif


+ 362
- 0
source/modules/daz-plugins/juce-host/FilterGraph.cpp View File

@@ -0,0 +1,362 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
//#include "MainHostWindow.h"
#include "FilterGraph.h"
#include "InternalFilters.h"
//#include "GraphEditorPanel.h"
//==============================================================================
const int FilterGraph::midiChannelNumber = 0x1000;
FilterGraph::FilterGraph (AudioPluginFormatManager& formatManager_)
: FileBasedDocument (filenameSuffix,
filenameWildcard,
"Load a filter graph",
"Save a filter graph"),
formatManager (formatManager_), lastUID (0)
{
setChangedFlag (false);
}
FilterGraph::~FilterGraph()
{
graph.clear();
}
void FilterGraph::ready()
{
InternalPluginFormat internalFormat;
addFilter (internalFormat.getDescriptionFor (InternalPluginFormat::audioInputFilter), 0.5f, 0.1f);
addFilter (internalFormat.getDescriptionFor (InternalPluginFormat::midiInputFilter), 0.25f, 0.1f);
addFilter (internalFormat.getDescriptionFor (InternalPluginFormat::audioOutputFilter), 0.5f, 0.9f);
}
uint32 FilterGraph::getNextUID() noexcept
{
return ++lastUID;
}
//==============================================================================
int FilterGraph::getNumFilters() const noexcept
{
return graph.getNumNodes();
}
const AudioProcessorGraph::Node::Ptr FilterGraph::getNode (const int index) const noexcept
{
return graph.getNode (index);
}
const AudioProcessorGraph::Node::Ptr FilterGraph::getNodeForId (const uint32 uid) const noexcept
{
return graph.getNodeForId (uid);
}
void FilterGraph::addFilter (const PluginDescription* desc, double x, double y)
{
if (desc != nullptr)
{
AudioProcessorGraph::Node* node = nullptr;
String errorMessage;
if (AudioPluginInstance* instance = formatManager.createPluginInstance (*desc, graph.getSampleRate(), graph.getBlockSize(), errorMessage))
node = graph.addNode (instance);
if (node != nullptr)
{
node->properties.set ("x", x);
node->properties.set ("y", y);
changed();
}
else
{
AlertWindow::showMessageBox (AlertWindow::WarningIcon,
TRANS("Couldn't create filter"),
errorMessage);
}
}
}
void FilterGraph::removeFilter (const uint32 id)
{
//PluginWindow::closeCurrentlyOpenWindowsFor (id);
if (graph.removeNode (id))
changed();
}
void FilterGraph::disconnectFilter (const uint32 id)
{
if (graph.disconnectNode (id))
changed();
}
void FilterGraph::removeIllegalConnections()
{
if (graph.removeIllegalConnections())
changed();
}
void FilterGraph::setNodePosition (const int nodeId, double x, double y)
{
const AudioProcessorGraph::Node::Ptr n (graph.getNodeForId (nodeId));
if (n != nullptr)
{
n->properties.set ("x", jlimit (0.0, 1.0, x));
n->properties.set ("y", jlimit (0.0, 1.0, y));
}
}
void FilterGraph::getNodePosition (const int nodeId, double& x, double& y) const
{
x = y = 0;
const AudioProcessorGraph::Node::Ptr n (graph.getNodeForId (nodeId));
if (n != nullptr)
{
x = (double) n->properties ["x"];
y = (double) n->properties ["y"];
}
}
//==============================================================================
int FilterGraph::getNumConnections() const noexcept
{
return graph.getNumConnections();
}
const AudioProcessorGraph::Connection* FilterGraph::getConnection (const int index) const noexcept
{
return graph.getConnection (index);
}
const AudioProcessorGraph::Connection* FilterGraph::getConnectionBetween (uint32 sourceFilterUID, int sourceFilterChannel,
uint32 destFilterUID, int destFilterChannel) const noexcept
{
return graph.getConnectionBetween (sourceFilterUID, sourceFilterChannel,
destFilterUID, destFilterChannel);
}
bool FilterGraph::canConnect (uint32 sourceFilterUID, int sourceFilterChannel,
uint32 destFilterUID, int destFilterChannel) const noexcept
{
return graph.canConnect (sourceFilterUID, sourceFilterChannel,
destFilterUID, destFilterChannel);
}
bool FilterGraph::addConnection (uint32 sourceFilterUID, int sourceFilterChannel,
uint32 destFilterUID, int destFilterChannel)
{
const bool result = graph.addConnection (sourceFilterUID, sourceFilterChannel,
destFilterUID, destFilterChannel);
if (result)
changed();
return result;
}
void FilterGraph::removeConnection (const int index)
{
graph.removeConnection (index);
changed();
}
void FilterGraph::removeConnection (uint32 sourceFilterUID, int sourceFilterChannel,
uint32 destFilterUID, int destFilterChannel)
{
if (graph.removeConnection (sourceFilterUID, sourceFilterChannel,
destFilterUID, destFilterChannel))
changed();
}
void FilterGraph::clear()
{
//PluginWindow::closeAllCurrentlyOpenWindows();
graph.clear();
changed();
}
//==============================================================================
String FilterGraph::getDocumentTitle()
{
if (! getFile().exists())
return "Unnamed";
return getFile().getFileNameWithoutExtension();
}
Result FilterGraph::loadDocument (const File& file)
{
XmlDocument doc (file);
ScopedPointer<XmlElement> xml (doc.getDocumentElement());
if (xml == nullptr || ! xml->hasTagName ("FILTERGRAPH"))
return Result::fail ("Not a valid filter graph file");
restoreFromXml (*xml);
return Result::ok();
}
Result FilterGraph::saveDocument (const File& file)
{
ScopedPointer<XmlElement> xml (createXml());
if (! xml->writeToFile (file, String::empty))
return Result::fail ("Couldn't write to the file");
return Result::ok();
}
File FilterGraph::getLastDocumentOpened()
{
return File();
}
void FilterGraph::setLastDocumentOpened (const File&)
{
}
//==============================================================================
static XmlElement* createNodeXml (AudioProcessorGraph::Node* const node) noexcept
{
AudioPluginInstance* plugin = dynamic_cast <AudioPluginInstance*> (node->getProcessor());
if (plugin == nullptr)
{
jassertfalse
return nullptr;
}
XmlElement* e = new XmlElement ("FILTER");
e->setAttribute ("uid", (int) node->nodeId);
e->setAttribute ("x", node->properties ["x"].toString());
e->setAttribute ("y", node->properties ["y"].toString());
e->setAttribute ("uiLastX", node->properties ["uiLastX"].toString());
e->setAttribute ("uiLastY", node->properties ["uiLastY"].toString());
PluginDescription pd;
plugin->fillInPluginDescription (pd);
e->addChildElement (pd.createXml());
XmlElement* state = new XmlElement ("STATE");
MemoryBlock m;
node->getProcessor()->getStateInformation (m);
state->addTextElement (m.toBase64Encoding());
e->addChildElement (state);
return e;
}
void FilterGraph::createNodeFromXml (const XmlElement& xml)
{
PluginDescription pd;
forEachXmlChildElement (xml, e)
{
if (pd.loadFromXml (*e))
break;
}
String errorMessage;
AudioPluginInstance* instance = formatManager.createPluginInstance (pd, graph.getSampleRate(), graph.getBlockSize(), errorMessage);
if (instance == nullptr)
{
// xxx handle ins + outs
}
if (instance == nullptr)
return;
AudioProcessorGraph::Node::Ptr node (graph.addNode (instance, xml.getIntAttribute ("uid")));
if (const XmlElement* const state = xml.getChildByName ("STATE"))
{
MemoryBlock m;
m.fromBase64Encoding (state->getAllSubText());
node->getProcessor()->setStateInformation (m.getData(), (int) m.getSize());
}
node->properties.set ("x", xml.getDoubleAttribute ("x"));
node->properties.set ("y", xml.getDoubleAttribute ("y"));
node->properties.set ("uiLastX", xml.getIntAttribute ("uiLastX"));
node->properties.set ("uiLastY", xml.getIntAttribute ("uiLastY"));
}
XmlElement* FilterGraph::createXml() const
{
XmlElement* xml = new XmlElement ("FILTERGRAPH");
for (int i = 0; i < graph.getNumNodes(); ++i)
xml->addChildElement (createNodeXml (graph.getNode (i)));
for (int i = 0; i < graph.getNumConnections(); ++i)
{
const AudioProcessorGraph::Connection* const fc = graph.getConnection(i);
XmlElement* e = new XmlElement ("CONNECTION");
e->setAttribute ("srcFilter", (int) fc->sourceNodeId);
e->setAttribute ("srcChannel", fc->sourceChannelIndex);
e->setAttribute ("dstFilter", (int) fc->destNodeId);
e->setAttribute ("dstChannel", fc->destChannelIndex);
xml->addChildElement (e);
}
return xml;
}
void FilterGraph::restoreFromXml (const XmlElement& xml)
{
clear();
forEachXmlChildElementWithTagName (xml, e, "FILTER")
{
createNodeFromXml (*e);
changed();
}
forEachXmlChildElementWithTagName (xml, e, "CONNECTION")
{
addConnection ((uint32) e->getIntAttribute ("srcFilter"),
e->getIntAttribute ("srcChannel"),
(uint32) e->getIntAttribute ("dstFilter"),
e->getIntAttribute ("dstChannel"));
}
graph.removeIllegalConnections();
}

+ 114
- 0
source/modules/daz-plugins/juce-host/FilterGraph.h View File

@@ -0,0 +1,114 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef __FILTERGRAPH_JUCEHEADER__
#define __FILTERGRAPH_JUCEHEADER__
class FilterInGraph;
class FilterGraph;
const char* const filenameSuffix = ".filtergraph";
const char* const filenameWildcard = "*.filtergraph";
//==============================================================================
/**
A collection of filters and some connections between them.
*/
class FilterGraph : public FileBasedDocument
{
public:
//==============================================================================
FilterGraph (AudioPluginFormatManager& formatManager);
~FilterGraph();
void ready();
//==============================================================================
AudioProcessorGraph& getGraph() noexcept { return graph; }
int getNumFilters() const noexcept;
const AudioProcessorGraph::Node::Ptr getNode (const int index) const noexcept;
const AudioProcessorGraph::Node::Ptr getNodeForId (const uint32 uid) const noexcept;
void addFilter (const PluginDescription* desc, double x, double y);
void removeFilter (const uint32 filterUID);
void disconnectFilter (const uint32 filterUID);
void removeIllegalConnections();
void setNodePosition (const int nodeId, double x, double y);
void getNodePosition (const int nodeId, double& x, double& y) const;
//==============================================================================
int getNumConnections() const noexcept;
const AudioProcessorGraph::Connection* getConnection (const int index) const noexcept;
const AudioProcessorGraph::Connection* getConnectionBetween (uint32 sourceFilterUID, int sourceFilterChannel,
uint32 destFilterUID, int destFilterChannel) const noexcept;
bool canConnect (uint32 sourceFilterUID, int sourceFilterChannel,
uint32 destFilterUID, int destFilterChannel) const noexcept;
bool addConnection (uint32 sourceFilterUID, int sourceFilterChannel,
uint32 destFilterUID, int destFilterChannel);
void removeConnection (const int index);
void removeConnection (uint32 sourceFilterUID, int sourceFilterChannel,
uint32 destFilterUID, int destFilterChannel);
void clear();
//==============================================================================
XmlElement* createXml() const;
void restoreFromXml (const XmlElement& xml);
//==============================================================================
String getDocumentTitle();
Result loadDocument (const File& file);
Result saveDocument (const File& file);
File getLastDocumentOpened();
void setLastDocumentOpened (const File& file);
/** The special channel index used to refer to a filter's midi channel.
*/
static const int midiChannelNumber;
private:
//==============================================================================
AudioPluginFormatManager& formatManager;
AudioProcessorGraph graph;
uint32 lastUID;
uint32 getNextUID() noexcept;
void createNodeFromXml (const XmlElement& xml);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilterGraph)
};
#endif // __FILTERGRAPH_JUCEHEADER__

+ 1125
- 0
source/modules/daz-plugins/juce-host/GraphEditorPanel.cpp
File diff suppressed because it is too large
View File


+ 141
- 0
source/modules/daz-plugins/juce-host/GraphEditorPanel.h View File

@@ -0,0 +1,141 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef __GRAPHEDITORPANEL_JUCEHEADER__
#define __GRAPHEDITORPANEL_JUCEHEADER__
#include "FilterGraph.h"
class FilterComponent;
class ConnectorComponent;
class PinComponent;
//==============================================================================
/**
A panel that displays and edits a FilterGraph.
*/
class GraphEditorPanel : public Component,
public ChangeListener
{
public:
GraphEditorPanel (FilterGraph& graph);
~GraphEditorPanel();
void paint (Graphics& g);
void mouseDown (const MouseEvent& e);
void createNewPlugin (const PluginDescription* desc, int x, int y);
FilterComponent* getComponentForFilter (uint32 filterID) const;
ConnectorComponent* getComponentForConnection (const AudioProcessorGraph::Connection& conn) const;
PinComponent* findPinAt (int x, int y) const;
void resized();
void changeListenerCallback (ChangeBroadcaster*);
void updateComponents();
//==============================================================================
void beginConnectorDrag (uint32 sourceFilterID, int sourceFilterChannel,
uint32 destFilterID, int destFilterChannel,
const MouseEvent& e);
void dragConnector (const MouseEvent& e);
void endDraggingConnector (const MouseEvent& e);
//==============================================================================
private:
FilterGraph& graph;
ScopedPointer<ConnectorComponent> draggingConnector;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GraphEditorPanel)
};
//==============================================================================
/**
A panel that embeds a GraphEditorPanel with a midi keyboard at the bottom.
It also manages the graph itself, and plays it.
*/
class GraphDocumentComponent : public Component
{
public:
//==============================================================================
GraphDocumentComponent (FilterGraph& graph);
~GraphDocumentComponent();
//==============================================================================
void createNewPlugin (const PluginDescription* desc, int x, int y);
//==============================================================================
FilterGraph& graph;
//==============================================================================
void resized();
private:
//==============================================================================
MidiKeyboardState keyState;
GraphEditorPanel* graphPanel;
Component* keyboardComp;
Component* statusBar;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GraphDocumentComponent)
};
//==============================================================================
/** A desktop window containing a plugin's UI. */
class PluginWindow : public DocumentWindow
{
public:
enum WindowFormatType
{
Normal = 0,
Generic,
Programs,
Parameters
};
PluginWindow (Component* pluginEditor, AudioProcessorGraph::Node*, WindowFormatType);
~PluginWindow();
static PluginWindow* getWindowFor (AudioProcessorGraph::Node*, WindowFormatType);
static void closeCurrentlyOpenWindowsFor (const uint32 nodeId);
static void closeAllCurrentlyOpenWindows();
void moved() override;
void closeButtonPressed() override;
private:
AudioProcessorGraph::Node* owner;
WindowFormatType type;
float getDesktopScaleFactor() const override { return 1.0f; }
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginWindow)
};
#endif // __GRAPHEDITORPANEL_JUCEHEADER__

+ 80
- 0
source/modules/daz-plugins/juce-host/InternalFilters.cpp View File

@@ -0,0 +1,80 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#include "InternalFilters.h"
#include "FilterGraph.h"
//==============================================================================
InternalPluginFormat::InternalPluginFormat()
{
{
AudioProcessorGraph::AudioGraphIOProcessor p (AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode);
p.fillInPluginDescription (audioOutDesc);
}
{
AudioProcessorGraph::AudioGraphIOProcessor p (AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode);
p.fillInPluginDescription (audioInDesc);
}
{
AudioProcessorGraph::AudioGraphIOProcessor p (AudioProcessorGraph::AudioGraphIOProcessor::midiInputNode);
p.fillInPluginDescription (midiInDesc);
}
}
AudioPluginInstance* InternalPluginFormat::createInstanceFromDescription (const PluginDescription& desc,
double /*sampleRate*/, int /*blockSize*/)
{
if (desc.name == audioOutDesc.name)
return new AudioProcessorGraph::AudioGraphIOProcessor (AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode);
if (desc.name == audioInDesc.name)
return new AudioProcessorGraph::AudioGraphIOProcessor (AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode);
if (desc.name == midiInDesc.name)
return new AudioProcessorGraph::AudioGraphIOProcessor (AudioProcessorGraph::AudioGraphIOProcessor::midiInputNode);
return 0;
}
const PluginDescription* InternalPluginFormat::getDescriptionFor (const InternalFilterType type)
{
switch (type)
{
case audioInputFilter: return &audioInDesc;
case audioOutputFilter: return &audioOutDesc;
case midiInputFilter: return &midiInDesc;
default: break;
}
return 0;
}
void InternalPluginFormat::getAllTypes (OwnedArray <PluginDescription>& results)
{
for (int i = 0; i < (int) endOfFilterTypes; ++i)
results.add (new PluginDescription (*getDescriptionFor ((InternalFilterType) i)));
}

+ 76
- 0
source/modules/daz-plugins/juce-host/InternalFilters.h View File

@@ -0,0 +1,76 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef __INTERNALFILTERS_JUCEHEADER__
#define __INTERNALFILTERS_JUCEHEADER__
#include "FilterGraph.h"
//==============================================================================
/**
Manages the internal plugin types.
*/
class InternalPluginFormat : public AudioPluginFormat
{
public:
//==============================================================================
InternalPluginFormat();
~InternalPluginFormat() {}
//==============================================================================
enum InternalFilterType
{
audioInputFilter = 0,
audioOutputFilter,
midiInputFilter,
endOfFilterTypes
};
const PluginDescription* getDescriptionFor (const InternalFilterType type);
void getAllTypes (OwnedArray <PluginDescription>& results);
//==============================================================================
String getName() const override { return "Internal"; }
bool fileMightContainThisPluginType (const String&) override { return false; }
FileSearchPath getDefaultLocationsToSearch() override { return FileSearchPath(); }
bool canScanForPlugins() const override { return false; }
void findAllTypesForFile (OwnedArray <PluginDescription>&, const String&) override {}
bool doesPluginStillExist (const PluginDescription&) override { return true; }
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override { return fileOrIdentifier; }
bool pluginNeedsRescanning (const PluginDescription&) override { return false; }
StringArray searchPathsForPlugins (const FileSearchPath&, bool) override { return StringArray(); }
AudioPluginInstance* createInstanceFromDescription (const PluginDescription&, double, int) override;
private:
//==============================================================================
PluginDescription audioInDesc;
PluginDescription audioOutDesc;
PluginDescription midiInDesc;
};
#endif // __INTERNALFILTERS_JUCEHEADER__

+ 439
- 0
source/modules/daz-plugins/juce-host/MainHostWindow.cpp View File

@@ -0,0 +1,439 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#include "juce_gui_basics.h"
#include "MainHostWindow.h"
#include "InternalFilters.h"
//==============================================================================
class MainHostWindow::PluginListWindow : public DocumentWindow
{
public:
PluginListWindow (MainHostWindow& owner_, AudioPluginFormatManager& formatManager)
: DocumentWindow ("Available Plugins", Colours::white,
DocumentWindow::minimiseButton | DocumentWindow::closeButton),
owner (owner_)
{
const File deadMansPedalFile (getAppProperties().getUserSettings()
->getFile().getSiblingFile ("RecentlyCrashedPluginsList"));
setContentOwned (new PluginListComponent (formatManager,
owner.knownPluginList,
deadMansPedalFile,
getAppProperties().getUserSettings()), true);
setResizable (true, false);
setResizeLimits (300, 400, 800, 1500);
setTopLeftPosition (60, 60);
restoreWindowStateFromString (getAppProperties().getUserSettings()->getValue ("listWindowPos"));
setVisible (true);
}
~PluginListWindow()
{
getAppProperties().getUserSettings()->setValue ("listWindowPos", getWindowStateAsString());
clearContentComponent();
}
void closeButtonPressed()
{
owner.pluginListWindow = nullptr;
}
private:
MainHostWindow& owner;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginListWindow)
};
//==============================================================================
MainHostWindow::MainHostWindow()
: DocumentWindow (JUCEApplication::getInstance()->getApplicationName(), Colours::lightgrey,
DocumentWindow::allButtons)
{
formatManager.addDefaultFormats();
formatManager.addFormat (new InternalPluginFormat());
ScopedPointer<XmlElement> savedAudioState (getAppProperties().getUserSettings()
->getXmlValue ("audioDeviceState"));
//deviceManager.initialise (256, 256, savedAudioState, true);
setResizable (true, false);
setResizeLimits (500, 400, 10000, 10000);
centreWithSize (800, 600);
//setContentOwned (new GraphDocumentComponent (formatManager, &deviceManager), false);
restoreWindowStateFromString (getAppProperties().getUserSettings()->getValue ("mainWindowPos"));
setVisible (true);
InternalPluginFormat internalFormat;
internalFormat.getAllTypes (internalTypes);
ScopedPointer<XmlElement> savedPluginList (getAppProperties().getUserSettings()->getXmlValue ("pluginList"));
if (savedPluginList != nullptr)
knownPluginList.recreateFromXml (*savedPluginList);
pluginSortMethod = (KnownPluginList::SortMethod) getAppProperties().getUserSettings()
->getIntValue ("pluginSortMethod", KnownPluginList::sortByManufacturer);
knownPluginList.addChangeListener (this);
addKeyListener (getCommandManager().getKeyMappings());
Process::setPriority (Process::HighPriority);
#if JUCE_MAC
setMacMainMenu (this);
#else
setMenuBar (this);
#endif
getCommandManager().setFirstCommandTarget (this);
}
MainHostWindow::~MainHostWindow()
{
pluginListWindow = nullptr;
#if JUCE_MAC
setMacMainMenu (nullptr);
#else
setMenuBar (nullptr);
#endif
knownPluginList.removeChangeListener (this);
getAppProperties().getUserSettings()->setValue ("mainWindowPos", getWindowStateAsString());
clearContentComponent();
}
void MainHostWindow::closeButtonPressed()
{
tryToQuitApplication();
}
bool MainHostWindow::tryToQuitApplication()
{
PluginWindow::closeAllCurrentlyOpenWindows();
if (getGraphEditor() == nullptr
|| getGraphEditor()->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
{
JUCEApplication::quit();
return true;
}
return false;
}
void MainHostWindow::changeListenerCallback (ChangeBroadcaster*)
{
menuItemsChanged();
// save the plugin list every time it gets chnaged, so that if we're scanning
// and it crashes, we've still saved the previous ones
ScopedPointer<XmlElement> savedPluginList (knownPluginList.createXml());
if (savedPluginList != nullptr)
{
getAppProperties().getUserSettings()->setValue ("pluginList", savedPluginList);
getAppProperties().saveIfNeeded();
}
}
StringArray MainHostWindow::getMenuBarNames()
{
const char* const names[] = { "File", "Plugins", "Options", nullptr };
return StringArray (names);
}
PopupMenu MainHostWindow::getMenuForIndex (int topLevelMenuIndex, const String& /*menuName*/)
{
PopupMenu menu;
if (topLevelMenuIndex == 0)
{
// "File" menu
menu.addCommandItem (&getCommandManager(), CommandIDs::open);
RecentlyOpenedFilesList recentFiles;
recentFiles.restoreFromString (getAppProperties().getUserSettings()
->getValue ("recentFilterGraphFiles"));
PopupMenu recentFilesMenu;
recentFiles.createPopupMenuItems (recentFilesMenu, 100, true, true);
menu.addSubMenu ("Open recent file", recentFilesMenu);
menu.addCommandItem (&getCommandManager(), CommandIDs::save);
menu.addCommandItem (&getCommandManager(), CommandIDs::saveAs);
menu.addSeparator();
menu.addCommandItem (&getCommandManager(), StandardApplicationCommandIDs::quit);
}
else if (topLevelMenuIndex == 1)
{
// "Plugins" menu
PopupMenu pluginsMenu;
addPluginsToMenu (pluginsMenu);
menu.addSubMenu ("Create plugin", pluginsMenu);
menu.addSeparator();
menu.addItem (250, "Delete all plugins");
}
else if (topLevelMenuIndex == 2)
{
// "Options" menu
menu.addCommandItem (&getCommandManager(), CommandIDs::showPluginListEditor);
PopupMenu sortTypeMenu;
sortTypeMenu.addItem (200, "List plugins in default order", true, pluginSortMethod == KnownPluginList::defaultOrder);
sortTypeMenu.addItem (201, "List plugins in alphabetical order", true, pluginSortMethod == KnownPluginList::sortAlphabetically);
sortTypeMenu.addItem (202, "List plugins by category", true, pluginSortMethod == KnownPluginList::sortByCategory);
sortTypeMenu.addItem (203, "List plugins by manufacturer", true, pluginSortMethod == KnownPluginList::sortByManufacturer);
sortTypeMenu.addItem (204, "List plugins based on the directory structure", true, pluginSortMethod == KnownPluginList::sortByFileSystemLocation);
menu.addSubMenu ("Plugin menu type", sortTypeMenu);
menu.addSeparator();
menu.addCommandItem (&getCommandManager(), CommandIDs::aboutBox);
}
return menu;
}
void MainHostWindow::menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/)
{
GraphDocumentComponent* const graphEditor = getGraphEditor();
if (menuItemID == 250)
{
if (graphEditor != nullptr)
graphEditor->graph.clear();
}
else if (menuItemID >= 100 && menuItemID < 200)
{
RecentlyOpenedFilesList recentFiles;
recentFiles.restoreFromString (getAppProperties().getUserSettings()
->getValue ("recentFilterGraphFiles"));
if (graphEditor != nullptr && graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
graphEditor->graph.loadFrom (recentFiles.getFile (menuItemID - 100), true);
}
else if (menuItemID >= 200 && menuItemID < 210)
{
if (menuItemID == 200) pluginSortMethod = KnownPluginList::defaultOrder;
else if (menuItemID == 201) pluginSortMethod = KnownPluginList::sortAlphabetically;
else if (menuItemID == 202) pluginSortMethod = KnownPluginList::sortByCategory;
else if (menuItemID == 203) pluginSortMethod = KnownPluginList::sortByManufacturer;
else if (menuItemID == 204) pluginSortMethod = KnownPluginList::sortByFileSystemLocation;
getAppProperties().getUserSettings()->setValue ("pluginSortMethod", (int) pluginSortMethod);
menuItemsChanged();
}
else
{
createPlugin (getChosenType (menuItemID),
proportionOfWidth (0.3f + Random::getSystemRandom().nextFloat() * 0.6f),
proportionOfHeight (0.3f + Random::getSystemRandom().nextFloat() * 0.6f));
}
}
void MainHostWindow::createPlugin (const PluginDescription* desc, int x, int y)
{
GraphDocumentComponent* const graphEditor = getGraphEditor();
if (graphEditor != nullptr)
graphEditor->createNewPlugin (desc, x, y);
}
void MainHostWindow::addPluginsToMenu (PopupMenu& m) const
{
for (int i = 0; i < internalTypes.size(); ++i)
m.addItem (i + 1, internalTypes.getUnchecked(i)->name);
m.addSeparator();
knownPluginList.addToMenu (m, pluginSortMethod);
}
const PluginDescription* MainHostWindow::getChosenType (const int menuID) const
{
if (menuID >= 1 && menuID < 1 + internalTypes.size())
return internalTypes [menuID - 1];
return knownPluginList.getType (knownPluginList.getIndexChosenByMenu (menuID));
}
//==============================================================================
ApplicationCommandTarget* MainHostWindow::getNextCommandTarget()
{
return findFirstTargetParentComponent();
}
void MainHostWindow::getAllCommands (Array <CommandID>& commands)
{
// this returns the set of all commands that this target can perform..
const CommandID ids[] = { CommandIDs::open,
CommandIDs::save,
CommandIDs::saveAs,
CommandIDs::showPluginListEditor,
CommandIDs::aboutBox
};
commands.addArray (ids, numElementsInArray (ids));
}
void MainHostWindow::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
{
const String category ("General");
switch (commandID)
{
case CommandIDs::open:
result.setInfo ("Open...",
"Opens a filter graph file",
category, 0);
result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0));
break;
case CommandIDs::save:
result.setInfo ("Save",
"Saves the current graph to a file",
category, 0);
result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier, 0));
break;
case CommandIDs::saveAs:
result.setInfo ("Save As...",
"Saves a copy of the current graph to a file",
category, 0);
result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::shiftModifier | ModifierKeys::commandModifier, 0));
break;
case CommandIDs::showPluginListEditor:
result.setInfo ("Edit the list of available plug-Ins...", String::empty, category, 0);
result.addDefaultKeypress ('p', ModifierKeys::commandModifier);
break;
case CommandIDs::aboutBox:
result.setInfo ("About...", String::empty, category, 0);
break;
default:
break;
}
}
bool MainHostWindow::perform (const InvocationInfo& info)
{
GraphDocumentComponent* const graphEditor = getGraphEditor();
switch (info.commandID)
{
case CommandIDs::open:
if (graphEditor != nullptr && graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
graphEditor->graph.loadFromUserSpecifiedFile (true);
break;
case CommandIDs::save:
if (graphEditor != nullptr)
graphEditor->graph.save (true, true);
break;
case CommandIDs::saveAs:
if (graphEditor != nullptr)
graphEditor->graph.saveAs (File::nonexistent, true, true, true);
break;
case CommandIDs::showPluginListEditor:
if (pluginListWindow == nullptr)
pluginListWindow = new PluginListWindow (*this, formatManager);
pluginListWindow->toFront (true);
break;
case CommandIDs::aboutBox:
// TODO
break;
default:
return false;
}
return true;
}
bool MainHostWindow::isInterestedInFileDrag (const StringArray&)
{
return true;
}
void MainHostWindow::fileDragEnter (const StringArray&, int, int)
{
}
void MainHostWindow::fileDragMove (const StringArray&, int, int)
{
}
void MainHostWindow::fileDragExit (const StringArray&)
{
}
void MainHostWindow::filesDropped (const StringArray& files, int x, int y)
{
GraphDocumentComponent* const graphEditor = getGraphEditor();
if (graphEditor != nullptr)
{
if (files.size() == 1 && File (files[0]).hasFileExtension (filenameSuffix))
{
if (graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
graphEditor->graph.loadFrom (File (files[0]), true);
}
else
{
OwnedArray <PluginDescription> typesFound;
knownPluginList.scanAndAddDragAndDroppedFiles (formatManager, files, typesFound);
Point<int> pos (graphEditor->getLocalPoint (this, Point<int> (x, y)));
for (int i = 0; i < jmin (5, typesFound.size()); ++i)
createPlugin (typesFound.getUnchecked(i), pos.getX(), pos.getY());
}
}
}
GraphDocumentComponent* MainHostWindow::getGraphEditor() const
{
return dynamic_cast <GraphDocumentComponent*> (getContentComponent());
}

+ 99
- 0
source/modules/daz-plugins/juce-host/MainHostWindow.h View File

@@ -0,0 +1,99 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef __MAINHOSTWINDOW_JUCEHEADER__
#define __MAINHOSTWINDOW_JUCEHEADER__
#include "FilterGraph.h"
#include "GraphEditorPanel.h"
//==============================================================================
namespace CommandIDs
{
static const int open = 0x30000;
static const int save = 0x30001;
static const int saveAs = 0x30002;
static const int showPluginListEditor = 0x30100;
static const int aboutBox = 0x30300;
}
//==============================================================================
/**
*/
class MainHostWindow : public DocumentWindow,
public MenuBarModel,
public ApplicationCommandTarget,
public ChangeListener,
public FileDragAndDropTarget
{
public:
//==============================================================================
MainHostWindow();
~MainHostWindow();
//==============================================================================
void closeButtonPressed();
void changeListenerCallback (ChangeBroadcaster*);
bool isInterestedInFileDrag (const StringArray& files);
void fileDragEnter (const StringArray& files, int, int);
void fileDragMove (const StringArray& files, int, int);
void fileDragExit (const StringArray& files);
void filesDropped (const StringArray& files, int, int);
StringArray getMenuBarNames();
PopupMenu getMenuForIndex (int topLevelMenuIndex, const String& menuName);
void menuItemSelected (int menuItemID, int topLevelMenuIndex);
ApplicationCommandTarget* getNextCommandTarget();
void getAllCommands (Array <CommandID>& commands);
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result);
bool perform (const InvocationInfo& info);
bool tryToQuitApplication();
void createPlugin (const PluginDescription* desc, int x, int y);
void addPluginsToMenu (PopupMenu& m) const;
const PluginDescription* getChosenType (const int menuID) const;
GraphDocumentComponent* getGraphEditor() const;
private:
//==============================================================================
//AudioDeviceManager deviceManager;
AudioPluginFormatManager formatManager;
OwnedArray <PluginDescription> internalTypes;
KnownPluginList knownPluginList;
KnownPluginList::SortMethod pluginSortMethod;
class PluginListWindow;
ScopedPointer <PluginListWindow> pluginListWindow;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainHostWindow)
};
#endif // __MAINHOSTWINDOW_JUCEHEADER__

+ 901
- 0
source/modules/daz-plugins/juce-host/juce_MidiKeyboardComponent.cpp View File

@@ -0,0 +1,901 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
class MidiKeyboardUpDownButton : public Button
{
public:
MidiKeyboardUpDownButton (MidiKeyboardComponent& comp, const int d)
: Button (String::empty),
owner (comp),
delta (d)
{
setOpaque (true);
}
void clicked() override
{
int note = owner.getLowestVisibleKey();
if (delta < 0)
note = (note - 1) / 12;
else
note = note / 12 + 1;
owner.setLowestVisibleKey (note * 12);
}
void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override
{
owner.drawUpDownButton (g, getWidth(), getHeight(),
isMouseOverButton, isButtonDown,
delta > 0);
}
private:
MidiKeyboardComponent& owner;
const int delta;
JUCE_DECLARE_NON_COPYABLE (MidiKeyboardUpDownButton)
};
//==============================================================================
MidiKeyboardComponent::MidiKeyboardComponent (MidiKeyboardState& s,
const Orientation o)
: state (s),
xOffset (0),
blackNoteLength (1),
keyWidth (16.0f),
orientation (o),
midiChannel (1),
midiInChannelMask (0xffff),
velocity (1.0f),
shouldCheckState (false),
rangeStart (0),
rangeEnd (127),
firstKey (12 * 4.0f),
canScroll (true),
useMousePositionForVelocity (true),
shouldCheckMousePos (false),
keyMappingOctave (6),
octaveNumForMiddleC (3)
{
addChildComponent (scrollDown = new MidiKeyboardUpDownButton (*this, -1));
addChildComponent (scrollUp = new MidiKeyboardUpDownButton (*this, 1));
// initialise with a default set of querty key-mappings..
const char* const keymap = "awsedftgyhujkolp;";
for (int i = 0; keymap[i] != 0; ++i)
setKeyPressForNote (KeyPress (keymap[i], 0, 0), i);
mouseOverNotes.insertMultiple (0, -1, 32);
mouseDownNotes.insertMultiple (0, -1, 32);
setOpaque (true);
setWantsKeyboardFocus (true);
state.addListener (this);
startTimer (1000 / 20);
}
MidiKeyboardComponent::~MidiKeyboardComponent()
{
state.removeListener (this);
}
//==============================================================================
void MidiKeyboardComponent::setKeyWidth (const float widthInPixels)
{
keyWidth = widthInPixels;
resized();
}
void MidiKeyboardComponent::setOrientation (const Orientation newOrientation)
{
if (orientation != newOrientation)
{
orientation = newOrientation;
resized();
}
}
void MidiKeyboardComponent::setAvailableRange (const int lowestNote,
const int highestNote)
{
jassert (lowestNote >= 0 && lowestNote <= 127);
jassert (highestNote >= 0 && highestNote <= 127);
jassert (lowestNote <= highestNote);
if (rangeStart != lowestNote || rangeEnd != highestNote)
{
rangeStart = jlimit (0, 127, lowestNote);
rangeEnd = jlimit (0, 127, highestNote);
firstKey = jlimit ((float) rangeStart, (float) rangeEnd, firstKey);
resized();
}
}
void MidiKeyboardComponent::setLowestVisibleKey (int noteNumber)
{
setLowestVisibleKeyFloat ((float) noteNumber);
}
void MidiKeyboardComponent::setLowestVisibleKeyFloat (float noteNumber)
{
noteNumber = jlimit ((float) rangeStart, (float) rangeEnd, noteNumber);
if (noteNumber != firstKey)
{
const bool hasMoved = (((int) firstKey) != (int) noteNumber);
firstKey = noteNumber;
if (hasMoved)
sendChangeMessage();
resized();
}
}
void MidiKeyboardComponent::setScrollButtonsVisible (const bool newCanScroll)
{
if (canScroll != newCanScroll)
{
canScroll = newCanScroll;
resized();
}
}
void MidiKeyboardComponent::colourChanged()
{
repaint();
}
//==============================================================================
void MidiKeyboardComponent::setMidiChannel (const int midiChannelNumber)
{
jassert (midiChannelNumber > 0 && midiChannelNumber <= 16);
if (midiChannel != midiChannelNumber)
{
resetAnyKeysInUse();
midiChannel = jlimit (1, 16, midiChannelNumber);
}
}
void MidiKeyboardComponent::setMidiChannelsToDisplay (const int midiChannelMask)
{
midiInChannelMask = midiChannelMask;
shouldCheckState = true;
}
void MidiKeyboardComponent::setVelocity (const float v, const bool useMousePosition)
{
velocity = jlimit (0.0f, 1.0f, v);
useMousePositionForVelocity = useMousePosition;
}
//==============================================================================
void MidiKeyboardComponent::getKeyPosition (int midiNoteNumber, const float keyWidth_, int& x, int& w) const
{
jassert (midiNoteNumber >= 0 && midiNoteNumber < 128);
static const float blackNoteWidth = 0.7f;
static const float notePos[] = { 0.0f, 1 - blackNoteWidth * 0.6f,
1.0f, 2 - blackNoteWidth * 0.4f,
2.0f,
3.0f, 4 - blackNoteWidth * 0.7f,
4.0f, 5 - blackNoteWidth * 0.5f,
5.0f, 6 - blackNoteWidth * 0.3f,
6.0f };
static const float widths[] = { 1.0f, blackNoteWidth,
1.0f, blackNoteWidth,
1.0f,
1.0f, blackNoteWidth,
1.0f, blackNoteWidth,
1.0f, blackNoteWidth,
1.0f };
const int octave = midiNoteNumber / 12;
const int note = midiNoteNumber % 12;
x = roundToInt (octave * 7.0f * keyWidth_ + notePos [note] * keyWidth_);
w = roundToInt (widths [note] * keyWidth_);
}
void MidiKeyboardComponent::getKeyPos (int midiNoteNumber, int& x, int& w) const
{
getKeyPosition (midiNoteNumber, keyWidth, x, w);
int rx, rw;
getKeyPosition (rangeStart, keyWidth, rx, rw);
x -= xOffset + rx;
}
Rectangle<int> MidiKeyboardComponent::getWhiteNotePos (int noteNum) const
{
int x, w;
getKeyPos (noteNum, x, w);
Rectangle<int> pos;
switch (orientation)
{
case horizontalKeyboard: pos.setBounds (x, 0, w, getHeight()); break;
case verticalKeyboardFacingLeft: pos.setBounds (0, x, getWidth(), w); break;
case verticalKeyboardFacingRight: pos.setBounds (0, getHeight() - x - w, getWidth(), w); break;
default: break;
}
return pos;
}
int MidiKeyboardComponent::getKeyStartPosition (const int midiNoteNumber) const
{
int x, w;
getKeyPos (midiNoteNumber, x, w);
return x;
}
int MidiKeyboardComponent::getNoteAtPosition (Point<int> p)
{
float v;
return xyToNote (p, v);
}
const uint8 MidiKeyboardComponent::whiteNotes[] = { 0, 2, 4, 5, 7, 9, 11 };
const uint8 MidiKeyboardComponent::blackNotes[] = { 1, 3, 6, 8, 10 };
int MidiKeyboardComponent::xyToNote (Point<int> pos, float& mousePositionVelocity)
{
if (! reallyContains (pos, false))
return -1;
Point<int> p (pos);
if (orientation != horizontalKeyboard)
{
p = Point<int> (p.y, p.x);
if (orientation == verticalKeyboardFacingLeft)
p = Point<int> (p.x, getWidth() - p.y);
else
p = Point<int> (getHeight() - p.x, p.y);
}
return remappedXYToNote (p + Point<int> (xOffset, 0), mousePositionVelocity);
}
int MidiKeyboardComponent::remappedXYToNote (Point<int> pos, float& mousePositionVelocity) const
{
if (pos.getY() < blackNoteLength)
{
for (int octaveStart = 12 * (rangeStart / 12); octaveStart <= rangeEnd; octaveStart += 12)
{
for (int i = 0; i < 5; ++i)
{
const int note = octaveStart + blackNotes [i];
if (note >= rangeStart && note <= rangeEnd)
{
int kx, kw;
getKeyPos (note, kx, kw);
kx += xOffset;
if (pos.x >= kx && pos.x < kx + kw)
{
mousePositionVelocity = pos.y / (float) blackNoteLength;
return note;
}
}
}
}
}
for (int octaveStart = 12 * (rangeStart / 12); octaveStart <= rangeEnd; octaveStart += 12)
{
for (int i = 0; i < 7; ++i)
{
const int note = octaveStart + whiteNotes [i];
if (note >= rangeStart && note <= rangeEnd)
{
int kx, kw;
getKeyPos (note, kx, kw);
kx += xOffset;
if (pos.x >= kx && pos.x < kx + kw)
{
const int whiteNoteLength = (orientation == horizontalKeyboard) ? getHeight() : getWidth();
mousePositionVelocity = pos.y / (float) whiteNoteLength;
return note;
}
}
}
}
mousePositionVelocity = 0;
return -1;
}
//==============================================================================
void MidiKeyboardComponent::repaintNote (const int noteNum)
{
if (noteNum >= rangeStart && noteNum <= rangeEnd)
repaint (getWhiteNotePos (noteNum));
}
void MidiKeyboardComponent::paint (Graphics& g)
{
g.fillAll (Colours::white.overlaidWith (findColour (whiteNoteColourId)));
const Colour lineColour (findColour (keySeparatorLineColourId));
const Colour textColour (findColour (textLabelColourId));
int octave;
for (octave = 0; octave < 128; octave += 12)
{
for (int white = 0; white < 7; ++white)
{
const int noteNum = octave + whiteNotes [white];
if (noteNum >= rangeStart && noteNum <= rangeEnd)
{
const Rectangle<int> pos (getWhiteNotePos (noteNum));
drawWhiteNote (noteNum, g, pos.getX(), pos.getY(), pos.getWidth(), pos.getHeight(),
state.isNoteOnForChannels (midiInChannelMask, noteNum),
mouseOverNotes.contains (noteNum), lineColour, textColour);
}
}
}
float x1 = 0.0f, y1 = 0.0f, x2 = 0.0f, y2 = 0.0f;
const int width = getWidth();
const int height = getHeight();
if (orientation == verticalKeyboardFacingLeft)
{
x1 = width - 1.0f;
x2 = width - 5.0f;
}
else if (orientation == verticalKeyboardFacingRight)
x2 = 5.0f;
else
y2 = 5.0f;
int x, w;
getKeyPos (rangeEnd, x, w);
x += w;
const Colour shadowCol (findColour (shadowColourId));
g.setGradientFill (ColourGradient (shadowCol, x1, y1, shadowCol.withAlpha (0.0f), x2, y2, false));
switch (orientation)
{
case horizontalKeyboard: g.fillRect (0, 0, x, 5); break;
case verticalKeyboardFacingLeft: g.fillRect (width - 5, 0, 5, x); break;
case verticalKeyboardFacingRight: g.fillRect (0, 0, 5, x); break;
default: break;
}
g.setColour (lineColour);
switch (orientation)
{
case horizontalKeyboard: g.fillRect (0, height - 1, x, 1); break;
case verticalKeyboardFacingLeft: g.fillRect (0, 0, 1, x); break;
case verticalKeyboardFacingRight: g.fillRect (width - 1, 0, 1, x); break;
default: break;
}
const Colour blackNoteColour (findColour (blackNoteColourId));
for (octave = 0; octave < 128; octave += 12)
{
for (int black = 0; black < 5; ++black)
{
const int noteNum = octave + blackNotes [black];
if (noteNum >= rangeStart && noteNum <= rangeEnd)
{
getKeyPos (noteNum, x, w);
Rectangle<int> pos;
switch (orientation)
{
case horizontalKeyboard: pos.setBounds (x, 0, w, blackNoteLength); break;
case verticalKeyboardFacingLeft: pos.setBounds (width - blackNoteLength, x, blackNoteLength, w); break;
case verticalKeyboardFacingRight: pos.setBounds (0, height - x - w, blackNoteLength, w); break;
default: break;
}
drawBlackNote (noteNum, g, pos.getX(), pos.getY(), pos.getWidth(), pos.getHeight(),
state.isNoteOnForChannels (midiInChannelMask, noteNum),
mouseOverNotes.contains (noteNum), blackNoteColour);
}
}
}
}
void MidiKeyboardComponent::drawWhiteNote (int midiNoteNumber,
Graphics& g, int x, int y, int w, int h,
bool isDown, bool isOver,
const Colour& lineColour,
const Colour& textColour)
{
Colour c (Colours::transparentWhite);
if (isDown) c = findColour (keyDownOverlayColourId);
if (isOver) c = c.overlaidWith (findColour (mouseOverKeyOverlayColourId));
g.setColour (c);
g.fillRect (x, y, w, h);
const String text (getWhiteNoteText (midiNoteNumber));
if (text.isNotEmpty())
{
g.setColour (textColour);
g.setFont (Font (jmin (12.0f, keyWidth * 0.9f)).withHorizontalScale (0.8f));
switch (orientation)
{
case horizontalKeyboard: g.drawFittedText (text, x + 1, y, w - 1, h - 2, Justification::centredBottom, 1); break;
case verticalKeyboardFacingLeft: g.drawFittedText (text, x + 2, y + 2, w - 4, h - 4, Justification::centredLeft, 1); break;
case verticalKeyboardFacingRight: g.drawFittedText (text, x + 2, y + 2, w - 4, h - 4, Justification::centredRight, 1); break;
default: break;
}
}
g.setColour (lineColour);
switch (orientation)
{
case horizontalKeyboard: g.fillRect (x, y, 1, h); break;
case verticalKeyboardFacingLeft: g.fillRect (x, y, w, 1); break;
case verticalKeyboardFacingRight: g.fillRect (x, y + h - 1, w, 1); break;
default: break;
}
if (midiNoteNumber == rangeEnd)
{
switch (orientation)
{
case horizontalKeyboard: g.fillRect (x + w, y, 1, h); break;
case verticalKeyboardFacingLeft: g.fillRect (x, y + h, w, 1); break;
case verticalKeyboardFacingRight: g.fillRect (x, y - 1, w, 1); break;
default: break;
}
}
}
void MidiKeyboardComponent::drawBlackNote (int /*midiNoteNumber*/,
Graphics& g, int x, int y, int w, int h,
bool isDown, bool isOver,
const Colour& noteFillColour)
{
Colour c (noteFillColour);
if (isDown) c = c.overlaidWith (findColour (keyDownOverlayColourId));
if (isOver) c = c.overlaidWith (findColour (mouseOverKeyOverlayColourId));
g.setColour (c);
g.fillRect (x, y, w, h);
if (isDown)
{
g.setColour (noteFillColour);
g.drawRect (x, y, w, h);
}
else
{
g.setColour (c.brighter());
const int xIndent = jmax (1, jmin (w, h) / 8);
switch (orientation)
{
case horizontalKeyboard: g.fillRect (x + xIndent, y, w - xIndent * 2, 7 * h / 8); break;
case verticalKeyboardFacingLeft: g.fillRect (x + w / 8, y + xIndent, w - w / 8, h - xIndent * 2); break;
case verticalKeyboardFacingRight: g.fillRect (x, y + xIndent, 7 * w / 8, h - xIndent * 2); break;
default: break;
}
}
}
void MidiKeyboardComponent::setOctaveForMiddleC (const int octaveNum)
{
octaveNumForMiddleC = octaveNum;
repaint();
}
String MidiKeyboardComponent::getWhiteNoteText (const int midiNoteNumber)
{
if (keyWidth > 11.0f && midiNoteNumber % 12 == 0)
return MidiMessage::getMidiNoteName (midiNoteNumber, true, true, octaveNumForMiddleC);
return String::empty;
}
void MidiKeyboardComponent::drawUpDownButton (Graphics& g, int w, int h,
const bool mouseOver,
const bool buttonDown,
const bool movesOctavesUp)
{
g.fillAll (findColour (upDownButtonBackgroundColourId));
float angle;
switch (orientation)
{
case horizontalKeyboard: angle = movesOctavesUp ? 0.0f : 0.5f; break;
case verticalKeyboardFacingLeft: angle = movesOctavesUp ? 0.25f : 0.75f; break;
case verticalKeyboardFacingRight: angle = movesOctavesUp ? 0.75f : 0.25f; break;
default: jassertfalse; angle = 0; break;
}
Path path;
path.addTriangle (0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f);
path.applyTransform (AffineTransform::rotation (float_Pi * 2.0f * angle, 0.5f, 0.5f));
g.setColour (findColour (upDownButtonArrowColourId)
.withAlpha (buttonDown ? 1.0f : (mouseOver ? 0.6f : 0.4f)));
g.fillPath (path, path.getTransformToScaleToFit (1.0f, 1.0f, w - 2.0f, h - 2.0f, true));
}
void MidiKeyboardComponent::resized()
{
int w = getWidth();
int h = getHeight();
if (w > 0 && h > 0)
{
if (orientation != horizontalKeyboard)
std::swap (w, h);
blackNoteLength = roundToInt (h * 0.7f);
int kx2, kw2;
getKeyPos (rangeEnd, kx2, kw2);
kx2 += kw2;
if ((int) firstKey != rangeStart)
{
int kx1, kw1;
getKeyPos (rangeStart, kx1, kw1);
if (kx2 - kx1 <= w)
{
firstKey = (float) rangeStart;
sendChangeMessage();
repaint();
}
}
scrollDown->setVisible (canScroll && firstKey > (float) rangeStart);
xOffset = 0;
if (canScroll)
{
const int scrollButtonW = jmin (12, w / 2);
Rectangle<int> r (getLocalBounds());
if (orientation == horizontalKeyboard)
{
scrollDown->setBounds (r.removeFromLeft (scrollButtonW));
scrollUp ->setBounds (r.removeFromRight (scrollButtonW));
}
else if (orientation == verticalKeyboardFacingLeft)
{
scrollDown->setBounds (r.removeFromTop (scrollButtonW));
scrollUp ->setBounds (r.removeFromBottom (scrollButtonW));
}
else
{
scrollDown->setBounds (r.removeFromBottom (scrollButtonW));
scrollUp ->setBounds (r.removeFromTop (scrollButtonW));
}
int endOfLastKey, kw;
getKeyPos (rangeEnd, endOfLastKey, kw);
endOfLastKey += kw;
float mousePositionVelocity;
const int spaceAvailable = w;
const int lastStartKey = remappedXYToNote (Point<int> (endOfLastKey - spaceAvailable, 0), mousePositionVelocity) + 1;
if (lastStartKey >= 0 && ((int) firstKey) > lastStartKey)
{
firstKey = (float) jlimit (rangeStart, rangeEnd, lastStartKey);
sendChangeMessage();
}
int newOffset = 0;
getKeyPos ((int) firstKey, newOffset, kw);
xOffset = newOffset;
}
else
{
firstKey = (float) rangeStart;
}
getKeyPos (rangeEnd, kx2, kw2);
scrollUp->setVisible (canScroll && kx2 > w);
repaint();
}
}
//==============================================================================
void MidiKeyboardComponent::handleNoteOn (MidiKeyboardState*, int /*midiChannel*/, int /*midiNoteNumber*/, float /*velocity*/)
{
shouldCheckState = true; // (probably being called from the audio thread, so avoid blocking in here)
}
void MidiKeyboardComponent::handleNoteOff (MidiKeyboardState*, int /*midiChannel*/, int /*midiNoteNumber*/)
{
shouldCheckState = true; // (probably being called from the audio thread, so avoid blocking in here)
}
//==============================================================================
void MidiKeyboardComponent::resetAnyKeysInUse()
{
if (! keysPressed.isZero())
{
for (int i = 128; --i >= 0;)
if (keysPressed[i])
state.noteOff (midiChannel, i);
keysPressed.clear();
}
for (int i = mouseDownNotes.size(); --i >= 0;)
{
const int noteDown = mouseDownNotes.getUnchecked(i);
if (noteDown >= 0)
{
state.noteOff (midiChannel, noteDown);
mouseDownNotes.set (i, -1);
}
mouseOverNotes.set (i, -1);
}
}
void MidiKeyboardComponent::updateNoteUnderMouse (const MouseEvent& e, bool isDown)
{
updateNoteUnderMouse (e.getPosition(), isDown, e.source.getIndex());
}
void MidiKeyboardComponent::updateNoteUnderMouse (Point<int> pos, bool isDown, int fingerNum)
{
float mousePositionVelocity = 0.0f;
const int newNote = xyToNote (pos, mousePositionVelocity);
const int oldNote = mouseOverNotes.getUnchecked (fingerNum);
if (oldNote != newNote)
{
repaintNote (oldNote);
repaintNote (newNote);
mouseOverNotes.set (fingerNum, newNote);
}
int oldNoteDown = mouseDownNotes.getUnchecked (fingerNum);
if (isDown)
{
if (newNote != oldNoteDown)
{
if (oldNoteDown >= 0)
{
mouseDownNotes.set (fingerNum, -1);
if (! mouseDownNotes.contains (oldNoteDown))
state.noteOff (midiChannel, oldNoteDown);
}
if (newNote >= 0)
{
if (! useMousePositionForVelocity)
mousePositionVelocity = 1.0f;
state.noteOn (midiChannel, newNote, mousePositionVelocity * velocity);
mouseDownNotes.set (fingerNum, newNote);
}
}
}
else if (oldNoteDown >= 0)
{
mouseDownNotes.set (fingerNum, -1);
if (! mouseDownNotes.contains (oldNoteDown))
state.noteOff (midiChannel, oldNoteDown);
}
}
void MidiKeyboardComponent::mouseMove (const MouseEvent& e)
{
updateNoteUnderMouse (e, false);
shouldCheckMousePos = false;
}
void MidiKeyboardComponent::mouseDrag (const MouseEvent& e)
{
float mousePositionVelocity;
const int newNote = xyToNote (e.getPosition(), mousePositionVelocity);
if (newNote >= 0)
mouseDraggedToKey (newNote, e);
updateNoteUnderMouse (e, true);
}
bool MidiKeyboardComponent::mouseDownOnKey (int, const MouseEvent&) { return true; }
void MidiKeyboardComponent::mouseDraggedToKey (int, const MouseEvent&) {}
void MidiKeyboardComponent::mouseUpOnKey (int, const MouseEvent&) {}
void MidiKeyboardComponent::mouseDown (const MouseEvent& e)
{
float mousePositionVelocity;
const int newNote = xyToNote (e.getPosition(), mousePositionVelocity);
if (newNote >= 0 && mouseDownOnKey (newNote, e))
{
updateNoteUnderMouse (e, true);
shouldCheckMousePos = true;
}
}
void MidiKeyboardComponent::mouseUp (const MouseEvent& e)
{
updateNoteUnderMouse (e, false);
shouldCheckMousePos = false;
float mousePositionVelocity;
const int note = xyToNote (e.getPosition(), mousePositionVelocity);
if (note >= 0)
mouseUpOnKey (note, e);
}
void MidiKeyboardComponent::mouseEnter (const MouseEvent& e)
{
updateNoteUnderMouse (e, false);
}
void MidiKeyboardComponent::mouseExit (const MouseEvent& e)
{
updateNoteUnderMouse (e, false);
}
void MidiKeyboardComponent::mouseWheelMove (const MouseEvent&, const MouseWheelDetails& wheel)
{
const float amount = (orientation == horizontalKeyboard && wheel.deltaX != 0)
? wheel.deltaX : (orientation == verticalKeyboardFacingLeft ? wheel.deltaY
: -wheel.deltaY);
setLowestVisibleKeyFloat (firstKey - amount * keyWidth);
}
void MidiKeyboardComponent::timerCallback()
{
if (shouldCheckState)
{
shouldCheckState = false;
for (int i = rangeStart; i <= rangeEnd; ++i)
{
if (keysCurrentlyDrawnDown[i] != state.isNoteOnForChannels (midiInChannelMask, i))
{
keysCurrentlyDrawnDown.setBit (i, state.isNoteOnForChannels (midiInChannelMask, i));
repaintNote (i);
}
}
}
if (shouldCheckMousePos)
{
const Array<MouseInputSource>& mouseSources = Desktop::getInstance().getMouseSources();
for (MouseInputSource* mi = mouseSources.begin(), * const e = mouseSources.end(); mi != e; ++mi)
updateNoteUnderMouse (getLocalPoint (nullptr, mi->getScreenPosition()), mi->isDragging(), mi->getIndex());
}
}
//==============================================================================
void MidiKeyboardComponent::clearKeyMappings()
{
resetAnyKeysInUse();
keyPressNotes.clear();
keyPresses.clear();
}
void MidiKeyboardComponent::setKeyPressForNote (const KeyPress& key,
const int midiNoteOffsetFromC)
{
removeKeyPressForNote (midiNoteOffsetFromC);
keyPressNotes.add (midiNoteOffsetFromC);
keyPresses.add (key);
}
void MidiKeyboardComponent::removeKeyPressForNote (const int midiNoteOffsetFromC)
{
for (int i = keyPressNotes.size(); --i >= 0;)
{
if (keyPressNotes.getUnchecked (i) == midiNoteOffsetFromC)
{
keyPressNotes.remove (i);
keyPresses.remove (i);
}
}
}
void MidiKeyboardComponent::setKeyPressBaseOctave (const int newOctaveNumber)
{
jassert (newOctaveNumber >= 0 && newOctaveNumber <= 10);
keyMappingOctave = newOctaveNumber;
}
bool MidiKeyboardComponent::keyStateChanged (const bool /*isKeyDown*/)
{
bool keyPressUsed = false;
for (int i = keyPresses.size(); --i >= 0;)
{
const int note = 12 * keyMappingOctave + keyPressNotes.getUnchecked (i);
if (keyPresses.getReference(i).isCurrentlyDown())
{
if (! keysPressed [note])
{
keysPressed.setBit (note);
state.noteOn (midiChannel, note, velocity);
keyPressUsed = true;
}
}
else
{
if (keysPressed [note])
{
keysPressed.clearBit (note);
state.noteOff (midiChannel, note);
keyPressUsed = true;
}
}
}
return keyPressUsed;
}
void MidiKeyboardComponent::focusLost (FocusChangeType)
{
resetAnyKeysInUse();
}

+ 409
- 0
source/modules/daz-plugins/juce-host/juce_MidiKeyboardComponent.h View File

@@ -0,0 +1,409 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MIDIKEYBOARDCOMPONENT_H_INCLUDED
#define JUCE_MIDIKEYBOARDCOMPONENT_H_INCLUDED
//==============================================================================
/**
A component that displays a piano keyboard, whose notes can be clicked on.
This component will mimic a physical midi keyboard, showing the current state of
a MidiKeyboardState object. When the on-screen keys are clicked on, it will play these
notes by calling the noteOn() and noteOff() methods of its MidiKeyboardState object.
Another feature is that the computer keyboard can also be used to play notes. By
default it maps the top two rows of a standard querty keyboard to the notes, but
these can be remapped if needed. It will only respond to keypresses when it has
the keyboard focus, so to disable this feature you can call setWantsKeyboardFocus (false).
The component is also a ChangeBroadcaster, so if you want to be informed when the
keyboard is scrolled, you can register a ChangeListener for callbacks.
@see MidiKeyboardState
*/
class JUCE_API MidiKeyboardComponent : public Component,
public MidiKeyboardStateListener,
public ChangeBroadcaster,
private Timer
{
public:
//==============================================================================
/** The direction of the keyboard.
@see setOrientation
*/
enum Orientation
{
horizontalKeyboard,
verticalKeyboardFacingLeft,
verticalKeyboardFacingRight,
};
/** Creates a MidiKeyboardComponent.
@param state the midi keyboard model that this component will represent
@param orientation whether the keyboard is horizonal or vertical
*/
MidiKeyboardComponent (MidiKeyboardState& state,
Orientation orientation);
/** Destructor. */
~MidiKeyboardComponent();
//==============================================================================
/** Changes the velocity used in midi note-on messages that are triggered by clicking
on the component.
Values are 0 to 1.0, where 1.0 is the heaviest.
@see setMidiChannel
*/
void setVelocity (float velocity, bool useMousePositionForVelocity);
/** Changes the midi channel number that will be used for events triggered by clicking
on the component.
The channel must be between 1 and 16 (inclusive). This is the channel that will be
passed on to the MidiKeyboardState::noteOn() method when the user clicks the component.
Although this is the channel used for outgoing events, the component can display
incoming events from more than one channel - see setMidiChannelsToDisplay()
@see setVelocity
*/
void setMidiChannel (int midiChannelNumber);
/** Returns the midi channel that the keyboard is using for midi messages.
@see setMidiChannel
*/
int getMidiChannel() const noexcept { return midiChannel; }
/** Sets a mask to indicate which incoming midi channels should be represented by
key movements.
The mask is a set of bits, where bit 0 = midi channel 1, bit 1 = midi channel 2, etc.
If the MidiKeyboardState has a key down for any of the channels whose bits are set
in this mask, the on-screen keys will also go down.
By default, this mask is set to 0xffff (all channels displayed).
@see setMidiChannel
*/
void setMidiChannelsToDisplay (int midiChannelMask);
/** Returns the current set of midi channels represented by the component.
This is the value that was set with setMidiChannelsToDisplay().
*/
int getMidiChannelsToDisplay() const noexcept { return midiInChannelMask; }
//==============================================================================
/** Changes the width used to draw the white keys. */
void setKeyWidth (float widthInPixels);
/** Returns the width that was set by setKeyWidth(). */
float getKeyWidth() const noexcept { return keyWidth; }
/** Changes the keyboard's current direction. */
void setOrientation (Orientation newOrientation);
/** Returns the keyboard's current direction. */
Orientation getOrientation() const noexcept { return orientation; }
/** Sets the range of midi notes that the keyboard will be limited to.
By default the range is 0 to 127 (inclusive), but you can limit this if you
only want a restricted set of the keys to be shown.
Note that the values here are inclusive and must be between 0 and 127.
*/
void setAvailableRange (int lowestNote,
int highestNote);
/** Returns the first note in the available range.
@see setAvailableRange
*/
int getRangeStart() const noexcept { return rangeStart; }
/** Returns the last note in the available range.
@see setAvailableRange
*/
int getRangeEnd() const noexcept { return rangeEnd; }
/** If the keyboard extends beyond the size of the component, this will scroll
it to show the given key at the start.
Whenever the keyboard's position is changed, this will use the ChangeBroadcaster
base class to send a callback to any ChangeListeners that have been registered.
*/
void setLowestVisibleKey (int noteNumber);
/** Returns the number of the first key shown in the component.
@see setLowestVisibleKey
*/
int getLowestVisibleKey() const noexcept { return (int) firstKey; }
/** Returns the length of the black notes.
This will be their vertical or horizontal length, depending on the keyboard's orientation.
*/
int getBlackNoteLength() const noexcept { return blackNoteLength; }
/** If set to true, then scroll buttons will appear at either end of the keyboard
if there are too many notes to fit them all in the component at once.
*/
void setScrollButtonsVisible (bool canScroll);
//==============================================================================
/** A set of colour IDs to use to change the colour of various aspects of the keyboard.
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
methods.
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
*/
enum ColourIds
{
whiteNoteColourId = 0x1005000,
blackNoteColourId = 0x1005001,
keySeparatorLineColourId = 0x1005002,
mouseOverKeyOverlayColourId = 0x1005003, /**< This colour will be overlaid on the normal note colour. */
keyDownOverlayColourId = 0x1005004, /**< This colour will be overlaid on the normal note colour. */
textLabelColourId = 0x1005005,
upDownButtonBackgroundColourId = 0x1005006,
upDownButtonArrowColourId = 0x1005007,
shadowColourId = 0x1005008
};
/** Returns the position within the component of the left-hand edge of a key.
Depending on the keyboard's orientation, this may be a horizontal or vertical
distance, in either direction.
*/
int getKeyStartPosition (int midiNoteNumber) const;
/** Returns the key at a given coordinate. */
int getNoteAtPosition (Point<int> position);
//==============================================================================
/** Deletes all key-mappings.
@see setKeyPressForNote
*/
void clearKeyMappings();
/** Maps a key-press to a given note.
@param key the key that should trigger the note
@param midiNoteOffsetFromC how many semitones above C the triggered note should
be. The actual midi note that gets played will be
this value + (12 * the current base octave). To change
the base octave, see setKeyPressBaseOctave()
*/
void setKeyPressForNote (const KeyPress& key,
int midiNoteOffsetFromC);
/** Removes any key-mappings for a given note.
For a description of what the note number means, see setKeyPressForNote().
*/
void removeKeyPressForNote (int midiNoteOffsetFromC);
/** Changes the base note above which key-press-triggered notes are played.
The set of key-mappings that trigger notes can be moved up and down to cover
the entire scale using this method.
The value passed in is an octave number between 0 and 10 (inclusive), and
indicates which C is the base note to which the key-mapped notes are
relative.
*/
void setKeyPressBaseOctave (int newOctaveNumber);
/** This sets the octave number which is shown as the octave number for middle C.
This affects only the default implementation of getWhiteNoteText(), which
passes this octave number to MidiMessage::getMidiNoteName() in order to
get the note text. See MidiMessage::getMidiNoteName() for more info about
the parameter.
By default this value is set to 3.
@see getOctaveForMiddleC
*/
void setOctaveForMiddleC (int octaveNumForMiddleC);
/** This returns the value set by setOctaveForMiddleC().
@see setOctaveForMiddleC
*/
int getOctaveForMiddleC() const noexcept { return octaveNumForMiddleC; }
//==============================================================================
/** @internal */
void paint (Graphics&) override;
/** @internal */
void resized() override;
/** @internal */
void mouseMove (const MouseEvent&) override;
/** @internal */
void mouseDrag (const MouseEvent&) override;
/** @internal */
void mouseDown (const MouseEvent&) override;
/** @internal */
void mouseUp (const MouseEvent&) override;
/** @internal */
void mouseEnter (const MouseEvent&) override;
/** @internal */
void mouseExit (const MouseEvent&) override;
/** @internal */
void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
/** @internal */
void timerCallback() override;
/** @internal */
bool keyStateChanged (bool isKeyDown) override;
/** @internal */
void focusLost (FocusChangeType) override;
/** @internal */
void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override;
/** @internal */
void handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber) override;
/** @internal */
void colourChanged() override;
protected:
//==============================================================================
/** Draws a white note in the given rectangle.
isOver indicates whether the mouse is over the key, isDown indicates whether the key is
currently pressed down.
When doing this, be sure to note the keyboard's orientation.
*/
virtual void drawWhiteNote (int midiNoteNumber,
Graphics& g,
int x, int y, int w, int h,
bool isDown, bool isOver,
const Colour& lineColour,
const Colour& textColour);
/** Draws a black note in the given rectangle.
isOver indicates whether the mouse is over the key, isDown indicates whether the key is
currently pressed down.
When doing this, be sure to note the keyboard's orientation.
*/
virtual void drawBlackNote (int midiNoteNumber,
Graphics& g,
int x, int y, int w, int h,
bool isDown, bool isOver,
const Colour& noteFillColour);
/** Allows text to be drawn on the white notes.
By default this is used to label the C in each octave, but could be used for other things.
@see setOctaveForMiddleC
*/
virtual String getWhiteNoteText (const int midiNoteNumber);
/** Draws the up and down buttons that change the base note. */
virtual void drawUpDownButton (Graphics& g, int w, int h,
const bool isMouseOver,
const bool isButtonPressed,
const bool movesOctavesUp);
/** Callback when the mouse is clicked on a key.
You could use this to do things like handle right-clicks on keys, etc.
Return true if you want the click to trigger the note, or false if you
want to handle it yourself and not have the note played.
@see mouseDraggedToKey
*/
virtual bool mouseDownOnKey (int midiNoteNumber, const MouseEvent& e);
/** Callback when the mouse is dragged from one key onto another.
@see mouseDownOnKey
*/
virtual void mouseDraggedToKey (int midiNoteNumber, const MouseEvent& e);
/** Callback when the mouse is released from a key.
@see mouseDownOnKey
*/
virtual void mouseUpOnKey (int midiNoteNumber, const MouseEvent& e);
/** Calculates the positon of a given midi-note.
This can be overridden to create layouts with custom key-widths.
@param midiNoteNumber the note to find
@param keyWidth the desired width in pixels of one key - see setKeyWidth()
@param x the x position of the left-hand edge of the key (this method
always works in terms of a horizontal keyboard)
@param w the width of the key
*/
virtual void getKeyPosition (int midiNoteNumber, float keyWidth,
int& x, int& w) const;
private:
//==============================================================================
friend class MidiKeyboardUpDownButton;
MidiKeyboardState& state;
int xOffset, blackNoteLength;
float keyWidth;
Orientation orientation;
int midiChannel, midiInChannelMask;
float velocity;
Array<int> mouseOverNotes, mouseDownNotes;
BigInteger keysPressed, keysCurrentlyDrawnDown;
bool shouldCheckState;
int rangeStart, rangeEnd;
float firstKey;
bool canScroll, useMousePositionForVelocity, shouldCheckMousePos;
ScopedPointer<Button> scrollDown, scrollUp;
Array<KeyPress> keyPresses;
Array<int> keyPressNotes;
int keyMappingOctave, octaveNumForMiddleC;
static const uint8 whiteNotes[];
static const uint8 blackNotes[];
void getKeyPos (int midiNoteNumber, int& x, int& w) const;
int xyToNote (Point<int>, float& mousePositionVelocity);
int remappedXYToNote (Point<int>, float& mousePositionVelocity) const;
void resetAnyKeysInUse();
void updateNoteUnderMouse (Point<int>, bool isDown, int fingerNum);
void updateNoteUnderMouse (const MouseEvent&, bool isDown);
void repaintNote (const int midiNoteNumber);
void setLowestVisibleKeyFloat (float noteNumber);
Rectangle<int> getWhiteNotePos (int noteNumber) const;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiKeyboardComponent)
};
#endif // JUCE_MIDIKEYBOARDCOMPONENT_H_INCLUDED

+ 181
- 0
source/modules/daz-plugins/juce-patchbay.cpp View File

@@ -0,0 +1,181 @@
/*
* Carla Native Plugins
* Copyright (C) 2013-2014 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program 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.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#include "CarlaNative.hpp"

#include "juce_audio_processors.h"
#include "juce_gui_extra.h"

#include "JucePluginWindow.hpp"

using namespace juce;

// -----------------------------------------------------------------------

#include "juce-host/FilterGraph.h"
#include "juce-host/InternalFilters.h"
#include "juce-host/GraphEditorPanel.h"

// -----------------------------------------------------------------------

class JucePatchbayPlugin : public NativePluginClass
{
public:
JucePatchbayPlugin(const NativeHostDescriptor* const host)
: NativePluginClass(host),
graph(formatManager),
fAudioBuffer(1, 0)
{
formatManager.addDefaultFormats();
formatManager.addFormat(new InternalPluginFormat());
graph.ready();

graph.getGraph().setPlayConfigDetails(2, 2, getSampleRate(), static_cast<int>(getBufferSize()));

fMidiBuffer.ensureSize(512*2);
fMidiBuffer.clear();
}

~JucePatchbayPlugin() override
{
}

protected:
// -------------------------------------------------------------------
// Plugin process calls

void activate() override
{
graph.getGraph().prepareToPlay(getSampleRate(), static_cast<int>(getBufferSize()));

fAudioBuffer.setSize(2, static_cast<int>(getBufferSize()));
}

void deactivate() override
{
graph.getGraph().releaseResources();
}

void process(float** inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const, const uint32_t) override
{
fAudioBuffer.copyFrom(0, 0, inBuffer[0], static_cast<int>(frames));
fAudioBuffer.copyFrom(1, 0, inBuffer[1], static_cast<int>(frames));

graph.getGraph().processBlock(fAudioBuffer, fMidiBuffer);

FloatVectorOperations::copy(outBuffer[0], fAudioBuffer.getSampleData(0), static_cast<int>(frames));
FloatVectorOperations::copy(outBuffer[1], fAudioBuffer.getSampleData(1), static_cast<int>(frames));
}

// -------------------------------------------------------------------
// Plugin UI calls

void uiShow(const bool show) override
{
if (show)
{
if (fWindow == nullptr)
{
fWindow = new JucePluginWindow();
fWindow->setName(getUiName());
fWindow->setResizable(true, false);
}

if (fComponent == nullptr)
{
fComponent = new GraphDocumentComponent(graph);
fComponent->setSize(300, 300);
}

fWindow->show(fComponent);
}
else if (fWindow != nullptr)
{
fWindow->hide();

fComponent = nullptr;
fWindow = nullptr;
}
}

void uiIdle() override
{
if (fWindow == nullptr)
return;

if (fWindow->wasClosedByUser())
{
uiShow(false);
uiClosed();
}
}

private:
AudioPluginFormatManager formatManager;
FilterGraph graph;

AudioSampleBuffer fAudioBuffer;
MidiBuffer fMidiBuffer;

ScopedPointer<GraphDocumentComponent> fComponent;
ScopedPointer<JucePluginWindow> fWindow;

//OwnedArray <PluginDescription> internalTypes;
//KnownPluginList knownPluginList;
//KnownPluginList::SortMethod pluginSortMethod;

PluginClassEND(JucePatchbayPlugin)
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(JucePatchbayPlugin)
};

// -----------------------------------------------------------------------

static const NativePluginDescriptor jucePatchbayDesc = {
/* category */ PLUGIN_CATEGORY_UTILITY,
/* hints */ static_cast<NativePluginHints>(PLUGIN_IS_SYNTH|PLUGIN_HAS_UI/*|PLUGIN_USES_STATE*/),
/* supports */ static_cast<NativePluginSupports>(0x0),
/* audioIns */ 2,
/* audioOuts */ 2,
/* midiIns */ 1,
/* midiOuts */ 1,
/* paramIns */ 0,
/* paramOuts */ 0,
/* name */ "Juce Patchbay",
/* label */ "jucePatchbay",
/* maker */ "falkTX",
/* copyright */ "GNU GPL v2+",
PluginDescriptorFILL(JucePatchbayPlugin)
};

// -----------------------------------------------------------------------

CARLA_EXPORT
void carla_register_native_plugin_jucePatchbay()
{
carla_register_native_plugin(&jucePatchbayDesc);
}

// -----------------------------------------------------------------------

#include "juce-host/juce_MidiKeyboardComponent.h"
#include "juce-host/juce_MidiKeyboardComponent.cpp"

#include "juce-host/FilterGraph.cpp"
#include "juce-host/InternalFilters.cpp"
#include "juce-host/GraphEditorPanel.cpp"

// -----------------------------------------------------------------------

+ 0
- 2
source/modules/daz-plugins/vex-synth.cpp View File

@@ -1019,8 +1019,6 @@ protected:

void uiShow(const bool show) override
{
MessageManagerLock mmLock;

if (show)
{
if (fWindow == nullptr)


+ 12
- 7
source/utils/JucePluginWindow.hpp View File

@@ -24,11 +24,11 @@

namespace juce {

class PluginWindow : public DocumentWindow
class JucePluginWindow : public DocumentWindow
{
public:
PluginWindow()
: DocumentWindow("PluginWindow", Colour(50, 50, 200), DocumentWindow::closeButton, false),
JucePluginWindow()
: DocumentWindow("JucePluginWindow", Colour(50, 50, 200), DocumentWindow::closeButton, false),
fClosed(false)
{
setVisible(false);
@@ -38,12 +38,16 @@ public:
setUsingNativeTitleBar(true);
}

void show(Component* const comp)
void show(Component* const comp, const bool useContentOwned = false)
{
fClosed = false;

centreWithSize(comp->getWidth(), comp->getHeight());
setContentNonOwned(comp, true);

if (useContentOwned)
setContentOwned(comp, false);
else
setContentNonOwned(comp, true);

if (! isOnDesktop())
addToDesktop();
@@ -75,12 +79,13 @@ protected:
private:
volatile bool fClosed;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(JucePluginWindow)
};

} // namespace juce

typedef juce::PluginWindow JucePluginWindow;
using juce::JucePluginWindow;
//typedef juce::PluginWindow JucePluginWindow;

// -----------------------------------------------------------------------



Loading…
Cancel
Save