mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-16 00:34:19 +00:00
1524 lines
51 KiB
Text
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, ¶mListSize);
|
|
|
|
if (paramListSize > 0)
|
|
{
|
|
parameterIds.insertMultiple (0, 0, paramListSize / sizeof (int));
|
|
|
|
AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
|
|
0, ¶meterIds.getReference(0), ¶mListSize);
|
|
}
|
|
}
|
|
|
|
{
|
|
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, ¤t, &sz);
|
|
|
|
return current.presetNumber;
|
|
}
|
|
|
|
void AudioUnitPluginInstance::setCurrentProgram (int newIndex)
|
|
{
|
|
AUPreset current;
|
|
current.presetNumber = newIndex;
|
|
current.presetName = 0;
|
|
|
|
AudioUnitSetProperty (audioUnit,
|
|
kAudioUnitProperty_FactoryPresets,
|
|
kAudioUnitScope_Global,
|
|
0, ¤t, 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
|