comparison flax/Submaster.py @ 167:79bc84310e80

changes from tonight's rehearsal: changes from tonight's rehearsal: - CueFader is closer to actually running the show, computes DMX levels to send. - KeyboardComposer is not a dummy. Use DMXDUMMY=1 to disable it. - Submaster: subs can now be "temporary" -- i.e. they shouldn't be saved or loaded. to save a temporary sub, make a copy of it with a proper name since the computed name will be ugly. Also, get_normalized_copy() and crossfade() methods added. linear_fade helper (shouldn't be in Submaster, probably) added too. - dmxchanedit: longer labels - cuelist1 now has some bogus data in it and some crap removed - dmxclient: now listens to the $DMXHOST and $DMXDUMMY env variables. - patchdata: now up to date with this year's show - danshow subs song{01..19}: removed. maybe we'll re-add them in an archive directory.
author dmcc
date Tue, 08 Jul 2003 16:19:55 +0000
parents 1fe54442db38
children 3905d3c92aaa
comparison
equal deleted inserted replaced
166:7ccf1d10804b 167:79bc84310e80
6 6
7 import Patch 7 import Patch
8 8
9 class Submaster: 9 class Submaster:
10 "Contain a dictionary of levels, but you didn't need to know that" 10 "Contain a dictionary of levels, but you didn't need to know that"
11 def __init__(self, name, leveldict=None): 11 def __init__(self, name, leveldict=None, temporary=0):
12 self.name = name 12 self.name = name
13 self.temporary = temporary
13 if leveldict: 14 if leveldict:
14 self.levels = leveldict 15 self.levels = leveldict
15 else: 16 else:
16 self.levels = {} 17 self.levels = {}
17 self.reload() 18 self.reload()
18 def reload(self): 19 def reload(self):
20 if self.temporary:
21 return
19 try: 22 try:
20 self.levels.clear() 23 self.levels.clear()
21 subfile = file("subs/%s" % self.name) 24 subfile = file("subs/%s" % self.name)
22 for line in subfile.readlines(): 25 for line in subfile.readlines():
23 if not line.strip(): # if line is only whitespace 26 if not line.strip(): # if line is only whitespace
31 print "(%s) Error with this line: %s" % (self.name, 34 print "(%s) Error with this line: %s" % (self.name,
32 line[:-1]) 35 line[:-1])
33 except IOError: 36 except IOError:
34 print "Can't read file for sub: %s" % self.name 37 print "Can't read file for sub: %s" % self.name
35 def save(self): 38 def save(self):
39 if self.temporary:
40 return
41
36 subfile = file("subs/%s" % self.name, 'w') 42 subfile = file("subs/%s" % self.name, 'w')
37 names = self.levels.keys() 43 names = self.levels.keys()
38 names.sort() 44 names.sort()
39 for name in names: 45 for name in names:
40 val = self.levels[name] 46 val = self.levels[name]
50 self.save() 56 self.save()
51 def get_levels(self): 57 def get_levels(self):
52 return self.levels 58 return self.levels
53 def __mul__(self, scalar): 59 def __mul__(self, scalar):
54 return Submaster("%s*%s" % (self.name, scalar), 60 return Submaster("%s*%s" % (self.name, scalar),
55 dict_scale(self.levels, scalar)) 61 dict_scale(self.levels, scalar), temporary=1)
56 __rmul__ = __mul__ 62 __rmul__ = __mul__
57 def max(self, *othersubs): 63 def max(self, *othersubs):
58 return sub_maxes(self, *othersubs) 64 return sub_maxes(self, *othersubs)
59 def __repr__(self): 65 def __repr__(self):
60 levels = ' '.join(["%s:%.2f" % item for item in self.levels.items()]) 66 levels = ' '.join(["%s:%.2f" % item for item in self.levels.items()])
67 dmxchan = Patch.get_dmx_channel(k) - 1 73 dmxchan = Patch.get_dmx_channel(k) - 1
68 levels[dmxchan] = max(v, levels[dmxchan]) 74 levels[dmxchan] = max(v, levels[dmxchan])
69 75
70 return levels 76 return levels
71 def normalize_patch_names(self): 77 def normalize_patch_names(self):
78 """Use only the primary patch names."""
72 # possibly busted -- don't use unless you know what you're doing 79 # possibly busted -- don't use unless you know what you're doing
73 self.set_all_levels(self.levels.copy()) 80 self.set_all_levels(self.levels.copy())
81 def get_normalized_copy(self):
82 """Get a copy of this sumbaster that only uses the primary patch
83 names. The levels will be the same."""
84 newsub = Submaster("%s (normalized)" % self.name, temporary=1)
85 newsub.set_all_levels(self.levels)
86 return newsub
87 def crossfade(self, othersub, amount):
88 """Returns a new sub that is a crossfade between this sub and
89 another submaster.
90
91 NOTE: You should only crossfade between normalized submasters."""
92 otherlevels = othersub.get_levels()
93 keys_set = {}
94 for k in self.levels.keys() + otherlevels.keys():
95 keys_set[k] = 1
96 all_keys = keys_set.keys()
97
98 xfaded_sub = Submaster("xfade", temporary=1)
99 for k in all_keys:
100 xfaded_sub.set_level(k,
101 linear_fade(self.levels.get(k, 0),
102 otherlevels.get(k, 0),
103 amount))
104
105 return xfaded_sub
106
107 def linear_fade(start, end, amount):
108 """Fades between two floats by an amount. amount is a float between
109 0 and 1. If amount is 0, it will return the start value. If it is 1,
110 the end value will be returned."""
111 level = start + (amount * (end - start))
112 return level
74 113
75 def sub_maxes(*subs): 114 def sub_maxes(*subs):
76 return Submaster("max(%r)" % (subs,), 115 return Submaster("max(%r)" % (subs,),
77 dict_max(*[sub.levels for sub in subs])) 116 dict_max(*[sub.levels for sub in subs]), temporary=1)
78 117
79 class Submasters: 118 class Submasters:
80 "Collection o' Submaster objects" 119 "Collection o' Submaster objects"
81 def __init__(self): 120 def __init__(self):
82 self.submasters = {} 121 self.submasters = {}