mirror of https://github.com/dcoredump/dexed.git
parent
1692e9a267
commit
36bd40be31
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,269 @@ |
||||
/* |
||||
============================================================================== |
||||
|
||||
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. |
||||
|
||||
============================================================================== |
||||
*/ |
||||
|
||||
// Your project must contain an AppConfig.h file with your project-specific settings in it, |
||||
// and your header search path must make it accessible to the module's files. |
||||
#include "AppConfig.h" |
||||
#include "../utility/juce_CheckSettingMacros.h" |
||||
|
||||
#if JucePlugin_Build_VST3 |
||||
|
||||
#define JUCE_MAC_WINDOW_VISIBITY_BODGE 1 |
||||
|
||||
#include "../utility/juce_IncludeSystemHeaders.h" |
||||
#include "../utility/juce_IncludeModuleHeaders.h" |
||||
#include "../utility/juce_FakeMouseMoveGenerator.h" |
||||
#include "../utility/juce_CarbonVisibility.h" |
||||
|
||||
#undef Component |
||||
#undef Point |
||||
|
||||
//============================================================================== |
||||
namespace juce |
||||
{ |
||||
static void initialiseMac() |
||||
{ |
||||
#if ! JUCE_64BIT |
||||
NSApplicationLoad(); |
||||
#endif |
||||
} |
||||
|
||||
#if ! JUCE_64BIT |
||||
static void updateComponentPos (Component* const comp) |
||||
{ |
||||
DBG ("updateComponentPos()"); |
||||
|
||||
HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int) |
||||
comp->getProperties() ["dummyViewRef"].toString().getHexValue64(); |
||||
|
||||
HIRect r; |
||||
HIViewGetFrame (dummyView, &r); |
||||
HIViewRef root; |
||||
HIViewFindByID (HIViewGetRoot (HIViewGetWindow (dummyView)), kHIViewWindowContentID, &root); |
||||
HIViewConvertRect (&r, HIViewGetSuperview (dummyView), root); |
||||
|
||||
Rect windowPos; |
||||
GetWindowBounds (HIViewGetWindow (dummyView), kWindowContentRgn, &windowPos); |
||||
|
||||
comp->setTopLeftPosition ((int) (windowPos.left + r.origin.x), |
||||
(int) (windowPos.top + r.origin.y)); |
||||
} |
||||
|
||||
static pascal OSStatus viewBoundsChangedEvent (EventHandlerCallRef, EventRef, void* user) |
||||
{ |
||||
updateComponentPos ((Component*) user); |
||||
return noErr; |
||||
} |
||||
#endif |
||||
|
||||
static void* attachComponentToWindowRef (Component* comp, void* windowRef, bool isHIView) |
||||
{ |
||||
DBG ("attachComponentToWindowRef()"); |
||||
|
||||
JUCE_AUTORELEASEPOOL |
||||
{ |
||||
#if JUCE_64BIT |
||||
NSView* parentView = (NSView*) windowRef; |
||||
|
||||
#if JucePlugin_EditorRequiresKeyboardFocus |
||||
comp->addToDesktop (0, parentView); |
||||
#else |
||||
comp->addToDesktop (ComponentPeer::windowIgnoresKeyPresses, parentView); |
||||
#endif |
||||
|
||||
// (this workaround is because Wavelab provides a zero-size parent view..) |
||||
if ([parentView frame].size.height == 0) |
||||
[((NSView*) comp->getWindowHandle()) setFrameOrigin: NSZeroPoint]; |
||||
|
||||
comp->setVisible (true); |
||||
comp->toFront (false); |
||||
|
||||
[[parentView window] setAcceptsMouseMovedEvents: YES]; |
||||
return parentView; |
||||
|
||||
#else |
||||
//treat NSView like 64bit |
||||
if (! isHIView) |
||||
{ |
||||
NSView* parentView = (NSView*) windowRef; |
||||
|
||||
#if JucePlugin_EditorRequiresKeyboardFocus |
||||
comp->addToDesktop (0, parentView); |
||||
#else |
||||
comp->addToDesktop (ComponentPeer::windowIgnoresKeyPresses, parentView); |
||||
#endif |
||||
|
||||
// (this workaround is because Wavelab provides a zero-size parent view..) |
||||
if ([parentView frame].size.height == 0) |
||||
[((NSView*) comp->getWindowHandle()) setFrameOrigin: NSZeroPoint]; |
||||
|
||||
comp->setVisible (true); |
||||
comp->toFront (false); |
||||
|
||||
[[parentView window] setAcceptsMouseMovedEvents: YES]; |
||||
return parentView; |
||||
} |
||||
|
||||
NSWindow* hostWindow = [[NSWindow alloc] initWithWindowRef: windowRef]; |
||||
[hostWindow retain]; |
||||
[hostWindow setCanHide: YES]; |
||||
[hostWindow setReleasedWhenClosed: YES]; |
||||
|
||||
HIViewRef parentView = nullptr; |
||||
|
||||
WindowAttributes attributes; |
||||
GetWindowAttributes ((WindowRef) windowRef, &attributes); |
||||
|
||||
if ((attributes & kWindowCompositingAttribute) != 0) |
||||
{ |
||||
HIViewRef root = HIViewGetRoot ((WindowRef) windowRef); |
||||
HIViewFindByID (root, kHIViewWindowContentID, &parentView); |
||||
|
||||
if (parentView == nullptr) |
||||
parentView = root; |
||||
} |
||||
else |
||||
{ |
||||
GetRootControl ((WindowRef) windowRef, (ControlRef*) &parentView); |
||||
|
||||
if (parentView == nullptr) |
||||
CreateRootControl ((WindowRef) windowRef, (ControlRef*) &parentView); |
||||
} |
||||
|
||||
// It seems that the only way to successfully position our overlaid window is by putting a dummy |
||||
// HIView into the host's carbon window, and then catching events to see when it gets repositioned |
||||
HIViewRef dummyView = 0; |
||||
HIImageViewCreate (0, &dummyView); |
||||
HIRect r = { {0, 0}, { (float) comp->getWidth(), (float) comp->getHeight()} }; |
||||
HIViewSetFrame (dummyView, &r); |
||||
HIViewAddSubview (parentView, dummyView); |
||||
comp->getProperties().set ("dummyViewRef", String::toHexString ((pointer_sized_int) (void*) dummyView)); |
||||
|
||||
EventHandlerRef ref; |
||||
const EventTypeSpec kControlBoundsChangedEvent = { kEventClassControl, kEventControlBoundsChanged }; |
||||
InstallEventHandler (GetControlEventTarget (dummyView), NewEventHandlerUPP (viewBoundsChangedEvent), 1, &kControlBoundsChangedEvent, (void*) comp, &ref); |
||||
comp->getProperties().set ("boundsEventRef", String::toHexString ((pointer_sized_int) (void*) ref)); |
||||
|
||||
updateComponentPos (comp); |
||||
|
||||
#if ! JucePlugin_EditorRequiresKeyboardFocus |
||||
comp->addToDesktop (ComponentPeer::windowIsTemporary | ComponentPeer::windowIgnoresKeyPresses); |
||||
#else |
||||
comp->addToDesktop (ComponentPeer::windowIsTemporary); |
||||
#endif |
||||
|
||||
comp->setVisible (true); |
||||
comp->toFront (false); |
||||
|
||||
NSView* pluginView = (NSView*) comp->getWindowHandle(); |
||||
NSWindow* pluginWindow = [pluginView window]; |
||||
[pluginWindow setExcludedFromWindowsMenu: YES]; |
||||
[pluginWindow setCanHide: YES]; |
||||
|
||||
[hostWindow addChildWindow: pluginWindow |
||||
ordered: NSWindowAbove]; |
||||
[hostWindow orderFront: nil]; |
||||
[pluginWindow orderFront: nil]; |
||||
|
||||
attachWindowHidingHooks (comp, (WindowRef) windowRef, hostWindow); |
||||
|
||||
return hostWindow; |
||||
#endif |
||||
} |
||||
} |
||||
|
||||
static void detachComponentFromWindowRef (Component* comp, void* nsWindow, bool isHIView) |
||||
{ |
||||
#if JUCE_64BIT |
||||
comp->removeFromDesktop(); |
||||
#else |
||||
//treat NSView like 64bit |
||||
if (! isHIView) |
||||
{ |
||||
comp->removeFromDesktop(); |
||||
} |
||||
else |
||||
{ |
||||
JUCE_AUTORELEASEPOOL |
||||
{ |
||||
EventHandlerRef ref = (EventHandlerRef) (void*) (pointer_sized_int) |
||||
comp->getProperties() ["boundsEventRef"].toString().getHexValue64(); |
||||
RemoveEventHandler (ref); |
||||
|
||||
removeWindowHidingHooks (comp); |
||||
|
||||
HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int) |
||||
comp->getProperties() ["dummyViewRef"].toString().getHexValue64(); |
||||
|
||||
if (HIViewIsValid (dummyView)) |
||||
CFRelease (dummyView); |
||||
|
||||
NSWindow* hostWindow = (NSWindow*) nsWindow; |
||||
NSView* pluginView = (NSView*) comp->getWindowHandle(); |
||||
NSWindow* pluginWindow = [pluginView window]; |
||||
|
||||
[hostWindow removeChildWindow: pluginWindow]; |
||||
comp->removeFromDesktop(); |
||||
|
||||
[hostWindow release]; |
||||
} |
||||
|
||||
// The event loop needs to be run between closing the window and deleting the plugin, |
||||
// presumably to let the cocoa objects get tidied up. Leaving out this line causes crashes |
||||
// in Live and Reaper when you delete the plugin with its window open. |
||||
// (Doing it this way rather than using a single longer timout means that we can guarantee |
||||
// how many messages will be dispatched, which seems to be vital in Reaper) |
||||
for (int i = 20; --i >= 0;) |
||||
MessageManager::getInstance()->runDispatchLoopUntil (1); |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
static void setNativeHostWindowSize (void* nsWindow, Component* component, int newWidth, int newHeight, bool isHIView) |
||||
{ |
||||
JUCE_AUTORELEASEPOOL |
||||
{ |
||||
#if JUCE_64BIT |
||||
component->setSize (newWidth, newHeight); |
||||
#else |
||||
if (! isHIView) |
||||
{ //Treat NSView like 64bit: |
||||
component->setSize (newWidth, newHeight); |
||||
} |
||||
else if (HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int) |
||||
component->getProperties() ["dummyViewRef"].toString().getHexValue64()) |
||||
{ |
||||
HIRect frameRect; |
||||
HIViewGetFrame (dummyView, &frameRect); |
||||
frameRect.size.width = newWidth; |
||||
frameRect.size.height = newHeight; |
||||
HIViewSetFrame (dummyView, &frameRect); |
||||
} |
||||
#endif |
||||
} |
||||
} |
||||
} // (juce namespace) |
||||
|
||||
|
||||
#endif |
@ -0,0 +1,393 @@ |
||||
/*
|
||||
============================================================================== |
||||
|
||||
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_VST3COMMON_H_INCLUDED |
||||
#define JUCE_VST3COMMON_H_INCLUDED |
||||
|
||||
//==============================================================================
|
||||
#define JUCE_DECLARE_VST3_COM_REF_METHODS \ |
||||
Steinberg::uint32 PLUGIN_API addRef() override { return (Steinberg::uint32) ++refCount; } \
|
||||
Steinberg::uint32 PLUGIN_API release() override { const int r = --refCount; if (r == 0) delete this; return (Steinberg::uint32) r; } |
||||
|
||||
#define JUCE_DECLARE_VST3_COM_QUERY_METHODS \ |
||||
Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID, void** obj) override \
|
||||
{ \
|
||||
jassertfalse; \
|
||||
*obj = nullptr; \
|
||||
return Steinberg::kNotImplemented; \
|
||||
} |
||||
|
||||
static bool doUIDsMatch (const Steinberg::TUID a, const Steinberg::TUID b) noexcept |
||||
{ |
||||
return std::memcmp (a, b, sizeof (Steinberg::TUID)) == 0; |
||||
} |
||||
|
||||
#define TEST_FOR_AND_RETURN_IF_VALID(ClassType) \ |
||||
if (doUIDsMatch (iid, ClassType::iid)) \
|
||||
{ \
|
||||
addRef(); \
|
||||
*obj = dynamic_cast<ClassType*> (this); \
|
||||
return Steinberg::kResultOk; \
|
||||
} |
||||
|
||||
#define TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID(CommonClassType, SourceClassType) \ |
||||
if (doUIDsMatch (iid, CommonClassType::iid)) \
|
||||
{ \
|
||||
addRef(); \
|
||||
*obj = (CommonClassType*) static_cast<SourceClassType*> (this); \
|
||||
return Steinberg::kResultOk; \
|
||||
} |
||||
|
||||
//==============================================================================
|
||||
static juce::String toString (const Steinberg::char8* string) noexcept { return juce::String (string); } |
||||
static juce::String toString (const Steinberg::char16* string) noexcept { return juce::String (juce::CharPointer_UTF16 ((juce::CharPointer_UTF16::CharType*) string)); } |
||||
|
||||
// NB: The casts are handled by a Steinberg::UString operator
|
||||
static juce::String toString (const Steinberg::UString128& string) noexcept { return toString (static_cast<const Steinberg::char16*> (string)); } |
||||
static juce::String toString (const Steinberg::UString256& string) noexcept { return toString (static_cast<const Steinberg::char16*> (string)); } |
||||
|
||||
static void toString128 (Steinberg::Vst::String128 result, const juce::String& source) |
||||
{ |
||||
Steinberg::UString (result, 128).fromAscii (source.toUTF8()); |
||||
} |
||||
|
||||
static Steinberg::Vst::TChar* toString (const juce::String& source) noexcept |
||||
{ |
||||
return reinterpret_cast<Steinberg::Vst::TChar*> (source.toUTF16().getAddress()); |
||||
} |
||||
|
||||
#if JUCE_WINDOWS |
||||
static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeHWND; |
||||
#else |
||||
static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeNSView; |
||||
#endif |
||||
|
||||
|
||||
//==============================================================================
|
||||
/** For the sake of simplicity, there can only be 1 arrangement type per channel count.
|
||||
i.e.: 4 channels == k31Cine OR k40Cine |
||||
*/ |
||||
static Steinberg::Vst::SpeakerArrangement getArrangementForNumChannels (int numChannels) noexcept |
||||
{ |
||||
using namespace Steinberg::Vst::SpeakerArr; |
||||
|
||||
switch (numChannels) |
||||
{ |
||||
case 0: return kEmpty; |
||||
case 1: return kMono; |
||||
case 2: return kStereo; |
||||
case 3: return k30Cine; |
||||
case 4: return k31Cine; |
||||
case 5: return k50; |
||||
case 6: return k51; |
||||
case 7: return k61Cine; |
||||
case 8: return k71CineFullFront; |
||||
case 9: return k90; |
||||
case 10: return k91; |
||||
case 11: return k101; |
||||
case 12: return k111; |
||||
case 13: return k130; |
||||
case 14: return k131; |
||||
case 24: return (Steinberg::Vst::SpeakerArrangement) 1929904127; // k222
|
||||
default: break; |
||||
} |
||||
|
||||
jassert (numChannels >= 0); |
||||
|
||||
juce::BigInteger bi; |
||||
bi.setRange (0, jmin (numChannels, (int) (sizeof (Steinberg::Vst::SpeakerArrangement) * 8)), true); |
||||
return (Steinberg::Vst::SpeakerArrangement) bi.toInt64(); |
||||
} |
||||
|
||||
//==============================================================================
|
||||
template <class ObjectType> |
||||
class ComSmartPtr |
||||
{ |
||||
public: |
||||
ComSmartPtr() noexcept : source (nullptr) {} |
||||
ComSmartPtr (ObjectType* object) noexcept : source (object) { if (source != nullptr) source->addRef(); } |
||||
ComSmartPtr (const ComSmartPtr& other) noexcept : source (other.source) { if (source != nullptr) source->addRef(); } |
||||
~ComSmartPtr() { if (source != nullptr) source->release(); } |
||||
|
||||
operator ObjectType*() const noexcept { return source; } |
||||
ObjectType* get() const noexcept { return source; } |
||||
ObjectType& operator*() const noexcept { return *source; } |
||||
ObjectType* operator->() const noexcept { return source; } |
||||
|
||||
ComSmartPtr& operator= (const ComSmartPtr& other) { return operator= (other.source); } |
||||
|
||||
ComSmartPtr& operator= (ObjectType* const newObjectToTakePossessionOf) |
||||
{ |
||||
ComSmartPtr p (newObjectToTakePossessionOf); |
||||
std::swap (p.source, source); |
||||
return *this; |
||||
} |
||||
|
||||
bool operator== (ObjectType* const other) noexcept { return source == other; } |
||||
bool operator!= (ObjectType* const other) noexcept { return source != other; } |
||||
|
||||
bool loadFrom (Steinberg::FUnknown* o) |
||||
{ |
||||
*this = nullptr; |
||||
return o != nullptr && o->queryInterface (ObjectType::iid, (void**) &source) == Steinberg::kResultOk; |
||||
} |
||||
|
||||
bool loadFrom (Steinberg::IPluginFactory* factory, const Steinberg::TUID& uuid) |
||||
{ |
||||
jassert (factory != nullptr); |
||||
*this = nullptr; |
||||
return factory->createInstance (uuid, ObjectType::iid, (void**) &source) == Steinberg::kResultOk; |
||||
} |
||||
|
||||
private: |
||||
ObjectType* source; |
||||
}; |
||||
|
||||
//==============================================================================
|
||||
class MidiEventList : public Steinberg::Vst::IEventList |
||||
{ |
||||
public: |
||||
MidiEventList() {} |
||||
virtual ~MidiEventList() {} |
||||
|
||||
JUCE_DECLARE_VST3_COM_REF_METHODS |
||||
JUCE_DECLARE_VST3_COM_QUERY_METHODS |
||||
|
||||
//==============================================================================
|
||||
void clear() |
||||
{ |
||||
events.clearQuick(); |
||||
} |
||||
|
||||
Steinberg::int32 PLUGIN_API getEventCount() override |
||||
{ |
||||
return (Steinberg::int32) events.size(); |
||||
} |
||||
|
||||
// NB: This has to cope with out-of-range indexes from some plugins.
|
||||
Steinberg::tresult PLUGIN_API getEvent (Steinberg::int32 index, Steinberg::Vst::Event& e) override |
||||
{ |
||||
if (isPositiveAndBelow ((int) index, events.size())) |
||||
{ |
||||
e = events.getReference ((int) index); |
||||
return Steinberg::kResultTrue; |
||||
} |
||||
|
||||
return Steinberg::kResultFalse; |
||||
} |
||||
|
||||
Steinberg::tresult PLUGIN_API addEvent (Steinberg::Vst::Event& e) override |
||||
{ |
||||
events.add (e); |
||||
return Steinberg::kResultTrue; |
||||
} |
||||
|
||||
//==============================================================================
|
||||
static void toMidiBuffer (MidiBuffer& result, Steinberg::Vst::IEventList& eventList) |
||||
{ |
||||
const int32 numEvents = eventList.getEventCount(); |
||||
|
||||
for (Steinberg::int32 i = 0; i < numEvents; ++i) |
||||
{ |
||||
Steinberg::Vst::Event e; |
||||
|
||||
if (eventList.getEvent (i, e) == Steinberg::kResultOk) |
||||
{ |
||||
switch (e.type) |
||||
{ |
||||
case Steinberg::Vst::Event::kNoteOnEvent: |
||||
result.addEvent (MidiMessage::noteOn (createSafeChannel (e.noteOn.channel), |
||||
createSafeNote (e.noteOn.pitch), |
||||
(Steinberg::uint8) denormaliseToMidiValue (e.noteOn.velocity)), |
||||
e.sampleOffset); |
||||
break; |
||||
|
||||
case Steinberg::Vst::Event::kNoteOffEvent: |
||||
result.addEvent (MidiMessage::noteOff (createSafeChannel (e.noteOff.channel), |
||||
createSafeNote (e.noteOff.pitch), |
||||
(Steinberg::uint8) denormaliseToMidiValue (e.noteOff.velocity)), |
||||
e.sampleOffset); |
||||
break; |
||||
|
||||
case Steinberg::Vst::Event::kPolyPressureEvent: |
||||
result.addEvent (MidiMessage::aftertouchChange (createSafeChannel (e.polyPressure.channel), |
||||
createSafeNote (e.polyPressure.pitch), |
||||
denormaliseToMidiValue (e.polyPressure.pressure)), |
||||
e.sampleOffset); |
||||
break; |
||||
|
||||
case Steinberg::Vst::Event::kDataEvent: |
||||
result.addEvent (MidiMessage::createSysExMessage (e.data.bytes, e.data.size), |
||||
e.sampleOffset); |
||||
break; |
||||
|
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void toEventList (Steinberg::Vst::IEventList& result, MidiBuffer& midiBuffer) |
||||
{ |
||||
MidiBuffer::Iterator iterator (midiBuffer); |
||||
MidiMessage msg; |
||||
int midiEventPosition = 0; |
||||
|
||||
enum { maxNumEvents = 2048 }; // Steinberg's Host Checker states that no more than 2048 events are allowed at once
|
||||
int numEvents = 0; |
||||
|
||||
while (iterator.getNextEvent (msg, midiEventPosition)) |
||||
{ |
||||
if (++numEvents > maxNumEvents) |
||||
break; |
||||
|
||||
Steinberg::Vst::Event e = { 0 }; |
||||
|
||||
if (msg.isNoteOn()) |
||||
{ |
||||
e.type = Steinberg::Vst::Event::kNoteOnEvent; |
||||
e.noteOn.channel = createSafeChannel (msg.getChannel()); |
||||
e.noteOn.pitch = createSafeNote (msg.getNoteNumber()); |
||||
e.noteOn.velocity = normaliseMidiValue (msg.getVelocity()); |
||||
e.noteOn.length = 0; |
||||
e.noteOn.tuning = 0.0f; |
||||
e.noteOn.noteId = -1; |
||||
} |
||||
else if (msg.isNoteOff()) |
||||
{ |
||||
e.type = Steinberg::Vst::Event::kNoteOffEvent; |
||||
e.noteOff.channel = createSafeChannel (msg.getChannel()); |
||||
e.noteOff.pitch = createSafeNote (msg.getNoteNumber()); |
||||
e.noteOff.velocity = normaliseMidiValue (msg.getVelocity()); |
||||
e.noteOff.tuning = 0.0f; |
||||
e.noteOff.noteId = -1; |
||||
} |
||||
else if (msg.isSysEx()) |
||||
{ |
||||
e.type = Steinberg::Vst::Event::kDataEvent; |
||||
e.data.bytes = msg.getSysExData(); |
||||
e.data.size = msg.getSysExDataSize(); |
||||
e.data.type = Steinberg::Vst::DataEvent::kMidiSysEx; |
||||
} |
||||
else if (msg.isAftertouch()) |
||||
{ |
||||
e.type = Steinberg::Vst::Event::kPolyPressureEvent; |
||||
e.polyPressure.channel = createSafeChannel (msg.getChannel()); |
||||
e.polyPressure.pitch = createSafeNote (msg.getNoteNumber()); |
||||
e.polyPressure.pressure = normaliseMidiValue (msg.getAfterTouchValue()); |
||||
} |
||||
else |
||||
{ |
||||
continue; |
||||
} |
||||
|
||||
e.busIndex = 0; |
||||
e.sampleOffset = midiEventPosition; |
||||
|
||||
result.addEvent (e); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
Array<Steinberg::Vst::Event, CriticalSection> events; |
||||
Atomic<int> refCount; |
||||
|
||||
static Steinberg::int16 createSafeChannel (int channel) noexcept { return (Steinberg::int16) jlimit (0, 15, channel - 1); } |
||||
static int createSafeChannel (Steinberg::int16 channel) noexcept { return (int) jlimit (1, 16, channel + 1); } |
||||
|
||||
static Steinberg::int16 createSafeNote (int note) noexcept { return (Steinberg::int16) jlimit (0, 127, note); } |
||||
static int createSafeNote (Steinberg::int16 note) noexcept { return jlimit (0, 127, (int) note); } |
||||
|
||||
static float normaliseMidiValue (int value) noexcept { return jlimit (0.0f, 1.0f, (float) value / 127.0f); } |
||||
static int denormaliseToMidiValue (float value) noexcept { return roundToInt (jlimit (0.0f, 127.0f, value * 127.0f)); } |
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiEventList) |
||||
}; |
||||
|
||||
//==============================================================================
|
||||
namespace VST3BufferExchange |
||||
{ |
||||
typedef Array<float*> Bus; |
||||
typedef Array<Bus> BusMap; |
||||
|
||||
/** Assigns a series of AudioSampleBuffer's channels to an AudioBusBuffers'
|
||||
|
||||
@warning For speed, does not check the channel count and offsets |
||||
according to the AudioSampleBuffer |
||||
*/ |
||||
void associateBufferTo (Steinberg::Vst::AudioBusBuffers& vstBuffers, |
||||
Bus& bus, |
||||
const AudioSampleBuffer& buffer, |
||||
int numChannels, int channelStartOffset, |
||||
int sampleOffset = 0) noexcept |
||||
{ |
||||
const int channelEnd = numChannels + channelStartOffset; |
||||
jassert (channelEnd >= 0 && channelEnd <= buffer.getNumChannels()); |
||||
|
||||
bus.clearQuick(); |
||||
|
||||
for (int i = channelStartOffset; i < channelEnd; ++i) |
||||
bus.add (buffer.getSampleData (i, sampleOffset)); |
||||
|
||||
vstBuffers.channelBuffers32 = bus.getRawDataPointer(); |
||||
vstBuffers.numChannels = numChannels; |
||||
vstBuffers.silenceFlags = 0; |
||||
} |
||||
|
||||
static void mapBufferToBusses (Array<Steinberg::Vst::AudioBusBuffers>& result, |
||||
Steinberg::Vst::IAudioProcessor& processor, |
||||
BusMap& busMapToUse, |
||||
bool isInput, int numBusses, |
||||
AudioSampleBuffer& source) |
||||
{ |
||||
int channelIndexOffset = 0; |
||||
|
||||
for (int i = 0; i < numBusses; ++i) |
||||
{ |
||||
Steinberg::Vst::SpeakerArrangement arrangement = 0; |
||||
processor.getBusArrangement (isInput ? Steinberg::Vst::kInput : Steinberg::Vst::kOutput, |
||||
(Steinberg::int32) i, arrangement); |
||||
|
||||
const int numChansForBus = BigInteger ((juce::int64) arrangement).countNumberOfSetBits(); |
||||
|
||||
if (i >= result.size()) |
||||
result.add (Steinberg::Vst::AudioBusBuffers()); |
||||
|
||||
if (i >= busMapToUse.size()) |
||||
busMapToUse.add (Bus()); |
||||
|
||||
if (numChansForBus > 0) |
||||
{ |
||||
associateBufferTo (result.getReference (i), |
||||
busMapToUse.getReference (i), |
||||
source, numChansForBus, channelIndexOffset); |
||||
} |
||||
|
||||
channelIndexOffset += numChansForBus; |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif // JUCE_VST3COMMON_H_INCLUDED
|
@ -0,0 +1,171 @@ |
||||
/*
|
||||
============================================================================== |
||||
|
||||
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_VST3HEADERS_H_INCLUDED |
||||
#define JUCE_VST3HEADERS_H_INCLUDED |
||||
|
||||
#undef Point |
||||
#undef Component |
||||
|
||||
// Wow, those Steinberg guys really don't worry too much about compiler warnings.
|
||||
#if _MSC_VER |
||||
#pragma warning (disable: 4505) |
||||
#pragma warning (push, 0) |
||||
#pragma warning (disable: 4702) |
||||
#elif __clang__ |
||||
#pragma clang diagnostic push |
||||
#pragma clang diagnostic ignored "-Wnon-virtual-dtor" |
||||
#pragma clang diagnostic ignored "-Wreorder" |
||||
#pragma clang diagnostic ignored "-Wunsequenced" |
||||
#pragma clang diagnostic ignored "-Wint-to-pointer-cast" |
||||
#pragma clang diagnostic ignored "-Wunused-parameter" |
||||
#pragma clang diagnostic ignored "-Wconversion" |
||||
#pragma clang diagnostic ignored "-Woverloaded-virtual" |
||||
#pragma clang diagnostic ignored "-Wshadow" |
||||
#endif |
||||
|
||||
/* These files come with the Steinberg VST3 SDK - to get them, you'll need to
|
||||
visit the Steinberg website and agree to whatever is currently required to |
||||
get them. |
||||
|
||||
Then, you'll need to make sure your include path contains your "VST3 SDK" |
||||
directory (or whatever you've named it on your machine). The Introjucer has |
||||
a special box for setting this path. |
||||
*/ |
||||
#if JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY |
||||
#include <base/source/fstring.h> |
||||
#include <pluginterfaces/base/conststringtable.h> |
||||
#include <pluginterfaces/base/funknown.h> |
||||
#include <pluginterfaces/base/ipluginbase.h> |
||||
#include <pluginterfaces/base/ustring.h> |
||||
#include <pluginterfaces/gui/iplugview.h> |
||||
#include <pluginterfaces/vst/ivstattributes.h> |
||||
#include <pluginterfaces/vst/ivstaudioprocessor.h> |
||||
#include <pluginterfaces/vst/ivstcomponent.h> |
||||
#include <pluginterfaces/vst/ivstcontextmenu.h> |
||||
#include <pluginterfaces/vst/ivsteditcontroller.h> |
||||
#include <pluginterfaces/vst/ivstevents.h> |
||||
#include <pluginterfaces/vst/ivsthostapplication.h> |
||||
#include <pluginterfaces/vst/ivstmessage.h> |
||||
#include <pluginterfaces/vst/ivstmidicontrollers.h> |
||||
#include <pluginterfaces/vst/ivstparameterchanges.h> |
||||
#include <pluginterfaces/vst/ivstplugview.h> |
||||
#include <pluginterfaces/vst/ivstprocesscontext.h> |
||||
#include <pluginterfaces/vst/vsttypes.h> |
||||
#include <pluginterfaces/vst/ivstunits.h> |
||||
#include <public.sdk/source/common/memorystream.h> |
||||
#else |
||||
#include <base/source/baseiids.cpp> |
||||
#include <base/source/fatomic.cpp> |
||||
#include <base/source/fbuffer.cpp> |
||||
#include <base/source/fdebug.cpp> |
||||
#include <base/source/fobject.cpp> |
||||
#include <base/source/frect.cpp> |
||||
#include <base/source/fstreamer.cpp> |
||||
#include <base/source/fstring.cpp> |
||||
#include <base/source/fthread.cpp> |
||||
#include <base/source/updatehandler.cpp> |
||||
#include <pluginterfaces/base/conststringtable.cpp> |
||||
#include <pluginterfaces/base/funknown.cpp> |
||||
#include <pluginterfaces/base/ipluginbase.h> |
||||
#include <pluginterfaces/base/ustring.cpp> |
||||
#include <pluginterfaces/gui/iplugview.h> |
||||
#include <public.sdk/source/common/memorystream.cpp> |
||||
#include <public.sdk/source/common/pluginview.cpp> |
||||
#include <public.sdk/source/vst/vsteditcontroller.cpp> |
||||
#include <public.sdk/source/vst/vstbus.cpp> |
||||
#include <public.sdk/source/vst/vstinitiids.cpp> |
||||
#include <public.sdk/source/vst/vstcomponent.cpp> |
||||
#include <public.sdk/source/vst/vstcomponentbase.cpp> |
||||
#include <public.sdk/source/vst/vstparameters.cpp> |
||||
#include <public.sdk/source/vst/hosting/hostclasses.cpp> |
||||
|
||||
//==============================================================================
|
||||
namespace Steinberg |
||||
{ |
||||
/** Missing IIDs */ |
||||
DEF_CLASS_IID (IPluginBase) |
||||
DEF_CLASS_IID (IPlugView) |
||||
DEF_CLASS_IID (IPlugFrame) |
||||
DEF_CLASS_IID (IBStream) |
||||
DEF_CLASS_IID (ISizeableStream) |
||||
DEF_CLASS_IID (IPluginFactory) |
||||
DEF_CLASS_IID (IPluginFactory2) |
||||
DEF_CLASS_IID (IPluginFactory3) |
||||
} |
||||
#endif //JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY
|
||||
|
||||
#if _MSC_VER |
||||
#pragma warning (pop) |
||||
#elif __clang__ |
||||
#pragma clang diagnostic pop |
||||
#endif |
||||
|
||||
//==============================================================================
|
||||
#undef ASSERT |
||||
#undef WARNING |
||||
#undef PRINTSYSERROR |
||||
#undef DEBUGSTR |
||||
#undef DBPRT0 |
||||
#undef DBPRT1 |
||||
#undef DBPRT2 |
||||
#undef DBPRT3 |
||||
#undef DBPRT4 |
||||
#undef DBPRT5 |
||||
#undef min |
||||
#undef max |
||||
#undef MIN |
||||
#undef MAX |
||||
#undef calloc |
||||
#undef free |
||||
#undef malloc |
||||
#undef realloc |
||||
#undef NEW |
||||
#undef NEWVEC |
||||
#undef VERIFY |
||||
#undef VERIFY_IS |
||||
#undef VERIFY_NOT |
||||
#undef META_CREATE_FUNC |
||||
#undef CLASS_CREATE_FUNC |
||||
#undef SINGLE_CREATE_FUNC |
||||
#undef _META_CLASS |
||||
#undef _META_CLASS_IFACE |
||||
#undef _META_CLASS_SINGLE |
||||
#undef META_CLASS |
||||
#undef META_CLASS_IFACE |
||||
#undef META_CLASS_SINGLE |
||||
#undef SINGLETON |
||||
#undef OBJ_METHODS |
||||
#undef QUERY_INTERFACE |
||||
#undef LICENCE_UID |
||||
#undef BEGIN_FACTORY |
||||
#undef DEF_CLASS |
||||
#undef DEF_CLASS1 |
||||
#undef DEF_CLASS2 |
||||
#undef DEF_CLASS_W |
||||
#undef END_FACTORY |
||||
#undef Point |
||||
#undef Component |
||||
|
||||
#endif // JUCE_VST3HEADERS_H_INCLUDED
|
@ -0,0 +1,41 @@ |
||||
/*
|
||||
============================================================================== |
||||
|
||||
This file is part of the juce_core module of the JUCE library. |
||||
Copyright (c) 2013 - Raw Material Software Ltd. |
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with |
||||
or without fee is hereby granted, provided that the above copyright notice and this |
||||
permission notice appear in all copies. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD |
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN |
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER |
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
|
||||
------------------------------------------------------------------------------ |
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module! |
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are |
||||
using any other modules, be sure to check that you also comply with their license. |
||||
|
||||
For more details, visit www.juce.com |
||||
|
||||
============================================================================== |
||||
*/ |
||||
|
||||
FileFilter::FileFilter (const String& filterDescription) |
||||
: description (filterDescription) |
||||
{ |
||||
} |
||||
|
||||
FileFilter::~FileFilter() |
||||
{ |
||||
} |
||||
|
||||
const String& FileFilter::getDescription() const noexcept |
||||
{ |
||||
return description; |
||||
} |
@ -0,0 +1,77 @@ |
||||
/*
|
||||
============================================================================== |
||||
|
||||
This file is part of the juce_core module of the JUCE library. |
||||
Copyright (c) 2013 - Raw Material Software Ltd. |
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with |
||||
or without fee is hereby granted, provided that the above copyright notice and this |
||||
permission notice appear in all copies. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD |
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN |
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER |
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
|
||||
------------------------------------------------------------------------------ |
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module! |
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are |
||||
using any other modules, be sure to check that you also comply with their license. |
||||
|
||||
For more details, visit www.juce.com |
||||
|
||||
============================================================================== |
||||
*/ |
||||
|
||||
#ifndef JUCE_FILEFILTER_H_INCLUDED |
||||
#define JUCE_FILEFILTER_H_INCLUDED |
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Interface for deciding which files are suitable for something. |
||||
|
||||
For example, this is used by DirectoryContentsList to select which files |
||||
go into the list. |
||||
|
||||
@see WildcardFileFilter, DirectoryContentsList, FileListComponent, FileBrowserComponent |
||||
*/ |
||||
class JUCE_API FileFilter |
||||
{ |
||||
public: |
||||
//==============================================================================
|
||||
/** Creates a filter with the given description.
|
||||
|
||||
The description can be returned later with the getDescription() method. |
||||
*/ |
||||
FileFilter (const String& filterDescription); |
||||
|
||||
/** Destructor. */ |
||||
virtual ~FileFilter(); |
||||
|
||||
//==============================================================================
|
||||
/** Returns the description that the filter was created with. */ |
||||
const String& getDescription() const noexcept; |
||||
|
||||
//==============================================================================
|
||||
/** Should return true if this file is suitable for inclusion in whatever context
|
||||
the object is being used. |
||||
*/ |
||||
virtual bool isFileSuitable (const File& file) const = 0; |
||||
|
||||
/** Should return true if this directory is suitable for inclusion in whatever context
|
||||
the object is being used. |
||||
*/ |
||||
virtual bool isDirectorySuitable (const File& file) const = 0; |
||||
|
||||
|
||||
protected: |
||||
//==============================================================================
|
||||
String description; |
||||
}; |
||||
|
||||
|
||||
#endif // JUCE_FILEFILTER_H_INCLUDED
|
@ -0,0 +1,77 @@ |
||||
/*
|
||||
============================================================================== |
||||
|
||||
This file is part of the juce_core module of the JUCE library. |
||||
Copyright (c) 2013 - Raw Material Software Ltd. |
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with |
||||
or without fee is hereby granted, provided that the above copyright notice and this |
||||
permission notice appear in all copies. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD |
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN |
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER |
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
|
||||
------------------------------------------------------------------------------ |
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module! |
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are |
||||
using any other modules, be sure to check that you also comply with their license. |
||||
|
||||
For more details, visit www.juce.com |
||||
|
||||
============================================================================== |
||||
*/ |
||||
|
||||
WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns, |
||||
const String& directoryWildcardPatterns, |
||||
const String& desc) |
||||
: FileFilter (desc.isEmpty() ? fileWildcardPatterns |
||||
: (desc + " (" + fileWildcardPatterns + ")")) |
||||
{ |
||||
parse (fileWildcardPatterns, fileWildcards); |
||||
parse (directoryWildcardPatterns, directoryWildcards); |
||||
} |
||||
|
||||
WildcardFileFilter::~WildcardFileFilter() |
||||
{ |
||||
} |
||||
|
||||
bool WildcardFileFilter::isFileSuitable (const File& file) const |
||||
{ |
||||
return match (file, fileWildcards); |
||||
} |
||||
|
||||
bool WildcardFileFilter::isDirectorySuitable (const File& file) const |
||||
{ |
||||
return match (file, directoryWildcards); |
||||
} |
||||
|
||||
//==============================================================================
|
||||
void WildcardFileFilter::parse (const String& pattern, StringArray& result) |
||||
{ |
||||
result.addTokens (pattern.toLowerCase(), ";,", "\"'"); |
||||
|
||||
result.trim(); |
||||
result.removeEmptyStrings(); |
||||
|
||||
// special case for *.*, because people use it to mean "any file", but it
|
||||
// would actually ignore files with no extension.
|
||||
for (int i = result.size(); --i >= 0;) |
||||
if (result[i] == "*.*") |
||||
result.set (i, "*"); |
||||
} |
||||
|
||||
bool WildcardFileFilter::match (const File& file, const StringArray& wildcards) |
||||
{ |
||||
const String filename (file.getFileName()); |
||||
|
||||
for (int i = wildcards.size(); --i >= 0;) |
||||
if (filename.matchesWildcard (wildcards[i], true)) |
||||
return true; |
||||
|
||||
return false; |
||||
} |
@ -0,0 +1,86 @@ |
||||
/*
|
||||
============================================================================== |
||||
|
||||
This file is part of the juce_core module of the JUCE library. |
||||
Copyright (c) 2013 - Raw Material Software Ltd. |
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with |
||||
or without fee is hereby granted, provided that the above copyright notice and this |
||||
permission notice appear in all copies. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD |
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN |
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER |
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
|
||||
------------------------------------------------------------------------------ |
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module! |
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are |
||||
using any other modules, be sure to check that you also comply with their license. |
||||
|
||||
For more details, visit www.juce.com |
||||
|
||||
============================================================================== |
||||
*/ |
||||
|
||||
#ifndef JUCE_WILDCARDFILEFILTER_H_INCLUDED |
||||
#define JUCE_WILDCARDFILEFILTER_H_INCLUDED |
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A type of FileFilter that works by wildcard pattern matching. |
||||
|
||||
This filter only allows files that match one of the specified patterns, but |
||||
allows all directories through. |
||||
|
||||
@see FileFilter, DirectoryContentsList, FileListComponent, FileBrowserComponent |
||||
*/ |
||||
class JUCE_API WildcardFileFilter : public FileFilter |
||||
{ |
||||
public: |
||||
//==============================================================================
|
||||
/**
|
||||
Creates a wildcard filter for one or more patterns. |
||||
|
||||
The wildcardPatterns parameter is a comma or semicolon-delimited set of |
||||
patterns, e.g. "*.wav;*.aiff" would look for files ending in either .wav |
||||
or .aiff. |
||||
|
||||
Passing an empty string as a pattern will fail to match anything, so by leaving |
||||
either the file or directory pattern parameter empty means you can control |
||||
whether files or directories are found. |
||||
|
||||
The description is a name to show the user in a list of possible patterns, so |
||||
for the wav/aiff example, your description might be "audio files". |
||||
*/ |
||||
WildcardFileFilter (const String& fileWildcardPatterns, |
||||
const String& directoryWildcardPatterns, |
||||
const String& description); |
||||
|
||||
/** Destructor. */ |
||||
~WildcardFileFilter(); |
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the filename matches one of the patterns specified. */ |
||||
bool isFileSuitable (const File& file) const; |
||||
|
||||
/** This always returns true. */ |
||||
bool isDirectorySuitable (const File& file) const; |
||||
|
||||
private: |
||||
//==============================================================================
|
||||
StringArray fileWildcards, directoryWildcards; |
||||
|
||||
static void parse (const String& pattern, StringArray& result); |
||||
static bool match (const File& file, const StringArray& wildcards); |
||||
|
||||
JUCE_LEAK_DETECTOR (WildcardFileFilter) |
||||
}; |
||||
|
||||
|
||||
|
||||
#endif // JUCE_WILDCARDFILEFILTER_H_INCLUDED
|
@ -0,0 +1,259 @@ |
||||
/*
|
||||
============================================================================== |
||||
|
||||
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. |
||||
|
||||
============================================================================== |
||||
*/ |
||||
|
||||
enum { magicMastSlaveConnectionHeader = 0x712baf04 }; |
||||
|
||||
static const char* startMessage = "__ipc_st"; |
||||
static const char* killMessage = "__ipc_k_"; |
||||
static const char* pingMessage = "__ipc_p_"; |
||||
enum { specialMessageSize = 8 }; |
||||
|
||||
static String getCommandLinePrefix (const String& commandLineUniqueID) |
||||
{ |
||||
return "--" + commandLineUniqueID + ":"; |
||||
} |
||||
|
||||
//==============================================================================
|
||||
// This thread sends and receives ping messages every second, so that it
|
||||
// can find out if the other process has stopped running.
|
||||
struct ChildProcessPingThread : public Thread, |
||||
private AsyncUpdater |
||||
{ |
||||
ChildProcessPingThread() : Thread ("IPC ping"), timeoutMs (8000) |
||||
{ |
||||
pingReceived(); |
||||
} |
||||
|
||||
static bool isPingMessage (const MemoryBlock& m) noexcept |
||||
{ |
||||
return memcmp (m.getData(), pingMessage, specialMessageSize) == 0; |
||||
} |
||||
|
||||
void pingReceived() noexcept { countdown = timeoutMs / 1000 + 1; } |
||||
void triggerConnectionLostMessage() { triggerAsyncUpdate(); } |
||||
|
||||
virtual bool sendPingMessage (const MemoryBlock&) = 0; |
||||
virtual void pingFailed() = 0; |
||||
|
||||
int timeoutMs; |
||||
|
||||
private: |
||||
Atomic<int> countdown; |
||||
|
||||
void handleAsyncUpdate() override { pingFailed(); } |
||||
|
||||
void run() override |
||||
{ |
||||
while (! threadShouldExit()) |
||||
{ |
||||
if (--countdown <= 0 || ! sendPingMessage (MemoryBlock (pingMessage, specialMessageSize))) |
||||
{ |
||||
triggerConnectionLostMessage(); |
||||
break; |
||||
} |
||||
|
||||
wait (1000); |
||||
} |
||||
} |
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ChildProcessPingThread) |
||||
}; |
||||
|
||||
//==============================================================================
|
||||
struct ChildProcessMaster::Connection : public InterprocessConnection, |
||||
private ChildProcessPingThread |
||||
{ |
||||
Connection (ChildProcessMaster& m, const String& pipeName) |
||||
: InterprocessConnection (false, magicMastSlaveConnectionHeader), owner (m) |
||||
{ |
||||
if (createPipe (pipeName, timeoutMs)) |
||||
startThread (4); |
||||
} |
||||
|
||||
~Connection() |
||||
{ |
||||
stopThread (10000); |
||||
} |
||||
|
||||
private: |
||||
void connectionMade() override {} |
||||
void connectionLost() override { owner.handleConnectionLost(); } |
||||
|
||||
bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToSlave (m); } |
||||
void pingFailed() override { connectionLost(); } |
||||
|
||||
void messageReceived (const MemoryBlock& m) override |
||||
{ |
||||
pingReceived(); |
||||
|
||||
if (m.getSize() != specialMessageSize || ! isPingMessage (m)) |
||||
owner.handleMessageFromSlave (m); |
||||
} |
||||
|
||||
ChildProcessMaster& owner; |
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection) |
||||
}; |
||||
|
||||
//==============================================================================
|
||||
ChildProcessMaster::ChildProcessMaster() {} |
||||
|
||||
ChildProcessMaster::~ChildProcessMaster() |
||||
{ |
||||
if (connection != nullptr) |
||||
{ |
||||
sendMessageToSlave (MemoryBlock (killMessage, specialMessageSize)); |
||||
connection->disconnect(); |
||||
connection = nullptr; |
||||
} |
||||
} |
||||
|
||||
void ChildProcessMaster::handleConnectionLost() {} |
||||
|
||||
bool ChildProcessMaster::sendMessageToSlave (const MemoryBlock& mb) |
||||
{ |
||||
if (connection != nullptr) |
||||
return connection->sendMessage (mb); |
||||
|
||||
jassertfalse; // this can only be used when the connection is active!
|
||||
return false; |
||||
} |
||||
|
||||
bool ChildProcessMaster::launchSlaveProcess (const File& executable, const String& commandLineUniqueID) |
||||
{ |
||||
connection = nullptr; |
||||
jassert (childProcess.kill()); |
||||
|
||||
const String pipeName ("p" + String::toHexString (Random().nextInt64())); |
||||
|
||||
StringArray args; |
||||
args.add (executable.getFullPathName()); |
||||
args.add (getCommandLinePrefix (commandLineUniqueID) + pipeName); |
||||
|
||||
if (childProcess.start (args)) |
||||
{ |
||||
connection = new Connection (*this, pipeName); |
||||
|
||||
if (connection->isConnected()) |
||||
{ |
||||
sendMessageToSlave (MemoryBlock (startMessage, specialMessageSize)); |
||||
return true; |
||||
} |
||||
|
||||
connection = nullptr; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
//==============================================================================
|
||||
struct ChildProcessSlave::Connection : public InterprocessConnection, |
||||
private ChildProcessPingThread |
||||
{ |
||||
Connection (ChildProcessSlave& p, const String& pipeName) |
||||
: InterprocessConnection (false, magicMastSlaveConnectionHeader), owner (p) |
||||
{ |
||||
connectToPipe (pipeName, timeoutMs); |
||||
startThread (4); |
||||
} |
||||
|
||||
~Connection() |
||||
{ |
||||
stopThread (10000); |
||||
} |
||||
|
||||
private: |
||||
ChildProcessSlave& owner; |
||||
|
||||
void connectionMade() override {} |
||||
void connectionLost() override { owner.handleConnectionLost(); } |
||||
|
||||
bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToMaster (m); } |
||||
void pingFailed() override { connectionLost(); } |
||||
|
||||
void messageReceived (const MemoryBlock& m) override |
||||
{ |
||||
pingReceived(); |
||||
|
||||
if (m.getSize() == specialMessageSize) |
||||
{ |
||||
if (isPingMessage (m)) |
||||
return; |
||||
|
||||
if (memcmp (m.getData(), killMessage, specialMessageSize) == 0) |
||||
{ |
||||
triggerConnectionLostMessage(); |
||||
return; |
||||
} |
||||
|
||||
if (memcmp (m.getData(), startMessage, specialMessageSize) == 0) |
||||
{ |
||||
owner.handleConnectionMade(); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
owner.handleMessageFromMaster (m); |
||||
} |
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection) |
||||
}; |
||||
|
||||
//==============================================================================
|
||||
ChildProcessSlave::ChildProcessSlave() {} |
||||
ChildProcessSlave::~ChildProcessSlave() {} |
||||
|
||||
void ChildProcessSlave::handleConnectionMade() {} |
||||
void ChildProcessSlave::handleConnectionLost() {} |
||||
|
||||
bool ChildProcessSlave::sendMessageToMaster (const MemoryBlock& mb) |
||||
{ |
||||
if (connection != nullptr) |
||||
return connection->sendMessage (mb); |
||||
|
||||
jassertfalse; // this can only be used when the connection is active!
|
||||
return false; |
||||
} |
||||
|
||||
bool ChildProcessSlave::initialiseFromCommandLine (const String& commandLine, |
||||
const String& commandLineUniqueID) |
||||
{ |
||||
String prefix (getCommandLinePrefix (commandLineUniqueID)); |
||||
|
||||
if (commandLine.trim().startsWith (prefix)) |
||||
{ |
||||
String pipeName (commandLine.fromFirstOccurrenceOf (prefix, false, false) |
||||
.upToFirstOccurrenceOf (" ", false, false).trim()); |
||||
|
||||
if (pipeName.isNotEmpty()) |
||||
{ |
||||
connection = new Connection (*this, pipeName); |
||||
|
||||
if (! connection->isConnected()) |
||||
connection = nullptr; |
||||
} |
||||
} |
||||
|
||||
return connection != nullptr; |
||||
} |
@ -0,0 +1,179 @@ |
||||
/*
|
||||
============================================================================== |
||||
|
||||
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_CONNECTEDCHILDPROCESS_H_INCLUDED |
||||
#define JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED |
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Acts as the slave end of a master/slave pair of connected processes. |
||||
|
||||
The ChildProcessSlave and ChildProcessMaster classes make it easy for an app |
||||
to spawn a child process, and to manage a 2-way messaging connection to control it. |
||||
|
||||
To use the system, you need to create subclasses of both ChildProcessSlave and |
||||
ChildProcessMaster. To instantiate the ChildProcessSlave object, you must |
||||
add some code to your main() or JUCEApplication::initialise() function that |
||||
calls the initialiseFromCommandLine() method to check the app's command-line |
||||
parameters to see whether it's being launched as a child process. If this returns |
||||
true then the slave process can be allowed to run, and its handleMessageFromMaster() |
||||
method will be called whenever a message arrives. |
||||
|
||||
The juce demo app has a good example of this class in action. |
||||
|
||||
@see ChildProcessMaster, InterprocessConnection, ChildProcess |
||||
*/ |
||||
class JUCE_API ChildProcessSlave |
||||
{ |
||||
public: |
||||
/** Creates a non-connected slave process.
|
||||
Use initialiseFromCommandLine to connect to a master process. |
||||
*/ |
||||
ChildProcessSlave(); |
||||
|
||||
/** Destructor. */ |
||||
virtual ~ChildProcessSlave(); |
||||
|
||||
/** This checks some command-line parameters to see whether they were generated by
|
||||
ChildProcessMaster::launchSlaveProcess(), and if so, connects to that master process. |
||||
|
||||
In an exe that can be used as a child process, you should add some code to your |
||||
main() or JUCEApplication::initialise() that calls this method. |
||||
|
||||
The commandLineUniqueID should be a short alphanumeric identifier (no spaces!) |
||||
that matches the string passed to ChildProcessMaster::launchSlaveProcess(). |
||||
|
||||
Returns true if the command-line matches and the connection is made successfully. |
||||
*/ |
||||
bool initialiseFromCommandLine (const String& commandLine, |
||||
const String& commandLineUniqueID); |
||||
|
||||
//==============================================================================
|
||||
/** This will be called to deliver messages from the master process.
|
||||
The call will probably be made on a background thread, so be careful with your |
||||
thread-safety! You may want to respond by sending back a message with |
||||
sendMessageToMaster() |
||||
*/ |
||||
virtual void handleMessageFromMaster (const MemoryBlock&) = 0; |
||||
|
||||
/** This will be called when the master process finishes connecting to this slave.
|
||||
The call will probably be made on a background thread, so be careful with your thread-safety! |
||||
*/ |
||||
virtual void handleConnectionMade(); |
||||
|
||||
/** This will be called when the connection to the master process is lost.
|
||||
The call may be made from any thread (including the message thread). |
||||
Typically, if your process only exists to act as a slave, you should probably exit |
||||
when this happens. |
||||
*/ |
||||
virtual void handleConnectionLost(); |
||||
|
||||
/** Tries to send a message to the master process.
|
||||
This returns true if the message was sent, but doesn't check that it actually gets |
||||
delivered at the other end. If successful, the data will emerge in a call to your |
||||
ChildProcessMaster::handleMessageFromSlave(). |
||||
*/ |
||||
bool sendMessageToMaster (const MemoryBlock&); |
||||
|
||||
private: |
||||
struct Connection; |
||||
friend struct Connection; |
||||
friend struct ContainerDeletePolicy<Connection>; |
||||
ScopedPointer<Connection> connection; |
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessSlave) |
||||
}; |
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Acts as the master in a master/slave pair of connected processes. |
||||
|
||||
The ChildProcessSlave and ChildProcessMaster classes make it easy for an app |
||||
to spawn a child process, and to manage a 2-way messaging connection to control it. |
||||
|
||||
To use the system, you need to create subclasses of both ChildProcessSlave and |
||||
ChildProcessMaster. When you want your master process to launch the slave, you |
||||
just call launchSlaveProcess(), and it'll attempt to launch the executable that |
||||
you specify (which may be the same exe), and assuming it has been set-up to |
||||
correctly parse the command-line parameters (see ChildProcessSlave) then a |
||||
two-way connection will be created. |
||||
|
||||
The juce demo app has a good example of this class in action. |
||||
|
||||
@see ChildProcessSlave, InterprocessConnection, ChildProcess |
||||
*/ |
||||
class JUCE_API ChildProcessMaster |
||||
{ |
||||
public: |
||||
/** Creates an uninitialised master process object.
|
||||
Use launchSlaveProcess to launch and connect to a child process. |
||||
*/ |
||||
ChildProcessMaster(); |
||||
|
||||
/** Destructor. */ |
||||
virtual ~ChildProcessMaster(); |
||||
|
||||
/** Attempts to launch and connect to a slave process.
|
||||
This will start the given executable, passing it a special command-line |
||||
parameter based around the commandLineUniqueID string, which must be a |
||||
short alphanumeric string (no spaces!) that identifies your app. The exe |
||||
that gets launched must respond by calling ChildProcessSlave::initialiseFromCommandLine() |
||||
in its startup code, and must use a matching ID to commandLineUniqueID. |
||||
|
||||
If this all works, the method returns true, and you can begin sending and |
||||
receiving messages with the slave process. |
||||
*/ |
||||
bool launchSlaveProcess (const File& executableToLaunch, |
||||
const String& commandLineUniqueID); |
||||
|
||||
/** This will be called to deliver a message from the slave process.
|
||||
The call will probably be made on a background thread, so be careful with your thread-safety! |
||||
*/ |
||||
virtual void handleMessageFromSlave (const MemoryBlock&) = 0; |
||||
|
||||
/** This will be called when the slave process dies or is somehow disconnected.
|
||||
The call will probably be made on a background thread, so be careful with your thread-safety! |
||||
*/ |
||||
virtual void handleConnectionLost(); |
||||
|
||||
/** Attempts to send a message to the slave process.
|
||||
This returns true if the message was dispatched, but doesn't check that it actually |
||||
gets delivered at the other end. If successful, the data will emerge in a call to |
||||
your ChildProcessSlave::handleMessageFromMaster(). |
||||
*/ |
||||
bool sendMessageToSlave (const MemoryBlock&); |
||||
|
||||
private: |
||||
ChildProcess childProcess; |
||||
|
||||
struct Connection; |
||||
friend struct Connection; |
||||
friend struct ContainerDeletePolicy<Connection>; |
||||
ScopedPointer<Connection> connection; |
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessMaster) |
||||
}; |
||||
|
||||
|
||||
#endif // JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED
|
Loading…
Reference in new issue