Skip to content

Instantly share code, notes, and snippets.

@liamr
Last active July 2, 2021 10:16
Show Gist options
  • Save liamr/a0322e94035e25cd912c07fa5b211ba9 to your computer and use it in GitHub Desktop.
Save liamr/a0322e94035e25cd912c07fa5b211ba9 to your computer and use it in GitHub Desktop.
.aupreset to .fxp.
/*
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))
@Naozumi520
Copy link

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