-
Notifications
You must be signed in to change notification settings - Fork 0
/
live.py
132 lines (107 loc) · 3.99 KB
/
live.py
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
129
130
131
132
from .context import Context
from .rules import Rule, RuleSet
from .note import Note
from collections import OrderedDict
class Live(object):
class RuleSets(object):
def __init__(self, parent):
self.sets = OrderedDict()
self.current = None
self.parent = parent
def load(self, rule_sets):
for rule_set in rule_sets:
self.add(rule_set)
def add(self, rule_set, current=False):
if not isinstance(rule_set, RuleSet):
raise TypeError(f'{rule_set} is not a RuleSet')
self.sets[rule_set.name] = rule_set
if current:
self.set_current(rule_set.name)
def set_current(self, name):
if not name:
# Set current to None
self.current = None
else:
try:
self.current = self.sets[name]
except KeyError:
print(f'Cannot find rule set {name}, not changing anything...')
return
print(f'Setting current rule set to {name}')
self.parent.send_status()
def set_default(self):
if self.sets:
self.set_current(next(iter(self.sets)))
def __init__(self):
self.context = Context()
self.rules = Live.RuleSets(self)
self.inport = None
self.emitters = []
# Emitter related functions
def add_emitter(self, emitter):
self.emitters.append(emitter)
def remove_emitter(self, emitter):
self.emitters.remove(emitter)
def emit(self, event):
for emitter in self.emitters:
emitter.emit(event)
def send_status(self):
for emitter in self.emitters:
emitter.send_status()
# The poll function called by tornado periodic callback
def midi_poll(self):
time_event = self.context.time_event()
if time_event:
event = {}
event['data'] = {
'type': 'timer',
'name': time_event
}
self.emit(event)
if self.inport:
for message in self.inport.iter_pending():
self.callback(message)
# Resets everything
def reset(self):
self.context.reset()
# The real callback when there is a MIDI event
# Called by Live.midi_poll() actually
def callback(self, message):
self.context.incoming_message(message)
# Deal with keystroke triggers
event = {}
event['data'] = {
'type': 'midi',
'message': {
'type': message.type,
'channel': message.channel,
'note': message.note if hasattr(message, 'note') else None,
'velocity': message.velocity if hasattr(message, 'velocity') else None,
'control': message.control if hasattr(message, 'control') else None,
'value': message.value if hasattr(message, 'value') else None,
'time': message.time,
'note_display': str(Note(message.note)) if hasattr(message, 'note') else '',
},
'context': {
'notes': [str(note) for note in sorted(self.context.notes)],
'pedal': self.context.pedal,
}
}
self.emit(event)
if self.rules.current and message.type.startswith('note'):
triggered = self.rules.current.examine(self.context)
for trigger in triggered:
event = {}
event['data'] = {
'type': 'signal',
'name': trigger
}
self.emit(event)
# Special signal
if trigger == 'reset':
self.reset()
def status(self):
out = OrderedDict()
out['device'] = self.inport.name if self.inport else None
out['ruleset'] = self.rules.current.name if self.rules.current else None
return out