1
2 import Live
3 from functools import partial
4 from CompoundComponent import CompoundComponent
5 from ToggleComponent import ToggleComponent
6 from Util import const, in_range, clamp
7 from SubjectSlot import subject_slot
8 import Task
9 TEMPO_TOP = 200.0
10 TEMPO_BOTTOM = 60.0
11 TEMPO_FINE_RANGE = 2.56
12 SEEK_SPEED = 10.0
15 """
16 Class encapsulating all functions in Live's transport section.
17 """
18
20 super(TransportComponent, self).__init__(*a, **k)
21 self._ffwd_button = None
22 self._rwd_button = None
23 self._tap_tempo_button = None
24 self._tempo_control = None
25 self._tempo_fine_control = None
26 self._song_position_control = None
27 self._rwd_task = Task.Task()
28 self._ffwd_task = Task.Task()
29 self._fine_tempo_needs_pickup = True
30 self._prior_fine_tempo_value = -1
31 self._end_undo_step_task = self._tasks.add(Task.sequence(Task.wait(1.5), Task.run(self.song().end_undo_step)))
32 self._end_undo_step_task.kill()
33 song = self.song()
34 self._loop_toggle, self._punch_in_toggle, self._punch_out_toggle, self._record_toggle, self._play_toggle, self._stop_toggle, self._nudge_down_toggle, self._nudge_up_toggle, self._metronome_toggle, self._session_record_toggle, self.arrangement_overdub_toggle, self._overdub_toggle = self.register_components(ToggleComponent('loop', song), ToggleComponent('punch_in', song, is_momentary=True), ToggleComponent('punch_out', song, is_momentary=True), ToggleComponent('record_mode', song), ToggleComponent('is_playing', song, model_transform=const(True)), ToggleComponent('is_playing', song, model_transform=const(False), view_transform=const(False)), ToggleComponent('nudge_down', song, is_momentary=True), ToggleComponent('nudge_up', song, is_momentary=True), ToggleComponent('metronome', song), ToggleComponent('session_record', song), ToggleComponent('arrangement_overdub', song), ToggleComponent('overdub', song))
35
38
41
51
57
63
67
70
73
76
82
85
89
92
95
98
101
104
107
109 if not (not control or control.message_map_mode() is Live.MidiMap.MapMode.absolute):
110 raise AssertionError
111 if not (not fine_control or fine_control.message_map_mode() is Live.MidiMap.MapMode.absolute):
112 raise AssertionError
113 self._tempo_control = self._tempo_control != control and control
114 self._tempo_value.subject = control
115 self._tempo_fine_control = self._tempo_fine_control != fine_control and fine_control
116 self._tempo_fine_value.subject = fine_control
117 self._fine_tempo_needs_pickup = True
118 self._prior_fine_tempo_value = -1
119
121 if not (not fine_control or fine_control.message_map_mode() is Live.MidiMap.MapMode.absolute):
122 raise AssertionError
123 self._tempo_fine_control = self._tempo_fine_control != fine_control and fine_control
124 self._tempo_fine_value.subject = fine_control
125 self._fine_tempo_needs_pickup = True
126 self._prior_fine_tempo_value = -1
127
132
136
137 @subject_slot('value')
140
148
149 @subject_slot('value')
152
161
166
167 @subject_slot('value')
176
180
181 @subject_slot('value')
186
187 @subject_slot('value')
189 if self.is_enabled():
190 if self._fine_tempo_needs_pickup:
191 if in_range(self._prior_fine_tempo_value, 0, 128):
192 range_max = max(value, self._prior_fine_tempo_value)
193 range_min = min(value, self._prior_fine_tempo_value)
194 if in_range(64, range_min, range_max + 1):
195 self._fine_tempo_needs_pickup = False
196 else:
197 raise in_range(self._prior_fine_tempo_value, 0, 128) or AssertionError
198 difference = value - self._prior_fine_tempo_value
199 ratio = 127.0 / TEMPO_FINE_RANGE
200 new_tempo = clamp(self.song().tempo + difference / ratio, TEMPO_BOTTOM, TEMPO_TOP)
201 self.song().tempo = new_tempo
202 self._prior_fine_tempo_value = value
203
204 @subject_slot('value')
206 raise NotImplementedError
207