audacia/src/effects/audiounits/AudioUnitEffect.cpp

2349 lines
61 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 "../../Audacity.h"
#if USE_AUDIO_UNITS
#include <wx/defs.h>
#include <wx/button.h>
#include <wx/control.h>
#include <wx/dir.h>
#include <wx/filename.h>
#include <wx/frame.h>
#include <wx/listctrl.h>
#include <wx/sizer.h>
#include <wx/settings.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/tokenzr.h>
#include "../../ShuttleGui.h"
#include "../../widgets/valnum.h"
#include "../../widgets/wxPanelWrapper.h"
#include "AudioUnitEffect.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
// Trust the module manager not to leak this
return safenew 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::GetPath()
{
return mPath;
}
wxString AudioUnitEffectsModule::GetSymbol()
{
return wxT("Audio Unit Effects");
}
wxString AudioUnitEffectsModule::GetName()
{
return XO("Audio Unit Effects");
}
wxString AudioUnitEffectsModule::GetVendor()
{
return XO("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 XO("Provides Audio Unit 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;
AudioComponent component = FindAudioUnit(path, name);
if (component == NULL)
{
return false;
}
AudioUnitEffect effect(path, name, component);
if (!effect.SetHost(NULL))
{
return false;
}
pm.RegisterPlugin(this, &effect);
return true;
}
bool AudioUnitEffectsModule::IsPluginValid(const wxString & path)
{
wxString name;
return FindAudioUnit(path, name) != NULL;
}
IdentInterface *AudioUnitEffectsModule::CreateInstance(const wxString & path)
{
// Acquires a resource for the application.
wxString name;
AudioComponent component = FindAudioUnit(path, name);
if (component == NULL)
{
return NULL;
}
// Safety of this depends on complementary calls to DeleteInstance on the module manager side.
return safenew AudioUnitEffect(path, name, component);
}
void AudioUnitEffectsModule::DeleteInstance(IdentInterface *instance)
{
std::unique_ptr < AudioUnitEffect > {
dynamic_cast<AudioUnitEffect *>(instance)
};
}
// ============================================================================
// AudioUnitEffectsModule implementation
// ============================================================================
void AudioUnitEffectsModule::LoadAudioUnitsOfType(OSType inAUType,
wxArrayString & effects)
{
AudioComponentDescription desc;
AudioComponent component;
desc.componentType = inAUType;
desc.componentSubType = 0;
desc.componentManufacturer = 0;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
component = AudioComponentFindNext(NULL, &desc);
while (component != NULL)
{
OSStatus result;
AudioComponentDescription found;
result = AudioComponentGetDescription(component, &found);
if (result == noErr)
{
CFStringRef cfName;
result = AudioComponentCopyName(component, &cfName);
if (result == noErr)
{
wxString name = wxCFStringRef::AsString(cfName);
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()));
CFRelease(cfName);
}
}
component = AudioComponentFindNext(component, &desc);
}
}
AudioComponent AudioUnitEffectsModule::FindAudioUnit(const wxString & path,
wxString & name)
{
wxStringTokenizer tokens(path, wxT("/"));
AudioComponentDescription 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 AudioComponentFindNext(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;
}
///////////////////////////////////////////////////////////////////////////////
//
// AudioUnitEffectOptionsDialog
//
///////////////////////////////////////////////////////////////////////////////
class AudioUnitEffectOptionsDialog final : public wxDialogWrapper
{
public:
AudioUnitEffectOptionsDialog(wxWindow * parent, EffectHostInterface *host);
virtual ~AudioUnitEffectOptionsDialog();
void PopulateOrExchange(ShuttleGui & S);
void OnOk(wxCommandEvent & evt);
private:
EffectHostInterface *mHost;
bool mUseLatency;
wxString mUIType;
wxArrayString mUITypes;
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(AudioUnitEffectOptionsDialog, wxDialogWrapper)
EVT_BUTTON(wxID_OK, AudioUnitEffectOptionsDialog::OnOk)
END_EVENT_TABLE()
AudioUnitEffectOptionsDialog::AudioUnitEffectOptionsDialog(wxWindow * parent, EffectHostInterface *host)
: wxDialogWrapper(parent, wxID_ANY, wxString(_("Audio Unit Effect Options")))
{
mHost = host;
mUITypes.Add(_("Full"));
mUITypes.Add(_("Generic"));
mUITypes.Add(_("Basic"));
mHost->GetSharedConfig(wxT("Options"), wxT("UseLatency"), mUseLatency, true);
mHost->GetSharedConfig(wxT("Options"), wxT("UIType"), mUIType, wxT("Full"));
mUIType = wxGetTranslation(mUIType);
ShuttleGui S(this, eIsCreating);
PopulateOrExchange(S);
}
AudioUnitEffectOptionsDialog::~AudioUnitEffectOptionsDialog()
{
}
void AudioUnitEffectOptionsDialog::PopulateOrExchange(ShuttleGui & S)
{
S.SetBorder(5);
S.StartHorizontalLay(wxEXPAND, 1);
{
S.StartVerticalLay(false);
{
S.StartStatic(_("Latency Compensation"));
{
S.AddVariableText(wxString() +
_("As part of their processing, some Audio Unit 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 option will provide that compensation, but it may ") +
_("not work for all Audio Unit effects."))->Wrap(650);
S.StartHorizontalLay(wxALIGN_LEFT);
{
S.TieCheckBox(_("Enable &compensation"),
mUseLatency);
}
S.EndHorizontalLay();
}
S.EndStatic();
S.StartStatic(_("User Interface"));
{
S.AddVariableText(wxString() +
_("Select \"Full\" to use the graphical interface if supplied by the Audio Unit.") +
_(" Select \"Generic\" to use the system supplied generic interface.") +
_(" Select \"Basic\" for A basic text-only interface. ") +
_(" Reopen the effect for this to take effect."))->Wrap(650);
S.StartHorizontalLay(wxALIGN_LEFT);
{
S.TieChoice(_("Select &interface"),
mUIType,
&mUITypes);
}
S.EndHorizontalLay();
}
S.EndStatic();
}
S.EndVerticalLay();
}
S.EndHorizontalLay();
S.AddStandardButtons();
Layout();
Fit();
Center();
}
void AudioUnitEffectOptionsDialog::OnOk(wxCommandEvent & WXUNUSED(evt))
{
if (!Validate())
{
return;
}
ShuttleGui S(this, eIsGettingFromDialog);
PopulateOrExchange(S);
if (mUIType == _("Full"))
{
mUIType = wxT("Full");
}
else if (mUIType == _("Generic"))
{
mUIType = wxT("Generic");
}
else if (mUIType == _("Basic"))
{
mUIType = wxT("Basic");
}
mHost->SetSharedConfig(wxT("Options"), wxT("UseLatency"), mUseLatency);
mHost->SetSharedConfig(wxT("Options"), wxT("UIType"), mUIType);
EndModal(wxID_OK);
}
///////////////////////////////////////////////////////////////////////////////
//
// AudioUnitEffectExportDialog
//
///////////////////////////////////////////////////////////////////////////////
#define PRESET_LOCAL_PATH wxT("/Library/Audio/Presets")
#define PRESET_USER_PATH wxT("~/Library/Audio/Presets")
class AudioUnitEffectExportDialog final : public wxDialogWrapper
{
public:
AudioUnitEffectExportDialog(wxWindow * parent, AudioUnitEffect *effect);
virtual ~AudioUnitEffectExportDialog();
void PopulateOrExchange(ShuttleGui & S);
void OnOk(wxCommandEvent & evt);
private:
wxWindow *mParent;
AudioUnitEffect *mEffect;
wxListCtrl *mList;
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(AudioUnitEffectExportDialog, wxDialogWrapper)
EVT_BUTTON(wxID_OK, AudioUnitEffectExportDialog::OnOk)
END_EVENT_TABLE()
AudioUnitEffectExportDialog::AudioUnitEffectExportDialog(wxWindow * parent, AudioUnitEffect *effect)
: wxDialogWrapper(parent, wxID_ANY, wxString(_("Export Audio Unit Presets")))
{
mEffect = effect;
ShuttleGui S(this, eIsCreating);
PopulateOrExchange(S);
}
AudioUnitEffectExportDialog::~AudioUnitEffectExportDialog()
{
}
void AudioUnitEffectExportDialog::PopulateOrExchange(ShuttleGui & S)
{
S.SetBorder(5);
S.StartHorizontalLay(wxEXPAND, 1);
{
S.StartVerticalLay(true);
{
S.StartStatic(_("Presets (may select multiple)"));
{
S.SetStyle(wxLC_REPORT | wxLC_HRULES | wxLC_VRULES |
wxLC_NO_SORT_HEADER);
mList = S.AddListControlReportMode();
mList->InsertColumn(0, _("Preset"), wxLIST_FORMAT_LEFT);
}
S.EndStatic();
}
S.EndVerticalLay();
}
S.EndHorizontalLay();
S.AddStandardButtons();
wxArrayString presets;
mEffect->mHost->GetPrivateConfigSubgroups(mEffect->mHost->GetUserPresetsGroup(wxEmptyString), presets);
presets.Sort();
for (size_t i = 0, cnt = presets.GetCount(); i < cnt; i++)
{
mList->InsertItem(i, presets[i]);
}
mList->SetColumnWidth(0, wxLIST_AUTOSIZE);
// Set the list size...with a little extra for good measure
wxSize sz = mList->GetBestSize();
sz.x += 5;
sz.y += 5;
mList->SetMinSize(sz);
Layout();
Fit();
Center();
}
void AudioUnitEffectExportDialog::OnOk(wxCommandEvent & WXUNUSED(evt))
{
// Save active settings
wxString settingsName(wxT("Export Save"));
mEffect->SaveParameters(settingsName);
// Look for selected presets
long sel = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
while (sel >= 0)
{
wxString name = mList->GetItemText(sel);
// Make the preset current
mEffect->LoadParameters(mEffect->mHost->GetUserPresetsGroup(name));
// Make sure the user preset directory exists
wxString path;
path.Printf(wxT("%s/%s/%s/%s.aupreset"),
PRESET_USER_PATH,
mEffect->mVendor.c_str(),
mEffect->mName.c_str(),
name.c_str());
wxFileName fn(path);
fn.Normalize();
fn.Mkdir(0755, wxPATH_MKDIR_FULL);
path = fn.GetFullPath();
// First set the name of the preset
wxCFStringRef cfname(name);
AUPreset preset;
preset.presetNumber = -1; // indicates user preset
preset.presetName = cfname;
AudioUnitSetProperty(mEffect->mUnit,
kAudioUnitProperty_PresentPreset,
kAudioUnitScope_Global,
0,
&preset,
sizeof(preset));
// Now retrieve the preset content
CFPropertyListRef content;
UInt32 size = sizeof(content);
AudioUnitGetProperty(mEffect->mUnit,
kAudioUnitProperty_ClassInfo,
kAudioUnitScope_Global,
0,
&content,
&size);
// And convert it to XML
CFDataRef xml = CFPropertyListCreateXMLData(kCFAllocatorDefault,
content);
if (xml)
{
// Create the CFURL for the path
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
wxCFStringRef(path),
kCFURLPOSIXPathStyle,
false);
if (url)
{
SInt32 error;
Boolean res = CFURLWriteDataAndPropertiesToResource(url,
xml,
NULL,
&error);
CFRelease(url);
}
// Get rid of the XML data
CFRelease(xml);
}
// And continue to the next selected preset
sel = mList->GetNextItem(sel, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
}
// Restore active settings
mEffect->LoadParameters(settingsName);
mEffect->mHost->RemovePrivateConfigSubgroup(settingsName);
EndModal(wxID_OK);
}
///////////////////////////////////////////////////////////////////////////////
//
// AudioUnitEffectImportDialog
//
///////////////////////////////////////////////////////////////////////////////
class AudioUnitEffectImportDialog final : public wxDialogWrapper
{
public:
AudioUnitEffectImportDialog(wxWindow * parent, AudioUnitEffect *effect);
virtual ~AudioUnitEffectImportDialog();
void PopulateOrExchange(ShuttleGui & S);
void OnOk(wxCommandEvent & evt);
private:
wxWindow *mParent;
AudioUnitEffect *mEffect;
wxListCtrl *mList;
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(AudioUnitEffectImportDialog, wxDialogWrapper)
EVT_BUTTON(wxID_OK, AudioUnitEffectImportDialog::OnOk)
END_EVENT_TABLE()
AudioUnitEffectImportDialog::AudioUnitEffectImportDialog(wxWindow * parent, AudioUnitEffect *effect)
: wxDialogWrapper(parent, wxID_ANY, wxString(_("Import Audio Unit Presets")))
{
mEffect = effect;
ShuttleGui S(this, eIsCreating);
PopulateOrExchange(S);
}
AudioUnitEffectImportDialog::~AudioUnitEffectImportDialog()
{
}
void AudioUnitEffectImportDialog::PopulateOrExchange(ShuttleGui & S)
{
S.SetBorder(5);
S.StartHorizontalLay(wxEXPAND, 1);
{
S.StartVerticalLay(true);
{
S.StartStatic(_("Presets (may select multiple)"));
{
S.SetStyle(wxLC_REPORT | wxLC_HRULES | wxLC_VRULES |
wxLC_NO_SORT_HEADER);
mList = S.AddListControlReportMode();
mList->InsertColumn(0, _("Preset"), wxLIST_FORMAT_LEFT);
mList->InsertColumn(1, _("Location"), wxLIST_FORMAT_LEFT);
}
S.EndStatic();
}
S.EndVerticalLay();
}
S.EndHorizontalLay();
S.AddStandardButtons();
wxArrayString presets;
// Make sure the user preset directory exists
wxString path;
path.Printf(wxT("%s/%s/%s"),
PRESET_LOCAL_PATH,
mEffect->mVendor.c_str(),
mEffect->mName.c_str());
wxFileName fn(path);
fn.Normalize();
// Get all presets in the local domain for this effect
wxDir::GetAllFiles(fn.GetFullPath(), &presets, wxT("*.aupreset"));
fn.PrependDir(wxT("~"));
fn.Normalize();
// Get all presets in the user domain for this effect
wxDir::GetAllFiles(fn.GetFullPath(), &presets, wxT("*.aupreset"));
presets.Sort();
for (size_t i = 0, cnt = presets.GetCount(); i < cnt; i++)
{
fn = presets[i];
mList->InsertItem(i, fn.GetName());
mList->SetItem(i, 1, fn.GetPath());
}
mList->SetColumnWidth(0, wxLIST_AUTOSIZE);
mList->SetColumnWidth(1, wxLIST_AUTOSIZE);
// Set the list size...with a little extra for good measure
wxSize sz = mList->GetBestSize();
sz.x += 5;
sz.y += 5;
mList->SetMinSize(sz);
Layout();
Fit();
Center();
}
void AudioUnitEffectImportDialog::OnOk(wxCommandEvent & WXUNUSED(evt))
{
// Save active settings
wxString settingsName(wxT("Import Save"));
mEffect->SaveParameters(settingsName);
// Look for selected presets
long sel = -1;
while ((sel = mList->GetNextItem(sel, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) >= 0)
{
wxListItem item;
item.SetId(sel);
item.SetColumn(1);
item.SetMask(wxLIST_MASK_TEXT);
mList->GetItem(item);
wxString path;
path.Printf(wxT("%s/%s.aupreset"),
item.GetText().c_str(),
mList->GetItemText(sel).c_str());
// Create the CFURL for the path
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
wxCFStringRef(path),
kCFURLPOSIXPathStyle,
false);
if (!url)
{
continue;
}
CFDataRef xml;
SInt32 error;
Boolean res = CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
url,
&xml,
NULL,
NULL,
&error);
CFRelease(url);
if (!res)
{
continue;
}
CFPropertyListRef content;
content = CFPropertyListCreateFromXMLData(kCFAllocatorDefault,
xml,
kCFPropertyListImmutable,
NULL);
CFRelease(xml);
if (!content)
{
continue;
}
OSStatus result = AudioUnitSetProperty(mEffect->mUnit,
kAudioUnitProperty_ClassInfo,
kAudioUnitScope_Global,
0,
&content,
sizeof(content));
CFRelease(content);
mEffect->SaveUserPreset(mEffect->mHost->GetUserPresetsGroup(mList->GetItemText(sel)));
}
// Restore active settings
mEffect->LoadParameters(settingsName);
mEffect->mHost->RemovePrivateConfigSubgroup(settingsName);
EndModal(wxID_OK);
}
///////////////////////////////////////////////////////////////////////////////
//
// AudioUnitEffect
//
///////////////////////////////////////////////////////////////////////////////
AudioUnitEffect::AudioUnitEffect(const wxString & path,
const wxString & name,
AudioComponent 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;
mBlockSize = 0.0;
mInteractive = false;
mIsGraphical = false;
mUIHost = NULL;
mDialog = NULL;
mParent = NULL;
mInputList = NULL;
mOutputList = NULL;
mUnitInitialized = false;
mEventListenerRef = NULL;
}
AudioUnitEffect::~AudioUnitEffect()
{
if (mUnitInitialized)
{
AudioUnitUninitialize(mUnit);
}
if (mEventListenerRef)
{
AUListenerDispose(mEventListenerRef);
}
if (mUnit)
{
AudioComponentInstanceDispose(mUnit);
}
}
// ============================================================================
// IdentInterface implementation
// ============================================================================
wxString AudioUnitEffect::GetPath()
{
return mPath;
}
wxString AudioUnitEffect::GetSymbol()
{
return mName;
}
wxString AudioUnitEffect::GetName()
{
return GetSymbol();
}
wxString AudioUnitEffect::GetVendor()
{
return mVendor;
}
wxString AudioUnitEffect::GetVersion()
{
UInt32 version;
OSStatus result = AudioComponentGetVersion(mComponent, &version);
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;
result = AudioComponentInstanceNew(mComponent, &mUnit);
if (!mUnit)
{
return false;
}
GetChannelCounts();
SetRateAndChannels();
// Retrieve the desired number of frames per slice
UInt32 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("Options"), wxT("UseLatency"), mUseLatency, true);
mHost->GetSharedConfig(wxT("Options"), wxT("UIType"), mUIType, wxT("Full"));
mUIType = wxGetTranslation(mUIType);
bool haveDefaults;
mHost->GetPrivateConfig(mHost->GetFactoryDefaultsGroup(), wxT("Initialized"), haveDefaults, false);
if (!haveDefaults)
{
SaveParameters(mHost->GetFactoryDefaultsGroup());
mHost->SetPrivateConfig(mHost->GetFactoryDefaultsGroup(), wxT("Initialized"), true);
}
LoadParameters(mHost->GetCurrentSettingsGroup());
}
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.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;
}
AudioUnitCocoaViewInfo cocoaViewInfo;
dataSize = sizeof(AudioUnitCocoaViewInfo);
// Check for a Cocoa UI
result = AudioUnitGetProperty(mUnit,
kAudioUnitProperty_CocoaUI,
kAudioUnitScope_Global,
0,
&cocoaViewInfo,
&dataSize);
bool hasCocoa = result == noErr;
// Check for a Carbon UI
AudioComponentDescription compDesc;
dataSize = sizeof(compDesc);
result = AudioUnitGetProperty(mUnit,
kAudioUnitProperty_GetUIComponentList,
kAudioUnitScope_Global,
0,
&compDesc,
&dataSize);
bool hasCarbon = result == noErr;
mInteractive = (cnt > 0) || hasCocoa || hasCarbon;
}
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::SetBlockSize(sampleCount maxBlockSize)
{
return mBlockSize;
}
sampleCount AudioUnitEffect::GetLatency()
{
// Retrieve the latency (can be updated via an event)
if (mUseLatency && !mLatencyDone)
{
mLatencyDone = true;
Float64 latency = 0.0;
UInt32 dataSize = sizeof(latency);
AudioUnitGetProperty(mUnit,
kAudioUnitProperty_Latency,
kAudioUnitScope_Global,
0,
&latency,
&dataSize);
return (sampleCount) (latency * mSampleRate);
}
return 0;
}
sampleCount AudioUnitEffect::GetTailSize()
{
// Retrieve the tail time
Float64 tailTime = 0.0;
UInt32 dataSize = sizeof(tailTime);
AudioUnitGetProperty(mUnit,
kAudioUnitProperty_TailTime,
kAudioUnitScope_Global,
0,
&tailTime,
&dataSize);
return (sampleCount) (tailTime * mSampleRate);
}
bool AudioUnitEffect::IsReady()
{
return mReady;
}
bool AudioUnitEffect::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames WXUNUSED(chanMap))
{
OSStatus result;
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;
result = AudioUnitSetProperty(mUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&callbackStruct,
sizeof(AURenderCallbackStruct));
if (result != noErr)
{
printf("Setting input render callback failed.\n");
return false;
}
result = AudioUnitReset(mUnit, kAudioUnitScope_Global, 0);
if (result != noErr)
{
return false;
}
mLatencyDone = false;
mReady = true;
return true;
}
bool AudioUnitEffect::ProcessFinalize()
{
mReady = false;
if (mOutputList)
{
delete [] mOutputList;
mOutputList = NULL;
}
if (mInputList)
{
delete [] mInputList;
mInputList = NULL;
}
return true;
}
sampleCount AudioUnitEffect::ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen)
{
for (int i = 0; i < mAudioIns; i++)
{
mInputList->mBuffers[i].mNumberChannels = 1;
mInputList->mBuffers[i].mData = inBlock[i];
mInputList->mBuffers[i].mDataByteSize = sizeof(float) * blockLen;
}
for (int i = 0; i < mAudioOuts; i++)
{
mOutputList->mBuffers[i].mNumberChannels = 1;
mOutputList->mBuffers[i].mData = outBlock[i];
mOutputList->mBuffers[i].mDataByteSize = sizeof(float) * blockLen;
}
AudioUnitRenderActionFlags flags = 0;
OSStatus result;
result = AudioUnitRender(mUnit,
&flags,
&mTimeStamp,
0,
blockLen,
mOutputList);
if (result != noErr)
{
printf("Render failed: %d %4.4s\n", (int)result, (char *)&result);
return 0;
}
mTimeStamp.mSampleTime += blockLen;
return blockLen;
}
bool AudioUnitEffect::RealtimeInitialize()
{
mMasterIn = new float *[mAudioIns];
for (int i = 0; i < mAudioIns; i++)
{
mMasterIn[i] = new float[mBlockSize];
memset(mMasterIn[i], 0, mBlockSize * sizeof(float));
}
mMasterOut = new float *[mAudioOuts];
for (int i = 0; i < mAudioOuts; i++)
{
mMasterOut[i] = new float[mBlockSize];
}
return ProcessInitialize(0);
}
bool AudioUnitEffect::RealtimeAddProcessor(int numChannels, float sampleRate)
{
AudioUnitEffect *slave = new AudioUnitEffect(mPath, mName, mComponent, this);
if (!slave->SetHost(NULL))
{
delete slave;
return false;
}
slave->SetBlockSize(mBlockSize);
slave->SetChannelCount(numChannels);
slave->SetSampleRate(sampleRate);
if (!CopyParameters(mUnit, slave->mUnit))
{
delete slave;
return false;
}
mSlaves.Add(slave);
return slave->ProcessInitialize(0);
}
bool AudioUnitEffect::RealtimeFinalize()
{
for (size_t i = 0, cnt = mSlaves.GetCount(); i < cnt; i++)
{
mSlaves[i]->ProcessFinalize();
delete mSlaves[i];
}
mSlaves.Clear();
for (int i = 0; i < mAudioIns; i++)
{
delete [] mMasterIn[i];
}
delete [] mMasterIn;
for (int i = 0; i < mAudioOuts; i++)
{
delete [] mMasterOut[i];
}
delete [] mMasterOut;
return ProcessFinalize();
}
bool AudioUnitEffect::RealtimeSuspend()
{
return true;
}
bool AudioUnitEffect::RealtimeResume()
{
OSStatus result;
result = AudioUnitReset(mUnit, kAudioUnitScope_Global, 0);
if (result != noErr)
{
return false;
}
return true;
}
bool AudioUnitEffect::RealtimeProcessStart()
{
for (int i = 0; i < mAudioIns; i++)
{
memset(mMasterIn[i], 0, mBlockSize * sizeof(float));
}
mNumSamples = 0;
return true;
}
sampleCount AudioUnitEffect::RealtimeProcess(int group,
float **inbuf,
float **outbuf,
sampleCount numSamples)
{
wxASSERT(numSamples <= mBlockSize);
for (int c = 0; c < mAudioIns; c++)
{
for (sampleCount s = 0; s < numSamples; s++)
{
mMasterIn[c][s] += inbuf[c][s];
}
}
mNumSamples = wxMax(numSamples, mNumSamples);
return mSlaves[group]->ProcessBlock(inbuf, outbuf, numSamples);
}
bool AudioUnitEffect::RealtimeProcessEnd()
{
ProcessBlock(mMasterIn, mMasterOut, mNumSamples);
return true;
}
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)
{
name = wxCFStringRef::AsString(info.cfNameString);
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)
{
name = wxCFStringRef::AsString(info.cfNameString);
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;
AudioUnitParameter aup;
aup.mAudioUnit = mUnit;
aup.mParameterID = kAUParameterListener_AnyParameter;
aup.mScope = kAudioUnitScope_Global;
aup.mElement = 0;
AUParameterListenerNotify(NULL, NULL, &aup);
return true;
}
bool AudioUnitEffect::LoadUserPreset(const wxString & name)
{
return LoadParameters(name);
}
bool AudioUnitEffect::SaveUserPreset(const wxString & name)
{
return SaveParameters(name);
}
bool 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)
{
return false;
}
if (id < 0 || id >= CFArrayGetCount(array))
{
return false;
}
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 result == noErr;
}
bool AudioUnitEffect::LoadFactoryDefaults()
{
return 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);
presets.Add(wxCFStringRef::AsString(preset->presetName));
}
CFRelease(array);
}
return presets;
}
// ============================================================================
// EffectUIClientInterface Implementation
// ============================================================================
void AudioUnitEffect::SetHostUI(EffectUIHostInterface *host)
{
mUIHost = host;
}
bool AudioUnitEffect::PopulateUI(wxWindow *parent)
{
OSStatus result;
mDialog = static_cast<wxDialog *>(wxGetTopLevelParent(parent));
mParent = parent;
wxPanel *container;
{
auto mainSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
wxASSERT(mParent); // To justify safenew
container = safenew wxPanelWrapper(mParent, wxID_ANY);
mainSizer->Add(container, 1, wxEXPAND);
mParent->SetSizer(mainSizer.release());
}
if (mUIType == wxT("Plain"))
{
if (!CreatePlain(mParent))
{
return false;
}
}
else
{
mControl = new AUControl;
if (!mControl)
{
return false;
}
if (!mControl->Create(container, mComponent, mUnit, mUIType == wxT("Full")))
{
return false;
}
{
auto innerSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
innerSizer->Add(mControl, 1, wxEXPAND);
container->SetSizer(innerSizer.release());
}
mParent->SetMinSize(wxDefaultSize);
}
mParent->PushEventHandler(this);
return true;
}
bool AudioUnitEffect::IsGraphicalUI()
{
return mUIType != wxT("Plain");
}
bool AudioUnitEffect::ValidateUI()
{
#if 0
if (!mParent->Validate())
{
return false;
}
if (GetType() == EffectTypeGenerate)
{
mHost->SetDuration(mDuration->GetValue());
}
#endif
return true;
}
bool AudioUnitEffect::CreatePlain(wxWindow *parent)
{
return false;
}
bool AudioUnitEffect::HideUI()
{
#if 0
if (GetType() == EffectTypeAnalyze || mNumOutputControls > 0)
{
return false;
}
#endif
return true;
}
bool AudioUnitEffect::CloseUI()
{
mParent->RemoveEventHandler(this);
mUIHost = NULL;
mParent = NULL;
mDialog = NULL;
return true;
}
bool AudioUnitEffect::CanExportPresets()
{
return true;
}
void AudioUnitEffect::ExportPresets()
{
AudioUnitEffectExportDialog dlg(mDialog, this);
dlg.ShowModal();
}
void AudioUnitEffect::ImportPresets()
{
AudioUnitEffectImportDialog dlg(mDialog, this);
dlg.ShowModal();
}
bool AudioUnitEffect::HasOptions()
{
return true;
}
void AudioUnitEffect::ShowOptions()
{
AudioUnitEffectOptionsDialog dlg(mParent, mHost);
if (dlg.ShowModal())
{
// Reinitialize configuration settings
mHost->GetSharedConfig(wxT("Options"), wxT("UseLatency"), mUseLatency, true);
mHost->GetSharedConfig(wxT("Options"), wxT("UIType"), mUIType, wxT("Full"));
mUIType = wxGetTranslation(mUIType);
}
}
// ============================================================================
// AudioUnitEffect Implementation
// ============================================================================
bool AudioUnitEffect::LoadParameters(const wxString & group)
{
wxString parms;
if (!mHost->GetPrivateConfig(group, wxT("Parameters"), parms, wxEmptyString))
{
return false;
}
EffectAutomationParameters eap;
if (!eap.SetParameters(parms))
{
return false;
}
return SetAutomationParameters(eap);
}
bool AudioUnitEffect::SaveParameters(const wxString & group)
{
EffectAutomationParameters eap;
if (!GetAutomationParameters(eap))
{
return false;
}
wxString parms;
if (!eap.GetParameters(parms))
{
return false;
}
return mHost->SetPrivateConfig(group, wxT("Parameters"), parms);
}
bool AudioUnitEffect::SetRateAndChannels()
{
OSStatus result;
if (mUnitInitialized)
{
AudioUnitUninitialize(mUnit);
mUnitInitialized = 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);
result = AudioUnitSetProperty(mUnit,
kAudioUnitProperty_SampleRate,
kAudioUnitScope_Global,
0,
&mSampleRate,
sizeof(Float64));
if (result != noErr)
{
printf("%ls Didn't accept sample rate on global\n", GetName().wx_str());
return false;
}
if (mAudioIns > 0)
{
result = AudioUnitSetProperty(mUnit,
kAudioUnitProperty_SampleRate,
kAudioUnitScope_Input,
0,
&mSampleRate,
sizeof(Float64));
if (result != noErr)
{
printf("%ls Didn't accept sample rate on input\n", GetName().wx_str());
return false;
}
result = AudioUnitSetProperty(mUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&streamFormat,
sizeof(AudioStreamBasicDescription));
if (result != noErr)
{
printf("%ls didn't accept stream format on input\n", GetName().wx_str());
return false;
}
}
if (mAudioOuts > 0)
{
result = AudioUnitSetProperty(mUnit,
kAudioUnitProperty_SampleRate,
kAudioUnitScope_Output,
0,
&mSampleRate,
sizeof(Float64));
if (result != noErr)
{
printf("%ls Didn't accept sample rate on output\n", GetName().wx_str());
return false;
}
streamFormat.mChannelsPerFrame = mAudioOuts;
result = AudioUnitSetProperty(mUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0,
&streamFormat,
sizeof(AudioStreamBasicDescription));
if (result != noErr)
{
printf("%ls didn't accept stream format on output\n", GetName().wx_str());
return false;
}
}
result = AudioUnitInitialize(mUnit);
if (result != noErr)
{
printf("Couldn't initialize audio unit\n");
return false;
}
mUnitInitialized = true;
return true;
}
bool AudioUnitEffect::CopyParameters(AudioUnit srcUnit, AudioUnit dstUnit)
{
OSStatus result;
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;
result = AudioUnitGetProperty(srcUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
NULL,
&size);
if (result != 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];
result = AudioUnitGetProperty(srcUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
parameters,
&size);
if (result != 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++)
{
result = AudioUnitGetParameter(srcUnit,
parameters[i],
kAudioUnitScope_Global,
0,
&parameterValue);
if (result != 0)
{
printf("Couldn't get parameter %d: ID=%d\n", i, (int)parameters[i]);
continue;
}
result = AudioUnitSetParameter(dstUnit,
parameters[i],
kAudioUnitScope_Global,
0,
parameterValue,
0);
if (result != 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)
{
// Handle property changes
if (inEvent->mEventType == kAudioUnitEvent_PropertyChange)
{
// We're only registered for Latency changes
if (inEvent->mArgument.mProperty.mPropertyID == kAudioUnitProperty_Latency)
{
// Allow change to be used
//mLatencyDone = false;
}
return;
}
// Only parameter changes at this point
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;
}
#endif