spotify / pedalboard

🎛 🔊 A Python library for audio.

Home Page:https://spotify.github.io/pedalboard

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Loading non .vstpreset files with load_preset function

gonzaloarca opened this issue · comments

I'm currently trying to load the Mini V3 plugin from the Arturia V Collection using the Pedalboard library and change presets programatically. I can load the plugin correctly, but for some reason I'm not able to load the factory presets.

The presets are not in .vstpreset format, and I actually believe they use some proprietary format from Arturia.

Library versions

  • Python: 3.11.4 (main, Jun 15 2023, 07:29:58) [Clang 14.0.3 (clang-1403.0.22.14.1)]
  • Pip: 23.0.1
  • Pedalboard: 0.7.8

OS: macOS 13.4

Steps to reproduce

from pedalboard import load_plugin 
mini = load_plugin('/Library/Arturia/Mini V3/Mini V3.vst3')

mini.load_preset("/Library/Arturia/Presets/Mini V3/Factory/Factory/24 VCO Unisson")

Output

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Cell In[68], line 1
----> 1 mini.load_preset("/Library/Arturia/Presets/Mini V3/Factory/Factory/24 VCO Unisson")

RuntimeError: Plugin returned an error when loading data from preset file: /Library/Arturia/Presets/Mini V3/Factory/Factory/24 VCO Unisson

Preset file structure

Here's what the 24 VCO Unisson preset file looks like in plain text:

22 serialization::archive 10 0 7 0 7 14 24 VCO Unisson 7 Factory 3 15 Katsunori Ujiie 4 Lead 0 0 10 0 3 80s 4 Hard 5 Harsh 5 Noise 9 Synthwave 6 Analog 5 Delay 11 Future Bass 5 Glide 7 Classic 1 0 0  1601683477 10 3.3.0.1391 0 0 0 0 0 0 0 0  0 0 2 0 0 0 7 Subtype 10 Dirty Lead 4 Type 10 Synth Lead 0 0 0 7 0 0 0 0 0 0 174 0 0 0 11 ACCORD_OSC2 0.50368762 11 ACCORD_OSC3 0.50030428 6 AMOUNT 0.1598158 8 ARP_HOLD 0 8 ARP_MODE 0 10 ARP_OCTAVE 0 8 ARP_PLAY 0 10 ARP_REPEAT 0 9 ARP_SPEED 0.64367568 8 ARP_SYNC 1 14 COURBE1_AMOUNT 1 12 COURBE1_DEST 0.91428572 12 COURBE1_LEFT 0.5 12 COURBE1_LOOP 1 13 COURBE1_QUANT 0 12 COURBE1_READ 0.5 13 COURBE1_RIGHT 0.5 13 COURBE1_SPEED 0.5 13 COURBE1_START 0 12 COURBE1_STOP 1 13 COURBE1_WRITE 0.5 14 COURBE2_AMOUNT 1 12 COURBE2_DEST 0.91428572 12 COURBE2_LEFT 0.5 12 COURBE2_LOOP 1 13 COURBE2_QUANT 0 12 COURBE2_READ 0.5 13 COURBE2_RIGHT 0.5 13 COURBE2_SPEED 0.5 13 COURBE2_START 0 12 COURBE2_STOP 1 13 COURBE2_WRITE 0.5 14 COURBE3_AMOUNT 1 12 COURBE3_DEST 0.91428572 12 COURBE3_LEFT 0.5 12 COURBE3_LOOP 1 13 COURBE3_QUANT 0 12 COURBE3_READ 0.5 13 COURBE3_RIGHT 0.5 13 COURBE3_SPEED 0.5 13 COURBE3_START 0 12 COURBE3_STOP 1 13 COURBE3_WRITE 0.5 14 COURBE4_AMOUNT 1 12 COURBE4_DEST 0.91428572 12 COURBE4_LEFT 0.5 12 COURBE4_LOOP 1 13 COURBE4_QUANT 0 12 COURBE4_READ 0.5 13 COURBE4_RIGHT 0.5 13 COURBE4_SPEED 0.5 13 COURBE4_START 0 12 COURBE4_STOP 1 13 COURBE4_WRITE 0.5 7 CUT_OFF 0.96024019 12 DEPTH_CHORUS 0.51844788 16 DEPTH_DELAI_LEFT 0.1484375 17 DEPTH_DELAI_RIGHT 0.140625 12 DETUNE_VOIES 0.21919708 14 DRY_WET_CHORUS 0.2001522 13 DRY_WET_DELAI 0.1386712 8 EMPHASIS 0.071220778 9 FINE_OSC2 0.65038037 9 FINE_OSC3 0.35022819 11 FORMANT_A_X 0.5 11 FORMANT_A_Y 0.1 15 FORMANT_DRY_WET 0.5 11 FORMANT_E_X 0.80000001 11 FORMANT_E_Y 0.40000001 11 FORMANT_I_X 0.69999999 11 FORMANT_I_Y 0.69999999 14 FORMANT_LFO_ON 0 16 FORMANT_LFO_RATE 0.5 18 FORMANT_LISTENER_X 0.5 18 FORMANT_LISTENER_Y 0.5 11 FORMANT_O_X 0.30000001 11 FORMANT_O_Y 0.69999999 9 FORMANT_Q 0.5 11 FORMANT_U_X 0.2 11 FORMANT_U_Y 0.40000001 15 KEYB_FOLLOW1_ON 0 15 KEYB_FOLLOW2_ON 1 9 KEYB_OSC2 0.5 9 KEYB_OSC3 1 9 LEVEL_EXT 0 11 LEVEL_NOISE 0 10 LEVEL_OSC1 0.60000002 10 LEVEL_OSC2 0.60000002 10 LEVEL_OSC3 0.60000002 17 LEVEL_SUSTAIN_VCA 1 17 LEVEL_SUSTAIN_VCF 0.23451909 8 LFO_RATE 0.62326628 8 LFO_SYNC 0 8 LFO_WAVE 0.5 12 LFO_WAVE_EXT 0.084126912 12 MIDI Channel 0 15 MIXE_MODULATION 0 11 MODE_LEGATO 1 9 MODE_ZONE 1 19 MODULATION01_AMOUNT 0.98594928 18 MODULATION01_INPUT 0.26666668 19 MODULATION01_OUTPUT 0.74285716 19 MODULATION02_AMOUNT 0.51762778 18 MODULATION02_INPUT 0.53333336 19 MODULATION02_OUTPUT 0.80000001 19 MODULATION03_AMOUNT 0.83200002 18 MODULATION03_INPUT 0.40000001 19 MODULATION03_OUTPUT 0.88571429 19 MODULATION04_AMOUNT 0.5 18 MODULATION04_INPUT 0.80000001 19 MODULATION04_OUTPUT 0.91428572 19 MODULATION05_AMOUNT 0.5 18 MODULATION05_INPUT 0.80000001 19 MODULATION05_OUTPUT 0.91428572 19 MODULATION06_AMOUNT 0.5 18 MODULATION06_INPUT 0.80000001 19 MODULATION06_OUTPUT 0.91428572 19 MODULATION07_AMOUNT 0.5 18 MODULATION07_INPUT 0.80000001 19 MODULATION07_OUTPUT 0.91428572 19 MODULATION08_AMOUNT 0.5 18 MODULATION08_INPUT 0.53333336 19 MODULATION08_OUTPUT 0.31428573 24 MODULATION_WHEEL_CONTROL 0 12 MODUL_FILTRE 0 9 MODUL_OSC 0 21 MOTION_REC_START_STOP 0 9 NBR_VOIES 0.77777779 7 ON_A440 0 9 ON_CHORUS 0 8 ON_DELAI 1 6 ON_EXT 0 17 ON_FORMANT_FILTER 0 8 ON_GLIDE 1 18 ON_MOTION_RECORDER 0 8 ON_NOISE 0 7 ON_OSC1 1 7 ON_OSC2 1 7 ON_OSC3 1 10 ON_RELEASE 0 7 ON_ROSE 0 11 OVERLOAD_ON 0 11 PEDAL_GLIDE 0 13 PEDAL_RELEASE 0 16 PITCH_BEND_ACTIF 1 17 PITCH_BEND_COARSE 0.3807849 7 POLY_ON 0 10 PORTAMENTO 0.56178528 10 RANGE_OSC1 0.35022819 10 RANGE_OSC2 0.39113161 10 RANGE_OSC3 0.47792691 9 SOFT_DIST 0 12 SPEED_CHORUS 0.022962 16 SPEED_DELAI_LEFT 0.3186712 17 SPEED_DELAI_RIGHT 0.39703941 12 SYNCHRO_OSC1 0 10 SYNC_DELAI 0 4 Skin 0 17 TEMPS_ATTAQUE_VCA 0.033737492 17 TEMPS_ATTAQUE_VCF 0.083452933 15 TEMPS_DECAY_VCA 0.97575009 15 TEMPS_DECAY_VCF 0.35022819 12 TRAPPE_MODUL 1 4 TUNE 0.5078125 11 TYPE_CHORUS 0.70833331 10 UNISSON_ON 1 6 VOLUME 0.43969849 17 VST3_CtrlModWheel 0 9 WAVE_OSC1 0.40000001 9 WAVE_OSC2 0.4687472 9 WAVE_OSC3 0.3614555 10 WIDTH_OSC1 0.5 10 WIDTH_OSC2 0.5 10 WIDTH_OSC3 0 48 0 16 COURBE1_RECORDER 1536 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 17 COURBE1_REMANENCE 1280 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 16 COURBE2_RECORDER 1536 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 17 COURBE2_REMANENCE 1280 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 16 COURBE3_RECORDER 1536 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 17 COURBE3_REMANENCE 1280 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 16 COURBE4_RECORDER 1536 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 17 COURBE4_REMANENCE 1280 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 14 __HW_Mapped__0 4 ˇˇˇˇ 14 __HW_Mapped__1 4 ˇˇˇˇ 15 __HW_Mapped__10 4 ˇˇˇˇ 15 __HW_Mapped__11 4 ˇˇˇˇ 15 __HW_Mapped__12 4 ˇˇˇˇ 15 __HW_Mapped__13 4 ˇˇˇˇ 15 __HW_Mapped__14 4 ˇˇˇˇ 15 __HW_Mapped__15 4 ˇˇˇˇ 15 __HW_Mapped__16 4 ˇˇˇˇ 15 __HW_Mapped__17 4 ˇˇˇˇ 15 __HW_Mapped__18 4 ˇˇˇˇ 15 __HW_Mapped__19 4 ˇˇˇˇ 14 __HW_Mapped__2 4 ˇˇˇˇ 14 __HW_Mapped__3 4 ˇˇˇˇ 14 __HW_Mapped__4 4 ˇˇˇˇ 14 __HW_Mapped__5 4 ˇˇˇˇ 14 __HW_Mapped__6 4 ˇˇˇˇ 14 __HW_Mapped__7 4 ˇˇˇˇ 14 __HW_Mapped__8 4 ˇˇˇˇ 14 __HW_Mapped__9 4 ˇˇˇˇ 11 __Mapped__0 4 ˇˇˇˇ 11 __Mapped__1 4 ˇˇˇˇ 12 __Mapped__10 4 ˇˇˇˇ 12 __Mapped__11 4 ˇˇˇˇ 12 __Mapped__12 4 ˇˇˇˇ 12 __Mapped__13 4 ˇˇˇˇ 12 __Mapped__14 4 ˇˇˇˇ 12 __Mapped__15 4 ˇˇˇˇ 12 __Mapped__16 4 ˇˇˇˇ 12 __Mapped__17 4 ˇˇˇˇ 12 __Mapped__18 4 ˇˇˇˇ 12 __Mapped__19 4 ˇˇˇˇ 11 __Mapped__2 4 ˇˇˇˇ 11 __Mapped__3 4 ˇˇˇˇ 11 __Mapped__4 4 ˇˇˇˇ 11 __Mapped__5 4 ˇˇˇˇ 11 __Mapped__6 4 ˇˇˇˇ 11 __Mapped__7 4 ˇˇˇˇ 11 __Mapped__8 4 ˇˇˇˇ 11 __Mapped__9 4 ˇˇˇˇ

Any idea as to what might be going on? Is there any way I could possibly load this preset? Maybe convert it to .vstpreset format? I apologize if that makes no sense, I'm not too familiar with this type of files and I'm pretty new to this library.

Thanks!

Googling for 22 serialization::archive lead me to the following resources, which seems to suggest it's using the Boost libraries serialization feature:

To confirm this theory, we can download a free preset pack as follows:

For macOS, this will give us a .pkg file like Vintouch_Vol_1__1_0_0_798.pkg

From the terminal, we can then expand that .pkg file without needing to install it like so:

pkgutil --expand-full Vintouch_Vol_1__1_0_0_798.pkg ./extracted

Which will give us a directory structure like the following:

⇒ ls -la ./extracted

 Distribution
 Resources/
 Scripts/
'Vintouch Vol 1-1.0.0-Darwin-presets.pkg'/
'Vintouch Vol 1-1.0.0-Darwin-samples.pkg'/
'Vintouch Vol 1-1.0.0-Darwin-shared.pkg'/

We can then look in the Vintouch Vol 1-1.0.0-Darwin-presets.pkg/Payload/Library/Arturia/Presets/Mini V3/Factory/Vintouch Vol 1 folder:

cd extracted/Vintouch\ Vol\ 1-1.0.0-Darwin-presets.pkg/Payload/Library/Arturia/Presets/Mini\ V3/Factory/Vintouch\ Vol\ 1

⇒ ls
'K.I.T. Bass'

⇒ file K.I.T.\ Bass
K.I.T. Bass: data

I was expecting that the file command would help and tell us what it is here, but apparently not as it just thinks it's data. But viewing the file I can see that it starts with the same data as yours:

22 serialization::archive 10 0 7 0 7 11 K.I.T. Bass 14 Vintouch Vol 1 ...

I then started wondering if we would be able to deserialise this format without having to use boost itself, which lead me to:

Looking closer at the docs for Boost Python there didn't seem to be any immediately obvious examples of using it together with the deserialisation code; so it may be that some C++ would still need to be written to support this use case if desired:


I then wondered if we could just parse the format more directly ourselves, and came across this page:

  • https://www.boost.org/doc/libs/1_83_0/libs/serialization/doc/archives.html#archive_models
    • Archive Models
      This library includes various implementations of the Archive concept. An archive is defined by two complementary classes. One is for saving data while the other is for loading it. This library includes a number of archive implementations that are "ready to go" for the most common requirements. These classes implement the archive concept for differing data formats.
      They can be used "as is" or as a basis for developing one's own particular type of archive. An archive is defined by two complementary classes. One is for saving data while the other is for loading it.

      • // a portable text archive
        [boost::archive::text_oarchive](https://www.boost.org/doc/libs/1_83_0/boost/archive/text_oarchive.hpp) // saving
        [boost::archive::text_iarchive](https://www.boost.org/doc/libs/1_83_0/boost/archive/text_iarchive.hpp) // loading
        
        // a portable text archive using a wide character stream
        [boost::archive::text_woarchive](https://www.boost.org/doc/libs/1_83_0/boost/archive/text_woarchive.hpp) // saving
        [boost::archive::text_wiarchive](https://www.boost.org/doc/libs/1_83_0/boost/archive/text_wiarchive.hpp) // loading
        
        // a portable XML archive
        [boost::archive::xml_oarchive](https://www.boost.org/doc/libs/1_83_0/boost/archive/xml_oarchive.hpp) // saving
        [boost::archive::xml_iarchive](https://www.boost.org/doc/libs/1_83_0/boost/archive/xml_iarchive.hpp) // loading
        
        // a portable XML archive which uses wide characters - use for utf-8 output
        [boost::archive::xml_woarchive](https://www.boost.org/doc/libs/1_83_0/boost/archive/xml_woarchive.hpp) // saving
        [boost::archive::xml_wiarchive](https://www.boost.org/doc/libs/1_83_0/boost/archive/xml_wiarchive.hpp) // loading
        
        // a non-portable native binary archive
        [boost::archive::binary_oarchive](https://www.boost.org/doc/libs/1_83_0/boost/archive/binary_oarchive.hpp) // saving
        [boost::archive::binary_iarchive](https://www.boost.org/doc/libs/1_83_0/boost/archive/binary_iarchive.hpp) // loading
        
  • https://www.boost.org/doc/libs/1_83_0/libs/serialization/doc/headers.html#libraryimplementation

Looking through some of the code files there, and using ChatGPT to help parse through them, I eventually got to these ones which seemed to give some useful info:

But even with that, it sounds like you would pretty much have to know/reverse engineer/guess the particular layout of the structs used in the Mini V loader to be able to decode it all properly:

The `basic_text_oprimitive.hpp` header file provides the implementation details for how primitive data types are serialized to a text stream in Boost Serialization. Let's extract the format details based on the code snippets provided:

1. **Boolean Types**: Serialized as plain text "0" or "1".
   - The `save` method for `bool` asserts the value is either 0 or 1 for correctness and writes it directly to the output stream.

2. **Characters**: Serialized as short integers.
   - Both `signed char` and `unsigned char` are cast to `short int` or `short unsigned int` respectively before serialization, which means characters are stored as numeric values rather than characters.

3. **Wide Characters**: Serialized as integers.
   - `wchar_t` is static_asserted to be no larger than `int` and is serialized as an integer.

4. **Floating Point Numbers**: Serialized with precision and scientific notation.
   - Floating-point numbers are serialized using `std::setprecision` and `std::scientific`. The precision is determined based on the `max_digits10` or `digits10 + 2` for the type being serialized, ensuring that the serialized value can be deserialized to the same floating-point value without loss of precision.

5. **Other Types**: Serialized using the stream insertion operator `<<`.
   - For types that are not explicitly handled (like integers and user-defined types), the `save_impl` method uses the `<<` operator to write the value to the stream. This relies on the presence of an overload of the `<<` operator for the type.

6. **Strings**: Serialized as they are.
   - The `put` method for strings writes a null-terminated character array directly to the output stream.

7. **Binary Data**: Not covered in the details of this file.
   - There is a mention of a `save_binary` method, but its implementation is not provided here. It would handle binary data as a blob, likely prefixing it with size information.

Based on these details, a text serialized file with basic types might look like this:

- Booleans as "0" or "1".
- Characters and wide characters as their integer representations.
- Floating-point numbers in scientific notation with maximum necessary precision.
- Other types as their natural string representations (if they have `<<` overloads).
- Strings as a series of characters terminated by a null character or some delimiter.

To manually deserialize such a file, you would:

- Read the file as text.
- Parse each expected type according to these rules.
- Convert the string representations back into the appropriate C++ types.

For example, if you know the first value in the file is a boolean, you would read until you hit a space or newline, then interpret "0" or "1" as `false` or `true`. If the next value is expected to be a `float`, you would read until the next space or newline and use the appropriate conversion function (like `std::stof` in C++) to convert the scientific notation string back to a floating point number.

The manual process would be significantly complex and error-prone because you need to know the exact sequence and types of serialized data. Additionally, complex objects and pointers would require a much more intricate approach, considering object tracking and potential polymorphism. If there is any variation from the expected format (such as additional metadata or versioning information), that would add another layer of complexity to the deserialization process.

Looking at the GitHub repo for the Boost serialization lib we can see some example code that writes out a file with a similar format as the presets:

We can also see where the BOOST_ARCHIVE_SIGNATURE ("serialization::archive") is defined:

And the BOOST_ARCHIVE_VERSION:

And where it's used in the implementations of the various archive serializer/deserializers:

It seems like anything not handled in those files can then fall back to implementations in other more common files, but it all got a bit deep/hard to follow for me at that point.

But based on that information, we can see that the start of my test file:

22 serialization::archive 10 0 7 0 7 11 K.I.T. Bass 14 Vintouch Vol 1 ...

Ends up parsing as something like:

  • 22: length of BOOST_ARCHIVE_SIGNATURE string
  • serialization::archive: BOOST_ARCHIVE_SIGNATURE
  • 10: BOOST_ARCHIVE_VERSION
  • 0: ?probably a boolean flag?
  • 7: ???
  • 0: ?probably a boolean flag?
  • 7:??
  • 11: length of the next string
  • K.I.T. Bass: string
  • 14: length of the next string
  • Vintouch Vol 1: string
  • etc

Looking at my test file, it seems like a LOT of the parameters seem to follow this [length][space][string][space] format as a basis, with some higher order formats at times as well.

For example near the start there seems to be a list of 22 strings, each string prefixed with it's length as above; though I'm not sure what the 0 near the start of it is for, nor what the 1 0 at the end of it is for off the top of my head:

22 0 3 70s 3 80s 3 90s 7 Ambient 6 Analog 6 Berlin 7 Classic 5 Clean 5 Delay 5 Glide 5 House 10 Jazz/Blues 12 Long Release 6 Mellow 3 Pop 4 Rock 6 Simple 4 Soft 9 Synthwave 6 Trance 14 Tropical House 7 Vibrato 1 0 

@gonzaloarca I can't test this myself since I don't have Mini V3.vst3, but I wonder if you can use some code like this to load the VST, show the GUI, manually load your preset, then close the GUI and print out the values of the various plugin.parameters?

from pprint import pprint

from pedalboard import load_plugin 

# Load the VST
mini = load_plugin('/Library/Arturia/Mini V3/Mini V3.vst3')

# Show the GUI
synth_plugin.show_editor()

# TODO: manually load the preset in the GUI here, then close the GUI

# Show the plugin parameters
print("Plugin Parameters:")
#pprint(synth_plugin.parameters)
for key, parameter in synth_plugin.parameters.items():
  raw_value = parameter.raw_value
  print(f". {key}: {raw_value}")

In the code I've been playing around with today, I do something a little more complex to capture the initial default state of the synth params, and then print out how they changed after showing the editor:

print("Capturing initial state of synth params..")
initial_synth_params = {key: synth_plugin.parameters[key].raw_value for key in synth_plugin.parameters.keys()}

print("Showing synth GUI..")
synth_plugin.show_editor()

print("Capturing state of synth params after showing GUI..")
new_synth_params = {key: synth_plugin.parameters[key].raw_value for key in synth_plugin.parameters.keys()}

Depending on your needs, you could then either use the parameter names and values shown here to help figure out the structs/order to reverse engineer the preset file format (alongside my notes above); or perhaps just save the param keys/values (eg. in a json file) and implement your own manual 'load_json_preset' sort of function (though for that to work, you would obviously then need to convert all of your existing presets to the new json format)

There's a basic example of saving preset patches as json like this here:

And a slightly more robust variation of it here:

And then can load back from the json file like this:

This other issue may also be be relevant here, as it talks about another way of controlling preset loading/changing within VST plugins:

Just wanted to note that #297 just got merged, which may provide one potential angle of a solution here:

Apologies that this took so long - I've just made a couple changes and merged this, and it should be available as of v0.9.6 (released later today).

I did rename this property to raw_state instead of just state, as the state data is often (but not always) encoded in a format that I hope we can parse and expose as a .state parameter later. (i.e.: if the state of a VST3 is valid XML, Pedalboard could unwrap and parse that XML directly to make the client code simpler.)

Originally posted by @psobot in #297 (comment)

Docs:

Here's some example code of using the new .raw_state feature to save/restore presets:

I can confirm that the new plugin.raw_state property fixes my issue and I can store/load the VST3 plugin state completely -- both the audio and UI settings.

Here is an example:

effect = load_plugin(r'C:\Program Files\Common Files\VST3\MeldaProduction\Stereo\MSpectralPan.vst3')

try:
	with open('params.bin', 'rb') as file:
		effect.raw_state = file.read()
except FileNotFoundError:
	pass

effect.show_editor()

with open('params.bin', 'wb') as file:
	file.write(effect.raw_state)

board = Pedalboard([effect])

Originally posted by @famzah in #187 (comment)


@gonzaloarca It would be interesting to see what the output looks like for your VST's .raw_state. Are you able to use code similar to the above, manually load a preset file while the effect.show_editor() GUI is visible, and then maybe share both the params.bin file that gets saved, and the preset you loaded on this issue? That would give us a good starting point at comparing/contrasting the 'built in preset load' with what we have access to via .raw-state; and may allow us to figure a way to implement an 'external load function'.

Edit: I express a similar idea (but for Serum) in this comment: #277 (comment)