1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-04 22:29:27 +02:00

Adds the ability to sort and group the Effects menus

Current options:

Publisher: name (the closest to what we have now)
Name (doesn't include the publisher, just a straigt up alpha sort
Publisher (creates submenus based on the publisher)
Type (creates submenus based on the type, VST, Nyquist, etc.)

And the Linux method of creating submenus based on number of items
is available to all and you can choose how menu you want per submenu.
I had to bring this back since I'd removed it when for the new effects
and I figured why limit it to only Linux...

Check it out in Preferences (effect page).

You'll also notice that the effects page is starting to talk about 
plugins.  That will progress further (baby steps) so bare with me
for just a bit more.
This commit is contained in:
lllucius 2014-11-04 01:38:13 +00:00
parent 0358e3eb09
commit 080b67ce9e
6 changed files with 359 additions and 182 deletions

View File

@ -213,9 +213,76 @@ void AudacityProjectCommandFunctor::operator()(int index, const wxEvent * evt)
#define FNI(X, I) new AudacityProjectCommandFunctor(this, &AudacityProject:: X, I) #define FNI(X, I) new AudacityProjectCommandFunctor(this, &AudacityProject:: X, I)
#define FNS(X, S) new AudacityProjectCommandFunctor(this, &AudacityProject:: X, S) #define FNS(X, S) new AudacityProjectCommandFunctor(this, &AudacityProject:: X, S)
static bool SortPlugs(const PluginDescriptor *a, const PluginDescriptor *b) //
// Effects menu arrays
//
WX_DEFINE_ARRAY_PTR(const PluginDescriptor *, EffectPlugs);
static int SortPlugsByDefault(const PluginDescriptor **a, const PluginDescriptor **b)
{ {
return a->GetMenuName() < b->GetMenuName(); wxString akey = (*a)->GetVendor();
wxString bkey = (*b)->GetVendor();
if ((*a)->IsEffectDefault())
{
akey = wxEmptyString;
}
if ((*b)->IsEffectDefault())
{
bkey = wxEmptyString;
}
akey += (*a)->GetName();
bkey += (*b)->GetName();
return akey.CmpNoCase(bkey);
}
static int SortPlugsByName(const PluginDescriptor **a, const PluginDescriptor **b)
{
wxString akey = (*a)->GetName();
wxString bkey = (*b)->GetName();
return akey.CmpNoCase(bkey);
}
static int SortPlugsByPublisher(const PluginDescriptor **a, const PluginDescriptor **b)
{
wxString akey = (*a)->GetVendor();
wxString bkey = (*b)->GetVendor();
if (akey.IsEmpty())
{
akey = _("Uncategorized");
}
if (bkey.IsEmpty())
{
bkey = _("Uncategorized");
}
akey += (*a)->GetName();
bkey += (*b)->GetName();
return akey.CmpNoCase(bkey);
}
static int SortPlugsByFamily(const PluginDescriptor **a, const PluginDescriptor **b)
{
wxString akey = (*a)->GetEffectFamily();
wxString bkey = (*b)->GetEffectFamily();
if (akey.IsEmpty())
{
akey = _("Uncategorized");
}
if (bkey.IsEmpty())
{
bkey = _("Uncategorized");
}
akey += (*a)->GetName();
bkey += (*b)->GetName();
return akey.CmpNoCase(bkey);
} }
/// CreateMenusAndCommands builds the menus, and also rebuilds them after /// CreateMenusAndCommands builds the menus, and also rebuilds them after
@ -866,8 +933,6 @@ void AudacityProject::CreateMenusAndCommands()
wxArrayString defaults; wxArrayString defaults;
PluginManager & pm = PluginManager::Get(); PluginManager & pm = PluginManager::Get();
const PluginDescriptor *plug; const PluginDescriptor *plug;
bool needsep;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Generate Menu // Generate Menu
@ -876,49 +941,11 @@ void AudacityProject::CreateMenusAndCommands()
c->BeginMenu(_("&Generate")); c->BeginMenu(_("&Generate"));
c->SetDefaultFlags(AudioIONotBusyFlag, AudioIONotBusyFlag); c->SetDefaultFlags(AudioIONotBusyFlag, AudioIONotBusyFlag);
typedef std::set<const PluginDescriptor *, bool (*)(const PluginDescriptor *, const PluginDescriptor *)> SortedPlugs;
SortedPlugs defaultplugs(SortPlugs);
SortedPlugs extraplugs(SortPlugs);
#ifndef EFFECT_CATEGORIES #ifndef EFFECT_CATEGORIES
plug = pm.GetFirstPluginForEffectType(EffectTypeGenerate); PopulateEffectsMenu(c,
while (plug) { EffectTypeGenerate,
if (plug->IsEffectDefault()) { AudioIONotBusyFlag,
defaultplugs.insert(plug); AudioIONotBusyFlag);
}
else {
extraplugs.insert(plug);
}
plug = pm.GetNextPluginForEffectType(EffectTypeGenerate);
}
for (SortedPlugs::iterator iter = defaultplugs.begin(); iter != defaultplugs.end(); iter++)
{
const PluginDescriptor *plug = *iter;
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
int flags = plug->IsEffectRealtimeCapable() ?
AudioIONotBusyFlag :
TracksExistFlag;
#else
int flags = TracksExistFlag;
#endif
c->AddItem(plug->GetName(),
plug->GetMenuName(),
FNS(OnEffect, plug->GetID()));
}
needsep = true;
for (SortedPlugs::iterator iter = extraplugs.begin(); iter != extraplugs.end(); iter++)
{
const PluginDescriptor *plug = *iter;
if (needsep) {
c->AddSeparator();
needsep = false;
}
c->AddItem(plug->GetName(),
plug->GetMenuName(),
FNS(OnEffect, plug->GetID()));
}
#else #else
int flags; int flags;
@ -981,57 +1008,10 @@ void AudacityProject::CreateMenusAndCommands()
// effects at all in the menu when EFFECT_CATEGORIES is undefined // effects at all in the menu when EFFECT_CATEGORIES is undefined
#ifndef EFFECT_CATEGORIES #ifndef EFFECT_CATEGORIES
defaultplugs.clear(); PopulateEffectsMenu(c,
extraplugs.clear(); EffectTypeProcess,
plug = pm.GetFirstPluginForEffectType(EffectTypeProcess); AudioIONotBusyFlag | TimeSelectedFlag | WaveTracksSelectedFlag,
while (plug) { TracksExistFlag);
if (plug->IsEffectDefault()) {
defaultplugs.insert(plug);
}
else {
extraplugs.insert(plug);
}
plug = pm.GetNextPluginForEffectType(EffectTypeProcess);
}
for (SortedPlugs::iterator iter = defaultplugs.begin(); iter != defaultplugs.end(); iter++)
{
const PluginDescriptor *plug = *iter;
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
int flags = plug->IsEffectRealtimeCapable() ?
TracksExistFlag :
AudioIONotBusyFlag | TimeSelectedFlag | WaveTracksSelectedFlag;
#else
int flags = AudioIONotBusyFlag | TimeSelectedFlag | WaveTracksSelectedFlag;
#endif
c->AddItem(plug->GetName(),
plug->GetMenuName(),
FNS(OnEffect, plug->GetID()),
flags,
flags);
}
needsep = true;
for (SortedPlugs::iterator iter = extraplugs.begin(); iter != extraplugs.end(); iter++)
{
const PluginDescriptor *plug = *iter;
if (needsep) {
c->AddSeparator();
needsep = false;
}
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
int flags = plug->IsEffectRealtimeCapable() ?
TracksExistFlag :
AudioIONotBusyFlag | TimeSelectedFlag | WaveTracksSelectedFlag;
#else
int flags = AudioIONotBusyFlag | TimeSelectedFlag | WaveTracksSelectedFlag;
#endif
c->AddItem(plug->GetName(),
plug->GetMenuName(),
FNS(OnEffect, plug->GetID()),
flags,
flags);
}
#else #else
int flags = PROCESS_EFFECT | BUILTIN_EFFECT | PLUGIN_EFFECT | ADVANCED_EFFECT; int flags = PROCESS_EFFECT | BUILTIN_EFFECT | PLUGIN_EFFECT | ADVANCED_EFFECT;
// The categories form a DAG, so we start at the roots (the categories // The categories form a DAG, so we start at the roots (the categories
@ -1075,57 +1055,10 @@ void AudacityProject::CreateMenusAndCommands()
AudioIONotBusyFlag | WaveTracksSelectedFlag | TimeSelectedFlag); AudioIONotBusyFlag | WaveTracksSelectedFlag | TimeSelectedFlag);
#ifndef EFFECT_CATEGORIES #ifndef EFFECT_CATEGORIES
defaultplugs.clear(); PopulateEffectsMenu(c,
extraplugs.clear(); EffectTypeAnalyze,
plug = pm.GetFirstPluginForEffectType(EffectTypeAnalyze); AudioIONotBusyFlag | TimeSelectedFlag | WaveTracksSelectedFlag,
while (plug) { TracksExistFlag);
if (plug->IsEffectDefault()) {
defaultplugs.insert(plug);
}
else {
extraplugs.insert(plug);
}
plug = pm.GetNextPluginForEffectType(EffectTypeAnalyze);
}
for (SortedPlugs::iterator iter = defaultplugs.begin(); iter != defaultplugs.end(); iter++)
{
const PluginDescriptor *plug = *iter;
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
int flags = plug->IsEffectRealtimeCapable() ?
TracksExistFlag :
AudioIONotBusyFlag | TimeSelectedFlag | WaveTracksSelectedFlag;
#else
int flags = AudioIONotBusyFlag | TimeSelectedFlag | WaveTracksSelectedFlag;
#endif
c->AddItem(plug->GetName(),
plug->GetMenuName(),
FNS(OnEffect, plug->GetID()),
flags,
flags);
}
needsep = true;
for (SortedPlugs::iterator iter = extraplugs.begin(); iter != extraplugs.end(); iter++)
{
const PluginDescriptor *plug = *iter;
if (needsep) {
c->AddSeparator();
needsep = false;
}
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
int flags = plug->IsEffectRealtimeCapable() ?
TracksExistFlag :
AudioIONotBusyFlag | TimeSelectedFlag | WaveTracksSelectedFlag;
#else
int flags = AudioIONotBusyFlag | TimeSelectedFlag | WaveTracksSelectedFlag;
#endif
c->AddItem(plug->GetName(),
plug->GetMenuName(),
FNS(OnEffect, plug->GetID()),
flags,
flags);
}
#else #else
flags = ANALYZE_EFFECT | BUILTIN_EFFECT | PLUGIN_EFFECT; flags = ANALYZE_EFFECT | BUILTIN_EFFECT | PLUGIN_EFFECT;
@ -1326,6 +1259,231 @@ void AudacityProject::CreateMenusAndCommands()
#endif #endif
} }
void AudacityProject::PopulateEffectsMenu(CommandManager* c,
EffectType type,
int batchflags,
int realflags)
{
PluginManager & pm = PluginManager::Get();
EffectPlugs defplugs;
EffectPlugs optplugs;
const PluginDescriptor *plug = pm.GetFirstPluginForEffectType(type);
while (plug)
{
if (plug->IsEffectDefault())
{
defplugs.Add(plug);
}
else
{
optplugs.Add(plug);
}
plug = pm.GetNextPluginForEffectType(type);
}
wxString groupby = gPrefs->Read(wxT("/Effects/GroupBy"), wxT("default"));
if (groupby == wxT("default"))
{
defplugs.Sort(SortPlugsByDefault);
optplugs.Sort(SortPlugsByDefault);
}
else if (groupby == wxT("publisher"))
{
defplugs.Sort(SortPlugsByPublisher);
optplugs.Sort(SortPlugsByPublisher);
}
else if (groupby == wxT("family"))
{
defplugs.Sort(SortPlugsByFamily);
optplugs.Sort(SortPlugsByFamily);
}
else // name
{
defplugs.Sort(SortPlugsByName);
optplugs.Sort(SortPlugsByName);
}
AddEffectMenuItems(c, defplugs, batchflags, realflags);
if (optplugs.GetCount())
{
c->AddSeparator();
}
AddEffectMenuItems(c, optplugs, batchflags, realflags);
return;
}
void AudacityProject::AddEffectMenuItems(CommandManager *c,
EffectPlugs & plugs,
int batchflags,
int realflags)
{
size_t pluginCnt = plugs.GetCount();
int perGroup;
#if defined(__WXGTK__)
gPrefs->Read(wxT("/Effects/MaxPerGroup"), &perGroup, 15);
#else
gPrefs->Read(wxT("/Effects/MaxPerGroup"), &perGroup, 0);
#endif
wxString groupBy = gPrefs->Read(wxT("/Effects/GroupBy"), wxT("default"));
bool grouped = true;
if (groupBy == wxT("default") || groupBy == wxT("name"))
{
grouped = false;
}
wxString last;
wxArrayString groupNames;
PluginIDList groupPlugs;
for (size_t i = 0; i < pluginCnt; i++)
{
const PluginDescriptor *plug = plugs[i];
#if defined(EXPERIMENTAL_REALTIME_EFFECTS)
if (plug->GetName() == wxT("Cross Fade In"))
{
int f = 1;
}
int flags = plug->IsEffectRealtimeCapable() ? realflags : batchflags;
#else
int flags = batchflags;
#endif
wxString name = plug->GetName();
wxString stripped;
if (name.EndsWith(wxT("..."), &stripped))
{
name = stripped;
}
if (plug->IsEffectInteractive())
{
name += wxT("...");
}
wxString current;
if (groupBy == wxT("default"))
{
current = plug->GetVendor();
if (plug->IsEffectDefault())
{
current = wxEmptyString;
}
if (!current.IsEmpty())
{
current += wxT(": ");
}
current += name;
name = current;
}
else if (groupBy == wxT("publisher"))
{
current = plug->GetVendor();
if (current.IsEmpty())
{
current = wxT("unknown");
}
}
else if (groupBy == wxT("family"))
{
current = plug->GetEffectFamily();
if (current.IsEmpty())
{
current = wxT("unknown");
}
}
else // name
{
current = plug->GetName();
name = current;
}
if (current != last || i + 1 == pluginCnt)
{
if (i + 1 == pluginCnt)
{
groupNames.Add(name);
groupPlugs.Add(plug->GetID());
}
size_t groupCnt = groupPlugs.GetCount();
if (grouped && groupCnt > 0 && i > 0)
{
c->BeginSubMenu(last);
}
if (grouped || i + 1 == pluginCnt)
{
int max = perGroup;
int items = perGroup;
if (max > groupCnt)
{
max = 0;
}
for (size_t j = 0; j < groupCnt; j++)
{
if (max > 0 && items == max)
{
int end = j + 1 + max;
if (end > groupCnt)
{
end = groupCnt;
}
c->BeginSubMenu(wxString::Format(_("Plug-ins %d to %d"),
j + 1,
end));
}
c->AddItem(groupNames[j],
groupNames[j],
FNS(OnEffect, groupPlugs[j]),
flags,
flags);
if (max > 0)
{
items--;
if (items == 0 || j + 1 == groupCnt)
{
c->EndSubMenu();
items = max;
}
}
}
}
if (grouped && groupCnt > 0 && i > 0)
{
c->EndSubMenu();
groupNames.Clear();
groupPlugs.Clear();
}
last = current;
}
groupNames.Add(name);
groupPlugs.Add(plug->GetID());
}
return;
}
#ifdef EFFECT_CATEGORIES #ifdef EFFECT_CATEGORIES
EffectSet AudacityProject::CreateEffectSubmenus(CommandManager* c, EffectSet AudacityProject::CreateEffectSubmenus(CommandManager* c,

View File

@ -38,6 +38,8 @@ void AddEffectsToMenu(CommandManager* c, const EffectSet& effects);
#endif #endif
void PopulateEffectsMenu(CommandManager *c, EffectType type, int batchflags, int realflags);
void AddEffectMenuItems(CommandManager *c, EffectPlugs & plugs, int batchflags, int realflags);
void CreateRecentFilesMenu(CommandManager *c); void CreateRecentFilesMenu(CommandManager *c);
void ModifyUndoMenuItems(); void ModifyUndoMenuItems();
void ModifyToolbarMenus(); void ModifyToolbarMenus();

View File

@ -1289,21 +1289,17 @@ PluginManager & PluginManager::Get()
void PluginManager::Initialize() void PluginManager::Initialize()
{ {
bool loaded = Load(); bool loaded = Load();
ModuleManager::Get().EarlyInit();
if (!loaded) ModuleManager::Get().EarlyInit();
{
PluginRegistrationDialog dlg;
dlg.ShowModal();
}
CheckForUpdates(); CheckForUpdates();
bool doRescan; bool doRescan;
gPrefs->Read(wxT("/VST/Rescan"), &doRescan, true); gPrefs->Read(wxT("/Plugins/Rescan"), &doRescan, true);
if (doRescan)
if (!loaded || doRescan)
{ {
gPrefs->Write(wxT("/VST/Rescan"), false); gPrefs->Write(wxT("/Plugins/Rescan"), false);
PluginRegistrationDialog dlg; PluginRegistrationDialog dlg;
dlg.ShowModal(); dlg.ShowModal();
} }

View File

@ -54,6 +54,7 @@ class ODLock;
class RecordingRecoveryHandler; class RecordingRecoveryHandler;
class TrackList; class TrackList;
class Tags; class Tags;
class EffectPlugs;
class TrackPanel; class TrackPanel;
class FreqWindow; class FreqWindow;

View File

@ -215,7 +215,7 @@ wxString Effect::GetVendor()
return mClient->GetVendor(); return mClient->GetVendor();
} }
return wxEmptyString; return _("Audacity");
} }
wxString Effect::GetVersion() wxString Effect::GetVersion()
@ -245,7 +245,7 @@ wxString Effect::GetFamily()
return mClient->GetFamily(); return mClient->GetFamily();
} }
return wxT("Legacy"); return _("Audacity");
} }
bool Effect::IsDefault() bool Effect::IsDefault()

View File

@ -97,33 +97,53 @@ void EffectsPrefs::PopulateOrExchange(ShuttleGui & S)
} }
S.EndStatic(); S.EndStatic();
#if USE_AUDIO_UNITS S.StartStatic(_("Effect Options"));
S.StartStatic(_("Audio Unit Effects"));
{ {
S.TieCheckBox(_("Display Audio Unit effects in Graphical Mode"), S.StartMultiColumn(2);
wxT("/AudioUnits/GUI"), {
true); wxArrayString visualgroups;
#if 0 wxArrayString prefsgroups;
S.TieCheckBox(_("Rescan VST effects next time Audacity is started"),
wxT("/VST/Rescan"), visualgroups.Add(_("Publisher: Effect Name"));
false); visualgroups.Add(_("Name"));
#endif visualgroups.Add(_("Publisher"));
} visualgroups.Add(_("Type (Internal, Ladspa, VST, etc.)"));
S.EndStatic();
#endif prefsgroups.Add(wxT("default"));
prefsgroups.Add(wxT("name"));
prefsgroups.Add(wxT("publisher"));
prefsgroups.Add(wxT("family"));
#if USE_VST S.TieChoice(_("Group effects in menus by:"),
S.StartStatic(_("VST Effects")); wxT("/Effects/GroupBy"),
{ wxT("default"),
S.TieCheckBox(_("&Display VST effects in Graphical Mode"), visualgroups,
wxT("/VST/GUI"), prefsgroups);
true); S.TieNumericTextBox(_("Maximum effects per group (0 to disable):"),
S.TieCheckBox(_("&Rescan VST effects next time Audacity is started"), wxT("/Effects/MaxPerGroup"),
wxT("/VST/Rescan"), 0,
false); 5);
}
S.EndMultiColumn();
S.AddSpace(5);
S.TieCheckBox(_("Display effects in graphical mode when supported"),
wxT("/Effects/GUI"),
true);
}
S.EndStatic();
S.StartStatic(_("Plugin Options"));
{
S.TieCheckBox(_("Check for updated plugins when Audacity starts"),
wxT("/Plugins/CheckForUpdates"),
true);
S.TieCheckBox(_("Rescan plugins next time Audacity is started"),
wxT("/Plugins/Rescan"),
false);
} }
S.EndStatic(); S.EndStatic();
#endif
#ifdef EXPERIMENTAL_EQ_SSE_THREADED #ifdef EXPERIMENTAL_EQ_SSE_THREADED
S.StartStatic(_("Instruction Set")); S.StartStatic(_("Instruction Set"));