audacia/src/effects/audiounits/AudioUnitEffect.cpp

2349 lines
61 KiB
C++
Raw Normal View History

/**********************************************************************
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;
}
2014-06-03 20:30:19 +00:00
// Safety of this depends on complementary calls to DeleteInstance on the module manager side.
return safenew AudioUnitEffect(path, name, component);
}
2014-06-03 20:30:19 +00:00
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;
2014-06-03 20:30:19 +00:00
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;
2014-06-03 20:30:19 +00:00
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);
}
2014-06-03 20:30:19 +00:00
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;
}
2014-06-03 20:30:19 +00:00
///////////////////////////////////////////////////////////////////////////////
//
// 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;
Resolves reported bugs and (most) recommendations All: Export/Import now disabled if the Effect (family) doesn't support it Options disabled if the Effect (family) doesn't support it Standarized on "Latency compensation" instead of "Buffer delay compensation" Correct loading/saving of factory default settings Fixed "Mannage" to be "Manage" Removed conditional code since we're keeping the transport buttons Play bases state on actual playback status Play no longer monkeys with selection Play will now start from where it left off IF: The user stops the playback via the Effect Stop button If the user wants to restart from the beginning of the selection then the user can click Rewind while stopped. Rewind will not go past start of selection (sorry Steve ;-)) If Rewind is clicked while not playing, playback will next start at beginning of selection. Fast Forward will stop at end of selection However, if FFwd is clicked while not playing, it will put playback at the end of selection and the next time the user clicks the Effect play button, playback will continue from the end of selection to the end of track. Deleting a preset now prompts user to confirm Button faces should now look a little better Bypass (should we rename that???) button now grays when disabled Audio Units: Corrected all user visible "AudioUnit" strings to be "Audio Unit" Removed unneeded "Buffer Size" in settings dialog Only uses latency if user said it was okay Ladspa: Added settings dialog to allow control of latency usage Removed unused "user selectable" buffer size Only use latency if user wants it Refresh controls when presets are loaded VST: Removed unused "Rescan at startup" setting Propogate parameter loads to slave effects
2014-12-04 06:10:27 +00:00
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);
{
Resolves reported bugs and (most) recommendations All: Export/Import now disabled if the Effect (family) doesn't support it Options disabled if the Effect (family) doesn't support it Standarized on "Latency compensation" instead of "Buffer delay compensation" Correct loading/saving of factory default settings Fixed "Mannage" to be "Manage" Removed conditional code since we're keeping the transport buttons Play bases state on actual playback status Play no longer monkeys with selection Play will now start from where it left off IF: The user stops the playback via the Effect Stop button If the user wants to restart from the beginning of the selection then the user can click Rewind while stopped. Rewind will not go past start of selection (sorry Steve ;-)) If Rewind is clicked while not playing, playback will next start at beginning of selection. Fast Forward will stop at end of selection However, if FFwd is clicked while not playing, it will put playback at the end of selection and the next time the user clicks the Effect play button, playback will continue from the end of selection to the end of track. Deleting a preset now prompts user to confirm Button faces should now look a little better Bypass (should we rename that???) button now grays when disabled Audio Units: Corrected all user visible "AudioUnit" strings to be "Audio Unit" Removed unneeded "Buffer Size" in settings dialog Only uses latency if user said it was okay Ladspa: Added settings dialog to allow control of latency usage Removed unused "user selectable" buffer size Only use latency if user wants it Refresh controls when presets are loaded VST: Removed unused "Rescan at startup" setting Propogate parameter loads to slave effects
2014-12-04 06:10:27 +00:00
S.StartStatic(_("Latency Compensation"));
{
S.AddVariableText(wxString() +
Resolves reported bugs and (most) recommendations All: Export/Import now disabled if the Effect (family) doesn't support it Options disabled if the Effect (family) doesn't support it Standarized on "Latency compensation" instead of "Buffer delay compensation" Correct loading/saving of factory default settings Fixed "Mannage" to be "Manage" Removed conditional code since we're keeping the transport buttons Play bases state on actual playback status Play no longer monkeys with selection Play will now start from where it left off IF: The user stops the playback via the Effect Stop button If the user wants to restart from the beginning of the selection then the user can click Rewind while stopped. Rewind will not go past start of selection (sorry Steve ;-)) If Rewind is clicked while not playing, playback will next start at beginning of selection. Fast Forward will stop at end of selection However, if FFwd is clicked while not playing, it will put playback at the end of selection and the next time the user clicks the Effect play button, playback will continue from the end of selection to the end of track. Deleting a preset now prompts user to confirm Button faces should now look a little better Bypass (should we rename that???) button now grays when disabled Audio Units: Corrected all user visible "AudioUnit" strings to be "Audio Unit" Removed unneeded "Buffer Size" in settings dialog Only uses latency if user said it was okay Ladspa: Added settings dialog to allow control of latency usage Removed unused "user selectable" buffer size Only use latency if user wants it Refresh controls when presets are loaded VST: Removed unused "Rescan at startup" setting Propogate parameter loads to slave effects
2014-12-04 06:10:27 +00:00
_("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 ") +
Resolves reported bugs and (most) recommendations All: Export/Import now disabled if the Effect (family) doesn't support it Options disabled if the Effect (family) doesn't support it Standarized on "Latency compensation" instead of "Buffer delay compensation" Correct loading/saving of factory default settings Fixed "Mannage" to be "Manage" Removed conditional code since we're keeping the transport buttons Play bases state on actual playback status Play no longer monkeys with selection Play will now start from where it left off IF: The user stops the playback via the Effect Stop button If the user wants to restart from the beginning of the selection then the user can click Rewind while stopped. Rewind will not go past start of selection (sorry Steve ;-)) If Rewind is clicked while not playing, playback will next start at beginning of selection. Fast Forward will stop at end of selection However, if FFwd is clicked while not playing, it will put playback at the end of selection and the next time the user clicks the Effect play button, playback will continue from the end of selection to the end of track. Deleting a preset now prompts user to confirm Button faces should now look a little better Bypass (should we rename that???) button now grays when disabled Audio Units: Corrected all user visible "AudioUnit" strings to be "Audio Unit" Removed unneeded "Buffer Size" in settings dialog Only uses latency if user said it was okay Ladspa: Added settings dialog to allow control of latency usage Removed unused "user selectable" buffer size Only use latency if user wants it Refresh controls when presets are loaded VST: Removed unused "Rescan at startup" setting Propogate parameter loads to slave effects
2014-12-04 06:10:27 +00:00
_("not work for all Audio Unit effects."))->Wrap(650);
S.StartHorizontalLay(wxALIGN_LEFT);
{
S.TieCheckBox(_("Enable &compensation"),
Resolves reported bugs and (most) recommendations All: Export/Import now disabled if the Effect (family) doesn't support it Options disabled if the Effect (family) doesn't support it Standarized on "Latency compensation" instead of "Buffer delay compensation" Correct loading/saving of factory default settings Fixed "Mannage" to be "Manage" Removed conditional code since we're keeping the transport buttons Play bases state on actual playback status Play no longer monkeys with selection Play will now start from where it left off IF: The user stops the playback via the Effect Stop button If the user wants to restart from the beginning of the selection then the user can click Rewind while stopped. Rewind will not go past start of selection (sorry Steve ;-)) If Rewind is clicked while not playing, playback will next start at beginning of selection. Fast Forward will stop at end of selection However, if FFwd is clicked while not playing, it will put playback at the end of selection and the next time the user clicks the Effect play button, playback will continue from the end of selection to the end of track. Deleting a preset now prompts user to confirm Button faces should now look a little better Bypass (should we rename that???) button now grays when disabled Audio Units: Corrected all user visible "AudioUnit" strings to be "Audio Unit" Removed unneeded "Buffer Size" in settings dialog Only uses latency if user said it was okay Ladspa: Added settings dialog to allow control of latency usage Removed unused "user selectable" buffer size Only use latency if user wants it Refresh controls when presets are loaded VST: Removed unused "Rescan at startup" setting Propogate parameter loads to slave effects
2014-12-04 06:10:27 +00:00
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))
{
2015-05-26 14:32:31 +00:00
// 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);
2015-05-26 14:32:31 +00:00
// 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);
}
2015-05-26 14:32:31 +00:00
// 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))
{
2015-05-26 14:32:31 +00:00
// 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,
2015-05-26 14:32:31 +00:00
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)));
}
2015-05-26 14:32:31 +00:00
// 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;
2015-05-22 17:28:44 +00:00
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;
}
2014-06-03 20:30:19 +00:00
if (mAudioIns == 0)
{
return EffectTypeGenerate;
}
if (mAudioOuts == 0)
{
return EffectTypeAnalyze;
}
2014-06-03 20:30:19 +00:00
return EffectTypeProcess;
}
2014-06-03 20:30:19 +00:00
wxString AudioUnitEffect::GetFamily()
{
return AUDIOUNITEFFECTS_FAMILY;
}
bool AudioUnitEffect::IsInteractive()
{
return mInteractive;
}
2014-06-03 20:30:19 +00:00
bool AudioUnitEffect::IsDefault()
{
return false;
}
2014-06-03 20:30:19 +00:00
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;
}
2014-06-03 20:30:19 +00:00
UInt32 cnt = dataSize / sizeof(AudioUnitParameterID);
AudioUnitParameterID *array = new AudioUnitParameterID[cnt];
2014-06-03 20:30:19 +00:00
result = AudioUnitGetProperty(mUnit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
array,
&dataSize);
if (result != noErr)
{
delete [] array;
return false;
}
2014-06-03 20:30:19 +00:00
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;
}
2014-06-03 20:30:19 +00:00
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;
}
2014-06-03 20:30:19 +00:00
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;
Resolves reported bugs and (most) recommendations All: Export/Import now disabled if the Effect (family) doesn't support it Options disabled if the Effect (family) doesn't support it Standarized on "Latency compensation" instead of "Buffer delay compensation" Correct loading/saving of factory default settings Fixed "Mannage" to be "Manage" Removed conditional code since we're keeping the transport buttons Play bases state on actual playback status Play no longer monkeys with selection Play will now start from where it left off IF: The user stops the playback via the Effect Stop button If the user wants to restart from the beginning of the selection then the user can click Rewind while stopped. Rewind will not go past start of selection (sorry Steve ;-)) If Rewind is clicked while not playing, playback will next start at beginning of selection. Fast Forward will stop at end of selection However, if FFwd is clicked while not playing, it will put playback at the end of selection and the next time the user clicks the Effect play button, playback will continue from the end of selection to the end of track. Deleting a preset now prompts user to confirm Button faces should now look a little better Bypass (should we rename that???) button now grays when disabled Audio Units: Corrected all user visible "AudioUnit" strings to be "Audio Unit" Removed unneeded "Buffer Size" in settings dialog Only uses latency if user said it was okay Ladspa: Added settings dialog to allow control of latency usage Removed unused "user selectable" buffer size Only use latency if user wants it Refresh controls when presets are loaded VST: Removed unused "Rescan at startup" setting Propogate parameter loads to slave effects
2014-12-04 06:10:27 +00:00
mHost->GetPrivateConfig(mHost->GetFactoryDefaultsGroup(), wxT("Initialized"), haveDefaults, false);
if (!haveDefaults)
{
Resolves reported bugs and (most) recommendations All: Export/Import now disabled if the Effect (family) doesn't support it Options disabled if the Effect (family) doesn't support it Standarized on "Latency compensation" instead of "Buffer delay compensation" Correct loading/saving of factory default settings Fixed "Mannage" to be "Manage" Removed conditional code since we're keeping the transport buttons Play bases state on actual playback status Play no longer monkeys with selection Play will now start from where it left off IF: The user stops the playback via the Effect Stop button If the user wants to restart from the beginning of the selection then the user can click Rewind while stopped. Rewind will not go past start of selection (sorry Steve ;-)) If Rewind is clicked while not playing, playback will next start at beginning of selection. Fast Forward will stop at end of selection However, if FFwd is clicked while not playing, it will put playback at the end of selection and the next time the user clicks the Effect play button, playback will continue from the end of selection to the end of track. Deleting a preset now prompts user to confirm Button faces should now look a little better Bypass (should we rename that???) button now grays when disabled Audio Units: Corrected all user visible "AudioUnit" strings to be "Audio Unit" Removed unneeded "Buffer Size" in settings dialog Only uses latency if user said it was okay Ladspa: Added settings dialog to allow control of latency usage Removed unused "user selectable" buffer size Only use latency if user wants it Refresh controls when presets are loaded VST: Removed unused "Rescan at startup" setting Propogate parameter loads to slave effects
2014-12-04 06:10:27 +00:00
SaveParameters(mHost->GetFactoryDefaultsGroup());
mHost->SetPrivateConfig(mHost->GetFactoryDefaultsGroup(), wxT("Initialized"), true);
}
2014-06-03 20:30:19 +00:00
Resolves reported bugs and (most) recommendations All: Export/Import now disabled if the Effect (family) doesn't support it Options disabled if the Effect (family) doesn't support it Standarized on "Latency compensation" instead of "Buffer delay compensation" Correct loading/saving of factory default settings Fixed "Mannage" to be "Manage" Removed conditional code since we're keeping the transport buttons Play bases state on actual playback status Play no longer monkeys with selection Play will now start from where it left off IF: The user stops the playback via the Effect Stop button If the user wants to restart from the beginning of the selection then the user can click Rewind while stopped. Rewind will not go past start of selection (sorry Steve ;-)) If Rewind is clicked while not playing, playback will next start at beginning of selection. Fast Forward will stop at end of selection However, if FFwd is clicked while not playing, it will put playback at the end of selection and the next time the user clicks the Effect play button, playback will continue from the end of selection to the end of track. Deleting a preset now prompts user to confirm Button faces should now look a little better Bypass (should we rename that???) button now grays when disabled Audio Units: Corrected all user visible "AudioUnit" strings to be "Audio Unit" Removed unneeded "Buffer Size" in settings dialog Only uses latency if user said it was okay Ladspa: Added settings dialog to allow control of latency usage Removed unused "user selectable" buffer size Only use latency if user wants it Refresh controls when presets are loaded VST: Removed unused "Rescan at startup" setting Propogate parameter loads to slave effects
2014-12-04 06:10:27 +00:00
LoadParameters(mHost->GetCurrentSettingsGroup());
}
2014-06-03 20:30:19 +00:00
if (!mMaster)
{
result = AUEventListenerCreate(AudioUnitEffect::EventListenerCallback,
this,
(CFRunLoopRef)GetCFRunLoopFromEventLoop(GetCurrentEventLoop()),
kCFRunLoopDefaultMode,
0.0,
0.0,
&mEventListenerRef);
if (result != noErr)
{
return false;
}
2014-06-03 20:30:19 +00:00
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;
}
2014-06-03 20:30:19 +00:00
// 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;
}
}
2014-06-03 20:30:19 +00:00
delete [] array;
2014-06-03 20:30:19 +00:00
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;
2014-06-03 20:30:19 +00:00
result = AUEventListenerAddEventType(mEventListenerRef,
this,
&event);
if (result != noErr)
{
return false;
}
2015-05-22 17:28:44 +00:00
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;
2015-05-22 17:28:44 +00:00
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)
Resolves reported bugs and (most) recommendations All: Export/Import now disabled if the Effect (family) doesn't support it Options disabled if the Effect (family) doesn't support it Standarized on "Latency compensation" instead of "Buffer delay compensation" Correct loading/saving of factory default settings Fixed "Mannage" to be "Manage" Removed conditional code since we're keeping the transport buttons Play bases state on actual playback status Play no longer monkeys with selection Play will now start from where it left off IF: The user stops the playback via the Effect Stop button If the user wants to restart from the beginning of the selection then the user can click Rewind while stopped. Rewind will not go past start of selection (sorry Steve ;-)) If Rewind is clicked while not playing, playback will next start at beginning of selection. Fast Forward will stop at end of selection However, if FFwd is clicked while not playing, it will put playback at the end of selection and the next time the user clicks the Effect play button, playback will continue from the end of selection to the end of track. Deleting a preset now prompts user to confirm Button faces should now look a little better Bypass (should we rename that???) button now grays when disabled Audio Units: Corrected all user visible "AudioUnit" strings to be "Audio Unit" Removed unneeded "Buffer Size" in settings dialog Only uses latency if user said it was okay Ladspa: Added settings dialog to allow control of latency usage Removed unused "user selectable" buffer size Only use latency if user wants it Refresh controls when presets are loaded VST: Removed unused "Rescan at startup" setting Propogate parameter loads to slave effects
2014-12-04 06:10:27 +00:00
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;
2014-12-07 01:43:03 +00:00
mOutputList = NULL;
}
if (mInputList)
{
delete [] mInputList;
mInputList = NULL;
}
2014-06-03 20:30:19 +00:00
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;
2014-06-03 20:30:19 +00:00
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()
Resolves reported bugs and (most) recommendations All: Export/Import now disabled if the Effect (family) doesn't support it Options disabled if the Effect (family) doesn't support it Standarized on "Latency compensation" instead of "Buffer delay compensation" Correct loading/saving of factory default settings Fixed "Mannage" to be "Manage" Removed conditional code since we're keeping the transport buttons Play bases state on actual playback status Play no longer monkeys with selection Play will now start from where it left off IF: The user stops the playback via the Effect Stop button If the user wants to restart from the beginning of the selection then the user can click Rewind while stopped. Rewind will not go past start of selection (sorry Steve ;-)) If Rewind is clicked while not playing, playback will next start at beginning of selection. Fast Forward will stop at end of selection However, if FFwd is clicked while not playing, it will put playback at the end of selection and the next time the user clicks the Effect play button, playback will continue from the end of selection to the end of track. Deleting a preset now prompts user to confirm Button faces should now look a little better Bypass (should we rename that???) button now grays when disabled Audio Units: Corrected all user visible "AudioUnit" strings to be "Audio Unit" Removed unneeded "Buffer Size" in settings dialog Only uses latency if user said it was okay Ladspa: Added settings dialog to allow control of latency usage Removed unused "user selectable" buffer size Only use latency if user wants it Refresh controls when presets are loaded VST: Removed unused "Rescan at startup" setting Propogate parameter loads to slave effects
2014-12-04 06:10:27 +00:00
{
return true;
}
void AudioUnitEffect::ExportPresets()
{
AudioUnitEffectExportDialog dlg(mDialog, this);
dlg.ShowModal();
}
void AudioUnitEffect::ImportPresets()
{
AudioUnitEffectImportDialog dlg(mDialog, this);
dlg.ShowModal();
}
Resolves reported bugs and (most) recommendations All: Export/Import now disabled if the Effect (family) doesn't support it Options disabled if the Effect (family) doesn't support it Standarized on "Latency compensation" instead of "Buffer delay compensation" Correct loading/saving of factory default settings Fixed "Mannage" to be "Manage" Removed conditional code since we're keeping the transport buttons Play bases state on actual playback status Play no longer monkeys with selection Play will now start from where it left off IF: The user stops the playback via the Effect Stop button If the user wants to restart from the beginning of the selection then the user can click Rewind while stopped. Rewind will not go past start of selection (sorry Steve ;-)) If Rewind is clicked while not playing, playback will next start at beginning of selection. Fast Forward will stop at end of selection However, if FFwd is clicked while not playing, it will put playback at the end of selection and the next time the user clicks the Effect play button, playback will continue from the end of selection to the end of track. Deleting a preset now prompts user to confirm Button faces should now look a little better Bypass (should we rename that???) button now grays when disabled Audio Units: Corrected all user visible "AudioUnit" strings to be "Audio Unit" Removed unneeded "Buffer Size" in settings dialog Only uses latency if user said it was okay Ladspa: Added settings dialog to allow control of latency usage Removed unused "user selectable" buffer size Only use latency if user wants it Refresh controls when presets are loaded VST: Removed unused "Rescan at startup" setting Propogate parameter loads to slave effects
2014-12-04 06:10:27 +00:00
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