-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathprocess_patcher.py
More file actions
110 lines (90 loc) · 3.89 KB
/
process_patcher.py
File metadata and controls
110 lines (90 loc) · 3.89 KB
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
from freezing_utils import get_patcher_dict
class Processor:
def process_elements(self, patcher, voice_count: int, abstraction_name=""):
"""Processes patchers."""
def get_results(self):
"""Returns the current results."""
return None
def process_patch(patcher, abstraction_entries: list[dict], processor: Processor):
process_patch_recursive(patcher, abstraction_entries, processor, 1, "")
def process_patch_recursive(
patcher,
abstraction_entries: list[dict],
processor: Processor,
voice_count: int,
abstraction_file_name: str,
):
"""Recursively progress through subpatchers, invoking the processor for every patcher
inluding every instance of the patch's dependencies that can be found among the
abstraction files that were passed in.
Arguments
patcher: patcher to process
abstraction_entries: list of abstraction entries in frozen device
processor: instance of a Processor that is invoked for this patch
voice_count: the amount of voices when this patcher occurs in a poly~ in its parent patch
abstraction_file_name: the file name of the abstraction this patch is in, if it is in an abstraction
"""
processor.process_elements(patcher, voice_count, abstraction_file_name)
for box_entry in patcher["boxes"]:
box = box_entry["box"]
if "patcher" in box and (
box.get("maxclass") != "bpatcher" or box.get("embed") == 1
):
patch = box["patcher"]
# get subpatcher or embedded bpatcher count
process_patch_recursive(patch, abstraction_entries, processor, 1, "")
# if no abstractions were passed in, we assume we don't want to recurse into abstarctions
if len(abstraction_entries) == 0:
continue
# check for known abstraction
file_name = get_abstraction_name(box, abstraction_entries)
if file_name is None:
continue
abstraction = [
item for item in abstraction_entries if item["file_name"] == file_name
][0]
patch = get_patcher_dict(abstraction)
if patch == {}:
continue # something went wrong when parsing the abstraction
voice_count = 1
if "text" in box and box["text"].startswith("poly~"):
# get poly abstraction count
tokens = box["text"].split(" ")
voice_count = int(tokens[2]) if len(tokens) > 2 else 1
process_patch_recursive(
patch,
abstraction_entries,
processor,
voice_count,
file_name,
)
def get_abstraction_name(box, abstraction_entries: list[dict]):
"""
Checks if this box is an abstraction and if so, return the name of the abstraction file.
- returns None if this is not an abstraction
- throws error if an abstraction name was expected but it was not found in the list of known names
"""
# cache names of known abstractions. TODO: find a way to do this more efficiently.
abstraction_filenames = [str(item["file_name"]) for item in abstraction_entries]
if "text" in box:
if box["text"].startswith("poly~"):
name = box["text"].split(" ")[1] + ".maxpat"
if name in abstraction_filenames:
return name
else:
raise ValueError(
"poly~ pointing to file that is not known as a dependency: " + name
)
else:
name = box["text"].split(" ")[0] + ".maxpat"
if name in abstraction_filenames:
return name
if box.get("maxclass") == "bpatcher" and box.get("embed") != 1:
if box.get("name") in abstraction_filenames:
return box["name"]
else:
raise ValueError(
"Non-embedded bpatcher pointing to file that is not known as a dependency: "
+ box["name"]
)
return None