1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
#!/usr/bin/env python3
import sys
import argparse
import subprocess
import xml.etree.ElementTree as ET
def run_faust(dsp_file, output_filename):
"""Run Faust on the DSP_FILE, write the output to OUTPUT_FILENAME, and return the parsed XML tree of the description file."""
with open(output_filename, "w") as outfile:
result = subprocess.run(
[
"faust",
"-xml", # generate description
"-lang",
"c", # C code, please
"-scal", # non-vectorized code
"-light", # keep it light
"-a",
"faust-ksoloti-object.c", # architecture file
dsp_file,
],
stdout=outfile,
check=True,
)
return result
def process_sliders(xml):
"""Process sliders and return inlets, params, and conversions."""
inlets = ""
params = ""
output = ""
sliders = xml.findall(".//ui/activewidgets/widget[@type='hslider']")
for slider in sliders:
attributes = slider.attrib
varname = slider.find("varname").text
label = slider.find("label").text
max = slider.find("max").text
inlets += f"<frac32 name=\"{label}\" />\n"
params += f"<frac32.u.map name=\"{label}\" />\n"
# TODO: summation and conversion based on type
output += f"int32_t {label} = mtof48k_ext_q31(param_{label} + inlet_{label});\n"
output += f"mdsp.{varname} = fminf(1.0f, q27_to_float({label})) * {max};\n"
return (inlets, params, output)
def massage_output(oldfile, newfile, xml):
"""
Process the Faust output.
"""
# - throw everything before <objdefs> away
# - throw everything after </objdefs> away
# - remove extern "C" chunk (wrapped in "#ifdef __cplusplus")
# - replace scaling factors for sliders
# - replace metadata
inlets, params, conversions = process_sliders(xml)
seen_start = False
seen_end = False
skip_ifdef = False
with open(oldfile, "r", encoding="utf-8") as infile, open(
newfile, "w", encoding="utf-8"
) as outfile:
for line in infile:
if not seen_start and line.startswith("<objdefs>"):
seen_start = True
if not seen_end and line.startswith("</objdefs>"):
seen_end = True
if seen_end:
outfile.write(line)
return
if seen_start:
if "<<INLETS>>" in line:
outfile.write(line.replace("<<INLETS>>", inlets))
continue
if "<<PARAMS>>" in line:
outfile.write(line.replace("<<PARAMS>>", params))
continue
if "<<SLIDERS>>" in line:
outfile.write(line.replace("<<SLIDERS>>", conversions))
continue
if line.startswith("#ifdef __cplusplus"):
skip_ifdef = True
if skip_ifdef:
if line.startswith("#endif"):
skip_ifdef = False
else:
continue
outfile.write(line)
else:
continue
def main(faust_input_file, axo_output_file):
axo_temp_output_file = axo_output_file + ".temp"
run_faust(faust_input_file, axo_temp_output_file)
# Parse generated XML description
dsp_xml = faust_input_file + ".xml"
xml = ET.parse(dsp_xml).getroot()
# Modify Axoloti object file
massage_output(axo_temp_output_file, axo_output_file, xml)
def arguments():
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input", type=str, required=True)
parser.add_argument("-o", "--output", type=str, required=True)
return parser.parse_args()
if __name__ == "__main__":
args = arguments()
main(args.input, args.output)
|