Last active
July 2, 2021 10:16
-
-
Save liamr/a0322e94035e25cd912c07fa5b211ba9 to your computer and use it in GitHub Desktop.
.aupreset to .fxp.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
From: https://forum.juce.com/t/script-to-convert-aupreset-to-fxp/7919 | |
Author: https://forum.juce.com/u/yairadix | |
I made a small Python script to convert .aupreset files to .fxp format (VST presets). | |
Hopefully others may find it useful too. | |
Our use case for it was creating our factory presets for SurferEQ once in Logic and then converting them to other formats. | |
This script could be easily adapted to convert the other way around. (it uses the Construct library to describe the fxb format declaratively for both parsing and building) | |
*/ | |
from construct import Array, BFloat32, Bytes, Const, Container, Enum, LazyBound, String, Struct, Switch, UBInt32, ULInt32 | |
from os import path | |
import sys | |
from xml.dom import minidom | |
# fxp/fxb file format. (VST/Cubase's preset or "bank" files from before VST3 era) | |
# based on VST SDK's vst2.x/vstfxstore.h | |
# names as in the source | |
vst2preset = Struct('vst2preset', | |
Const(Bytes('chunkMagic', 4), 'CcnK'), | |
UBInt32('byteSize'), | |
Enum(Bytes('fxMagic', 4), | |
FXP_PARAMS = 'FxCk', FXP_OPAQUE_CHUNK = 'FPCh', | |
FXB_REGULAR = 'FxBk', FXB_OPAQUE_CHUNK = 'FBCh', | |
), | |
UBInt32('version'), | |
UBInt32('fxID'), | |
UBInt32('fxVersion'), | |
UBInt32('count'), | |
Switch('data', lambda ctx: ctx['fxMagic'], { | |
'FXP_PARAMS': Struct('data', | |
String('prgName', 28, padchar = '\0'), | |
Array(lambda ctx: ctx['_']['count'], BFloat32('params')), | |
), | |
'FXP_OPAQUE_CHUNK': Struct('data', | |
String('prgName', 28, padchar = '\0'), | |
UBInt32('size'), | |
Bytes('chunk', lambda ctx: ctx['size']), | |
), | |
'FXB_REGULAR': Struct('data', | |
Bytes('future', 128), # zeros | |
# Array of FXP_PARAMS vst2preset | |
Array(lambda ctx: ctx['_']['count'], LazyBound('presets', lambda: vst2preset)), | |
), | |
'FXB_OPAQUE_CHUNK': Struct('data', | |
Bytes('future', 128), # zeros | |
UBInt32('size'), | |
# Unknown format of internal chunk | |
Bytes('chunk', lambda ctx: ctx['size']), | |
), | |
}), | |
) | |
def get_aupreset_value_node_for_key(dom, key, value_tag): | |
for key_node in dom.getElementsByTagName('key'): | |
[key_data] = key_node.childNodes | |
if key_data.data == key: | |
break | |
else: | |
raise KeyError | |
# Advance to the value node. | |
node = key_node | |
while True: | |
node = node.nextSibling | |
if node.hasChildNodes(): | |
value_node = node | |
break | |
assert value_node.tagName == value_tag | |
return value_node | |
def get_xml_node_data(node): | |
[data_node] = node.childNodes | |
return data_node.data | |
def get_aupreset_subtype_node(dom): | |
return get_aupreset_value_node_for_key(dom, 'subtype', 'integer') | |
def parse_aupreset(dom): | |
return { | |
'data': get_xml_node_data(get_aupreset_value_node_for_key(dom, 'jucePluginState', 'data')).decode('base64'), | |
'plugin_id_int': int(get_xml_node_data(get_aupreset_subtype_node(dom))), | |
} | |
[src_filename, dst_filename] = sys.argv[1:] | |
preset_name = path.split(src_filename)[1].rsplit('.', 1)[0] | |
au_preset = parse_aupreset(minidom.parseString(file(src_filename, 'rb').read())) | |
# Save to vst format | |
fxp_data = Container( | |
chunkMagic = 'CcnK', | |
byteSize = 0, # will fill later | |
fxMagic = 'FXP_OPAQUE_CHUNK', | |
version = 1, | |
fxID = au_preset['plugin_id_int'], | |
fxVersion = 1, | |
count = 0, | |
data = Container( | |
prgName = preset_name, | |
size = len(au_preset['data']), | |
chunk = au_preset['data'], | |
), | |
) | |
fxp_data.byteSize = len(vst2preset.build(fxp_data)) - 8 | |
file(dst_filename, 'wb').write(vst2preset.build(fxp_data)) |
Traceback (most recent call last):
File "C:\Users\Naozumi\Desktop\2au\first.py", line 77, in
au_preset = parse_aupreset(minidom.parseString(open(src_filename, 'rb').read()))
File "C:\Users\Naozumi\Desktop\2au\first.py", line 70, in parse_aupreset
'data': get_xml_node_data(get_aupreset_value_node_for_key(dom, 'jucePluginState', 'data')).decode('base64'),
File "C:\Users\Naozumi\Desktop\2au\first.py", line 50, in get_aupreset_value_node_for_key
raise KeyError
KeyError
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Traceback (most recent call last):
File "E:\TEST\autovst.py", line 1, in
from construct import Array, BFloat32, Bytes, Const, Container, Enum, LazyBo
und, String, Struct, Switch, UBInt32, ULInt32
ImportError: cannot import name 'BFloat32'