diff --git a/modules/juce_audio_devices/midi_io/juce_MidiInput.h b/modules/juce_audio_devices/midi_io/juce_MidiInput.h index 2a3c44b314..11102abcb9 100644 --- a/modules/juce_audio_devices/midi_io/juce_MidiInput.h +++ b/modules/juce_audio_devices/midi_io/juce_MidiInput.h @@ -72,8 +72,8 @@ public: */ virtual void handlePartialSysexMessage (MidiInput* source, const uint8* messageData, - const int numBytesSoFar, - const double timestamp) + int numBytesSoFar, + double timestamp) { // (this bit is just to avoid compiler warnings about unused variables) (void) source; (void) messageData; (void) numBytesSoFar; (void) timestamp; diff --git a/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h b/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h index 83d0517be6..02aae01088 100644 --- a/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h +++ b/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h @@ -48,8 +48,9 @@ public: pendingDataTime = 0; } + template void pushMidiData (const void* inputData, int numBytes, double time, - MidiInput* input, MidiInputCallback& callback) + UserDataType* input, CallbackType& callback) { const uint8* d = static_cast (inputData); @@ -105,8 +106,9 @@ public: } private: + template void processSysex (const uint8*& d, int& numBytes, double time, - MidiInput* input, MidiInputCallback& callback) + UserDataType* input, CallbackType& callback) { if (*d == 0xf0) { diff --git a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm index 2876f0d997..2b82ee294f 100644 --- a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm +++ b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm @@ -31,6 +31,7 @@ #include #include #include +#include #if JUCE_SUPPORT_CARBON #include @@ -39,6 +40,8 @@ namespace juce { +#include "../../juce_audio_devices/native/juce_MidiDataConcatenator.h" + #if JUCE_SUPPORT_CARBON #include "../../juce_gui_extra/native/juce_mac_CarbonViewWrapperComponent.h" #endif @@ -120,7 +123,7 @@ namespace AudioUnitFormatHelpers if (nameString != 0 && nameString[0] != 0) { const String all ((const char*) nameString + 1, nameString[0]); - DBG ("name: "+ all); + DBG ("name: " + all); manufacturer = all.upToFirstOccurrenceOf (":", false, false).trim(); name = all.fromFirstOccurrenceOf (":", false, false).trim(); @@ -185,9 +188,9 @@ namespace AudioUnitFormatHelpers return false; const char* const utf8 = fileOrIdentifier.toUTF8(); - CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, - strlen (utf8), file.isDirectory()); - if (url != 0) + + if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, + strlen (utf8), file.isDirectory())) { CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url); CFRelease (url); @@ -212,14 +215,12 @@ namespace AudioUnitFormatHelpers if (manuString != 0 && CFGetTypeID (manuString) == CFStringGetTypeID()) manufacturer = String::fromCFString ((CFStringRef) manuString); - short resFileId = CFBundleOpenBundleResourceMap (bundleRef); + const short resFileId = CFBundleOpenBundleResourceMap (bundleRef); UseResFile (resFileId); for (int i = 1; i <= Count1Resources ('thng'); ++i) { - Handle h = Get1IndResource ('thng', i); - - if (h != 0) + if (Handle h = Get1IndResource ('thng', i)) { HLock (h); const uint32* const types = (const uint32*) *h; @@ -275,13 +276,17 @@ class AudioUnitPluginInstance : public AudioPluginInstance public: AudioUnitPluginInstance (const String& fileOrIdentifier) : fileOrIdentifier (fileOrIdentifier), - wantsMidiMessages (false), wasPlaying (false), prepared (false), + wantsMidiMessages (false), + producesMidiMessages (false), + wasPlaying (false), + prepared (false), currentBuffer (nullptr), numInputBusChannels (0), numOutputBusChannels (0), numInputBusses (0), numOutputBusses (0), - audioUnit (0) + audioUnit (0), + midiConcatenator (2048) { using namespace AudioUnitFormatHelpers; @@ -328,6 +333,7 @@ public: { refreshParameterList(); updateNumChannels(); + producesMidiMessages = canProduceMidiOutput(); setPluginCallbacks(); setPlayConfigDetails (numInputBusChannels * numInputBusses, numOutputBusChannels * numOutputBusses, 0, 0); @@ -358,7 +364,7 @@ public: void* getPlatformSpecificData() { return audioUnit; } const String getName() const { return pluginName; } bool acceptsMidi() const { return wantsMidiMessages; } - bool producesMidi() const { return false; } + bool producesMidi() const { return producesMidiMessages; } //============================================================================== // AudioProcessor methods: @@ -462,6 +468,8 @@ public: currentBuffer = nullptr; prepared = false; } + + incomingMidi.clear(); } void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) @@ -519,6 +527,13 @@ public: for (int i = 0; i < getNumOutputChannels(); ++i) buffer.clear (i, 0, buffer.getNumSamples()); } + + if (producesMidiMessages) + { + const ScopedLock sl (midiInLock); + midiMessages.swapWith (incomingMidi); + incomingMidi.clear(); + } } //============================================================================== @@ -799,6 +814,14 @@ public: } } + void handleIncomingMidiMessage (void*, const MidiMessage& message) + { + const ScopedLock sl (midiInLock); + incomingMidi.addEvent (message, 0); + } + + void handlePartialSysexMessage (void*, const uint8*, int, double) {} + private: //============================================================================== friend class AudioUnitPluginWindowCarbon; @@ -809,7 +832,7 @@ private: String pluginName, manufacturer, version; String fileOrIdentifier; CriticalSection lock; - bool wantsMidiMessages, wasPlaying, prepared; + bool wantsMidiMessages, producesMidiMessages, wasPlaying, prepared; HeapBlock outputBufferList; AudioTimeStamp timeStamp; @@ -819,6 +842,10 @@ private: AudioUnit audioUnit; Array parameterIds; + MidiDataConcatenator midiConcatenator; + CriticalSection midiInLock; + MidiBuffer incomingMidi; + void setPluginCallbacks() { if (audioUnit != 0) @@ -833,6 +860,16 @@ private: kAudioUnitScope_Input, i, &info, sizeof (info)); } + if (producesMidiMessages) + { + AUMIDIOutputCallbackStruct info = { 0 }; + info.userData = this; + info.midiOutputCallback = renderMidiOutputCallback; + + producesMidiMessages = (AudioUnitSetProperty (audioUnit, kAudioUnitProperty_MIDIOutputCallback, + kAudioUnitScope_Global, 0, &info, sizeof (info)) == noErr); + } + { HostCallbackInfo info = { 0 }; info.hostUserData = this; @@ -840,13 +877,12 @@ private: info.musicalTimeLocationProc = getMusicalTimeLocationCallback; info.transportStateProc = getTransportStateCallback; - AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global, - 0, &info, sizeof (info)); + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, + kAudioUnitScope_Global, 0, &info, sizeof (info)); } } } - //============================================================================== OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, @@ -879,6 +915,23 @@ private: return noErr; } + OSStatus renderMidiOutput (const MIDIPacketList* pktlist) + { + if (pktlist != nullptr && pktlist->numPackets) + { + const double time = Time::getMillisecondCounterHiRes() * 0.001; + const MIDIPacket* packet = &pktlist->packet[0]; + + for (int i = 0; i < pktlist->numPackets; ++i) + { + midiConcatenator.pushMidiData (packet->data, (int) packet->length, time, (void*) nullptr, *this); + packet = MIDIPacketNext (packet); + } + } + + return noErr; + } + OSStatus getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const { AudioPlayHead* const ph = getPlayHead(); @@ -969,6 +1022,12 @@ private: ->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); } + static OSStatus renderMidiOutputCallback (void* userData, const AudioTimeStamp* timeStamp, UInt32 midiOutNum, + const struct MIDIPacketList* pktlist) + { + return static_cast (userData)->renderMidiOutput (pktlist); + } + static OSStatus getBeatAndTempoCallback (void* inHostUserData, Float64* outCurrentBeat, Float64* outCurrentTempo) { return static_cast (inHostUserData) @@ -1075,6 +1134,29 @@ private: } } + bool canProduceMidiOutput() + { + UInt32 dataSize = 0; + Boolean isWritable = false; + + if (AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_MIDIOutputCallbackInfo, + kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr + && dataSize != 0) + { + CFArrayRef midiArray; + + if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_MIDIOutputCallbackInfo, + kAudioUnitScope_Global, 0, &midiArray, &dataSize) == noErr) + { + bool result = (CFArrayGetCount (midiArray) > 0); + CFRelease (midiArray); + return result; + } + } + + return false; + } + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginInstance); }; diff --git a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index fe937ccb53..e8e24f9e5b 100644 --- a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -233,10 +233,10 @@ extern XContext windowHandleXContext; typedef void (*EventProcPtr) (XEvent* ev); -static bool xErrorTriggered; - namespace { + static bool xErrorTriggered = false; + int temporaryErrorHandler (Display*, XErrorEvent*) { xErrorTriggered = true; @@ -489,15 +489,13 @@ public: bool open() { bool ok = false; - const String filename (file.getFullPathName()); if (file.hasFileExtension (".vst")) { - const char* const utf8 = filename.toUTF8().getAddress(); - CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, - strlen (utf8), file.isDirectory()); + const char* const utf8 = file.getFullPathName().toUTF8().getAddress(); - if (url != 0) + if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, + strlen (utf8), file.isDirectory())) { bundleRef = CFBundleCreate (kCFAllocatorDefault, url); CFRelease (url); @@ -513,9 +511,7 @@ public: if (moduleMain != 0) { - CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName")); - - if (name != 0) + if (CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName"))) { if (CFGetTypeID (name) == CFStringGetTypeID()) { @@ -557,7 +553,7 @@ public: { FSRef fn; - if (FSPathMakeRef ((UInt8*) filename.toUTF8().getAddress(), &fn, 0) == noErr) + if (FSPathMakeRef ((UInt8*) file.getFullPathName().toUTF8().getAddress(), &fn, 0) == noErr) { resFileId = FSOpenResFile (&fn, fsRdPerm);