1 /**********************************************************************
2 
3   Audacity: A Digital Audio Editor
4 
5   VSTEffect.cpp
6 
7   Dominic Mazzoni
8 
9   This class implements a VST Plug-in effect.  The plug-in must be
10   loaded in a platform-specific way and passed into the constructor,
11   but from here this class handles the interfacing.
12 
13 ********************************************************************//**
14 
15 \class AEffect
16 \brief VST Effects class, conforming to VST layout.
17 
18 *//********************************************************************/
19 
20 //#define VST_DEBUG
21 //#define DEBUG_VST
22 
23 // *******************************************************************
24 // WARNING:  This is NOT 64-bit safe
25 // *******************************************************************
26 
27 
28 #include "VSTEffect.h"
29 #include "../../ModuleManager.h"
30 #include "SampleCount.h"
31 
32 #include "../../widgets/ProgressDialog.h"
33 
34 #if 0
35 #if defined(BUILDING_AUDACITY)
36 #include "../../PlatformCompatibility.h"
37 
38 // Make the main function private
39 #else
40 #define USE_VST 1
41 #endif
42 #endif
43 
44 #if USE_VST
45 
46 #include <limits.h>
47 #include <stdio.h>
48 
49 #include <wx/setup.h> // for wxUSE_* macros
50 #include <wx/dynlib.h>
51 #include <wx/app.h>
52 #include <wx/defs.h>
53 #include <wx/buffer.h>
54 #include <wx/busyinfo.h>
55 #include <wx/button.h>
56 #include <wx/combobox.h>
57 #include <wx/file.h>
58 #include <wx/filename.h>
59 #include <wx/imaglist.h>
60 #include <wx/listctrl.h>
61 #include <wx/log.h>
62 #include <wx/module.h>
63 #include <wx/process.h>
64 #include <wx/recguard.h>
65 #include <wx/sizer.h>
66 #include <wx/slider.h>
67 #include <wx/scrolwin.h>
68 #include <wx/sstream.h>
69 #include <wx/statbox.h>
70 #include <wx/stattext.h>
71 #include <wx/timer.h>
72 #include <wx/tokenzr.h>
73 #include <wx/utils.h>
74 
75 #if defined(__WXMSW__)
76 #include <shlwapi.h>
77 #pragma comment(lib, "shlwapi")
78 #else
79 #include <dlfcn.h>
80 #endif
81 
82 // TODO:  Unfortunately we have some dependencies on Audacity provided
83 //        dialogs, widgets and other stuff.  This will need to be cleaned up.
84 
85 #include "FileNames.h"
86 #include "PlatformCompatibility.h"
87 #include "../../SelectFile.h"
88 #include "../../ShuttleGui.h"
89 #include "../../effects/Effect.h"
90 #include "../../widgets/valnum.h"
91 #include "../../widgets/AudacityMessageBox.h"
92 #include "../../widgets/NumericTextCtrl.h"
93 #include "XMLFileReader.h"
94 
95 #if wxUSE_ACCESSIBILITY
96 #include "../../widgets/WindowAccessible.h"
97 #endif
98 
99 #include "ConfigInterface.h"
100 
101 #include <cstring>
102 
103 // Put this inclusion last.  On Linux it makes some unfortunate pollution of
104 // preprocessor macro name space that interferes with other headers.
105 #if defined(__WXOSX__)
106 #include "VSTControlOSX.h"
107 #elif defined(__WXMSW__)
108 #include "VSTControlMSW.h"
109 #elif defined(__WXGTK__)
110 #include "VSTControlGTK.h"
111 #endif
112 
reinterpretAsFloat(uint32_t x)113 static float reinterpretAsFloat(uint32_t x)
114 {
115     static_assert(sizeof(float) == sizeof(uint32_t), "Cannot reinterpret uint32_t to float since sizes are different.");
116     float f;
117     std::memcpy(&f, &x, sizeof(float));
118     return f;
119 }
120 
reinterpretAsUint32(float f)121 static uint32_t reinterpretAsUint32(float f)
122 {
123     static_assert(sizeof(float) == sizeof(uint32_t), "Cannot reinterpret float to uint32_t since sizes are different.");
124 
125     uint32_t x;
126     std::memcpy(&x, &f, sizeof(uint32_t));
127     return x;
128 }
129 
130 // NOTE:  To debug the subprocess, use wxLogDebug and, on Windows, Debugview
131 //        from TechNet (Sysinternals).
132 
133 // ============================================================================
134 //
135 // Module registration entry point
136 //
137 // This is the symbol that Audacity looks for when the module is built as a
138 // dynamic library.
139 //
140 // When the module is builtin to Audacity, we use the same function, but it is
141 // declared static so as not to clash with other builtin modules.
142 //
143 // ============================================================================
DECLARE_MODULE_ENTRY(AudacityModule)144 DECLARE_MODULE_ENTRY(AudacityModule)
145 {
146    // Create our effects module and register
147    // Trust the module manager not to leak this
148    return safenew VSTEffectsModule();
149 }
150 
151 // ============================================================================
152 //
153 // Register this as a builtin module
154 //
155 // We also take advantage of the fact that wxModules are initialized before
156 // the wxApp::OnInit() method is called.  We check to see if Audacity was
157 // executed to scan a VST effect in a different process.
158 //
159 // ============================================================================
160 DECLARE_BUILTIN_MODULE(VSTBuiltin);
161 
162 
163 ///////////////////////////////////////////////////////////////////////////////
164 ///
165 /// Auto created at program start up, this initialises VST.
166 ///
167 ///////////////////////////////////////////////////////////////////////////////
168 class VSTSubEntry final : public wxModule
169 {
170 public:
OnInit()171    bool OnInit()
172    {
173       // Have we been started to check a plugin?
174       if (wxTheApp && wxTheApp->argc == 3 && wxStrcmp(wxTheApp->argv[1], VSTCMDKEY) == 0)
175       {
176          // NOTE:  This can really hide failures, which is what we want for those pesky
177          //        VSTs that are bad or that our support isn't correct.  But, it can also
178          //        hide Audacity failures in the subprocess, so if you're having an unruley
179          //        VST or odd Audacity failures, comment it out and you might get more info.
180          //wxHandleFatalExceptions();
181          VSTEffectsModule::Check(wxTheApp->argv[2]);
182 
183          // Returning false causes default processing to display a message box, but we don't
184          // want that so disable logging.
185          wxLog::EnableLogging(false);
186 
187          return false;
188       }
189 
190       return true;
191    };
192 
OnExit()193    void OnExit() {};
194 
195    DECLARE_DYNAMIC_CLASS(VSTSubEntry)
196 };
197 IMPLEMENT_DYNAMIC_CLASS(VSTSubEntry, wxModule);
198 
199 //----------------------------------------------------------------------------
200 // VSTSubProcess
201 //----------------------------------------------------------------------------
202 #define OUTPUTKEY wxT("<VSTLOADCHK>-")
203 enum InfoKeys
204 {
205    kKeySubIDs,
206    kKeyBegin,
207    kKeyName,
208    kKeyPath,
209    kKeyVendor,
210    kKeyVersion,
211    kKeyDescription,
212    kKeyEffectType,
213    kKeyInteractive,
214    kKeyAutomatable,
215    kKeyEnd
216 };
217 
218 
219 ///////////////////////////////////////////////////////////////////////////////
220 ///
221 /// Information about one VST effect.
222 ///
223 ///////////////////////////////////////////////////////////////////////////////
224 class VSTSubProcess final : public wxProcess,
225                       public EffectDefinitionInterface
226 {
227 public:
VSTSubProcess()228    VSTSubProcess()
229    {
230       Redirect();
231    }
232 
233    // EffectClientInterface implementation
234 
GetPath()235    PluginPath GetPath() override
236    {
237       return mPath;
238    }
239 
GetSymbol()240    ComponentInterfaceSymbol GetSymbol() override
241    {
242       return mName;
243    }
244 
GetVendor()245    VendorSymbol GetVendor() override
246    {
247       return { mVendor };
248    }
249 
GetVersion()250    wxString GetVersion() override
251    {
252       return mVersion;
253    }
254 
GetDescription()255    TranslatableString GetDescription() override
256    {
257       return mDescription;
258    }
259 
GetFamily()260    EffectFamilySymbol GetFamily() override
261    {
262       return VSTPLUGINTYPE;
263    }
264 
GetType()265    EffectType GetType() override
266    {
267       return mType;
268    }
269 
IsInteractive()270    bool IsInteractive() override
271    {
272       return mInteractive;
273    }
274 
IsDefault()275    bool IsDefault() override
276    {
277       return false;
278    }
279 
IsLegacy()280    bool IsLegacy() override
281    {
282       return false;
283    }
284 
SupportsRealtime()285    bool SupportsRealtime() override
286    {
287       return mType == EffectTypeProcess;
288    }
289 
SupportsAutomation()290    bool SupportsAutomation() override
291    {
292       return mAutomatable;
293    }
294 
295 public:
296    wxString mPath;
297    wxString mName;
298    wxString mVendor;
299    wxString mVersion;
300    TranslatableString mDescription;
301    EffectType mType;
302    bool mInteractive;
303    bool mAutomatable;
304 };
305 
306 // ============================================================================
307 //
308 // VSTEffectsModule
309 //
310 // ============================================================================
VSTEffectsModule()311 VSTEffectsModule::VSTEffectsModule()
312 {
313 }
314 
~VSTEffectsModule()315 VSTEffectsModule::~VSTEffectsModule()
316 {
317 }
318 
319 // ============================================================================
320 // ComponentInterface implementation
321 // ============================================================================
322 
GetPath()323 PluginPath VSTEffectsModule::GetPath()
324 {
325    return {};
326 }
327 
GetSymbol()328 ComponentInterfaceSymbol VSTEffectsModule::GetSymbol()
329 {
330    return XO("VST Effects");
331 }
332 
GetVendor()333 VendorSymbol VSTEffectsModule::GetVendor()
334 {
335    return XO("The Audacity Team");
336 }
337 
GetVersion()338 wxString VSTEffectsModule::GetVersion()
339 {
340    // This "may" be different if this were to be maintained as a separate DLL
341    return AUDACITY_VERSION_STRING;
342 }
343 
GetDescription()344 TranslatableString VSTEffectsModule::GetDescription()
345 {
346    return XO("Adds the ability to use VST effects in Audacity.");
347 }
348 
349 // ============================================================================
350 // ModuleInterface implementation
351 // ============================================================================
352 
Initialize()353 bool VSTEffectsModule::Initialize()
354 {
355    // Nothing to do here
356    return true;
357 }
358 
Terminate()359 void VSTEffectsModule::Terminate()
360 {
361    // Nothing to do here
362    return;
363 }
364 
GetOptionalFamilySymbol()365 EffectFamilySymbol VSTEffectsModule::GetOptionalFamilySymbol()
366 {
367 #if USE_VST
368    return VSTPLUGINTYPE;
369 #else
370    return {};
371 #endif
372 }
373 
GetFileExtensions()374 const FileExtensions &VSTEffectsModule::GetFileExtensions()
375 {
376    static FileExtensions result{{ _T("vst") }};
377    return result;
378 }
379 
InstallPath()380 FilePath VSTEffectsModule::InstallPath()
381 {
382    // Not yet ready for VST drag-and-drop...
383    // return FileNames::PlugInDir();
384 
385    return {};
386 }
387 
AutoRegisterPlugins(PluginManagerInterface & WXUNUSED (pm))388 bool VSTEffectsModule::AutoRegisterPlugins(PluginManagerInterface & WXUNUSED(pm))
389 {
390    // We don't auto-register
391    return true;
392 }
393 
FindPluginPaths(PluginManagerInterface & pm)394 PluginPaths VSTEffectsModule::FindPluginPaths(PluginManagerInterface & pm)
395 {
396    FilePaths pathList;
397    FilePaths files;
398 
399    // Check for the VST_PATH environment variable
400    wxString vstpath = wxString::FromUTF8(getenv("VST_PATH"));
401    if (!vstpath.empty())
402    {
403       wxStringTokenizer tok(vstpath, wxPATH_SEP);
404       while (tok.HasMoreTokens())
405       {
406          pathList.push_back(tok.GetNextToken());
407       }
408    }
409 
410 #if defined(__WXMAC__)
411 #define VSTPATH wxT("/Library/Audio/Plug-Ins/VST")
412 
413    // Look in ~/Library/Audio/Plug-Ins/VST and /Library/Audio/Plug-Ins/VST
414    pathList.push_back(wxGetHomeDir() + wxFILE_SEP_PATH + VSTPATH);
415    pathList.push_back(VSTPATH);
416 
417    // Recursively search all paths for Info.plist files.  This will identify all
418    // bundles.
419    pm.FindFilesInPathList(wxT("Info.plist"), pathList, files, true);
420 
421    // Remove the 'Contents/Info.plist' portion of the names
422    for (size_t i = 0; i < files.size(); i++)
423    {
424       files[i] = wxPathOnly(wxPathOnly(files[i]));
425       if (!files[i].EndsWith(wxT(".vst")))
426       {
427          files.erase( files.begin() + i-- );
428       }
429    }
430 
431 #elif defined(__WXMSW__)
432 
433    TCHAR dpath[MAX_PATH];
434    TCHAR tpath[MAX_PATH];
435    DWORD len;
436 
437    // Try HKEY_CURRENT_USER registry key first
438    len = WXSIZEOF(tpath);
439    if (SHRegGetUSValue(wxT("Software\\VST"),
440                        wxT("VSTPluginsPath"),
441                        NULL,
442                        tpath,
443                        &len,
444                        FALSE,
445                        NULL,
446                        0) == ERROR_SUCCESS)
447    {
448       tpath[len] = 0;
449       dpath[0] = 0;
450       ExpandEnvironmentStrings(tpath, dpath, WXSIZEOF(dpath));
451       pathList.push_back(dpath);
452    }
453 
454    // Then try HKEY_LOCAL_MACHINE registry key
455    len = WXSIZEOF(tpath);
456    if (SHRegGetUSValue(wxT("Software\\VST"),
457                        wxT("VSTPluginsPath"),
458                        NULL,
459                        tpath,
460                        &len,
461                        TRUE,
462                        NULL,
463                        0) == ERROR_SUCCESS)
464    {
465       tpath[len] = 0;
466       dpath[0] = 0;
467       ExpandEnvironmentStrings(tpath, dpath, WXSIZEOF(dpath));
468       pathList.push_back(dpath);
469    }
470 
471    // Add the default path last
472    dpath[0] = 0;
473    ExpandEnvironmentStrings(wxT("%ProgramFiles%\\Steinberg\\VSTPlugins"),
474                             dpath,
475                             WXSIZEOF(dpath));
476    pathList.push_back(dpath);
477 
478    // Recursively scan for all DLLs
479    pm.FindFilesInPathList(wxT("*.dll"), pathList, files, true);
480 
481 #else
482 
483    // Nothing specified in the VST_PATH environment variable...provide defaults
484    if (vstpath.empty())
485    {
486       // We add this "non-default" one
487       pathList.push_back(wxT(LIBDIR) wxT("/vst"));
488 
489       // These are the defaults used by other hosts
490       pathList.push_back(wxT("/usr/lib/vst"));
491       pathList.push_back(wxT("/usr/local/lib/vst"));
492       pathList.push_back(wxGetHomeDir() + wxFILE_SEP_PATH + wxT(".vst"));
493    }
494 
495    // Recursively scan for all shared objects
496    pm.FindFilesInPathList(wxT("*.so"), pathList, files, true);
497 
498 #endif
499 
500    return { files.begin(), files.end() };
501 }
502 
DiscoverPluginsAtPath(const PluginPath & path,TranslatableString & errMsg,const RegistrationCallback & callback)503 unsigned VSTEffectsModule::DiscoverPluginsAtPath(
504    const PluginPath & path, TranslatableString &errMsg,
505    const RegistrationCallback &callback)
506 {
507    bool error = false;
508    unsigned nFound = 0;
509    errMsg = {};
510    // TODO:  Fix this for external usage
511    const auto &cmdpath = PlatformCompatibility::GetExecutablePath();
512 
513    wxString effectIDs = wxT("0;");
514    wxStringTokenizer effectTzr(effectIDs, wxT(";"));
515 
516    Optional<ProgressDialog> progress{};
517    size_t idCnt = 0;
518    size_t idNdx = 0;
519 
520    bool cont = true;
521 
522    while (effectTzr.HasMoreTokens() && cont)
523    {
524       wxString effectID = effectTzr.GetNextToken();
525 
526       wxString cmd;
527       cmd.Printf(wxT("\"%s\" %s \"%s;%s\""), cmdpath, VSTCMDKEY, path, effectID);
528 
529       VSTSubProcess proc;
530       try
531       {
532          int flags = wxEXEC_SYNC | wxEXEC_NODISABLE;
533 #if defined(__WXMSW__)
534          flags += wxEXEC_NOHIDE;
535 #endif
536          wxExecute(cmd, flags, &proc);
537       }
538       catch (...)
539       {
540          wxLogMessage(wxT("VST plugin registration failed for %s\n"), path);
541          error = true;
542       }
543 
544       wxString output;
545       wxStringOutputStream ss(&output);
546       proc.GetInputStream()->Read(ss);
547 
548       int keycount = 0;
549       bool haveBegin = false;
550       wxStringTokenizer tzr(output, wxT("\n"));
551       while (tzr.HasMoreTokens())
552       {
553          wxString line = tzr.GetNextToken();
554 
555          // Our output may follow any output the plugin may have written.
556          if (!line.StartsWith(OUTPUTKEY))
557          {
558             continue;
559          }
560 
561          long key;
562          if (!line.Mid(wxStrlen(OUTPUTKEY)).BeforeFirst(wxT('=')).ToLong(&key))
563          {
564             continue;
565          }
566          wxString val = line.AfterFirst(wxT('=')).BeforeFirst(wxT('\r'));
567 
568          switch (key)
569          {
570             case kKeySubIDs:
571                effectIDs = val;
572                effectTzr.Reinit(effectIDs);
573                idCnt = effectTzr.CountTokens();
574                if (idCnt > 3)
575                {
576                   progress.emplace( XO("Scanning Shell VST"),
577                         XO("Registering %d of %d: %-64.64s")
578                            .Format( 0, idCnt, proc.GetSymbol().Translation())
579                                    /*
580                         , wxPD_APP_MODAL |
581                            wxPD_AUTO_HIDE |
582                            wxPD_CAN_ABORT |
583                            wxPD_ELAPSED_TIME |
584                            wxPD_ESTIMATED_TIME |
585                            wxPD_REMAINING_TIME
586                                     */
587                   );
588                   progress->Show();
589                }
590             break;
591 
592             case kKeyBegin:
593                haveBegin = true;
594                keycount++;
595             break;
596 
597             case kKeyName:
598                proc.mName = val;
599                keycount++;
600             break;
601 
602             case kKeyPath:
603                proc.mPath = val;
604                keycount++;
605             break;
606 
607             case kKeyVendor:
608                proc.mVendor = val;
609                keycount++;
610             break;
611 
612             case kKeyVersion:
613                proc.mVersion = val;
614                keycount++;
615             break;
616 
617             case kKeyDescription:
618                proc.mDescription = Verbatim( val );
619                keycount++;
620             break;
621 
622             case kKeyEffectType:
623                long type;
624                val.ToLong(&type);
625                proc.mType = (EffectType) type;
626                keycount++;
627             break;
628 
629             case kKeyInteractive:
630                proc.mInteractive = val == wxT("1");
631                keycount++;
632             break;
633 
634             case kKeyAutomatable:
635                proc.mAutomatable = val == wxT("1");
636                keycount++;
637             break;
638 
639             case kKeyEnd:
640             {
641                if (!haveBegin || ++keycount != kKeyEnd)
642                {
643                   keycount = 0;
644                   haveBegin = false;
645                   continue;
646                }
647 
648                bool skip = false;
649                if (progress)
650                {
651                   idNdx++;
652                   auto result = progress->Update((int)idNdx, (int)idCnt,
653                      XO("Registering %d of %d: %-64.64s")
654                         .Format( idNdx, idCnt, proc.GetSymbol().Translation() ));
655                   cont = (result == ProgressResult::Success);
656                }
657 
658                if (!skip && cont)
659                {
660                   if (callback)
661                      callback( this, &proc );
662                   ++nFound;
663                }
664             }
665             break;
666 
667             default:
668                keycount = 0;
669                haveBegin = false;
670             break;
671          }
672       }
673    }
674 
675    if (error)
676       errMsg = XO("Could not load the library");
677 
678    return nFound;
679 }
680 
IsPluginValid(const PluginPath & path,bool bFast)681 bool VSTEffectsModule::IsPluginValid(const PluginPath & path, bool bFast)
682 {
683    if( bFast )
684       return true;
685    wxString realPath = path.BeforeFirst(wxT(';'));
686    return wxFileName::FileExists(realPath) || wxFileName::DirExists(realPath);
687 }
688 
689 std::unique_ptr<ComponentInterface>
CreateInstance(const PluginPath & path)690 VSTEffectsModule::CreateInstance(const PluginPath & path)
691 {
692    // Acquires a resource for the application.
693    // For us, the ID is simply the path to the effect
694    return std::make_unique<VSTEffect>(path);
695 }
696 
697 // ============================================================================
698 // ModuleEffectInterface implementation
699 // ============================================================================
700 
701 // ============================================================================
702 // VSTEffectsModule implementation
703 // ============================================================================
704 
705 // static
706 //
707 // Called from reinvokation of Audacity or DLL to check in a separate process
Check(const wxChar * path)708 void VSTEffectsModule::Check(const wxChar *path)
709 {
710    VSTEffect effect(path);
711    if (effect.SetHost(NULL))
712    {
713       auto effectIDs = effect.GetEffectIDs();
714       wxString out;
715 
716       if (effectIDs.size() > 0)
717       {
718          wxString subids;
719 
720          for (size_t i = 0, cnt = effectIDs.size(); i < cnt; i++)
721          {
722             subids += wxString::Format(wxT("%d;"), effectIDs[i]);
723          }
724 
725          out = wxString::Format(wxT("%s%d=%s\n"), OUTPUTKEY, kKeySubIDs, subids.RemoveLast());
726       }
727       else
728       {
729          out += wxString::Format(wxT("%s%d=%s\n"), OUTPUTKEY, kKeyBegin, wxEmptyString);
730          out += wxString::Format(wxT("%s%d=%s\n"), OUTPUTKEY, kKeyPath, effect.GetPath());
731          out += wxString::Format(wxT("%s%d=%s\n"), OUTPUTKEY, kKeyName, effect.GetSymbol().Internal());
732          out += wxString::Format(wxT("%s%d=%s\n"), OUTPUTKEY, kKeyVendor,
733                                  effect.GetVendor().Internal());
734          out += wxString::Format(wxT("%s%d=%s\n"), OUTPUTKEY, kKeyVersion, effect.GetVersion());
735          out += wxString::Format(wxT("%s%d=%s\n"), OUTPUTKEY, kKeyDescription, effect.GetDescription().Translation());
736          out += wxString::Format(wxT("%s%d=%d\n"), OUTPUTKEY, kKeyEffectType, effect.GetType());
737          out += wxString::Format(wxT("%s%d=%d\n"), OUTPUTKEY, kKeyInteractive, effect.IsInteractive());
738          out += wxString::Format(wxT("%s%d=%d\n"), OUTPUTKEY, kKeyAutomatable, effect.SupportsAutomation());
739          out += wxString::Format(wxT("%s%d=%s\n"), OUTPUTKEY, kKeyEnd, wxEmptyString);
740       }
741 
742       // We want to output info in one chunk to prevent output
743       // from the effect intermixing with the info
744       const wxCharBuffer buf = out.ToUTF8();
745       fwrite(buf, 1, strlen(buf), stdout);
746       fflush(stdout);
747    }
748 }
749 
750 ///////////////////////////////////////////////////////////////////////////////
751 //
752 // Dialog for configuring latency, buffer size and graphics mode for a
753 // VST effect.
754 //
755 ///////////////////////////////////////////////////////////////////////////////
756 class VSTEffectOptionsDialog final : public wxDialogWrapper
757 {
758 public:
759    VSTEffectOptionsDialog(wxWindow * parent, EffectHostInterface *host);
760    virtual ~VSTEffectOptionsDialog();
761 
762    void PopulateOrExchange(ShuttleGui & S);
763 
764    void OnOk(wxCommandEvent & evt);
765 
766 private:
767    EffectHostInterface *mHost;
768    int mBufferSize;
769    bool mUseLatency;
770    bool mUseGUI;
771 
772    DECLARE_EVENT_TABLE()
773 };
774 
BEGIN_EVENT_TABLE(VSTEffectOptionsDialog,wxDialogWrapper)775 BEGIN_EVENT_TABLE(VSTEffectOptionsDialog, wxDialogWrapper)
776    EVT_BUTTON(wxID_OK, VSTEffectOptionsDialog::OnOk)
777 END_EVENT_TABLE()
778 
779 VSTEffectOptionsDialog::VSTEffectOptionsDialog(wxWindow * parent, EffectHostInterface *host)
780 :  wxDialogWrapper(parent, wxID_ANY, XO("VST Effect Options"))
781 {
782    mHost = host;
783 
784    mHost->GetSharedConfig(wxT("Options"), wxT("BufferSize"), mBufferSize, 8192);
785    mHost->GetSharedConfig(wxT("Options"), wxT("UseLatency"), mUseLatency, true);
786    mHost->GetSharedConfig(wxT("Options"), wxT("UseGUI"), mUseGUI, true);
787 
788    ShuttleGui S(this, eIsCreating);
789    PopulateOrExchange(S);
790 }
791 
~VSTEffectOptionsDialog()792 VSTEffectOptionsDialog::~VSTEffectOptionsDialog()
793 {
794 }
795 
PopulateOrExchange(ShuttleGui & S)796 void VSTEffectOptionsDialog::PopulateOrExchange(ShuttleGui & S)
797 {
798    S.SetBorder(5);
799    S.StartHorizontalLay(wxEXPAND, 1);
800    {
801       S.StartVerticalLay(false);
802       {
803          S.StartStatic(XO("Buffer Size"));
804          {
805             S.AddVariableText( XO(
806 "The buffer size controls the number of samples sent to the effect "
807 "on each iteration. Smaller values will cause slower processing and "
808 "some effects require 8192 samples or less to work properly. However "
809 "most effects can accept large buffers and using them will greatly "
810 "reduce processing time."),
811                false, 0, 650);
812 
813             S.StartHorizontalLay(wxALIGN_LEFT);
814             {
815                wxTextCtrl *t;
816                t = S.Validator<IntegerValidator<int>>(
817                      &mBufferSize, NumValidatorStyle::DEFAULT, 8, 1048576 * 1)
818                   .MinSize( { 100, -1 } )
819                   .TieNumericTextBox(XXO("&Buffer Size (8 to 1048576 samples):"),
820                                        mBufferSize,
821                                        12);
822             }
823             S.EndHorizontalLay();
824          }
825          S.EndStatic();
826 
827          S.StartStatic(XO("Latency Compensation"));
828          {
829             S.AddVariableText( XO(
830 "As part of their processing, some VST effects must delay returning "
831 "audio to Audacity. When not compensating for this delay, you will "
832 "notice that small silences have been inserted into the audio. "
833 "Enabling this option will provide that compensation, but it may "
834 "not work for all VST effects."),
835                false, 0, 650);
836 
837             S.StartHorizontalLay(wxALIGN_LEFT);
838             {
839                S.TieCheckBox(XXO("Enable &compensation"),
840                              mUseLatency);
841             }
842             S.EndHorizontalLay();
843          }
844          S.EndStatic();
845 
846          S.StartStatic(XO("Graphical Mode"));
847          {
848             S.AddVariableText( XO(
849 "Most VST effects have a graphical interface for setting parameter values."
850 " A basic text-only method is also available. "
851 " Reopen the effect for this to take effect."),
852                false, 0, 650);
853             S.TieCheckBox(XXO("Enable &graphical interface"),
854                           mUseGUI);
855          }
856          S.EndStatic();
857       }
858       S.EndVerticalLay();
859    }
860    S.EndHorizontalLay();
861 
862    S.AddStandardButtons();
863 
864    Layout();
865    Fit();
866    Center();
867 }
868 
OnOk(wxCommandEvent & WXUNUSED (evt))869 void VSTEffectOptionsDialog::OnOk(wxCommandEvent & WXUNUSED(evt))
870 {
871    if (!Validate())
872    {
873       return;
874    }
875 
876    ShuttleGui S(this, eIsGettingFromDialog);
877    PopulateOrExchange(S);
878 
879    mHost->SetSharedConfig(wxT("Options"), wxT("BufferSize"), mBufferSize);
880    mHost->SetSharedConfig(wxT("Options"), wxT("UseLatency"), mUseLatency);
881    mHost->SetSharedConfig(wxT("Options"), wxT("UseGUI"), mUseGUI);
882 
883    EndModal(wxID_OK);
884 }
885 
886 ///////////////////////////////////////////////////////////////////////////////
887 ///
888 /// Wrapper for wxTimer that calls a VST effect at regular intervals.
889 ///
890 /// \todo should there be tests for no timer available?
891 ///
892 ///////////////////////////////////////////////////////////////////////////////
893 class VSTEffectTimer final : public wxTimer
894 {
895 public:
VSTEffectTimer(VSTEffect * effect)896    VSTEffectTimer(VSTEffect *effect)
897    :  wxTimer(),
898       mEffect(effect)
899    {
900    }
901 
~VSTEffectTimer()902    ~VSTEffectTimer()
903    {
904    }
905 
Notify()906    void Notify()
907    {
908       mEffect->OnTimer();
909    }
910 
911 private:
912    VSTEffect *mEffect;
913 };
914 
915 ///////////////////////////////////////////////////////////////////////////////
916 //
917 // VSTEffect
918 //
919 ///////////////////////////////////////////////////////////////////////////////
920 enum
921 {
922    ID_Duration = 20000,
923    ID_Sliders = 21000,
924 };
925 
926 DEFINE_LOCAL_EVENT_TYPE(EVT_SIZEWINDOW);
927 DEFINE_LOCAL_EVENT_TYPE(EVT_UPDATEDISPLAY);
928 
929 BEGIN_EVENT_TABLE(VSTEffect, wxEvtHandler)
930    EVT_COMMAND_RANGE(ID_Sliders, ID_Sliders + 999, wxEVT_COMMAND_SLIDER_UPDATED, VSTEffect::OnSlider)
931 
932    // Events from the audioMaster callback
933    EVT_COMMAND(wxID_ANY, EVT_SIZEWINDOW, VSTEffect::OnSizeWindow)
934 END_EVENT_TABLE()
935 
936 // Needed to support shell plugins...sucks, but whatcha gonna do???
937 intptr_t VSTEffect::mCurrentEffectID;
938 
939 typedef AEffect *(*vstPluginMain)(audioMasterCallback audioMaster);
940 
AudioMaster(AEffect * effect,int32_t opcode,int32_t index,intptr_t value,void * ptr,float opt)941 intptr_t VSTEffect::AudioMaster(AEffect * effect,
942                                 int32_t opcode,
943                                 int32_t index,
944                                 intptr_t value,
945                                 void * ptr,
946                                 float opt)
947 {
948    VSTEffect *vst = (effect ? (VSTEffect *) effect->ptr2 : NULL);
949 
950    // Handles operations during initialization...before VSTEffect has had a
951    // chance to set its instance pointer.
952    switch (opcode)
953    {
954       case audioMasterVersion:
955          return (intptr_t) 2400;
956 
957       case audioMasterCurrentId:
958          return mCurrentEffectID;
959 
960       case audioMasterGetVendorString:
961          strcpy((char *) ptr, "Audacity Team");    // Do not translate, max 64 + 1 for null terminator
962          return 1;
963 
964       case audioMasterGetProductString:
965          strcpy((char *) ptr, "Audacity");         // Do not translate, max 64 + 1 for null terminator
966          return 1;
967 
968       case audioMasterGetVendorVersion:
969          return (intptr_t) (AUDACITY_VERSION << 24 |
970                             AUDACITY_RELEASE << 16 |
971                             AUDACITY_REVISION << 8 |
972                             AUDACITY_MODLEVEL);
973 
974       // Some (older) effects depend on an effIdle call when requested.  An
975       // example is the Antress Modern plugins which uses the call to update
976       // the editors display when the program (preset) changes.
977       case audioMasterNeedIdle:
978          if (vst)
979          {
980             vst->NeedIdle();
981             return 1;
982          }
983          return 0;
984 
985       // We would normally get this if the effect editor is dipslayed and something "major"
986       // has changed (like a program change) instead of multiple automation calls.
987       // Since we don't do anything with the parameters while the editor is displayed,
988       // there's no need for us to do anything.
989       case audioMasterUpdateDisplay:
990          if (vst)
991          {
992             vst->UpdateDisplay();
993             return 1;
994          }
995          return 0;
996 
997       // Return the current time info.
998       case audioMasterGetTime:
999          if (vst)
1000          {
1001             return (intptr_t) vst->GetTimeInfo();
1002          }
1003          return 0;
1004 
1005       // Inputs, outputs, or initial delay has changed...all we care about is initial delay.
1006       case audioMasterIOChanged:
1007          if (vst)
1008          {
1009             vst->SetBufferDelay(effect->initialDelay);
1010             return 1;
1011          }
1012          return 0;
1013 
1014       case audioMasterGetSampleRate:
1015          if (vst)
1016          {
1017             return (intptr_t) vst->GetSampleRate();
1018          }
1019          return 0;
1020 
1021       case audioMasterIdle:
1022          wxYieldIfNeeded();
1023          return 1;
1024 
1025       case audioMasterGetCurrentProcessLevel:
1026          if (vst)
1027          {
1028             return vst->GetProcessLevel();
1029          }
1030          return 0;
1031 
1032       case audioMasterGetLanguage:
1033          return kVstLangEnglish;
1034 
1035       // We always replace, never accumulate
1036       case audioMasterWillReplaceOrAccumulate:
1037          return 1;
1038 
1039       // Resize the window to accommodate the effect size
1040       case audioMasterSizeWindow:
1041          if (vst)
1042          {
1043             vst->SizeWindow(index, value);
1044          }
1045          return 1;
1046 
1047       case audioMasterCanDo:
1048       {
1049          char *s = (char *) ptr;
1050          if (strcmp(s, "acceptIOChanges") == 0 ||
1051             strcmp(s, "sendVstTimeInfo") == 0 ||
1052             strcmp(s, "startStopProcess") == 0 ||
1053             strcmp(s, "shellCategory") == 0 ||
1054             strcmp(s, "sizeWindow") == 0)
1055          {
1056             return 1;
1057          }
1058 
1059 #if defined(VST_DEBUG)
1060 #if defined(__WXMSW__)
1061          wxLogDebug(wxT("VST canDo: %s"), wxString::FromAscii((char *)ptr));
1062 #else
1063          wxPrintf(wxT("VST canDo: %s\n"), wxString::FromAscii((char *)ptr));
1064 #endif
1065 #endif
1066 
1067          return 0;
1068       }
1069 
1070       case audioMasterBeginEdit:
1071       case audioMasterEndEdit:
1072          return 0;
1073 
1074       case audioMasterAutomate:
1075          if (vst)
1076          {
1077             vst->Automate(index, opt);
1078          }
1079          return 0;
1080 
1081       // We're always connected (sort of)
1082       case audioMasterPinConnected:
1083 
1084       // We don't do MIDI yet
1085       case audioMasterWantMidi:
1086       case audioMasterProcessEvents:
1087 
1088          // Don't need to see any messages about these
1089          return 0;
1090    }
1091 
1092 #if defined(VST_DEBUG)
1093 #if defined(__WXMSW__)
1094    wxLogDebug(wxT("vst: %p opcode: %d index: %d value: %p ptr: %p opt: %f user: %p"),
1095               effect, (int) opcode, (int) index, (void *) value, ptr, opt, vst);
1096 #else
1097    wxPrintf(wxT("vst: %p opcode: %d index: %d value: %p ptr: %p opt: %f user: %p\n"),
1098             effect, (int) opcode, (int) index, (void *) value, ptr, opt, vst);
1099 #endif
1100 #endif
1101 
1102    return 0;
1103 }
1104 
1105 #if !defined(__WXMSW__)
operator ()(void * p) const1106 void VSTEffect::ModuleDeleter::operator() (void* p) const
1107 {
1108    if (p)
1109       dlclose(p);
1110 }
1111 #endif
1112 
1113 #if defined(__WXMAC__)
operator ()(void * p) const1114 void VSTEffect::BundleDeleter::operator() (void* p) const
1115 {
1116    if (p)
1117       CFRelease(static_cast<CFBundleRef>(p));
1118 }
1119 
reset()1120 void VSTEffect::ResourceHandle::reset()
1121 {
1122    if (mpHandle)
1123       CFBundleCloseBundleResourceMap(mpHandle, mNum);
1124    mpHandle = nullptr;
1125    mNum = 0;
1126 }
1127 #endif
1128 
VSTEffect(const PluginPath & path,VSTEffect * master)1129 VSTEffect::VSTEffect(const PluginPath & path, VSTEffect *master)
1130 :  mPath(path),
1131    mMaster(master)
1132 {
1133    mHost = NULL;
1134    mModule = NULL;
1135    mAEffect = NULL;
1136    mDialog = NULL;
1137 
1138    mTimer = std::make_unique<VSTEffectTimer>(this);
1139    mTimerGuard = 0;
1140 
1141    mInteractive = false;
1142    mAudioIns = 0;
1143    mAudioOuts = 0;
1144    mMidiIns = 0;
1145    mMidiOuts = 0;
1146    mSampleRate = 44100;
1147    mBlockSize = mUserBlockSize = 8192;
1148    mBufferDelay = 0;
1149    mProcessLevel = 1;         // in GUI thread
1150    mHasPower = false;
1151    mWantsIdle = false;
1152    mWantsEditIdle = false;
1153    mUseLatency = true;
1154    mReady = false;
1155 
1156    memset(&mTimeInfo, 0, sizeof(mTimeInfo));
1157    mTimeInfo.samplePos = 0.0;
1158    mTimeInfo.sampleRate = 44100.0;  // this is a bogus value, but it's only for the display
1159    mTimeInfo.nanoSeconds = wxGetUTCTimeMillis().ToDouble();
1160    mTimeInfo.tempo = 120.0;
1161    mTimeInfo.timeSigNumerator = 4;
1162    mTimeInfo.timeSigDenominator = 4;
1163    mTimeInfo.flags = kVstTempoValid | kVstNanosValid;
1164 
1165    // UI
1166 
1167    mGui = false;
1168    mContainer = NULL;
1169 
1170    // If we're a slave then go ahead a load immediately
1171    if (mMaster)
1172    {
1173       Load();
1174    }
1175 }
1176 
~VSTEffect()1177 VSTEffect::~VSTEffect()
1178 {
1179    if (mDialog)
1180    {
1181       mDialog->Close();
1182    }
1183 
1184    Unload();
1185 }
1186 
1187 // ============================================================================
1188 // ComponentInterface Implementation
1189 // ============================================================================
1190 
GetPath()1191 PluginPath VSTEffect::GetPath()
1192 {
1193    return mPath;
1194 }
1195 
GetSymbol()1196 ComponentInterfaceSymbol VSTEffect::GetSymbol()
1197 {
1198    return mName;
1199 }
1200 
GetVendor()1201 VendorSymbol VSTEffect::GetVendor()
1202 {
1203    return { mVendor };
1204 }
1205 
GetVersion()1206 wxString VSTEffect::GetVersion()
1207 {
1208    wxString version;
1209 
1210    bool skipping = true;
1211    for (int i = 0, s = 0; i < 4; i++, s += 8)
1212    {
1213       int dig = (mVersion >> s) & 0xff;
1214       if (dig != 0 || !skipping)
1215       {
1216          version += !skipping ? wxT(".") : wxT("");
1217          version += wxString::Format(wxT("%d"), dig);
1218          skipping = false;
1219       }
1220    }
1221 
1222    return version;
1223 }
1224 
GetDescription()1225 TranslatableString VSTEffect::GetDescription()
1226 {
1227    // VST does have a product string opcode and some effects return a short
1228    // description, but most do not or they just return the name again.  So,
1229    // try to provide some sort of useful information.
1230    return XO("Audio In: %d, Audio Out: %d").Format( mAudioIns, mAudioOuts );
1231 }
1232 
1233 // ============================================================================
1234 // EffectDefinitionInterface Implementation
1235 // ============================================================================
1236 
GetType()1237 EffectType VSTEffect::GetType()
1238 {
1239    if (mAudioIns == 0 && mAudioOuts == 0 && mMidiIns == 0 && mMidiOuts == 0)
1240    {
1241       return EffectTypeTool;
1242    }
1243 
1244    if (mAudioIns == 0 && mMidiIns == 0)
1245    {
1246       return EffectTypeGenerate;
1247    }
1248 
1249    if (mAudioOuts == 0 && mMidiOuts == 0)
1250    {
1251       return EffectTypeAnalyze;
1252    }
1253 
1254    return EffectTypeProcess;
1255 }
1256 
1257 
GetFamily()1258 EffectFamilySymbol VSTEffect::GetFamily()
1259 {
1260    return VSTPLUGINTYPE;
1261 }
1262 
IsInteractive()1263 bool VSTEffect::IsInteractive()
1264 {
1265    return mInteractive;
1266 }
1267 
IsDefault()1268 bool VSTEffect::IsDefault()
1269 {
1270    return false;
1271 }
1272 
IsLegacy()1273 bool VSTEffect::IsLegacy()
1274 {
1275    return false;
1276 }
1277 
SupportsRealtime()1278 bool VSTEffect::SupportsRealtime()
1279 {
1280    return GetType() == EffectTypeProcess;
1281 }
1282 
SupportsAutomation()1283 bool VSTEffect::SupportsAutomation()
1284 {
1285    return mAutomatable;
1286 }
1287 
1288 // ============================================================================
1289 // EffectClientInterface Implementation
1290 // ============================================================================
1291 
SetHost(EffectHostInterface * host)1292 bool VSTEffect::SetHost(EffectHostInterface *host)
1293 {
1294    mHost = host;
1295 
1296    if (!mAEffect)
1297    {
1298       Load();
1299    }
1300 
1301    if (!mAEffect)
1302    {
1303       return false;
1304    }
1305 
1306    // If we have a master then there's no need to load settings since the master will feed
1307    // us everything we need.
1308    if (mMaster)
1309    {
1310       return true;
1311    }
1312 
1313    if (mHost)
1314    {
1315       int userBlockSize;
1316       mHost->GetSharedConfig(wxT("Options"), wxT("BufferSize"), userBlockSize, 8192);
1317       mUserBlockSize = std::max( 1, userBlockSize );
1318       mHost->GetSharedConfig(wxT("Options"), wxT("UseLatency"), mUseLatency, true);
1319 
1320       mBlockSize = mUserBlockSize;
1321 
1322       bool haveDefaults;
1323       mHost->GetPrivateConfig(mHost->GetFactoryDefaultsGroup(), wxT("Initialized"), haveDefaults, false);
1324       if (!haveDefaults)
1325       {
1326          SaveParameters(mHost->GetFactoryDefaultsGroup());
1327          mHost->SetPrivateConfig(mHost->GetFactoryDefaultsGroup(), wxT("Initialized"), true);
1328       }
1329 
1330       LoadParameters(mHost->GetCurrentSettingsGroup());
1331    }
1332 
1333    return true;
1334 }
1335 
GetAudioInCount()1336 unsigned VSTEffect::GetAudioInCount()
1337 {
1338    return mAudioIns;
1339 }
1340 
GetAudioOutCount()1341 unsigned VSTEffect::GetAudioOutCount()
1342 {
1343    return mAudioOuts;
1344 }
1345 
GetMidiInCount()1346 int VSTEffect::GetMidiInCount()
1347 {
1348    return mMidiIns;
1349 }
1350 
GetMidiOutCount()1351 int VSTEffect::GetMidiOutCount()
1352 {
1353    return mMidiOuts;
1354 }
1355 
SetBlockSize(size_t maxBlockSize)1356 size_t VSTEffect::SetBlockSize(size_t maxBlockSize)
1357 {
1358    mBlockSize = std::min( maxBlockSize, mUserBlockSize );
1359    return mBlockSize;
1360 }
1361 
GetBlockSize() const1362 size_t VSTEffect::GetBlockSize() const
1363 {
1364    return mBlockSize;
1365 }
1366 
SetSampleRate(double rate)1367 void VSTEffect::SetSampleRate(double rate)
1368 {
1369    mSampleRate = (float) rate;
1370 }
1371 
GetLatency()1372 sampleCount VSTEffect::GetLatency()
1373 {
1374    if (mUseLatency)
1375    {
1376       // ??? Threading issue ???
1377       auto delay = mBufferDelay;
1378       mBufferDelay = 0;
1379       return delay;
1380    }
1381 
1382    return 0;
1383 }
1384 
GetTailSize()1385 size_t VSTEffect::GetTailSize()
1386 {
1387    return 0;
1388 }
1389 
IsReady()1390 bool VSTEffect::IsReady()
1391 {
1392    return mReady;
1393 }
1394 
ProcessInitialize(sampleCount WXUNUSED (totalLen),ChannelNames WXUNUSED (chanMap))1395 bool VSTEffect::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames WXUNUSED(chanMap))
1396 {
1397    // Initialize time info
1398    memset(&mTimeInfo, 0, sizeof(mTimeInfo));
1399    mTimeInfo.sampleRate = mSampleRate;
1400    mTimeInfo.nanoSeconds = wxGetUTCTimeMillis().ToDouble();
1401    mTimeInfo.tempo = 120.0;
1402    mTimeInfo.timeSigNumerator = 4;
1403    mTimeInfo.timeSigDenominator = 4;
1404    mTimeInfo.flags = kVstTempoValid | kVstNanosValid | kVstTransportPlaying;
1405 
1406    // Set processing parameters...power must be off for this
1407    callDispatcher(effSetSampleRate, 0, 0, NULL, mSampleRate);
1408    callDispatcher(effSetBlockSize, 0, mBlockSize, NULL, 0.0);
1409 
1410    // Turn on the power
1411    PowerOn();
1412 
1413    // Set the initial buffer delay
1414    SetBufferDelay(mAEffect->initialDelay);
1415 
1416    mReady = true;
1417 
1418    return true;
1419 }
1420 
ProcessFinalize()1421 bool VSTEffect::ProcessFinalize()
1422 {
1423    mReady = false;
1424 
1425    PowerOff();
1426 
1427    return true;
1428 }
1429 
ProcessBlock(float ** inBlock,float ** outBlock,size_t blockLen)1430 size_t VSTEffect::ProcessBlock(float **inBlock, float **outBlock, size_t blockLen)
1431 {
1432    // Only call the effect if there's something to do...some do not like zero-length block
1433    if (blockLen)
1434    {
1435       // Go let the plugin moleste the samples
1436       callProcessReplacing(inBlock, outBlock, blockLen);
1437 
1438       // And track the position
1439       mTimeInfo.samplePos += (double) blockLen;
1440    }
1441 
1442    return blockLen;
1443 }
1444 
GetChannelCount()1445 unsigned VSTEffect::GetChannelCount()
1446 {
1447    return mNumChannels;
1448 }
1449 
SetChannelCount(unsigned numChannels)1450 void VSTEffect::SetChannelCount(unsigned numChannels)
1451 {
1452    mNumChannels = numChannels;
1453 }
1454 
RealtimeInitialize()1455 bool VSTEffect::RealtimeInitialize()
1456 {
1457    mMasterIn.reinit( mAudioIns, mBlockSize, true );
1458    mMasterOut.reinit( mAudioOuts, mBlockSize );
1459 
1460    return ProcessInitialize(0, NULL);
1461 }
1462 
RealtimeAddProcessor(unsigned numChannels,float sampleRate)1463 bool VSTEffect::RealtimeAddProcessor(unsigned numChannels, float sampleRate)
1464 {
1465    mSlaves.push_back(std::make_unique<VSTEffect>(mPath, this));
1466    VSTEffect *const slave = mSlaves.back().get();
1467 
1468    slave->SetBlockSize(mBlockSize);
1469    slave->SetChannelCount(numChannels);
1470    slave->SetSampleRate(sampleRate);
1471 
1472    int clen = 0;
1473    if (mAEffect->flags & effFlagsProgramChunks)
1474    {
1475       void *chunk = NULL;
1476 
1477       clen = (int) callDispatcher(effGetChunk, 1, 0, &chunk, 0.0); // get master's chunk, for the program only
1478       if (clen != 0)
1479       {
1480          slave->callSetChunk(true, clen, chunk); // copy state to slave, for the program only
1481       }
1482    }
1483 
1484    if (clen == 0)
1485    {
1486       callDispatcher(effBeginSetProgram, 0, 0, NULL, 0.0);
1487 
1488       for (int i = 0; i < mAEffect->numParams; i++)
1489       {
1490          slave->callSetParameter(i, callGetParameter(i));
1491       }
1492 
1493       callDispatcher(effEndSetProgram, 0, 0, NULL, 0.0);
1494    }
1495 
1496    return slave->ProcessInitialize(0, NULL);
1497 }
1498 
RealtimeFinalize()1499 bool VSTEffect::RealtimeFinalize()
1500 {
1501    for (const auto &slave : mSlaves)
1502       slave->ProcessFinalize();
1503    mSlaves.clear();
1504 
1505    mMasterIn.reset();
1506 
1507    mMasterOut.reset();
1508 
1509    return ProcessFinalize();
1510 }
1511 
RealtimeSuspend()1512 bool VSTEffect::RealtimeSuspend()
1513 {
1514    PowerOff();
1515 
1516    for (const auto &slave : mSlaves)
1517       slave->PowerOff();
1518 
1519    return true;
1520 }
1521 
RealtimeResume()1522 bool VSTEffect::RealtimeResume()
1523 {
1524    PowerOn();
1525 
1526    for (const auto &slave : mSlaves)
1527       slave->PowerOn();
1528 
1529    return true;
1530 }
1531 
RealtimeProcessStart()1532 bool VSTEffect::RealtimeProcessStart()
1533 {
1534    for (unsigned int i = 0; i < mAudioIns; i++)
1535       memset(mMasterIn[i].get(), 0, mBlockSize * sizeof(float));
1536 
1537    mNumSamples = 0;
1538 
1539    return true;
1540 }
1541 
RealtimeProcess(int group,float ** inbuf,float ** outbuf,size_t numSamples)1542 size_t VSTEffect::RealtimeProcess(int group, float **inbuf, float **outbuf, size_t numSamples)
1543 {
1544    wxASSERT(numSamples <= mBlockSize);
1545 
1546    for (unsigned int c = 0; c < mAudioIns; c++)
1547    {
1548       for (decltype(numSamples) s = 0; s < numSamples; s++)
1549       {
1550          mMasterIn[c][s] += inbuf[c][s];
1551       }
1552    }
1553    mNumSamples = std::max(numSamples, mNumSamples);
1554 
1555    return mSlaves[group]->ProcessBlock(inbuf, outbuf, numSamples);
1556 }
1557 
RealtimeProcessEnd()1558 bool VSTEffect::RealtimeProcessEnd()
1559 {
1560    // These casts to float** should be safe...
1561    ProcessBlock(
1562       reinterpret_cast <float**> (mMasterIn.get()),
1563       reinterpret_cast <float**> (mMasterOut.get()),
1564       mNumSamples);
1565 
1566    return true;
1567 }
1568 
1569 ///
1570 /// Some history...
1571 ///
1572 /// Before we ran into the Antress plugin problem with buffer size limitations,
1573 /// (see below) we just had a plain old effect loop...get the input samples, pass
1574 /// them to the effect, save the output samples.
1575 ///
1576 /// But, the hack I put in to limit the buffer size to only 8k (normally 512k or so)
1577 /// severely impacted performance.  So, Michael C. added some intermediate buffering
1578 /// that sped things up quite a bit and this is how things have worked for quite a
1579 /// while.  It still didn't get the performance back to the pre-hack stage, but it
1580 /// was a definite benefit.
1581 ///
1582 /// History over...
1583 ///
1584 /// I've recently (May 2014) tried newer versions of the Antress effects and they
1585 /// no longer seem to have a problem with buffer size.  So, I've made a bit of a
1586 /// compromise...I've made the buffer size user configurable.  Should have done this
1587 /// from the beginning.  I've left the default 8k, just in case, but now the user
1588 /// can set the buffering based on their specific setup and needs.
1589 ///
1590 /// And at the same time I added buffer delay compensation, which allows Audacity
1591 /// to account for latency introduced by some effects.  This is based on information
1592 /// provided by the effect, so it will not work with all effects since they don't
1593 /// all provide the information (kn0ck0ut is one).
1594 ///
ShowInterface(wxWindow & parent,const EffectDialogFactory & factory,bool forceModal)1595 bool VSTEffect::ShowInterface(
1596    wxWindow &parent, const EffectDialogFactory &factory, bool forceModal)
1597 {
1598    if (mDialog)
1599    {
1600       if ( mDialog->Close(true) )
1601          mDialog = nullptr;
1602       return false;
1603    }
1604 
1605    // mDialog is null
1606    auto cleanup = valueRestorer( mDialog );
1607 
1608    //   mProcessLevel = 1;      // in GUI thread
1609 
1610    // Set some defaults since some VSTs need them...these will be reset when
1611    // normal or realtime processing begins
1612    if (!IsReady())
1613    {
1614       mSampleRate = 44100;
1615       mBlockSize = 8192;
1616       ProcessInitialize(0, NULL);
1617    }
1618 
1619    if ( factory )
1620       mDialog = factory(parent, mHost, this);
1621    if (!mDialog)
1622    {
1623       return false;
1624    }
1625    mDialog->CentreOnParent();
1626 
1627    if (SupportsRealtime() && !forceModal)
1628    {
1629       mDialog->Show();
1630       cleanup.release();
1631 
1632       return false;
1633    }
1634 
1635    bool res = mDialog->ShowModal() != 0;
1636 
1637    return res;
1638 }
1639 
GetAutomationParameters(CommandParameters & parms)1640 bool VSTEffect::GetAutomationParameters(CommandParameters & parms)
1641 {
1642    for (int i = 0; i < mAEffect->numParams; i++)
1643    {
1644       wxString name = GetString(effGetParamName, i);
1645       if (name.empty())
1646       {
1647          name.Printf(wxT("parm_%d"), i);
1648       }
1649 
1650       float value = callGetParameter(i);
1651       if (!parms.Write(name, value))
1652       {
1653          return false;
1654       }
1655    }
1656 
1657    return true;
1658 }
1659 
SetAutomationParameters(CommandParameters & parms)1660 bool VSTEffect::SetAutomationParameters(CommandParameters & parms)
1661 {
1662    callDispatcher(effBeginSetProgram, 0, 0, NULL, 0.0);
1663    for (int i = 0; i < mAEffect->numParams; i++)
1664    {
1665       wxString name = GetString(effGetParamName, i);
1666       if (name.empty())
1667       {
1668          name.Printf(wxT("parm_%d"), i);
1669       }
1670 
1671       double d = 0.0;
1672       if (!parms.Read(name, &d))
1673       {
1674          return false;
1675       }
1676 
1677       if (d >= -1.0 && d <= 1.0)
1678       {
1679          callSetParameter(i, d);
1680          for (const auto &slave : mSlaves)
1681             slave->callSetParameter(i, d);
1682       }
1683    }
1684    callDispatcher(effEndSetProgram, 0, 0, NULL, 0.0);
1685 
1686    return true;
1687 }
1688 
1689 
LoadUserPreset(const RegistryPath & name)1690 bool VSTEffect::LoadUserPreset(const RegistryPath & name)
1691 {
1692    if (!LoadParameters(name))
1693    {
1694       return false;
1695    }
1696 
1697    RefreshParameters();
1698 
1699    return true;
1700 }
1701 
SaveUserPreset(const RegistryPath & name)1702 bool VSTEffect::SaveUserPreset(const RegistryPath & name)
1703 {
1704    return SaveParameters(name);
1705 }
1706 
GetFactoryPresets()1707 RegistryPaths VSTEffect::GetFactoryPresets()
1708 {
1709    RegistryPaths progs;
1710 
1711    // Some plugins, like Guitar Rig 5, only report 128 programs while they have hundreds.  While
1712    // I was able to come up with a hack in the Guitar Rig case to gather all of the program names
1713    // it would not let me set a program outside of the first 128.
1714    if (mVstVersion >= 2)
1715    {
1716       for (int i = 0; i < mAEffect->numPrograms; i++)
1717       {
1718          progs.push_back(GetString(effGetProgramNameIndexed, i));
1719       }
1720    }
1721 
1722    return progs;
1723 }
1724 
LoadFactoryPreset(int id)1725 bool VSTEffect::LoadFactoryPreset(int id)
1726 {
1727    callSetProgram(id);
1728 
1729    RefreshParameters();
1730 
1731    return true;
1732 }
1733 
LoadFactoryDefaults()1734 bool VSTEffect::LoadFactoryDefaults()
1735 {
1736    if (!LoadParameters(mHost->GetFactoryDefaultsGroup()))
1737    {
1738       return false;
1739    }
1740 
1741    RefreshParameters();
1742 
1743    return true;
1744 }
1745 
1746 // ============================================================================
1747 // EffectUIClientInterface implementation
1748 // ============================================================================
1749 
SetHostUI(EffectUIHostInterface * host)1750 void VSTEffect::SetHostUI(EffectUIHostInterface *host)
1751 {
1752    mUIHost = host;
1753 }
1754 
PopulateUI(ShuttleGui & S)1755 bool VSTEffect::PopulateUI(ShuttleGui &S)
1756 {
1757    auto parent = S.GetParent();
1758    mDialog = static_cast<wxDialog *>(wxGetTopLevelParent(parent));
1759    mParent = parent;
1760 
1761    mParent->PushEventHandler(this);
1762 
1763    // Determine if the VST editor is supposed to be used or not
1764    mHost->GetSharedConfig(wxT("Options"),
1765                           wxT("UseGUI"),
1766                           mGui,
1767                           true);
1768    mGui = mAEffect->flags & effFlagsHasEditor ? mGui : false;
1769 
1770    // Must use the GUI editor if parameters aren't provided
1771    if (mAEffect->numParams == 0)
1772    {
1773       mGui = true;
1774    }
1775 
1776    // Build the appropriate dialog type
1777    if (mGui)
1778    {
1779       BuildFancy();
1780    }
1781    else
1782    {
1783       BuildPlain();
1784    }
1785 
1786    return true;
1787 }
1788 
IsGraphicalUI()1789 bool VSTEffect::IsGraphicalUI()
1790 {
1791    return mGui;
1792 }
1793 
ValidateUI()1794 bool VSTEffect::ValidateUI()
1795 {
1796    if (!mParent->Validate() || !mParent->TransferDataFromWindow())
1797    {
1798       return false;
1799    }
1800 
1801    if (GetType() == EffectTypeGenerate)
1802    {
1803       mHost->SetDuration(mDuration->GetValue());
1804    }
1805 
1806    return true;
1807 }
1808 
HideUI()1809 bool VSTEffect::HideUI()
1810 {
1811    return true;
1812 }
1813 
CloseUI()1814 bool VSTEffect::CloseUI()
1815 {
1816 #ifdef __WXMAC__
1817 #ifdef __WX_EVTLOOP_BUSY_WAITING__
1818    wxEventLoop::SetBusyWaiting(false);
1819 #endif
1820    mControl->Close();
1821 #endif
1822 
1823    mParent->RemoveEventHandler(this);
1824 
1825    PowerOff();
1826 
1827    NeedEditIdle(false);
1828 
1829    RemoveHandler();
1830 
1831    mNames.reset();
1832    mSliders.reset();
1833    mDisplays.reset();
1834    mLabels.reset();
1835 
1836    mUIHost = NULL;
1837    mParent = NULL;
1838    mDialog = NULL;
1839 
1840    return true;
1841 }
1842 
CanExportPresets()1843 bool VSTEffect::CanExportPresets()
1844 {
1845    return true;
1846 }
1847 
1848 // Throws exceptions rather than reporting errors.
ExportPresets()1849 void VSTEffect::ExportPresets()
1850 {
1851    wxString path;
1852 
1853    // Ask the user for the real name
1854    //
1855    // Passing a valid parent will cause some effects dialogs to malfunction
1856    // upon returning from the SelectFile().
1857    path = SelectFile(FileNames::Operation::Presets,
1858       XO("Save VST Preset As:"),
1859       wxEmptyString,
1860       wxT("preset"),
1861       wxT("xml"),
1862       {
1863         { XO("Standard VST bank file"), { wxT("fxb") }, true },
1864         { XO("Standard VST program file"), { wxT("fxp") }, true },
1865         { XO("Audacity VST preset file"), { wxT("xml") }, true },
1866       },
1867       wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER,
1868       NULL);
1869 
1870    // User canceled...
1871    if (path.empty())
1872    {
1873       return;
1874    }
1875 
1876    wxFileName fn(path);
1877    wxString ext = fn.GetExt();
1878    if (ext.CmpNoCase(wxT("fxb")) == 0)
1879    {
1880       SaveFXB(fn);
1881    }
1882    else if (ext.CmpNoCase(wxT("fxp")) == 0)
1883    {
1884       SaveFXP(fn);
1885    }
1886    else if (ext.CmpNoCase(wxT("xml")) == 0)
1887    {
1888       // may throw
1889       SaveXML(fn);
1890    }
1891    else
1892    {
1893       // This shouldn't happen, but complain anyway
1894       AudacityMessageBox(
1895          XO("Unrecognized file extension."),
1896          XO("Error Saving VST Presets"),
1897          wxOK | wxCENTRE,
1898          mParent);
1899 
1900       return;
1901    }
1902 }
1903 
1904 //
1905 // Load an "fxb", "fxp" or Audacuty "xml" file
1906 //
1907 // Based on work by Sven Giermann
1908 //
ImportPresets()1909 void VSTEffect::ImportPresets()
1910 {
1911    wxString path;
1912 
1913    // Ask the user for the real name
1914    path = SelectFile(FileNames::Operation::Presets,
1915       XO("Load VST Preset:"),
1916       wxEmptyString,
1917       wxT("preset"),
1918       wxT("xml"),
1919       { {
1920          XO("VST preset files"),
1921          { wxT("fxb"), wxT("fxp"), wxT("xml") },
1922          true
1923       } },
1924       wxFD_OPEN | wxRESIZE_BORDER,
1925       mParent);
1926 
1927    // User canceled...
1928    if (path.empty())
1929    {
1930       return;
1931    }
1932 
1933    wxFileName fn(path);
1934    wxString ext = fn.GetExt();
1935    bool success = false;
1936    if (ext.CmpNoCase(wxT("fxb")) == 0)
1937    {
1938       success = LoadFXB(fn);
1939    }
1940    else if (ext.CmpNoCase(wxT("fxp")) == 0)
1941    {
1942       success = LoadFXP(fn);
1943    }
1944    else if (ext.CmpNoCase(wxT("xml")) == 0)
1945    {
1946       success = LoadXML(fn);
1947    }
1948    else
1949    {
1950       // This shouldn't happen, but complain anyway
1951       AudacityMessageBox(
1952          XO("Unrecognized file extension."),
1953          XO("Error Loading VST Presets"),
1954          wxOK | wxCENTRE,
1955          mParent);
1956 
1957          return;
1958    }
1959 
1960    if (!success)
1961    {
1962       AudacityMessageBox(
1963          XO("Unable to load presets file."),
1964          XO("Error Loading VST Presets"),
1965          wxOK | wxCENTRE,
1966          mParent);
1967 
1968       return;
1969    }
1970 
1971    RefreshParameters();
1972 
1973    return;
1974 }
1975 
HasOptions()1976 bool VSTEffect::HasOptions()
1977 {
1978    return true;
1979 }
1980 
ShowOptions()1981 void VSTEffect::ShowOptions()
1982 {
1983    VSTEffectOptionsDialog dlg(mParent, mHost);
1984    if (dlg.ShowModal())
1985    {
1986       // Reinitialize configuration settings
1987       int userBlockSize;
1988       mHost->GetSharedConfig(wxT("Options"), wxT("BufferSize"), userBlockSize, 8192);
1989       mUserBlockSize = std::max( 1, userBlockSize );
1990       mHost->GetSharedConfig(wxT("Options"), wxT("UseLatency"), mUseLatency, true);
1991    }
1992 }
1993 
1994 // ============================================================================
1995 // VSTEffect implementation
1996 // ============================================================================
1997 
Load()1998 bool VSTEffect::Load()
1999 {
2000    vstPluginMain pluginMain;
2001    bool success = false;
2002 
2003    long effectID = 0;
2004    wxString realPath = mPath.BeforeFirst(wxT(';'));
2005    mPath.AfterFirst(wxT(';')).ToLong(&effectID);
2006    mCurrentEffectID = (intptr_t) effectID;
2007 
2008    mModule = NULL;
2009    mAEffect = NULL;
2010 
2011 #if defined(__WXMAC__)
2012    // Start clean
2013    mBundleRef.reset();
2014 
2015    mResource = ResourceHandle{};
2016 
2017    // Convert the path to a CFSTring
2018    wxCFStringRef path(realPath);
2019 
2020    // Convert the path to a URL
2021    CFURLRef urlRef =
2022       CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
2023       path,
2024       kCFURLPOSIXPathStyle,
2025       true);
2026    if (urlRef == NULL)
2027    {
2028       return false;
2029    }
2030 
2031    // Create the bundle using the URL
2032    BundleHandle bundleRef{ CFBundleCreate(kCFAllocatorDefault, urlRef) };
2033 
2034    // Done with the URL
2035    CFRelease(urlRef);
2036 
2037    // Bail if the bundle wasn't created
2038    if (!bundleRef)
2039    {
2040       return false;
2041    }
2042 
2043    // Retrieve a reference to the executable
2044    CFURLRef exeRef = CFBundleCopyExecutableURL(bundleRef.get());
2045    if (!exeRef)
2046       return false;
2047 
2048    // Convert back to path
2049    UInt8 exePath[PLATFORM_MAX_PATH];
2050    Boolean good = CFURLGetFileSystemRepresentation(exeRef, true, exePath, sizeof(exePath));
2051 
2052    // Done with the executable reference
2053    CFRelease(exeRef);
2054 
2055    // Bail if we couldn't resolve the executable path
2056    if (good == FALSE)
2057       return false;
2058 
2059    // Attempt to open it
2060    mModule.reset((char*)dlopen((char *) exePath, RTLD_NOW | RTLD_LOCAL));
2061    if (!mModule)
2062       return false;
2063 
2064    // Try to locate the NEW plugin entry point
2065    pluginMain = (vstPluginMain) dlsym(mModule.get(), "VSTPluginMain");
2066 
2067    // If not found, try finding the old entry point
2068    if (pluginMain == NULL)
2069    {
2070       pluginMain = (vstPluginMain) dlsym(mModule.get(), "main_macho");
2071    }
2072 
2073    // Must not be a VST plugin
2074    if (pluginMain == NULL)
2075    {
2076       mModule.reset();
2077       return false;
2078    }
2079 
2080    // Need to keep the bundle reference around so we can map the
2081    // resources.
2082    mBundleRef = std::move(bundleRef);
2083 
2084    // Open the resource map ... some plugins (like GRM Tools) need this.
2085    mResource = ResourceHandle{
2086       mBundleRef.get(), CFBundleOpenBundleResourceMap(mBundleRef.get())
2087    };
2088 
2089 #elif defined(__WXMSW__)
2090 
2091    {
2092       wxLogNull nolog;
2093 
2094       // Try to load the library
2095       auto lib = std::make_unique<wxDynamicLibrary>(realPath);
2096       if (!lib)
2097          return false;
2098 
2099       // Bail if it wasn't successful
2100       if (!lib->IsLoaded())
2101          return false;
2102 
2103       // Try to find the entry point, while suppressing error messages
2104       pluginMain = (vstPluginMain) lib->GetSymbol(wxT("VSTPluginMain"));
2105       if (pluginMain == NULL)
2106       {
2107          pluginMain = (vstPluginMain) lib->GetSymbol(wxT("main"));
2108          if (pluginMain == NULL)
2109             return false;
2110       }
2111 
2112       // Save the library reference
2113       mModule = std::move(lib);
2114    }
2115 
2116 #else
2117 
2118    // Attempt to load it
2119    //
2120    // Spent a few days trying to figure out why some VSTs where running okay and
2121    // others were hit or miss.  The cause was that we export all of Audacity's
2122    // symbols and some of the loaded libraries were picking up Audacity's and
2123    // not their own.
2124    //
2125    // So far, I've only seen this issue on Linux, but we might just be getting
2126    // lucky on the Mac and Windows.  The sooner we stop exporting everything
2127    // the better.
2128    //
2129    // To get around the problem, I just added the RTLD_DEEPBIND flag to the load
2130    // and that "basically" puts Audacity last when the loader needs to resolve
2131    // symbols.
2132    //
2133    // Once we define a proper external API, the flags can be removed.
2134 #ifndef RTLD_DEEPBIND
2135 #define RTLD_DEEPBIND 0
2136 #endif
2137    ModuleHandle lib {
2138       (char*) dlopen((const char *)wxString(realPath).ToUTF8(),
2139                      RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND)
2140    };
2141    if (!lib)
2142    {
2143       return false;
2144    }
2145 
2146    // Try to find the entry point, while suppressing error messages
2147    pluginMain = (vstPluginMain) dlsym(lib.get(), "VSTPluginMain");
2148    if (pluginMain == NULL)
2149    {
2150       pluginMain = (vstPluginMain) dlsym(lib.get(), "main");
2151       if (pluginMain == NULL)
2152          return false;
2153    }
2154 
2155    // Save the library reference
2156    mModule = std::move(lib);
2157 
2158 #endif
2159 
2160    // Initialize the plugin
2161    try
2162    {
2163       mAEffect = pluginMain(VSTEffect::AudioMaster);
2164    }
2165    catch (...)
2166    {
2167       wxLogMessage(wxT("VST plugin initialization failed\n"));
2168       mAEffect = NULL;
2169    }
2170 
2171    // Was it successful?
2172    if (mAEffect)
2173    {
2174       // Save a reference to ourselves
2175       //
2176       // Note:  Some hosts use "user" and some use "ptr2/resvd2".  It might
2177       //        be worthwhile to check if user is NULL before using it and
2178       //        then falling back to "ptr2/resvd2".
2179       mAEffect->ptr2 = this;
2180 
2181       // Give the plugin an initial sample rate and blocksize
2182       callDispatcher(effSetSampleRate, 0, 0, NULL, 48000.0);
2183       callDispatcher(effSetBlockSize, 0, 512, NULL, 0);
2184 
2185       // Ask the plugin to identify itself...might be needed for older plugins
2186       callDispatcher(effIdentify, 0, 0, NULL, 0);
2187 
2188       // Open the plugin
2189       callDispatcher(effOpen, 0, 0, NULL, 0.0);
2190 
2191       // Get the VST version the plugin understands
2192       mVstVersion = callDispatcher(effGetVstVersion, 0, 0, NULL, 0);
2193 
2194       // Set it again in case plugin ignored it before the effOpen
2195       callDispatcher(effSetSampleRate, 0, 0, NULL, 48000.0);
2196       callDispatcher(effSetBlockSize, 0, 512, NULL, 0);
2197 
2198       // Ensure that it looks like a plugin and can deal with ProcessReplacing
2199       // calls.  Also exclude synths for now.
2200       if (mAEffect->magic == kEffectMagic &&
2201          !(mAEffect->flags & effFlagsIsSynth) &&
2202          mAEffect->flags & effFlagsCanReplacing)
2203       {
2204          if (mVstVersion >= 2)
2205          {
2206             mName = GetString(effGetEffectName);
2207             if (mName.length() == 0)
2208             {
2209                mName = GetString(effGetProductString);
2210             }
2211          }
2212          if (mName.length() == 0)
2213          {
2214             mName = wxFileName{realPath}.GetName();
2215          }
2216 
2217          if (mVstVersion >= 2)
2218          {
2219             mVendor = GetString(effGetVendorString);
2220             mVersion = wxINT32_SWAP_ON_LE(callDispatcher(effGetVendorVersion, 0, 0, NULL, 0));
2221          }
2222          if (mVersion == 0)
2223          {
2224             mVersion = wxINT32_SWAP_ON_LE(mAEffect->version);
2225          }
2226 
2227          if (mAEffect->flags & effFlagsHasEditor || mAEffect->numParams != 0)
2228          {
2229             mInteractive = true;
2230          }
2231 
2232          mAudioIns = mAEffect->numInputs;
2233          mAudioOuts = mAEffect->numOutputs;
2234 
2235          mMidiIns = 0;
2236          mMidiOuts = 0;
2237 
2238          // Check to see if parameters can be automated.  This isn't a guarantee
2239          // since it could be that the effect simply doesn't support the opcode.
2240          mAutomatable = false;
2241          for (int i = 0; i < mAEffect->numParams; i++)
2242          {
2243             if (callDispatcher(effCanBeAutomated, 0, i, NULL, 0.0))
2244             {
2245                mAutomatable = true;
2246                break;
2247             }
2248          }
2249 
2250          // Make sure we start out with a valid program selection
2251          // I've found one plugin (SoundHack +morphfilter) that will
2252          // crash Audacity when saving the initial default parameters
2253          // with this.
2254          callSetProgram(0);
2255 
2256          // Pretty confident that we're good to go
2257          success = true;
2258       }
2259    }
2260 
2261    if (!success)
2262    {
2263       Unload();
2264    }
2265 
2266    return success;
2267 }
2268 
Unload()2269 void VSTEffect::Unload()
2270 {
2271    if (mDialog)
2272    {
2273       CloseUI();
2274    }
2275 
2276    if (mAEffect)
2277    {
2278       // Turn the power off
2279       PowerOff();
2280 
2281       // Finally, close the plugin
2282       callDispatcher(effClose, 0, 0, NULL, 0.0);
2283       mAEffect = NULL;
2284    }
2285 
2286    if (mModule)
2287    {
2288 #if defined(__WXMAC__)
2289       mResource.reset();
2290       mBundleRef.reset();
2291 #endif
2292 
2293       mModule.reset();
2294       mAEffect = NULL;
2295    }
2296 }
2297 
GetEffectIDs()2298 std::vector<int> VSTEffect::GetEffectIDs()
2299 {
2300    std::vector<int> effectIDs;
2301 
2302    // Are we a shell?
2303    if (mVstVersion >= 2 && (VstPlugCategory) callDispatcher(effGetPlugCategory, 0, 0, NULL, 0) == kPlugCategShell)
2304    {
2305       char name[64];
2306       int effectID;
2307 
2308       effectID = (int) callDispatcher(effShellGetNextPlugin, 0, 0, &name, 0);
2309       while (effectID)
2310       {
2311          effectIDs.push_back(effectID);
2312          effectID = (int) callDispatcher(effShellGetNextPlugin, 0, 0, &name, 0);
2313       }
2314    }
2315 
2316    return effectIDs;
2317 }
2318 
LoadParameters(const RegistryPath & group)2319 bool VSTEffect::LoadParameters(const RegistryPath & group)
2320 {
2321    wxString value;
2322 
2323    VstPatchChunkInfo info = {1, mAEffect->uniqueID, mAEffect->version, mAEffect->numParams, ""};
2324    mHost->GetPrivateConfig(group, wxT("UniqueID"), info.pluginUniqueID, info.pluginUniqueID);
2325    mHost->GetPrivateConfig(group, wxT("Version"), info.pluginVersion, info.pluginVersion);
2326    mHost->GetPrivateConfig(group, wxT("Elements"), info.numElements, info.numElements);
2327 
2328    if ((info.pluginUniqueID != mAEffect->uniqueID) ||
2329        (info.pluginVersion != mAEffect->version) ||
2330        (info.numElements != mAEffect->numParams))
2331    {
2332       return false;
2333    }
2334 
2335    if (mHost->GetPrivateConfig(group, wxT("Chunk"), value, wxEmptyString))
2336    {
2337       ArrayOf<char> buf{ value.length() / 4 * 3 };
2338 
2339       int len = VSTEffect::b64decode(value, buf.get());
2340       if (len)
2341       {
2342          callSetChunk(true, len, buf.get(), &info);
2343       }
2344 
2345       return true;
2346    }
2347 
2348    wxString parms;
2349    if (!mHost->GetPrivateConfig(group, wxT("Parameters"), parms, wxEmptyString))
2350    {
2351       return false;
2352    }
2353 
2354    CommandParameters eap;
2355    if (!eap.SetParameters(parms))
2356    {
2357       return false;
2358    }
2359 
2360    return SetAutomationParameters(eap);
2361 }
2362 
SaveParameters(const RegistryPath & group)2363 bool VSTEffect::SaveParameters(const RegistryPath & group)
2364 {
2365    mHost->SetPrivateConfig(group, wxT("UniqueID"), mAEffect->uniqueID);
2366    mHost->SetPrivateConfig(group, wxT("Version"), mAEffect->version);
2367    mHost->SetPrivateConfig(group, wxT("Elements"), mAEffect->numParams);
2368 
2369    if (mAEffect->flags & effFlagsProgramChunks)
2370    {
2371       void *chunk = NULL;
2372       int clen = (int) callDispatcher(effGetChunk, 1, 0, &chunk, 0.0);
2373       if (clen <= 0)
2374       {
2375          return false;
2376       }
2377 
2378       mHost->SetPrivateConfig(group, wxT("Chunk"), VSTEffect::b64encode(chunk, clen));
2379       return true;
2380    }
2381 
2382    CommandParameters eap;
2383    if (!GetAutomationParameters(eap))
2384    {
2385       return false;
2386    }
2387 
2388    wxString parms;
2389    if (!eap.GetParameters(parms))
2390    {
2391       return false;
2392    }
2393 
2394    return mHost->SetPrivateConfig(group, wxT("Parameters"), parms);
2395 }
2396 
OnTimer()2397 void VSTEffect::OnTimer()
2398 {
2399    wxRecursionGuard guard(mTimerGuard);
2400 
2401    // Ignore it if we're recursing
2402    if (guard.IsInside())
2403    {
2404       return;
2405    }
2406 
2407    if (mVstVersion >= 2 && mWantsIdle)
2408    {
2409       int ret = callDispatcher(effIdle, 0, 0, NULL, 0.0);
2410       if (!ret)
2411       {
2412          mWantsIdle = false;
2413       }
2414    }
2415 
2416    if (mWantsEditIdle)
2417    {
2418       callDispatcher(effEditIdle, 0, 0, NULL, 0.0);
2419    }
2420 }
2421 
NeedIdle()2422 void VSTEffect::NeedIdle()
2423 {
2424    mWantsIdle = true;
2425    mTimer->Start(100);
2426 }
2427 
NeedEditIdle(bool state)2428 void VSTEffect::NeedEditIdle(bool state)
2429 {
2430    mWantsEditIdle = state;
2431    mTimer->Start(100);
2432 }
2433 
GetTimeInfo()2434 VstTimeInfo *VSTEffect::GetTimeInfo()
2435 {
2436    mTimeInfo.nanoSeconds = wxGetUTCTimeMillis().ToDouble();
2437    return &mTimeInfo;
2438 }
2439 
GetSampleRate()2440 float VSTEffect::GetSampleRate()
2441 {
2442    return mTimeInfo.sampleRate;
2443 }
2444 
GetProcessLevel()2445 int VSTEffect::GetProcessLevel()
2446 {
2447    return mProcessLevel;
2448 }
2449 
PowerOn()2450 void VSTEffect::PowerOn()
2451 {
2452    if (!mHasPower)
2453    {
2454       // Turn the power on
2455       callDispatcher(effMainsChanged, 0, 1, NULL, 0.0);
2456 
2457       // Tell the effect we're going to start processing
2458       if (mVstVersion >= 2)
2459       {
2460          callDispatcher(effStartProcess, 0, 0, NULL, 0.0);
2461       }
2462 
2463       // Set state
2464       mHasPower = true;
2465    }
2466 }
2467 
PowerOff()2468 void VSTEffect::PowerOff()
2469 {
2470    if (mHasPower)
2471    {
2472       // Tell the effect we're going to stop processing
2473       if (mVstVersion >= 2)
2474       {
2475          callDispatcher(effStopProcess, 0, 0, NULL, 0.0);
2476       }
2477 
2478       // Turn the power off
2479       callDispatcher(effMainsChanged, 0, 0, NULL, 0.0);
2480 
2481       // Set state
2482       mHasPower = false;
2483    }
2484 }
2485 
SizeWindow(int w,int h)2486 void VSTEffect::SizeWindow(int w, int h)
2487 {
2488    // Queue the event to make the resizes smoother
2489    if (mParent)
2490    {
2491       wxCommandEvent sw(EVT_SIZEWINDOW);
2492       sw.SetInt(w);
2493       sw.SetExtraLong(h);
2494       mParent->GetEventHandler()->AddPendingEvent(sw);
2495    }
2496 
2497    return;
2498 }
2499 
UpdateDisplay()2500 void VSTEffect::UpdateDisplay()
2501 {
2502 #if 0
2503    // Tell the dialog to refresh effect information
2504    if (mParent)
2505    {
2506       wxCommandEvent ud(EVT_UPDATEDISPLAY);
2507       mParent->GetEventHandler()->AddPendingEvent(ud);
2508    }
2509 #endif
2510    return;
2511 }
2512 
Automate(int index,float value)2513 void VSTEffect::Automate(int index, float value)
2514 {
2515    // Just ignore it if we're a slave
2516    if (mMaster)
2517    {
2518       return;
2519    }
2520 
2521    for (const auto &slave : mSlaves)
2522       slave->callSetParameter(index, value);
2523 
2524    return;
2525 }
2526 
SetBufferDelay(int samples)2527 void VSTEffect::SetBufferDelay(int samples)
2528 {
2529    // We do not support negative delay
2530    if (samples >= 0 && mUseLatency)
2531    {
2532       mBufferDelay = samples;
2533    }
2534 
2535    return;
2536 }
2537 
GetString(wxString & outstr,int opcode,int index)2538 int VSTEffect::GetString(wxString & outstr, int opcode, int index)
2539 {
2540    char buf[256];
2541 
2542    memset(buf, 0, sizeof(buf));
2543 
2544    callDispatcher(opcode, index, 0, buf, 0.0);
2545 
2546    outstr = wxString::FromUTF8(buf);
2547 
2548    return 0;
2549 }
2550 
GetString(int opcode,int index)2551 wxString VSTEffect::GetString(int opcode, int index)
2552 {
2553    wxString str;
2554 
2555    GetString(str, opcode, index);
2556 
2557    return str;
2558 }
2559 
SetString(int opcode,const wxString & str,int index)2560 void VSTEffect::SetString(int opcode, const wxString & str, int index)
2561 {
2562    char buf[256];
2563    strcpy(buf, str.Left(255).ToUTF8());
2564 
2565    callDispatcher(opcode, index, 0, buf, 0.0);
2566 }
2567 
callDispatcher(int opcode,int index,intptr_t value,void * ptr,float opt)2568 intptr_t VSTEffect::callDispatcher(int opcode,
2569                                    int index, intptr_t value, void *ptr, float opt)
2570 {
2571    // Needed since we might be in the dispatcher when the timer pops
2572    wxCRIT_SECT_LOCKER(locker, mDispatcherLock);
2573    return mAEffect->dispatcher(mAEffect, opcode, index, value, ptr, opt);
2574 }
2575 
callProcessReplacing(float ** inputs,float ** outputs,int sampleframes)2576 void VSTEffect::callProcessReplacing(float **inputs,
2577                                      float **outputs, int sampleframes)
2578 {
2579    mAEffect->processReplacing(mAEffect, inputs, outputs, sampleframes);
2580 }
2581 
callGetParameter(int index)2582 float VSTEffect::callGetParameter(int index)
2583 {
2584    return mAEffect->getParameter(mAEffect, index);
2585 }
2586 
callSetParameter(int index,float value)2587 void VSTEffect::callSetParameter(int index, float value)
2588 {
2589    if (mVstVersion == 0 || callDispatcher(effCanBeAutomated, 0, index, NULL, 0.0))
2590    {
2591       mAEffect->setParameter(mAEffect, index, value);
2592 
2593       for (const auto &slave : mSlaves)
2594          slave->callSetParameter(index, value);
2595    }
2596 }
2597 
callSetProgram(int index)2598 void VSTEffect::callSetProgram(int index)
2599 {
2600    callDispatcher(effBeginSetProgram, 0, 0, NULL, 0.0);
2601 
2602    callDispatcher(effSetProgram, 0, index, NULL, 0.0);
2603    for (const auto &slave : mSlaves)
2604       slave->callSetProgram(index);
2605 
2606    callDispatcher(effEndSetProgram, 0, 0, NULL, 0.0);
2607 }
2608 
callSetChunk(bool isPgm,int len,void * buf)2609 void VSTEffect::callSetChunk(bool isPgm, int len, void *buf)
2610 {
2611    VstPatchChunkInfo info;
2612 
2613    memset(&info, 0, sizeof(info));
2614    info.version = 1;
2615    info.pluginUniqueID = mAEffect->uniqueID;
2616    info.pluginVersion = mAEffect->version;
2617    info.numElements = isPgm ? mAEffect->numParams : mAEffect->numPrograms;
2618 
2619    callSetChunk(isPgm, len, buf, &info);
2620 }
2621 
callSetChunk(bool isPgm,int len,void * buf,VstPatchChunkInfo * info)2622 void VSTEffect::callSetChunk(bool isPgm, int len, void *buf, VstPatchChunkInfo *info)
2623 {
2624    if (isPgm)
2625    {
2626       // Ask the effect if this is an acceptable program
2627       if (callDispatcher(effBeginLoadProgram, 0, 0, info, 0.0) == -1)
2628       {
2629          return;
2630       }
2631    }
2632    else
2633    {
2634       // Ask the effect if this is an acceptable bank
2635       if (callDispatcher(effBeginLoadBank, 0, 0, info, 0.0) == -1)
2636       {
2637          return;
2638       }
2639    }
2640 
2641    callDispatcher(effBeginSetProgram, 0, 0, NULL, 0.0);
2642    callDispatcher(effSetChunk, isPgm ? 1 : 0, len, buf, 0.0);
2643    callDispatcher(effEndSetProgram, 0, 0, NULL, 0.0);
2644 
2645    for (const auto &slave : mSlaves)
2646       slave->callSetChunk(isPgm, len, buf, info);
2647 }
2648 
2649 ////////////////////////////////////////////////////////////////////////////////
2650 // Base64 en/decoding
2651 //
2652 // Original routines marked as public domain and found at:
2653 //
2654 // http://en.wikibooks.org/wiki/Algorithm_implementation/Miscellaneous/Base64
2655 //
2656 ////////////////////////////////////////////////////////////////////////////////
2657 
2658 // Lookup table for encoding
2659 const static wxChar cset[] = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
2660 const static char padc = wxT('=');
2661 
b64encode(const void * in,int len)2662 wxString VSTEffect::b64encode(const void *in, int len)
2663 {
2664    unsigned char *p = (unsigned char *) in;
2665    wxString out;
2666 
2667    unsigned long temp;
2668    for (int i = 0; i < len / 3; i++)
2669    {
2670       temp  = (*p++) << 16; //Convert to big endian
2671       temp += (*p++) << 8;
2672       temp += (*p++);
2673       out += cset[(temp & 0x00FC0000) >> 18];
2674       out += cset[(temp & 0x0003F000) >> 12];
2675       out += cset[(temp & 0x00000FC0) >> 6];
2676       out += cset[(temp & 0x0000003F)];
2677    }
2678 
2679    switch (len % 3)
2680    {
2681       case 1:
2682          temp  = (*p++) << 16; //Convert to big endian
2683          out += cset[(temp & 0x00FC0000) >> 18];
2684          out += cset[(temp & 0x0003F000) >> 12];
2685          out += padc;
2686          out += padc;
2687       break;
2688 
2689       case 2:
2690          temp  = (*p++) << 16; //Convert to big endian
2691          temp += (*p++) << 8;
2692          out += cset[(temp & 0x00FC0000) >> 18];
2693          out += cset[(temp & 0x0003F000) >> 12];
2694          out += cset[(temp & 0x00000FC0) >> 6];
2695          out += padc;
2696       break;
2697    }
2698 
2699    return out;
2700 }
2701 
b64decode(const wxString & in,void * out)2702 int VSTEffect::b64decode(const wxString &in, void *out)
2703 {
2704    int len = in.length();
2705    unsigned char *p = (unsigned char *) out;
2706 
2707    if (len % 4)  //Sanity check
2708    {
2709       return 0;
2710    }
2711 
2712    int padding = 0;
2713    if (len)
2714    {
2715       if (in[len - 1] == padc)
2716       {
2717          padding++;
2718       }
2719 
2720       if (in[len - 2] == padc)
2721       {
2722          padding++;
2723       }
2724    }
2725 
2726    //const char *a = in.mb_str();
2727    //Setup a vector to hold the result
2728    unsigned long temp = 0; //Holds decoded quanta
2729    int i = 0;
2730    while (i < len)
2731    {
2732       for (int quantumPosition = 0; quantumPosition < 4; quantumPosition++)
2733       {
2734          unsigned char c = in[i];
2735          temp <<= 6;
2736 
2737          if (c >= 0x41 && c <= 0x5A)
2738          {
2739             temp |= c - 0x41;
2740          }
2741          else if (c >= 0x61 && c <= 0x7A)
2742          {
2743             temp |= c - 0x47;
2744          }
2745          else if (c >= 0x30 && c <= 0x39)
2746          {
2747             temp |= c + 0x04;
2748          }
2749          else if (c == 0x2B)
2750          {
2751             temp |= 0x3E;
2752          }
2753          else if (c == 0x2F)
2754          {
2755             temp |= 0x3F;
2756          }
2757          else if (c == padc)
2758          {
2759             switch (len - i)
2760             {
2761                case 1: //One pad character
2762                   *p++ = (temp >> 16) & 0x000000FF;
2763                   *p++ = (temp >> 8) & 0x000000FF;
2764                   return p - (unsigned char *) out;
2765                case 2: //Two pad characters
2766                   *p++ = (temp >> 10) & 0x000000FF;
2767                   return p - (unsigned char *) out;
2768             }
2769          }
2770          i++;
2771       }
2772       *p++ = (temp >> 16) & 0x000000FF;
2773       *p++ = (temp >> 8) & 0x000000FF;
2774       *p++ = temp & 0x000000FF;
2775    }
2776 
2777    return p - (unsigned char *) out;
2778 }
2779 
RemoveHandler()2780 void VSTEffect::RemoveHandler()
2781 {
2782 }
2783 
OnSize(wxSizeEvent & evt)2784 static void OnSize(wxSizeEvent & evt)
2785 {
2786    evt.Skip();
2787 
2788    // Once the parent dialog reaches its final size as indicated by
2789    // a non-default minimum size, we set the maximum size to match.
2790    // This is a bit of a hack to prevent VSTs GUI windows from resizing
2791    // there's no real reason to allow it.  But, there should be a better
2792    // way of handling it.
2793    wxWindow *w = (wxWindow *) evt.GetEventObject();
2794    wxSize sz = w->GetMinSize();
2795 
2796    if (sz != wxDefaultSize)
2797    {
2798       w->SetMaxSize(sz);
2799    }
2800 }
2801 
BuildFancy()2802 void VSTEffect::BuildFancy()
2803 {
2804    // Turn the power on...some effects need this when the editor is open
2805    PowerOn();
2806 
2807    auto control = Destroy_ptr<VSTControl>{ safenew VSTControl };
2808    if (!control)
2809    {
2810       return;
2811    }
2812 
2813    if (!control->Create(mParent, this))
2814    {
2815       return;
2816    }
2817 
2818    {
2819       auto mainSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
2820 
2821       mainSizer->Add((mControl = control.release()), 0, wxALIGN_CENTER);
2822 
2823       mParent->SetMinSize(wxDefaultSize);
2824       mParent->SetSizer(mainSizer.release());
2825    }
2826 
2827    NeedEditIdle(true);
2828 
2829    mDialog->Bind(wxEVT_SIZE, OnSize);
2830 
2831 #ifdef __WXMAC__
2832 #ifdef __WX_EVTLOOP_BUSY_WAITING__
2833    wxEventLoop::SetBusyWaiting(true);
2834 #endif
2835 #endif
2836 
2837    return;
2838 }
2839 
BuildPlain()2840 void VSTEffect::BuildPlain()
2841 {
2842    wxASSERT(mParent); // To justify safenew
2843    wxScrolledWindow *const scroller = safenew wxScrolledWindow(mParent,
2844       wxID_ANY,
2845       wxDefaultPosition,
2846       wxDefaultSize,
2847       wxVSCROLL | wxTAB_TRAVERSAL);
2848 
2849    {
2850       auto mainSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
2851 
2852       // Try to give the window a sensible default/minimum size
2853       scroller->SetMinSize(wxSize(wxMax(600, mParent->GetSize().GetWidth() * 2 / 3),
2854          mParent->GetSize().GetHeight() / 2));
2855       scroller->SetScrollRate(0, 20);
2856 
2857       // This fools NVDA into not saying "Panel" when the dialog gets focus
2858       scroller->SetName(wxT("\a"));
2859       scroller->SetLabel(wxT("\a"));
2860 
2861       mainSizer->Add(scroller, 1, wxEXPAND | wxALL, 5);
2862       mParent->SetSizer(mainSizer.release());
2863    }
2864 
2865    mNames.reinit(static_cast<size_t>(mAEffect->numParams));
2866    mSliders.reinit(static_cast<size_t>(mAEffect->numParams));
2867    mDisplays.reinit(static_cast<size_t>(mAEffect->numParams));
2868    mLabels.reinit(static_cast<size_t>(mAEffect->numParams));
2869 
2870    {
2871       auto paramSizer = std::make_unique<wxStaticBoxSizer>(wxVERTICAL, scroller, _("Effect Settings"));
2872 
2873       {
2874          auto gridSizer = std::make_unique<wxFlexGridSizer>(4, 0, 0);
2875          gridSizer->AddGrowableCol(1);
2876 
2877          // Add the duration control for generators
2878          if (GetType() == EffectTypeGenerate)
2879          {
2880             wxControl *item = safenew wxStaticText(scroller, 0, _("Duration:"));
2881             gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5);
2882             mDuration = safenew
2883                NumericTextCtrl(scroller, ID_Duration,
2884                   NumericConverter::TIME,
2885                   mHost->GetDurationFormat(),
2886                   mHost->GetDuration(),
2887                   mSampleRate,
2888                   NumericTextCtrl::Options{}
2889                      .AutoPos(true));
2890             mDuration->SetName( XO("Duration") );
2891             gridSizer->Add(mDuration, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
2892             gridSizer->Add(1, 1, 0);
2893             gridSizer->Add(1, 1, 0);
2894          }
2895 
2896          // Find the longest parameter name.
2897          int namew = 0;
2898          int w;
2899          int h;
2900          for (int i = 0; i < mAEffect->numParams; i++)
2901          {
2902             wxString text = GetString(effGetParamName, i);
2903 
2904             if (text.Right(1) != wxT(':'))
2905             {
2906                text += wxT(':');
2907             }
2908 
2909             scroller->GetTextExtent(text, &w, &h);
2910             if (w > namew)
2911             {
2912                namew = w;
2913             }
2914          }
2915 
2916          scroller->GetTextExtent(wxT("HHHHHHHH"), &w, &h);
2917 
2918          for (int i = 0; i < mAEffect->numParams; i++)
2919          {
2920             mNames[i] = safenew wxStaticText(scroller,
2921                wxID_ANY,
2922                wxEmptyString,
2923                wxDefaultPosition,
2924                wxSize(namew, -1),
2925                wxALIGN_RIGHT | wxST_NO_AUTORESIZE);
2926             gridSizer->Add(mNames[i], 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5);
2927 
2928             mSliders[i] = safenew wxSliderWrapper(scroller,
2929                ID_Sliders + i,
2930                0,
2931                0,
2932                1000,
2933                wxDefaultPosition,
2934                wxSize(200, -1));
2935             gridSizer->Add(mSliders[i], 0, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxALL, 5);
2936 #if wxUSE_ACCESSIBILITY
2937             // so that name can be set on a standard control
2938             mSliders[i]->SetAccessible(safenew WindowAccessible(mSliders[i]));
2939 #endif
2940 
2941             mDisplays[i] = safenew wxStaticText(scroller,
2942                wxID_ANY,
2943                wxEmptyString,
2944                wxDefaultPosition,
2945                wxSize(w, -1),
2946                wxALIGN_RIGHT | wxST_NO_AUTORESIZE);
2947             gridSizer->Add(mDisplays[i], 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5);
2948 
2949             mLabels[i] = safenew wxStaticText(scroller,
2950                wxID_ANY,
2951                wxEmptyString,
2952                wxDefaultPosition,
2953                wxSize(w, -1),
2954                wxALIGN_LEFT | wxST_NO_AUTORESIZE);
2955             gridSizer->Add(mLabels[i], 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, 5);
2956          }
2957 
2958          paramSizer->Add(gridSizer.release(), 1, wxEXPAND | wxALL, 5);
2959       }
2960       scroller->SetSizer(paramSizer.release());
2961    }
2962 
2963    RefreshParameters();
2964 
2965    mSliders[0]->SetFocus();
2966 }
2967 
RefreshParameters(int skip)2968 void VSTEffect::RefreshParameters(int skip)
2969 {
2970    if (!mNames)
2971    {
2972       return;
2973    }
2974 
2975    for (int i = 0; i < mAEffect->numParams; i++)
2976    {
2977       wxString text = GetString(effGetParamName, i);
2978 
2979       text = text.Trim(true).Trim(false);
2980 
2981       wxString name = text;
2982 
2983       if (text.Right(1) != wxT(':'))
2984       {
2985          text += wxT(':');
2986       }
2987       mNames[i]->SetLabel(text);
2988 
2989       // For some parameters types like on/off, setting the slider value has
2990       // a side effect that causes it to only move when the parameter changes
2991       // from off to on.  However, this prevents changing the value using the
2992       // keyboard, so we skip the active slider if any.
2993       if (i != skip)
2994       {
2995          mSliders[i]->SetValue(callGetParameter(i) * 1000);
2996       }
2997       name = text;
2998 
2999       text = GetString(effGetParamDisplay, i);
3000       if (text.empty())
3001       {
3002          text.Printf(wxT("%.5g"),callGetParameter(i));
3003       }
3004       mDisplays[i]->SetLabel(wxString::Format(wxT("%8s"), text));
3005       name += wxT(' ') + text;
3006 
3007       text = GetString(effGetParamDisplay, i);
3008       if (!text.empty())
3009       {
3010          text.Printf(wxT("%-8s"), GetString(effGetParamLabel, i));
3011          mLabels[i]->SetLabel(wxString::Format(wxT("%8s"), text));
3012          name += wxT(' ') + text;
3013       }
3014 
3015       mSliders[i]->SetName(name);
3016    }
3017 }
3018 
OnSizeWindow(wxCommandEvent & evt)3019 void VSTEffect::OnSizeWindow(wxCommandEvent & evt)
3020 {
3021    if (!mControl)
3022    {
3023       return;
3024    }
3025 
3026    mControl->SetMinSize(wxSize(evt.GetInt(), (int) evt.GetExtraLong()));
3027    mControl->SetSize(wxSize(evt.GetInt(), (int) evt.GetExtraLong()));
3028 
3029    // DO NOT CHANGE THE ORDER OF THESE
3030    //
3031    // Guitar Rig (and possibly others) Cocoa VSTs can resize too large
3032    // if the bounds are unlimited.
3033    mDialog->SetMinSize(wxDefaultSize);
3034    mDialog->SetMaxSize(wxDefaultSize);
3035    mDialog->Layout();
3036    mDialog->SetMinSize(mDialog->GetBestSize());
3037    mDialog->SetMaxSize(mDialog->GetBestSize());
3038    mDialog->Fit();
3039 }
3040 
OnSlider(wxCommandEvent & evt)3041 void VSTEffect::OnSlider(wxCommandEvent & evt)
3042 {
3043    wxSlider *s = (wxSlider *) evt.GetEventObject();
3044    int i = s->GetId() - ID_Sliders;
3045 
3046    callSetParameter(i, s->GetValue() / 1000.0);
3047 
3048    RefreshParameters(i);
3049 }
3050 
LoadFXB(const wxFileName & fn)3051 bool VSTEffect::LoadFXB(const wxFileName & fn)
3052 {
3053    bool ret = false;
3054 
3055    // Try to open the file...will be closed automatically when method returns
3056    wxFFile f(fn.GetFullPath(), wxT("rb"));
3057    if (!f.IsOpened())
3058    {
3059       return false;
3060    }
3061 
3062    // Allocate memory for the contents
3063    ArrayOf<unsigned char> data{ size_t(f.Length()) };
3064    if (!data)
3065    {
3066       AudacityMessageBox(
3067          XO("Unable to allocate memory when loading presets file."),
3068          XO("Error Loading VST Presets"),
3069          wxOK | wxCENTRE,
3070          mParent);
3071       return false;
3072    }
3073    unsigned char *bptr = data.get();
3074 
3075    do
3076    {
3077       // Read in the whole file
3078       ssize_t len = f.Read((void *) bptr, f.Length());
3079       if (f.Error())
3080       {
3081          AudacityMessageBox(
3082             XO("Unable to read presets file."),
3083             XO("Error Loading VST Presets"),
3084             wxOK | wxCENTRE,
3085             mParent);
3086          break;
3087       }
3088 
3089       // Most references to the data are via an "int" array
3090       int32_t *iptr = (int32_t *) bptr;
3091 
3092       // Verify that we have at least enough for the header
3093       if (len < 156)
3094       {
3095          break;
3096       }
3097 
3098       // Verify that we probably have an FX file
3099       if (wxINT32_SWAP_ON_LE(iptr[0]) != CCONST('C', 'c', 'n', 'K'))
3100       {
3101          break;
3102       }
3103 
3104       // Ignore the size...sometimes it's there, other times it's zero
3105 
3106       // Get the version and verify
3107       int version = wxINT32_SWAP_ON_LE(iptr[3]);
3108       if (version != 1 && version != 2)
3109       {
3110          break;
3111       }
3112 
3113       VstPatchChunkInfo info =
3114       {
3115          1,
3116          wxINT32_SWAP_ON_LE(iptr[4]),
3117          wxINT32_SWAP_ON_LE(iptr[5]),
3118          wxINT32_SWAP_ON_LE(iptr[6]),
3119          ""
3120       };
3121 
3122       // Ensure this program looks to belong to the current plugin
3123       if ((info.pluginUniqueID != mAEffect->uniqueID) &&
3124           (info.pluginVersion != mAEffect->version) &&
3125           (info.numElements != mAEffect->numPrograms))
3126       {
3127          break;
3128       }
3129 
3130       // Get the number of programs
3131       int numProgs = info.numElements;
3132 
3133       // Get the current program index
3134       int curProg = 0;
3135       if (version >= 2)
3136       {
3137          curProg = wxINT32_SWAP_ON_LE(iptr[7]);
3138          if (curProg < 0 || curProg >= numProgs)
3139          {
3140             break;
3141          }
3142       }
3143 
3144       // Is it a bank of programs?
3145       if (wxINT32_SWAP_ON_LE(iptr[2]) == CCONST('F', 'x', 'B', 'k'))
3146       {
3147          // Drop the header
3148          bptr += 156;
3149          len -= 156;
3150 
3151          unsigned char *tempPtr = bptr;
3152          ssize_t tempLen = len;
3153 
3154          // Validate all of the programs
3155          for (int i = 0; i < numProgs; i++)
3156          {
3157             if (!LoadFXProgram(&tempPtr, tempLen, i, true))
3158             {
3159                break;
3160             }
3161          }
3162 
3163          // Ask the effect if this is an acceptable bank
3164          if (callDispatcher(effBeginLoadBank, 0, 0, &info, 0.0) == -1)
3165          {
3166             return false;
3167          }
3168 
3169          // Start loading the individual programs
3170          for (int i = 0; i < numProgs; i++)
3171          {
3172             ret = LoadFXProgram(&bptr, len, i, false);
3173          }
3174       }
3175       // Or maybe a bank chunk?
3176       else if (wxINT32_SWAP_ON_LE(iptr[2]) == CCONST('F', 'B', 'C', 'h'))
3177       {
3178          // Can't load programs chunks if the plugin doesn't support it
3179          if (!(mAEffect->flags & effFlagsProgramChunks))
3180          {
3181             break;
3182          }
3183 
3184          // Verify that we have enough to grab the chunk size
3185          if (len < 160)
3186          {
3187             break;
3188          }
3189 
3190          // Get the chunk size
3191          int size = wxINT32_SWAP_ON_LE(iptr[39]);
3192 
3193          // We finally know the full length of the program
3194          int proglen = 160 + size;
3195 
3196          // Verify that we have enough for the entire program
3197          if (len < proglen)
3198          {
3199             break;
3200          }
3201 
3202          // Set the entire bank in one shot
3203          callSetChunk(false, size, &iptr[40], &info);
3204 
3205          // Success
3206          ret = true;
3207       }
3208       // Unrecognizable type
3209       else
3210       {
3211          break;
3212       }
3213 
3214       // Set the active program
3215       if (ret && version >= 2)
3216       {
3217          callSetProgram(curProg);
3218       }
3219    } while (false);
3220 
3221    return ret;
3222 }
3223 
LoadFXP(const wxFileName & fn)3224 bool VSTEffect::LoadFXP(const wxFileName & fn)
3225 {
3226    bool ret = false;
3227 
3228    // Try to open the file...will be closed automatically when method returns
3229    wxFFile f(fn.GetFullPath(), wxT("rb"));
3230    if (!f.IsOpened())
3231    {
3232       return false;
3233    }
3234 
3235    // Allocate memory for the contents
3236    ArrayOf<unsigned char> data{ size_t(f.Length()) };
3237    if (!data)
3238    {
3239       AudacityMessageBox(
3240          XO("Unable to allocate memory when loading presets file."),
3241          XO("Error Loading VST Presets"),
3242          wxOK | wxCENTRE,
3243          mParent);
3244       return false;
3245    }
3246    unsigned char *bptr = data.get();
3247 
3248    do
3249    {
3250       // Read in the whole file
3251       ssize_t len = f.Read((void *) bptr, f.Length());
3252       if (f.Error())
3253       {
3254          AudacityMessageBox(
3255             XO("Unable to read presets file."),
3256             XO("Error Loading VST Presets"),
3257             wxOK | wxCENTRE,
3258             mParent);
3259          break;
3260       }
3261 
3262       // Get (or default) currently selected program
3263       int i = 0; //mProgram->GetCurrentSelection();
3264       if (i < 0)
3265       {
3266          i = 0;   // default to first program
3267       }
3268 
3269       // Go verify and set the program
3270       ret = LoadFXProgram(&bptr, len, i, false);
3271    } while (false);
3272 
3273    return ret;
3274 }
3275 
LoadFXProgram(unsigned char ** bptr,ssize_t & len,int index,bool dryrun)3276 bool VSTEffect::LoadFXProgram(unsigned char **bptr, ssize_t & len, int index, bool dryrun)
3277 {
3278    // Most references to the data are via an "int" array
3279    int32_t *iptr = (int32_t *) *bptr;
3280 
3281    // Verify that we have at least enough for a program without parameters
3282    if (len < 28)
3283    {
3284       return false;
3285    }
3286 
3287    // Verify that we probably have an FX file
3288    if (wxINT32_SWAP_ON_LE(iptr[0]) != CCONST('C', 'c', 'n', 'K'))
3289    {
3290       return false;
3291    }
3292 
3293    // Ignore the size...sometimes it's there, other times it's zero
3294 
3295    // Get the version and verify
3296 #if defined(IS_THIS_AN_FXP_ARTIFICAL_LIMITATION)
3297    int version = wxINT32_SWAP_ON_LE(iptr[3]);
3298    if (version != 1)
3299    {
3300       return false;
3301    }
3302 #endif
3303 
3304    VstPatchChunkInfo info =
3305    {
3306       1,
3307       wxINT32_SWAP_ON_LE(iptr[4]),
3308       wxINT32_SWAP_ON_LE(iptr[5]),
3309       wxINT32_SWAP_ON_LE(iptr[6]),
3310       ""
3311    };
3312 
3313    // Ensure this program looks to belong to the current plugin
3314    if ((info.pluginUniqueID != mAEffect->uniqueID) &&
3315          (info.pluginVersion != mAEffect->version) &&
3316          (info.numElements != mAEffect->numParams))
3317    {
3318       return false;
3319    }
3320 
3321    // Get the number of parameters
3322    int numParams = info.numElements;
3323 
3324    // At this point, we have to have enough to include the program name as well
3325    if (len < 56)
3326    {
3327       return false;
3328    }
3329 
3330    // Get the program name
3331    wxString progName(wxString::From8BitData((char *)&iptr[7]));
3332 
3333    // Might be a regular program
3334    if (wxINT32_SWAP_ON_LE(iptr[2]) == CCONST('F', 'x', 'C', 'k'))
3335    {
3336       // We finally know the full length of the program
3337       int proglen = 56 + (numParams * sizeof(float));
3338 
3339       // Verify that we have enough for all of the parameter values
3340       if (len < proglen)
3341       {
3342          return false;
3343       }
3344 
3345       // Validate all of the parameter values
3346       for (int i = 0; i < numParams; i++)
3347       {
3348          uint32_t ival = wxUINT32_SWAP_ON_LE(iptr[14 + i]);
3349          float val = reinterpretAsFloat(ival);
3350          if (val < 0.0 || val > 1.0)
3351          {
3352             return false;
3353          }
3354       }
3355 
3356       // They look okay...time to start changing things
3357       if (!dryrun)
3358       {
3359          // Ask the effect if this is an acceptable program
3360          if (callDispatcher(effBeginLoadProgram, 0, 0, &info, 0.0) == -1)
3361          {
3362             return false;
3363          }
3364 
3365          // Load all of the parameters
3366          callDispatcher(effBeginSetProgram, 0, 0, NULL, 0.0);
3367          for (int i = 0; i < numParams; i++)
3368          {
3369             wxUint32 val = wxUINT32_SWAP_ON_LE(iptr[14 + i]);
3370             callSetParameter(i, reinterpretAsFloat(val));
3371          }
3372          callDispatcher(effEndSetProgram, 0, 0, NULL, 0.0);
3373       }
3374 
3375       // Update in case we're loading an "FxBk" format bank file
3376       *bptr += proglen;
3377       len -= proglen;
3378    }
3379    // Maybe we have a program chunk
3380    else if (wxINT32_SWAP_ON_LE(iptr[2]) == CCONST('F', 'P', 'C', 'h'))
3381    {
3382       // Can't load programs chunks if the plugin doesn't support it
3383       if (!(mAEffect->flags & effFlagsProgramChunks))
3384       {
3385          return false;
3386       }
3387 
3388       // Verify that we have enough to grab the chunk size
3389       if (len < 60)
3390       {
3391          return false;
3392       }
3393 
3394       // Get the chunk size
3395       int size = wxINT32_SWAP_ON_LE(iptr[14]);
3396 
3397       // We finally know the full length of the program
3398       int proglen = 60 + size;
3399 
3400       // Verify that we have enough for the entire program
3401       if (len < proglen)
3402       {
3403          return false;
3404       }
3405 
3406       // Set the entire program in one shot
3407       if (!dryrun)
3408       {
3409          callSetChunk(true, size, &iptr[15], &info);
3410       }
3411 
3412       // Update in case we're loading an "FxBk" format bank file
3413       *bptr += proglen;
3414       len -= proglen;
3415    }
3416    else
3417    {
3418       // Unknown type
3419       return false;
3420    }
3421 
3422    if (!dryrun)
3423    {
3424       SetString(effSetProgramName, wxString(progName), index);
3425    }
3426 
3427    return true;
3428 }
3429 
LoadXML(const wxFileName & fn)3430 bool VSTEffect::LoadXML(const wxFileName & fn)
3431 {
3432    mInChunk = false;
3433    mInSet = false;
3434 
3435    // default to read as XML file
3436    // Load the program
3437    XMLFileReader reader;
3438    bool ok = reader.Parse(this, fn.GetFullPath());
3439 
3440    // Something went wrong with the file, clean up
3441    if (mInSet)
3442    {
3443       callDispatcher(effEndSetProgram, 0, 0, NULL, 0.0);
3444 
3445       mInSet = false;
3446    }
3447 
3448    if (!ok)
3449    {
3450       // Inform user of load failure
3451       AudacityMessageBox(
3452          reader.GetErrorStr(),
3453          XO("Error Loading VST Presets"),
3454          wxOK | wxCENTRE,
3455          mParent);
3456       return false;
3457    }
3458 
3459    return true;
3460 }
3461 
SaveFXB(const wxFileName & fn)3462 void VSTEffect::SaveFXB(const wxFileName & fn)
3463 {
3464    // Create/Open the file
3465    const wxString fullPath{fn.GetFullPath()};
3466    wxFFile f(fullPath, wxT("wb"));
3467    if (!f.IsOpened())
3468    {
3469       AudacityMessageBox(
3470          XO("Could not open file: \"%s\"").Format( fullPath ),
3471          XO("Error Saving VST Presets"),
3472          wxOK | wxCENTRE,
3473          mParent);
3474       return;
3475    }
3476 
3477    wxMemoryBuffer buf;
3478    wxInt32 subType;
3479    void *chunkPtr = nullptr;
3480    int chunkSize = 0;
3481    int dataSize = 148;
3482    wxInt32 tab[8];
3483    int curProg = 0 ; //mProgram->GetCurrentSelection();
3484 
3485    if (mAEffect->flags & effFlagsProgramChunks)
3486    {
3487       subType = CCONST('F', 'B', 'C', 'h');
3488 
3489       chunkSize = callDispatcher(effGetChunk, 0, 0, &chunkPtr, 0.0);
3490       dataSize += 4 + chunkSize;
3491    }
3492    else
3493    {
3494       subType = CCONST('F', 'x', 'B', 'k');
3495 
3496       for (int i = 0; i < mAEffect->numPrograms; i++)
3497       {
3498          SaveFXProgram(buf, i);
3499       }
3500 
3501       dataSize += buf.GetDataLen();
3502    }
3503 
3504    tab[0] = wxINT32_SWAP_ON_LE(CCONST('C', 'c', 'n', 'K'));
3505    tab[1] = wxINT32_SWAP_ON_LE(dataSize);
3506    tab[2] = wxINT32_SWAP_ON_LE(subType);
3507    tab[3] = wxINT32_SWAP_ON_LE(curProg >= 0 ? 2 : 1);
3508    tab[4] = wxINT32_SWAP_ON_LE(mAEffect->uniqueID);
3509    tab[5] = wxINT32_SWAP_ON_LE(mAEffect->version);
3510    tab[6] = wxINT32_SWAP_ON_LE(mAEffect->numPrograms);
3511    tab[7] = wxINT32_SWAP_ON_LE(curProg >= 0 ? curProg : 0);
3512 
3513    f.Write(tab, sizeof(tab));
3514    if (!f.Error())
3515    {
3516       char padding[124];
3517       memset(padding, 0, sizeof(padding));
3518       f.Write(padding, sizeof(padding));
3519 
3520       if (!f.Error())
3521       {
3522          if (mAEffect->flags & effFlagsProgramChunks)
3523          {
3524             wxInt32 size = wxINT32_SWAP_ON_LE(chunkSize);
3525             f.Write(&size, sizeof(size));
3526             f.Write(chunkPtr, chunkSize);
3527          }
3528          else
3529          {
3530             f.Write(buf.GetData(), buf.GetDataLen());
3531          }
3532       }
3533    }
3534 
3535    if (f.Error())
3536    {
3537       AudacityMessageBox(
3538          XO("Error writing to file: \"%s\"").Format( fullPath ),
3539          XO("Error Saving VST Presets"),
3540          wxOK | wxCENTRE,
3541          mParent);
3542    }
3543 
3544    f.Close();
3545 
3546    return;
3547 }
3548 
SaveFXP(const wxFileName & fn)3549 void VSTEffect::SaveFXP(const wxFileName & fn)
3550 {
3551    // Create/Open the file
3552    const wxString fullPath{ fn.GetFullPath() };
3553    wxFFile f(fullPath, wxT("wb"));
3554    if (!f.IsOpened())
3555    {
3556       AudacityMessageBox(
3557          XO("Could not open file: \"%s\"").Format( fullPath ),
3558          XO("Error Saving VST Presets"),
3559          wxOK | wxCENTRE,
3560          mParent);
3561       return;
3562    }
3563 
3564    wxMemoryBuffer buf;
3565 
3566    int ndx = callDispatcher(effGetProgram, 0, 0, NULL, 0.0);
3567    SaveFXProgram(buf, ndx);
3568 
3569    f.Write(buf.GetData(), buf.GetDataLen());
3570    if (f.Error())
3571    {
3572       AudacityMessageBox(
3573          XO("Error writing to file: \"%s\"").Format( fullPath ),
3574          XO("Error Saving VST Presets"),
3575          wxOK | wxCENTRE,
3576          mParent);
3577    }
3578 
3579    f.Close();
3580 
3581    return;
3582 }
3583 
SaveFXProgram(wxMemoryBuffer & buf,int index)3584 void VSTEffect::SaveFXProgram(wxMemoryBuffer & buf, int index)
3585 {
3586    wxInt32 subType;
3587    void *chunkPtr;
3588    int chunkSize;
3589    int dataSize = 48;
3590    char progName[28];
3591    wxInt32 tab[7];
3592 
3593    callDispatcher(effGetProgramNameIndexed, index, 0, &progName, 0.0);
3594    progName[27] = '\0';
3595    chunkSize = strlen(progName);
3596    memset(&progName[chunkSize], 0, sizeof(progName) - chunkSize);
3597 
3598    if (mAEffect->flags & effFlagsProgramChunks)
3599    {
3600       subType = CCONST('F', 'P', 'C', 'h');
3601 
3602       chunkSize = callDispatcher(effGetChunk, 1, 0, &chunkPtr, 0.0);
3603       dataSize += 4 + chunkSize;
3604    }
3605    else
3606    {
3607       subType = CCONST('F', 'x', 'C', 'k');
3608 
3609       dataSize += (mAEffect->numParams << 2);
3610    }
3611 
3612    tab[0] = wxINT32_SWAP_ON_LE(CCONST('C', 'c', 'n', 'K'));
3613    tab[1] = wxINT32_SWAP_ON_LE(dataSize);
3614    tab[2] = wxINT32_SWAP_ON_LE(subType);
3615    tab[3] = wxINT32_SWAP_ON_LE(1);
3616    tab[4] = wxINT32_SWAP_ON_LE(mAEffect->uniqueID);
3617    tab[5] = wxINT32_SWAP_ON_LE(mAEffect->version);
3618    tab[6] = wxINT32_SWAP_ON_LE(mAEffect->numParams);
3619 
3620    buf.AppendData(tab, sizeof(tab));
3621    buf.AppendData(progName, sizeof(progName));
3622 
3623    if (mAEffect->flags & effFlagsProgramChunks)
3624    {
3625       wxInt32 size = wxINT32_SWAP_ON_LE(chunkSize);
3626       buf.AppendData(&size, sizeof(size));
3627       buf.AppendData(chunkPtr, chunkSize);
3628    }
3629    else
3630    {
3631       for (int i = 0; i < mAEffect->numParams; i++)
3632       {
3633          float val = callGetParameter(i);
3634          wxUint32 ival = wxUINT32_SWAP_ON_LE(reinterpretAsUint32(val));
3635          buf.AppendData(&ival, sizeof(ival));
3636       }
3637    }
3638 
3639    return;
3640 }
3641 
3642 // Throws exceptions rather than giving error return.
SaveXML(const wxFileName & fn)3643 void VSTEffect::SaveXML(const wxFileName & fn)
3644 // may throw
3645 {
3646    XMLFileWriter xmlFile{ fn.GetFullPath(), XO("Error Saving Effect Presets") };
3647 
3648    xmlFile.StartTag(wxT("vstprogrampersistence"));
3649    xmlFile.WriteAttr(wxT("version"), wxT("2"));
3650 
3651    xmlFile.StartTag(wxT("effect"));
3652    // Use internal name only in persistent information
3653    xmlFile.WriteAttr(wxT("name"), GetSymbol().Internal());
3654    xmlFile.WriteAttr(wxT("uniqueID"), mAEffect->uniqueID);
3655    xmlFile.WriteAttr(wxT("version"), mAEffect->version);
3656    xmlFile.WriteAttr(wxT("numParams"), mAEffect->numParams);
3657 
3658    xmlFile.StartTag(wxT("program"));
3659    xmlFile.WriteAttr(wxT("name"), wxEmptyString); //mProgram->GetValue());
3660 
3661    int clen = 0;
3662    if (mAEffect->flags & effFlagsProgramChunks)
3663    {
3664       void *chunk = NULL;
3665 
3666       clen = (int) callDispatcher(effGetChunk, 1, 0, &chunk, 0.0);
3667       if (clen != 0)
3668       {
3669          xmlFile.StartTag(wxT("chunk"));
3670          xmlFile.WriteSubTree(VSTEffect::b64encode(chunk, clen) + wxT('\n'));
3671          xmlFile.EndTag(wxT("chunk"));
3672       }
3673    }
3674 
3675    if (clen == 0)
3676    {
3677       for (int i = 0; i < mAEffect->numParams; i++)
3678       {
3679          xmlFile.StartTag(wxT("param"));
3680 
3681          xmlFile.WriteAttr(wxT("index"), i);
3682          xmlFile.WriteAttr(wxT("name"),
3683                            GetString(effGetParamName, i));
3684          xmlFile.WriteAttr(wxT("value"),
3685                            wxString::Format(wxT("%f"),
3686                            callGetParameter(i)));
3687 
3688          xmlFile.EndTag(wxT("param"));
3689       }
3690    }
3691 
3692    xmlFile.EndTag(wxT("program"));
3693 
3694    xmlFile.EndTag(wxT("effect"));
3695 
3696    xmlFile.EndTag(wxT("vstprogrampersistence"));
3697 
3698    xmlFile.Commit();
3699 }
3700 
HandleXMLTag(const std::string_view & tag,const AttributesList & attrs)3701 bool VSTEffect::HandleXMLTag(const std::string_view& tag, const AttributesList &attrs)
3702 {
3703    if (tag == "vstprogrampersistence")
3704    {
3705       for (auto pair : attrs)
3706       {
3707          auto attr = pair.first;
3708          auto value = pair.second;
3709 
3710          if (attr == "version")
3711          {
3712             if (!value.TryGet(mXMLVersion))
3713             {
3714                return false;
3715             }
3716 
3717             if (mXMLVersion < 1 || mXMLVersion > 2)
3718             {
3719                return false;
3720             }
3721          }
3722          else
3723          {
3724             return false;
3725          }
3726       }
3727 
3728       return true;
3729    }
3730 
3731    if (tag == "effect")
3732    {
3733       memset(&mXMLInfo, 0, sizeof(mXMLInfo));
3734       mXMLInfo.version = 1;
3735       mXMLInfo.pluginUniqueID = mAEffect->uniqueID;
3736       mXMLInfo.pluginVersion = mAEffect->version;
3737       mXMLInfo.numElements = mAEffect->numParams;
3738 
3739       for (auto pair : attrs)
3740       {
3741          auto attr = pair.first;
3742          auto value = pair.second;
3743 
3744          if (attr == "name")
3745          {
3746             wxString strValue = value.ToWString();
3747 
3748             if (strValue != GetSymbol().Internal())
3749             {
3750                auto msg = XO("This parameter file was saved from %s. Continue?")
3751                   .Format( strValue );
3752                int result = AudacityMessageBox(
3753                   msg,
3754                   XO("Confirm"),
3755                   wxYES_NO,
3756                   mParent );
3757                if (result == wxNO)
3758                {
3759                   return false;
3760                }
3761             }
3762          }
3763          else if (attr == "version")
3764          {
3765             long version;
3766             if (!value.TryGet(version))
3767             {
3768                return false;
3769             }
3770 
3771             mXMLInfo.pluginVersion = (int) version;
3772          }
3773          else if (mXMLVersion > 1 && attr == "uniqueID")
3774          {
3775             long uniqueID;
3776             if (!value.TryGet(uniqueID))
3777             {
3778                return false;
3779             }
3780 
3781             mXMLInfo.pluginUniqueID = (int) uniqueID;
3782          }
3783          else if (mXMLVersion > 1 && attr == "numParams")
3784          {
3785             long numParams;
3786             if (!value.TryGet(numParams))
3787             {
3788                return false;
3789             }
3790 
3791             mXMLInfo.numElements = (int) numParams;
3792          }
3793          else
3794          {
3795             return false;
3796          }
3797       }
3798 
3799       return true;
3800    }
3801 
3802    if (tag == "program")
3803    {
3804       for (auto pair : attrs)
3805       {
3806          auto attr = pair.first;
3807          auto value = pair.second;
3808 
3809          if (attr == "name")
3810          {
3811             const wxString strValue = value.ToWString();
3812 
3813             if (strValue.length() > 24)
3814             {
3815                return false;
3816             }
3817 
3818             int ndx = 0; //mProgram->GetCurrentSelection();
3819             if (ndx == wxNOT_FOUND)
3820             {
3821                ndx = 0;
3822             }
3823 
3824             SetString(effSetProgramName, strValue, ndx);
3825          }
3826          else
3827          {
3828             return false;
3829          }
3830       }
3831 
3832       mInChunk = false;
3833 
3834       if (callDispatcher(effBeginLoadProgram, 0, 0, &mXMLInfo, 0.0) == -1)
3835       {
3836          return false;
3837       }
3838 
3839       callDispatcher(effBeginSetProgram, 0, 0, NULL, 0.0);
3840 
3841       mInSet = true;
3842 
3843       return true;
3844    }
3845 
3846    if (tag == "param")
3847    {
3848       long ndx = -1;
3849       double val = -1.0;
3850 
3851       for (auto pair : attrs)
3852       {
3853          auto attr = pair.first;
3854          auto value = pair.second;
3855 
3856          if (attr == "index")
3857          {
3858             if (!value.TryGet(ndx))
3859             {
3860                return false;
3861             }
3862 
3863             if (ndx < 0 || ndx >= mAEffect->numParams)
3864             {
3865                // Could be a different version of the effect...probably should
3866                // tell the user
3867                return false;
3868             }
3869          }
3870          // "name" attribute is ignored for params
3871          /* else if (attr == "name")
3872          {
3873 
3874             // Nothing to do with it for now
3875          }*/
3876          else if (attr == "value")
3877          {
3878             if (!value.TryGet(val))
3879             {
3880                return false;
3881             }
3882 
3883             if (val < 0.0 || val > 1.0)
3884             {
3885                return false;
3886             }
3887          }
3888       }
3889 
3890       if (ndx == -1 || val == -1.0)
3891       {
3892          return false;
3893       }
3894 
3895       callSetParameter(ndx, val);
3896 
3897       return true;
3898    }
3899 
3900    if (tag == "chunk")
3901    {
3902       mInChunk = true;
3903       return true;
3904    }
3905 
3906    return false;
3907 }
3908 
HandleXMLEndTag(const std::string_view & tag)3909 void VSTEffect::HandleXMLEndTag(const std::string_view& tag)
3910 {
3911    if (tag == "chunk")
3912    {
3913       if (mChunk.length())
3914       {
3915          ArrayOf<char> buf{ mChunk.length() / 4 * 3 };
3916 
3917          int len = VSTEffect::b64decode(mChunk, buf.get());
3918          if (len)
3919          {
3920             callSetChunk(true, len, buf.get(), &mXMLInfo);
3921          }
3922 
3923          mChunk.clear();
3924       }
3925       mInChunk = false;
3926    }
3927 
3928    if (tag == "program")
3929    {
3930       if (mInSet)
3931       {
3932          callDispatcher(effEndSetProgram, 0, 0, NULL, 0.0);
3933 
3934          mInSet = false;
3935       }
3936    }
3937 }
3938 
HandleXMLContent(const std::string_view & content)3939 void VSTEffect::HandleXMLContent(const std::string_view& content)
3940 {
3941    if (mInChunk)
3942    {
3943       mChunk += wxString(std::string(content)).Trim(true).Trim(false);
3944    }
3945 }
3946 
HandleXMLChild(const std::string_view & tag)3947 XMLTagHandler *VSTEffect::HandleXMLChild(const std::string_view& tag)
3948 {
3949    if (tag == "vstprogrampersistence")
3950    {
3951       return this;
3952    }
3953 
3954    if (tag == "effect")
3955    {
3956       return this;
3957    }
3958 
3959    if (tag == "program")
3960    {
3961       return this;
3962    }
3963 
3964    if (tag == "param")
3965    {
3966       return this;
3967    }
3968 
3969    if (tag == "chunk")
3970    {
3971       return this;
3972    }
3973 
3974    return NULL;
3975 }
3976 
3977 #endif // USE_VST
3978