1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-24 08:10:05 +02:00
audacity/src/effects/audiounits/AudioUnitEffect.cpp
lllucius@gmail.com d60225cb61 A few more changes for AudioUnits
I added a few assertions just to be safe.  Haven't hit
the particular situations yet, but I've only tested less
than 50 or so AUs.

We were missing a few Apple AUs like panners and mixers.

The plugin "installation" dialog sort is reversed on the Mac...weird

Made the effect windows float on top of the owning project window.
Looking for opinions on which method is best.
2014-11-29 01:22:41 +00:00

2223 lines
58 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
AudioUnitEffect.cpp
Dominic Mazzoni
Leland Lucius
*******************************************************************//**
\class AudioUnitEffect
\brief An Effect class that handles a wide range of effects. ??Mac only??
*//*******************************************************************/
#include <wx/defs.h>
#include <wx/button.h>
#include <wx/control.h>
#include <wx/frame.h>
#include <wx/panel.h>
#include <wx/sizer.h>
#include <wx/settings.h>
#include <wx/textctrl.h>
#include <wx/tokenzr.h>
#include <wx/mac/private.h>
#include "../../widgets/valnum.h"
#include "AudioUnitEffect.h"
#include "AudioUnitCocoaHelper.h"
// ============================================================================
// Module registration entry point
//
// This is the symbol that Audacity looks for when the module is built as a
// dynamic library.
//
// When the module is builtin to Audacity, we use the same function, but it is
// declared static so as not to clash with other builtin modules.
// ============================================================================
DECLARE_MODULE_ENTRY(AudacityModule)
{
// Create and register the importer
return new AudioUnitEffectsModule(moduleManager, path);
}
// ============================================================================
// Register this as a builtin module
// ============================================================================
DECLARE_BUILTIN_MODULE(AudioUnitEffectsBuiltin);
///////////////////////////////////////////////////////////////////////////////
//
// AudioUnitEffectsModule
//
///////////////////////////////////////////////////////////////////////////////
AudioUnitEffectsModule::AudioUnitEffectsModule(ModuleManagerInterface *moduleManager,
const wxString *path)
{
mModMan = moduleManager;
if (path)
{
mPath = *path;
}
}
AudioUnitEffectsModule::~AudioUnitEffectsModule()
{
mPath.Clear();
}
// ============================================================================
// IdentInterface implementation
// ============================================================================
wxString AudioUnitEffectsModule::GetID()
{
// Can be anything, but this is a v4 UUID
return wxT("1e767ec4-6f78-4c94-b6b8-c50b5780093b");
}
wxString AudioUnitEffectsModule::GetPath()
{
return mPath;
}
wxString AudioUnitEffectsModule::GetName()
{
return _("AudioUnit Effects Module");
}
wxString AudioUnitEffectsModule::GetVendor()
{
return _("The Audacity Team");
}
wxString AudioUnitEffectsModule::GetVersion()
{
// This "may" be different if this were to be maintained as a separate DLL
return AUDIOUNITEFFECTS_VERSION;
}
wxString AudioUnitEffectsModule::GetDescription()
{
return _("Provides AudioUnit Effects support to Audacity");
}
// ============================================================================
// ModuleInterface implementation
// ============================================================================
bool AudioUnitEffectsModule::Initialize()
{
// Nothing to do here
return true;
}
void AudioUnitEffectsModule::Terminate()
{
// Nothing to do here
return;
}
bool AudioUnitEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm)
{
// Nothing to be done here
return true;
}
wxArrayString AudioUnitEffectsModule::FindPlugins(PluginManagerInterface & pm)
{
wxArrayString effects;
LoadAudioUnitsOfType(kAudioUnitType_Effect, effects);
LoadAudioUnitsOfType(kAudioUnitType_Generator, effects);
LoadAudioUnitsOfType(kAudioUnitType_MusicEffect, effects);
LoadAudioUnitsOfType(kAudioUnitType_Mixer, effects);
LoadAudioUnitsOfType(kAudioUnitType_Panner, effects);
return effects;
}
bool AudioUnitEffectsModule::RegisterPlugin(PluginManagerInterface & pm, const wxString & path)
{
wxString name;
Component component = FindAudioUnit(path, name);
if (component == NULL)
{
return false;
}
AudioUnitEffect effect(path, name, component);
if (!effect.SetHost(NULL))
{
return false;
}
pm.RegisterEffectPlugin(this, &effect);
return true;
}
bool AudioUnitEffectsModule::IsPluginValid(const PluginID & ID,
const wxString & path)
{
wxString name;
return FindAudioUnit(path, name) != NULL;
}
IdentInterface *AudioUnitEffectsModule::CreateInstance(const PluginID & ID,
const wxString & path)
{
wxString name;
Component component = FindAudioUnit(path, name);
if (component == NULL)
{
return NULL;
}
return new AudioUnitEffect(path, name, component);
}
void AudioUnitEffectsModule::DeleteInstance(IdentInterface *instance)
{
AudioUnitEffect *effect = dynamic_cast<AudioUnitEffect *>(instance);
if (effect)
{
delete effect;
}
}
// ============================================================================
// AudioUnitEffectsModule implementation
// ============================================================================
void AudioUnitEffectsModule::LoadAudioUnitsOfType(OSType inAUType,
wxArrayString & effects)
{
ComponentDescription desc;
Component component;
desc.componentType = inAUType;
desc.componentSubType = 0;
desc.componentManufacturer = 0;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
component = FindNextComponent(NULL, &desc);
while (component != NULL)
{
ComponentDescription found;
Handle nameHandle = NewHandle(0);
GetComponentInfo(component, &found, nameHandle, 0, 0);
HLock(nameHandle);
int len = ((const char *)(*nameHandle))[0];
wxString name(((const char *)(*nameHandle)+1), wxConvISO8859_1, len);
HUnlock(nameHandle);
DisposeHandle(nameHandle);
effects.Add(wxString::Format(wxT("%-4.4s/%-4.4s/%-4.4s/%s"),
FromOSType(found.componentManufacturer).c_str(),
FromOSType(found.componentType).c_str(),
FromOSType(found.componentSubType).c_str(),
name.c_str()));
component = FindNextComponent (component, &desc);
}
}
Component AudioUnitEffectsModule::FindAudioUnit(const wxString & path,
wxString & name)
{
wxStringTokenizer tokens(path, wxT("/"));
ComponentDescription desc;
desc.componentManufacturer = ToOSType(tokens.GetNextToken());
desc.componentType = ToOSType(tokens.GetNextToken());
desc.componentSubType = ToOSType(tokens.GetNextToken());
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
name = tokens.GetNextToken();
return FindNextComponent(NULL, &desc);
}
wxString AudioUnitEffectsModule::FromOSType(OSType type)
{
OSType rev = (type & 0xff000000) >> 24 |
(type & 0x00ff0000) >> 8 |
(type & 0x0000ff00) << 8 |
(type & 0x000000ff) << 24;
return wxString::FromUTF8((char *)&rev, 4).c_str();
}
OSType AudioUnitEffectsModule::ToOSType(const wxString & type)
{
wxCharBuffer buf = type.ToUTF8();
OSType rev = ((unsigned char)buf.data()[0]) << 24 |
((unsigned char)buf.data()[1]) << 16 |
((unsigned char)buf.data()[2]) << 8 |
((unsigned char)buf.data()[3]);
return rev;
}
///////////////////////////////////////////////////////////////////////////////
//
// AudioUnitEffectSettingsDialog
//
///////////////////////////////////////////////////////////////////////////////
class AudioUnitEffectSettingsDialog:public wxDialog
{
public:
AudioUnitEffectSettingsDialog(wxWindow * parent, EffectHostInterface *host);
virtual ~AudioUnitEffectSettingsDialog();
void PopulateOrExchange(ShuttleGui & S);
void OnOk(wxCommandEvent & evt);
private:
EffectHostInterface *mHost;
int mBufferSize;
bool mUseBufferDelay;
bool mUseGUI;
bool mRescan;
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(AudioUnitEffectSettingsDialog, wxDialog)
EVT_BUTTON(wxID_OK, AudioUnitEffectSettingsDialog::OnOk)
END_EVENT_TABLE()
AudioUnitEffectSettingsDialog::AudioUnitEffectSettingsDialog(wxWindow * parent, EffectHostInterface *host)
: wxDialog(parent, wxID_ANY, wxString(_("AudioUnit Effect Settings")))
{
#if defined(EXPERIMENTAL_REALTIME_EFFECTS) && defined(__WXMAC__)
HIWindowChangeClass((WindowRef) MacGetWindowRef(), kMovableModalWindowClass);
#endif
mHost = host;
mHost->GetSharedConfig(wxT("Settings"), wxT("BufferSize"), mBufferSize, 8192);
mHost->GetSharedConfig(wxT("Settings"), wxT("UseBufferDelay"), mUseBufferDelay, true);
mHost->GetSharedConfig(wxT("Settings"), wxT("UseGUI"), mUseGUI, true);
ShuttleGui S(this, eIsCreating);
PopulateOrExchange(S);
}
AudioUnitEffectSettingsDialog::~AudioUnitEffectSettingsDialog()
{
}
void AudioUnitEffectSettingsDialog::PopulateOrExchange(ShuttleGui & S)
{
S.SetBorder(5);
S.StartHorizontalLay(wxEXPAND, 1);
{
S.StartVerticalLay(false);
{
S.StartStatic(_("Buffer Size"));
{
wxIntegerValidator<int> vld(&mBufferSize);
vld.SetRange(8, 1048576 * 1);
S.AddVariableText(wxString() +
_("The buffer size controls the number of samples sent to the effect ") +
_("on each iteration. Smaller values will cause slower processing and ") +
_("some effects require 8192 samples or less to work properly. However ") +
_("most effects can accept large buffers and using them will greatly ") +
_("reduce processing time."))->Wrap(650);
S.StartHorizontalLay(wxALIGN_LEFT);
{
wxTextCtrl *t;
t = S.TieNumericTextBox(_("&Buffer Size (8 to 1048576 samples):"),
mBufferSize,
12);
t->SetMinSize(wxSize(100, -1));
t->SetValidator(vld);
}
S.EndHorizontalLay();
}
S.EndStatic();
S.StartStatic(_("Buffer Delay Compensation"));
{
S.AddVariableText(wxString() +
_("As part of their processing, some AudioUnit effects must delay returning ") +
_("audio to Audacity. When not compensating for this delay, you will ") +
_("notice that small silences have been inserted into the audio. ") +
_("Enabling this setting will provide that compensation, but it may ") +
_("not work for all AudioUnit effects."))->Wrap(650);
S.StartHorizontalLay(wxALIGN_LEFT);
{
S.TieCheckBox(_("Enable &compensation"),
mUseBufferDelay);
}
S.EndHorizontalLay();
}
S.EndStatic();
S.StartStatic(_("Graphical Mode"));
{
S.AddVariableText(wxString() +
_("Most AudioUnit effects have a graphical interface for setting parameter values.") +
_(" A basic text-only method is also available. ") +
_(" Reopen the effect for this to take effect."))->Wrap(650);
S.TieCheckBox(_("Enable &graphical interface"),
mUseGUI);
}
S.EndStatic();
}
S.EndVerticalLay();
}
S.EndHorizontalLay();
S.AddStandardButtons();
Layout();
Fit();
Center();
}
void AudioUnitEffectSettingsDialog::OnOk(wxCommandEvent & WXUNUSED(evt))
{
if (!Validate())
{
return;
}
ShuttleGui S(this, eIsGettingFromDialog);
PopulateOrExchange(S);
mHost->SetSharedConfig(wxT("Settings"), wxT("BufferSize"), mBufferSize);
mHost->SetSharedConfig(wxT("Settings"), wxT("UseBufferDelay"), mUseBufferDelay);
mHost->SetSharedConfig(wxT("Settings"), wxT("UseGUI"), mUseGUI);
EndModal(wxID_OK);
}
///////////////////////////////////////////////////////////////////////////////
//
// AudioUnitEffectEventHelper
//
///////////////////////////////////////////////////////////////////////////////
class AudioUnitEffectEventHelper : public wxEvtHandler
{
public:
AudioUnitEffectEventHelper(AudioUnitEffect *effect);
virtual ~AudioUnitEffectEventHelper();
// AudioUnitEffectEventHelper implementatino
void OnSize(wxSizeEvent & evt);
private:
AudioUnitEffect *mEffect;
DECLARE_EVENT_TABLE();
};
///////////////////////////////////////////////////////////////////////////////
//
// AudioUnitEffectEventHelper
//
///////////////////////////////////////////////////////////////////////////////
BEGIN_EVENT_TABLE(AudioUnitEffectEventHelper, wxEvtHandler)
EVT_SIZE(AudioUnitEffectEventHelper::OnSize)
END_EVENT_TABLE()
AudioUnitEffectEventHelper::AudioUnitEffectEventHelper(AudioUnitEffect *effect)
{
mEffect = effect;
}
AudioUnitEffectEventHelper::~AudioUnitEffectEventHelper()
{
}
// ============================================================================
// AudioUnitEffectEventHelper implementation
// ============================================================================
void AudioUnitEffectEventHelper::OnSize(wxSizeEvent & evt)
{
HIViewRef viewRef = mEffect->mAUView;
wxWindow *parent = mEffect->mParent;
// The parent panel has been resized, so make the AU the
// same size.
HIRect rect;
HIViewGetFrame((HIViewRef) parent->GetHandle(), &rect);
HIViewSetFrame(viewRef, &rect);
}
///////////////////////////////////////////////////////////////////////////////
//
// AudioUnitEffect
//
///////////////////////////////////////////////////////////////////////////////
/* Not using this yet...was intended to improve resizing
// Event handler to capture the window close event
static const EventTypeSpec controlEventList[] =
{
{kEventClassControl,kEventControlBoundsChanged},
};
pascal OSStatus
AudioUnitEffect::ControlEventHandlerCallback(EventHandlerCallRef handler, EventRef event, void *data)
{
return ((AudioUnitEffect *)data)->ControlEventHandler(event);
}
OSStatus AudioUnitEffect::ControlEventHandler(EventRef event)
{
OSStatus result = eventNotHandledErr;
printf("CONTROL class %d kind %d\n", GetEventClass(event), GetEventKind(event));
if (GetEventClass(event) == kEventClassControl && GetEventKind(event) == kEventControlBoundsChanged)
{
HIRect rect;
HIViewGetFrame(mAUView, &rect);
...
return noErr;
}
return result;
}
*/
// Event handler to capture the window close event
static const EventTypeSpec windowEventList[] =
{
{kEventClassMouse, kEventMouseDown},
{kEventClassMouse, kEventMouseUp},
{kEventClassMouse, kEventMouseMoved},
{kEventClassMouse, kEventMouseDragged},
{kEventClassMouse, kEventMouseEntered},
{kEventClassMouse, kEventMouseExited},
{kEventClassMouse, kEventMouseWheelMoved},
{kEventClassMouse, kEventMouseScroll},
// {kEventClassWindow, kEventWindowClose},
};
pascal OSStatus
AudioUnitEffect::WindowEventHandlerCallback(EventHandlerCallRef handler, EventRef event, void *data)
{
return ((AudioUnitEffect *)data)->WindowEventHandler(event);
}
OSStatus AudioUnitEffect::WindowEventHandler(EventRef eventRef)
{
wxMacCarbonEvent event(eventRef);
OSStatus result = eventNotHandledErr;
// Give Cocoa (in HIView) controls first dibs at mouse event
if (GetEventClass(event) == kEventClassMouse)
{
OSStatus result;
HIPoint pt;
result = event.GetParameter(kEventParamMouseLocation,
typeHIPoint,
sizeof(pt),
&pt);
WindowRef rootWindow = (WindowRef)mDialog->MacGetTopLevelWindowRef();
ControlRef rootControl = HIViewGetRoot(rootWindow);
HIViewRef hitRef = 0;
result = HIViewGetViewForMouseEvent(rootControl,
eventRef,
&hitRef);
if (hitRef == mAUView && !mIsCarbon)
{
return SendEventToEventTarget(event, mEventRef);
}
}
/* Not used, but leaving just in case
if (GetEventClass(event) == kEventClassWindow && GetEventKind(event) == kEventWindowClose)
{
result = noErr;
}
*/
return result;
}
AudioUnitEffect::AudioUnitEffect(const wxString & path,
const wxString & name,
Component component,
AudioUnitEffect *master)
{
mPath = path;
mName = name.AfterFirst(wxT(':')).Trim(true).Trim(false);
mVendor = name.BeforeFirst(wxT(':')).Trim(true).Trim(false);
mComponent = component;
mMaster = master;
mUnit = NULL;
mLatency = 0.0;
mTailTime = 0.0;
mBlockSize = 0.0;
mUIHost = NULL;
mDialog = NULL;
mParent = NULL;
mCarbonView = NULL;
mHandlerRef = NULL;
mHandlerUPP = NULL;
mEventHelper = NULL;
mEventListenerRef = NULL;
}
AudioUnitEffect::~AudioUnitEffect()
{
if (mEventListenerRef)
{
AUListenerDispose(mEventListenerRef);
}
if (mUnit)
{
CloseComponent(mUnit);
}
}
// ============================================================================
// IdentInterface implementation
// ============================================================================
wxString AudioUnitEffect::GetID()
{
return mPath;
}
wxString AudioUnitEffect::GetPath()
{
return mPath;
}
wxString AudioUnitEffect::GetName()
{
return mName;
}
wxString AudioUnitEffect::GetVendor()
{
return mVendor;
}
wxString AudioUnitEffect::GetVersion()
{
ComponentResult version = GetComponentVersion((ComponentInstance) mComponent);
return wxString::Format(wxT("%d.%d.%d"),
(version >> 16) & 0xffff,
(version >> 8) & 0xff,
version & 0xff);
}
wxString AudioUnitEffect::GetDescription()
{
return wxT("N/A");
}
// ============================================================================
// EffectIdentInterface implementation
// ============================================================================
EffectType AudioUnitEffect::GetType()
{
if (mAudioIns == 0 && mAudioOuts == 0)
{
return EffectTypeNone;
}
if (mAudioIns == 0)
{
return EffectTypeGenerate;
}
if (mAudioOuts == 0)
{
return EffectTypeAnalyze;
}
return EffectTypeProcess;
}
wxString AudioUnitEffect::GetFamily()
{
return AUDIOUNITEFFECTS_FAMILY;
}
bool AudioUnitEffect::IsInteractive()
{
return mInteractive;
}
bool AudioUnitEffect::IsDefault()
{
return false;
}
bool AudioUnitEffect::IsLegacy()
{
return false;
}
bool AudioUnitEffect::SupportsRealtime()
{
return GetType() == EffectTypeProcess;
}
bool AudioUnitEffect::SupportsAutomation()
{
OSStatus result;
UInt32 dataSize;
Boolean isWritable;
result = AudioUnitGetPropertyInfo(mUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
&dataSize,
&isWritable);
if (result != noErr)
{
return false;
}
UInt32 cnt = dataSize / sizeof(AudioUnitParameterID);
AudioUnitParameterID *array = new AudioUnitParameterID[cnt];
result = AudioUnitGetProperty(mUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
array,
&dataSize);
if (result != noErr)
{
delete [] array;
return false;
}
for (int i = 0; i < cnt; i++)
{
AudioUnitParameterInfo info;
dataSize = sizeof(info);
result = AudioUnitGetProperty(mUnit,
kAudioUnitProperty_ParameterInfo,
kAudioUnitScope_Global,
array[i],
&info,
&dataSize);
if (result != noErr)
{
delete [] array;
return false;
}
if (info.flags & kAudioUnitParameterFlag_IsWritable)
{
// All we need is one
delete [] array;
return true;
}
}
delete [] array;
return false;
}
// ============================================================================
// EffectClientInterface Implementation
// ============================================================================
bool AudioUnitEffect::SetHost(EffectHostInterface *host)
{
OSStatus result;
mHost = host;
mSampleRate = 44100;
ComponentResult auResult;
auResult = OpenAComponent(mComponent, &mUnit);
if (!mUnit)
{
return false;
}
GetChannelCounts();
SetRateAndChannels();
UInt32 dataSize;
// Retrieve the latency (can be updated via an event)
dataSize = sizeof(mLatency);
mLatency = 0.0;
AudioUnitGetProperty(mUnit,
kAudioUnitProperty_Latency,
kAudioUnitScope_Global,
0,
&mLatency,
&dataSize);
// Retrieve the tail time
dataSize = sizeof(mTailTime);
mTailTime = 0.0;
AudioUnitGetProperty(mUnit,
kAudioUnitProperty_TailTime,
kAudioUnitScope_Global,
0,
&mTailTime,
&dataSize);
// Retrieve the desired number of frames per slice
dataSize = sizeof(mBlockSize);
mBlockSize = 512;
AudioUnitGetProperty(mUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global,
0,
&mBlockSize,
&dataSize);
// mHost will be null during registration
if (mHost)
{
mHost->GetSharedConfig(wxT("Settings"), wxT("BufferSize"), mBufferSize, 8192);
mHost->GetSharedConfig(wxT("Settings"), wxT("UseBufferDelay"), mUseBufferDelay, true);
mHost->GetSharedConfig(wxT("Settings"), wxT("UseGUI"), mUseGUI, true);
bool haveDefaults;
mHost->GetPrivateConfig(wxT("Default"), wxT("Initialized"), haveDefaults, false);
if (!haveDefaults)
{
SaveParameters(wxT("Default"));
mHost->SetPrivateConfig(wxT("Default"), wxT("Initialized"), true);
}
LoadParameters(wxT("Current"));
}
if (!mMaster)
{
result = AUEventListenerCreate(AudioUnitEffect::EventListenerCallback,
this,
(CFRunLoopRef)GetCFRunLoopFromEventLoop(GetCurrentEventLoop()),
kCFRunLoopDefaultMode,
0.0,
0.0,
&mEventListenerRef);
if (result != noErr)
{
return false;
}
AudioUnitEvent event;
event.mEventType = kAudioUnitEvent_ParameterValueChange;
event.mArgument.mParameter.mAudioUnit = mUnit;
event.mArgument.mParameter.mParameterID = 1; //kAUParameterListener_AnyParameter;
event.mArgument.mParameter.mScope = kAudioUnitScope_Global;
event.mArgument.mParameter.mElement = 0;
UInt32 dataSize;
Boolean isWritable;
// Retrieve the list of properties
result = AudioUnitGetPropertyInfo(mUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
&dataSize,
&isWritable);
if (result != noErr)
{
return false;
}
// And get them
UInt32 cnt = dataSize / sizeof(AudioUnitParameterID);
AudioUnitParameterID *array = new AudioUnitParameterID[cnt];
result = AudioUnitGetProperty(mUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
array,
&dataSize);
if (result != noErr)
{
delete [] array;
return false;
}
// Register them as something we're interested in
for (int i = 0; i < cnt; i++)
{
event.mArgument.mParameter.mParameterID = array[i];
result = AUEventListenerAddEventType(mEventListenerRef,
this,
&event);
if (result != noErr)
{
delete [] array;
return false;
}
}
delete [] array;
event.mEventType = kAudioUnitEvent_PropertyChange;
event.mArgument.mProperty.mAudioUnit = mUnit;
event.mArgument.mProperty.mPropertyID = kAudioUnitProperty_Latency;
event.mArgument.mProperty.mScope = kAudioUnitScope_Global;
event.mArgument.mProperty.mElement = 0;
result = AUEventListenerAddEventType(mEventListenerRef,
this,
&event);
if (result != noErr)
{
return false;
}
}
return true;
}
int AudioUnitEffect::GetAudioInCount()
{
return mAudioIns;
}
int AudioUnitEffect::GetAudioOutCount()
{
return mAudioOuts;
}
int AudioUnitEffect::GetMidiInCount()
{
return 0;
}
int AudioUnitEffect::GetMidiOutCount()
{
return 0;
}
void AudioUnitEffect::SetSampleRate(sampleCount rate)
{
mSampleRate = rate;
}
sampleCount AudioUnitEffect::GetBlockSize(sampleCount maxBlockSize)
{
return mBlockSize;
}
sampleCount AudioUnitEffect::GetLatency()
{
if (!mLatencyDone)
{
mLatencyDone = true;
return mLatency * mSampleRate;
}
return 0;
}
sampleCount AudioUnitEffect::GetTailSize()
{
return mTailTime * mSampleRate;
}
bool AudioUnitEffect::IsReady()
{
return mReady;
}
bool AudioUnitEffect::ProcessInitialize()
{
ComponentResult auResult;
mInputList = new AudioBufferList[mAudioIns];
mInputList->mNumberBuffers = mAudioIns;
mOutputList = new AudioBufferList[mAudioOuts];
mOutputList->mNumberBuffers = mAudioOuts;
memset(&mTimeStamp, 0, sizeof(AudioTimeStamp));
mTimeStamp.mSampleTime = 0; // This is a double-precision number that should
// accumulate the number of frames processed so far
mTimeStamp.mFlags = kAudioTimeStampSampleTimeValid;
if (!SetRateAndChannels())
{
return false;
}
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = RenderCallback;
callbackStruct.inputProcRefCon = this;
auResult = AudioUnitSetProperty(mUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&callbackStruct,
sizeof(AURenderCallbackStruct));
if (auResult != 0)
{
printf("Setting input render callback failed.\n");
return false;
}
auResult = AudioUnitInitialize(mUnit);
if (auResult != 0)
{
printf("Couldn't initialize audio unit\n");
return false;
}
auResult = AudioUnitReset(mUnit, kAudioUnitScope_Global, 0);
if (auResult != 0)
{
return false;
}
mReady = true;
return true;
}
bool AudioUnitEffect::ProcessFinalize()
{
if (mReady)
{
AudioUnitUninitialize(mUnit);
mReady = false;
}
if (mOutputList)
{
delete [] mOutputList;
mInputList = NULL;
}
if (mInputList)
{
delete [] mInputList;
mInputList = NULL;
}
return true;
}
sampleCount AudioUnitEffect::ProcessBlock(float **inbuf, float **outbuf, sampleCount size)
{
for (int i = 0; i < mAudioIns; i++)
{
mInputList->mBuffers[i].mNumberChannels = 1;
mInputList->mBuffers[i].mData = inbuf[i];
mInputList->mBuffers[i].mDataByteSize = sizeof(float) * size;
}
for (int i = 0; i < mAudioOuts; i++)
{
mOutputList->mBuffers[i].mNumberChannels = 1;
mOutputList->mBuffers[i].mData = outbuf[i];
mOutputList->mBuffers[i].mDataByteSize = sizeof(float) * size;
}
AudioUnitRenderActionFlags flags = 0;
ComponentResult auResult;
auResult = AudioUnitRender(mUnit,
&flags,
&mTimeStamp,
0,
size,
mOutputList);
if (auResult != 0)
{
printf("Render failed: %d %4.4s\n", (int)auResult, (char *)&auResult);
return 0;
}
mTimeStamp.mSampleTime += size;
return size;
}
bool AudioUnitEffect::RealtimeInitialize()
{
// This is really just a dummy value and one to make the dialog happy since
// all processing is handled by slaves.
SetSampleRate(44100);
mMasterIn = NULL;
mMasterInLen = 0;
mMasterOut = NULL;
mMasterOutLen = 0;
return ProcessInitialize();
}
bool AudioUnitEffect::RealtimeFinalize()
{
for (size_t i = 0, cnt = mSlaves.GetCount(); i < cnt; i++)
{
mSlaves[i]->RealtimeFinalize();
delete mSlaves[i];
}
mSlaves.Clear();
if (mMasterIn)
{
for (int i = 0; i < mAudioIns; i++)
{
delete [] mMasterIn[i];
}
delete [] mMasterIn;
mMasterIn = NULL;
}
if (mMasterOut)
{
for (int i = 0; i < mAudioOuts; i++)
{
delete [] mMasterOut[i];
}
delete [] mMasterOut;
mMasterOut = NULL;
}
return ProcessFinalize();
}
bool AudioUnitEffect::RealtimeSuspend()
{
return true;
}
bool AudioUnitEffect::RealtimeResume()
{
ComponentResult auResult;
auResult = AudioUnitReset(mUnit, kAudioUnitScope_Global, 0);
if (auResult != 0)
{
return false;
}
return true;
}
sampleCount AudioUnitEffect::RealtimeProcess(int group,
float **inbuf,
float **outbuf,
sampleCount numSamples)
{
if (group < 0 || group >= (int) mSlaves.GetCount())
{
return 0;
}
if (group == 0)
{
if (mMasterIn == NULL || mMasterInLen < numSamples)
{
if (mMasterIn)
{
for (int i = 0; i < mAudioIns; i++)
{
delete [] mMasterIn[i];
}
delete [] mMasterIn;
}
mMasterIn = new float *[mAudioIns];
for (int i = 0; i < mAudioIns; i++)
{
mMasterIn[i] = new float[numSamples];
}
mMasterInLen = numSamples;
}
for (int i = 0; i < mAudioIns; i++)
{
memset(mMasterIn[i], 0, numSamples * sizeof(float));
}
}
// This should never happen, but let's make sure
wxASSERT(numSamples <= mMasterInLen);
int chanCnt = wxMin(mSlaves[group]->GetChannelCount(), mAudioIns);
for (int c = 0; c < chanCnt; c++)
{
for (int i = 0; i < numSamples; i++)
{
mMasterIn[c][i] += inbuf[c][i];
}
}
if (group == (int) mSlaves.GetCount() - 1)
{
if (mMasterOut == NULL || mMasterOutLen < numSamples)
{
if (mMasterOut)
{
for (int i = 0; i < mAudioOuts; i++)
{
delete [] mMasterOut[i];
}
delete [] mMasterOut;
mMasterOut = NULL;
}
mMasterOut = new float *[mAudioOuts];
for (int i = 0; i < mAudioOuts; i++)
{
mMasterOut[i] = new float[numSamples];
}
mMasterOutLen = numSamples;
}
// This should never happen, but let's make sure
wxASSERT(numSamples <= mMasterOutLen);
ProcessBlock(mMasterIn, mMasterOut, numSamples);
}
return mSlaves[group]->ProcessBlock(inbuf, outbuf, numSamples);
}
bool AudioUnitEffect::RealtimeAddProcessor(int numChannels, float sampleRate)
{
AudioUnitEffect *slave = new AudioUnitEffect(mPath, mName, mComponent, this);
if (!slave->SetHost(NULL))
{
delete slave;
return false;
}
slave->SetChannelCount(numChannels);
slave->SetSampleRate(sampleRate);
if (!CopyParameters(mUnit, slave->mUnit))
{
delete slave;
return false;
}
mSlaves.Add(slave);
return slave->RealtimeInitialize();
}
bool AudioUnitEffect::ShowInterface(wxWindow *parent, bool forceModal)
{
if (mDialog)
{
mDialog->Close(true);
return false;
}
mDialog = mHost->CreateUI(parent, this);
if (!mDialog)
{
return false;
}
if ((SupportsRealtime() || GetType() == EffectTypeAnalyze) && !forceModal)
{
mDialog->Show();
return false;
}
bool res = mDialog->ShowModal() != 0;
mDialog = NULL;
return res;
}
bool AudioUnitEffect::GetAutomationParameters(EffectAutomationParameters & parms)
{
OSStatus result;
UInt32 dataSize;
Boolean isWritable;
result = AudioUnitGetPropertyInfo(mUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
&dataSize,
&isWritable);
if (result != noErr)
{
return false;
}
UInt32 cnt = dataSize / sizeof(AudioUnitParameterID);
AudioUnitParameterID *array = new AudioUnitParameterID[cnt];
result = AudioUnitGetProperty(mUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
array,
&dataSize);
if (result != noErr)
{
delete [] array;
return false;
}
for (int i = 0; i < cnt; i++)
{
AudioUnitParameterInfo info;
dataSize = sizeof(info);
result = AudioUnitGetProperty(mUnit,
kAudioUnitProperty_ParameterInfo,
kAudioUnitScope_Global,
array[i],
&info,
&dataSize);
if (result != noErr)
{
delete [] array;
return false;
}
wxString name;
if (info.flags & kAudioUnitParameterFlag_HasCFNameString)
{
wxMacCFStringHolder nameHolder(info.cfNameString, false);
name = nameHolder.AsString();
if (info.flags & kAudioUnitParameterFlag_CFNameRelease)
{
CFRelease(info.cfNameString);
}
}
if (name.IsEmpty())
{
continue;
}
AudioUnitParameterValue value;
result = AudioUnitGetParameter(mUnit,
array[i],
kAudioUnitScope_Global,
0,
&value);
if (result != noErr)
{
delete [] array;
return false;
}
parms.Write(name, value);
}
delete [] array;
return true;
}
bool AudioUnitEffect::SetAutomationParameters(EffectAutomationParameters & parms)
{
OSStatus result;
UInt32 dataSize;
Boolean isWritable;
result = AudioUnitGetPropertyInfo(mUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
&dataSize,
&isWritable);
if (result != noErr)
{
return false;
}
UInt32 cnt = dataSize / sizeof(AudioUnitParameterID);
AudioUnitParameterID *array = new AudioUnitParameterID[cnt];
result = AudioUnitGetProperty(mUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
array,
&dataSize);
if (result != noErr)
{
delete [] array;
return false;
}
for (int i = 0; i < cnt; i++)
{
AudioUnitParameterInfo info;
dataSize = sizeof(info);
result = AudioUnitGetProperty(mUnit,
kAudioUnitProperty_ParameterInfo,
kAudioUnitScope_Global,
array[i],
&info,
&dataSize);
if (result != noErr)
{
delete [] array;
return false;
}
wxString name;
if (info.flags & kAudioUnitParameterFlag_HasCFNameString)
{
wxMacCFStringHolder nameHolder(info.cfNameString, false);
name = nameHolder.AsString();
if (info.flags & kAudioUnitParameterFlag_CFNameRelease)
{
CFRelease(info.cfNameString);
}
}
if (name.IsEmpty())
{
continue;
}
double d = 0.0;
if (!parms.Read(name, &d))
{
delete [] array;
return false;
}
AudioUnitParameterValue value = d;
result = AudioUnitSetParameter(mUnit,
array[i],
kAudioUnitScope_Global,
0,
value,
0);
if (result != noErr)
{
delete [] array;
return false;
}
}
delete [] array;
return true;
}
// ============================================================================
// EffectUIClientInterface Implementation
// ============================================================================
void AudioUnitEffect::SetUIHost(EffectUIHostInterface *host)
{
mUIHost = host;
}
bool AudioUnitEffect::PopulateUI(wxWindow *parent)
{
OSStatus result;
mCarbonView = NULL;
mDialog = (wxDialog *) wxGetTopLevelParent(parent);
mParent = parent;
HIWindowChangeClass((WindowRef) mDialog->MacGetWindowRef(), kFloatingWindowClass);
WindowRef windowRef = (WindowRef) mDialog->MacGetWindowRef();
ControlRef rootControl = HIViewGetRoot(windowRef);
// Find the content view within our window
HIViewRef contentView;
HIViewFindByID(HIViewGetRoot(windowRef), kHIViewWindowContentID, &contentView);
mIsCocoa = false;
mIsCarbon = false;
mIsGeneric = false;
// Create the AU editor
HIViewRef auView = NULL;
if (mUseGUI)
{
auView = createCocoa(mUnit);
if (auView != NULL)
{
mIsCocoa = true;
}
else
{
auView = createCarbon(mUnit, windowRef, &mCarbonView);
if (auView != NULL)
{
mIsCarbon = true;
// Some effects do not work unless the default handler is removed since
// it captures many of the events that the plugins need. But, it must be
// done last since proper window sizing will not occur otherwise.
::RemoveEventHandler((EventHandlerRef) mDialog->MacGetEventHandler());
}
}
}
// Either GUI creation failed or the user wants the generic view
if (auView == NULL)
{
ComponentDescription desc;
result = GetComponentInfo(mComponent, &desc, NULL, NULL, NULL);
if (result == noErr && desc.componentType == kAudioUnitType_Panner)
{
auView = createPanner(mUnit);
}
if (auView == NULL)
{
auView = createGeneric(mUnit);
if (auView != NULL)
{
mIsGeneric = true;
}
}
}
// Total failure...bail
if (auView == NULL)
{
return false;
}
mAUView = auView;
HIViewAddSubview((HIViewRef) mParent->GetHandle(), auView);
HIViewPlaceInSuperviewAt(auView, 0, 0);
HIViewSetVisible(auView, true);
HIRect rect;
HIViewGetFrame(auView, &rect);
mParent->SetMinSize(wxSize(rect.size.width, rect.size.height));
mParent->SetSize(wxSize(rect.size.width, rect.size.height));
mDialog->Layout();
mDialog->Fit();
mDialog->SetMinSize(mDialog->GetSize());
mEventHelper = new AudioUnitEffectEventHelper(this);
mParent->PushEventHandler(mEventHelper);
mEventRef = GetControlEventTarget(auView);
// Install a bare minimum handler so we can capture the window close event. If
// it's not captured, we will crash at Audacity termination since the window
// is still on the wxWidgets toplevel window lists, but it's already gone.
mHandlerUPP = NewEventHandlerUPP(AudioUnitEffect::WindowEventHandlerCallback);
InstallWindowEventHandler(windowRef,
mHandlerUPP,
GetEventTypeCount(windowEventList),
windowEventList,
this,
&mHandlerRef);
/* Was intended for improved resizing...not being used
// Install a bare minimum handler so we can capture the window close event. If
// it's not captured, we will crash at Audacity termination since the window
// is still on the wxWidgets toplevel window lists, but it's already gone.
mControlHandlerUPP = NewEventHandlerUPP(AudioUnitEffect::ControlEventHandlerCallback);
InstallControlEventHandler(auView,
mControlHandlerUPP,
GetEventTypeCount(controlEventList),
controlEventList,
this,
&mControlHandlerRef);
*/
return true;
}
bool AudioUnitEffect::ValidateUI()
{
#if 0
if (!mParent->Validate())
{
return false;
}
if (GetType() == EffectTypeGenerate)
{
mHost->SetDuration(mDuration->GetValue());
}
#endif
return true;
}
bool AudioUnitEffect::HideUI()
{
#if 0
if (GetType() == EffectTypeAnalyze || mNumOutputControls > 0)
{
return false;
}
#endif
return true;
}
bool AudioUnitEffect::CloseUI()
{
if (mEventHelper)
{
mParent->RemoveEventHandler(mEventHelper);
delete mEventHelper;
}
if (mCarbonView)
{
CloseComponent(mCarbonView);
}
if (mIsCarbon)
{
// Reinstall the wxWidgets toplevel event handler
mDialog->MacInstallTopLevelWindowEventHandler();
}
mUIHost = NULL;
mParent = NULL;
mDialog = NULL;
return true;
}
void AudioUnitEffect::LoadUserPreset(const wxString & name)
{
LoadParameters(name);
}
void AudioUnitEffect::SaveUserPreset(const wxString & name)
{
SaveParameters(name);
}
void AudioUnitEffect::LoadFactoryPreset(int id)
{
OSStatus result;
// Retrieve the list of factory presets
CFArrayRef array;
UInt32 dataSize = sizeof(CFArrayRef);
result = AudioUnitGetProperty(mUnit,
kAudioUnitProperty_FactoryPresets,
kAudioUnitScope_Global,
0,
&array,
&dataSize);
if (result == noErr)
{
if (id < CFArrayGetCount(array))
{
AUPreset *preset = (AUPreset *) CFArrayGetValueAtIndex(array, id);
result = AudioUnitSetProperty(mUnit,
kAudioUnitProperty_PresentPreset,
kAudioUnitScope_Global,
0,
preset,
sizeof(AUPreset));
if (result == noErr)
{
AudioUnitParameter aup;
aup.mAudioUnit = mUnit;
aup.mParameterID = kAUParameterListener_AnyParameter;
aup.mScope = kAudioUnitScope_Global;
aup.mElement = 0;
AUParameterListenerNotify(NULL, NULL, &aup);
}
}
CFRelease(array);
}
return;
}
void AudioUnitEffect::LoadFactoryDefaults()
{
LoadParameters(mHost->GetFactoryDefaultsGroup());
}
wxArrayString AudioUnitEffect::GetFactoryPresets()
{
OSStatus result;
wxArrayString presets;
// Retrieve the list of factory presets
CFArrayRef array;
UInt32 dataSize = sizeof(CFArrayRef);
result = AudioUnitGetProperty(mUnit,
kAudioUnitProperty_FactoryPresets,
kAudioUnitScope_Global,
0,
&array,
&dataSize);
if (result == noErr)
{
for (CFIndex i = 0, cnt = CFArrayGetCount(array); i < cnt; i++)
{
AUPreset *preset = (AUPreset *) CFArrayGetValueAtIndex(array, i);
wxMacCFStringHolder holder(preset->presetName, false);
presets.Add(holder.AsString());
}
CFRelease(array);
}
return presets;
}
void AudioUnitEffect::ExportPresets()
{
}
void AudioUnitEffect::ImportPresets()
{
}
void AudioUnitEffect::ShowOptions()
{
AudioUnitEffectSettingsDialog dlg(mParent, mHost);
dlg.ShowModal();
}
// ============================================================================
// AudioUnitEffect Implementation
// ============================================================================
void AudioUnitEffect::LoadParameters(const wxString & group)
{
OSStatus result;
UInt32 dataSize;
Boolean isWritable;
wxString value;
if (!mHost->GetPrivateConfig(group, wxT("Value"), value, wxEmptyString))
{
return;
}
wxStringTokenizer tokens(value, wxT(','));
result = AudioUnitGetPropertyInfo(mUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
&dataSize,
&isWritable);
if (result != noErr)
{
return;
}
UInt32 cnt = dataSize / sizeof(AudioUnitParameterID);
if (cnt != tokens.CountTokens())
{
return;
}
AudioUnitParameterID *array = new AudioUnitParameterID[cnt];
result = AudioUnitGetProperty(mUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
array,
&dataSize);
if (result != noErr)
{
delete [] array;
return;
}
for (int i = 0; i < cnt; i++)
{
double d = 0.0;
tokens.GetNextToken().ToDouble(&d);
AudioUnitParameterValue value = d;
result = AudioUnitSetParameter(mUnit,
array[i],
kAudioUnitScope_Global,
0,
value,
0);
if (result != noErr)
{
delete [] array;
return;
}
}
AudioUnitParameter aup;
aup.mAudioUnit = mUnit;
aup.mParameterID = kAUParameterListener_AnyParameter;
aup.mScope = kAudioUnitScope_Global;
aup.mElement = 0;
AUParameterListenerNotify(NULL, NULL, &aup);
delete [] array;
}
void AudioUnitEffect::SaveParameters(const wxString & group)
{
OSStatus result;
UInt32 dataSize;
Boolean isWritable;
wxString parms;
result = AudioUnitGetPropertyInfo(mUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
&dataSize,
&isWritable);
if (result != noErr)
{
return;
}
UInt32 cnt = dataSize / sizeof(AudioUnitParameterID);
AudioUnitParameterID *array = new AudioUnitParameterID[cnt];
result = AudioUnitGetProperty(mUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
array,
&dataSize);
if (result != noErr)
{
delete [] array;
return;
}
for (int i = 0; i < cnt; i++)
{
AudioUnitParameterValue value;
result = AudioUnitGetParameter(mUnit,
array[i],
kAudioUnitScope_Global,
0,
&value);
if (result != noErr)
{
delete [] array;
return;
}
parms += wxString::Format(wxT(",%f"), value);
}
mHost->SetPrivateConfig(group, wxT("Value"), parms.Mid(1));
delete [] array;
}
bool AudioUnitEffect::SetRateAndChannels()
{
ComponentResult auResult;
auResult = AudioUnitSetProperty(mUnit,
kAudioUnitProperty_SampleRate,
kAudioUnitScope_Global,
0,
&mSampleRate,
sizeof(Float64));
if (auResult != 0)
{
printf("Didn't accept sample rate\n");
return false;
}
AudioStreamBasicDescription streamFormat = {0};
streamFormat.mSampleRate = mSampleRate;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked |
kAudioFormatFlagIsNonInterleaved;
streamFormat.mBitsPerChannel = sizeof(float) * 8;
streamFormat.mChannelsPerFrame = mAudioIns;
streamFormat.mFramesPerPacket = 1;
streamFormat.mBytesPerFrame = sizeof(float);
streamFormat.mBytesPerPacket = sizeof(float);
auResult = AudioUnitSetProperty(mUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&streamFormat,
sizeof(AudioStreamBasicDescription));
if (auResult != 0)
{
return false;
}
streamFormat.mChannelsPerFrame = mAudioOuts;
auResult = AudioUnitSetProperty(mUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0,
&streamFormat,
sizeof(AudioStreamBasicDescription));
if (auResult != 0)
{
return false;
}
return true;
}
bool AudioUnitEffect::CopyParameters(AudioUnit srcUnit, AudioUnit dstUnit)
{
ComponentResult auResult;
int numParameters, i;
AudioUnitParameterID *parameters;
Float32 parameterValue;
UInt32 size;
// Get number of parameters by passing NULL in the data field and
// getting back the size of the parameter list
size = 0;
auResult = AudioUnitGetProperty(srcUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
NULL,
&size);
if (auResult != 0)
{
printf("Couldn't get number of parameters\n");
return false;
}
// Now get the list of all parameter IDs
numParameters = size / sizeof(AudioUnitParameterID);
parameters = new AudioUnitParameterID[numParameters];
auResult = AudioUnitGetProperty(srcUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
parameters,
&size);
if (auResult != 0)
{
printf("Couldn't get parameter list\n");
delete[] parameters;
return false;
}
// Copy the parameters from the main unit to the unit specific to
// this track
for (i = 0; i < numParameters; i++)
{
auResult = AudioUnitGetParameter(srcUnit,
parameters[i],
kAudioUnitScope_Global,
0,
&parameterValue);
if (auResult != 0)
{
printf("Couldn't get parameter %d: ID=%d\n", i, (int)parameters[i]);
continue;
}
auResult = AudioUnitSetParameter(dstUnit,
parameters[i],
kAudioUnitScope_Global,
0,
parameterValue,
0);
if (auResult != 0)
{
printf("Couldn't set parameter %d: ID=%d\n", i, (int)parameters[i]);
}
}
delete[] parameters;
return true;
}
int AudioUnitEffect::GetChannelCount()
{
return mNumChannels;
}
void AudioUnitEffect::SetChannelCount(int numChannels)
{
mNumChannels = numChannels;
}
OSStatus AudioUnitEffect::Render(AudioUnitRenderActionFlags *inActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumFrames,
AudioBufferList *ioData)
{
for (int i = 0; i < ioData->mNumberBuffers; i++)
{
ioData->mBuffers[i].mData = mInputList->mBuffers[i].mData;
}
return 0;
}
// static
OSStatus AudioUnitEffect::RenderCallback(void *inRefCon,
AudioUnitRenderActionFlags *inActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumFrames,
AudioBufferList *ioData)
{
return ((AudioUnitEffect *) inRefCon)->Render(inActionFlags,
inTimeStamp,
inBusNumber,
inNumFrames,
ioData);
}
void AudioUnitEffect::EventListener(const AudioUnitEvent *inEvent,
AudioUnitParameterValue inParameterValue)
{
if (mMaster)
{
// We're a slave, so just set the parameter
AudioUnitSetParameter(mUnit,
inEvent->mArgument.mParameter.mParameterID,
kAudioUnitScope_Global,
0,
inParameterValue,
0);
}
else
{
// We're the master, so propogate
for (size_t i = 0, cnt = mSlaves.GetCount(); i < cnt; i++)
{
mSlaves[i]->EventListener(inEvent, inParameterValue);
}
}
}
// static
void AudioUnitEffect::EventListenerCallback(void *inCallbackRefCon,
void *inObject,
const AudioUnitEvent *inEvent,
UInt64 inEventHostTime,
AudioUnitParameterValue inParameterValue)
{
((AudioUnitEffect *) inCallbackRefCon)->EventListener(inEvent,
inParameterValue);
}
void AudioUnitEffect::GetChannelCounts()
{
Boolean isWritable = 0;
UInt32 dataSize = 0;
OSStatus result;
// Does AU have channel info
result = AudioUnitGetPropertyInfo(mUnit,
kAudioUnitProperty_SupportedNumChannels,
kAudioUnitScope_Global,
0,
&dataSize,
&isWritable);
if (result)
{
// None supplied. Apparently all FX type units can do any number of INs
// and OUTs as long as they are the same number. In this case, we'll
// just say stereo.
//
// We should probably check to make sure we're dealing with an FX type.
mAudioIns = 2;
mAudioOuts = 2;
return;
}
AUChannelInfo *info = (AUChannelInfo *) malloc(dataSize);
// Retrieve the channel info
result = AudioUnitGetProperty(mUnit,
kAudioUnitProperty_SupportedNumChannels,
kAudioUnitScope_Global,
0,
info,
&dataSize);
if (result)
{
// Oh well, not much we can do out this case
mAudioIns = 2;
mAudioOuts = 2;
free(info);
return;
}
// This is where it gets weird...not sure what is the best
// way to do this really. If we knew how many ins/outs we
// really needed, we could make a better choice.
bool haven2m = false; // nothing -> mono
bool haven2s = false; // nothing -> stereo
bool havem2n = false; // mono -> nothing
bool haves2n = false; // stereo -> nothing
bool havem2m = false; // mono -> mono
bool haves2s = false; // stereo -> stereo
bool havem2s = false; // mono -> stereo
bool haves2m = false; // stereo -> mono
mAudioIns = 2;
mAudioOuts = 2;
// Look only for exact channel constraints
for (int i = 0; i < dataSize / sizeof(AUChannelInfo); i++)
{
AUChannelInfo *ci = &info[i];
int ic = ci->inChannels;
int oc = ci->outChannels;
if (ic < 0 && oc >= 0)
{
ic = 2;
}
else if (ic >= 0 && oc < 0)
{
oc = 2;
}
else if (ic < 0 && oc < 0)
{
ic = 2;
oc = 2;
}
if (ic == 2 && oc == 2)
{
haves2s = true;
}
else if (ic == 1 && oc == 1)
{
havem2m = true;
}
else if (ic == 1 && oc == 2)
{
havem2s = true;
}
else if (ic == 2 && oc == 1)
{
haves2m = true;
}
else if (ic == 0 && oc == 2)
{
haven2s = true;
}
else if (ic == 0 && oc == 1)
{
haven2m = true;
}
else if (ic == 1 && oc == 0)
{
havem2n = true;
}
else if (ic == 2 && oc == 0)
{
haves2n = true;
}
}
if (haves2s)
{
mAudioIns = 2;
mAudioOuts = 2;
}
else if (havem2m)
{
mAudioIns = 1;
mAudioOuts = 1;
}
else if (havem2s)
{
mAudioIns = 1;
mAudioOuts = 2;
}
else if (haves2m)
{
mAudioIns = 2;
mAudioOuts = 1;
}
else if (haven2m)
{
mAudioIns = 0;
mAudioOuts = 1;
}
else if (haven2s)
{
mAudioIns = 0;
mAudioOuts = 2;
}
else if (haves2n)
{
mAudioIns = 2;
mAudioOuts = 0;
}
else if (havem2n)
{
mAudioIns = 1;
mAudioOuts = 0;
}
return;
}