#!/usr/bin/env python3 import sys from xml.dom.minidom import parse import re from collections import defaultdict import argparse def key_change(key): if key[-1].isdigit(): num = int(key[-1]) return '%s_%d' % (key[:-1], num+1) return key def starting(key, args): '''returns True if key starts with any of the string in args''' return any(key.startswith(x) for x in args) def search(key, *regexps): return any(bool(re.search(r, key)) for r in regexps) def parse_params(preset): return {par.attributes['name'].value: par.attributes['value'].value for par in preset.getElementsByTagName('param')} def multibandcompressor_get_params(params): newparams = {} skipped = 0 for key, val in params.items(): if starting(key, ('level_', 'meter_', 'clip_', 'detection', 'compression', 'mode', 'output', 'notebook', )): skipped += 1 continue if key == 'bypass' or key.startswith('solo'): newparams[key_change(key)] = bool(int(val)) elif search(key, r'p\d_q$'): par['q_%c' % key[1]] = float(val) elif key.startswith('bypass') or re.match(r'output\d', key): continue elif search(key, 's_level$'): newparams['level_%c' % key[0]] = bool(int(val)) elif starting(key, ('attack', 'ratio', 'release', 'makeup', 'knee', 'threshold')): newparams[key_change(key)] = float(val) elif search(key, '^mode', '^output', '_mode$'): newparams[key_change(key)] = int(val) elif key.startswith('freq'): num = int(key[-1]) newparams['split_%d_%d' % (num+1, num+2)] = float(val) else: print('!!', key, val, file=sys.stderr) if skipped: print('%d skipped' % skipped, file=sys.stderr) return newparams def filter_get_params(params): newparams = {} skipped = 0 for key in params: if key == 'freq': newparams['frequency'] = float(params[key]) elif key == 'res': newparams['resonance'] = float(params[key]) elif key == 'inertia': newparams[key] = float(params[key]) elif key in ('mode',): newparams[key] = int(params[key]) else: print('!!', key, params[key], file=sys.stderr) skipped += 1 if skipped: print('%d skipped' % skipped, file=sys.stderr) return newparams def eq_get_params(params): par = {} skipped = 0 for key, val in params.items(): if starting(key, ('level_', 'meter_', 'clip_', 'detection', 'compression', 'mode', 'output', 'notebook', 'analyzer', 'zoom', 'individuals', 'bypass', )) or\ search(key, '^.s_freq$'): skipped += 1 continue if search(key, '^bypass$', '^solo'): par[key_change(key)] = bool(int(val)) elif search(key, r'p\d_q$'): par['q_%c' % key[1]] = float(val) elif search(key, r'^p\d_active$'): par['f%c_active' % key[1]] = bool(int(val)) elif key.endswith('_active'): par[key] = bool(int(val)) elif search(key, r'\d_freq$'): par['freq_%c' % key[1]] = float(val) elif search(key, r'^.p_freq$'): par['freq_%c' % key[0]] = float(val) elif search(key, 's_level$'): par['level_%c' % key[0]] = float(val) elif search(key, r'^p\d_level$'): par['level_%c' % key[1]] = float(val) elif re.match(r'output\d', key): continue elif starting(key, ('attack', 'ratio', 'release', 'makeup', 'knee', 'threshold')) \ or re.search('_freq$', key): par[key_change(key)] = float(val) elif search(key, '_mode$'): par[key_change(key)] = int(val) elif key.startswith('freq'): num = int(key[-1]) par['split_%d_%d' % (num+1, num+2)] = float(val) else: print('!! <%s> = `%s`' % (key, val), file=sys.stderr) par[key] = val print('%d skipped' % skipped, file=sys.stderr) return par get_params = { 'multibandcompressor': multibandcompressor_get_params, 'filter': filter_get_params, 'eq8': eq_get_params, } fx_names = {'eq8': 'equalizer8band'} def ls_value_format(v): if type(v) is bool: return 'true' if v else 'false' if type(v) is int: return str(v) if type(v) is float: return str(v) if type(v) is str: return '"%s"' % v # let's hope no quote inside! raise ValueError('Unsupported type') def ls_format(params, plugin_name): param_set = ', '.join(['{}={}'.format(k, ls_value_format(v)) for k, v in sorted(params.items())]) return ' a = ladspa.%s(%s, a)' % \ (fx_names[plugin_name] if plugin_name in fx_names else plugin_name, param_set) def usage(): print('%s: Convert an XML file created by calfjackhost to ' 'liquidsoap-equivalent code, making use of calf-ladspa' % sys.argv[0]) print('\nUsage:') print(' %s FILENAME.xml [PRESET]' % sys.argv[0]) print('\nIf PRESET is omitted, the command will print a list of available ' 'presets found in the specified file') print('\nIf PRESET is given, the command will convert that preset in ' 'liquidsoap ladspa code') def get_parser(): p = argparse.ArgumentParser() p.add_argument('--function-name', type=str, default='calffilter', help='Generate a liquidsoap function with this name') p.add_argument('fname', nargs=1, help='An XML file generated by calfjackhost') p.add_argument('presets', nargs='*', help='All the filters you want to use') return p if __name__ == '__main__': p = get_parser() args = p.parse_args() dom = parse(args.fname[0]) if not args.presets: print('available presets:') for preset in dom.getElementsByTagName('preset'): print('-', preset.attributes['name'].value) sys.exit(0) print('def %s(a) =' % args.function_name) for preset in args.presets: pr_info = [p for p in dom.getElementsByTagName('preset') if p.attributes['name'].value == preset][0] raw_params = parse_params(pr_info) fx_type = pr_info.attributes['plugin'].value if fx_type not in get_params: print('plugin <%s> unsupported! output will probably be wrong' % fx_type, file=sys.stderr) params = raw_params else: params = get_params[fx_type](raw_params) print(ls_format(params, fx_type)) print(' a') print('end')