diff --git a/include/audacity/EffectAutomationParameters.h b/include/audacity/EffectAutomationParameters.h index 59b42924e..5f3c9bad7 100644 --- a/include/audacity/EffectAutomationParameters.h +++ b/include/audacity/EffectAutomationParameters.h @@ -172,6 +172,8 @@ public: return false; } } + + return true; } }; diff --git a/include/audacity/ModuleInterface.h b/include/audacity/ModuleInterface.h index 93eea9c4e..48f2d68f7 100644 --- a/include/audacity/ModuleInterface.h +++ b/include/audacity/ModuleInterface.h @@ -74,7 +74,7 @@ public: virtual void Terminate() = 0; // Modules providing a single or static set of plugins may use - // AutoRegisterPlugins() to register those plugins. + // AutoRegisterPlugins() to register those plugins. virtual bool AutoRegisterPlugins(PluginManagerInterface & pluginManager) = 0; // For modules providing an interface to other dynamically loaded plugins, @@ -89,6 +89,10 @@ public: virtual bool RegisterPlugin(PluginManagerInterface & pluginManager, const wxString & path) = 0; + // For modules providing an interface to other dynamically loaded plugins, + // the module returns true if the plugin is still valid, otherwise false. + virtual bool IsPluginValid(const PluginID & ID, const wxString & path) = 0; + // When appropriate, CreateInstance() will be called to instantiate the plugin. virtual IdentInterface *CreateInstance(const PluginID & ID, const wxString & path) = 0; diff --git a/include/audacity/Types.h b/include/audacity/Types.h index 03b93f2bc..48fe68c3e 100644 --- a/include/audacity/Types.h +++ b/include/audacity/Types.h @@ -122,9 +122,69 @@ typedef enum ChannelNameBottomFrontRight, } ChannelName; -// ---------------------------------------------------------------------------- -// Convenience macro to suppress unused parameter warnings -// ---------------------------------------------------------------------------- -#define AUNUSED(p) +// LLL FIXME: Until a complete API is devised, we have to use +// AUDACITY_DLL_API when defining API classes. This +// it ugly, but a part of the game. Remove it when +// the API is complete. + +#if !defined(AUDACITY_DLL_API) + // This was copied from "Audacity.h" so these headers wouldn't have + // to include it. + + /* Magic for dynamic library import and export. This is unfortunately + * compiler-specific because there isn't a standard way to do it. Currently it + * works with the Visual Studio compiler for windows, and for GCC 4+. Anything + * else gets all symbols made public, which gets messy */ + /* The Visual Studio implementation */ + #ifdef _MSC_VER + #ifndef AUDACITY_DLL_API + #ifdef BUILDING_AUDACITY + #define AUDACITY_DLL_API _declspec(dllexport) + #else + #ifdef _DLL + #define AUDACITY_DLL_API _declspec(dllimport) + #else + #define AUDACITY_DLL_API + #endif + #endif + #endif + #endif //_MSC_VER + + #ifdef __GNUC__ + #include "configunix.h" + #endif + + /* The GCC-elf implementation */ + #ifdef HAVE_VISIBILITY // this is provided by the configure script, is only + // enabled for suitable GCC versions + /* The incantation is a bit weird here because it uses ELF symbol stuff. If we + * make a symbol "default" it makes it visible (for import or export). Making it + * "hidden" means it is invisible outside the shared object. */ + #ifndef AUDACITY_DLL_API + #ifdef BUILDING_AUDACITY + #define AUDACITY_DLL_API __attribute__((visibility("default"))) + #else + #define AUDACITY_DLL_API __attribute__((visibility("default"))) + #endif + #endif + #endif + + /* The GCC-win32 implementation */ + // bizzarely, GCC-for-win32 supports Visual Studio style symbol visibility, so + // we use that if building on Cygwin + #if defined __CYGWIN__ && defined __GNUC__ + #ifndef AUDACITY_DLL_API + #ifdef BUILDING_AUDACITY + #define AUDACITY_DLL_API _declspec(dllexport) + #else + #ifdef _DLL + #define AUDACITY_DLL_API _declspec(dllimport) + #else + #define AUDACITY_DLL_API + #endif + #endif + #endif + #endif +#endif #endif // __AUDACITY_TYPES_H__ diff --git a/plug-ins/SilenceMarker.ny b/plug-ins/SilenceMarker.ny index db53eb8e9..5146bfacb 100644 --- a/plug-ins/SilenceMarker.ny +++ b/plug-ins/SilenceMarker.ny @@ -5,6 +5,8 @@ ;name "Silence Finder..." ;action "Finding silence..." ;info "Adds point labels in areas of silence according to the specified\nlevel and duration of silence. If too many silences are detected,\nincrease the silence level and duration; if too few are detected,\nreduce the level and duration." +;author "Alex S. Brown" +;copyright "Released under terms of the GNU General Public License version 2" ;; by Alex S. Brown, PMP (http://www.alexsbrown.com) ;; Released under terms of the GNU General Public License version 2: diff --git a/plug-ins/SoundFinder.ny b/plug-ins/SoundFinder.ny index 5c6bf6bd0..67688769a 100644 --- a/plug-ins/SoundFinder.ny +++ b/plug-ins/SoundFinder.ny @@ -5,7 +5,8 @@ ;name "Sound Finder..." ;action "Finding sound..." ;info "Adds region labels for areas of sound according to the specified level\nand duration of surrounding silence. If too many labels are produced,\nincrease the silence level and duration; if too few are produced,\nreduce the level and duration." - +;author "Jeremy R. Brown" +;copyright "Released under terms of the GNU General Public License version 2" ;; by Jeremy R. Brown (http://www.jeremy-brown.com/) ;; based on the Silence Finder script by Alex S. Brown (http://www.alexsbrown.com) diff --git a/plug-ins/SpectralEditMulti.ny b/plug-ins/SpectralEditMulti.ny index ef928c893..d59d8bce3 100644 --- a/plug-ins/SpectralEditMulti.ny +++ b/plug-ins/SpectralEditMulti.ny @@ -3,6 +3,8 @@ ;type process ;name "Spectral edit multi tool" ;action "Calculating..." +;author "Paul Licameli" +;copyright "Unknown" (defun wet (sig) (cond diff --git a/plug-ins/SpectralEditParametricEQ.ny b/plug-ins/SpectralEditParametricEQ.ny index d17f4c55d..fb79dc7ca 100644 --- a/plug-ins/SpectralEditParametricEQ.ny +++ b/plug-ins/SpectralEditParametricEQ.ny @@ -3,6 +3,8 @@ ;type process ;name "Spectral edit parametric EQ..." ;action "Calculating..." +;author "Paul Licameli" +;copyright "Unknown" ;control control-gain "Gain (dB)" real "" 0 -24 24 diff --git a/plug-ins/SpectralEditShelves.ny b/plug-ins/SpectralEditShelves.ny index d6bbda5cc..ff83a5881 100644 --- a/plug-ins/SpectralEditShelves.ny +++ b/plug-ins/SpectralEditShelves.ny @@ -3,6 +3,8 @@ ;type process ;name "Spectral edit shelves..." ;action "Calculating..." +;author "Paul Licameli" +;copyright "Unknown" ;control control-gain "Gain (dB)" real "" 0 -24 24 diff --git a/plug-ins/StudioFadeOut.ny b/plug-ins/StudioFadeOut.ny index 7fe6fd422..352a457a7 100644 --- a/plug-ins/StudioFadeOut.ny +++ b/plug-ins/StudioFadeOut.ny @@ -4,6 +4,8 @@ ;categories "http://lv2plug.in/ns/lv2core#MixerPlugin" ;name "Studio Fade Out" ;action "Applying Fade..." +;author "Steve Daulton" +;copyright "Released under terms of the GNU General Public License version 2" ;; StudioFadeOut.ny by Steve Daulton December 2012. ;; Released under terms of the GNU General Public License version 2: diff --git a/plug-ins/adjustable-fade.ny b/plug-ins/adjustable-fade.ny index 5919e9372..c0233a635 100644 --- a/plug-ins/adjustable-fade.ny +++ b/plug-ins/adjustable-fade.ny @@ -4,6 +4,8 @@ ;categories "http://lv2plug.in/ns/lv2core#MixerPlugin" ;name "Adjustable Fade..." ;action "Applying Fade..." +;author "Steve Daulton" +;copyright "Released under terms of the GNU General Public License version 2" ;; adjustable-fade.ny by Steve Daulton Dec 2012 ;; Released under terms of the GNU General Public License version 2: diff --git a/plug-ins/beat.ny b/plug-ins/beat.ny index d2fc7f7bc..8f2d020b0 100644 --- a/plug-ins/beat.ny +++ b/plug-ins/beat.ny @@ -4,6 +4,8 @@ ;categories "http://audacityteam.org/namespace#OnsetDetector" ;name "Beat Finder..." ;action "Finding beats..." +;author "Audacity" +;copyright "Released under terms of the GNU General Public License version 2" ;; Released under terms of the GNU General Public License version 2: ;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html diff --git a/plug-ins/clicktrack.ny b/plug-ins/clicktrack.ny index c48255998..79a6b2420 100644 --- a/plug-ins/clicktrack.ny +++ b/plug-ins/clicktrack.ny @@ -5,6 +5,8 @@ ;name "Click Track..." ;action "Generating Click Track..." ;info "For help, select one of two help screens in 'Action choice' below." +;author "Dominic Mazzoni" +;copyright "Released under terms of the GNU General Public License version 2" ;; by Dominic Mazzoni, modified by David R. Sky and Steve Daulton. ;; Released under terms of the GNU General Public License version 2: diff --git a/plug-ins/clipfix.ny b/plug-ins/clipfix.ny index a3cee31af..637f4d8d3 100644 --- a/plug-ins/clipfix.ny +++ b/plug-ins/clipfix.ny @@ -5,6 +5,8 @@ ;categories "http://audacityteam.org/namespace#NoiseRemoval" ;name "Clip Fix..." ;action "Reconstructing clips..." +;author "Benjamin Schwartz" +;copyright "Licensing confirmed under terms of the GNU General Public License version 2" ;; clipfix.ny by Benjamin Schwartz. ;; Licensing confirmed under terms of the GNU General Public License version 2: diff --git a/plug-ins/crossfadein.ny b/plug-ins/crossfadein.ny index 6eb9c8a94..10f3c8af0 100644 --- a/plug-ins/crossfadein.ny +++ b/plug-ins/crossfadein.ny @@ -4,4 +4,7 @@ ;categories "http://lv2plug.in/ns/lv2core#MixerPlugin" ;name "Cross Fade In" ;action "Cross-Fading In..." +;author "Audacity" +;copyright "Unknown" + (mult s (control-srate-abs *sound-srate* (s-sqrt (pwlv 0 1 1)))) diff --git a/plug-ins/crossfadeout.ny b/plug-ins/crossfadeout.ny index bf344f075..742077e8e 100644 --- a/plug-ins/crossfadeout.ny +++ b/plug-ins/crossfadeout.ny @@ -4,6 +4,9 @@ ;categories "http://lv2plug.in/ns/lv2core#MixerPlugin" ;name "Cross Fade Out" ;action "Cross-Fading Out..." +;author "Audacity" +;copyright "Unknown" + (mult s (snd-exp (snd-scale 0.5 (snd-log (sum 1 (snd-scale -1 (ramp))))))) diff --git a/plug-ins/delay.ny b/plug-ins/delay.ny index bd99adf76..4c9dd3b88 100644 --- a/plug-ins/delay.ny +++ b/plug-ins/delay.ny @@ -5,6 +5,8 @@ ;categories "http://lv2plug.in/ns/lv2core#DelayPlugin" ;name "Delay..." ;action "Applying Delay Effect..." +;author "Steve Daulton" +;copyright "Released under terms of the GNU General Public License version 2" ;; by Steve Daulton, July 2012. ;; based on 'Delay' by David R. Sky diff --git a/plug-ins/equalabel.ny b/plug-ins/equalabel.ny index f21bbd05f..3df974d56 100644 --- a/plug-ins/equalabel.ny +++ b/plug-ins/equalabel.ny @@ -3,6 +3,8 @@ ;type analyze ;name "Regular Interval Labels..." ;action "Adding equally-spaced labels to the label track..." +;author "David R. Sky" +;copyright "Released under terms of the GNU General Public License version 2" ;; by David R. Sky (http://www.garyallendj.com/davidsky/), June-October 2007. ;; Code for label placement based on silencemarker.ny by Alex S.Brown. diff --git a/plug-ins/highpass.ny b/plug-ins/highpass.ny index 526e2f87e..60c0636de 100644 --- a/plug-ins/highpass.ny +++ b/plug-ins/highpass.ny @@ -5,6 +5,8 @@ ;categories "http://lv2plug.in/ns/lv2core#HighpassPlugin" ;name "High Pass Filter..." ;action "Performing High Pass Filter..." +;author "Dominic Mazzoni" +;copyright "Released under terms of the GNU General Public License version 2" ;; highpass.ny by Dominic Mazzoni ;; Modified by David R. Sky diff --git a/plug-ins/lowpass.ny b/plug-ins/lowpass.ny index 5c6a5f3bc..f10427ae5 100644 --- a/plug-ins/lowpass.ny +++ b/plug-ins/lowpass.ny @@ -5,6 +5,8 @@ ;categories "http://lv2plug.in/ns/lv2core#LowpassPlugin" ;name "Low Pass Filter..." ;action "Performing Low Pass Filter..." +;author "Dominic Mazzoni" +;copyright "Released under terms of the GNU General Public License version 2" ;; lowpass.ny by Dominic Mazzoni ;; Modified by David R. Sky diff --git a/plug-ins/notch.ny b/plug-ins/notch.ny index 1d97f19f4..3a6248030 100644 --- a/plug-ins/notch.ny +++ b/plug-ins/notch.ny @@ -5,6 +5,8 @@ ;categories "http://lv2plug.in/ns/lv2core/#FilterPlugin" ;name "Notch Filter..." ;action "Performing Notch Filter..." +;author "Steve Daulton and Bill Wharrie" +;copyright "Released under terms of the GNU General Public License version 2" ;control freq "Frequency" real "Hz" 60 0 10000 ;control q "Q (higher value reduces width)" real "" 1 0.1 20 diff --git a/plug-ins/pluck.ny b/plug-ins/pluck.ny index 199fe648f..6d3cd691e 100644 --- a/plug-ins/pluck.ny +++ b/plug-ins/pluck.ny @@ -6,6 +6,8 @@ ;name "Pluck..." ;action "Generating pluck sound..." ;info "MIDI values for C notes: 36, 48, 60 [middle C], 72, 84, 96." +;author "David R.Sky" +;copyright "Released under terms of the GNU General Public License version 2" ;; Released under terms of the GNU General Public License version 2: ;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html . diff --git a/plug-ins/rissetdrum.ny b/plug-ins/rissetdrum.ny index 2189a04f5..959839e58 100644 --- a/plug-ins/rissetdrum.ny +++ b/plug-ins/rissetdrum.ny @@ -4,6 +4,8 @@ ;categories "http://lv2plug.in/ns/lv2core#GeneratorPlugin" ;name "Risset Drum..." ;action "Generating Risset Drum..." +;author "Steven Jones" +;copyright "Released under terms of the GNU General Public License version 2" ;; rissetdrum.ny by Steven Jones, after Jean Claude Risset. ;; Updated by Steve Daulton July 2012. diff --git a/plug-ins/sample-data-export.ny b/plug-ins/sample-data-export.ny index e92d329af..3dfe327f7 100644 --- a/plug-ins/sample-data-export.ny +++ b/plug-ins/sample-data-export.ny @@ -4,6 +4,8 @@ ;name "Sample Data Export..." ;action "Analyzing..." ;categories "http://lv2plug.in/ns/lv2core#AnalyserPlugin" +;author "Steve Daulton" +;copyright "Released under terms of the GNU General Public License version 2" ;; sample-data-export.ny by Steve Daulton June 2012. ;; Updated July 16 2012. diff --git a/plug-ins/tremolo.ny b/plug-ins/tremolo.ny index e0f7dbf43..6552b7c87 100644 --- a/plug-ins/tremolo.ny +++ b/plug-ins/tremolo.ny @@ -5,6 +5,8 @@ ;categories "http://lv2plug.in/ns/lv2core#ModulatorPlugin" ;name "Tremolo..." ;action "Applying Tremolo..." +;author "Steve Daulton" +;copyright "Released under terms of the GNU General Public License version 2" ;; tremolo.ny by Steve Daulton (www.easyspacepro.com) July 2012. ;; Based on Tremolo by Dominic Mazzoni and David R. Sky." diff --git a/plug-ins/vocalremover.ny b/plug-ins/vocalremover.ny index 1077dc3ae..4321ae618 100644 --- a/plug-ins/vocalremover.ny +++ b/plug-ins/vocalremover.ny @@ -6,6 +6,8 @@ ;name "Vocal Remover..." ;action "Removing center-panned audio..." ;info "For reducing center-panned vocals" +;author "Steve Daulton" +;copyright "Released under terms of the GNU General Public License version 2" ;; This version of vocalremover.ny by Steve Daulton June 2013. ;; diff --git a/plug-ins/vocoder.ny b/plug-ins/vocoder.ny index 39eb9d048..2509d8056 100644 --- a/plug-ins/vocoder.ny +++ b/plug-ins/vocoder.ny @@ -5,6 +5,8 @@ ;categories "http://lv2plug.in/ns/lv2core#SpectralPlugin" ;name "Vocoder..." ;action "Processing Vocoder..." +;author "Edgar-RFT" +;copyright "Released under terms of the GNU General Public License version 2" ;; vocoder.ny by Edgar-RFT ;; a bit of code added by David R. Sky diff --git a/src/AudacityApp.cpp b/src/AudacityApp.cpp index b819ca253..ead967b4c 100644 --- a/src/AudacityApp.cpp +++ b/src/AudacityApp.cpp @@ -17,7 +17,7 @@ It handles initialization and termination by subclassing wxApp. #if 0 // This may be used to debug memory leaks. -// See: Visual Leak Dectector @ http://dmoulding.googlepages.com/vld +// See: Visual Leak Dectector @ http://vld.codeplex.com/ #include #endif diff --git a/src/BatchCommandDialog.cpp b/src/BatchCommandDialog.cpp index 91a8b3965..f5d355966 100644 --- a/src/BatchCommandDialog.cpp +++ b/src/BatchCommandDialog.cpp @@ -177,6 +177,7 @@ void BatchCommandDialog::OnEditParams(wxCommandEvent & WXUNUSED(event)) { wxString command = mCommand->GetValue(); wxString params = mParameters->GetValue(); + if (BatchCommands::SetCurrentParametersFor( command, params )) { if( BatchCommands::PromptForParamsFor( command, this )) diff --git a/src/BatchCommands.cpp b/src/BatchCommands.cpp index 744a686bf..cef8af383 100644 --- a/src/BatchCommands.cpp +++ b/src/BatchCommands.cpp @@ -288,12 +288,14 @@ wxArrayString BatchCommands::GetAllCommands() command = em.GetEffectIdentifier(plug->GetID()); if (!command.IsEmpty()) { - commands.Add( command); + commands.Add(command); } } plug = pm.GetNextPlugin(PluginTypeEffect); } + commands.Sort(); + /* This is for later in development: include the menu commands. CommandManager * mManager = project->GetCommandManager(); wxArrayString mNames; diff --git a/src/Menus.cpp b/src/Menus.cpp index 58633adfe..1f428f4b7 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -264,7 +264,36 @@ static int SortEffectsByPublisherAndName(const PluginDescriptor **a, const Plugi return akey.CmpNoCase(bkey); } -static int SortEffectsByFamily(const PluginDescriptor **a, const PluginDescriptor **b) +static int SortEffectsByTypeAndName(const PluginDescriptor **a, const PluginDescriptor **b) +{ + wxString akey = (*a)->GetEffectFamily(); + wxString bkey = (*b)->GetEffectFamily(); + + if (akey.IsEmpty()) + { + akey = _("Uncategorized"); + } + if (bkey.IsEmpty()) + { + bkey = _("Uncategorized"); + } + + if ((*a)->IsEffectDefault()) + { + akey = wxEmptyString; + } + if ((*b)->IsEffectDefault()) + { + bkey = wxEmptyString; + } + + akey += (*a)->GetName(); + bkey += (*b)->GetName(); + + return akey.CmpNoCase(bkey); +} + +static int SortEffectsByType(const PluginDescriptor **a, const PluginDescriptor **b) { wxString akey = (*a)->GetEffectFamily(); wxString bkey = (*b)->GetEffectFamily(); @@ -1284,27 +1313,32 @@ void AudacityProject::PopulateEffectsMenu(CommandManager* c, plug = pm.GetNextPluginForEffectType(type); } - wxString groupby = gPrefs->Read(wxT("/Effects/GroupBy"), wxT("default")); + wxString groupby = gPrefs->Read(wxT("/Effects/GroupBy"), wxT("name")); - if (groupby == wxT("name")) + if (groupby == wxT("sortby:name")) { defplugs.Sort(SortEffectsByName); optplugs.Sort(SortEffectsByName); } - else if (groupby == wxT("publisher")) + else if (groupby == wxT("sortby:publisher:name")) + { + defplugs.Sort(SortEffectsByName); + optplugs.Sort(SortEffectsByPublisherAndName); + } + else if (groupby == wxT("sortby:type:name")) + { + defplugs.Sort(SortEffectsByName); + optplugs.Sort(SortEffectsByTypeAndName); + } + else if (groupby == wxT("groupby:publisher")) { defplugs.Sort(SortEffectsByPublisher); optplugs.Sort(SortEffectsByPublisher); } - else if (groupby == wxT("publisher:name")) + else if (groupby == wxT("groupby:type")) { - defplugs.Sort(SortEffectsByPublisherAndName); - optplugs.Sort(SortEffectsByPublisherAndName); - } - else if (groupby == wxT("family")) - { - defplugs.Sort(SortEffectsByFamily); - optplugs.Sort(SortEffectsByFamily); + defplugs.Sort(SortEffectsByType); + optplugs.Sort(SortEffectsByType); } else // name { @@ -1341,151 +1375,193 @@ void AudacityProject::AddEffectMenuItems(CommandManager *c, wxString groupBy = gPrefs->Read(wxT("/Effects/GroupBy"), wxT("name")); bool grouped = false; - if (groupBy == wxT("publisher") || groupBy == wxT("family")) + if (groupBy.StartsWith(wxT("groupby"))) { grouped = true; } - wxString last; wxArrayString groupNames; PluginIDList groupPlugs; - for (size_t i = 0; i < pluginCnt; i++) + wxArrayInt groupFlags; + if (grouped) { - const PluginDescriptor *plug = plugs[i]; - -#if defined(EXPERIMENTAL_REALTIME_EFFECTS) - int flags = plug->IsEffectRealtime() ? 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 last; wxString current; - if (groupBy == wxT("publisher:name")) + + for (size_t i = 0; i < pluginCnt; i++) { - current = plug->GetVendor(); - if (plug->IsEffectDefault()) + const PluginDescriptor *plug = plugs[i]; + + wxString name = plug->GetName(); + + // This goes away once everything has been converted to new format + wxString stripped; + if (name.EndsWith(wxT("..."), &stripped)) { - current = wxEmptyString; + name = stripped; } - if (!current.IsEmpty()) + if (plug->IsEffectInteractive()) { - current += wxT(": "); + name += wxT("..."); } - current += name; - name = current; - } - else if (groupBy == wxT("publisher")) - { - current = plug->GetVendor(); - if (current.IsEmpty()) + if (groupBy == wxT("groupby:publisher")) { - current = _("Unknown"); - } - } - else if (groupBy == wxT("family")) - { - current = plug->GetEffectFamily(); - if (current.IsEmpty()) - { - current = _("Unknown"); - } - } - else if (groupBy == wxT("name")) - { - current = plug->GetName(); - name = current; - } - else // default to "name" - { - current = plug->GetName(); - name = current; - } - - if (current != last || i + 1 == pluginCnt) - { - if (i + 1 == pluginCnt) - { - groupNames.Add(name); - groupPlugs.Add(plug->GetID()); - } - - int groupCnt = (int) 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) + current = plug->GetVendor(); + if (current.IsEmpty()) { - max = 0; + current = _("Unknown"); } - - for (int j = 0; j < groupCnt; j++) + } + else if (groupBy == wxT("groupby:type")) + { + current = plug->GetEffectFamily(); + if (current.IsEmpty()) { - if (max > 0 && items == max) - { - int end = j + max; - if (end + 1 > 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; - } - } + current = _("Unknown"); } } - if (grouped && groupCnt > 0 && i > 0) + if (current != last) { - c->EndSubMenu(); + if (!last.IsEmpty()) + { + c->BeginSubMenu(last); + } + + AddEffectMenuItemGroup(c, groupNames, groupPlugs, groupFlags); + + if (!last.IsEmpty()) + { + c->EndSubMenu(); + } groupNames.Clear(); groupPlugs.Clear(); + groupFlags.Clear(); + last = current; } - last = current; + groupNames.Add(name); + groupPlugs.Add(plug->GetID()); + groupFlags.Add(plug->IsEffectRealtime() ? realflags : batchflags); } - groupNames.Add(name); - groupPlugs.Add(plug->GetID()); + if (groupNames.GetCount() > 0) + { + c->BeginSubMenu(current); + + AddEffectMenuItemGroup(c, groupNames, groupPlugs, groupFlags); + + c->EndSubMenu(); + } + } + else + { + for (size_t i = 0; i < pluginCnt; i++) + { + const PluginDescriptor *plug = plugs[i]; + + wxString name = plug->GetName(); + + // This goes away once everything has been converted to new format + wxString stripped; + if (name.EndsWith(wxT("..."), &stripped)) + { + name = stripped; + } + + if (plug->IsEffectInteractive()) + { + name += wxT("..."); + } + + wxString group = wxEmptyString; + if (groupBy == wxT("sortby:publisher:name")) + { + group = plug->GetVendor(); + } + else if (groupBy == wxT("sortby:type:name")) + { + group = plug->GetEffectFamily(); + } + + if (plug->IsEffectDefault()) + { + group = wxEmptyString; + } + + if (!group.IsEmpty()) + { + group += wxT(": "); + } + + groupNames.Add(group + name); + groupPlugs.Add(plug->GetID()); + groupFlags.Add(plug->IsEffectRealtime() ? realflags : batchflags); + } + if (groupNames.GetCount() > 0) + { + AddEffectMenuItemGroup(c, groupNames, groupPlugs, groupFlags); + } + + } + + return; +} + +void AudacityProject::AddEffectMenuItemGroup(CommandManager *c, + const wxArrayString & names, + const PluginIDList & plugs, + const wxArrayInt & flags) +{ + int groupCnt = (int) names.GetCount(); + int perGroup; + +#if defined(__WXGTK__) + gPrefs->Read(wxT("/Effects/MaxPerGroup"), &perGroup, 15); +#else + gPrefs->Read(wxT("/Effects/MaxPerGroup"), &perGroup, 0); +#endif + + int max = perGroup; + int items = perGroup; + + if (max > groupCnt) + { + max = 0; + } + + for (int j = 0; j < groupCnt; j++) + { + if (max > 0 && items == max) + { + int end = j + max; + if (end + 1 > groupCnt) + { + end = groupCnt; + } + c->BeginSubMenu(wxString::Format(_("Plug-ins %d to %d"), + j + 1, + end)); + } + + c->AddItem(names[j], + names[j], + FNS(OnEffect, plugs[j]), + flags[j], + flags[j]); + + if (max > 0) + { + items--; + if (items == 0 || j + 1 == groupCnt) + { + c->EndSubMenu(); + items = max; + } + } } return; diff --git a/src/Menus.h b/src/Menus.h index 0015fc9bd..7b27f9c47 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -40,6 +40,7 @@ void AddEffectsToMenu(CommandManager* c, const EffectSet& effects); void PopulateEffectsMenu(CommandManager *c, EffectType type, int batchflags, int realflags); void AddEffectMenuItems(CommandManager *c, EffectPlugs & plugs, int batchflags, int realflags); +void AddEffectMenuItemGroup(CommandManager *c, const wxArrayString & names, const PluginIDList & plugs, const wxArrayInt & flags); void CreateRecentFilesMenu(CommandManager *c); void ModifyUndoMenuItems(); void ModifyToolbarMenus(); diff --git a/src/ModuleManager.cpp b/src/ModuleManager.cpp index c74d639ee..5ec33d8e0 100644 --- a/src/ModuleManager.cpp +++ b/src/ModuleManager.cpp @@ -18,6 +18,7 @@ i.e. an alternative to the usual interface, for Audacity. *//*******************************************************************/ +#include #include #include #include @@ -44,6 +45,8 @@ i.e. an alternative to the usual interface, for Audacity. #include "ModuleManager.h" #include "widgets/MultiDialog.h" +#include + #define initFnName "ExtensionModuleInit" #define versionFnName "GetVersionString" #define scriptFnName "RegScriptServerFunc" @@ -182,9 +185,12 @@ static wxArrayPtrVoid *pBuiltinModuleList = NULL; void RegisterBuiltinModule(ModuleMain moduleMain) { if (pBuiltinModuleList == NULL) + { pBuiltinModuleList = new wxArrayPtrVoid; + } pBuiltinModuleList->Add((void *)moduleMain); + return; } @@ -205,14 +211,17 @@ ModuleManager::~ModuleManager() } mModules.Clear(); - for (ModuleMap::iterator iter = mDynModules.begin(); iter != mDynModules.end(); iter++) + ModuleMap::iterator iter = mDynModules.begin(); + while (iter != mDynModules.end()) { - ModuleInterface *mod = iter->second; - delete mod; + UnloadModule(iter->second); + iter = mDynModules.begin(); } - mDynModules.clear(); - if( pBuiltinModuleList != NULL ) + + if (pBuiltinModuleList != NULL) + { delete pBuiltinModuleList; + } } // static @@ -352,38 +361,10 @@ ModuleManager & ModuleManager::Get() return mInstance; } -void ModuleManager::InitializeBuiltins() -{ - PluginManager & pm = PluginManager::Get(); - - if (pBuiltinModuleList==NULL) - return; - - for (size_t i = 0, cnt = pBuiltinModuleList->GetCount(); i < cnt; i++) - { - ModuleMain audacityMain = (ModuleMain) (*pBuiltinModuleList)[i]; - ModuleInterface *module = audacityMain(this, NULL); - - mDynModules[module->GetID()] = module; - - module->Initialize(); - - // First, we need to remember it - pm.RegisterModulePlugin(module); - - // Now, allow the module to auto-register children - module->AutoRegisterPlugins(pm); - } -} - -// static -void ModuleManager::EarlyInit() +bool ModuleManager::DiscoverProviders() { InitializeBuiltins(); -} -bool ModuleManager::DiscoverProviders(wxArrayString & providers) -{ wxArrayString provList; wxArrayString pathList; @@ -407,31 +388,43 @@ bool ModuleManager::DiscoverProviders(wxArrayString & providers) wxGetApp().FindFilesInPathList(wxT("*.so"), pathList, provList); #endif + PluginManager & pm = PluginManager::Get(); + for (int i = 0, cnt = provList.GetCount(); i < cnt; i++) { - providers.push_back(provList[i]); + ModuleInterface *module = LoadModule(provList[i]); + if (module) + { + // First, we need to remember it + pm.RegisterModulePlugin(module); + + // Now, allow the module to auto-register children + module->AutoRegisterPlugins(pm); + } } return true; } -bool ModuleManager::DiscoverProvider(const wxString & path) +void ModuleManager::InitializeBuiltins() { - ModuleInterface *module = LoadModule(path); - if (module) + PluginManager & pm = PluginManager::Get(); + + for (size_t i = 0, cnt = pBuiltinModuleList->GetCount(); i < cnt; i++) { - PluginManager & pm = PluginManager::Get(); + ModuleInterface *module = ((ModuleMain) (*pBuiltinModuleList)[i])(this, NULL); - // First, we need to remember it - pm.RegisterModulePlugin(module); + if (module->Initialize()) + { + mDynModules[module->GetID()] = module; - // Now, allow the module to auto-register children - module->AutoRegisterPlugins(pm); + // First, we need to remember it + pm.RegisterModulePlugin(module); -// UnloadModule(module); + // Now, allow the module to auto-register children + module->AutoRegisterPlugins(pm); + } } - - return true; } ModuleInterface *ModuleManager::LoadModule(const wxString & path) @@ -489,19 +482,6 @@ void ModuleManager::UnloadModule(ModuleInterface *module) } } -void ModuleManager::InitializePlugins() -{ - InitializeBuiltins(); - - // Look for dynamic modules here - - for (ModuleMap::iterator iter = mDynModules.begin(); iter != mDynModules.end(); iter++) - { - ModuleInterface *mod = iter->second; - mod->Initialize(); - } -} - void ModuleManager::RegisterModule(ModuleInterface *module) { wxString id = module->GetID(); @@ -548,7 +528,7 @@ void ModuleManager::FindAllPlugins(PluginIDList & providers, wxArrayString & pat } wxArrayString ModuleManager::FindPluginsForProvider(const PluginID & providerID, - const wxString & path) + const wxString & path) { // Instantiate if it hasn't already been done if (mDynModules.find(providerID) == mDynModules.end()) @@ -573,15 +553,10 @@ bool ModuleManager::RegisterPlugin(const PluginID & providerID, const wxString & return mDynModules[providerID]->RegisterPlugin(PluginManager::Get(), path); } -bool ModuleManager::IsProviderBuiltin(const PluginID & providerID) -{ - return mModuleMains.find(providerID) != mModuleMains.end(); -} - IdentInterface *ModuleManager::CreateProviderInstance(const PluginID & providerID, const wxString & path) { - if (path.empty() && mDynModules.find(providerID) != mDynModules.end()) + if (path.IsEmpty() && mDynModules.find(providerID) != mDynModules.end()) { return mDynModules[providerID]; } @@ -611,3 +586,34 @@ void ModuleManager::DeleteInstance(const PluginID & providerID, mDynModules[providerID]->DeleteInstance(instance); } + +bool ModuleManager::IsProviderValid(const PluginID & providerID, + const wxString & path) +{ + // Builtin modules do not have a path + if (path.IsEmpty()) + { + return true; + } + + wxFileName lib(path); + if (lib.FileExists() || lib.DirExists()) + { + return true; + } + + return false; +} + +bool ModuleManager::IsPluginValid(const PluginID & providerID, + const PluginID & ID, + const wxString & path) +{ + if (mDynModules.find(providerID) == mDynModules.end()) + { + return false; + } + + return mDynModules[providerID]->IsPluginValid(ID, path); +} + diff --git a/src/ModuleManager.h b/src/ModuleManager.h index 2b58b7808..845666f36 100644 --- a/src/ModuleManager.h +++ b/src/ModuleManager.h @@ -86,22 +86,20 @@ public: void Initialize(CommandHandler & cmdHandler); int Dispatch(ModuleDispatchTypes type); - void EarlyInit(); - // PluginManager use - bool DiscoverProviders(wxArrayString & providers); - bool DiscoverProvider(const wxString & path); + bool DiscoverProviders(); void FindAllPlugins(PluginIDList & providers, wxArrayString & paths); wxArrayString FindPluginsForProvider(const PluginID & provider, const wxString & path); bool RegisterPlugin(const PluginID & provider, const wxString & path); - void InitializePlugins(); - bool IsProviderBuiltin(const PluginID & provider); IdentInterface *CreateProviderInstance(const PluginID & ID, const wxString & path); IdentInterface *CreateInstance(const PluginID & provider, const PluginID & ID, const wxString & path); void DeleteInstance(const PluginID & provider, IdentInterface *instance); + bool IsProviderValid(const PluginID & provider, const wxString & path); + bool IsPluginValid(const PluginID & provider, const PluginID & ID, const wxString & path); + private: void InitializeBuiltins(); ModuleInterface *LoadModule(const wxString & path); diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp index 983f7292b..20433f04a 100644 --- a/src/PluginManager.cpp +++ b/src/PluginManager.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,8 @@ #include +WX_DECLARE_STRING_HASH_MAP(wxString, ProviderMap); + // ============================================================================ // // @@ -373,11 +376,19 @@ wxAccStatus CheckListAx::GetValue( int childId, wxString *strValue ) #define EffectClearAllID 7002 #define EffectSelectAllID 7003 +int wxCALLBACK SortCompare(long item1, long item2, long WXUNUSED(sortData)) +{ + wxString *str1 = (wxString *) item1; + wxString *str2 = (wxString *) item2; + + return str2->Cmp(*str1); +} + class PluginRegistrationDialog : public wxDialog { public: // constructors and destructors - PluginRegistrationDialog(); + PluginRegistrationDialog(ProviderMap & map); virtual ~PluginRegistrationDialog(); private: @@ -405,10 +416,12 @@ private: wxListCtrl *mEffects; PluginIDList mProvs; wxArrayString mPaths; - std::vector miState; + wxArrayInt miState; bool mCancelClicked; + ProviderMap & mMap; + DECLARE_EVENT_TABLE() }; @@ -419,12 +432,13 @@ BEGIN_EVENT_TABLE(PluginRegistrationDialog, wxDialog) EVT_BUTTON(EffectSelectAllID, PluginRegistrationDialog::OnSelectAll) END_EVENT_TABLE() -PluginRegistrationDialog::PluginRegistrationDialog() +PluginRegistrationDialog::PluginRegistrationDialog(ProviderMap & map) : wxDialog(wxGetApp().GetTopWindow(), wxID_ANY, _("Register Effects"), wxDefaultPosition, wxDefaultSize, - wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) + wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), + mMap(map) { mEffects = NULL; SetLabel(_("Register Effects")); // Provide visual label @@ -443,6 +457,15 @@ PluginRegistrationDialog::~PluginRegistrationDialog() wxKeyEventHandler(PluginRegistrationDialog::OnListChar), NULL, this); + + for (int i = 0, cnt = mEffects->GetItemCount(); i < cnt; i++) + { + wxString *str = (wxString *) mEffects->GetItemData(i); + if (str) + { + delete str; + } + } } void PluginRegistrationDialog::Populate() @@ -453,8 +476,6 @@ void PluginRegistrationDialog::Populate() // ----------------------- End of main section -------------- } -WX_DECLARE_STRING_HASH_MAP(wxString, ProviderMap); - /// Defines the dialog and does data exchange with it. void PluginRegistrationDialog::PopulateOrExchange(ShuttleGui &S) { @@ -521,44 +542,6 @@ void PluginRegistrationDialog::PopulateOrExchange(ShuttleGui &S) } S.EndVerticalLay(); - PluginManager & pm = PluginManager::Get(); - ModuleManager & mm = ModuleManager::Get(); - - // Capture all of the module IDs and paths so the iterate stays valid - ProviderMap provs; - wxArrayString keys; - wxArrayString paths; - wxString padding = wxT("0000000000"); - const PluginDescriptor *plug = pm.GetFirstPlugin(PluginTypeModule); - while (plug) - { - wxString key = wxString::Format(wxT("%d"), (int) provs.size()); - key.insert(0, padding.substr(0, padding.length() - key.length())); - provs[key] = plug->GetID(); - keys.push_back(key); - paths.push_back(plug->GetPath()); - - plug = pm.GetNextPlugin(PluginTypeModule); - } - - wxArrayString sortable; - for (size_t i = 0, icnt = paths.size(); i < icnt; i++) - { - wxString key = keys[i]; - PluginID provID = provs[key]; - wxString provPath = paths[i]; - - wxArrayString newPaths = mm.FindPluginsForProvider(provID, provPath); - - for (size_t j = 0, jcnt = newPaths.size(); j < jcnt; j++) - { - sortable.push_back(key + wxT(" ") + newPaths[j]); - } - } - - // With the index - sortable.Sort(); - // The dc is used to compute the text width in pixels. // FIXME: That works fine for PC, but apparently comes out too small for wxMAC. // iLen is minimum width in pixels shown for the file names. 200 is reasonable. @@ -566,20 +549,18 @@ void PluginRegistrationDialog::PopulateOrExchange(ShuttleGui &S) int iPathLen = 0; int x, y; wxRect iconrect; - for (int i = 0, cnt = sortable.size(); i < cnt; i++) + int i = 0; + for (ProviderMap::iterator iter = mMap.begin(); iter != mMap.end(); iter++, i++) { - miState.push_back( SHOW_CHECKED ); + miState.Add( SHOW_CHECKED ); - wxString item = sortable[i]; - int split = item.find(wxT(" ")); - mProvs.push_back(provs[item.substr(0, split)]); - mPaths.push_back(item.substr(split + 1, wxString::npos)); - - wxFileName fn(mPaths.back()); - wxString name(fn.GetName()); - wxString path(fn.GetFullPath()); + wxFileName fname = iter->first; + wxString name = fname.GetName(); + wxString path = fname.GetFullPath(); + wxString *key = new wxString(iter->second + name); mEffects->InsertItem(i, name, SHOW_CHECKED); + mEffects->SetItemPtrData(i, (wxUIntPtr) key); mEffects->SetItem(i, COL_PATH, path); // Only need to get the icon width once @@ -606,6 +587,8 @@ void PluginRegistrationDialog::PopulateOrExchange(ShuttleGui &S) iPathLen = wxMax(iPathLen, x + iconrect.width + (iconrect.x * 2)); } + mEffects->SortItems(SortCompare, 0); + mEffects->SetColumnWidth(COL_NAME, iNameLen + /* fudge */ 5); mEffects->SetColumnWidth(COL_PATH, iPathLen + /* fudge */ 5); @@ -713,18 +696,50 @@ void PluginRegistrationDialog::OnClearAll(wxCommandEvent & WXUNUSED(evt)) void PluginRegistrationDialog::OnOK(wxCommandEvent & WXUNUSED(evt)) { mCancelClicked = false; + FindWindowById(EffectListID)->Disable(); FindWindowById(wxID_OK)->Disable(); FindWindowById(EffectListID)->Disable(); FindWindowById(EffectClearAllID)->Disable(); FindWindowById(EffectSelectAllID)->Disable(); - for (size_t i = 0, cnt = mPaths.size(); i < cnt && !mCancelClicked; i++) + PluginManager & pm = PluginManager::Get(); + ModuleManager & mm = ModuleManager::Get(); + + wxListItem li; + li.Clear(); + for (int i = 0, cnt = mEffects->GetItemCount(); i < cnt && !mCancelClicked; i++) { mEffects->EnsureVisible(i); + li.SetId(i); + li.SetColumn(COL_PATH); + li.SetMask(wxLIST_MASK_TEXT); + mEffects->GetItem(li); + wxString path = li.GetText(); + + // Create a placeholder descriptor to show we've seen this plugin before and not + // to show it as new the next time Audacity starts. + // + // If the user chooses not to enable it or the scan of the plugin fails, the dummy + // entry will remain in the plugin list as disabled. + // + // However, if the scan of the of the plugin succeeds, then this dummy entry will be + // replaced during registration. + PluginDescriptor & plug = pm.mPlugins[path]; + + plug.SetID(path); + plug.SetPath(path); + plug.SetEnabled(false); + if (miState[i] == SHOW_CHECKED) { + li.SetId(i); + li.SetColumn(COL_PATH); + li.SetMask(wxLIST_MASK_TEXT); + mEffects->GetItem(li); + wxString path = li.GetText(); + mEffects->SetItemImage(i, SHOW_ARROW); - ModuleManager::Get().RegisterPlugin(mProvs[i], wxString(mPaths[i])); + mm.RegisterPlugin(mMap[path], path); mEffects->SetItemImage(i, SHOW_CHECKED); } wxYield(); @@ -841,11 +856,6 @@ const PluginID & PluginDescriptor::GetProviderID() const return mProviderID; } -const wxString & PluginDescriptor::GetDateTime() const -{ - return mDateTime; -} - bool PluginDescriptor::IsEnabled() const { return mEnabled; @@ -902,11 +912,6 @@ void PluginDescriptor::SetProviderID(const PluginID & providerID) mProviderID = providerID; } -void PluginDescriptor::SetDateTime(const wxString & dateTime) -{ - mDateTime = dateTime; -} - void PluginDescriptor::SetEnabled(bool enable) { mEnabled = enable; @@ -1054,7 +1059,9 @@ void PluginDescriptor::SetImporterExtensions(const wxArrayString & extensions) void PluginManager::RegisterModulePlugin(IdentInterface *module) { - CreatePlugin(module, PluginTypeModule); + PluginDescriptor & plug = CreatePlugin(module, PluginTypeModule); + + plug.SetEnabled(true); } void PluginManager::RegisterEffectPlugin(IdentInterface *provider, EffectIdentInterface *effect) @@ -1332,26 +1339,14 @@ PluginManager & PluginManager::Get() void PluginManager::Initialize() { + // Always load the config first bool loaded = Load(); - ModuleManager::Get().EarlyInit(); + // Then look for providers (they may autoregister plugins) + ModuleManager::Get().DiscoverProviders(); - CheckForUpdates(); - - bool doRescan; - gPrefs->Read(wxT("/Plugins/Rescan"), &doRescan, true); - - if (!loaded || doRescan) - { - gPrefs->Write(wxT("/Plugins/Rescan"), false); - PluginRegistrationDialog dlg; - dlg.ShowModal(); - - if (mConfig) - { - mConfig->Flush(); - } - } + // And finally check for updates + CheckForUpdates(!loaded); } void PluginManager::Terminate() @@ -1416,6 +1411,8 @@ bool PluginManager::Load() LoadGroup(wxT("exporters"), PluginTypeExporter); LoadGroup(wxT("importers"), PluginTypeImporter); + LoadGroup(wxT("placeholders"), PluginTypeNone); + return true; } @@ -1494,16 +1491,6 @@ void PluginManager::LoadGroup(const wxChar * group, PluginType type) } plug.SetDescription(wxString(strVal)); - // Get the last update time and bypass group if not found - if (!plug.GetPath().IsEmpty()) - { - if (!mConfig->Read(KEY_LASTUPDATED, &strVal)) - { - continue; - } - plug.SetDateTime(wxString(strVal)); - } - // Is it enabled...default to no if not found mConfig->Read(KEY_ENABLED, &boolVal, false); plug.SetEnabled(boolVal); @@ -1511,9 +1498,13 @@ void PluginManager::LoadGroup(const wxChar * group, PluginType type) switch (type) { case PluginTypeModule: + { + // Nothing to do here yet + } break; case PluginTypeEffect: + { // Get the effect type and bypass group if not found if (!mConfig->Read(KEY_EFFECTTYPE, &strVal)) { @@ -1571,7 +1562,7 @@ void PluginManager::LoadGroup(const wxChar * group, PluginType type) continue; } plug.SetEffectAutomatable(boolVal); - + } break; case PluginTypeImporter: @@ -1605,8 +1596,16 @@ void PluginManager::LoadGroup(const wxChar * group, PluginType type) } break; + case PluginTypeNone: + { + // Used for placeholder groups + } + break; + default: + { continue; + } } // Everything checked out...accept the plugin @@ -1624,12 +1623,13 @@ void PluginManager::Save() return; } - // TODO: This is a bit drastic...only save groups when new plugins are registerd + // TODO: This is a bit drastic...only save groups when new plugins are registered // Save the individual groups SaveGroup(wxT("effects"), PluginTypeEffect); SaveGroup(wxT("exporters"), PluginTypeExporter); SaveGroup(wxT("importers"), PluginTypeImporter); + SaveGroup(wxT("placeholders"), PluginTypeNone); // And now the providers SaveGroup(wxT("modules"), PluginTypeModule); @@ -1656,7 +1656,6 @@ void PluginManager::SaveGroup(const wxChar *group, PluginType type) mConfig->Write(KEY_VENDOR, plug.GetVendor()); mConfig->Write(KEY_DESCRIPTION, plug.GetDescription()); mConfig->Write(KEY_PROVIDERID, plug.GetProviderID()); - mConfig->Write(KEY_LASTUPDATED, plug.GetDateTime()); mConfig->Write(KEY_ENABLED, plug.IsEnabled()); switch (type) @@ -1712,118 +1711,93 @@ void PluginManager::SaveGroup(const wxChar *group, PluginType type) return; } -void PluginManager::CheckForUpdates() +void PluginManager::CheckForUpdates(bool forceRescan) { - // Always check for and remove missing plugins - RemoveMissing(); - // Get ModuleManager reference ModuleManager & mm = ModuleManager::Get(); - wxArrayString providers; - wxArrayString paths; - - // Always check for new or updated modules - if (mm.DiscoverProviders(paths)) - { - paths = IsNewOrUpdated(paths); - for (size_t i = 0, cnt = paths.size(); i < cnt; i++) - { - mm.DiscoverProvider(paths[i]); - } - } + bool doRescan; + gPrefs->Read(wxT("/Plugins/Rescan"), &doRescan, true); bool doCheck; gPrefs->Read(wxT("/Plugins/CheckForUpdates"), &doCheck, true); - if (doCheck && paths.size() > 0) - { - PluginRegistrationDialog dlg; - dlg.ShowModal(); - } - return; -} + ProviderMap map; -void PluginManager::RemoveMissing() -{ - // Check for plugins that no longer exist + // Always check for and disable missing plugins + // + // Since the user's saved presets are in the registery, never delete them. That is + // a job for the plugin manager UI (once it is written) + // Check for plugins that are no longer valid PluginMap::iterator iter = mPlugins.begin(); while (iter != mPlugins.end()) { PluginDescriptor & plug = iter->second; + const PluginID & plugID = plug.GetID(); + const wxString & plugPath = plug.GetPath(); - if (!plug.GetPath().IsEmpty()) + if (plug.GetPluginType() == PluginTypeModule) { - wxFileName plugPath = plug.GetPath(); - - if (!(plugPath.FileExists() || plugPath.DirExists())) + if (!mm.IsProviderValid(plugID, plugPath)) { - mPlugins.erase(iter++); - continue; + plug.SetEnabled(false); + } + else + { + // Only collect plugin paths if we're doing a full scan or checking for updates + if (doRescan || doCheck) + { + wxArrayString paths = mm.FindPluginsForProvider(plugID, plugPath); + for (size_t i = 0, cnt = paths.GetCount(); i < cnt; i++) + { + map[paths[i]] = plugID; + } + } + } + } + else + { + if (!mm.IsPluginValid(plug.GetProviderID(), plugID, plugPath)) + { + plug.SetEnabled(false); } } - ++iter; - } -} - -wxArrayString PluginManager::IsNewOrUpdated(const wxArrayString & paths) -{ - wxArrayString plugsToAddOrUpdate; - - // Create a map of plugins indexed by their path - std::map pathPlugs; - for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); iter++) - { - PluginDescriptor & plug = iter->second; - pathPlugs[plug.GetPath()] = &plug; + iter++; } - // Now check for new or updated paths - for (size_t i = 0, cnt = paths.size(); i < cnt; i++) + // If we're only checking for new plugins, then remove all of the known ones + if (doCheck && !doRescan) { - wxFileName scanPath = paths[i]; - wxString scanFull = scanPath.GetFullPath(); - - if (pathPlugs.find(scanFull) != pathPlugs.end()) + for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); iter++) { - if (GetDateTime(scanFull) <= pathPlugs[scanFull]->GetDateTime()) + PluginDescriptor & plug = iter->second; + const wxString & plugPath = plug.GetPath(); + + ProviderMap::iterator mapiter = map.find(plugPath); + if (map.find(plugPath) != map.end()) { - continue; + map.erase(mapiter); } } - - plugsToAddOrUpdate.push_back(scanFull); } - - return plugsToAddOrUpdate; -} - -bool PluginManager::HasType(PluginType type) -{ - for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); iter++) + + // Allow the user to choose which ones to enable + if (map.size() != 0) { - if (iter->second.GetPluginType() == type) + PluginRegistrationDialog dlg(map); + if (dlg.ShowModal() == wxID_OK) { - return true; + gPrefs->Write(wxT("/Plugins/Rescan"), false); } } - return false; -} - -void PluginManager::PurgeType(PluginType type) -{ -#if 0 - for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); iter++) + if (mConfig) { - if (iter->second.GetPluginType() == type) - { - mPlugins.erase(iter->first); - mConfig->DeleteGroup(CACHEROOT + iter->second.GetID() - } + Save(); } -#endif + + return; } int PluginManager::GetPluginCount(PluginType type) @@ -1855,7 +1829,13 @@ const PluginDescriptor *PluginManager::GetFirstPlugin(PluginType type) { for (mPluginsIter = mPlugins.begin(); mPluginsIter != mPlugins.end(); mPluginsIter++) { - if (mPluginsIter->second.GetPluginType() == type) + PluginDescriptor & plug = mPluginsIter->second; + bool familyEnabled = true; + if (type == PluginTypeEffect) + { + gPrefs->Read(plug.GetEffectFamily() + wxT("/Enable"), &familyEnabled, true); + } + if (plug.IsEnabled() && plug.GetPluginType() == type && familyEnabled) { return &mPluginsIter->second; } @@ -1868,33 +1848,13 @@ const PluginDescriptor *PluginManager::GetNextPlugin(PluginType type) { while (++mPluginsIter != mPlugins.end()) { - if (mPluginsIter->second.GetPluginType() == type) + PluginDescriptor & plug = mPluginsIter->second; + bool familyEnabled = true; + if (type == PluginTypeEffect) { - return &mPluginsIter->second; + gPrefs->Read(plug.GetEffectFamily() + wxT("/Enable"), &familyEnabled, true); } - } - - return NULL; -} - -const PluginDescriptor *PluginManager::GetFirstPluginForProvider(const PluginID & ID) -{ - for (mPluginsIter = mPlugins.begin(); mPluginsIter != mPlugins.end(); mPluginsIter++) - { - if (mPluginsIter->second.GetProviderID() == ID) - { - return &mPluginsIter->second; - } - } - - return NULL; -} - -const PluginDescriptor *PluginManager::GetNextPluginForProvider(const PluginID & ID) -{ - while (++mPluginsIter != mPlugins.end()) - { - if (mPluginsIter->second.GetProviderID() == ID) + if (plug.IsEnabled() && plug.GetPluginType() == type && familyEnabled) { return &mPluginsIter->second; } @@ -1908,7 +1868,9 @@ const PluginDescriptor *PluginManager::GetFirstPluginForEffectType(EffectType ty for (mPluginsIter = mPlugins.begin(); mPluginsIter != mPlugins.end(); mPluginsIter++) { PluginDescriptor & plug = mPluginsIter->second; - if (plug.IsEnabled() && plug.GetEffectType() == type) + bool familyEnabled; + gPrefs->Read(plug.GetEffectFamily() + wxT("/Enable"), &familyEnabled, true); + if (plug.IsEnabled() && plug.GetEffectType() == type && familyEnabled) { return &plug; } @@ -1922,7 +1884,9 @@ const PluginDescriptor *PluginManager::GetNextPluginForEffectType(EffectType typ while (++mPluginsIter != mPlugins.end()) { PluginDescriptor & plug = mPluginsIter->second; - if (plug.IsEnabled() && plug.GetEffectType() == type) + bool familyEnabled; + gPrefs->Read(plug.GetEffectFamily() + wxT("/Enable"), &familyEnabled, true); + if (plug.IsEnabled() && plug.GetEffectType() == type && familyEnabled) { return &plug; } @@ -1931,33 +1895,6 @@ const PluginDescriptor *PluginManager::GetNextPluginForEffectType(EffectType typ return NULL; } - -const PluginDescriptor *PluginManager::GetFirstPluginForEffectFamily(const wxString & family) -{ - for (mPluginsIter = mPlugins.begin(); mPluginsIter != mPlugins.end(); mPluginsIter++) - { - if (mPluginsIter->second.GetEffectFamily() == family) - { - return &mPluginsIter->second; - } - } - - return NULL; -} - -const PluginDescriptor *PluginManager::GetNextPluginForEffectFamily(const wxString & family) -{ - while (++mPluginsIter != mPlugins.end()) - { - if (mPluginsIter->second.GetEffectFamily() == family) - { - return &mPluginsIter->second; - } - } - - return NULL; -} - bool PluginManager::IsRegistered(const PluginID & ID) { if (mPlugins.find(ID) == mPlugins.end()) @@ -2053,7 +1990,8 @@ void PluginManager::SetInstance(const PluginID & ID, IdentInterface *instance) PluginDescriptor & PluginManager::CreatePlugin(IdentInterface *ident, PluginType type) { - PluginDescriptor plug; + // This will either create a new entry or replace an existing entry + PluginDescriptor & plug = mPlugins[ident->GetID()]; plug.SetPluginType(type); @@ -2063,24 +2001,8 @@ PluginDescriptor & PluginManager::CreatePlugin(IdentInterface *ident, PluginType plug.SetVendor(ident->GetVendor()); plug.SetVersion(ident->GetVersion()); plug.SetDescription(ident->GetDescription()); - plug.SetEnabled(false); - plug.SetDateTime(GetDateTime(ident->GetPath())); - // This will either create a new entry or replace an existing entry - mPlugins[plug.GetID()] = plug; - return mPlugins[plug.GetID()]; -} - -wxString PluginManager::GetDateTime(const wxString & path) -{ - wxFileName fn(path); - if (fn.FileExists()) - { - wxDateTime mod = fn.GetModificationTime(); - return wxString(mod.FormatISODate() + wxT(' ') + mod.FormatISOTime()); - } - - return wxEmptyString; + return plug; } bool PluginManager::GetSubgroups(const wxString & group, wxArrayString & subgroups) @@ -2358,24 +2280,149 @@ wxString PluginManager::PrivateKey(const PluginID & ID, const wxString & group, } // Sanitize the ID...not the best solution, but will suffice until this -// is converted to XML +// is converted to XML. We use base64 encoding to preserve case. wxString PluginManager::ConvertID(const PluginID & ID) { - wxString id = ID; - size_t cnt = 0; - - cnt += id.Replace(wxT("\x01"), wxT(":")); - cnt += id.Replace(wxT("\x02"), wxT("/")); - cnt += id.Replace(wxT("\x03"), wxT("\\")); - - if (cnt > 0) + if (ID.StartsWith(wxT("base64:"))) { + wxString id = ID.Mid(7); + char *buf = new char[id.Length() / 4 * 3]; + id = wxString::FromUTF8(buf, b64decode(id, buf)); + delete [] buf; return id; } - id.Replace(wxT(":"), wxT("\x01")); - id.Replace(wxT("/"), wxT("\x02")); - id.Replace(wxT("\\"), wxT("\x03")); - - return id; + const wxCharBuffer & buf = ID.ToUTF8(); + return wxT("base64:") + b64encode(buf, strlen(buf)); +} + +//////////////////////////////////////////////////////////////////////////////// +// Base64 en/decoding +// +// Original routines marked as public domain and found at: +// +// http://en.wikibooks.org/wiki/Algorithm_implementation/Miscellaneous/Base64 +// +//////////////////////////////////////////////////////////////////////////////// + +// Lookup table for encoding +const static wxChar cset[] = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); +const static char padc = wxT('='); + +wxString PluginManager::b64encode(const void *in, int len) +{ + unsigned char *p = (unsigned char *) in; + wxString out; + + unsigned long temp; + for (int i = 0; i < len / 3; i++) + { + temp = (*p++) << 16; //Convert to big endian + temp += (*p++) << 8; + temp += (*p++); + out += cset[(temp & 0x00FC0000) >> 18]; + out += cset[(temp & 0x0003F000) >> 12]; + out += cset[(temp & 0x00000FC0) >> 6]; + out += cset[(temp & 0x0000003F)]; + } + + switch (len % 3) + { + case 1: + temp = (*p++) << 16; //Convert to big endian + out += cset[(temp & 0x00FC0000) >> 18]; + out += cset[(temp & 0x0003F000) >> 12]; + out += padc; + out += padc; + break; + + case 2: + temp = (*p++) << 16; //Convert to big endian + temp += (*p++) << 8; + out += cset[(temp & 0x00FC0000) >> 18]; + out += cset[(temp & 0x0003F000) >> 12]; + out += cset[(temp & 0x00000FC0) >> 6]; + out += padc; + break; + } + + return out; +} + +int PluginManager::b64decode(wxString in, void *out) +{ + int len = in.length(); + unsigned char *p = (unsigned char *) out; + + if (len % 4) //Sanity check + { + return 0; + } + + int padding = 0; + if (len) + { + if (in[len - 1] == padc) + { + padding++; + } + + if (in[len - 2] == padc) + { + padding++; + } + } + + //const char *a = in.mb_str(); + //Setup a vector to hold the result + unsigned long temp = 0; //Holds decoded quanta + int i = 0; + while (i < len) + { + for (int quantumPosition = 0; quantumPosition < 4; quantumPosition++) + { + unsigned char c = in[i]; + temp <<= 6; + + if (c >= 0x41 && c <= 0x5A) + { + temp |= c - 0x41; + } + else if (c >= 0x61 && c <= 0x7A) + { + temp |= c - 0x47; + } + else if (c >= 0x30 && c <= 0x39) + { + temp |= c + 0x04; + } + else if (c == 0x2B) + { + temp |= 0x3E; + } + else if (c == 0x2F) + { + temp |= 0x3F; + } + else if (c == padc) + { + switch (len - i) + { + case 1: //One pad character + *p++ = (temp >> 16) & 0x000000FF; + *p++ = (temp >> 8) & 0x000000FF; + return p - (unsigned char *) out; + case 2: //Two pad characters + *p++ = (temp >> 10) & 0x000000FF; + return p - (unsigned char *) out; + } + } + i++; + } + *p++ = (temp >> 16) & 0x000000FF; + *p++ = (temp >> 8) & 0x000000FF; + *p++ = temp & 0x000000FF; + } + + return p - (unsigned char *) out; } diff --git a/src/PluginManager.h b/src/PluginManager.h index 56a67cb5e..8f6d494ac 100644 --- a/src/PluginManager.h +++ b/src/PluginManager.h @@ -25,7 +25,7 @@ /////////////////////////////////////////////////////////////////////////////// // -// PluginManager +// PluginDescriptor // /////////////////////////////////////////////////////////////////////////////// @@ -53,6 +53,7 @@ public: void SetPluginType(PluginType type); // All plugins + const wxString & GetID() const; const wxString & GetPath() const; const wxString & GetName() const; @@ -60,7 +61,6 @@ public: const wxString & GetVendor() const; const wxString & GetDescription() const; const wxString & GetProviderID() const; - const wxString & GetDateTime() const; bool IsEnabled() const; void SetID(const PluginID & ID); @@ -70,7 +70,6 @@ public: void SetVendor(const wxString & vendor); void SetDescription(const wxString & description); void SetProviderID(const PluginID & providerID); - void SetDateTime(const wxString & dateTime); void SetEnabled(bool enable); wxString GetMenuName() const; @@ -118,7 +117,6 @@ private: wxString mVendor; wxString mDescription; wxString mProviderID; - wxString mDateTime; bool mEnabled; // Effects @@ -138,10 +136,21 @@ private: wxArrayString mImporterExtensions; }; +/////////////////////////////////////////////////////////////////////////////// +// +// PluginManager +// +/////////////////////////////////////////////////////////////////////////////// + +WX_DECLARE_STRING_HASH_MAP(wxArrayString, ArrayStringMap); + //WX_DECLARE_STRING_HASH_MAP(PluginDescriptor, PluginMap); typedef std::map PluginMap; + typedef wxArrayString PluginIDList; +class PluginRegistrationDialog; + class PluginManager : public PluginManagerInterface { public: @@ -213,15 +222,9 @@ public: const PluginDescriptor *GetFirstPlugin(PluginType type); const PluginDescriptor *GetNextPlugin(PluginType type); - const PluginDescriptor *GetFirstPluginForProvider(const PluginID & ID); - const PluginDescriptor *GetNextPluginForProvider(const PluginID & ID); - const PluginDescriptor *GetFirstPluginForEffectType(EffectType type); const PluginDescriptor *GetNextPluginForEffectType(EffectType type); - const PluginDescriptor *GetFirstPluginForEffectFamily(const PluginID & ID); - const PluginDescriptor *GetNextPluginForEffectFamily(const PluginID & ID); - bool IsRegistered(const PluginID & ID); void RegisterPlugin(const wxString & type, const wxString & path); @@ -235,19 +238,17 @@ public: // const PluginID & RegisterLegacyEffectPlugin(EffectIdentInterface *effect); - void CheckForUpdates(); - private: bool Load(); void LoadGroup(const wxChar *group, PluginType type); void Save(); void SaveGroup(const wxChar *group, PluginType type); - void RemoveMissing(); + void CheckForUpdates(bool forceRescan); + void DisableMissing(); wxArrayString IsNewOrUpdated(const wxArrayString & paths); PluginDescriptor & CreatePlugin(IdentInterface *ident, PluginType type); - wxString GetDateTime(const wxString & path); bool GetSubgroups(const wxString & group, wxArrayString & subgroups); @@ -269,7 +270,12 @@ private: wxString SharedKey(const PluginID & ID, const wxString & group, const wxString & key); wxString PrivateGroup(const PluginID & ID, const wxString & group); wxString PrivateKey(const PluginID & ID, const wxString & group, const wxString & key); + + // The PluginID must be kept unique. Since the wxFileConfig class does not preserve + // case, we use base64 encoding. wxString ConvertID(const PluginID & ID); + wxString b64encode(const void *in, int len); + int b64decode(wxString in, void *out); private: static PluginManager mInstance; @@ -283,6 +289,8 @@ private: PluginMap mPlugins; PluginMap::iterator mPluginsIter; + + friend class PluginRegistrationDialog; }; #endif /* __AUDACITY_PLUGINMANAGER_H__ */ diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index a932dbb8d..4dbe931a5 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -168,7 +168,7 @@ wxString Effect::GetName() return mClient->GetName(); } - return GetEffectName(); + return GetEffectIdentifier(); } wxString Effect::GetVendor() @@ -509,6 +509,10 @@ bool Effect::Startup(EffectClientInterface *client) case EffectTypeAnalyze: flags |= INSERT_EFFECT; break; + + case EffectTypeNone: + // Nothing to set + break; } SetEffectFlags(flags); @@ -708,13 +712,17 @@ bool Effect::PromptUser(wxWindow *parent, bool forceModal) // Really need to clean this up...should get easier when // all effects get converted. - if (!res || SupportsRealtime()) + if (!res || (SupportsRealtime() && !forceModal)) { // Return false to force DoEffect() to skip processing since // this UI has either been shown modeless or there was an error. return false; } } + else + { + PromptUser(); + } return true; } @@ -1998,8 +2006,8 @@ EffectUIHost::~EffectUIHost() { if (mClient) { - mClient->CloseUI(); - mClient = NULL; + mClient->CloseUI(); + mClient = NULL; } } @@ -2096,7 +2104,14 @@ void EffectUIHost::OnCancel(wxCommandEvent & WXUNUSED(evt)) { if (IsModal()) { + SetReturnCode(false); + + Close(); + +#if !defined(__WXGTK__) EndModal(false); +#endif + return; } diff --git a/src/effects/EffectManager.cpp b/src/effects/EffectManager.cpp index 530446eb2..53459db48 100644 --- a/src/effects/EffectManager.cpp +++ b/src/effects/EffectManager.cpp @@ -224,6 +224,20 @@ EffectManager::~EffectManager() } } +void EffectManager::RegisterEffect(IdentInterface *p, Effect *f, int NewFlags) +{ + f->SetEffectID(mNumEffects++); + + if( NewFlags != 0) + { + f->SetEffectFlags( NewFlags ); + } + + PluginManager::Get().RegisterEffectPlugin(p, f); + + mEffects[f->GetID()] = f; +} + void EffectManager::RegisterEffect(Effect *f, int NewFlags) { f->SetEffectID(mNumEffects++); @@ -679,9 +693,10 @@ Effect *EffectManager::GetEffect(const PluginID & ID) const PluginID & EffectManager::GetEffectByIdentifier(const wxString & strTarget) { + static PluginID empty; if (strTarget == wxEmptyString) // set GetEffectIdentifier to wxT("") to not show an effect in Batch mode { - return PluginID(wxEmptyString); + return empty; } PluginManager & pm = PluginManager::Get(); @@ -695,7 +710,7 @@ const PluginID & EffectManager::GetEffectByIdentifier(const wxString & strTarget plug = pm.GetNextPlugin(PluginTypeEffect); } - return PluginID(wxEmptyString); + return empty;; } #ifdef EFFECT_CATEGORIES diff --git a/src/effects/EffectManager.h b/src/effects/EffectManager.h index dafbc5bf1..fa330622f 100644 --- a/src/effects/EffectManager.h +++ b/src/effects/EffectManager.h @@ -67,6 +67,7 @@ class AUDACITY_DLL_API EffectManager /** Register an effect so it will appear in the menu. */ void RegisterEffect(Effect *f, int AdditionalFlags=0); + void RegisterEffect(IdentInterface *p, Effect *f, int AdditionalFlags=0); /** Unregister all effects. */ void UnregisterEffects(); diff --git a/src/effects/EffectRack.cpp b/src/effects/EffectRack.cpp index 262bef2f7..e53568130 100644 --- a/src/effects/EffectRack.cpp +++ b/src/effects/EffectRack.cpp @@ -265,7 +265,7 @@ void EffectRack::OnClose(wxCloseEvent & evt) evt.Veto(); } -void EffectRack::OnTimer(wxTimerEvent & AUNUSED(evt)) +void EffectRack::OnTimer(wxTimerEvent & WXUNUSED(evt)) { int latency = EffectManager::Get().GetRealtimeLatency(); if (latency != mLastLatency) @@ -276,7 +276,7 @@ void EffectRack::OnTimer(wxTimerEvent & AUNUSED(evt)) } } -void EffectRack::OnApply(wxCommandEvent & AUNUSED(evt)) +void EffectRack::OnApply(wxCommandEvent & WXUNUSED(evt)) { AudacityProject *project = GetActiveProject(); diff --git a/src/effects/LoadEffects.cpp b/src/effects/LoadEffects.cpp index 5c150c178..ca747b730 100644 --- a/src/effects/LoadEffects.cpp +++ b/src/effects/LoadEffects.cpp @@ -57,18 +57,10 @@ #include "ChangeTempo.h" #endif -#ifdef USE_NYQUIST -#include "nyquist/LoadNyquist.h" -#endif - #ifdef USE_AUDIO_UNITS #include "audiounits/LoadAudioUnits.h" #endif -#ifdef USE_LV2 -#include "lv2/LoadLV2.h" -#endif - #ifdef USE_VAMP #include "vamp/LoadVamp.h" #endif @@ -279,42 +271,15 @@ void LoadEffects() // Analyze menu em.RegisterEffect(new EffectFindClipping()); -#ifdef USE_NYQUIST - if (gPrefs->Read(wxT("/Nyquist/Enable"), true)) { - LoadNyquistPlugins(); - } -#endif - -#ifdef USE_LV2 - if (gPrefs->Read(wxT("/LV2/Enable"), true)) { - LoadLV2Plugins(); - } -#endif - #ifdef USE_AUDIO_UNITS if (gPrefs->Read(wxT("/AudioUnits/Enable"), true)) { LoadAudioUnits(); } #endif - -#ifdef USE_VAMP - if (gPrefs->Read(wxT("/VAMP/Enable"), true)) { - LoadVampPlugins(); - } -#endif - } void UnloadEffects() { EffectManager::Get().UnregisterEffects(); - -#ifdef USE_LV2 - UnloadLV2Plugins(); -#endif - -#ifdef USE_VAMP - UnloadVampPlugins(); -#endif } diff --git a/src/effects/VST/VSTEffect.cpp b/src/effects/VST/VSTEffect.cpp index 6f9d5e842..58652662e 100644 --- a/src/effects/VST/VSTEffect.cpp +++ b/src/effects/VST/VSTEffect.cpp @@ -551,6 +551,12 @@ bool VSTEffectsModule::RegisterPlugin(PluginManagerInterface & pm, const wxStrin return valid; } +bool VSTEffectsModule::IsPluginValid(const PluginID & ID, + const wxString & path) +{ + return wxFileName::FileExists(path); +} + IdentInterface *VSTEffectsModule::CreateInstance(const PluginID & WXUNUSED(ID), const wxString & path) { diff --git a/src/effects/VST/VSTEffect.h b/src/effects/VST/VSTEffect.h index 706acbdea..7d0766440 100644 --- a/src/effects/VST/VSTEffect.h +++ b/src/effects/VST/VSTEffect.h @@ -397,6 +397,8 @@ public: virtual wxArrayString FindPlugins(PluginManagerInterface & pm); virtual bool RegisterPlugin(PluginManagerInterface & pm, const wxString & path); + virtual bool IsPluginValid(const PluginID & ID, const wxString & path); + virtual IdentInterface *CreateInstance(const PluginID & ID, const wxString & path); virtual void DeleteInstance(IdentInterface *instance); diff --git a/src/effects/ladspa/LadspaEffect.cpp b/src/effects/ladspa/LadspaEffect.cpp index c37f60ddd..39a6b276d 100644 --- a/src/effects/ladspa/LadspaEffect.cpp +++ b/src/effects/ladspa/LadspaEffect.cpp @@ -346,6 +346,12 @@ bool LadspaEffectsModule::RegisterPlugin(PluginManagerInterface & pm, const wxSt return index > 0; } +bool LadspaEffectsModule::IsPluginValid(const PluginID & ID, + const wxString & path) +{ + return wxFileName::FileExists(path); +} + IdentInterface *LadspaEffectsModule::CreateInstance(const PluginID & ID, const wxString & path) { @@ -1222,7 +1228,6 @@ bool LadspaEffect::PopulateUI(wxWindow *parent) gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5); wxString fieldText; - LADSPA_PortRangeHint hint = mData->PortRangeHints[p]; mFields[p] = new wxTextCtrl(mParent, wxID_ANY, fieldText, @@ -1558,24 +1563,6 @@ void LadspaEffect::RefreshControls(bool outputOnly) continue; } - wxString bound; - double lower = -FLT_MAX; - double upper = FLT_MAX; - bool haslo = false; - bool hashi = false; - - if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)) - { - lower = hint.LowerBound; - haslo = true; - } - - if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor)) - { - upper = hint.UpperBound; - hashi = true; - } - if (LADSPA_IS_HINT_INTEGER(hint.HintDescriptor) || forceint) { fieldText.Printf(wxT("%d"), (int)(mInputControls[p] + 0.5)); diff --git a/src/effects/ladspa/LadspaEffect.h b/src/effects/ladspa/LadspaEffect.h index c710a930e..b61fed33c 100644 --- a/src/effects/ladspa/LadspaEffect.h +++ b/src/effects/ladspa/LadspaEffect.h @@ -244,6 +244,8 @@ public: virtual wxArrayString FindPlugins(PluginManagerInterface & pm); virtual bool RegisterPlugin(PluginManagerInterface & pm, const wxString & path); + virtual bool IsPluginValid(const PluginID & ID, const wxString & path); + virtual IdentInterface *CreateInstance(const PluginID & ID, const wxString & path); virtual void DeleteInstance(IdentInterface *instance); diff --git a/src/effects/lv2/LV2Effect.cpp b/src/effects/lv2/LV2Effect.cpp index 0b1809a1b..c74b0ede3 100644 --- a/src/effects/lv2/LV2Effect.cpp +++ b/src/effects/lv2/LV2Effect.cpp @@ -53,6 +53,12 @@ #include +/////////////////////////////////////////////////////////////////////////////// +// +// LV2Effect +// +/////////////////////////////////////////////////////////////////////////////// + WX_DEFINE_OBJARRAY(LV2PortArray); LV2Effect::LV2Effect(const LilvPlugin *data, @@ -292,6 +298,92 @@ LV2Effect::~LV2Effect() } } +// ============================================================================ +// IdentInterface implementation +// ============================================================================ + +wxString LV2Effect::GetID() +{ + return GetString(lilv_plugin_get_uri(mData)); +} + +wxString LV2Effect::GetPath() +{ + return GetString(lilv_plugin_get_bundle_uri(mData)); +} + +wxString LV2Effect::GetName() +{ + return pluginName; +} + +wxString LV2Effect::GetVendor() +{ + wxString vendor = GetString(lilv_plugin_get_author_name(mData), true); + + if (vendor.IsEmpty()) + { + vendor = wxT("N/A"); + } + + return vendor; +} + +wxString LV2Effect::GetVersion() +{ + return wxT("N/A"); +} + +wxString LV2Effect::GetDescription() +{ + return wxT("N/A"); +} + +// ============================================================================ +// EffectIdentInterface implementation +// ============================================================================ + +EffectType LV2Effect::GetType() +{ + // For now, relegate to Effect() + return Effect::GetType(); +} + +wxString LV2Effect::GetFamily() +{ + return LV2EFFECTS_FAMILY; +} + +bool LV2Effect::IsInteractive() +{ + // For now, relegate to Effect() + return Effect::IsInteractive(); +} + +bool LV2Effect::IsDefault() +{ + return false; +} + +bool LV2Effect::IsLegacy() +{ + return true; +} + +bool LV2Effect::SupportsRealtime() +{ + return false; +} + +bool LV2Effect::SupportsAutomation() +{ + return true; +} + +// ============================================================================ +// Effect Implementation +// ============================================================================ + wxString LV2Effect::GetEffectName() { if (mControlInputs.GetCount() > 0) @@ -714,6 +806,9 @@ void LV2Effect::End() } } +// ============================================================================ +// LV2Effect Implementation +// ============================================================================ bool LV2Effect::IsValid() { @@ -1246,9 +1341,11 @@ LV2EffectDialog::LV2EffectDialog(LV2Effect *effect, LV2EffectDialog::~LV2EffectDialog() { + delete [] mToggles; delete [] mSliders; delete [] mFields; delete [] mLabels; + delete [] mEnums; } void LV2EffectDialog::OnCheckBox(wxCommandEvent &event) diff --git a/src/effects/lv2/LV2Effect.h b/src/effects/lv2/LV2Effect.h index 82e7bad2a..5d667d1c4 100644 --- a/src/effects/lv2/LV2Effect.h +++ b/src/effects/lv2/LV2Effect.h @@ -24,6 +24,8 @@ class wxCheckBox; #include "../Effect.h" #include "LV2PortGroup.h" +#define LV2EFFECTS_VERSION wxT("1.0.0.0"); +#define LV2EFFECTS_FAMILY L"LV2" /** A structure that contains information about a single LV2 plugin port. */ struct LV2Port @@ -66,6 +68,27 @@ public: const std::set & categories = std::set()); virtual ~LV2Effect(); + // IdentInterface implementation + + virtual PluginID GetID(); + virtual wxString GetPath(); + virtual wxString GetName(); + virtual wxString GetVendor(); + virtual wxString GetVersion(); + virtual wxString GetDescription(); + + // EffectIdentInterface implementation + + virtual EffectType GetType(); + virtual wxString GetFamily(); + virtual bool IsInteractive(); + virtual bool IsDefault(); + virtual bool IsLegacy(); + virtual bool SupportsRealtime(); + virtual bool SupportsAutomation(); + + // Effect implementation + /** Get the name of the effect. */ virtual wxString GetEffectName(); diff --git a/src/effects/lv2/LoadLV2.cpp b/src/effects/lv2/LoadLV2.cpp index a1c971e59..4dbc3038c 100644 --- a/src/effects/lv2/LoadLV2.cpp +++ b/src/effects/lv2/LoadLV2.cpp @@ -41,6 +41,32 @@ Functions that find and load all LV2 plugins on the system. #include "LoadLV2.h" +// ============================================================================ +// Module registration entry point +// +// This is the symbol that Audacity looks for when the module is built as a +// dynamic library. +// +// When the module is builtin to Audacity, we use the same function, but it is +// declared static so as not to clash with other builtin modules. +// ============================================================================ +DECLARE_MODULE_ENTRY(AudacityModule) +{ + // Create and register the importer + return new LV2EffectsModule(moduleManager, path); +} + +// ============================================================================ +// Register this as a builtin module +// ============================================================================ +DECLARE_BUILTIN_MODULE(LV2sEffectBuiltin); + +/////////////////////////////////////////////////////////////////////////////// +// +// LV2EffectsModule +// +/////////////////////////////////////////////////////////////////////////////// + LilvWorld *gWorld = NULL; // This is the URI Map Feature object. It is required for loading synth @@ -96,24 +122,67 @@ LilvNode *gName; LilvNode *gPortGroup; LilvNode *gSubGroupOf; -void LoadLV2Plugins() +LV2EffectsModule::LV2EffectsModule(ModuleManagerInterface *moduleManager, + const wxString *path) { - - EffectManager& em = EffectManager::Get(); - - // If gWorld isn't 0 we have already initialised Lilv - unload all plugins - // and initialise again. - if (gWorld) + mModMan = moduleManager; + if (path) { - UnloadLV2Plugins(); + mPath = *path; } +} +LV2EffectsModule::~LV2EffectsModule() +{ +} + +// ============================================================================ +// IdentInterface implementation +// ============================================================================ + +wxString LV2EffectsModule::GetID() +{ + // Can be anything, but this is a v4 UUID + return wxT("5d03b6ad-ca64-41b2-a3f2-785ff5b279d9"); +} + +wxString LV2EffectsModule::GetPath() +{ + return mPath; +} + +wxString LV2EffectsModule::GetName() +{ + return _("LV2 Effects Module"); +} + +wxString LV2EffectsModule::GetVendor() +{ + return _("The Audacity Team"); +} + +wxString LV2EffectsModule::GetVersion() +{ + // This "may" be different if this were to be maintained as a separate DLL + return LV2EFFECTS_VERSION; +} + +wxString LV2EffectsModule::GetDescription() +{ + return _("Provides LV2 Effects support to Audacity"); +} + +// ============================================================================ +// ModuleInterface implementation +// ============================================================================ + +bool LV2EffectsModule::Initialize() +{ // Try to initialise Lilv, or return. gWorld = lilv_world_new(); if (!gWorld) { - wxLogMessage(wxT("Could not initialise lilv!")); - return; + return false; } gAudioPortClass = lilv_new_uri(gWorld, LV2_CORE__AudioPort); @@ -133,49 +202,10 @@ void LoadLV2Plugins() lilv_world_load_all(gWorld); -#ifdef EFFECT_CATEGORIES - - // Add all LV2 categories and their relationships - LilvPluginClasses classes = Lilv_world_get_plugin_classes(gWorld); - for (unsigned index = 0; index < Lilv_plugin_classes_size(classes);++index){ - LilvPluginClass c = Lilv_plugin_classes_get_at(classes, index); - em.AddCategory(wxString::FromUTF8(lilv_node_as_uri(Lilv_plugin_class_get_uri(c))), - wxString::FromUTF8(lilv_node_as_string(Lilv_plugin_class_get_label(c)))); - } - for (unsigned index = 0; index < Lilv_plugin_classes_size(classes);++index){ - LilvPluginClass c = Lilv_plugin_classes_get_at(classes, index); - LilvPluginClasses ch = Lilv_plugin_class_get_children(c); - EffectCategory* pCat = em.LookupCategory(wxString::FromUTF8(lilv_node_as_uri(Lilv_plugin_class_get_uri(c)))); - for (unsigned j = 0; j < Lilv_plugin_classes_size(ch); ++j) { - EffectCategory* chCat = em.LookupCategory(wxString::FromUTF8(lilv_node_as_uri(Lilv_plugin_class_get_uri(Lilv_plugin_classes_get_at(ch, j))))); - if (chCat && pCat) { - em.AddCategoryParent(chCat, pCat); - } - } - } - -#endif - - // Retrieve data about all plugins - const LilvPlugins *plugs = lilv_world_get_all_plugins(gWorld); - - // Iterate over all plugins and register them with the EffectManager - LILV_FOREACH(plugins, i, plugs) - { - const LilvPlugin *plug = lilv_plugins_get(plugs, i); - std::set cats; - cats.insert(wxString::FromUTF8(lilv_node_as_uri(lilv_plugin_class_get_uri(lilv_plugin_get_class(plug))))); - LV2Effect *effect = new LV2Effect(plug, cats); - if (effect->IsValid()) - em.RegisterEffect(effect); - else - delete effect; - //std::cerr<<"Loaded LV2 \""< cats; + cats.insert(wxString::FromUTF8(lilv_node_as_uri(lilv_plugin_class_get_uri(lilv_plugin_get_class(plug))))); + LV2Effect *effect = new LV2Effect(plug, cats); + if (effect->IsValid()) + { + em.RegisterEffect(this, effect); + } + else + { + delete effect; + } + } + + return true; +} + +wxArrayString LV2EffectsModule::FindPlugins(PluginManagerInterface & pm) +{ + // Nothing to do here yet + return false; +} + +bool LV2EffectsModule::RegisterPlugin(PluginManagerInterface & pm, const wxString & path) +{ + // Nothing to do here yet + return false; +} + +bool LV2EffectsModule::IsPluginValid(const PluginID & ID, + const wxString & path) +{ + LilvNode *uri = lilv_new_uri(gWorld, path.ToUTF8()); + const LilvPlugin *plugin = lilv_plugins_get_by_uri(lilv_world_get_all_plugins(gWorld), uri); + lilv_node_free(uri); + + return plugin != NULL; +} + +IdentInterface *LV2EffectsModule::CreateInstance(const PluginID & ID, + const wxString & path) +{ + // Nothing to do here yet since we are autoregistering (and creating legacy + // effects anyway). + return NULL; +} + +void LV2EffectsModule::DeleteInstance(IdentInterface *instance) +{ + // Nothing to do here yet +} + +// ============================================================================ +// LV2EffectsModule implementation +// ============================================================================ + #endif diff --git a/src/effects/lv2/LoadLV2.h b/src/effects/lv2/LoadLV2.h index 43de36e6d..e21eff141 100644 --- a/src/effects/lv2/LoadLV2.h +++ b/src/effects/lv2/LoadLV2.h @@ -11,6 +11,52 @@ #include +#include "audacity/ModuleInterface.h" +#include "audacity/EffectInterface.h" +#include "audacity/PluginInterface.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// LV2EffectsModule +// +/////////////////////////////////////////////////////////////////////////////// + +class LV2EffectsModule : public ModuleInterface +{ +public: + LV2EffectsModule(ModuleManagerInterface *moduleManager, const wxString *path); + virtual ~LV2EffectsModule(); + + // IdentInterface implementatino + + virtual wxString GetID(); + virtual wxString GetPath(); + virtual wxString GetName(); + virtual wxString GetVendor(); + virtual wxString GetVersion(); + virtual wxString GetDescription(); + + // ModuleInterface implementation + + virtual bool Initialize(); + virtual void Terminate(); + + virtual bool AutoRegisterPlugins(PluginManagerInterface & pm); + virtual wxArrayString FindPlugins(PluginManagerInterface & pm); + virtual bool RegisterPlugin(PluginManagerInterface & pm, const wxString & path); + + virtual bool IsPluginValid(const PluginID & ID, const wxString & path); + + virtual IdentInterface *CreateInstance(const PluginID & ID, const wxString & path); + virtual void DeleteInstance(IdentInterface *instance); + + // LV2EffectModule implementation + +private: + ModuleManagerInterface *mModMan; + wxString mPath; +}; + extern LilvWorld *gWorld; // This is the LV2 Feature array. It is passed to every LV2 plugin on diff --git a/src/effects/nyquist/LoadNyquist.cpp b/src/effects/nyquist/LoadNyquist.cpp index 22d7eb70a..fb5bee75d 100644 --- a/src/effects/nyquist/LoadNyquist.cpp +++ b/src/effects/nyquist/LoadNyquist.cpp @@ -8,43 +8,174 @@ **********************************************************************/ -#include -#include -#include -#include -#include - -#include "../../Audacity.h" -#include "../../AudacityApp.h" #include "../EffectManager.h" #include "Nyquist.h" #include "LoadNyquist.h" -void LoadNyquistEffect(wxString fname) +// ============================================================================ +// 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) { - EffectNyquist *effect = new EffectNyquist(fname); - if (effect->LoadedNyFile()) - EffectManager::Get().RegisterEffect(effect); - else - delete effect; + // Create and register the importer + return new NyquistEffectsModule(moduleManager, path); } -void LoadNyquistPlugins() +// ============================================================================ +// Register this as a builtin module +// ============================================================================ +DECLARE_BUILTIN_MODULE(NyquistsEffectBuiltin); + +/////////////////////////////////////////////////////////////////////////////// +// +// NyquistEffectsModule +// +/////////////////////////////////////////////////////////////////////////////// + +NyquistEffectsModule::NyquistEffectsModule(ModuleManagerInterface *moduleManager, + const wxString *path) { + mModMan = moduleManager; + if (path) + { + mPath = *path; + } +} + +NyquistEffectsModule::~NyquistEffectsModule() +{ + mPath.Clear(); +} + +// ============================================================================ +// IdentInterface implementation +// ============================================================================ + +wxString NyquistEffectsModule::GetID() +{ + // Can be anything, but this is a v4 UUID + return wxT("42a58b1e-cc24-4b55-861a-4b2008a7cf7b"); +} + +wxString NyquistEffectsModule::GetPath() +{ + return mPath; +} + +wxString NyquistEffectsModule::GetName() +{ + return _("Nyquist Effects Module"); +} + +wxString NyquistEffectsModule::GetVendor() +{ + return _("The Audacity Team"); +} + +wxString NyquistEffectsModule::GetVersion() +{ + // This "may" be different if this were to be maintained as a separate DLL + return NYQUISTEFFECTS_VERSION; +} + +wxString NyquistEffectsModule::GetDescription() +{ + return _("Provides Nyquist Effects support to Audacity"); +} + +// ============================================================================ +// ModuleInterface implementation +// ============================================================================ + +bool NyquistEffectsModule::Initialize() +{ + // Nothing to do here + return true; +} + +void NyquistEffectsModule::Terminate() +{ + // Nothing to do here + return; +} + +bool NyquistEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm) +{ + // For Nyquist, we autoregister plugins at this time using the legacy + // interface. This will change eventually. + wxArrayString pathList = EffectNyquist::GetNyquistSearchPath(); wxArrayString files; - unsigned int i; - // Create one "interactive Nyquist" + // Create one "interactive Nyquist" effect EffectNyquist *effect = new EffectNyquist(wxT("")); - EffectManager::Get().RegisterEffect(effect); + EffectManager::Get().RegisterEffect(this, effect); // Load .ny plug-ins - wxGetApp().FindFilesInPathList(wxT("*.ny"), pathList, files); + pm.FindFilesInPathList(wxT("*.ny"), pathList, files); #ifdef __WXGTK__ - wxGetApp().FindFilesInPathList(wxT("*.NY"), pathList, files); // Ed's fix for bug 179 + pm.FindFilesInPathList(wxT("*.NY"), pathList, files); // Ed's fix for bug 179 #endif - for(i=0; iLoadedNyFile()) + { + EffectManager::Get().RegisterEffect(this, effect); + } + else + { + delete effect; + } + } + + return true; } + +wxArrayString NyquistEffectsModule::FindPlugins(PluginManagerInterface & pm) +{ + // Nothing to do here yet + return wxArrayString(); +} + +bool NyquistEffectsModule::RegisterPlugin(PluginManagerInterface & pm, const wxString & path) +{ + // Nothing to do here yet + return false; +} + +bool NyquistEffectsModule::IsPluginValid(const PluginID & ID, + const wxString & path) +{ + if (ID == wxT("nyquist prompt")) + { + return true; + } + + return wxFileName::FileExists(path); +} + +IdentInterface *NyquistEffectsModule::CreateInstance(const PluginID & ID, + const wxString & path) +{ + // Nothing to do here yet since we are autoregistering (and creating legacy + // effects anyway). + return NULL; +} + +void NyquistEffectsModule::DeleteInstance(IdentInterface *instance) +{ + // Nothing to do here yet +} + +// ============================================================================ +// NyquistEffectsModule implementation +// ============================================================================ + diff --git a/src/effects/nyquist/LoadNyquist.h b/src/effects/nyquist/LoadNyquist.h index aef23e2a1..3d653bda9 100644 --- a/src/effects/nyquist/LoadNyquist.h +++ b/src/effects/nyquist/LoadNyquist.h @@ -8,5 +8,48 @@ **********************************************************************/ -void LoadNyquistPlugins(); +#include "audacity/ModuleInterface.h" +#include "audacity/EffectInterface.h" +#include "audacity/PluginInterface.h" +/////////////////////////////////////////////////////////////////////////////// +// +// NyquistEffectsModule +// +/////////////////////////////////////////////////////////////////////////////// + +class NyquistEffectsModule : public ModuleInterface +{ +public: + NyquistEffectsModule(ModuleManagerInterface *moduleManager, const wxString *path); + virtual ~NyquistEffectsModule(); + + // IdentInterface implementatino + + virtual wxString GetID(); + virtual wxString GetPath(); + virtual wxString GetName(); + virtual wxString GetVendor(); + virtual wxString GetVersion(); + virtual wxString GetDescription(); + + // ModuleInterface implementation + + virtual bool Initialize(); + virtual void Terminate(); + + virtual bool AutoRegisterPlugins(PluginManagerInterface & pm); + virtual wxArrayString FindPlugins(PluginManagerInterface & pm); + virtual bool RegisterPlugin(PluginManagerInterface & pm, const wxString & path); + + virtual bool IsPluginValid(const PluginID & ID, const wxString & path); + + virtual IdentInterface *CreateInstance(const PluginID & ID, const wxString & path); + virtual void DeleteInstance(IdentInterface *instance); + + // NyquistEffectModule implementation + +private: + ModuleManagerInterface *mModMan; + wxString mPath; +}; diff --git a/src/effects/nyquist/Nyquist.cpp b/src/effects/nyquist/Nyquist.cpp index 548745d57..a1596cd8f 100644 --- a/src/effects/nyquist/Nyquist.cpp +++ b/src/effects/nyquist/Nyquist.cpp @@ -74,6 +74,15 @@ effects from this one class. #include #include + +/////////////////////////////////////////////////////////////////////////////// +// +// NyquistEffect +// +/////////////////////////////////////////////////////////////////////////////// + +#define UNINITIALIZED_CONTROL ((double)99999999.99) + WX_DEFINE_OBJARRAY(NyqControlArray); EffectNyquist::EffectNyquist(wxString fName) @@ -88,6 +97,8 @@ EffectNyquist::EffectNyquist(wxString fName) mDebug = false; mIsSal = false; mOK = false; + mAuthor = wxT("N/A"); + mCopyright = wxT("N/A"); mVersion = 4; @@ -120,331 +131,103 @@ EffectNyquist::EffectNyquist(wxString fName) EffectNyquist::~EffectNyquist() { + nyx_set_xlisp_path(NULL); } -wxString EffectNyquist::NyquistToWxString(const char *nyqString) +// ============================================================================ +// IdentInterface implementation +// ============================================================================ + +wxString EffectNyquist::GetID() { - wxString str(nyqString, wxConvUTF8); - if (nyqString != NULL && nyqString[0] && str.IsEmpty()) { - // invalid UTF-8 string, convert as Latin-1 - str = _("[Warning: Nyquist returned invalid UTF-8 string, converted here as Latin-1]"); - str += LAT1CTOWX(nyqString); - } - return str; + return GetPath(); } - -void EffectNyquist::Break() +wxString EffectNyquist::GetPath() { - mBreak = true; + if (mFileName.GetFullPath().IsEmpty()) + { + return wxT("nyquist prompt"); + } + + return mFileName.GetFullPath(); } -void EffectNyquist::Continue() +wxString EffectNyquist::GetName() { - mCont = true; + return mName; } -void EffectNyquist::Stop() +wxString EffectNyquist::GetVendor() { - mStop = true; + if (GetID() == wxT("nyquist prompt")) + { + return wxT("Audacity"); + } + + return mAuthor; } -#define UNINITIALIZED_CONTROL ((double)99999999.99) - -wxString EffectNyquist::UnQuote(wxString s) +wxString EffectNyquist::GetVersion() { - wxString out; - int len = s.Length(); - - if (len >= 2 && s[0] == wxT('\"') && s[len - 1] == wxT('\"')) { - return s.Mid(1, len - 2); - } - - return s; + return wxT("N/A"); } -double EffectNyquist::GetCtrlValue(wxString s) +wxString EffectNyquist::GetDescription() { - if (s == wxT("rate")) { - TrackListOfKindIterator iter(Track::Wave, mTracks); - return ((WaveTrack *)iter.First())->GetRate(); - } - - return Internat::CompatibleToDouble(s); + return mCopyright; } -void EffectNyquist::Parse(wxString line) +// ============================================================================ +// EffectIdentInterface implementation +// ============================================================================ + +EffectType EffectNyquist::GetType() { - wxArrayString tokens; - - int i; - int len = line.Length(); - bool sl = false; - bool q = false; - wxString tok = wxT(""); - - for (i = 1; i < len; i++) { - wxChar c = line[i]; - - if (c == wxT('\\')) { - sl = true; - } - else if (c == wxT('"')) { - q = !q; - } - else { - if ((!q && !sl && c == wxT(' ')) || c == wxT('\t')) { - tokens.Add(tok); - tok = wxT(""); - } - else if (sl && c == wxT('n')) { - tok += wxT('\n'); - } - else { - tok += c; - } - - sl = false; - } - } - - if (tok != wxT("")) { - tokens.Add(tok); - } - - len = tokens.GetCount(); - if (len < 1) { - return; - } - - if (len == 2 && tokens[0] == wxT("nyquist") && tokens[1] == wxT("plug-in")) { - mOK = true; - return; - } - - if (len >= 2 && tokens[0] == wxT("type")) { - if (tokens[1] == wxT("process")) { - SetEffectFlags(PROCESS_EFFECT | PLUGIN_EFFECT); - } - else if (tokens[1] == wxT("generate")) { - SetEffectFlags(INSERT_EFFECT | PLUGIN_EFFECT); - } - else if (tokens[1] == wxT("analyze")) { - SetEffectFlags(ANALYZE_EFFECT | PLUGIN_EFFECT); - } - return; - } - - if (len == 2 && tokens[0] == wxT("codetype")) { - if (tokens[1] == wxT("lisp")) { - mIsSal = false; - } - else if (tokens[1] == wxT("sal")) { - mIsSal = true; - } - return; - } - - if (len >= 2 && tokens[0] == wxT("debugflags")) { - for (int i = 1; i < len; i++) { - // Note: "trace" and "notrace" are overridden by "Debug" and "OK" - // buttons if the plug-in generates a dialog box by using controls - if (tokens[i] == wxT("trace")) { - mDebug = true; - } - else if (tokens[i] == wxT("notrace")) { - mDebug = false; - } - else if (tokens[i] == wxT("compiler")) { - mCompiler = true; - } - else if (tokens[i] == wxT("nocompiler")) { - mCompiler = false; - } - } - return; - } - - // We support versions 1, 2 and 3 - // (Version 2 added support for string parameters.) - // (Version 3 added support for choice parameters.) - // (Version 4 added support for project/track/selection information.) - if (len >= 2 && tokens[0] == wxT("version")) { - long v; - tokens[1].ToLong(&v); - if (v < 1 && v > 4) { - // This is an unsupported plug-in version - mOK = false; - return; - } - mVersion = (int) v; - } - - if (len >= 2 && tokens[0] == wxT("name")) { - mName = UnQuote(tokens[1]); - return; - } - - if (len >= 2 && tokens[0] == wxT("action")) { - mAction = UnQuote(tokens[1]); - return; - } - - if (len >= 2 && tokens[0] == wxT("info")) { - mInfo = UnQuote(tokens[1]); - return; - } - - if (len >= 2 && tokens[0] == wxT("preview")) { - if (tokens[1] == wxT("enabled") || tokens[1] == wxT("true")) { - mEnablePreview = true; - } - return; - } - - if (len >= 6 && tokens[0] == wxT("control")) { - NyqControl ctrl; - - ctrl.var = tokens[1]; - ctrl.name = tokens[2]; - ctrl.label = tokens[4]; - ctrl.valStr = tokens[5]; - - if (tokens[3] == wxT("string")) { - ctrl.type = NYQ_CTRL_STRING; - } - else if (tokens[ 3 ] == wxT("choice")) { - ctrl.type = NYQ_CTRL_CHOICE; - } - else { - if (len < 8) { - return; - } - - if ((tokens[3] == wxT("real")) || - (tokens[3] == wxT("float"))) // undocumented, but useful, alternative - ctrl.type = NYQ_CTRL_REAL; - else if (tokens[3] == wxT("int")) - ctrl.type = NYQ_CTRL_INT; - else - { - wxString str; - str.Printf(_("Bad Nyquist 'control' type specification: '%s' in plugin file '%s'.\nControl not created."), - tokens[3].c_str(), mFileName.GetFullPath().c_str()); - - // Too disturbing to show alert before Audacity frame is up. - // wxMessageBox(str, wxT("Nyquist Warning"), wxOK | wxICON_EXCLAMATION); - - // Note that the AudacityApp's mLogger has not yet been created, - // so this brings up an alert box, but after the Audacity frame is up. - wxLogWarning(str); - return; - } - - ctrl.lowStr = tokens[6]; - ctrl.highStr = tokens[7]; - } - - ctrl.val = UNINITIALIZED_CONTROL; - - if( mPresetNames.Index( ctrl.var ) == wxNOT_FOUND ) - { - mControls.Add(ctrl); - } - } - - if (len >= 2 && tokens[0] == wxT("categories")) { - for (size_t i = 1; i < tokens.GetCount(); ++i) { - mCategories.Add(tokens[i]); - } - } + // For now, relegate to Effect() + return Effect::GetType(); } -void EffectNyquist::ParseFile() +wxString EffectNyquist::GetFamily() { - wxTextFile f(mFileName.GetFullPath()); - if (!f.Open()) - return; - - mCmd = wxT(""); - SetEffectFlags(PROCESS_EFFECT | PLUGIN_EFFECT); - mOK = false; - mEnablePreview = false; - mIsSal = false; - mControls.Clear(); - mDebug = false; - - int i; - int len = f.GetLineCount(); - wxString line; - for (i = 0; i < len; i++) { - line = f[i]; - if (line.Length() > 1 && line[0] == wxT(';')) { - Parse(line); - } - // preserve comments so that SAL effects compile with proper line numbers - mCmd += line + wxT("\n"); - } + return NYQUISTEFFECTS_FAMILY; } -void EffectNyquist::SetCommand(wxString cmd) +bool EffectNyquist::IsInteractive() { - mExternal = true; - mInteractive = false; - mCmd = wxT(""); - SetEffectFlags(INSERT_EFFECT | HIDDEN_EFFECT); - mOK = false; - mIsSal = false; - mControls.Clear(); - - wxStringTokenizer lines(cmd, wxT("\n")); - while (lines.HasMoreTokens()) { - wxString line = lines.GetNextToken(); - - if (line.Length() > 1 && line[0] == wxT(';')) { - Parse(line); - } - else { - mCmd += line + wxT("\n"); - } - } + // For now, relegate to Effect() + return Effect::IsInteractive(); } -bool EffectNyquist::SetXlispPath() +bool EffectNyquist::IsDefault() { - wxString fname; - - fname = mXlispPath + wxFILE_SEP_PATH + wxT("nyinit.lsp"); - if (!(::wxFileExists(fname))) { - mXlispPath = wxT(""); + if (GetID() == wxT("nyquist prompt")) + { + return true; } - if (mXlispPath == wxT("")) { - wxArrayString audacityPathList = wxGetApp().audacityPathList; - wxArrayString pathList; - wxArrayString files; - unsigned int i; - - for (i = 0; i < audacityPathList.GetCount(); i++) { - wxString prefix = audacityPathList[i] + wxFILE_SEP_PATH; - wxGetApp().AddUniquePathToPathList(prefix + wxT("nyquist"), - pathList); - } - - wxGetApp().FindFilesInPathList(wxT("nyquist.lsp"), pathList, files); - - if (files.GetCount() > 0) { - mXlispPath = ::wxPathOnly(files[0]); - } - } - - /* set_xlisp_path doesn't handle fn_Str() in Unicode build. May or may not actually work. */ - nyx_set_xlisp_path(mXlispPath.mb_str()); - - fname = mXlispPath + wxFILE_SEP_PATH + wxT("nyinit.lsp"); - return ::wxFileExists(fname); + return false; } +bool EffectNyquist::IsLegacy() +{ + return true; +} + +bool EffectNyquist::SupportsRealtime() +{ + return false; +} + +bool EffectNyquist::SupportsAutomation() +{ + return true; +} + +// ============================================================================ +// Effect Implementation +// ============================================================================ + bool EffectNyquist::SupportsChains() { return (GetEffectFlags() & PROCESS_EFFECT) != 0; @@ -1270,6 +1053,341 @@ bool EffectNyquist::ProcessOne() return true; } +// ============================================================================ +// EffectNyquist Implementation +// ============================================================================ + +wxString EffectNyquist::NyquistToWxString(const char *nyqString) +{ + wxString str(nyqString, wxConvUTF8); + if (nyqString != NULL && nyqString[0] && str.IsEmpty()) { + // invalid UTF-8 string, convert as Latin-1 + str = _("[Warning: Nyquist returned invalid UTF-8 string, converted here as Latin-1]"); + str += LAT1CTOWX(nyqString); + } + return str; +} + + +void EffectNyquist::Break() +{ + mBreak = true; +} + +void EffectNyquist::Continue() +{ + mCont = true; +} + +void EffectNyquist::Stop() +{ + mStop = true; +} + +wxString EffectNyquist::UnQuote(wxString s) +{ + wxString out; + int len = s.Length(); + + if (len >= 2 && s[0] == wxT('\"') && s[len - 1] == wxT('\"')) { + return s.Mid(1, len - 2); + } + + return s; +} + +double EffectNyquist::GetCtrlValue(wxString s) +{ + if (s == wxT("rate")) { + TrackListOfKindIterator iter(Track::Wave, mTracks); + return ((WaveTrack *)iter.First())->GetRate(); + } + + return Internat::CompatibleToDouble(s); +} + +void EffectNyquist::Parse(wxString line) +{ + wxArrayString tokens; + + int i; + int len = line.Length(); + bool sl = false; + bool q = false; + wxString tok = wxT(""); + + for (i = 1; i < len; i++) { + wxChar c = line[i]; + + if (c == wxT('\\')) { + sl = true; + } + else if (c == wxT('"')) { + q = !q; + } + else { + if ((!q && !sl && c == wxT(' ')) || c == wxT('\t')) { + tokens.Add(tok); + tok = wxT(""); + } + else if (sl && c == wxT('n')) { + tok += wxT('\n'); + } + else { + tok += c; + } + + sl = false; + } + } + + if (tok != wxT("")) { + tokens.Add(tok); + } + + len = tokens.GetCount(); + if (len < 1) { + return; + } + + if (len == 2 && tokens[0] == wxT("nyquist") && tokens[1] == wxT("plug-in")) { + mOK = true; + return; + } + + if (len >= 2 && tokens[0] == wxT("type")) { + if (tokens[1] == wxT("process")) { + SetEffectFlags(PROCESS_EFFECT | PLUGIN_EFFECT); + } + else if (tokens[1] == wxT("generate")) { + SetEffectFlags(INSERT_EFFECT | PLUGIN_EFFECT); + } + else if (tokens[1] == wxT("analyze")) { + SetEffectFlags(ANALYZE_EFFECT | PLUGIN_EFFECT); + } + return; + } + + if (len == 2 && tokens[0] == wxT("codetype")) { + if (tokens[1] == wxT("lisp")) { + mIsSal = false; + } + else if (tokens[1] == wxT("sal")) { + mIsSal = true; + } + return; + } + + if (len >= 2 && tokens[0] == wxT("debugflags")) { + for (int i = 1; i < len; i++) { + // Note: "trace" and "notrace" are overridden by "Debug" and "OK" + // buttons if the plug-in generates a dialog box by using controls + if (tokens[i] == wxT("trace")) { + mDebug = true; + } + else if (tokens[i] == wxT("notrace")) { + mDebug = false; + } + else if (tokens[i] == wxT("compiler")) { + mCompiler = true; + } + else if (tokens[i] == wxT("nocompiler")) { + mCompiler = false; + } + } + return; + } + + // We support versions 1, 2 and 3 + // (Version 2 added support for string parameters.) + // (Version 3 added support for choice parameters.) + // (Version 4 added support for project/track/selection information.) + if (len >= 2 && tokens[0] == wxT("version")) { + long v; + tokens[1].ToLong(&v); + if (v < 1 && v > 4) { + // This is an unsupported plug-in version + mOK = false; + return; + } + mVersion = (int) v; + } + + if (len >= 2 && tokens[0] == wxT("name")) { + mName = UnQuote(tokens[1]); + return; + } + + if (len >= 2 && tokens[0] == wxT("action")) { + mAction = UnQuote(tokens[1]); + return; + } + + if (len >= 2 && tokens[0] == wxT("info")) { + mInfo = UnQuote(tokens[1]); + return; + } + + if (len >= 2 && tokens[0] == wxT("preview")) { + if (tokens[1] == wxT("enabled") || tokens[1] == wxT("true")) { + mEnablePreview = true; + } + return; + } + + if (len >= 2 && tokens[0] == wxT("author")) { + mAuthor = UnQuote(tokens[1]); + return; + } + + if (len >= 2 && tokens[0] == wxT("copyright")) { + mCopyright = UnQuote(tokens[1]); + return; + } + + if (len >= 6 && tokens[0] == wxT("control")) { + NyqControl ctrl; + + ctrl.var = tokens[1]; + ctrl.name = tokens[2]; + ctrl.label = tokens[4]; + ctrl.valStr = tokens[5]; + + if (tokens[3] == wxT("string")) { + ctrl.type = NYQ_CTRL_STRING; + } + else if (tokens[ 3 ] == wxT("choice")) { + ctrl.type = NYQ_CTRL_CHOICE; + } + else { + if (len < 8) { + return; + } + + if ((tokens[3] == wxT("real")) || + (tokens[3] == wxT("float"))) // undocumented, but useful, alternative + ctrl.type = NYQ_CTRL_REAL; + else if (tokens[3] == wxT("int")) + ctrl.type = NYQ_CTRL_INT; + else + { + wxString str; + str.Printf(_("Bad Nyquist 'control' type specification: '%s' in plugin file '%s'.\nControl not created."), + tokens[3].c_str(), mFileName.GetFullPath().c_str()); + + // Too disturbing to show alert before Audacity frame is up. + // wxMessageBox(str, wxT("Nyquist Warning"), wxOK | wxICON_EXCLAMATION); + + // Note that the AudacityApp's mLogger has not yet been created, + // so this brings up an alert box, but after the Audacity frame is up. + wxLogWarning(str); + return; + } + + ctrl.lowStr = tokens[6]; + ctrl.highStr = tokens[7]; + } + + ctrl.val = UNINITIALIZED_CONTROL; + + if( mPresetNames.Index( ctrl.var ) == wxNOT_FOUND ) + { + mControls.Add(ctrl); + } + } + + if (len >= 2 && tokens[0] == wxT("categories")) { + for (size_t i = 1; i < tokens.GetCount(); ++i) { + mCategories.Add(tokens[i]); + } + } +} + +void EffectNyquist::ParseFile() +{ + wxTextFile f(mFileName.GetFullPath()); + if (!f.Open()) + return; + + mCmd = wxT(""); + SetEffectFlags(PROCESS_EFFECT | PLUGIN_EFFECT); + mOK = false; + mEnablePreview = false; + mIsSal = false; + mControls.Clear(); + mDebug = false; + + int i; + int len = f.GetLineCount(); + wxString line; + for (i = 0; i < len; i++) { + line = f[i]; + if (line.Length() > 1 && line[0] == wxT(';')) { + Parse(line); + } + // preserve comments so that SAL effects compile with proper line numbers + mCmd += line + wxT("\n"); + } +} + +void EffectNyquist::SetCommand(wxString cmd) +{ + mExternal = true; + mInteractive = false; + mCmd = wxT(""); + SetEffectFlags(INSERT_EFFECT | HIDDEN_EFFECT); + mOK = false; + mIsSal = false; + mControls.Clear(); + + wxStringTokenizer lines(cmd, wxT("\n")); + while (lines.HasMoreTokens()) { + wxString line = lines.GetNextToken(); + + if (line.Length() > 1 && line[0] == wxT(';')) { + Parse(line); + } + else { + mCmd += line + wxT("\n"); + } + } +} + +bool EffectNyquist::SetXlispPath() +{ + wxString fname; + + fname = mXlispPath + wxFILE_SEP_PATH + wxT("nyinit.lsp"); + if (!(::wxFileExists(fname))) { + mXlispPath = wxT(""); + } + + if (mXlispPath == wxT("")) { + wxArrayString audacityPathList = wxGetApp().audacityPathList; + wxArrayString pathList; + wxArrayString files; + unsigned int i; + + for (i = 0; i < audacityPathList.GetCount(); i++) { + wxString prefix = audacityPathList[i] + wxFILE_SEP_PATH; + wxGetApp().AddUniquePathToPathList(prefix + wxT("nyquist"), + pathList); + } + + wxGetApp().FindFilesInPathList(wxT("nyquist.lsp"), pathList, files); + + if (files.GetCount() > 0) { + mXlispPath = ::wxPathOnly(files[0]); + } + } + + /* set_xlisp_path doesn't handle fn_Str() in Unicode build. May or may not actually work. */ + nyx_set_xlisp_path(mXlispPath.mb_str()); + + fname = mXlispPath + wxFILE_SEP_PATH + wxT("nyinit.lsp"); + return ::wxFileExists(fname); +} + int EffectNyquist::StaticGetCallback(float *buffer, int channel, long start, long len, long totlen, void *userdata) @@ -1427,7 +1545,11 @@ wxArrayString EffectNyquist::GetNyquistSearchPath() return pathList; } -/**********************************************************/ +/////////////////////////////////////////////////////////////////////////////// +// +// NyquistDialog +// +/////////////////////////////////////////////////////////////////////////////// #define ID_NYQ_SLIDER 2000 #define ID_NYQ_TEXT 3000 @@ -1584,6 +1706,10 @@ NyquistDialog::NyquistDialog(wxWindow * parent, wxWindowID id, mainSizer->SetSizeHints(this); } +// ============================================================================ +// NyquistDialog implementation +// ============================================================================ + void NyquistDialog::OnSlider(wxCommandEvent & /* event */) { if (mInHandler) { @@ -1715,7 +1841,12 @@ void NyquistDialog::OnPreview(wxCommandEvent & /* event */) mEffect->Preview(); } -/**********************************************************/ +/////////////////////////////////////////////////////////////////////////////// +// +// NyquistInputDialog +// +/////////////////////////////////////////////////////////////////////////////// + #define ID_VERSION 1001 BEGIN_EVENT_TABLE(NyquistInputDialog, wxDialog) @@ -1770,6 +1901,10 @@ NyquistInputDialog::NyquistInputDialog(wxWindow * parent, wxWindowID id, mCommandText->SetFocus(); } +// ============================================================================ +// NyquistInputDialog implementation +// ============================================================================ + wxString NyquistInputDialog::GetCommand() { return mCommandText->GetValue(); @@ -1805,7 +1940,11 @@ void NyquistInputDialog::OnPreview(wxCommandEvent & /* event */) } -/**********************************************************/ +/////////////////////////////////////////////////////////////////////////////// +// +// NyquistOutputDialog +// +/////////////////////////////////////////////////////////////////////////////// BEGIN_EVENT_TABLE(NyquistOutputDialog, wxDialog) @@ -1849,6 +1988,10 @@ NyquistOutputDialog::NyquistOutputDialog(wxWindow * parent, wxWindowID id, mainSizer->SetSizeHints(this); } +// ============================================================================ +// NyquistOutputDialog implementation +// ============================================================================ + void NyquistOutputDialog::OnOk(wxCommandEvent & /* event */) { EndModal(wxID_OK); diff --git a/src/effects/nyquist/Nyquist.h b/src/effects/nyquist/Nyquist.h index 61e7bf42b..556b957b1 100644 --- a/src/effects/nyquist/Nyquist.h +++ b/src/effects/nyquist/Nyquist.h @@ -28,6 +28,9 @@ #include +#define NYQUISTEFFECTS_VERSION wxT("1.0.0.0"); +#define NYQUISTEFFECTS_FAMILY L"Nyquist" + class NyqControl { public: @@ -60,18 +63,26 @@ class AUDACITY_DLL_API EffectNyquist:public Effect EffectNyquist(wxString fName); virtual ~EffectNyquist(); - bool SetXlispPath(); + // IdentInterface implementation - bool LoadedNyFile() { - return mOK; - } + virtual PluginID GetID(); + virtual wxString GetPath(); + virtual wxString GetName(); + virtual wxString GetVendor(); + virtual wxString GetVersion(); + virtual wxString GetDescription(); - void Continue(); - void Break(); - void Stop(); + // EffectIdentInterface implementation - void SetCommand(wxString cmd); - wxString GetOutput(); + virtual EffectType GetType(); + virtual wxString GetFamily(); + virtual bool IsInteractive(); + virtual bool IsDefault(); + virtual bool IsLegacy(); + virtual bool SupportsRealtime(); + virtual bool SupportsAutomation(); + + // Effect implementation /** Get the name of the effect (taken from the script that is loaded). Note * that this name is currently not translated because the translations system @@ -121,6 +132,22 @@ class AUDACITY_DLL_API EffectNyquist:public Effect virtual bool SupportsChains(); virtual bool TransferParameters( Shuttle & shuttle ); + // EffectNyquist implementation + + bool SetXlispPath(); + + bool LoadedNyFile() { + return mOK; + } + + void Continue(); + void Break(); + void Stop(); + + void SetCommand(wxString cmd); + wxString GetOutput(); + + static wxArrayString GetNyquistSearchPath(); private: @@ -175,6 +202,8 @@ class AUDACITY_DLL_API EffectNyquist:public Effect wxString mName; ///< Name of the Effect wxString mAction; wxString mInfo; + wxString mAuthor; + wxString mCopyright; bool mEnablePreview; bool mDebug; std::string mDebugOutput; diff --git a/src/effects/vamp/LoadVamp.cpp b/src/effects/vamp/LoadVamp.cpp index 09877aee6..5c76428c9 100644 --- a/src/effects/vamp/LoadVamp.cpp +++ b/src/effects/vamp/LoadVamp.cpp @@ -19,60 +19,100 @@ using namespace Vamp; using namespace Vamp::HostExt; -#ifdef EFFECT_CATEGORIES - -static std::map gCategoryMap; - -#define VAMP(S) wxT("http://audacityteam.org/namespace#VampCategories") wxT(S) -#define ATEAM(S) wxT("http://audacityteam.org/namespace#") wxT(S) - -/** Initialise the data structure that maps locally generated URI strings to - internal ones. */ -static void InitCategoryMap() +// ============================================================================ +// 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) { - gCategoryMap[VAMP("/Time")] = ATEAM("TimeAnalyser"); - gCategoryMap[VAMP("/Time/Onsets")] = ATEAM("OnsetDetector"); + // Create and register the importer + return new VampEffectsModule(moduleManager, path); } -/** Map the generated VAMP category URI to the internal ones. */ -static wxString MapCategoryUri(const wxString& uri) -{ - std::map::const_iterator iter; - iter = gCategoryMap.find(uri); - if (iter != gCategoryMap.end()) - return iter->second; - return uri; -} +// ============================================================================ +// Register this as a builtin module +// ============================================================================ +DECLARE_BUILTIN_MODULE(VampsEffectBuiltin); -/** Generate category URIs for all levels in a VAMP category hierarchy, - add them to the EffectManager and return the most detailed one. */ -static wxString VampHierarchyToUri(const PluginLoader::PluginCategoryHierarchy& h) +/////////////////////////////////////////////////////////////////////////////// +// +// VampEffectsModule +// +/////////////////////////////////////////////////////////////////////////////// + +VampEffectsModule::VampEffectsModule(ModuleManagerInterface *moduleManager, + const wxString *path) { - // Else, generate URIs and add them to the EffectManager - EffectManager& em = EffectManager::Get(); - wxString vampCategory = - wxString::FromAscii("http://audacityteam.org/namespace#VampCategories"); - EffectCategory* parent = - em.LookupCategory(wxT("http://lv2plug.in/ns/lv2core#AnalyserPlugin")); - if (parent) { - for (size_t c = 0; c < h.size(); ++c) { - vampCategory += wxT("/"); - wxString catName = wxString::FromAscii(h[c].c_str()); - vampCategory += catName; - EffectCategory* ec = em.AddCategory(MapCategoryUri(vampCategory), - catName); - em.AddCategoryParent(ec, parent); - parent = ec; - } + mModMan = moduleManager; + if (path) + { + mPath = *path; } - return MapCategoryUri(vampCategory); } -#endif - -void LoadVampPlugins() +VampEffectsModule::~VampEffectsModule() { +} +// ============================================================================ +// IdentInterface implementation +// ============================================================================ + +wxString VampEffectsModule::GetID() +{ + // Can be anything, but this is a v4 UUID + return wxT("33a86140-7310-4bdf-ad8e-a4af29079f61"); +} + +wxString VampEffectsModule::GetPath() +{ + return mPath; +} + +wxString VampEffectsModule::GetName() +{ + return _("Vamp Effects Module"); +} + +wxString VampEffectsModule::GetVendor() +{ + return _("The Audacity Team"); +} + +wxString VampEffectsModule::GetVersion() +{ + // This "may" be different if this were to be maintained as a separate DLL + return VAMPEFFECTS_VERSION; +} + +wxString VampEffectsModule::GetDescription() +{ + return _("Provides Vamp Effects support to Audacity"); +} + +// ============================================================================ +// ModuleInterface implementation +// ============================================================================ + +bool VampEffectsModule::Initialize() +{ + // Nothing to do here + return true; +} + +void VampEffectsModule::Terminate() +{ + // Nothing to do here + return; +} + +bool VampEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm) +{ #ifdef EFFECT_CATEGORIES InitCategoryMap(); #endif @@ -155,13 +195,115 @@ void LoadVampPlugins() #else VampEffect *effect = new VampEffect(*i, n, hasParameters, name); #endif - em.RegisterEffect(effect); + em.RegisterEffect(this, effect); ++n; } delete vp; } + + return true; +} + +wxArrayString VampEffectsModule::FindPlugins(PluginManagerInterface & pm) +{ + // Nothing to do here yet + return wxArrayString(); +} + +bool VampEffectsModule::RegisterPlugin(PluginManagerInterface & pm, const wxString & path) +{ + // Nothing to do here yet + return false; +} + +bool VampEffectsModule::IsPluginValid(const PluginID & ID, + const wxString & path) +{ + Vamp::HostExt::PluginLoader *loader = Vamp::HostExt::PluginLoader::getInstance(); + Vamp::Plugin *plug = loader->loadPlugin(ID.ToUTF8().data(), 48000); // rate doesn't matter here + + if (plug) + { + delete plug; + return true; + } + + return false; +} + +IdentInterface *VampEffectsModule::CreateInstance(const PluginID & ID, + const wxString & path) +{ + // Nothing to do here yet since we are autoregistering (and creating legacy + // effects anyway). + return NULL; +} + +void VampEffectsModule::DeleteInstance(IdentInterface *instance) +{ + // Nothing to do here yet +} + +// ============================================================================ +// VampEffectsModule implementation +// ============================================================================ + +#ifdef EFFECT_CATEGORIES + +static std::map gCategoryMap; + +#define VAMP(S) wxT("http://audacityteam.org/namespace#VampCategories") wxT(S) +#define ATEAM(S) wxT("http://audacityteam.org/namespace#") wxT(S) + +/** Initialise the data structure that maps locally generated URI strings to + internal ones. */ +static void InitCategoryMap() +{ + gCategoryMap[VAMP("/Time")] = ATEAM("TimeAnalyser"); + gCategoryMap[VAMP("/Time/Onsets")] = ATEAM("OnsetDetector"); +} + +/** Map the generated VAMP category URI to the internal ones. */ +static wxString MapCategoryUri(const wxString& uri) +{ + std::map::const_iterator iter; + iter = gCategoryMap.find(uri); + if (iter != gCategoryMap.end()) + return iter->second; + return uri; +} + +/** Generate category URIs for all levels in a VAMP category hierarchy, + add them to the EffectManager and return the most detailed one. */ +static wxString VampHierarchyToUri(const PluginLoader::PluginCategoryHierarchy& h) +{ + // Else, generate URIs and add them to the EffectManager + EffectManager& em = EffectManager::Get(); + wxString vampCategory = + wxString::FromAscii("http://audacityteam.org/namespace#VampCategories"); + EffectCategory* parent = + em.LookupCategory(wxT("http://lv2plug.in/ns/lv2core#AnalyserPlugin")); + if (parent) { + for (size_t c = 0; c < h.size(); ++c) { + vampCategory += wxT("/"); + wxString catName = wxString::FromAscii(h[c].c_str()); + vampCategory += catName; + EffectCategory* ec = em.AddCategory(MapCategoryUri(vampCategory), + catName); + em.AddCategoryParent(ec, parent); + parent = ec; + } + } + return MapCategoryUri(vampCategory); +} + +#endif + +void LoadVampPlugins() +{ + } void UnloadVampPlugins() diff --git a/src/effects/vamp/LoadVamp.h b/src/effects/vamp/LoadVamp.h index cb6334e84..6be2498a4 100644 --- a/src/effects/vamp/LoadVamp.h +++ b/src/effects/vamp/LoadVamp.h @@ -8,5 +8,48 @@ **********************************************************************/ -void LoadVampPlugins(); -void UnloadVampPlugins(); +#include "audacity/ModuleInterface.h" +#include "audacity/EffectInterface.h" +#include "audacity/PluginInterface.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// VampEffectsModule +// +/////////////////////////////////////////////////////////////////////////////// + +class VampEffectsModule : public ModuleInterface +{ +public: + VampEffectsModule(ModuleManagerInterface *moduleManager, const wxString *path); + virtual ~VampEffectsModule(); + + // IdentInterface implementatino + + virtual wxString GetID(); + virtual wxString GetPath(); + virtual wxString GetName(); + virtual wxString GetVendor(); + virtual wxString GetVersion(); + virtual wxString GetDescription(); + + // ModuleInterface implementation + + virtual bool Initialize(); + virtual void Terminate(); + + virtual bool AutoRegisterPlugins(PluginManagerInterface & pm); + virtual wxArrayString FindPlugins(PluginManagerInterface & pm); + virtual bool RegisterPlugin(PluginManagerInterface & pm, const wxString & path); + + virtual bool IsPluginValid(const PluginID & ID, const wxString & path); + + virtual IdentInterface *CreateInstance(const PluginID & ID, const wxString & path); + virtual void DeleteInstance(IdentInterface *instance); + + // VampEffectModule implementation + +private: + ModuleManagerInterface *mModMan; + wxString mPath; +}; diff --git a/src/effects/vamp/VampEffect.cpp b/src/effects/vamp/VampEffect.cpp index ffe5af462..a9221724b 100644 --- a/src/effects/vamp/VampEffect.cpp +++ b/src/effects/vamp/VampEffect.cpp @@ -33,6 +33,12 @@ #include #include +/////////////////////////////////////////////////////////////////////////////// +// +// VampEffect +// +/////////////////////////////////////////////////////////////////////////////// + VampEffect::VampEffect(Vamp::HostExt::PluginLoader::PluginKey key, int output, bool hasParameters, @@ -46,6 +52,9 @@ VampEffect::VampEffect(Vamp::HostExt::PluginLoader::PluginKey key, mCategory(category), mPlugin(NULL) { + Vamp::HostExt::PluginLoader *loader = Vamp::HostExt::PluginLoader::getInstance(); + mPlugin = loader->loadPlugin(mKey, 48000); // rate doesn't matter here + SetEffectFlags(PLUGIN_EFFECT | ANALYZE_EFFECT); } @@ -55,6 +64,82 @@ VampEffect::~VampEffect() mPlugin = NULL; } +// ============================================================================ +// IdentInterface implementation +// ============================================================================ + +wxString VampEffect::GetID() +{ + return mName; +} + +wxString VampEffect::GetPath() +{ + Vamp::HostExt::PluginLoader *loader = Vamp::HostExt::PluginLoader::getInstance(); + return LAT1CTOWX(loader->getLibraryPathForPlugin(mKey).c_str()); +} + +wxString VampEffect::GetName() +{ + return mName; +} + +wxString VampEffect::GetVendor() +{ + return LAT1CTOWX(mPlugin->getMaker().c_str()); +} + +wxString VampEffect::GetVersion() +{ + return wxString::Format(wxT("%d"), mPlugin->getPluginVersion()); +} + +wxString VampEffect::GetDescription() +{ + return LAT1CTOWX(mPlugin->getCopyright().c_str()); +} + +// ============================================================================ +// EffectIdentInterface implementation +// ============================================================================ + +EffectType VampEffect::GetType() +{ + // For now, relegate to Effect() + return Effect::GetType(); +} + +wxString VampEffect::GetFamily() +{ + return VAMPEFFECTS_FAMILY; +} + +bool VampEffect::IsInteractive() +{ + // For now, relegate to Effect() + return Effect::IsInteractive(); +} + +bool VampEffect::IsDefault() +{ + return false; +} + +bool VampEffect::IsLegacy() +{ + return true; +} + +bool VampEffect::SupportsRealtime() +{ + return false; +} + +bool VampEffect::SupportsAutomation() +{ + return true; +} + wxString VampEffect::GetEffectName() { if (mHasParameters) { diff --git a/src/effects/vamp/VampEffect.h b/src/effects/vamp/VampEffect.h index e3c3823fa..8ae5c228d 100644 --- a/src/effects/vamp/VampEffect.h +++ b/src/effects/vamp/VampEffect.h @@ -24,7 +24,8 @@ class wxComboBox; #include -void LoadVampPlugins(); +#define VAMPEFFECTS_VERSION wxT("1.0.0.0"); +#define VAMPEFFECTS_FAMILY L"Vamp" class VampEffect : public Effect { @@ -37,6 +38,27 @@ class VampEffect : public Effect { wxString category = wxString()); virtual ~VampEffect(); + // IdentInterface implementation + + virtual PluginID GetID(); + virtual wxString GetPath(); + virtual wxString GetName(); + virtual wxString GetVendor(); + virtual wxString GetVersion(); + virtual wxString GetDescription(); + + // EffectIdentInterface implementation + + virtual EffectType GetType(); + virtual wxString GetFamily(); + virtual bool IsInteractive(); + virtual bool IsDefault(); + virtual bool IsLegacy(); + virtual bool SupportsRealtime(); + virtual bool SupportsAutomation(); + + // Effect implementation + virtual wxString GetEffectName(); virtual std::set GetEffectCategories(); diff --git a/src/prefs/EffectsPrefs.cpp b/src/prefs/EffectsPrefs.cpp index cefe5f515..1ade02700 100644 --- a/src/prefs/EffectsPrefs.cpp +++ b/src/prefs/EffectsPrefs.cpp @@ -92,8 +92,6 @@ void EffectsPrefs::PopulateOrExchange(ShuttleGui & S) wxT("/VST/Enable"), true); #endif - - S.AddFixedText(_("Restart Audacity to apply changes.")); } S.EndStatic(); @@ -106,13 +104,15 @@ void EffectsPrefs::PopulateOrExchange(ShuttleGui & S) visualgroups.Add(_("Sorted by Effect Name")); visualgroups.Add(_("Sorted by Publisher and Effect Name")); + visualgroups.Add(_("Sorted by Type and Effect Name")); visualgroups.Add(_("Grouped by Publisher")); - visualgroups.Add(_("Grouped by Type (Ladspa, VST, etc.)")); + visualgroups.Add(_("Grouped by Type")); - prefsgroups.Add(wxT("name")); - prefsgroups.Add(wxT("publisher:name")); - prefsgroups.Add(wxT("publisher")); - prefsgroups.Add(wxT("family")); + prefsgroups.Add(wxT("sortby:name")); + prefsgroups.Add(wxT("sortby:publisher:name")); + prefsgroups.Add(wxT("sortby:type:name")); + prefsgroups.Add(wxT("groupby:publisher")); + prefsgroups.Add(wxT("groupby:type")); wxChoice *c = S.TieChoice(_("Effects in menus are:"), wxT("/Effects/GroupBy"), @@ -158,43 +158,10 @@ void EffectsPrefs::PopulateOrExchange(ShuttleGui & S) #endif } -void EffectsPrefs::SetState(const wxString & family, const wxString & key) -{ - PluginManager & pm = PluginManager::Get(); - bool state = gPrefs->Read(wxT("/Nyquist/Enable"), true); - - const PluginDescriptor *plug = pm.GetFirstPluginForEffectFamily(family); - while (plug) - { - pm.EnablePlugin(plug->GetID(), state); - plug = pm.GetNextPluginForEffectFamily(family); - } -} - bool EffectsPrefs::Apply() { ShuttleGui S(this, eIsSavingToPrefs); PopulateOrExchange(S); -#ifdef USE_NYQUIST - SetState(wxT("Nyquist"), wxT("/Nyquist/Enable")); -#endif - -#ifdef USE_LADSPA - SetState(wxT("Ladspa"), wxT("/Ladspa/Enable")); -#endif - -#ifdef USE_LV2 - SetState(wxT("LV2"), wxT("/LV2/Enable")); -#endif - -#ifdef USE_AUDIO_UNITS - SetState(wxT("AudioUnit"), wxT("/AudioUnits/Enable")); -#endif - -#ifdef USE_VAMP - SetState(wxT("VAMP"), wxT("/VAMP/Enable")); -#endif - return true; } diff --git a/src/prefs/EffectsPrefs.h b/src/prefs/EffectsPrefs.h index 73f048fc9..845e101cb 100644 --- a/src/prefs/EffectsPrefs.h +++ b/src/prefs/EffectsPrefs.h @@ -32,7 +32,6 @@ class EffectsPrefs:public PrefsPanel private: void Populate(); void PopulateOrExchange(ShuttleGui & S); - void SetState(const wxString & family, const wxString & key); }; #endif diff --git a/win/Projects/Audacity/Audacity.vcxproj b/win/Projects/Audacity/Audacity.vcxproj index aeb843f21..0f2196eb0 100755 --- a/win/Projects/Audacity/Audacity.vcxproj +++ b/win/Projects/Audacity/Audacity.vcxproj @@ -784,6 +784,7 @@ + @@ -794,6 +795,9 @@ + + + diff --git a/win/Projects/Audacity/Audacity.vcxproj.filters b/win/Projects/Audacity/Audacity.vcxproj.filters index be6c75c5c..39bb13271 100755 --- a/win/Projects/Audacity/Audacity.vcxproj.filters +++ b/win/Projects/Audacity/Audacity.vcxproj.filters @@ -1852,5 +1852,17 @@ plug-ins + + plug-ins + + + plug-ins + + + plug-ins + + + plug-ins + \ No newline at end of file