1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-16 00:34:19 +00:00
JUCE/src/audio/plugins/formats/juce_AudioUnitPluginFormat.mm

1524 lines
51 KiB
Text

/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-9 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online 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.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../../../core/juce_TargetPlatform.h"
#include "../../../../juce_Config.h"
#if JUCE_PLUGINHOST_AU && ! (JUCE_LINUX || JUCE_WINDOWS)
#include <AudioUnit/AudioUnit.h>
#include <AudioUnit/AUCocoaUIView.h>
#include <CoreAudioKit/AUGenericView.h>
#if JUCE_SUPPORT_CARBON
#include <AudioToolbox/AudioUnitUtilities.h>
#include <AudioUnit/AudioUnitCarbonView.h>
#endif
#include "../../../core/juce_StandardHeader.h"
BEGIN_JUCE_NAMESPACE
#include "juce_AudioUnitPluginFormat.h"
#include "../juce_PluginDescription.h"
#include "../../../threads/juce_ScopedLock.h"
#include "../../../events/juce_Timer.h"
#include "../../../core/juce_PlatformUtilities.h"
#include "../../../gui/components/layout/juce_ComponentMovementWatcher.h"
#include "../../../gui/components/windows/juce_ComponentPeer.h"
#include "../../../gui/components/special/juce_NSViewComponent.h"
#if JUCE_MAC && JUCE_SUPPORT_CARBON
#include "../../../native/mac/juce_mac_CarbonViewWrapperComponent.h"
#endif
#if JUCE_MAC
// Change this to disable logging of various activities
#ifndef AU_LOGGING
#define AU_LOGGING 1
#endif
#if AU_LOGGING
#define log(a) Logger::writeToLog(a);
#else
#define log(a)
#endif
static int insideCallback = 0;
//==============================================================================
static const String osTypeToString (OSType type)
{
char s[4];
s[0] = (char) (((uint32) type) >> 24);
s[1] = (char) (((uint32) type) >> 16);
s[2] = (char) (((uint32) type) >> 8);
s[3] = (char) ((uint32) type);
return String (s, 4);
}
static OSType stringToOSType (const String& s1)
{
const String s (s1 + " ");
return (((OSType) (unsigned char) s[0]) << 24)
| (((OSType) (unsigned char) s[1]) << 16)
| (((OSType) (unsigned char) s[2]) << 8)
| ((OSType) (unsigned char) s[3]);
}
static const char* auIdentifierPrefix = "AudioUnit:";
static const String createAUPluginIdentifier (const ComponentDescription& desc)
{
jassert (osTypeToString ('abcd') == "abcd"); // agh, must have got the endianness wrong..
jassert (stringToOSType ("abcd") == (OSType) 'abcd'); // ditto
String s (auIdentifierPrefix);
if (desc.componentType == kAudioUnitType_MusicDevice)
s << "Synths/";
else if (desc.componentType == kAudioUnitType_MusicEffect
|| desc.componentType == kAudioUnitType_Effect)
s << "Effects/";
else if (desc.componentType == kAudioUnitType_Generator)
s << "Generators/";
else if (desc.componentType == kAudioUnitType_Panner)
s << "Panners/";
s << osTypeToString (desc.componentType) << ","
<< osTypeToString (desc.componentSubType) << ","
<< osTypeToString (desc.componentManufacturer);
return s;
}
static void getAUDetails (ComponentRecord* comp, String& name, String& manufacturer)
{
Handle componentNameHandle = NewHandle (sizeof (void*));
Handle componentInfoHandle = NewHandle (sizeof (void*));
if (componentNameHandle != 0 && componentInfoHandle != 0)
{
ComponentDescription desc;
if (GetComponentInfo (comp, &desc, componentNameHandle, componentInfoHandle, 0) == noErr)
{
ConstStr255Param nameString = (ConstStr255Param) (*componentNameHandle);
ConstStr255Param infoString = (ConstStr255Param) (*componentInfoHandle);
if (nameString != 0 && nameString[0] != 0)
{
const String all ((const char*) nameString + 1, nameString[0]);
DBG ("name: "+ all);
manufacturer = all.upToFirstOccurrenceOf (":", false, false).trim();
name = all.fromFirstOccurrenceOf (":", false, false).trim();
}
if (infoString != 0 && infoString[0] != 0)
{
DBG ("info: " + String ((const char*) infoString + 1, infoString[0]));
}
if (name.isEmpty())
name = "<Unknown>";
}
DisposeHandle (componentNameHandle);
DisposeHandle (componentInfoHandle);
}
}
static bool getComponentDescFromIdentifier (const String& fileOrIdentifier, ComponentDescription& desc,
String& name, String& version, String& manufacturer)
{
zerostruct (desc);
if (fileOrIdentifier.startsWithIgnoreCase (auIdentifierPrefix))
{
String s (fileOrIdentifier.substring (jmax (fileOrIdentifier.lastIndexOfChar (':'),
fileOrIdentifier.lastIndexOfChar ('/')) + 1));
StringArray tokens;
tokens.addTokens (s, ",", String::empty);
tokens.trim();
tokens.removeEmptyStrings();
if (tokens.size() == 3)
{
desc.componentType = stringToOSType (tokens[0]);
desc.componentSubType = stringToOSType (tokens[1]);
desc.componentManufacturer = stringToOSType (tokens[2]);
ComponentRecord* comp = FindNextComponent (0, &desc);
if (comp != 0)
{
getAUDetails (comp, name, manufacturer);
return true;
}
}
}
return false;
}
//==============================================================================
class AudioUnitPluginWindowCarbon;
class AudioUnitPluginWindowCocoa;
//==============================================================================
class AudioUnitPluginInstance : public AudioPluginInstance
{
public:
//==============================================================================
~AudioUnitPluginInstance();
//==============================================================================
// AudioPluginInstance methods:
void fillInPluginDescription (PluginDescription& desc) const
{
desc.name = pluginName;
desc.fileOrIdentifier = createAUPluginIdentifier (componentDesc);
desc.uid = ((int) componentDesc.componentType)
^ ((int) componentDesc.componentSubType)
^ ((int) componentDesc.componentManufacturer);
desc.lastFileModTime = 0;
desc.pluginFormatName = "AudioUnit";
desc.category = getCategory();
desc.manufacturerName = manufacturer;
desc.version = version;
desc.numInputChannels = getNumInputChannels();
desc.numOutputChannels = getNumOutputChannels();
desc.isInstrument = (componentDesc.componentType == kAudioUnitType_MusicDevice);
}
const String getName() const { return pluginName; }
bool acceptsMidi() const { return wantsMidiMessages; }
bool producesMidi() const { return false; }
//==============================================================================
// AudioProcessor methods:
void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock);
void releaseResources();
void processBlock (AudioSampleBuffer& buffer,
MidiBuffer& midiMessages);
AudioProcessorEditor* createEditor();
const String getInputChannelName (const int index) const;
bool isInputChannelStereoPair (int index) const;
const String getOutputChannelName (const int index) const;
bool isOutputChannelStereoPair (int index) const;
//==============================================================================
int getNumParameters();
float getParameter (int index);
void setParameter (int index, float newValue);
const String getParameterName (int index);
const String getParameterText (int index);
bool isParameterAutomatable (int index) const;
//==============================================================================
int getNumPrograms();
int getCurrentProgram();
void setCurrentProgram (int index);
const String getProgramName (int index);
void changeProgramName (int index, const String& newName);
//==============================================================================
void getStateInformation (MemoryBlock& destData);
void getCurrentProgramStateInformation (MemoryBlock& destData);
void setStateInformation (const void* data, int sizeInBytes);
void setCurrentProgramStateInformation (const void* data, int sizeInBytes);
//==============================================================================
juce_UseDebuggingNewOperator
private:
friend class AudioUnitPluginWindowCarbon;
friend class AudioUnitPluginWindowCocoa;
friend class AudioUnitPluginFormat;
ComponentDescription componentDesc;
String pluginName, manufacturer, version;
String fileOrIdentifier;
CriticalSection lock;
bool initialised, wantsMidiMessages, wasPlaying;
HeapBlock <AudioBufferList> outputBufferList;
AudioTimeStamp timeStamp;
AudioSampleBuffer* currentBuffer;
AudioUnit audioUnit;
Array <int> parameterIds;
//==============================================================================
bool getComponentDescFromFile (const String& fileOrIdentifier);
void initialise();
//==============================================================================
OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* ioData) const;
static OSStatus renderGetInputCallback (void* inRefCon,
AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* ioData)
{
return ((AudioUnitPluginInstance*) inRefCon)
->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
}
OSStatus getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const;
OSStatus getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator,
UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat) const;
OSStatus getTransportState (Boolean* outIsPlaying, Boolean* outTransportStateChanged,
Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling,
Float64* outCycleStartBeat, Float64* outCycleEndBeat);
static OSStatus getBeatAndTempoCallback (void* inHostUserData, Float64* outCurrentBeat, Float64* outCurrentTempo)
{
return ((AudioUnitPluginInstance*) inHostUserData)->getBeatAndTempo (outCurrentBeat, outCurrentTempo);
}
static OSStatus getMusicalTimeLocationCallback (void* inHostUserData, UInt32* outDeltaSampleOffsetToNextBeat,
Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator,
Float64* outCurrentMeasureDownBeat)
{
return ((AudioUnitPluginInstance*) inHostUserData)
->getMusicalTimeLocation (outDeltaSampleOffsetToNextBeat, outTimeSig_Numerator,
outTimeSig_Denominator, outCurrentMeasureDownBeat);
}
static OSStatus getTransportStateCallback (void* inHostUserData, Boolean* outIsPlaying, Boolean* outTransportStateChanged,
Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling,
Float64* outCycleStartBeat, Float64* outCycleEndBeat)
{
return ((AudioUnitPluginInstance*) inHostUserData)
->getTransportState (outIsPlaying, outTransportStateChanged,
outCurrentSampleInTimeLine, outIsCycling,
outCycleStartBeat, outCycleEndBeat);
}
//==============================================================================
void getNumChannels (int& numIns, int& numOuts)
{
numIns = 0;
numOuts = 0;
AUChannelInfo supportedChannels [128];
UInt32 supportedChannelsSize = sizeof (supportedChannels);
if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global,
0, supportedChannels, &supportedChannelsSize) == noErr
&& supportedChannelsSize > 0)
{
for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i)
{
numIns = jmax (numIns, (int) supportedChannels[i].inChannels);
numOuts = jmax (numOuts, (int) supportedChannels[i].outChannels);
}
}
else
{
// (this really means the plugin will take any number of ins/outs as long
// as they are the same)
numIns = numOuts = 2;
}
}
const String getCategory() const;
//==============================================================================
AudioUnitPluginInstance (const String& fileOrIdentifier);
};
//==============================================================================
AudioUnitPluginInstance::AudioUnitPluginInstance (const String& fileOrIdentifier)
: fileOrIdentifier (fileOrIdentifier),
initialised (false),
wantsMidiMessages (false),
audioUnit (0),
currentBuffer (0)
{
try
{
++insideCallback;
log ("Opening AU: " + fileOrIdentifier);
if (getComponentDescFromFile (fileOrIdentifier))
{
ComponentRecord* const comp = FindNextComponent (0, &componentDesc);
if (comp != 0)
{
audioUnit = (AudioUnit) OpenComponent (comp);
wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice
|| componentDesc.componentType == kAudioUnitType_MusicEffect;
}
}
--insideCallback;
}
catch (...)
{
--insideCallback;
}
}
AudioUnitPluginInstance::~AudioUnitPluginInstance()
{
const ScopedLock sl (lock);
jassert (insideCallback == 0);
if (audioUnit != 0)
{
AudioUnitUninitialize (audioUnit);
CloseComponent (audioUnit);
audioUnit = 0;
}
}
bool AudioUnitPluginInstance::getComponentDescFromFile (const String& fileOrIdentifier)
{
zerostruct (componentDesc);
if (getComponentDescFromIdentifier (fileOrIdentifier, componentDesc, pluginName, version, manufacturer))
return true;
const File file (fileOrIdentifier);
if (! file.hasFileExtension (".component"))
return false;
const char* const utf8 = fileOrIdentifier.toUTF8();
CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8,
strlen (utf8), file.isDirectory());
if (url != 0)
{
CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url);
CFRelease (url);
if (bundleRef != 0)
{
CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName"));
if (name != 0 && CFGetTypeID (name) == CFStringGetTypeID())
pluginName = PlatformUtilities::cfStringToJuceString ((CFStringRef) name);
if (pluginName.isEmpty())
pluginName = file.getFileNameWithoutExtension();
CFTypeRef versionString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleVersion"));
if (versionString != 0 && CFGetTypeID (versionString) == CFStringGetTypeID())
version = PlatformUtilities::cfStringToJuceString ((CFStringRef) versionString);
CFTypeRef manuString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleGetInfoString"));
if (manuString != 0 && CFGetTypeID (manuString) == CFStringGetTypeID())
manufacturer = PlatformUtilities::cfStringToJuceString ((CFStringRef) manuString);
short resFileId = CFBundleOpenBundleResourceMap (bundleRef);
UseResFile (resFileId);
for (int i = 1; i <= Count1Resources ('thng'); ++i)
{
Handle h = Get1IndResource ('thng', i);
if (h != 0)
{
HLock (h);
const uint32* const types = (const uint32*) *h;
if (types[0] == kAudioUnitType_MusicDevice
|| types[0] == kAudioUnitType_MusicEffect
|| types[0] == kAudioUnitType_Effect
|| types[0] == kAudioUnitType_Generator
|| types[0] == kAudioUnitType_Panner)
{
componentDesc.componentType = types[0];
componentDesc.componentSubType = types[1];
componentDesc.componentManufacturer = types[2];
break;
}
HUnlock (h);
ReleaseResource (h);
}
}
CFBundleCloseBundleResourceMap (bundleRef, resFileId);
CFRelease (bundleRef);
}
}
return componentDesc.componentType != 0 && componentDesc.componentSubType != 0;
}
//==============================================================================
void AudioUnitPluginInstance::initialise()
{
if (initialised || audioUnit == 0)
return;
log ("Initialising AU: " + pluginName);
parameterIds.clear();
{
UInt32 paramListSize = 0;
AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
0, 0, &paramListSize);
if (paramListSize > 0)
{
parameterIds.insertMultiple (0, 0, paramListSize / sizeof (int));
AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
0, &parameterIds.getReference(0), &paramListSize);
}
}
{
AURenderCallbackStruct info;
zerostruct (info);
info.inputProcRefCon = this;
info.inputProc = renderGetInputCallback;
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
0, &info, sizeof (info));
}
{
HostCallbackInfo info;
zerostruct (info);
info.hostUserData = this;
info.beatAndTempoProc = getBeatAndTempoCallback;
info.musicalTimeLocationProc = getMusicalTimeLocationCallback;
info.transportStateProc = getTransportStateCallback;
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global,
0, &info, sizeof (info));
}
int numIns, numOuts;
getNumChannels (numIns, numOuts);
setPlayConfigDetails (numIns, numOuts, 0, 0);
initialised = AudioUnitInitialize (audioUnit) == noErr;
setLatencySamples (0);
}
//==============================================================================
void AudioUnitPluginInstance::prepareToPlay (double sampleRate_,
int samplesPerBlockExpected)
{
initialise();
if (initialised)
{
int numIns, numOuts;
getNumChannels (numIns, numOuts);
setPlayConfigDetails (numIns, numOuts, sampleRate_, samplesPerBlockExpected);
Float64 latencySecs = 0.0;
UInt32 latencySize = sizeof (latencySecs);
AudioUnitGetProperty (audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global,
0, &latencySecs, &latencySize);
setLatencySamples (roundToInt (latencySecs * sampleRate_));
AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0);
AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0);
AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0);
AudioStreamBasicDescription stream;
zerostruct (stream);
stream.mSampleRate = sampleRate_;
stream.mFormatID = kAudioFormatLinearPCM;
stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
stream.mFramesPerPacket = 1;
stream.mBytesPerPacket = 4;
stream.mBytesPerFrame = 4;
stream.mBitsPerChannel = 32;
stream.mChannelsPerFrame = numIns;
OSStatus err = AudioUnitSetProperty (audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0, &stream, sizeof (stream));
stream.mChannelsPerFrame = numOuts;
err = AudioUnitSetProperty (audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0, &stream, sizeof (stream));
outputBufferList.calloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * (numOuts + 1), 1);
outputBufferList->mNumberBuffers = numOuts;
for (int i = numOuts; --i >= 0;)
outputBufferList->mBuffers[i].mNumberChannels = 1;
zerostruct (timeStamp);
timeStamp.mSampleTime = 0;
timeStamp.mHostTime = AudioGetCurrentHostTime();
timeStamp.mFlags = kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid;
currentBuffer = 0;
wasPlaying = false;
}
}
void AudioUnitPluginInstance::releaseResources()
{
if (initialised)
{
AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0);
AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0);
AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0);
outputBufferList.free();
currentBuffer = 0;
}
}
OSStatus AudioUnitPluginInstance::renderGetInput (AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* ioData) const
{
if (inBusNumber == 0
&& currentBuffer != 0)
{
jassert (inNumberFrames == currentBuffer->getNumSamples()); // if this ever happens, might need to add extra handling
for (int i = 0; i < ioData->mNumberBuffers; ++i)
{
if (i < currentBuffer->getNumChannels())
{
memcpy (ioData->mBuffers[i].mData,
currentBuffer->getSampleData (i, 0),
sizeof (float) * inNumberFrames);
}
else
{
zeromem (ioData->mBuffers[i].mData, sizeof (float) * inNumberFrames);
}
}
}
return noErr;
}
void AudioUnitPluginInstance::processBlock (AudioSampleBuffer& buffer,
MidiBuffer& midiMessages)
{
const int numSamples = buffer.getNumSamples();
if (initialised)
{
AudioUnitRenderActionFlags flags = 0;
timeStamp.mHostTime = AudioGetCurrentHostTime();
for (int i = getNumOutputChannels(); --i >= 0;)
{
outputBufferList->mBuffers[i].mDataByteSize = sizeof (float) * numSamples;
outputBufferList->mBuffers[i].mData = buffer.getSampleData (i, 0);
}
currentBuffer = &buffer;
if (wantsMidiMessages)
{
const uint8* midiEventData;
int midiEventSize, midiEventPosition;
MidiBuffer::Iterator i (midiMessages);
while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
{
if (midiEventSize <= 3)
MusicDeviceMIDIEvent (audioUnit,
midiEventData[0], midiEventData[1], midiEventData[2],
midiEventPosition);
else
MusicDeviceSysEx (audioUnit, midiEventData, midiEventSize);
}
midiMessages.clear();
}
AudioUnitRender (audioUnit, &flags, &timeStamp,
0, numSamples, outputBufferList);
timeStamp.mSampleTime += numSamples;
}
else
{
// Not initialised, so just bypass..
for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
buffer.clear (i, 0, buffer.getNumSamples());
}
}
//==============================================================================
OSStatus AudioUnitPluginInstance::getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const
{
AudioPlayHead* const ph = getPlayHead();
AudioPlayHead::CurrentPositionInfo result;
if (ph != 0 && ph->getCurrentPosition (result))
{
if (outCurrentBeat != 0)
*outCurrentBeat = result.ppqPosition;
if (outCurrentTempo != 0)
*outCurrentTempo = result.bpm;
}
else
{
if (outCurrentBeat != 0)
*outCurrentBeat = 0;
if (outCurrentTempo != 0)
*outCurrentTempo = 120.0;
}
return noErr;
}
OSStatus AudioUnitPluginInstance::getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat,
Float32* outTimeSig_Numerator,
UInt32* outTimeSig_Denominator,
Float64* outCurrentMeasureDownBeat) const
{
AudioPlayHead* const ph = getPlayHead();
AudioPlayHead::CurrentPositionInfo result;
if (ph != 0 && ph->getCurrentPosition (result))
{
if (outTimeSig_Numerator != 0)
*outTimeSig_Numerator = result.timeSigNumerator;
if (outTimeSig_Denominator != 0)
*outTimeSig_Denominator = result.timeSigDenominator;
if (outDeltaSampleOffsetToNextBeat != 0)
*outDeltaSampleOffsetToNextBeat = 0; //xxx
if (outCurrentMeasureDownBeat != 0)
*outCurrentMeasureDownBeat = result.ppqPositionOfLastBarStart; //xxx wrong
}
else
{
if (outDeltaSampleOffsetToNextBeat != 0)
*outDeltaSampleOffsetToNextBeat = 0;
if (outTimeSig_Numerator != 0)
*outTimeSig_Numerator = 4;
if (outTimeSig_Denominator != 0)
*outTimeSig_Denominator = 4;
if (outCurrentMeasureDownBeat != 0)
*outCurrentMeasureDownBeat = 0;
}
return noErr;
}
OSStatus AudioUnitPluginInstance::getTransportState (Boolean* outIsPlaying,
Boolean* outTransportStateChanged,
Float64* outCurrentSampleInTimeLine,
Boolean* outIsCycling,
Float64* outCycleStartBeat,
Float64* outCycleEndBeat)
{
AudioPlayHead* const ph = getPlayHead();
AudioPlayHead::CurrentPositionInfo result;
if (ph != 0 && ph->getCurrentPosition (result))
{
if (outIsPlaying != 0)
*outIsPlaying = result.isPlaying;
if (outTransportStateChanged != 0)
{
*outTransportStateChanged = result.isPlaying != wasPlaying;
wasPlaying = result.isPlaying;
}
if (outCurrentSampleInTimeLine != 0)
*outCurrentSampleInTimeLine = roundToInt (result.timeInSeconds * getSampleRate());
if (outIsCycling != 0)
*outIsCycling = false;
if (outCycleStartBeat != 0)
*outCycleStartBeat = 0;
if (outCycleEndBeat != 0)
*outCycleEndBeat = 0;
}
else
{
if (outIsPlaying != 0)
*outIsPlaying = false;
if (outTransportStateChanged != 0)
*outTransportStateChanged = false;
if (outCurrentSampleInTimeLine != 0)
*outCurrentSampleInTimeLine = 0;
if (outIsCycling != 0)
*outIsCycling = false;
if (outCycleStartBeat != 0)
*outCycleStartBeat = 0;
if (outCycleEndBeat != 0)
*outCycleEndBeat = 0;
}
return noErr;
}
//==============================================================================
static VoidArray activeWindows;
class AudioUnitPluginWindowCocoa : public AudioProcessorEditor
{
public:
AudioUnitPluginWindowCocoa (AudioUnitPluginInstance& plugin_, const bool createGenericViewIfNeeded)
: AudioProcessorEditor (&plugin_),
plugin (plugin_),
wrapper (0)
{
addAndMakeVisible (wrapper = new NSViewComponent());
activeWindows.add (this);
setOpaque (true);
setVisible (true);
setSize (100, 100);
createView (createGenericViewIfNeeded);
}
~AudioUnitPluginWindowCocoa()
{
const bool wasValid = isValid();
wrapper->setView (0);
activeWindows.removeValue (this);
if (wasValid)
plugin.editorBeingDeleted (this);
delete wrapper;
}
bool isValid() const { return wrapper->getView() != 0; }
void paint (Graphics& g)
{
g.fillAll (Colours::white);
}
void resized()
{
wrapper->setSize (getWidth(), getHeight());
}
private:
AudioUnitPluginInstance& plugin;
NSViewComponent* wrapper;
bool createView (const bool createGenericViewIfNeeded)
{
NSView* pluginView = 0;
UInt32 dataSize = 0;
Boolean isWritable = false;
if (AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
0, &dataSize, &isWritable) == noErr
&& dataSize != 0
&& AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
0, &dataSize, &isWritable) == noErr)
{
HeapBlock <AudioUnitCocoaViewInfo> info;
info.calloc (dataSize, 1);
if (AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
0, info, &dataSize) == noErr)
{
NSString* viewClassName = (NSString*) (info->mCocoaAUViewClass[0]);
NSString* path = (NSString*) CFURLCopyPath (info->mCocoaAUViewBundleLocation);
NSBundle* viewBundle = [NSBundle bundleWithPath: [path autorelease]];
Class viewClass = [viewBundle classNamed: viewClassName];
if ([viewClass conformsToProtocol: @protocol (AUCocoaUIBase)]
&& [viewClass instancesRespondToSelector: @selector (interfaceVersion)]
&& [viewClass instancesRespondToSelector: @selector (uiViewForAudioUnit: withSize:)])
{
id factory = [[[viewClass alloc] init] autorelease];
pluginView = [factory uiViewForAudioUnit: plugin.audioUnit
withSize: NSMakeSize (getWidth(), getHeight())];
}
for (int i = (dataSize - sizeof (CFURLRef)) / sizeof (CFStringRef); --i >= 0;)
{
CFRelease (info->mCocoaAUViewClass[i]);
CFRelease (info->mCocoaAUViewBundleLocation);
}
}
}
if (createGenericViewIfNeeded && (pluginView == 0))
pluginView = [[AUGenericView alloc] initWithAudioUnit: plugin.audioUnit];
wrapper->setView (pluginView);
if (pluginView != 0)
setSize ([pluginView frame].size.width,
[pluginView frame].size.height);
return pluginView != 0;
}
};
#if JUCE_SUPPORT_CARBON
//==============================================================================
class AudioUnitPluginWindowCarbon : public AudioProcessorEditor
{
public:
//==============================================================================
AudioUnitPluginWindowCarbon (AudioUnitPluginInstance& plugin_)
: AudioProcessorEditor (&plugin_),
plugin (plugin_),
viewComponent (0)
{
addAndMakeVisible (innerWrapper = new InnerWrapperComponent (this));
activeWindows.add (this);
setOpaque (true);
setVisible (true);
setSize (400, 300);
ComponentDescription viewList [16];
UInt32 viewListSize = sizeof (viewList);
AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global,
0, &viewList, &viewListSize);
componentRecord = FindNextComponent (0, &viewList[0]);
}
~AudioUnitPluginWindowCarbon()
{
innerWrapper = 0;
activeWindows.removeValue (this);
if (isValid())
plugin.editorBeingDeleted (this);
}
bool isValid() const throw() { return componentRecord != 0; }
//==============================================================================
void paint (Graphics& g)
{
g.fillAll (Colours::black);
}
void resized()
{
innerWrapper->setSize (getWidth(), getHeight());
}
//==============================================================================
bool keyStateChanged (bool)
{
return false;
}
bool keyPressed (const KeyPress&)
{
return false;
}
//==============================================================================
void broughtToFront()
{
activeWindows.removeValue (this);
activeWindows.add (this);
}
//==============================================================================
AudioUnit getAudioUnit() const { return plugin.audioUnit; }
AudioUnitCarbonView getViewComponent()
{
if (viewComponent == 0 && componentRecord != 0)
viewComponent = (AudioUnitCarbonView) OpenComponent (componentRecord);
return viewComponent;
}
void closeViewComponent()
{
if (viewComponent != 0)
{
CloseComponent (viewComponent);
viewComponent = 0;
}
}
//==============================================================================
juce_UseDebuggingNewOperator
private:
AudioUnitPluginInstance& plugin;
ComponentRecord* componentRecord;
AudioUnitCarbonView viewComponent;
//==============================================================================
class InnerWrapperComponent : public CarbonViewWrapperComponent
{
public:
InnerWrapperComponent (AudioUnitPluginWindowCarbon* const owner_)
: owner (owner_)
{
}
~InnerWrapperComponent()
{
deleteWindow();
}
HIViewRef attachView (WindowRef windowRef, HIViewRef rootView)
{
log ("Opening AU GUI: " + owner->plugin.getName());
AudioUnitCarbonView viewComponent = owner->getViewComponent();
if (viewComponent == 0)
return 0;
Float32Point pos = { 0, 0 };
Float32Point size = { 250, 200 };
HIViewRef pluginView = 0;
AudioUnitCarbonViewCreate (viewComponent,
owner->getAudioUnit(),
windowRef,
rootView,
&pos,
&size,
(ControlRef*) &pluginView);
return pluginView;
}
void removeView (HIViewRef)
{
log ("Closing AU GUI: " + owner->plugin.getName());
owner->closeViewComponent();
}
private:
AudioUnitPluginWindowCarbon* const owner;
};
friend class InnerWrapperComponent;
ScopedPointer<InnerWrapperComponent> innerWrapper;
};
#endif
//==============================================================================
AudioProcessorEditor* AudioUnitPluginInstance::createEditor()
{
ScopedPointer<AudioProcessorEditor> w (new AudioUnitPluginWindowCocoa (*this, false));
if (! static_cast <AudioUnitPluginWindowCocoa*> (static_cast <AudioProcessorEditor*> (w))->isValid())
w = 0;
#if JUCE_SUPPORT_CARBON
if (w == 0)
{
w = new AudioUnitPluginWindowCarbon (*this);
if (! static_cast <AudioUnitPluginWindowCarbon*> (static_cast <AudioProcessorEditor*> (w))->isValid())
w = 0;
}
#endif
if (w == 0)
w = new AudioUnitPluginWindowCocoa (*this, true); // use AUGenericView as a fallback
return w.release();
}
//==============================================================================
const String AudioUnitPluginInstance::getCategory() const
{
const char* result = 0;
switch (componentDesc.componentType)
{
case kAudioUnitType_Effect:
case kAudioUnitType_MusicEffect:
result = "Effect";
break;
case kAudioUnitType_MusicDevice:
result = "Synth";
break;
case kAudioUnitType_Generator:
result = "Generator";
break;
case kAudioUnitType_Panner:
result = "Panner";
break;
default:
break;
}
return result;
}
//==============================================================================
int AudioUnitPluginInstance::getNumParameters()
{
return parameterIds.size();
}
float AudioUnitPluginInstance::getParameter (int index)
{
const ScopedLock sl (lock);
Float32 value = 0.0f;
if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size())
{
AudioUnitGetParameter (audioUnit,
(UInt32) parameterIds.getUnchecked (index),
kAudioUnitScope_Global, 0,
&value);
}
return value;
}
void AudioUnitPluginInstance::setParameter (int index, float newValue)
{
const ScopedLock sl (lock);
if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size())
{
AudioUnitSetParameter (audioUnit,
(UInt32) parameterIds.getUnchecked (index),
kAudioUnitScope_Global, 0,
newValue, 0);
}
}
const String AudioUnitPluginInstance::getParameterName (int index)
{
AudioUnitParameterInfo info;
zerostruct (info);
UInt32 sz = sizeof (info);
String name;
if (AudioUnitGetProperty (audioUnit,
kAudioUnitProperty_ParameterInfo,
kAudioUnitScope_Global,
parameterIds [index], &info, &sz) == noErr)
{
if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0)
name = PlatformUtilities::cfStringToJuceString (info.cfNameString);
else
name = String (info.name, sizeof (info.name));
}
return name;
}
const String AudioUnitPluginInstance::getParameterText (int index)
{
return String (getParameter (index));
}
bool AudioUnitPluginInstance::isParameterAutomatable (int index) const
{
AudioUnitParameterInfo info;
UInt32 sz = sizeof (info);
if (AudioUnitGetProperty (audioUnit,
kAudioUnitProperty_ParameterInfo,
kAudioUnitScope_Global,
parameterIds [index], &info, &sz) == noErr)
{
return (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0;
}
return true;
}
//==============================================================================
int AudioUnitPluginInstance::getNumPrograms()
{
CFArrayRef presets;
UInt32 sz = sizeof (CFArrayRef);
int num = 0;
if (AudioUnitGetProperty (audioUnit,
kAudioUnitProperty_FactoryPresets,
kAudioUnitScope_Global,
0, &presets, &sz) == noErr)
{
num = (int) CFArrayGetCount (presets);
CFRelease (presets);
}
return num;
}
int AudioUnitPluginInstance::getCurrentProgram()
{
AUPreset current;
current.presetNumber = 0;
UInt32 sz = sizeof (AUPreset);
AudioUnitGetProperty (audioUnit,
kAudioUnitProperty_FactoryPresets,
kAudioUnitScope_Global,
0, &current, &sz);
return current.presetNumber;
}
void AudioUnitPluginInstance::setCurrentProgram (int newIndex)
{
AUPreset current;
current.presetNumber = newIndex;
current.presetName = 0;
AudioUnitSetProperty (audioUnit,
kAudioUnitProperty_FactoryPresets,
kAudioUnitScope_Global,
0, &current, sizeof (AUPreset));
}
const String AudioUnitPluginInstance::getProgramName (int index)
{
String s;
CFArrayRef presets;
UInt32 sz = sizeof (CFArrayRef);
if (AudioUnitGetProperty (audioUnit,
kAudioUnitProperty_FactoryPresets,
kAudioUnitScope_Global,
0, &presets, &sz) == noErr)
{
for (CFIndex i = 0; i < CFArrayGetCount (presets); ++i)
{
const AUPreset* p = (const AUPreset*) CFArrayGetValueAtIndex (presets, i);
if (p != 0 && p->presetNumber == index)
{
s = PlatformUtilities::cfStringToJuceString (p->presetName);
break;
}
}
CFRelease (presets);
}
return s;
}
void AudioUnitPluginInstance::changeProgramName (int index, const String& newName)
{
jassertfalse // xxx not implemented!
}
//==============================================================================
const String AudioUnitPluginInstance::getInputChannelName (const int index) const
{
if (((unsigned int) index) < (unsigned int) getNumInputChannels())
return "Input " + String (index + 1);
return String::empty;
}
bool AudioUnitPluginInstance::isInputChannelStereoPair (int index) const
{
if (((unsigned int) index) >= (unsigned int) getNumInputChannels())
return false;
return true;
}
const String AudioUnitPluginInstance::getOutputChannelName (const int index) const
{
if (((unsigned int) index) < (unsigned int) getNumOutputChannels())
return "Output " + String (index + 1);
return String::empty;
}
bool AudioUnitPluginInstance::isOutputChannelStereoPair (int index) const
{
if (((unsigned int) index) >= (unsigned int) getNumOutputChannels())
return false;
return true;
}
//==============================================================================
void AudioUnitPluginInstance::getStateInformation (MemoryBlock& destData)
{
getCurrentProgramStateInformation (destData);
}
void AudioUnitPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData)
{
CFPropertyListRef propertyList = 0;
UInt32 sz = sizeof (CFPropertyListRef);
if (AudioUnitGetProperty (audioUnit,
kAudioUnitProperty_ClassInfo,
kAudioUnitScope_Global,
0, &propertyList, &sz) == noErr)
{
CFWriteStreamRef stream = CFWriteStreamCreateWithAllocatedBuffers (kCFAllocatorDefault, kCFAllocatorDefault);
CFWriteStreamOpen (stream);
CFIndex bytesWritten = CFPropertyListWriteToStream (propertyList, stream, kCFPropertyListBinaryFormat_v1_0, 0);
CFWriteStreamClose (stream);
CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty (stream, kCFStreamPropertyDataWritten);
destData.setSize (bytesWritten);
destData.copyFrom (CFDataGetBytePtr (data), 0, destData.getSize());
CFRelease (data);
CFRelease (stream);
CFRelease (propertyList);
}
}
void AudioUnitPluginInstance::setStateInformation (const void* data, int sizeInBytes)
{
setCurrentProgramStateInformation (data, sizeInBytes);
}
void AudioUnitPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes)
{
CFReadStreamRef stream = CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault,
(const UInt8*) data,
sizeInBytes,
kCFAllocatorNull);
CFReadStreamOpen (stream);
CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0;
CFPropertyListRef propertyList = CFPropertyListCreateFromStream (kCFAllocatorDefault,
stream,
0,
kCFPropertyListImmutable,
&format,
0);
CFRelease (stream);
if (propertyList != 0)
AudioUnitSetProperty (audioUnit,
kAudioUnitProperty_ClassInfo,
kAudioUnitScope_Global,
0, &propertyList, sizeof (propertyList));
}
//==============================================================================
//==============================================================================
AudioUnitPluginFormat::AudioUnitPluginFormat()
{
}
AudioUnitPluginFormat::~AudioUnitPluginFormat()
{
}
void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray <PluginDescription>& results,
const String& fileOrIdentifier)
{
if (! fileMightContainThisPluginType (fileOrIdentifier))
return;
PluginDescription desc;
desc.fileOrIdentifier = fileOrIdentifier;
desc.uid = 0;
try
{
ScopedPointer <AudioPluginInstance> createdInstance (createInstanceFromDescription (desc));
AudioUnitPluginInstance* const auInstance = dynamic_cast <AudioUnitPluginInstance*> ((AudioPluginInstance*) createdInstance);
if (auInstance != 0)
{
auInstance->fillInPluginDescription (desc);
results.add (new PluginDescription (desc));
}
}
catch (...)
{
// crashed while loading...
}
}
AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const PluginDescription& desc)
{
if (fileMightContainThisPluginType (desc.fileOrIdentifier))
{
ScopedPointer <AudioUnitPluginInstance> result (new AudioUnitPluginInstance (desc.fileOrIdentifier));
if (result->audioUnit != 0)
{
result->initialise();
return result.release();
}
}
return 0;
}
const StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath& /*directoriesToSearch*/,
const bool /*recursive*/)
{
StringArray result;
ComponentRecord* comp = 0;
ComponentDescription desc;
zerostruct (desc);
for (;;)
{
zerostruct (desc);
comp = FindNextComponent (comp, &desc);
if (comp == 0)
break;
GetComponentInfo (comp, &desc, 0, 0, 0);
if (desc.componentType == kAudioUnitType_MusicDevice
|| desc.componentType == kAudioUnitType_MusicEffect
|| desc.componentType == kAudioUnitType_Effect
|| desc.componentType == kAudioUnitType_Generator
|| desc.componentType == kAudioUnitType_Panner)
{
const String s (createAUPluginIdentifier (desc));
DBG (s);
result.add (s);
}
}
return result;
}
bool AudioUnitPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier)
{
ComponentDescription desc;
String name, version, manufacturer;
if (getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer))
return FindNextComponent (0, &desc) != 0;
const File f (fileOrIdentifier);
return f.hasFileExtension (".component")
&& f.isDirectory();
}
const String AudioUnitPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier)
{
ComponentDescription desc;
String name, version, manufacturer;
getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer);
if (name.isEmpty())
name = fileOrIdentifier;
return name;
}
bool AudioUnitPluginFormat::doesPluginStillExist (const PluginDescription& desc)
{
if (desc.fileOrIdentifier.startsWithIgnoreCase (auIdentifierPrefix))
return fileMightContainThisPluginType (desc.fileOrIdentifier);
else
return File (desc.fileOrIdentifier).exists();
}
const FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch()
{
return FileSearchPath ("/(Default AudioUnit locations)");
}
#endif
END_JUCE_NAMESPACE
#undef log
#endif