1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Added midi-out support for AU hosting.

This commit is contained in:
jules 2012-10-12 09:45:18 +01:00
parent 43a1037656
commit 08b57b8a0d
4 changed files with 110 additions and 30 deletions

View file

@ -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;

View file

@ -48,8 +48,9 @@ public:
pendingDataTime = 0;
}
template <typename UserDataType, typename CallbackType>
void pushMidiData (const void* inputData, int numBytes, double time,
MidiInput* input, MidiInputCallback& callback)
UserDataType* input, CallbackType& callback)
{
const uint8* d = static_cast <const uint8*> (inputData);
@ -105,8 +106,9 @@ public:
}
private:
template <typename UserDataType, typename CallbackType>
void processSysex (const uint8*& d, int& numBytes, double time,
MidiInput* input, MidiInputCallback& callback)
UserDataType* input, CallbackType& callback)
{
if (*d == 0xf0)
{

View file

@ -31,6 +31,7 @@
#include <AudioUnit/AUCocoaUIView.h>
#include <CoreAudioKit/AUGenericView.h>
#include <AudioToolbox/AudioUnitUtilities.h>
#include <CoreMIDI/MIDIServices.h>
#if JUCE_SUPPORT_CARBON
#include <AudioUnit/AudioUnitCarbonView.h>
@ -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 <AudioBufferList> outputBufferList;
AudioTimeStamp timeStamp;
@ -819,6 +842,10 @@ private:
AudioUnit audioUnit;
Array <int> 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 <AudioUnitPluginInstance*> (userData)->renderMidiOutput (pktlist);
}
static OSStatus getBeatAndTempoCallback (void* inHostUserData, Float64* outCurrentBeat, Float64* outCurrentTempo)
{
return static_cast <AudioUnitPluginInstance*> (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);
};

View file

@ -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);