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