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