Audacity 3.2.0
VSTWrapper.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 VSTWrapper.cpp
6
7 Dominic Mazzoni
8
9 Paul Licameli split from VSTEffect.cpp
10
11 This class implements a VST Plug-in effect. The plug-in must be
12 loaded in a platform-specific way and passed into the constructor,
13 but from here this class handles the interfacing.
14
15*//********************************************************************/
16
17#include "VSTWrapper.h"
18
19#include <wx/log.h>
20#include <wx/time.h>
21
22#if defined(__WXMSW__)
23#include <shlwapi.h>
24#pragma comment(lib, "shlwapi")
25#else
26#include <dlfcn.h>
27#endif
28
29#if defined(__WXMAC__)
30#include <wx/osx/core/private.h>
31#endif
32
33#include <cstring>
34
35#include "BasicUI.h"
36#include "FileNames.h"
37#include "XMLFileReader.h"
38#include "Base64.h"
39
40static float reinterpretAsFloat(uint32_t x)
41{
42 static_assert(sizeof(float) == sizeof(uint32_t), "Cannot reinterpret uint32_t to float since sizes are different.");
43 float f;
44 std::memcpy(&f, &x, sizeof(float));
45 return f;
46}
47
48static uint32_t reinterpretAsUint32(float f)
49{
50 static_assert(sizeof(float) == sizeof(uint32_t), "Cannot reinterpret float to uint32_t since sizes are different.");
51
52 uint32_t x;
53 std::memcpy(&x, &f, sizeof(uint32_t));
54 return x;
55}
56
57typedef AEffect *(*vstPluginMain)(audioMasterCallback audioMaster);
58
60 int32_t opcode,
61 int32_t index,
62 intptr_t value,
63 void * ptr,
64 float opt)
65{
66 VSTWrapper* vst = (effect ? static_cast<VSTWrapper*>(effect->ptr2) : nullptr);
67
68 // Handles operations during initialization...before VSTEffect has had a
69 // chance to set its instance pointer.
70 switch (opcode)
71 {
73 return (intptr_t) 2400;
74
76 return vst->mCurrentEffectID;
77
79 strcpy((char *) ptr, "Audacity Team"); // Do not translate, max 64 + 1 for null terminator
80 return 1;
81
83 strcpy((char *) ptr, "Audacity"); // Do not translate, max 64 + 1 for null terminator
84 return 1;
85
87 return (intptr_t) (AUDACITY_VERSION << 24 |
88 AUDACITY_RELEASE << 16 |
89 AUDACITY_REVISION << 8 |
90 AUDACITY_MODLEVEL);
91
92 // Some (older) effects depend on an effIdle call when requested. An
93 // example is the Antress Modern plugins which uses the call to update
94 // the editors display when the program (preset) changes.
96 if (vst)
97 {
98 vst->NeedIdle();
99 return 1;
100 }
101 return 0;
102
103 // We would normally get this if the effect editor is dipslayed and something "major"
104 // has changed (like a program change) instead of multiple automation calls.
105 // Since we don't do anything with the parameters while the editor is displayed,
106 // there's no need for us to do anything.
108 if (vst)
109 {
110 return 1;
111 }
112 return 0;
113
114 // Return the current time info.
116 if (vst)
117 {
118 return (intptr_t) vst->GetTimeInfo();
119 }
120 return 0;
121
122 // Inputs, outputs, or initial delay has changed...all we care about is initial delay.
124 if (vst)
125 {
126 vst->SetBufferDelay(effect->initialDelay);
127 return 1;
128 }
129 return 0;
130
132 if (vst)
133 {
134 return (intptr_t) vst->GetSampleRate();
135 }
136 return 0;
137
138 case audioMasterIdle:
139 if (vst)
140 vst->Idle();
141 return 1;
142
144 if (vst)
145 {
146 return vst->GetProcessLevel();
147 }
148 return 0;
149
151 return kVstLangEnglish;
152
153 // We always replace, never accumulate
155 return 1;
156
157 // Resize the window to accommodate the effect size
159 if (vst)
160 {
161 vst->SizeWindow(index, value);
162 }
163 return 1;
164
165 case audioMasterCanDo:
166 {
167 char *s = (char *) ptr;
168 if (strcmp(s, "acceptIOChanges") == 0 ||
169 strcmp(s, "sendVstTimeInfo") == 0 ||
170 strcmp(s, "startStopProcess") == 0 ||
171 strcmp(s, "shellCategory") == 0 ||
172 strcmp(s, "sizeWindow") == 0)
173 {
174 return 1;
175 }
176
177#if defined(VST_DEBUG)
178#if defined(__WXMSW__)
179 wxLogDebug(wxT("VST canDo: %s"), wxString::FromAscii((char *)ptr));
180#else
181 wxPrintf(wxT("VST canDo: %s\n"), wxString::FromAscii((char *)ptr));
182#endif
183#endif
184
185 return 0;
186 }
187
190 return 0;
191
193 if (vst)
194 {
195 vst->Automate(index, opt);
196 }
197 return 0;
198
199 // We're always connected (sort of)
201
202 // We don't do MIDI yet
205
206 // Don't need to see any messages about these
207 return 0;
208 }
209
210#if defined(VST_DEBUG)
211#if defined(__WXMSW__)
212 wxLogDebug(wxT("vst: %p opcode: %d index: %d value: %p ptr: %p opt: %f user: %p"),
213 effect, (int) opcode, (int) index, (void *) value, ptr, opt, vst);
214#else
215 wxPrintf(wxT("vst: %p opcode: %d index: %d value: %p ptr: %p opt: %f user: %p\n"),
216 effect, (int) opcode, (int) index, (void *) value, ptr, opt, vst);
217#endif
218#endif
219
220 return 0;
221}
222
223#if !defined(__WXMSW__)
224void VSTWrapper::ModuleDeleter::operator() (void* p) const
225{
226 if (p)
227 dlclose(p);
228}
229#endif
230
231#if defined(__WXMAC__)
233{
234 if (mpHandle)
235 CFBundleCloseBundleResourceMap(mpHandle, mNum);
236 mpHandle = nullptr;
237 mNum = 0;
238}
239#endif
240
241VSTMessage::~VSTMessage() = default;
242
243auto VSTMessage::Clone() const -> std::unique_ptr<Message>
244{
245 auto result = std::make_unique<VSTMessage>(*this);
246 // Make sure of the chunk capacity
247 result->mChunk.reserve(this->mChunk.capacity());
248
249 return result;
250}
251
253{
254 VSTMessage& vstSrc = static_cast<VSTMessage&>(src);
255
256 mChunk = vstSrc.mChunk;
257 vstSrc.mChunk.resize(0); // capacity will be preserved though
258
259 assert(mParamsVec.size() == vstSrc.mParamsVec.size());
260
261 for (size_t i = 0; i < mParamsVec.size(); i++)
262 {
263 mParamsVec[i] = vstSrc.mParamsVec[i];
264
265 // consume the source value
266 vstSrc.mParamsVec[i] = std::nullopt;
267 }
268}
269
271{
272 VSTMessage& vstSrc = static_cast<VSTMessage&>(src);
273
274 bool chunkWasAssigned = false;
275
276 if ( ! vstSrc.mChunk.empty() )
277 {
278 mChunk = vstSrc.mChunk;
279 chunkWasAssigned = true;
280 }
281
282 vstSrc.mChunk.resize(0); // capacity will be preserved though
283
284 assert(mParamsVec.size() == vstSrc.mParamsVec.size());
285
286 for (size_t i = 0; i < mParamsVec.size(); i++)
287 {
288 if (chunkWasAssigned)
289 {
290 mParamsVec[i] = vstSrc.mParamsVec[i];
291 }
292 else
293 {
294 // if src val is nullopt, do not copy it to dest
295 if (vstSrc.mParamsVec[i] != std::nullopt)
296 {
297 mParamsVec[i] = vstSrc.mParamsVec[i];
298 }
299 }
300
301 // consume the source value
302 vstSrc.mParamsVec[i] = std::nullopt;
303 }
304
305}
306
308{
309 vstPluginMain pluginMain;
310 bool success = false;
311
312 long effectID = 0;
313 wxString realPath = mPath.BeforeFirst(wxT(';'));
314 mPath.AfterFirst(wxT(';')).ToLong(&effectID);
315 mCurrentEffectID = (intptr_t) effectID;
316
317 mModule = NULL;
318 mAEffect = NULL;
319
320#if defined(__WXMAC__)
321 // Start clean
322 mBundleRef.reset();
323
325
326 // Convert the path to a CFSTring
327 wxCFStringRef path(realPath);
328
329 // Create the bundle using the URL
330 BundleHandle bundleRef{ CFBundleCreate(kCFAllocatorDefault,
331 // Convert the path to a URL
332 CF_ptr<CFURLRef>{
333 CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
334 path, kCFURLPOSIXPathStyle, true)
335 }.get()
336 )};
337
338 // Bail if the bundle wasn't created
339 if (!bundleRef)
340 return false;
341
342
343 // Convert back to path
344 UInt8 exePath[PLATFORM_MAX_PATH];
345 Boolean good = CFURLGetFileSystemRepresentation(
346 // Retrieve a reference to the executable
347 CF_ptr<CFURLRef>{ CFBundleCopyExecutableURL(bundleRef.get()) }.get(),
348 true, exePath, sizeof(exePath)
349 );
350
351 // Bail if we couldn't resolve the executable path
352 if (good == FALSE)
353 return false;
354
355 // Attempt to open it
356 mModule.reset((char*)dlopen((char *) exePath, RTLD_NOW | RTLD_LOCAL));
357 if (!mModule)
358 return false;
359
360 // Try to locate the NEW plugin entry point
361 pluginMain = (vstPluginMain) dlsym(mModule.get(), "VSTPluginMain");
362
363 // If not found, try finding the old entry point
364 if (pluginMain == NULL)
365 {
366 pluginMain = (vstPluginMain) dlsym(mModule.get(), "main_macho");
367 }
368
369 // Must not be a VST plugin
370 if (pluginMain == NULL)
371 {
372 mModule.reset();
373 return false;
374 }
375
376 // Need to keep the bundle reference around so we can map the
377 // resources.
378 mBundleRef = std::move(bundleRef);
379
380 // Open the resource map ... some plugins (like GRM Tools) need this.
382 mBundleRef.get(), CFBundleOpenBundleResourceMap(mBundleRef.get())
383 };
384
385#elif defined(__WXMSW__)
386
387 {
388 wxLogNull nolog;
389
390 // Try to load the library
391 auto lib = std::make_unique<wxDynamicLibrary>(realPath);
392 if (!lib)
393 return false;
394
395 // Bail if it wasn't successful
396 if (!lib->IsLoaded())
397 return false;
398
399 // Try to find the entry point, while suppressing error messages
400 pluginMain = (vstPluginMain) lib->GetSymbol(wxT("VSTPluginMain"));
401 if (pluginMain == NULL)
402 {
403 pluginMain = (vstPluginMain) lib->GetSymbol(wxT("main"));
404 if (pluginMain == NULL)
405 return false;
406 }
407
408 // Save the library reference
409 mModule = std::move(lib);
410 }
411
412#else
413
414 // Attempt to load it
415 //
416 // Spent a few days trying to figure out why some VSTs where running okay and
417 // others were hit or miss. The cause was that we export all of Audacity's
418 // symbols and some of the loaded libraries were picking up Audacity's and
419 // not their own.
420 //
421 // So far, I've only seen this issue on Linux, but we might just be getting
422 // lucky on the Mac and Windows. The sooner we stop exporting everything
423 // the better.
424 //
425 // To get around the problem, I just added the RTLD_DEEPBIND flag to the load
426 // and that "basically" puts Audacity last when the loader needs to resolve
427 // symbols.
428 //
429 // Once we define a proper external API, the flags can be removed.
430#ifndef RTLD_DEEPBIND
431#define RTLD_DEEPBIND 0
432#endif
433 ModuleHandle lib {
434 (char*) dlopen((const char *)wxString(realPath).ToUTF8(),
435 RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND)
436 };
437 if (!lib)
438 {
439 return false;
440 }
441
442 // Try to find the entry point, while suppressing error messages
443 pluginMain = (vstPluginMain) dlsym(lib.get(), "VSTPluginMain");
444 if (pluginMain == NULL)
445 {
446 pluginMain = (vstPluginMain) dlsym(lib.get(), "main");
447 if (pluginMain == NULL)
448 return false;
449 }
450
451 // Save the library reference
452 mModule = std::move(lib);
453
454#endif
455
456 // Initialize the plugin
457 try
458 {
459 mAEffect = pluginMain(VSTWrapper::AudioMaster);
460 }
461 catch (...)
462 {
463 wxLogMessage(wxT("VST plugin initialization failed\n"));
464 mAEffect = NULL;
465 }
466
467 // Was it successful?
468 if (mAEffect)
469 {
471
472 // Must use the GUI editor if parameters aren't provided
473 if (mAEffect->numParams == 0)
474 {
475 mGui = true;
476 }
477
478 // Save a reference to ourselves
479 //
480 // Note: Some hosts use "user" and some use "ptr2/resvd2". It might
481 // be worthwhile to check if user is NULL before using it and
482 // then falling back to "ptr2/resvd2".
483 mAEffect->ptr2 = static_cast<VSTWrapper*>(this);
484
485 // Give the plugin an initial sample rate and blocksize
486 callDispatcher(effSetSampleRate, 0, 0, NULL, 48000.0);
487 callDispatcher(effSetBlockSize, 0, 512, NULL, 0);
488
489 // Ask the plugin to identify itself...might be needed for older plugins
490 callDispatcher(effIdentify, 0, 0, NULL, 0);
491
492 // Open the plugin
493 callDispatcher(effOpen, 0, 0, NULL, 0.0);
494
495 // Get the VST version the plugin understands
497
498 // Set it again in case plugin ignored it before the effOpen
499 callDispatcher(effSetSampleRate, 0, 0, NULL, 48000.0);
500 callDispatcher(effSetBlockSize, 0, 512, NULL, 0);
501
502 // Ensure that it looks like a plugin and can deal with ProcessReplacing
503 // calls. Also exclude synths for now.
504 if (mAEffect->magic == kEffectMagic &&
507 {
508 if (mVstVersion >= 2)
509 {
511 if (mName.length() == 0)
512 {
514 }
515 }
516 if (mName.length() == 0)
517 {
518 mName = wxFileName{realPath}.GetName();
519 }
520
521 if (mVstVersion >= 2)
522 {
524 mVersion = wxINT32_SWAP_ON_LE(callDispatcher(effGetVendorVersion, 0, 0, NULL, 0));
525 }
526 if (mVersion == 0)
527 {
528 mVersion = wxINT32_SWAP_ON_LE(mAEffect->version);
529 }
530
532 {
533 mInteractive = true;
534 }
535
538
539 // Check to see if parameters can be automated. This isn't a guarantee
540 // since it could be that the effect simply doesn't support the opcode.
541 mAutomatable = false;
542 for (int i = 0; i < mAEffect->numParams; i++)
543 {
544 if (callDispatcher(effCanBeAutomated, 0, i, NULL, 0.0))
545 {
546 mAutomatable = true;
547 break;
548 }
549 }
550
551 // Make sure we start out with a valid program selection
552 // I've found one plugin (SoundHack +morphfilter) that will
553 // crash Audacity when saving the initial default parameters
554 // with this.
556
557 // Pretty confident that we're good to go
558 success = true;
559 }
560 }
561
562 if (!success)
563 {
564 Unload();
566 }
567
568 return success;
569}
570
572{
573 if (mAEffect)
574 {
575 // Finally, close the plugin
576 callDispatcher(effClose, 0, 0, NULL, 0.0);
577 mAEffect = NULL;
578 }
579
580 //ResetModuleAndHandle();
581}
582
584{
585 if (mModule)
586 {
587#if defined(__WXMAC__)
589 mBundleRef.reset();
590#endif
591
592 mModule.reset();
593 mAEffect = NULL;
594 }
595}
596
598{
599 Unload();
601}
602
604{
606 return info;
607}
608
610{
611 return (info.pluginUniqueID == mAEffect->uniqueID) &&
612 (info.pluginVersion == mAEffect->version) &&
613 (info.numElements == mAEffect->numParams);
614}
615
617{}
618
620{
621}
622
624{
625}
626
628{
629 mTimeInfo.nanoSeconds = wxGetUTCTimeMillis().ToDouble();
630 return &mTimeInfo;
631}
632
634{
635 return mTimeInfo.sampleRate;
636}
637
639{
640 return mProcessLevel;
641}
642
643void VSTUIWrapper::SizeWindow(int w, int h)
644{
645}
646
648{
649}
650
651int VSTWrapper::GetString(wxString & outstr, int opcode, int index) const
652{
653 char buf[256];
654
655 memset(buf, 0, sizeof(buf));
656
657 // Assume we are passed a read-only dispatcher function code
658 constCallDispatcher(opcode, index, 0, buf, 0.0);
659
660 outstr = wxString::FromUTF8(buf);
661
662 return 0;
663}
664
665wxString VSTWrapper::GetString(int opcode, int index) const
666{
667 wxString str;
668
669 GetString(str, opcode, index);
670
671 return str;
672}
673
674void VSTWrapper::SetString(int opcode, const wxString & str, int index)
675{
676 char buf[256];
677 strcpy(buf, str.Left(255).ToUTF8());
678
679 callDispatcher(opcode, index, 0, buf, 0.0);
680}
681
682intptr_t VSTWrapper::callDispatcher(int opcode,
683 int index, intptr_t value, void *ptr, float opt)
684{
685 // Needed since we might be in the dispatcher when the timer pops
686 std::lock_guard guard(mDispatcherLock);
687
688 return mAEffect->dispatcher(mAEffect, opcode, index, value, ptr, opt);
689}
690
692 int index, intptr_t value, void *ptr, float opt) const
693{
694 // Assume we are passed a read-only dispatcher function code
695 return const_cast<VSTWrapper*>(this)
696 ->callDispatcher(opcode, index, value, ptr, opt);
697}
698
699float VSTWrapper::callGetParameter(int index) const
700{
701 return mAEffect->getParameter(mAEffect, index);
702}
703
704void VSTWrapper::callSetParameter(int index, float value) const
705{
706 if (mVstVersion == 0 || constCallDispatcher(effCanBeAutomated, 0, index, NULL, 0.0))
707 {
708 mAEffect->setParameter(mAEffect, index, value);
709 }
710}
711
713{
714 callDispatcher(effBeginSetProgram, 0, 0, NULL, 0.0);
715
716 callDispatcher(effSetProgram, 0, index, NULL, 0.0);
717
718 callDispatcher(effEndSetProgram, 0, 0, NULL, 0.0);
719}
720
721void VSTWrapper::callSetChunk(bool isPgm, int len, void *buf)
722{
724
725 memset(&info, 0, sizeof(info));
726 info.version = 1;
730
731 callSetChunk(isPgm, len, buf, &info);
732}
733
734void VSTWrapper::callSetChunk(bool isPgm, int len, void *buf, VstPatchChunkInfo *info) const
735{
736 if (isPgm)
737 {
738 // Ask the effect if this is an acceptable program
739 if (constCallDispatcher(effBeginLoadProgram, 0, 0, info, 0.0) == -1)
740 {
741 return;
742 }
743 }
744 else
745 {
746 // Ask the effect if this is an acceptable bank
747 if (constCallDispatcher(effBeginLoadBank, 0, 0, info, 0.0) == -1)
748 {
749 return;
750 }
751 }
752
753 constCallDispatcher(effBeginSetProgram, 0, 0, NULL, 0.0);
754 constCallDispatcher(effSetChunk, isPgm ? 1 : 0, len, buf, 0.0);
755 constCallDispatcher(effEndSetProgram, 0, 0, NULL, 0.0);
756}
757
758bool VSTWrapper::LoadFXB(const wxFileName & fn)
759{
760 bool ret = false;
761
762 // Try to open the file...will be closed automatically when method returns
763 wxFFile f(fn.GetFullPath(), wxT("rb"));
764 if (!f.IsOpened())
765 {
766 return false;
767 }
768
769 // Allocate memory for the contents
770 ArrayOf<unsigned char> data{ size_t(f.Length()) };
771 if (!data)
772 {
773 using namespace BasicUI;
775 XO("Unable to allocate memory when loading presets file."),
777 .Caption(XO("Error Loading VST Presets")));
778 return false;
779 }
780 unsigned char *bptr = data.get();
781
782 do
783 {
784 // Read in the whole file
785 ssize_t len = f.Read((void *) bptr, f.Length());
786 if (f.Error())
787 {
788 using namespace BasicUI;
790 XO("Unable to read presets file."),
792 .Caption(XO("Error Loading VST Presets")));
793 break;
794 }
795
796 // Most references to the data are via an "int" array
797 int32_t *iptr = (int32_t *) bptr;
798
799 // Verify that we have at least enough for the header
800 if (len < 156)
801 {
802 break;
803 }
804
805 // Verify that we probably have an FX file
806 if (wxINT32_SWAP_ON_LE(iptr[0]) != CCONST('C', 'c', 'n', 'K'))
807 {
808 break;
809 }
810
811 // Ignore the size...sometimes it's there, other times it's zero
812
813 // Get the version and verify
814 int version = wxINT32_SWAP_ON_LE(iptr[3]);
815 if (version != 1 && version != 2)
816 {
817 break;
818 }
819
820 VstPatchChunkInfo info =
821 {
822 1,
823 wxINT32_SWAP_ON_LE(iptr[4]),
824 wxINT32_SWAP_ON_LE(iptr[5]),
825 wxINT32_SWAP_ON_LE(iptr[6]),
826 ""
827 };
828
829 // Ensure this program looks to belong to the current plugin
830 if ((info.pluginUniqueID != mAEffect->uniqueID) &&
831 (info.pluginVersion != mAEffect->version) &&
833 {
834 break;
835 }
836
837 // Get the number of programs
838 int numProgs = info.numElements;
839
840 // Get the current program index
841 int curProg = 0;
842 if (version >= 2)
843 {
844 curProg = wxINT32_SWAP_ON_LE(iptr[7]);
845 if (curProg < 0 || curProg >= numProgs)
846 {
847 break;
848 }
849 }
850
851 // Is it a bank of programs?
852 if (wxINT32_SWAP_ON_LE(iptr[2]) == CCONST('F', 'x', 'B', 'k'))
853 {
854 // Drop the header
855 bptr += 156;
856 len -= 156;
857
858 unsigned char *tempPtr = bptr;
859 ssize_t tempLen = len;
860
861 // Validate all of the programs
862 for (int i = 0; i < numProgs; i++)
863 {
864 if (!LoadFXProgram(&tempPtr, tempLen, i, true))
865 {
866 break;
867 }
868 }
869
870 // Ask the effect if this is an acceptable bank
871 if (callDispatcher(effBeginLoadBank, 0, 0, &info, 0.0) == -1)
872 {
873 return false;
874 }
875
876 // Start loading the individual programs
877 for (int i = 0; i < numProgs; i++)
878 {
879 ret = LoadFXProgram(&bptr, len, i, false);
880 }
881 }
882 // Or maybe a bank chunk?
883 else if (wxINT32_SWAP_ON_LE(iptr[2]) == CCONST('F', 'B', 'C', 'h'))
884 {
885 // Can't load programs chunks if the plugin doesn't support it
887 {
888 break;
889 }
890
891 // Verify that we have enough to grab the chunk size
892 if (len < 160)
893 {
894 break;
895 }
896
897 // Get the chunk size
898 int size = wxINT32_SWAP_ON_LE(iptr[39]);
899
900 // We finally know the full length of the program
901 int proglen = 160 + size;
902
903 // Verify that we have enough for the entire program
904 if (len < proglen)
905 {
906 break;
907 }
908
909 // Set the entire bank in one shot
910 callSetChunk(false, size, &iptr[40], &info);
911
912 // Success
913 ret = true;
914 }
915 // Unrecognizable type
916 else
917 {
918 break;
919 }
920
921 // Set the active program
922 if (ret && version >= 2)
923 {
924 callSetProgram(curProg);
925 }
926 } while (false);
927
928 return ret;
929}
930
931bool VSTWrapper::LoadFXP(const wxFileName & fn)
932{
933 bool ret = false;
934
935 // Try to open the file...will be closed automatically when method returns
936 wxFFile f(fn.GetFullPath(), wxT("rb"));
937 if (!f.IsOpened())
938 {
939 return false;
940 }
941
942 // Allocate memory for the contents
943 ArrayOf<unsigned char> data{ size_t(f.Length()) };
944 if (!data)
945 {
946 using namespace BasicUI;
948 XO("Unable to allocate memory when loading presets file."),
950 .Caption(XO("Error Loading VST Presets")));
951 return false;
952 }
953 unsigned char *bptr = data.get();
954
955 do
956 {
957 // Read in the whole file
958 ssize_t len = f.Read((void *) bptr, f.Length());
959 if (f.Error())
960 {
961 using namespace BasicUI;
963 XO("Unable to read presets file."),
965 .Caption(XO("Error Loading VST Presets")));
966 break;
967 }
968
969 // Get (or default) currently selected program
970 int i = 0; //mProgram->GetCurrentSelection();
971 if (i < 0)
972 {
973 i = 0; // default to first program
974 }
975
976 // Go verify and set the program
977 ret = LoadFXProgram(&bptr, len, i, false);
978 } while (false);
979
980 return ret;
981}
982
983bool VSTWrapper::LoadFXProgram(unsigned char **bptr, ssize_t & len, int index, bool dryrun)
984{
985 // Most references to the data are via an "int" array
986 int32_t *iptr = (int32_t *) *bptr;
987
988 // Verify that we have at least enough for a program without parameters
989 if (len < 28)
990 {
991 return false;
992 }
993
994 // Verify that we probably have an FX file
995 if (wxINT32_SWAP_ON_LE(iptr[0]) != CCONST('C', 'c', 'n', 'K'))
996 {
997 return false;
998 }
999
1000 // Ignore the size...sometimes it's there, other times it's zero
1001
1002 // Get the version and verify
1003#if defined(IS_THIS_AN_FXP_ARTIFICAL_LIMITATION)
1004 int version = wxINT32_SWAP_ON_LE(iptr[3]);
1005 if (version != 1)
1006 {
1007 return false;
1008 }
1009#endif
1010
1011 VstPatchChunkInfo info =
1012 {
1013 1,
1014 wxINT32_SWAP_ON_LE(iptr[4]),
1015 wxINT32_SWAP_ON_LE(iptr[5]),
1016 wxINT32_SWAP_ON_LE(iptr[6]),
1017 ""
1018 };
1019
1020 // Ensure this program looks to belong to the current plugin
1021 if ((info.pluginUniqueID != mAEffect->uniqueID) &&
1022 (info.pluginVersion != mAEffect->version) &&
1023 (info.numElements != mAEffect->numParams))
1024 {
1025 return false;
1026 }
1027
1028 // Get the number of parameters
1029 int numParams = info.numElements;
1030
1031 // At this point, we have to have enough to include the program name as well
1032 if (len < 56)
1033 {
1034 return false;
1035 }
1036
1037 // Get the program name
1038 wxString progName(wxString::From8BitData((char *)&iptr[7]));
1039
1040 // Might be a regular program
1041 if (wxINT32_SWAP_ON_LE(iptr[2]) == CCONST('F', 'x', 'C', 'k'))
1042 {
1043 // We finally know the full length of the program
1044 int proglen = 56 + (numParams * sizeof(float));
1045
1046 // Verify that we have enough for all of the parameter values
1047 if (len < proglen)
1048 {
1049 return false;
1050 }
1051
1052 // Validate all of the parameter values
1053 for (int i = 0; i < numParams; i++)
1054 {
1055 uint32_t ival = wxUINT32_SWAP_ON_LE(iptr[14 + i]);
1056 float val = reinterpretAsFloat(ival);
1057 if (val < 0.0 || val > 1.0)
1058 {
1059 return false;
1060 }
1061 }
1062
1063 // They look okay...time to start changing things
1064 if (!dryrun)
1065 {
1066 // Ask the effect if this is an acceptable program
1067 if (callDispatcher(effBeginLoadProgram, 0, 0, &info, 0.0) == -1)
1068 {
1069 return false;
1070 }
1071
1072 // Load all of the parameters
1073 callDispatcher(effBeginSetProgram, 0, 0, NULL, 0.0);
1074 for (int i = 0; i < numParams; i++)
1075 {
1076 wxUint32 val = wxUINT32_SWAP_ON_LE(iptr[14 + i]);
1078 }
1079 callDispatcher(effEndSetProgram, 0, 0, NULL, 0.0);
1080 }
1081
1082 // Update in case we're loading an "FxBk" format bank file
1083 *bptr += proglen;
1084 len -= proglen;
1085 }
1086 // Maybe we have a program chunk
1087 else if (wxINT32_SWAP_ON_LE(iptr[2]) == CCONST('F', 'P', 'C', 'h'))
1088 {
1089 // Can't load programs chunks if the plugin doesn't support it
1091 {
1092 return false;
1093 }
1094
1095 // Verify that we have enough to grab the chunk size
1096 if (len < 60)
1097 {
1098 return false;
1099 }
1100
1101 // Get the chunk size
1102 int size = wxINT32_SWAP_ON_LE(iptr[14]);
1103
1104 // We finally know the full length of the program
1105 int proglen = 60 + size;
1106
1107 // Verify that we have enough for the entire program
1108 if (len < proglen)
1109 {
1110 return false;
1111 }
1112
1113 // Set the entire program in one shot
1114 if (!dryrun)
1115 {
1116 callSetChunk(true, size, &iptr[15], &info);
1117 }
1118
1119 // Update in case we're loading an "FxBk" format bank file
1120 *bptr += proglen;
1121 len -= proglen;
1122 }
1123 else
1124 {
1125 // Unknown type
1126 return false;
1127 }
1128
1129 if (!dryrun)
1130 {
1131 SetString(effSetProgramName, wxString(progName), index);
1132 }
1133
1134 return true;
1135}
1136
1137bool VSTWrapper::LoadXML(const wxFileName & fn)
1138{
1139 mInChunk = false;
1140 mInSet = false;
1141
1142 // default to read as XML file
1143 // Load the program
1144 XMLFileReader reader;
1145 bool ok = reader.Parse(this, fn.GetFullPath());
1146
1147 // Something went wrong with the file, clean up
1148 if (mInSet)
1149 {
1150 callDispatcher(effEndSetProgram, 0, 0, NULL, 0.0);
1151
1152 mInSet = false;
1153 }
1154
1155 if (!ok)
1156 {
1157 using namespace BasicUI;
1158 // Inform user of load failure
1160 reader.GetErrorStr(),
1162 .Caption(XO("Error Loading VST Presets")));
1163 return false;
1164 }
1165
1166 return true;
1167}
1168
1169void VSTWrapper::SaveFXB(const wxFileName & fn) const
1170{
1171 // Create/Open the file
1172 const wxString fullPath{fn.GetFullPath()};
1173 wxFFile f(fullPath, wxT("wb"));
1174 if (!f.IsOpened())
1175 {
1176 using namespace BasicUI;
1178 XO("Could not open file: \"%s\"").Format( fullPath ),
1180 .Caption(XO("Error Saving VST Presets")));
1181 return;
1182 }
1183
1184 wxMemoryBuffer buf;
1185 wxInt32 subType;
1186 void *chunkPtr = nullptr;
1187 int chunkSize = 0;
1188 int dataSize = 148;
1189 wxInt32 tab[8];
1190 int curProg = 0 ; //mProgram->GetCurrentSelection();
1191
1193 {
1194 subType = CCONST('F', 'B', 'C', 'h');
1195
1196 // read-only dispatcher function
1197 chunkSize = constCallDispatcher(effGetChunk, 0, 0, &chunkPtr, 0.0);
1198 dataSize += 4 + chunkSize;
1199 }
1200 else
1201 {
1202 subType = CCONST('F', 'x', 'B', 'k');
1203
1204 for (int i = 0; i < mAEffect->numPrograms; i++)
1205 {
1206 SaveFXProgram(buf, i);
1207 }
1208
1209 dataSize += buf.GetDataLen();
1210 }
1211
1212 tab[0] = wxINT32_SWAP_ON_LE(CCONST('C', 'c', 'n', 'K'));
1213 tab[1] = wxINT32_SWAP_ON_LE(dataSize);
1214 tab[2] = wxINT32_SWAP_ON_LE(subType);
1215 tab[3] = wxINT32_SWAP_ON_LE(curProg >= 0 ? 2 : 1);
1216 tab[4] = wxINT32_SWAP_ON_LE(mAEffect->uniqueID);
1217 tab[5] = wxINT32_SWAP_ON_LE(mAEffect->version);
1218 tab[6] = wxINT32_SWAP_ON_LE(mAEffect->numPrograms);
1219 tab[7] = wxINT32_SWAP_ON_LE(curProg >= 0 ? curProg : 0);
1220
1221 f.Write(tab, sizeof(tab));
1222 if (!f.Error())
1223 {
1224 char padding[124];
1225 memset(padding, 0, sizeof(padding));
1226 f.Write(padding, sizeof(padding));
1227
1228 if (!f.Error())
1229 {
1231 {
1232 wxInt32 size = wxINT32_SWAP_ON_LE(chunkSize);
1233 f.Write(&size, sizeof(size));
1234 f.Write(chunkPtr, chunkSize);
1235 }
1236 else
1237 {
1238 f.Write(buf.GetData(), buf.GetDataLen());
1239 }
1240 }
1241 }
1242
1243 if (f.Error())
1244 {
1245 using namespace BasicUI;
1247 XO("Error writing to file: \"%s\"").Format( fullPath ),
1249 .Caption(XO("Error Saving VST Presets")));
1250 }
1251
1252 f.Close();
1253
1254 return;
1255}
1256
1257void VSTWrapper::SaveFXP(const wxFileName & fn) const
1258{
1259 // Create/Open the file
1260 const wxString fullPath{ fn.GetFullPath() };
1261 wxFFile f(fullPath, wxT("wb"));
1262 if (!f.IsOpened())
1263 {
1264 using namespace BasicUI;
1266 XO("Could not open file: \"%s\"").Format( fullPath ),
1268 .Caption(XO("Error Saving VST Presets")));
1269 return;
1270 }
1271
1272 wxMemoryBuffer buf;
1273
1274 // read-only dispatcher function
1275 int ndx = constCallDispatcher(effGetProgram, 0, 0, NULL, 0.0);
1276 SaveFXProgram(buf, ndx);
1277
1278 f.Write(buf.GetData(), buf.GetDataLen());
1279 if (f.Error())
1280 {
1281 using namespace BasicUI;
1283 XO("Error writing to file: \"%s\"").Format( fullPath ),
1285 .Caption(XO("Error Saving VST Presets")));
1286 }
1287
1288 f.Close();
1289
1290 return;
1291}
1292
1293void VSTWrapper::SaveFXProgram(wxMemoryBuffer & buf, int index) const
1294{
1295 wxInt32 subType;
1296 void *chunkPtr;
1297 int chunkSize;
1298 int dataSize = 48;
1299 char progName[28];
1300 wxInt32 tab[7];
1301
1302 // read-only dispatcher function
1303 constCallDispatcher(effGetProgramNameIndexed, index, 0, &progName, 0.0);
1304 progName[27] = '\0';
1305 chunkSize = strlen(progName);
1306 memset(&progName[chunkSize], 0, sizeof(progName) - chunkSize);
1307
1309 {
1310 subType = CCONST('F', 'P', 'C', 'h');
1311
1312 // read-only dispatcher function
1313 chunkSize = constCallDispatcher(effGetChunk, 1, 0, &chunkPtr, 0.0);
1314 dataSize += 4 + chunkSize;
1315 }
1316 else
1317 {
1318 subType = CCONST('F', 'x', 'C', 'k');
1319
1320 dataSize += (mAEffect->numParams << 2);
1321 }
1322
1323 tab[0] = wxINT32_SWAP_ON_LE(CCONST('C', 'c', 'n', 'K'));
1324 tab[1] = wxINT32_SWAP_ON_LE(dataSize);
1325 tab[2] = wxINT32_SWAP_ON_LE(subType);
1326 tab[3] = wxINT32_SWAP_ON_LE(1);
1327 tab[4] = wxINT32_SWAP_ON_LE(mAEffect->uniqueID);
1328 tab[5] = wxINT32_SWAP_ON_LE(mAEffect->version);
1329 tab[6] = wxINT32_SWAP_ON_LE(mAEffect->numParams);
1330
1331 buf.AppendData(tab, sizeof(tab));
1332 buf.AppendData(progName, sizeof(progName));
1333
1335 {
1336 wxInt32 size = wxINT32_SWAP_ON_LE(chunkSize);
1337 buf.AppendData(&size, sizeof(size));
1338 buf.AppendData(chunkPtr, chunkSize);
1339 }
1340 else
1341 {
1342 for (int i = 0; i < mAEffect->numParams; i++)
1343 {
1344 float val = callGetParameter(i);
1345 wxUint32 ival = wxUINT32_SWAP_ON_LE(reinterpretAsUint32(val));
1346 buf.AppendData(&ival, sizeof(ival));
1347 }
1348 }
1349
1350 return;
1351}
1352
1353// Throws exceptions rather than giving error return.
1354void VSTWrapper::SaveXML(const wxFileName & fn) const
1355// may throw
1356{
1357 XMLFileWriter xmlFile{ fn.GetFullPath(), XO("Error Saving Effect Presets") };
1358
1359 xmlFile.StartTag(wxT("vstprogrampersistence"));
1360 xmlFile.WriteAttr(wxT("version"), wxT("2"));
1361
1362 xmlFile.StartTag(wxT("effect"));
1363 // Use internal name only in persistent information
1364 xmlFile.WriteAttr(wxT("name"), GetSymbol().Internal());
1365 xmlFile.WriteAttr(wxT("uniqueID"), mAEffect->uniqueID);
1366 xmlFile.WriteAttr(wxT("version"), mAEffect->version);
1367 xmlFile.WriteAttr(wxT("numParams"), mAEffect->numParams);
1368
1369 xmlFile.StartTag(wxT("program"));
1370 xmlFile.WriteAttr(wxT("name"), wxEmptyString); //mProgram->GetValue());
1371
1372 int clen = 0;
1374 {
1375 void *chunk = NULL;
1376
1377 // read-only dispatcher function
1378 clen = (int) constCallDispatcher(effGetChunk, 1, 0, &chunk, 0.0);
1379 if (clen != 0)
1380 {
1381 xmlFile.StartTag(wxT("chunk"));
1382 xmlFile.WriteSubTree(Base64::Encode(chunk, clen) + wxT('\n'));
1383 xmlFile.EndTag(wxT("chunk"));
1384 }
1385 }
1386
1387 if (clen == 0)
1388 {
1389 for (int i = 0; i < mAEffect->numParams; i++)
1390 {
1391 xmlFile.StartTag(wxT("param"));
1392
1393 xmlFile.WriteAttr(wxT("index"), i);
1394 xmlFile.WriteAttr(wxT("name"),
1396 xmlFile.WriteAttr(wxT("value"),
1397 wxString::Format(wxT("%f"),
1398 callGetParameter(i)));
1399
1400 xmlFile.EndTag(wxT("param"));
1401 }
1402 }
1403
1404 xmlFile.EndTag(wxT("program"));
1405
1406 xmlFile.EndTag(wxT("effect"));
1407
1408 xmlFile.EndTag(wxT("vstprogrampersistence"));
1409
1410 xmlFile.Commit();
1411}
1412
1413bool VSTWrapper::HandleXMLTag(const std::string_view& tag, const AttributesList &attrs)
1414{
1415 if (tag == "vstprogrampersistence")
1416 {
1417 for (auto pair : attrs)
1418 {
1419 auto attr = pair.first;
1420 auto value = pair.second;
1421
1422 if (attr == "version")
1423 {
1424 if (!value.TryGet(mXMLVersion))
1425 {
1426 return false;
1427 }
1428
1429 if (mXMLVersion < 1 || mXMLVersion > 2)
1430 {
1431 return false;
1432 }
1433 }
1434 else
1435 {
1436 return false;
1437 }
1438 }
1439
1440 return true;
1441 }
1442
1443 if (tag == "effect")
1444 {
1445 memset(&mXMLInfo, 0, sizeof(mXMLInfo));
1446 mXMLInfo.version = 1;
1450
1451 for (auto pair : attrs)
1452 {
1453 auto attr = pair.first;
1454 auto value = pair.second;
1455
1456 if (attr == "name")
1457 {
1458 wxString strValue = value.ToWString();
1459
1460 if (strValue != GetSymbol().Internal())
1461 {
1462 using namespace BasicUI;
1463 auto msg = XO("This parameter file was saved from %s. Continue?")
1464 .Format( strValue );
1465 auto result = ShowMessageBox(
1466 msg,
1468 .Caption(XO("Confirm"))
1469 .ButtonStyle(Button::YesNo));
1470 if (result == MessageBoxResult::No)
1471 return false;
1472 }
1473 }
1474 else if (attr == "version")
1475 {
1476 long version;
1477 if (!value.TryGet(version))
1478 {
1479 return false;
1480 }
1481
1482 mXMLInfo.pluginVersion = (int) version;
1483 }
1484 else if (mXMLVersion > 1 && attr == "uniqueID")
1485 {
1486 long uniqueID;
1487 if (!value.TryGet(uniqueID))
1488 {
1489 return false;
1490 }
1491
1492 mXMLInfo.pluginUniqueID = (int) uniqueID;
1493 }
1494 else if (mXMLVersion > 1 && attr == "numParams")
1495 {
1496 long numParams;
1497 if (!value.TryGet(numParams))
1498 {
1499 return false;
1500 }
1501
1502 mXMLInfo.numElements = (int) numParams;
1503 }
1504 else
1505 {
1506 return false;
1507 }
1508 }
1509
1510 return true;
1511 }
1512
1513 if (tag == "program")
1514 {
1515 for (auto pair : attrs)
1516 {
1517 auto attr = pair.first;
1518 auto value = pair.second;
1519
1520 if (attr == "name")
1521 {
1522 const wxString strValue = value.ToWString();
1523
1524 if (strValue.length() > 24)
1525 {
1526 return false;
1527 }
1528
1529 int ndx = 0; //mProgram->GetCurrentSelection();
1530 if (ndx == wxNOT_FOUND)
1531 {
1532 ndx = 0;
1533 }
1534
1535 SetString(effSetProgramName, strValue, ndx);
1536 }
1537 else
1538 {
1539 return false;
1540 }
1541 }
1542
1543 mInChunk = false;
1544
1545 if (callDispatcher(effBeginLoadProgram, 0, 0, &mXMLInfo, 0.0) == -1)
1546 {
1547 return false;
1548 }
1549
1550 callDispatcher(effBeginSetProgram, 0, 0, NULL, 0.0);
1551
1552 mInSet = true;
1553
1554 return true;
1555 }
1556
1557 if (tag == "param")
1558 {
1559 long ndx = -1;
1560 double val = -1.0;
1561
1562 for (auto pair : attrs)
1563 {
1564 auto attr = pair.first;
1565 auto value = pair.second;
1566
1567 if (attr == "index")
1568 {
1569 if (!value.TryGet(ndx))
1570 {
1571 return false;
1572 }
1573
1574 if (ndx < 0 || ndx >= mAEffect->numParams)
1575 {
1576 // Could be a different version of the effect...probably should
1577 // tell the user
1578 return false;
1579 }
1580 }
1581 // "name" attribute is ignored for params
1582 /* else if (attr == "name")
1583 {
1584
1585 // Nothing to do with it for now
1586 }*/
1587 else if (attr == "value")
1588 {
1589 if (!value.TryGet(val))
1590 {
1591 return false;
1592 }
1593
1594 if (val < 0.0 || val > 1.0)
1595 {
1596 return false;
1597 }
1598 }
1599 }
1600
1601 if (ndx == -1 || val == -1.0)
1602 {
1603 return false;
1604 }
1605
1606 callSetParameter(ndx, val);
1607
1608 return true;
1609 }
1610
1611 if (tag == "chunk")
1612 {
1613 mInChunk = true;
1614 return true;
1615 }
1616
1617 return false;
1618}
1619
1620void VSTWrapper::HandleXMLEndTag(const std::string_view& tag)
1621{
1622 if (tag == "chunk")
1623 {
1624 if (mChunk.length())
1625 {
1626 ArrayOf<char> buf{ mChunk.length() / 4 * 3 };
1627
1628 int len = Base64::Decode(mChunk, buf.get());
1629 if (len)
1630 {
1631 callSetChunk(true, len, buf.get(), &mXMLInfo);
1632 }
1633
1634 mChunk.clear();
1635 }
1636 mInChunk = false;
1637 }
1638
1639 if (tag == "program")
1640 {
1641 if (mInSet)
1642 {
1643 callDispatcher(effEndSetProgram, 0, 0, NULL, 0.0);
1644
1645 mInSet = false;
1646 }
1647 }
1648}
1649
1650void VSTWrapper::HandleXMLContent(const std::string_view& content)
1651{
1652 if (mInChunk)
1653 {
1654 mChunk += wxString(std::string(content)).Trim(true).Trim(false);
1655 }
1656}
1657
1658XMLTagHandler *VSTWrapper::HandleXMLChild(const std::string_view& tag)
1659{
1660 if (tag == "vstprogrampersistence")
1661 {
1662 return this;
1663 }
1664
1665 if (tag == "effect")
1666 {
1667 return this;
1668 }
1669
1670 if (tag == "program")
1671 {
1672 return this;
1673 }
1674
1675 if (tag == "param")
1676 {
1677 return this;
1678 }
1679
1680 if (tag == "chunk")
1681 {
1682 return this;
1683 }
1684
1685 return NULL;
1686}
1687
1689{
1690 for (int i = 0; i < mAEffect->numParams; i++)
1691 {
1692 wxString name = GetString(effGetParamName, i);
1693 if (name.empty())
1694 {
1695 name.Printf(wxT("parm_%d"), i);
1696 }
1697 else
1698 /* Easy fix for now for issue 3854, but this should be reconsidered
1699 There is the possibility that two parameter names might collide
1700 after normalizing. A question is whether the normalizing was ever
1701 really needed for saving in a wxConfigFile. Maybe not. But then
1702 redefinition of the keys stored in the file may introduce versioning
1703 difficulties if there is an attempt to fix this in future Audacity.
1704 */
1706
1707 ParameterInfo pi{ i, name };
1708
1709 if (!visitor(pi))
1710 break;
1711 }
1712}
1713
1714bool VSTWrapper::FetchSettings(VSTSettings& vstSettings, bool doFetch) const
1715{
1716 // Get the fallback ID-value parameters
1718 (
1719 [&](const ParameterInfo& pi)
1720 {
1721 if (doFetch)
1722 {
1723 float val = callGetParameter(pi.mID);
1724 vstSettings.mParamsMap[pi.mName] = val;
1725 }
1726 else
1727 {
1728 vstSettings.mParamsMap[pi.mName] = std::nullopt;
1729 }
1730 return true;
1731 }
1732 );
1733
1734 // These are here to be checked against for compatibility later
1735 vstSettings.mVersion = mAEffect->version;
1736 vstSettings.mUniqueID = mAEffect->uniqueID;
1737 vstSettings.mNumParams = mAEffect->numParams;
1738
1739 // Get the chunk (if supported)
1740 vstSettings.mChunk.resize(0);
1741
1743 {
1744 void* chunk = nullptr;
1745 int clen = (int)constCallDispatcher(effGetChunk, 1, 0, &chunk, 0.0);
1746 if (clen > 0 && chunk) {
1747 vstSettings.mChunk.resize(clen);
1748 memcpy(vstSettings.mChunk.data(), chunk, clen);
1749 }
1750
1751 if (!doFetch)
1752 {
1753 // Don't keep the contents, but keep a sufficiently allocated string,
1754 // with some extra space in case chunk length might vary
1755 auto size = vstSettings.mChunk.size();
1756 vstSettings.mChunk.resize(0);
1757 vstSettings.mChunk.reserve(2 * size);
1758 }
1759 }
1760
1761 return true;
1762}
1763
1764bool VSTWrapper::StoreSettings(const VSTSettings& vstSettings) const
1765{
1766 // First, make sure settings are compatibile with the plugin
1767 if ((vstSettings.mUniqueID != mAEffect->uniqueID) ||
1768// (vstSettings.mVersion != mAEffect->version) ||
1769 (vstSettings.mNumParams != mAEffect->numParams) )
1770 {
1771 return false;
1772 }
1773
1774
1775 // Try using the chunk first (if available)
1776 auto &chunk = vstSettings.mChunk;
1777 if (!chunk.empty())
1778 {
1780 callSetChunk(true, chunk.size(), const_cast<char *>(chunk.data()), &info);
1781 }
1782
1783
1784 // Settings (like the message) may store both a chunk, and also accumulated
1785 // slider movements to reapply after the chunk change. Or it might be
1786 // no chunk and id-value pairs only
1787
1788 constCallDispatcher(effBeginSetProgram, 0, 0, NULL, 0.0);
1789
1791 (
1792 [&](const ParameterInfo& pi)
1793 {
1794 const auto itr = vstSettings.mParamsMap.find(pi.mName);
1795 if (itr != vstSettings.mParamsMap.end())
1796 {
1797 const float& value = *(itr->second);
1798
1799 if (value >= -1.0 && value <= 1.0)
1800 {
1801 callSetParameter(pi.mID, value);
1802 }
1803 }
1804 return true;
1805 }
1806 );
1807
1808 constCallDispatcher(effEndSetProgram, 0, 0, NULL, 0.0);
1809
1810 return true;
1811}
1812
1814{
1815 return mName;
1816}
1817
1818std::unique_ptr<EffectInstance::Message>
1820{
1821 VSTMessage::ParamVector paramVector;
1822 paramVector.resize(mAEffect->numParams, std::nullopt);
1823
1825 (
1826 [&](const VSTWrapper::ParameterInfo& pi)
1827 {
1828 auto &slot = paramVector[pi.mID]; // operator [] may make a nullopt
1829 const auto iter = settings.mParamsMap.find(pi.mName),
1830 end = settings.mParamsMap.end();
1831 if (iter != end)
1832 slot = iter->second;
1833 return true;
1834 }
1835 );
1836
1837 return std::make_unique<VSTMessage>(
1838 settings.mChunk /* vector copy */, std::move(paramVector));
1839}
1840
1841void VSTUIWrapper::Automate(int index, float value)
1842{
1843}
wxT("CloseDown"))
@ Internal
Indicates internal failure from Audacity.
Toolkit-neutral facade for basic user interface services.
#define str(a)
const TranslatableString name
Definition: Distortion.cpp:76
#define PLATFORM_MAX_PATH
Definition: FileNames.h:42
XO("Cut/Copy/Paste")
static Settings & settings()
Definition: TrackInfo.cpp:69
static uint32_t reinterpretAsUint32(float f)
Definition: VSTWrapper.cpp:48
AEffect *(* vstPluginMain)(audioMasterCallback audioMaster)
Definition: VSTWrapper.cpp:57
static float reinterpretAsFloat(uint32_t x)
Definition: VSTWrapper.cpp:40
static const auto fn
std::vector< Attribute > AttributesList
Definition: XMLTagHandler.h:40
const int effGetVendorVersion
Definition: aeffectx.h:124
const int effGetVstVersion
Definition: aeffectx.h:128
const int kVstLangEnglish
Definition: aeffectx.h:145
const int effGetProgram
Definition: aeffectx.h:94
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 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 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 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 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 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
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
This simplifies arrays of arrays, each array separately allocated with NEW[] But it might be better t...
Definition: MemoryX.h:28
static wxString NormalizeName(const wxString &name)
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
Abstract base class used in importing a file.
double sampleRate
Definition: aeffectx.h:311
double nanoSeconds
Definition: aeffectx.h:313
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:84
This class is an interface which should be implemented by classes which wish to be able to load and s...
Definition: XMLTagHandler.h:42
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
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
Definition: BasicUI.h:279
TranslatableString Message(unsigned trackCount)
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
STL namespace.
MessageBoxOptions && Caption(TranslatableString caption_) &&
Definition: BasicUI.h:101
std::vector< std::optional< double > > ParamVector
Definition: VSTWrapper.h:300
ParamVector mParamsVec
Definition: VSTWrapper.h:325
std::unique_ptr< Message > Clone() const override
Definition: VSTWrapper.cpp:243
void Merge(Message &&src) override
Definition: VSTWrapper.cpp:270
~VSTMessage() override
void Assign(Message &&src) override
Definition: VSTWrapper.cpp:252
std::vector< char > mChunk
Definition: VSTWrapper.h:324
std::vector< char > mChunk
Definition: VSTWrapper.h:85
std::unordered_map< wxString, std::optional< double > > mParamsMap
Definition: VSTWrapper.h:88
int32_t mVersion
Definition: VSTWrapper.h:76
int32_t mNumParams
Definition: VSTWrapper.h:77
int32_t mUniqueID
Definition: VSTWrapper.h:75
virtual void Flush()
Definition: VSTWrapper.cpp:616
virtual void SizeWindow(int w, int h)
Definition: VSTWrapper.cpp:643
virtual void Automate(int index, float value)
virtual void Idle()
Definition: VSTWrapper.cpp:619
virtual void NeedIdle()
Definition: VSTWrapper.cpp:623
bool mAutomatable
Definition: VSTWrapper.h:224
unsigned mAudioOuts
Definition: VSTWrapper.h:221
void SaveFXP(const wxFileName &fn) const
bool LoadFXProgram(unsigned char **bptr, ssize_t &len, int index, bool dryrun)
Definition: VSTWrapper.cpp:983
float GetSampleRate()
Definition: VSTWrapper.cpp:633
int GetString(wxString &outstr, int opcode, int index=0) const
Definition: VSTWrapper.cpp:651
int mVstVersion
Definition: VSTWrapper.h:165
wxString mName
Definition: VSTWrapper.h:166
wxString mChunk
Definition: VSTWrapper.h:171
bool FetchSettings(VSTSettings &vst3Settings, bool doFetch=true) const
std::unique_ptr< wxDynamicLibrary > ModuleHandle
Definition: VSTWrapper.h:207
bool HandleXMLTag(const std::string_view &tag, const AttributesList &attrs) override
BundleHandle mBundleRef
Definition: VSTWrapper.h:235
bool LoadXML(const wxFileName &fn)
long mXMLVersion
Definition: VSTWrapper.h:172
bool StoreSettings(const VSTSettings &vst3settings) const
int GetProcessLevel()
Definition: VSTWrapper.cpp:638
AEffect * mAEffect
Definition: VSTWrapper.h:124
static intptr_t AudioMaster(AEffect *effect, int32_t opcode, int32_t index, intptr_t value, void *ptr, float opt)
Definition: VSTWrapper.cpp:59
void Unload()
Definition: VSTWrapper.cpp:571
void SetString(int opcode, const wxString &str, int index=0)
Definition: VSTWrapper.cpp:674
CF_ptr< CFBundleRef > BundleHandle
Definition: VSTWrapper.h:233
VstTimeInfo mTimeInfo
Definition: VSTWrapper.h:264
XMLTagHandler * HandleXMLChild(const std::string_view &tag) override
void HandleXMLEndTag(const std::string_view &tag) override
intptr_t mCurrentEffectID
Definition: VSTWrapper.h:200
std::recursive_mutex mDispatcherLock
Definition: VSTWrapper.h:133
intptr_t constCallDispatcher(int opcode, int index, intptr_t value, void *ptr, float opt) const
Definition: VSTWrapper.cpp:691
void ResetModuleAndHandle()
Definition: VSTWrapper.cpp:583
void SaveFXB(const wxFileName &fn) const
bool LoadFXP(const wxFileName &fn)
Definition: VSTWrapper.cpp:931
void ForEachParameter(ParameterVisitor visitor) const
bool IsCompatible(const VstPatchChunkInfo &) const
Definition: VSTWrapper.cpp:609
intptr_t callDispatcher(int opcode, int index, intptr_t value, void *ptr, float opt) override
Definition: VSTWrapper.cpp:682
bool mInteractive
Definition: VSTWrapper.h:219
int mVersion
Definition: VSTWrapper.h:218
void callSetParameter(int index, float value) const
Definition: VSTWrapper.cpp:704
ResourceHandle mResource
Definition: VSTWrapper.h:258
virtual void SetBufferDelay(int samples)
Definition: VSTWrapper.cpp:647
float callGetParameter(int index) const
Definition: VSTWrapper.cpp:699
VstPatchChunkInfo mXMLInfo
Definition: VSTWrapper.h:173
bool Load()
Definition: VSTWrapper.cpp:307
void SaveFXProgram(wxMemoryBuffer &buf, int index) const
void callSetProgram(int index)
Definition: VSTWrapper.cpp:712
std::unique_ptr< EffectInstance::Message > MakeMessageFS(const VSTSettings &settings) const
unsigned mAudioIns
Definition: VSTWrapper.h:220
ModuleHandle mModule
Definition: VSTWrapper.h:214
void HandleXMLContent(const std::string_view &content) override
std::function< bool(const ParameterInfo &pi) > ParameterVisitor
Definition: VSTWrapper.h:152
void SaveXML(const wxFileName &fn) const
PluginPath mPath
Definition: VSTWrapper.h:203
int mProcessLevel
Definition: VSTWrapper.h:269
bool LoadFXB(const wxFileName &fn)
Definition: VSTWrapper.cpp:758
bool mInSet
Definition: VSTWrapper.h:169
VstPatchChunkInfo GetChunkInfo() const
Definition: VSTWrapper.cpp:603
wxString mVendor
Definition: VSTWrapper.h:216
bool mInChunk
Definition: VSTWrapper.h:170
void callSetChunk(bool isPgm, int len, void *buf)
Definition: VSTWrapper.cpp:721
ComponentInterfaceSymbol GetSymbol() const
VstTimeInfo * GetTimeInfo()
Definition: VSTWrapper.cpp:627
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